diff --git a/404.html b/404.html deleted file mode 100644 index 54c5a92b..00000000 --- a/404.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Selenite - - - - - - - - - - - -
- - -
- -

sewenite

-
-

۶( ゚д゚) oopsies.. we couwdn't find the page you wewe wooking fow >w<

- (๑•﹏•)⋆* ⁑⋆* pwease go home.. 👉👈
- awso wepowt a bug by cwicking hewe.. (*/ω\*) me wouwd appweciate it.. this isnt nyowmaw :゚(ノω\)゚・。 - -
- - diff --git a/bookmarklets.html b/bookmarklets.html index 17ffa003..95f3452f 100644 --- a/bookmarklets.html +++ b/bookmarklets.html @@ -1,98 +1,115 @@ - - + - - - - - + + + - Selenite + Bookmarklets | Selenite - + - - + + + + - -
- -

Bookmarklets

- Selenite Save Utility -

Drag the link above to your bookmarks bar. This is the official Selenite Save Utility, which allows you to download your data from any website easily.

- Selenite Upload Utility (UNFINISHED) -

Drag the link above to your bookmarks bar. This is the official Selenite Upload Utility, which allows you to upload a Selenite save file to any website easily.

+ +
+ Home + Bookmarklets + Games + Settings + Support + +
+ +
+

Bookmarklets

+

Official Selenite Bookmarklets

+
+
+ Selenite Save Utility +

This is the official Selenite Save Utility, which allows you to download your data from any website easily.

+
+
+ Selenite Upload Utility +

This is the official Selenite Upload Utility, which allows you to upload a Selenite save file to any website easily.

+
+
Selenite Minified -

Drag the link above to your bookmarks bar. This is a minified version of Selenite, that you can open at any time, without relying on a single website.

+

This is a minified version of Selenite, that you can open at any time, without relying on a single website.

+
+
+ The Ultimate Tab Cloak +

Disguise any website you're on as Canvas, Desmos, or Google Drive!

+
+

Other Bookmarklets

+
Edit any page -

Drag the link above to your bookmarks bar. This allows you to edit the text of any webpage.

+

This allows you to edit the text of any webpage.

+
+
X-Ray Goggles -

Drag the link above to your bookmarks bar. This allows you to view and edit the source code of any website, like a more powerful version of "Edit any page".

+

This allows you to view and edit the source code of any website, like a more powerful version of "Edit any page".

+
+
Dark Mode -

Drag the link above to your bookmarks bar. This sets any page to dark mode.

+

This sets any page to dark mode.

+
+
Piano -

Drag the link above to your bookmarks bar. This adds a piano to the page.

+

This adds a piano to the page.

+
+
Developer Console -

Drag the link above to your bookmarks bar. This adds all features of the developer console to whatever page your on.

+

This adds all features of the developer console to whatever page your on.

+
+
Asteroids -

Drag the link above to your bookmarks bar. This allows you to play Asteroids on the website you are on, destroying everything.

+

This allows you to play Asteroids on the website you are on, destroying everything.

+
+
Katamari -

Drag the link above to your bookmarks bar. This allows you to collect the words on the page into a ball. Fun game, I recommend playing it.

- -

Tab Cloaks

- The Ultimate Tab Cloak -

Drag the link above to your bookmarks bar. This adds multiple cloaks in one bookmarklet.

- Tab Cloak (Canvas) -

Drag the link above to your bookmarks bar. This disguises whatever website you're on as the Canvas Dashboard.

- Tab Cloak (Google Drive) -

Drag the link above to your bookmarks bar. This disguises whatever website you're on as Google Drive.

- Tab Cloak (Desmos) -

Drag the link above to your bookmarks bar. This disguises whatever website you're on Desmos.

-
+
+ + - + \ No newline at end of file diff --git a/celestemods/icon.png b/celestemods/icon.png new file mode 100644 index 00000000..5a97623f Binary files /dev/null and b/celestemods/icon.png differ diff --git a/celestemods/index.html b/celestemods/index.html new file mode 100644 index 00000000..f3e50f94 --- /dev/null +++ b/celestemods/index.html @@ -0,0 +1,11 @@ + + + + + + Document + + +

placeholder shit until i get the energy to actually add the mods

+ + \ No newline at end of file diff --git a/clickerheroes/staticdata.txt b/clickerheroes/staticdata.txt new file mode 100644 index 00000000..978f9571 --- /dev/null +++ b/clickerheroes/staticdata.txt @@ -0,0 +1,3521 @@ +{ + "achievements": { + "1": { + "iconId": 1, + "id": 1, + "rewardText": "", + "checkFunction": "highestFinishedZone", + "rewardParams": "", + "name": "Zone Explorer", + "checkParams": "10", + "description": "Finish %1 zones.", + "rewardFunction": "" + }, + "2": { + "iconId": 2, + "id": 2, + "rewardText": "", + "checkFunction": "highestFinishedZone", + "rewardParams": "", + "name": "Zone Warrior", + "checkParams": "25", + "description": "Finish %1 zones.", + "rewardFunction": "" + }, + "3": { + "iconId": 3, + "id": 3, + "rewardText": "", + "checkFunction": "highestFinishedZone", + "rewardParams": "", + "name": "Zone Master", + "checkParams": "50", + "description": "Finish %1 zones.", + "rewardFunction": "" + }, + "4": { + "iconId": 4, + "id": 4, + "rewardText": "", + "checkFunction": "highestFinishedZone", + "rewardParams": "", + "name": "Zone Lord", + "checkParams": "100", + "description": "Finish %1 zones.", + "rewardFunction": "" + }, + "5": { + "iconId": 5, + "id": 5, + "rewardText": "", + "checkFunction": "totalClicks", + "rewardParams": "", + "name": "Proficient Clicking", + "checkParams": "1000", + "description": "Click %1 times.", + "rewardFunction": "" + }, + "6": { + "iconId": 6, + "id": 6, + "rewardText": "", + "checkFunction": "totalClicks", + "rewardParams": "", + "name": "Sore Finger", + "checkParams": "10000", + "description": "Click %1 times.", + "rewardFunction": "" + }, + "7": { + "iconId": 7, + "id": 7, + "rewardText": "", + "checkFunction": "totalClicks", + "rewardParams": "", + "name": "Carpal Tunnel", + "checkParams": "50000", + "description": "Click %1 times.", + "rewardFunction": "" + }, + "8": { + "iconId": 8, + "id": 8, + "rewardText": "", + "checkFunction": "totalClicks", + "rewardParams": "", + "name": "Broken Mouse", + "checkParams": "100000", + "description": "Click %1 times.", + "rewardFunction": "" + }, + "9": { + "iconId": 9, + "id": 9, + "rewardText": "", + "checkFunction": "totalGold", + "rewardParams": "", + "name": "Uptown", + "checkParams": "1000000", + "description": "Receive (cumulative) %1 gold.", + "rewardFunction": "" + }, + "10": { + "iconId": 10, + "id": 10, + "rewardText": "", + "checkFunction": "totalGold", + "rewardParams": "", + "name": "Fat Cat", + "checkParams": "1000000000", + "description": "Receive (cumulative) %1 gold.", + "rewardFunction": "" + }, + "11": { + "iconId": 11, + "id": 11, + "rewardText": "", + "checkFunction": "totalGold", + "rewardParams": "", + "name": "Loaded", + "checkParams": "100000000000000", + "description": "Receive (cumulative) %1 gold.", + "rewardFunction": "" + }, + "12": { + "iconId": 12, + "id": 12, + "rewardText": "", + "checkFunction": "totalGold", + "rewardParams": "", + "name": "The 1%", + "checkParams": "1000000000000000000", + "description": "Receive (cumulative) %1 gold.", + "rewardFunction": "" + }, + "13": { + "iconId": 13, + "id": 13, + "rewardText": "", + "checkFunction": "gold", + "rewardParams": "", + "name": "Frugal", + "checkParams": "500000", + "description": "Hoard %1 gold.", + "rewardFunction": "" + }, + "14": { + "iconId": 14, + "id": 14, + "rewardText": "", + "checkFunction": "gold", + "rewardParams": "", + "name": "Stingy", + "checkParams": "5000000000", + "description": "Hoard %1 gold.", + "rewardFunction": "" + }, + "15": { + "iconId": 15, + "id": 15, + "rewardText": "", + "checkFunction": "gold", + "rewardParams": "", + "name": "Miserly", + "checkParams": "50000000000000", + "description": "Hoard %1 gold.", + "rewardFunction": "" + }, + "16": { + "iconId": 16, + "id": 16, + "rewardText": "", + "checkFunction": "gold", + "rewardParams": "", + "name": "Greedy", + "checkParams": "500000000000000000", + "description": "Hoard %1 gold.", + "rewardFunction": "" + }, + "17": { + "iconId": 17, + "id": 17, + "rewardText": "", + "checkFunction": "totalKills", + "rewardParams": "", + "name": "Killer", + "checkParams": "100", + "description": "Kill %1 monsters.", + "rewardFunction": "" + }, + "18": { + "iconId": 18, + "id": 18, + "rewardText": "", + "checkFunction": "totalKills", + "rewardParams": "", + "name": "Butcher", + "checkParams": "1000", + "description": "Kill %1 monsters.", + "rewardFunction": "" + }, + "19": { + "iconId": 19, + "id": 19, + "rewardText": "", + "checkFunction": "totalKills", + "rewardParams": "", + "name": "Executioner", + "checkParams": "10000", + "description": "Kill %1 monsters.", + "rewardFunction": "" + }, + "20": { + "iconId": 20, + "id": 20, + "rewardText": "", + "checkFunction": "totalKills", + "rewardParams": "", + "name": "Holocaust", + "checkParams": "100000", + "description": "Kill %1 monsters.", + "rewardFunction": "" + }, + "21": { + "iconId": 21, + "id": 21, + "rewardText": "", + "checkFunction": "totalBossKills", + "rewardParams": "", + "name": "Boss Slaughter", + "checkParams": "10", + "description": "Kill %1 bosses.", + "rewardFunction": "" + }, + "22": { + "iconId": 22, + "id": 22, + "rewardText": "", + "checkFunction": "totalBossKills", + "rewardParams": "", + "name": "Boss Massacre", + "checkParams": "100", + "description": "Kill %1 bosses.", + "rewardFunction": "" + }, + "23": { + "iconId": 23, + "id": 23, + "rewardText": "", + "checkFunction": "totalBossKills", + "rewardParams": "", + "name": "Boss Exterminator", + "checkParams": "1000", + "description": "Kill %1 bosses.", + "rewardFunction": "" + }, + "24": { + "iconId": 24, + "id": 24, + "rewardText": "", + "checkFunction": "totalBossKills", + "rewardParams": "", + "name": "Boss Murderer", + "checkParams": "10000", + "description": "Kill %1 bosses.", + "rewardFunction": "" + }, + "25": { + "iconId": 25, + "id": 25, + "rewardText": "", + "checkFunction": "totalUpgrades", + "rewardParams": "", + "name": "Considerate", + "checkParams": "25", + "description": "Get %1 hero upgrades.", + "rewardFunction": "" + }, + "26": { + "iconId": 26, + "id": 26, + "rewardText": "", + "checkFunction": "totalUpgrades", + "rewardParams": "", + "name": "Generous", + "checkParams": "50", + "description": "Get %1 hero upgrades.", + "rewardFunction": "" + }, + "27": { + "iconId": 27, + "id": 27, + "rewardText": "", + "checkFunction": "totalUpgrades", + "rewardParams": "", + "name": "Benevolent", + "checkParams": "75", + "description": "Get %1 hero upgrades.", + "rewardFunction": "" + }, + "28": { + "iconId": 28, + "id": 28, + "rewardText": "", + "checkFunction": "totalUpgrades", + "rewardParams": "", + "name": "Magnanimous", + "checkParams": "83", + "description": "Get %1 hero upgrades.", + "rewardFunction": "" + }, + "29": { + "iconId": 29, + "id": 29, + "rewardText": "", + "checkFunction": "totalHeroLevels", + "rewardParams": "", + "name": "Guide", + "checkParams": "10", + "description": "Level up heroes %1 times.", + "rewardFunction": "" + }, + "30": { + "iconId": 30, + "id": 30, + "rewardText": "", + "checkFunction": "totalHeroLevels", + "rewardParams": "", + "name": "Coach", + "checkParams": "100", + "description": "Level up heroes %1 times.", + "rewardFunction": "" + }, + "31": { + "iconId": 31, + "id": 31, + "rewardText": "", + "checkFunction": "totalHeroLevels", + "rewardParams": "", + "name": "Teacher", + "checkParams": "1000", + "description": "Level up heroes %1 times.", + "rewardFunction": "" + }, + "32": { + "iconId": 32, + "id": 32, + "rewardText": "", + "checkFunction": "totalHeroLevels", + "rewardParams": "", + "name": "Mentor", + "checkParams": "10000", + "description": "Level up heroes %1 times.", + "rewardFunction": "" + }, + "33": { + "iconId": 33, + "id": 33, + "rewardText": "", + "checkFunction": "maxDps", + "rewardParams": "", + "name": "Lethal", + "checkParams": "1000000", + "description": "Reach %1 DPS.", + "rewardFunction": "" + }, + "34": { + "iconId": 34, + "id": 34, + "rewardText": "", + "checkFunction": "maxDps", + "rewardParams": "", + "name": "Ruinous", + "checkParams": "1000000000", + "description": "Reach %1 DPS.", + "rewardFunction": "" + }, + "35": { + "iconId": 35, + "id": 35, + "rewardText": "", + "checkFunction": "maxDps", + "rewardParams": "", + "name": "Calamitous", + "checkParams": "1000000000000", + "description": "Reach %1 DPS.", + "rewardFunction": "" + }, + "36": { + "iconId": 36, + "id": 36, + "rewardText": "", + "checkFunction": "maxDps", + "rewardParams": "", + "name": "Cataclysmic", + "checkParams": "1000000000000000", + "description": "Reach %1 DPS.", + "rewardFunction": "" + }, + "37": { + "iconId": 37, + "id": 37, + "rewardText": "", + "checkFunction": "mostClicksPerSecond", + "rewardParams": "", + "name": "Frantic", + "checkParams": "5", + "description": "Click %1 times in one second.", + "rewardFunction": "" + }, + "38": { + "iconId": 38, + "id": 38, + "rewardText": "", + "checkFunction": "mostClicksPerSecond", + "rewardParams": "", + "name": "Frenetic", + "checkParams": "10", + "description": "Click %1 times in one second.", + "rewardFunction": "" + }, + "39": { + "iconId": 39, + "id": 39, + "rewardText": "", + "checkFunction": "mostClicksPerSecond", + "rewardParams": "", + "name": "Frenzied", + "checkParams": "15", + "description": "Click %1 times in one second.", + "rewardFunction": "" + }, + "40": { + "iconId": 40, + "id": 40, + "rewardText": "", + "checkFunction": "mostClicksPerSecond", + "rewardParams": "", + "name": "Convulsions", + "checkParams": "20", + "description": "Click %1 times in one second.", + "rewardFunction": "" + } + }, + "monsters": { + "1": { + "id": 1, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 1, + "baseSize": 1.5, + "name": "Mushroom Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "2": { + "id": 2, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 1, + "goldFormula": "monsterGoldFormula1", + "assetId": 2, + "baseSize": 1.5, + "name": "Mudball", + "description": "Monster", + "dieSoundId": 16, + "baseLife": 10 + }, + "3": { + "id": 3, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 4, + "goldFormula": "monsterGoldFormula1", + "assetId": 26, + "baseSize": 1.5, + "name": "Angry Potato", + "description": "Monster", + "dieSoundId": 2, + "baseLife": 10 + }, + "4": { + "id": 4, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 27, + "baseSize": 1.5, + "name": "Arachnagrass", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "5": { + "id": 5, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 28, + "baseSize": 1.3, + "name": "Mouseketeer", + "description": "Monster", + "dieSoundId": 15, + "baseLife": 10 + }, + "7": { + "id": 7, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 30, + "baseSize": 1.5, + "name": "Box Monster", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "9": { + "id": 9, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 4, + "goldFormula": "monsterGoldFormula1", + "assetId": 32, + "baseSize": 1.2, + "name": "Prime Slime", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "10": { + "id": 10, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 33, + "baseSize": 1.5, + "name": "Devilled Crab", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "11": { + "id": 11, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 3, + "goldFormula": "monsterGoldFormula1", + "assetId": 34, + "baseSize": 1.3, + "name": "d'Orcling", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "12": { + "id": 12, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 35, + "baseSize": 2, + "name": "Emperor Skorpinch", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "13": { + "id": 13, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 36, + "baseSize": 1.5, + "name": "Eye Slime", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "14": { + "id": 14, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 37, + "baseSize": 1.2, + "name": "Foomgus", + "description": "Monster", + "dieSoundId": 9, + "baseLife": 10 + }, + "15": { + "id": 15, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 38, + "baseSize": 1.5, + "name": "Golem", + "description": "Monster", + "dieSoundId": 31, + "baseLife": 10 + }, + "16": { + "id": 16, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 39, + "baseSize": 1.5, + "name": "Loggernaut", + "description": "Monster", + "dieSoundId": 13, + "baseLife": 10 + }, + "17": { + "id": 17, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 40, + "baseSize": 2, + "name": "Mama Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "18": { + "id": 18, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 42, + "baseSize": 2.5, + "name": "Octotentacle", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "19": { + "id": 19, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 3, + "goldFormula": "monsterGoldFormula1", + "assetId": 43, + "baseSize": 1.5, + "name": "Ominous d'Orc", + "description": "Monster", + "dieSoundId": 29, + "baseLife": 10 + }, + "20": { + "id": 20, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 44, + "baseSize": 1.2, + "name": "Trolgre", + "description": "Monster", + "dieSoundId": 29, + "baseLife": 10 + }, + "21": { + "id": 21, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 2, + "goldFormula": "monsterGoldFormula1", + "assetId": 45, + "baseSize": 1.5, + "name": "Sand Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "22": { + "id": 22, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 46, + "baseSize": 2, + "name": "Sasquish", + "description": "Monster", + "dieSoundId": 20, + "baseLife": 10 + }, + "23": { + "id": 23, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 47, + "baseSize": 1.5, + "name": "Big Feets", + "description": "Monster", + "dieSoundId": 20, + "baseLife": 10 + }, + "24": { + "id": 24, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 48, + "baseSize": 1.2, + "name": "Bluzebleeb", + "description": "Monster", + "dieSoundId": 3, + "baseLife": 10 + }, + "26": { + "id": 26, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 50, + "baseSize": 1.5, + "name": "Caperticus", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "27": { + "id": 27, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 51, + "baseSize": 1.5, + "name": "Flowering Caperticus", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "28": { + "id": 28, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 52, + "baseSize": 2, + "name": "Cassoweary", + "description": "Monster", + "dieSoundId": 7, + "baseLife": 10 + }, + "29": { + "id": 29, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 53, + "baseSize": 2, + "name": "Catra", + "description": "Monster", + "dieSoundId": 4, + "baseLife": 10 + }, + "30": { + "id": 30, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 54, + "baseSize": 1.5, + "name": "Dearth Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "31": { + "id": 31, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 55, + "baseSize": 2, + "name": "Dirge Beetle", + "description": "Monster", + "dieSoundId": 5, + "baseLife": 10 + }, + "32": { + "id": 32, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 56, + "baseSize": 1.5, + "name": "Fat Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "33": { + "id": 33, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 57, + "baseSize": 2, + "name": "Finky", + "description": "Monster", + "dieSoundId": 15, + "baseLife": 10 + }, + "34": { + "id": 34, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 58, + "baseSize": 2, + "name": "Flamingogo", + "description": "Monster", + "dieSoundId": 4, + "baseLife": 10 + }, + "35": { + "id": 35, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 59, + "baseSize": 1.5, + "name": "Floatsie", + "description": "Monster", + "dieSoundId": 8, + "baseLife": 10 + }, + "36": { + "id": 36, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 60, + "baseSize": 2, + "name": "Flower Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "37": { + "id": 37, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 61, + "baseSize": 2, + "name": "Gerbeel", + "description": "Monster", + "dieSoundId": 10, + "baseLife": 10 + }, + "38": { + "id": 38, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 62, + "baseSize": 2, + "name": "Gerbeelpillar", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "39": { + "id": 39, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 63, + "baseSize": 1.5, + "name": "Ghostly Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "40": { + "id": 40, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 64, + "baseSize": 2, + "name": "Ghostly Fat Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "41": { + "id": 41, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 65, + "baseSize": 1.5, + "name": "Gloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "42": { + "id": 42, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 66, + "baseSize": 1.5, + "name": "Goboolin", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "43": { + "id": 43, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 67, + "baseSize": 1.5, + "name": "Grablin", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "46": { + "id": 46, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 70, + "baseSize": 2, + "name": "Ice Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "47": { + "id": 47, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 71, + "baseSize": 2, + "name": "Ice Salamander", + "description": "Monster", + "dieSoundId": 35, + "baseLife": 10 + }, + "49": { + "id": 49, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 73, + "baseSize": 2, + "name": "Impossumble", + "description": "Monster", + "dieSoundId": 12, + "baseLife": 10 + }, + "50": { + "id": 50, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 74, + "baseSize": 1.5, + "name": "Mage Shroom", + "description": "Monster", + "dieSoundId": 14, + "baseLife": 10 + }, + "51": { + "id": 51, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 75, + "baseSize": 2, + "name": "Mamma Ferny Fat Bat", + "description": "Monster", + "dieSoundId": 6, + "baseLife": 10 + }, + "52": { + "id": 52, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 76, + "baseSize": 2, + "name": "Mousekewich", + "description": "Monster", + "dieSoundId": 15, + "baseLife": 10 + }, + "53": { + "id": 53, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 77, + "baseSize": 1.5, + "name": "Mud Golem", + "description": "Monster", + "dieSoundId": 17, + "baseLife": 10 + }, + "54": { + "id": 54, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 78, + "baseSize": 1.5, + "name": "Mushrimp", + "description": "Monster", + "dieSoundId": 18, + "baseLife": 10 + }, + "55": { + "id": 55, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 79, + "baseSize": 2, + "name": "Polaburrr", + "description": "Monster", + "dieSoundId": 29, + "baseLife": 10 + }, + "56": { + "id": 56, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 80, + "baseSize": 1.5, + "name": "Putrefaction Ooze", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "58": { + "id": 58, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 82, + "baseSize": 2, + "name": "Ramoicorn", + "description": "Monster", + "dieSoundId": 32, + "baseLife": 10 + }, + "59": { + "id": 59, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 83, + "baseSize": 2, + "name": "Ratty", + "description": "Monster", + "dieSoundId": 15, + "baseLife": 10 + }, + "60": { + "id": 60, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 84, + "baseSize": 1.2, + "name": "Sage Shroom", + "description": "Monster", + "dieSoundId": 19, + "baseLife": 10 + }, + "61": { + "id": 61, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 85, + "baseSize": 2, + "name": "Sand Ball", + "description": "Monster", + "dieSoundId": 16, + "baseLife": 10 + }, + "62": { + "id": 62, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 86, + "baseSize": 2, + "name": "Sasshay", + "description": "Monster", + "dieSoundId": 20, + "baseLife": 10 + }, + "63": { + "id": 63, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 87, + "baseSize": 2, + "name": "Scarecrow", + "description": "Monster", + "dieSoundId": 21, + "baseLife": 10 + }, + "64": { + "id": 64, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 88, + "baseSize": 2, + "name": "Scorpinch", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "65": { + "id": 65, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 89, + "baseSize": 2, + "name": "Sea Snail", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "66": { + "id": 66, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 90, + "baseSize": 2, + "name": "Sealed Elemental", + "description": "Monster", + "dieSoundId": 22, + "baseLife": 10 + }, + "67": { + "id": 67, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 91, + "baseSize": 2, + "name": "Snowball", + "description": "Monster", + "dieSoundId": 16, + "baseLife": 10 + }, + "68": { + "id": 68, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 92, + "baseSize": 2, + "name": "Snow Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "69": { + "id": 69, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 93, + "baseSize": 2, + "name": "Snow Ghost", + "description": "Monster", + "dieSoundId": 23, + "baseLife": 10 + }, + "70": { + "id": 70, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 94, + "baseSize": 2, + "name": "Snow Golem", + "description": "Monster", + "dieSoundId": 31, + "baseLife": 10 + }, + "71": { + "id": 71, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 95, + "baseSize": 2, + "name": "Snowdier", + "description": "Monster", + "dieSoundId": 25, + "baseLife": 10 + }, + "72": { + "id": 72, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 96, + "baseSize": 2, + "name": "Snowkin", + "description": "Monster", + "dieSoundId": 25, + "baseLife": 10 + }, + "73": { + "id": 73, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 97, + "baseSize": 2, + "name": "Snowlouse", + "description": "Monster", + "dieSoundId": 15, + "baseLife": 10 + }, + "74": { + "id": 74, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 98, + "baseSize": 2, + "name": "Snowmagus", + "description": "Monster", + "dieSoundId": 26, + "baseLife": 10 + }, + "75": { + "id": 75, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 99, + "baseSize": 2, + "name": "Spit Flower", + "description": "Monster", + "dieSoundId": 27, + "baseLife": 10 + }, + "76": { + "id": 76, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 100, + "baseSize": 2, + "name": "Stankape", + "description": "Monster", + "dieSoundId": 20, + "baseLife": 10 + }, + "77": { + "id": 77, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 101, + "baseSize": 2, + "name": "Stoney Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + }, + "79": { + "id": 79, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 103, + "baseSize": 2, + "name": "Turtle Turret", + "description": "Monster", + "dieSoundId": 28, + "baseLife": 10 + }, + "80": { + "id": 80, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 104, + "baseSize": 2, + "name": "Turtloid", + "description": "Monster", + "dieSoundId": 28, + "baseLife": 10 + }, + "81": { + "id": 81, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 105, + "baseSize": 2, + "name": "Turtloid Warlock", + "description": "Monster", + "dieSoundId": 28, + "baseLife": 10 + }, + "82": { + "id": 82, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 106, + "baseSize": 2, + "name": "Tyrantula", + "description": "Monster", + "dieSoundId": 34, + "baseLife": 10 + }, + "83": { + "id": 83, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 107, + "baseSize": 2, + "name": "Wilderburr", + "description": "Monster", + "dieSoundId": 29, + "baseLife": 10 + }, + "84": { + "id": 84, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 108, + "baseSize": 2, + "name": "Yeti", + "description": "Monster", + "dieSoundId": 29, + "baseLife": 10 + }, + "85": { + "id": 85, + "lifeFormula": "monsterLifeFormula1", + "baseGold": 50, + "goldFormula": "monsterGoldFormula1", + "assetId": 109, + "baseSize": 2, + "name": "Zombie Bloop", + "description": "Monster", + "dieSoundId": 33, + "baseLife": 10 + } + }, + "upgrades": { + "2": { + "iconId": 2, + "cost": 100, + "heroId": 1, + "attribute": "1", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 2, + "amount": 2, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Big Clicks", + "heroLevelRequired": 10, + "description": "Cid has the unique ability to strengthen your clicks. Upgrade him to make your clicks more powerful.", + "displayOrder": 2 + }, + "3": { + "iconId": 3, + "cost": 250, + "heroId": 1, + "attribute": "2", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 3, + "amount": 4, + "isPercentage": 0, + "upgradeRequired": 2, + "name": "Huge Clicks", + "heroLevelRequired": 25, + "description": "Cid's unique ability can grow in power, allowing you to click harder.", + "displayOrder": 3 + }, + "4": { + "iconId": 4, + "cost": 500, + "heroId": 1, + "attribute": "3", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 4, + "amount": 8, + "isPercentage": 0, + "upgradeRequired": 3, + "name": "Massive Clicks", + "heroLevelRequired": 50, + "description": "\"We make a great team,\" Cid says. \"Let's get this upgrade and we'll be an even better team.\"", + "displayOrder": 4 + }, + "5": { + "iconId": 5, + "cost": 750, + "heroId": 1, + "attribute": "4", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 5, + "amount": 16, + "isPercentage": 0, + "upgradeRequired": 4, + "name": "Titanic Clicks", + "heroLevelRequired": 75, + "description": "Cid looks off into the distance and wonders, \"What could be bigger than Titanic?\"", + "displayOrder": 5 + }, + "6": { + "iconId": 6, + "cost": 1000, + "heroId": 1, + "attribute": "5", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 6, + "amount": 32, + "isPercentage": 0, + "upgradeRequired": 5, + "name": "Colossal Clicks", + "heroLevelRequired": 100, + "description": "\"Colossal clicks are bigger than Titanic clicks. We should get this one. I don't think there could be anything bigger,\" Cid says.", + "displayOrder": 6 + }, + "7": { + "iconId": 7, + "cost": 1250, + "heroId": 1, + "attribute": "6", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 7, + "amount": 64, + "isPercentage": 0, + "upgradeRequired": 6, + "name": "Gargantuan Clicks", + "heroLevelRequired": 125, + "description": "\"I was surprised too, but it turns out we can upgrade these even further,\" Cid says. \"I suspect you've gotten tired of clicks, though. Maybe you don't need this one?\"", + "displayOrder": 7 + }, + "8": { + "iconId": 8, + "cost": 1500, + "heroId": 1, + "attribute": "7", + "upgradeFunction": "upgradeClickPercent", + "upgradeParams": "50", + "id": 8, + "amount": 128, + "isPercentage": 0, + "upgradeRequired": 7, + "name": "Monumental Clicks", + "heroLevelRequired": 150, + "description": "\"The final upgrade. These clicks are so big that every time you click, they build a monument to commemorate it.\"", + "displayOrder": 8 + }, + "9": { + "iconId": 9, + "cost": 40000, + "heroId": 5, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "5,100", + "id": 9, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Spear Training", + "heroLevelRequired": 10, + "description": "\"I've only ever used this spear for fishing. I need to learn how to use it better in combat.\"", + "displayOrder": 9 + }, + "10": { + "iconId": 10, + "cost": 100000, + "heroId": 5, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "5,100", + "id": 10, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 9, + "name": "Crab Net", + "heroLevelRequired": 25, + "description": "\"I'll have a way to get food with very little effort.\" Your team will be grateful for this..", + "displayOrder": 10 + }, + "11": { + "iconId": 11, + "cost": 200000, + "heroId": 5, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "5,100", + "id": 11, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 10, + "name": "Whetstone", + "heroLevelRequired": 50, + "description": "\"I used to have one of these. No idea what happened to it.\" If he can sharpen his spear, he will do more damage.", + "displayOrder": 11 + }, + "12": { + "iconId": 12, + "cost": 300000, + "heroId": 5, + "attribute": "4", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "25", + "id": 12, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Fish Cooking", + "heroLevelRequired": 75, + "description": "\"Betty's been teaching me. We'll be able to cook fish for the team!\"", + "displayOrder": 12 + }, + "13": { + "iconId": 13, + "cost": 10000, + "heroId": 4, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "4,100", + "id": 13, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Combat Makeup", + "heroLevelRequired": 10, + "description": "You doubt she'll use it for camouflage like she's supposed to. In fact, she probably doesn't even know what \"camouflage\" means.", + "displayOrder": 14 + }, + "14": { + "iconId": 14, + "cost": 25000, + "heroId": 4, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "4,100", + "id": 14, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 13, + "name": "Brand Name Equipment", + "heroLevelRequired": 25, + "description": "\"I'm not like other girls. I would never, like, take you for granite,\" she says.", + "displayOrder": 15 + }, + "15": { + "iconId": 15, + "cost": 50000, + "heroId": 4, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "4,100", + "id": 15, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 14, + "name": "Elixir of Deditzification", + "heroLevelRequired": 50, + "description": "The effect of this elixir may be too mild to notice at first glance, but Brittany's combat prowess will improve significantly.", + "displayOrder": 16 + }, + "16": { + "iconId": 16, + "cost": 75000, + "heroId": 4, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "4,150", + "id": 16, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 15, + "name": "Vegan Meat", + "heroLevelRequired": 75, + "description": "With the help of the Great Forest Seer, he performs a \"blessing\" on your cooked meats to make them vegetarian, so Brittany can eat them.", + "displayOrder": 17 + }, + "17": { + "iconId": 17, + "cost": 500, + "heroId": 2, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "2,100", + "id": 17, + "amount": 1, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Fertilizer ", + "heroLevelRequired": 10, + "description": "Treebeast grunts with approval as you consider this upgrade. It will make him grow stronger.", + "displayOrder": 18 + }, + "18": { + "iconId": 18, + "cost": 1250, + "heroId": 2, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "2,100", + "id": 18, + "amount": 2, + "isPercentage": 0, + "upgradeRequired": 17, + "name": "Thorns", + "heroLevelRequired": 25, + "description": "Sharp points on Treebeast's weapon that help cut up enemies.", + "displayOrder": 19 + }, + "19": { + "iconId": 19, + "cost": 2500, + "heroId": 2, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "2,100", + "id": 19, + "amount": 4, + "isPercentage": 0, + "upgradeRequired": 18, + "name": "Megastick", + "heroLevelRequired": 50, + "description": "A really big stick for Treebeast.", + "displayOrder": 20 + }, + "20": { + "iconId": 20, + "cost": 3750, + "heroId": 2, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "2,150", + "id": 20, + "amount": 8, + "isPercentage": 0, + "upgradeRequired": 19, + "name": "Ultrastick", + "heroLevelRequired": 75, + "description": "The biggest stick in the forest.", + "displayOrder": 21 + }, + "21": { + "iconId": 21, + "cost": 2500, + "heroId": 3, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "3,100", + "id": 21, + "amount": 16, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Hard Cider", + "heroLevelRequired": 10, + "description": "An appetizer for a long night of drinking. ", + "displayOrder": 23 + }, + "22": { + "iconId": 22, + "cost": 6250, + "heroId": 3, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "3,100", + "id": 22, + "amount": 32, + "isPercentage": 0, + "upgradeRequired": 21, + "name": "Pint of Ale", + "heroLevelRequired": 25, + "description": "Ivan likes a pint, to take the edge off his edge.", + "displayOrder": 24 + }, + "23": { + "iconId": 23, + "cost": 12500, + "heroId": 3, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "3,100", + "id": 23, + "amount": 64, + "isPercentage": 0, + "upgradeRequired": 22, + "name": "Pitcher", + "heroLevelRequired": 50, + "description": "Ivan throws his inhibitions to the wind and his fists into the faces.", + "displayOrder": 25 + }, + "24": { + "iconId": 24, + "cost": 18750, + "heroId": 3, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "3,150", + "id": 24, + "amount": 128, + "isPercentage": 0, + "upgradeRequired": 23, + "name": "Pint of Pig's Whiskey", + "heroLevelRequired": 75, + "description": "It's so strong that it would put chest hairs on your great Aunt Sally, if she didn't already have them.", + "displayOrder": 26 + }, + "25": { + "iconId": 25, + "cost": 200000, + "heroId": 6, + "attribute": "1", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 25, + "amount": 256, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Wilderburr Dumplings", + "heroLevelRequired": 10, + "description": "Delicate and oh so delicious, these delightful morsels make you very vigorous.", + "displayOrder": 28 + }, + "26": { + "iconId": 26, + "cost": 500000, + "heroId": 6, + "attribute": "2", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 26, + "amount": 512, + "isPercentage": 0, + "upgradeRequired": 25, + "name": "Braised Flamingogo Casserole", + "heroLevelRequired": 25, + "description": "\"You know what they say. An army runs on good homemade Casseroles.\"", + "displayOrder": 29 + }, + "27": { + "iconId": 27, + "cost": 1000000, + "heroId": 6, + "attribute": "3", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 27, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 26, + "name": "Truffled Trollgre with Bloop Reduction", + "heroLevelRequired": 50, + "description": "\"A new dish I came up with. It's exquisite.\"", + "displayOrder": 30 + }, + "28": { + "iconId": 28, + "cost": 1500000, + "heroId": 6, + "attribute": "4", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 28, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 27, + "name": "Foomgus Risotto", + "heroLevelRequired": 75, + "description": "\"One bite and you won't have to eat for days. It has something to do with the magical qualities of the Foomgus.\"", + "displayOrder": 31 + }, + "29": { + "iconId": 29, + "cost": 1000000, + "heroId": 7, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "7,100", + "id": 29, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Jutsu I", + "heroLevelRequired": 10, + "description": "The Masked Samurai says he knows the greatest swordfighting teacher in the world. This training will greatly benefit his abilities.", + "displayOrder": 33 + }, + "30": { + "iconId": 30, + "cost": 2500000, + "heroId": 7, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "7,100", + "id": 30, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 29, + "name": "Jutsu II", + "heroLevelRequired": 25, + "description": "You've never seen him train with this \"Great Teacher\" he always refers to. But his combat skills have improved, so what do you have to complain about?", + "displayOrder": 34 + }, + "31": { + "iconId": 31, + "cost": 5000000, + "heroId": 7, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "7,100", + "id": 31, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 30, + "name": "Jutsu III", + "heroLevelRequired": 50, + "description": "What if he's lying? What if he's just holding back his DPS for money, and doing a bit extra each time you pay for his \"training\"?", + "displayOrder": 35 + }, + "32": { + "iconId": 32, + "cost": 7500000, + "heroId": 7, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "7,150", + "id": 32, + "amount": 35, + "isPercentage": 0, + "upgradeRequired": 31, + "name": "Jutsu IV", + "heroLevelRequired": 75, + "description": "He's probably making everything up. Roman numerals don't even belong with Japanese words! Ah well, what can you do? The added damage would be useful.", + "displayOrder": 36 + }, + "33": { + "iconId": 33, + "cost": 4000000, + "heroId": 8, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "8,100", + "id": 33, + "amount": 70, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Courage Tonic", + "heroLevelRequired": 10, + "description": "He's pretty strong, but fearful of things. This tonic will help him overcome his fears. It smells like paint thinner.", + "displayOrder": 37 + }, + "34": { + "iconId": 34, + "cost": 10000000, + "heroId": 8, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "8,100", + "id": 34, + "amount": 140, + "isPercentage": 0, + "upgradeRequired": 33, + "name": "Stronger Claws", + "heroLevelRequired": 25, + "description": "A more powerful replacement for Leon's current claws.", + "displayOrder": 38 + }, + "35": { + "iconId": 35, + "cost": 20000000, + "heroId": 8, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "8,100", + "id": 35, + "amount": 280, + "isPercentage": 0, + "upgradeRequired": 34, + "name": "Lionheart Potion", + "heroLevelRequired": 50, + "description": "A magic concoction that allows Leon to truly believe in himself.", + "displayOrder": 39 + }, + "36": { + "iconId": 36, + "cost": 30000000, + "heroId": 8, + "attribute": "4", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "25", + "id": 36, + "amount": 560, + "isPercentage": 0, + "upgradeRequired": 35, + "name": "Lion's Roar", + "heroLevelRequired": 75, + "description": "With his tonics and potions, Leon is finally capable of roaring like a real lion. This provides a boost to your entire team.", + "displayOrder": 40 + }, + "37": { + "iconId": 37, + "cost": 25000000, + "heroId": 9, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "9,100", + "id": 37, + "amount": 1120, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Forest Creatures", + "heroLevelRequired": 10, + "description": "The Seer can call upon the forest creatures to do his bidding. Enraged rabbits, squirrels, and birds will launch themselves at your opponents.", + "displayOrder": 41 + }, + "38": { + "iconId": 38, + "cost": 62500000, + "heroId": 9, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "9,100", + "id": 38, + "amount": 2240, + "isPercentage": 0, + "upgradeRequired": 37, + "name": "Insight", + "heroLevelRequired": 25, + "description": "\"Leon is not a real lion,\" he says. \"And Brittany is vain.\" These, and other important insights that only the Seer can know, will help your entire team.", + "displayOrder": 42 + }, + "39": { + "iconId": 39, + "cost": 125000000, + "heroId": 9, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "9,100", + "id": 39, + "amount": 4480, + "isPercentage": 0, + "upgradeRequired": 38, + "name": "Dark Lore", + "heroLevelRequired": 50, + "description": "\"I must not shy away from the world of darkness,\" he tells you. This upgrade will grant him greater power, and the capability for terrifying upgrades in the future.", + "displayOrder": 43 + }, + "40": { + "iconId": 40, + "cost": 187000000, + "heroId": 9, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "9,150", + "id": 40, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 39, + "name": "Swarm", + "heroLevelRequired": 75, + "description": "The Seer can control the arthropods with this grotesque power. Spiders, millipedes, and other bugs weave in and out of his clothing, and will swarm your enemies at his command.", + "displayOrder": 44 + }, + "41": { + "iconId": 41, + "cost": 150000000, + "heroId": 10, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "10,100", + "id": 41, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Critical Strike", + "heroLevelRequired": 10, + "description": "An improvement to Alexa's knowledge of monster anatomy. She hits to kill.", + "displayOrder": 45 + }, + "42": { + "iconId": 42, + "cost": 375000000, + "heroId": 10, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "10,100", + "id": 42, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 41, + "name": "Clairvoyance", + "heroLevelRequired": 25, + "description": "An improvement to her ability to see surroundings without her eyes, increasing her combat prowess.", + "displayOrder": 46 + }, + "43": { + "iconId": 43, + "cost": 750000000, + "heroId": 10, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "10,100", + "id": 43, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 42, + "name": "Poisoned Blades", + "heroLevelRequired": 50, + "description": "\"Don't use these as steak knives,\" she warns.", + "displayOrder": 47 + }, + "44": { + "iconId": 44, + "cost": 1125000000, + "heroId": 10, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "10,150", + "id": 44, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 43, + "name": "Invisible Strikes", + "heroLevelRequired": 75, + "description": "Imbued with great speed, Alexa's attacks having become blindingly fast as to appear invisible.", + "displayOrder": 48 + }, + "45": { + "iconId": 45, + "cost": 1000000000, + "heroId": 11, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "11,100", + "id": 45, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Magic 101", + "heroLevelRequired": 10, + "description": "This book is standard reading at any Mage university. It would be cruel to keep her from studying it.", + "displayOrder": 49 + }, + "46": { + "iconId": 46, + "cost": 2500000000, + "heroId": 11, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "11,100", + "id": 46, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 45, + "name": "Below Zero", + "heroLevelRequired": 25, + "description": "Canonical undergraduate Ice reading. She needs it to become a successful Ice Mage.", + "displayOrder": 50 + }, + "47": { + "iconId": 47, + "cost": 5000000000, + "heroId": 11, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "11,100", + "id": 47, + "amount": 200, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Frozen Warfare", + "heroLevelRequired": 50, + "description": "A graduate-level book on using Ice Magic for combat purposes. Personally recommended by Jerator.", + "displayOrder": 51 + }, + "48": { + "iconId": 48, + "cost": 7500000000, + "heroId": 11, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "11,150", + "id": 48, + "amount": 400, + "isPercentage": 0, + "upgradeRequired": 47, + "name": "The Book of Frost", + "heroLevelRequired": 75, + "description": "A frigid mist billows out of this rare book, owned only by the greatest Ice Wizards. Natalia's eyes are wide with desire.", + "displayOrder": 52 + }, + "49": { + "iconId": 49, + "cost": 8000000000, + "heroId": 12, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "12,100", + "id": 49, + "amount": 800, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Mithril Edge", + "heroLevelRequired": 10, + "description": "By reinforcing the edge of her blade with Mithril, she can cut through much more difficult materials.", + "displayOrder": 53 + }, + "50": { + "iconId": 50, + "cost": 20000000000, + "heroId": 12, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "12,100", + "id": 50, + "amount": 1600, + "isPercentage": 0, + "upgradeRequired": 49, + "name": "Enchanted Blade", + "heroLevelRequired": 25, + "description": "With the research and help of some of your more magic-inclined friends, Mercedes' blade can be empowered with magic.", + "displayOrder": 54 + }, + "51": { + "iconId": 51, + "cost": 40000000000, + "heroId": 12, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "12,100", + "id": 51, + "amount": 3200, + "isPercentage": 0, + "upgradeRequired": 50, + "name": "Quickblade", + "heroLevelRequired": 50, + "description": "Another upgrade to her sword, giving her a much faster swing. \"Nobody's head will be safe from me.\"", + "displayOrder": 55 + }, + "52": { + "iconId": 52, + "cost": 60000000000, + "heroId": 12, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "12,150", + "id": 52, + "amount": 6400, + "isPercentage": 0, + "upgradeRequired": 51, + "name": "Blessed Sword", + "heroLevelRequired": 75, + "description": "\"My sword, she's already historic. If she receives a blessing from the gods, I'll have to give her a name...\"", + "displayOrder": 56 + }, + "53": { + "iconId": 53, + "cost": 65000000000, + "heroId": 13, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "13,100", + "id": 53, + "amount": 12800, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Impressive Moves", + "heroLevelRequired": 10, + "description": "You get the impression that these moves are not really optimized for battle damage, but for looks.", + "displayOrder": 58 + }, + "54": { + "iconId": 54, + "cost": 162000000000, + "heroId": 13, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "13,100", + "id": 54, + "amount": 25600, + "isPercentage": 0, + "upgradeRequired": 53, + "name": "Acrobatic Jetpack", + "heroLevelRequired": 25, + "description": "\"I need this upgrade so I can move in and out of combat more efficiently,\" Bobby says. But through your experienced eyes, you know he's up to something else.", + "displayOrder": 59 + }, + "55": { + "iconId": 55, + "cost": 325000000000, + "heroId": 13, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "13,100", + "id": 55, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 54, + "name": "Jetpack Dance", + "heroLevelRequired": 50, + "description": "\"It's like she doesn't even know I exist,\" you hear him mutter to himself. This upgrade allows Bobby to perform sideflips through the air with minor combat bonus.", + "displayOrder": 60 + }, + "56": { + "iconId": 56, + "cost": 487000000000, + "heroId": 13, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "13,150", + "id": 56, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 55, + "name": "Whirling Skyblade", + "heroLevelRequired": 75, + "description": "\"It's Natalia,\" he admits. \"She only cares about her books.\" How can you not feel pity for the poor guy? Buy him this upgrade. At least he'll do more DPS.", + "displayOrder": 61 + }, + "57": { + "iconId": 57, + "cost": 500000000000, + "heroId": 14, + "attribute": "1", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "25", + "id": 57, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Roast Monsters", + "heroLevelRequired": 10, + "description": "Betty Clicker and Lindeoven pair up to produce delicious food for the team.", + "displayOrder": 62 + }, + "58": { + "iconId": 58, + "cost": 1250000000000, + "heroId": 14, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "14,100", + "id": 58, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 57, + "name": "Combustible Air", + "heroLevelRequired": 25, + "description": "This magic seems extremely dangerous. Once the spell is cast, the air can burst into flames with a single spark.", + "displayOrder": 63 + }, + "59": { + "iconId": 59, + "cost": 2500000000000, + "heroId": 14, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "14,100", + "id": 59, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 58, + "name": "Inner Fire", + "heroLevelRequired": 50, + "description": "\"With this magic, I can concentrate large amounts of heat on very specific, small locations in space. I will effectively set fire to the insides of our enemies.\"", + "displayOrder": 64 + }, + "60": { + "iconId": 60, + "cost": 3750000000000, + "heroId": 14, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "14,150", + "id": 60, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 59, + "name": "The Floor is Lava", + "heroLevelRequired": 75, + "description": "\"I used to play this game as a child. I can turn it into a reality for our enemies with this spell,\" he says.", + "displayOrder": 65 + }, + "61": { + "iconId": 61, + "cost": 5000000000000, + "heroId": 15, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "15,100", + "id": 61, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Abandoned Regret", + "heroLevelRequired": 10, + "description": "Though he swore to uphold his vows to protect his King, the guard must move on with his life. \"It will take some effort,\" he says.", + "displayOrder": 66 + }, + "62": { + "iconId": 62, + "cost": 12500000000000, + "heroId": 15, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "15,100", + "id": 62, + "amount": 1000, + "isPercentage": 0, + "upgradeRequired": 61, + "name": "Offensive Strategies", + "heroLevelRequired": 25, + "description": "\"I no longer protect my King. He has lost his way, and I am in your service now.\" He is willing to learn new fighting strategies.", + "displayOrder": 67 + }, + "63": { + "iconId": 63, + "cost": 25000000000000, + "heroId": 15, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "15,100", + "id": 63, + "amount": 2000, + "isPercentage": 0, + "upgradeRequired": 62, + "name": "Combat Strategy", + "heroLevelRequired": 50, + "description": "As of late, the mood of Sir George II has improved significantly. This upgrade provides a massive improvement to George's DPS.", + "displayOrder": 68 + }, + "64": { + "iconId": 64, + "cost": 37500000000000, + "heroId": 15, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "15,150", + "id": 64, + "amount": 4000, + "isPercentage": 0, + "upgradeRequired": 63, + "name": "Burning Blade", + "heroLevelRequired": 75, + "description": "Lindeoven can enchant Sir George's weapon with fire.", + "displayOrder": 69 + }, + "65": { + "iconId": 65, + "cost": 60000000000000, + "heroId": 16, + "attribute": "1", + "upgradeFunction": "upgradeGoldFoundPercent", + "upgradeParams": "25", + "id": 65, + "amount": 8000, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Bag of Holding", + "heroLevelRequired": 10, + "description": "This space-bending bag can store things much bigger than itself. Get Midas one of these so he doesn't have to haul his fortune around with him in the midst of battle.", + "displayOrder": 71 + }, + "66": { + "iconId": 66, + "cost": 150000000000000, + "heroId": 16, + "attribute": "2", + "upgradeFunction": "upgradeGoldFoundPercent", + "upgradeParams": "25", + "id": 66, + "amount": 16000, + "isPercentage": 0, + "upgradeRequired": 65, + "name": "Heart of Gold ", + "heroLevelRequired": 25, + "description": "Midas can find more gold from your enemies. Where it comes from, you're not really sure. \"These are riches beyond belief!\" he says. \"How can I thank Sir George?\"", + "displayOrder": 72 + }, + "67": { + "iconId": 67, + "cost": 300000000000000, + "heroId": 16, + "attribute": "3", + "upgradeFunction": "upgradeGoldFoundPercent", + "upgradeParams": "25", + "id": 67, + "amount": 32000, + "isPercentage": 0, + "upgradeRequired": 66, + "name": "Touch of Gold", + "heroLevelRequired": 50, + "description": "This upgrade gives Midas the power to turn things (and enemies!) into gold by touching them. He promises to share the wealth with you.", + "displayOrder": 73 + }, + "68": { + "iconId": 68, + "cost": 450000000000000, + "heroId": 16, + "attribute": "4", + "upgradeFunction": "upgradeGoldFoundPercent", + "upgradeParams": "50", + "id": 68, + "amount": 64000, + "isPercentage": 0, + "upgradeRequired": 67, + "name": "Golden Dimension", + "heroLevelRequired": 75, + "description": "\"I can open rifts to the Planes of Gold,\" he says, with madness in his eyes. The rift can only open after killing a monster, and heavy golden furniture falls through.", + "displayOrder": 74 + }, + "69": { + "iconId": 69, + "cost": 800000000000000, + "heroId": 17, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "17,100", + "id": 69, + "amount": 128000, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Defrosting", + "heroLevelRequired": 10, + "description": "This spell will allow Jerator to cast his spells more effectively because he can see clearly.", + "displayOrder": 75 + }, + "70": { + "iconId": 70, + "cost": 2000000000000000, + "heroId": 17, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "17,100", + "id": 70, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 69, + "name": "Headbashing", + "heroLevelRequired": 25, + "description": "While his magic is paramount, this upgrade will allow Jerator to smash his enemies in head-to-head combat.", + "displayOrder": 76 + }, + "71": { + "iconId": 71, + "cost": 4000000000000000, + "heroId": 17, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "17,100", + "id": 71, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 70, + "name": "Iceberg Rain", + "heroLevelRequired": 50, + "description": "Icebergs rain down from the sky, smashing your enemies.", + "displayOrder": 77 + }, + "72": { + "iconId": 72, + "cost": 6000000000000000, + "heroId": 17, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "17,150", + "id": 72, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 71, + "name": "Glacierstorm", + "heroLevelRequired": 75, + "description": "A hurricane of massive glaciers forms in the sky. They fall on your enemies.", + "displayOrder": 78 + }, + "73": { + "iconId": 73, + "cost": 1000000000000000, + "heroId": 18, + "attribute": "1", + "upgradeFunction": "upgradeAbaddon", + "upgradeParams": "0.25", + "id": 73, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Rise of the Dead", + "heroLevelRequired": 10, + "description": "Abaddon can raise the corpses of the fallen, to fight by your side.", + "displayOrder": 79 + }, + "74": { + "iconId": 74, + "cost": 2500000000000000, + "heroId": 18, + "attribute": "2", + "upgradeFunction": "upgradeAbaddon", + "upgradeParams": "0.5", + "id": 74, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 73, + "name": "Curse of the Dark God", + "heroLevelRequired": 25, + "description": "\"To call this a 'Curse' is a misnomer, mortal. This power is a blessing.\"", + "displayOrder": 80 + }, + "75": { + "iconId": 75, + "cost": 5000000000000000, + "heroId": 18, + "attribute": "3", + "upgradeFunction": "upgradeAbaddon", + "upgradeParams": "0.75", + "id": 75, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 74, + "name": "Epidemic Evil", + "heroLevelRequired": 50, + "description": "\"The powers of darkness breed more power,\" he says. Abaddon is kind of scary.", + "displayOrder": 81 + }, + "76": { + "iconId": 76, + "cost": 7500000000000000, + "heroId": 18, + "attribute": "4", + "upgradeFunction": "upgradeAbaddon", + "upgradeParams": "1.0", + "id": 76, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 75, + "name": "The Dark Ritual", + "heroLevelRequired": 75, + "description": "The ground beneath Abaddon glows with runes. He beckons for you to help him with his final ritual.", + "displayOrder": 82 + }, + "77": { + "iconId": 77, + "cost": 12000000000000000, + "heroId": 19, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "19,100", + "id": 77, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Heaven's Hand", + "heroLevelRequired": 10, + "description": "The eastern gods bless Ma Zhu with a portion of their holy might increasing Ma Zhu's damage. ", + "displayOrder": 83 + }, + "78": { + "iconId": 78, + "cost": 30000000000000000, + "heroId": 19, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "19,100", + "id": 78, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 77, + "name": "Plasma Arc", + "heroLevelRequired": 25, + "description": "Ma Zhu sparks and crackles with electricity. The heat from a focused arc can inflict incredible damage.", + "displayOrder": 84 + }, + "79": { + "iconId": 79, + "cost": 60000000000000000, + "heroId": 19, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "19,100", + "id": 79, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 75, + "name": "Ancient Wrath", + "heroLevelRequired": 50, + "description": "\"I fear you may not be balancing the powers of the Gods,\" Ma Zhu tells you, hinting at this upgrade. \"Abaddon is a dangerous one.\"", + "displayOrder": 85 + }, + "80": { + "iconId": 80, + "cost": 90000000000000000, + "heroId": 19, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "19,150", + "id": 80, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 79, + "name": "Pet Dragon", + "heroLevelRequired": 75, + "description": "\"I can go get my dragon, if you like. His name is Bashe, and he eats elephants,\" Ma Zhu says.", + "displayOrder": 86 + }, + "81": { + "iconId": 81, + "cost": 150000000000000000, + "heroId": 20, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "20,100", + "id": 81, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 0, + "name": "Smite ", + "heroLevelRequired": 10, + "description": "\"I am here to help you, mortal. Grant me this power and you will be rewarded with the loot of many corpses.\"", + "displayOrder": 87 + }, + "82": { + "iconId": 82, + "cost": 375000000000000000, + "heroId": 20, + "attribute": "2", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 82, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 76, + "name": "Genesis Research", + "heroLevelRequired": 25, + "description": "\"Foolish mortal! You have let Abaddon grow too powerful,\" he scolds you. \"We must undo what has been done! It will take time. Do not let Abaddon know.\"", + "displayOrder": 88 + }, + "83": { + "iconId": 83, + "cost": 750000000000000000, + "heroId": 20, + "attribute": "3", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "20", + "id": 83, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 82, + "name": "Prepare the Rebeginning", + "heroLevelRequired": 50, + "description": "With a terrifying rumble, the ground and the skies ripple. Amenhotep glows brilliantly with power as he prepares the final act.", + "displayOrder": 89 + }, + "84": { + "iconId": 42, + "cost": 3000000000000000000, + "heroId": 21, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "21,100", + "id": 84, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Eye in the Sky", + "heroLevelRequired": 10, + "description": "The birds in the sky can spot when our enemies are their most vulnerable. ", + "displayOrder": 90 + }, + "85": { + "iconId": 37, + "cost": 7500000000000000000, + "heroId": 21, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "21,100", + "id": 85, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 84, + "name": "Critters", + "heroLevelRequired": 25, + "description": "They are small, but in they make up for it in numbers and ferocity.", + "displayOrder": 91 + }, + "86": { + "iconId": 34, + "cost": 15000000000000000000, + "heroId": 21, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "21,100", + "id": 86, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 85, + "name": "Beastmode", + "heroLevelRequired": 50, + "description": "The Beastmaster murmurs an incantation. Fur sprouts from his skin, quills shoot from his back. His bares his claws and teeth, which bear resemblance to a bear. ", + "displayOrder": 92 + }, + "87": { + "iconId": 24, + "cost": 22500000000000000000, + "heroId": 21, + "attribute": "4", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "10", + "id": 87, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 86, + "name": "Sacrficial Lamb's Blood", + "heroLevelRequired": 100, + "description": "\"Bathe our heroes with the blood of the sacrificed to increase their strength and stamina.\"", + "displayOrder": 93 + }, + "88": { + "iconId": 81, + "cost": 90000000000000000000, + "heroId": 22, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "22,100", + "id": 88, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Hand-to-Head Combat", + "heroLevelRequired": 10, + "description": "\"Fresh skulls for the taking, one of my favorite pastimes.\"", + "displayOrder": 94 + }, + "89": { + "iconId": 84, + "cost": 225000000000000000000, + "heroId": 22, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "22,100", + "id": 89, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 88, + "name": "War Scream", + "heroLevelRequired": 25, + "description": "Athena takes in a deep breath and lets out a hellish scream that reverberates miles across land and sea. Monsters tremble in its wake. ", + "displayOrder": 95 + }, + "90": { + "iconId": 39, + "cost": 450000000000000000000, + "heroId": 22, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "22,100", + "id": 90, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 89, + "name": "Bloodlust", + "heroLevelRequired": 50, + "description": "Taste for blood thickens as enemies fall to Athena's feet. Her hunger drives her DPS through the roof.", + "displayOrder": 96 + }, + "91": { + "iconId": 60, + "cost": 675000000000000000000, + "heroId": 22, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "22,100", + "id": 91, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 90, + "name": "Boiling Blood", + "heroLevelRequired": 100, + "description": "Angering the Goddess of War will totally ruin someone's day. ", + "displayOrder": 97 + }, + "92": { + "iconId": 36, + "cost": 3.5e+21, + "heroId": 23, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "23,100", + "id": 92, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Lasso of Love", + "heroLevelRequired": 10, + "description": "\"Anyone trapped in my lasso falls head over heels in love with me.\"", + "displayOrder": 98 + }, + "93": { + "iconId": 15, + "cost": 8.75e+21, + "heroId": 23, + "attribute": "2", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "23,100", + "id": 93, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 92, + "name": "Love Potion", + "heroLevelRequired": 25, + "description": "One little taste and enemies practically kill each other for Aphrodite's affection.", + "displayOrder": 99 + }, + "94": { + "iconId": 14, + "cost": 1.75e+22, + "heroId": 23, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "23,100", + "id": 94, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 93, + "name": "Love Hurts", + "heroLevelRequired": 50, + "description": "\"All is destroyed in love and war,\" says Aphrodite.", + "displayOrder": 100 + }, + "95": { + "iconId": 13, + "cost": 2.625e+22, + "heroId": 23, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "23,100", + "id": 95, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 94, + "name": "Kiss of Death", + "heroLevelRequired": 100, + "description": "\"Pucker up sweetheart, today's your unlucky day.\"", + "displayOrder": 101 + }, + "96": { + "iconId": 52, + "cost": 1.4e+23, + "heroId": 24, + "attribute": "1", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "24,100", + "id": 96, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 0, + "name": "Dancing Blades", + "heroLevelRequired": 10, + "description": "Swords dance in the wind, dicing and chopping enemies into bits and pieces.", + "displayOrder": 102 + }, + "97": { + "iconId": 56, + "cost": 3.5e+23, + "heroId": 24, + "attribute": "2", + "upgradeFunction": "upgradeEveryonePercent", + "upgradeParams": "10", + "id": 97, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 96, + "name": "Annoying Winds", + "heroLevelRequired": 25, + "description": "Strong gusts of wind break down enemy morale.", + "displayOrder": 103 + }, + "98": { + "iconId": 68, + "cost": 7e+23, + "heroId": 24, + "attribute": "3", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "24,100", + "id": 98, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 97, + "name": "Bladestorm", + "heroLevelRequired": 50, + "description": "\"Amass any and all weapons and throw them into this tornado I've conjured,\" says Shinatobe. Pieces of sharp metal twirl violently at the edges of the tornado.", + "displayOrder": 104 + }, + "99": { + "iconId": 84, + "cost": 1.05e+24, + "heroId": 24, + "attribute": "4", + "upgradeFunction": "upgradeHeroPercent", + "upgradeParams": "24,100", + "id": 99, + "amount": 100, + "isPercentage": 1, + "upgradeRequired": 98, + "name": "Eye of the Storm", + "heroLevelRequired": 100, + "description": "A storm of epic proportions, perhaps the biggest storm in history, to constantly ravage your enemies.", + "displayOrder": 105 + }, + "100": { + "iconId": 35, + "cost": 25000, + "heroId": 3, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 100, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 24, + "name": "Embalming Fluid", + "heroLevelRequired": 100, + "description": "Having drunk the town dry, Ivan must now resort to embalming fluid to prevent the shakes.", + "displayOrder": 27 + }, + "101": { + "iconId": 55, + "cost": 40000, + "heroId": 5, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 101, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 12, + "name": "State of the Art Fishing Gear", + "heroLevelRequired": 100, + "description": "\"We should be able to buy all this spiffy new gear because we've been selling off all the extra fish we didn't need. Now we can haul in a bigger load!\"", + "displayOrder": 13 + }, + "102": { + "iconId": 45, + "cost": 0, + "heroId": 6, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 102, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 28, + "name": "World Famous Cookbook", + "heroLevelRequired": 100, + "description": "\"This is the most famous cookbook ever written. It contains recipes that make your clicks do huge damage.\"", + "displayOrder": 32 + }, + "103": { + "iconId": 31, + "cost": 0, + "heroId": 2, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 103, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 20, + "name": "Lacquer", + "heroLevelRequired": 100, + "description": "Apply to a big stick for durability and strength.", + "displayOrder": 22 + }, + "104": { + "iconId": 63, + "cost": 0, + "heroId": 12, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 104, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 52, + "name": "Art of Swordfighting", + "heroLevelRequired": 100, + "description": "This rare book was co-authored by three ancient sword masters. \"I know it's expensive, but I think it's well worth it.\"", + "displayOrder": 57 + }, + "105": { + "iconId": 30, + "cost": 0, + "heroId": 15, + "attribute": "5", + "upgradeFunction": "upgradeClickDpsPercent", + "upgradeParams": "0.5", + "id": 105, + "amount": 100, + "isPercentage": 0, + "upgradeRequired": 64, + "name": "King's Pardon", + "heroLevelRequired": 100, + "description": "\"Even though he lost his way, my King was still mine to protect and I felt much regret leaving his side. This pardon has lifted a great weight off my shoulders.\"", + "displayOrder": 70 + }, + "106": { + "iconId": 84, + "cost": 0, + "heroId": 20, + "attribute": "7", + "upgradeFunction": "finalUpgrade", + "upgradeParams": "0", + "id": 106, + "amount": 0, + "isPercentage": 0, + "upgradeRequired": 83, + "name": "The Last Upgrade", + "heroLevelRequired": 150, + "description": "Glowing bright magma chasms open up in the distance as Amenhotep prepares the incantation that will destroy this universe. \"Whenever you're ready,\" he says, with arms crossed, tapping his foot.", + "displayOrder": 106 + } + }, + "zones": { + "1": { + "minLevel": 1, + "id": 1, + "monsterIds": "1,3,16,22,29,33,36,37,38,41,49,75,82", + "tile": 1, + "name": "Forest", + "bossId": 51, + "background": 1 + }, + "2": { + "minLevel": 1, + "id": 2, + "monsterIds": "21,26,27,31,33,61,29,42,43", + "tile": 2, + "name": "Drylands", + "bossId": 12, + "background": 3 + }, + "3": { + "minLevel": 10, + "id": 3, + "monsterIds": "21,26,27,31,33,42,43,61,64,82", + "tile": 4, + "name": "Desert", + "bossId": 20, + "background": 4 + }, + "4": { + "minLevel": 25, + "id": 4, + "monsterIds": "2,5,10,33,34,52,59,65,80,81", + "tile": 7, + "name": "Beach", + "bossId": 18, + "background": 1 + }, + "5": { + "minLevel": 15, + "id": 5, + "monsterIds": "5,21,27,37,42,43,52,58,64", + "tile": 5, + "name": "Rocklands", + "bossId": 23, + "background": 5 + }, + "6": { + "minLevel": 5, + "id": 6, + "monsterIds": "2,11,14,20,24,32,35,42,43,50,54,60", + "tile": 3, + "name": "Caverns", + "bossId": 30, + "background": 7 + }, + "7": { + "minLevel": 30, + "id": 7, + "monsterIds": "5,23,28,30,58,60,66,76,77,85", + "tile": 8, + "name": "Stone Fields", + "bossId": 15, + "background": 5 + }, + "8": { + "minLevel": 35, + "id": 8, + "monsterIds": "40,46,55,58,67,68,69,71,72,73,74", + "tile": 9, + "name": "Tundra", + "bossId": 84, + "background": 6 + }, + "9": { + "minLevel": 20, + "id": 9, + "monsterIds": "7,9,13,39,40,53,60,66", + "tile": 6, + "name": "Astral Rift", + "bossId": 56, + "background": 2 + } + }, + "heroes": { + "1": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 0, + "baseClickDamage": 2, + "baseGoldPerSecond": 0, + "baseCost": 5, + "id": 1, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 3, + "name": "Cid, the Helpful Adventurer", + "description": "This nice young fighter showed up to take some of those clicks off your fingers." + }, + "2": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 5, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 50, + "id": 2, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 4, + "name": "Treebeast", + "description": "A series of slow, wooden grunts from this creature indicates to you that it is friendly. It must have come from the Great Forest." + }, + "3": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 22, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 250, + "id": 3, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 113, + "name": "Ivan, the Drunken Brawler", + "description": "Once a prized fighter, Ivan can now be seen frequenting taverns challenging foes in his drunken stupor." + }, + "4": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 74, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 1000, + "id": 4, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 6, + "name": "Brittany, Beach Princess", + "description": "\"Are we going on an adventure? Like, you know, ohmygod?\"" + }, + "5": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 245, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 4000, + "id": 5, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 23, + "name": "The Wandering Fisherman", + "description": "He wouldn't tell you his name, but maybe you'd rather not know. His spear is good for catching fish, and popping bloops." + }, + "6": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 976, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 20000, + "id": 6, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 8, + "name": "Betty Clicker", + "description": "This world-renowned master chef is aligned with your interests for only one reason. She goes out of her way to gather only the freshest ingredients for her recipes, including her famous Fat Bat Fondue." + }, + "7": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 3725, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 100000, + "id": 7, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 13, + "name": "The Masked Samurai", + "description": "His menacing weapon will no doubt prove useful to you. He says he was once a great leader in the eastern lands, but you're not sure whether or not to believe him." + }, + "8": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 10859, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 400000, + "id": 8, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 25, + "name": "Leon", + "description": "This walking lion-creature seems friendly enough, but something seems very unnatural about it - especially its face. Its claws seem sharp enough, however, to help you kill some monsters." + }, + "9": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 47143, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 2500000, + "id": 9, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 14, + "name": "The Great Forest Seer", + "description": "News travels fast, and even the Trees can hear. They sent the Seer of the Great Forest to assist you in your journey." + }, + "10": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 186000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 15000000, + "id": 10, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 111, + "name": "Alexa, Assassin", + "description": "She gives you a name that you are sure is not real. She will help you, but you should probably be careful around her." + }, + "11": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 782000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 100000000, + "id": 11, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 16, + "name": "Natalia, Ice Apprentice", + "description": "Not everyone is capable of magic, but Natalia has shown much promise. She is an apprentice of the renowned Ice Wizard, Jerator." + }, + "12": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 3721000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 800000000, + "id": 12, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 12, + "name": "Mercedes, Dutchess of Blades", + "description": "The best warriors are flocking to you for a chance to participate in this madness we call an adventure. Her blade is really sharp, and she will gladly put it to good use by chopping off heads in your service." + }, + "13": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 17010000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 6500000000, + "id": 13, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 11, + "name": "Bobby, Bounty Hunter", + "description": "He claims to be the best mercenary money can buy. And by your best judgement, he does appear to be very skilled. Except with that strange contraption on his back, he seems to have developed a habit of frequently spiralling through the air and smashing head-first into the ground." + }, + "14": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 69480000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 50000000000, + "id": 14, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 17, + "name": "Broyle Lindeoven, Fire Mage", + "description": "This mage is the most efficient means for turning gold into fireballs. Lindeoven was hired many years ago to oppose Jerator in the Third War of the Skies, but it is well-known that he only won on account of the physical miscalculations of his opponent." + }, + "15": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 511000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 500000000000, + "id": 15, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 19, + "name": "Sir George II, King's Guard", + "description": "He left the service of his king, Midas, when he heard the news of your efforts. He did not say why he left, and in his silence you can sense something amiss. His cunning and expertise in battle can provide valuable DPS to the mobs." + }, + "16": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 4526000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 6000000000000, + "id": 16, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 15, + "name": "King Midas", + "description": "When his highest guard, George, left to join you, it wasn't long before Midas himself came looking for him. Then it dawned on him that there is more gold to be made at your side than from exploiting his own kingdom. Also he always wanted to try some of Betty Clicker's chocolate covered bloops." + }, + "17": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 44466000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 80000000000000, + "id": 17, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 18, + "name": "Referi Jerator, Ice Wizard", + "description": "By most accounts, the greatest wizard of all the lands. So powerful, he was hired by the gods on many occasions to fight in their wars. But in the Third War of the Skies, during a powerful incantation, he tripped on a rock and misfired - permanently freezing solid his own head. And lost the war." + }, + "18": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 40953000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 100000000000000, + "id": 18, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 22, + "name": "Abaddon", + "description": "A great being has taken material form to discuss the massacre you've performed. He demands payment, and offers a compelling amount of power in return. Perhaps you should do what he says." + }, + "19": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 362000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 1200000000000000, + "id": 19, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 110, + "name": "Ma Zhu", + "description": "He does not reveal his face, but an aura of power betrays who he is. The Eastern Gods have now been alerted to your cause (whatever it is) and this one wants to help. This one will help for a hefty sum of gold." + }, + "20": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 3334000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 15000000000000000, + "id": 20, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 20, + "name": "Amenhotep", + "description": "He stares at you with a smug, knowing look. Perhaps you don't know why you're here, or what you're really doing, but the Gods have taken a definite interest. Why they demand so much money, though, is astounding..." + }, + "21": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 49143000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 300000000000000000, + "id": 21, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 157, + "name": "Beastlord", + "description": "As an abandoned child, he would befriend the beasts in the wilderness. He is now known as the Beastlord, and his powers are greater than those of any mortal." + }, + "22": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 1086000000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 9000000000000000000, + "id": 22, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 158, + "name": "Athena, Goddess of War", + "description": "Born with hate and bloodlust, Athena finds joy in the demise of others. She will gladly drain the life of your enemies. " + }, + "23": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 31124000000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 350000000000000000000, + "id": 23, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 159, + "name": "Aphrodite, Goddess of Love", + "description": "Aphrodite's power lies in her beauty. Her looks can melt the hearts of her foes." + }, + "24": { + "specialSkill": "", + "goldPerSecondFormula": "heroGoldPerSecondFormula1", + "specialSkillDescription": "", + "costFormula": "heroCostFormula1", + "baseAttack": 917000000000000000, + "baseClickDamage": 0, + "baseGoldPerSecond": 0, + "baseCost": 1.4e+22, + "id": 24, + "attackFormula": "heroAttackFormula1", + "clickDamageFormula": "heroClickDamageFormula1", + "assetId": 160, + "name": "Shinatobe, Wind Deity", + "description": "\"The winds have picked up your call for aid,\" she says. She appears to levitate above the ground, and her presence suggests she holds within her the power of a thousand hurricanes." + } + } +} \ No newline at end of file diff --git a/cloaks.json b/cloaks.json new file mode 100644 index 00000000..42a86d04 --- /dev/null +++ b/cloaks.json @@ -0,0 +1,17 @@ +{ + "google": [ + "Google", "https://google.com/favicon.ico" + ], + "canvas": [ + "Dashboard", "https://du11hjcvx0uqb.cloudfront.net/dist/images/favicon-e10d657a73.ico" + ], + "desmos": [ + "Desmos | Graphing Calculator", "https://www.desmos.com/assets/img/apps/graphing/favicon.ico" + ], + "drive": [ + "My Drive - Google Drive", "https://ssl.gstatic.com/docs/doclist/images/drive_2022q3_32dp.png" + ], + "classroom": [ + "Home", "https://ssl.gstatic.com/classroom/ic_product_classroom_144.png" + ] +} \ No newline at end of file diff --git a/cookieclicker/index.html b/cookieclicker/index.html index b485825d..889b2d4b 100644 --- a/cookieclicker/index.html +++ b/cookieclicker/index.html @@ -150,8 +150,6 @@ var App=typeof App==='undefined'?0:App;
- -
Store
@@ -160,8 +158,6 @@ var App=typeof App==='undefined'?0:App;
- -
diff --git a/cookieclicker/main.js b/cookieclicker/main.js index 82b0dfa3..f43c5e59 100644 --- a/cookieclicker/main.js +++ b/cookieclicker/main.js @@ -6621,6 +6621,8 @@ Game.Launch=function() '
'+ (App?Game.WritePrefButton('bgMusic','bgMusicButton',loc("Music in background")+ON,loc("Music in background")+OFF,'')+'
':'')+ (App?Game.WritePrefButton('fullscreen','fullscreenButton',loc("Fullscreen")+ON,loc("Fullscreen")+OFF,'Game.ToggleFullscreen();')+'
':'')+ + Game.WritePrefButton('cheat','cheatToggle',loc("Cheat menu")+ON,loc("Cheat menu")+OFF,'Game.OpenSesame();')+'
'+ + Game.WritePrefButton('mod','modMenu',loc("Mod Menu"),loc("Mod Menu"),'Game.ModMenu();')+'
'+ Game.WritePrefButton('fancy','fancyButton',loc("Fancy graphics")+ON,loc("Fancy graphics")+OFF,'Game.ToggleFancy();')+'
'+ Game.WritePrefButton('filters','filtersButton',loc("CSS filters")+ON,loc("CSS filters")+OFF,'Game.ToggleFilters();')+'
'+ Game.WritePrefButton('particles','particlesButton',loc("Particles")+ON,loc("Particles")+OFF)+(EN?'':'')+'
'+ @@ -16025,7 +16027,15 @@ Game.Launch=function() Game.Achievements['Cheated cookies taste awful'].won=1; } - + Game.ModMenu=function() + { + Game.modURL = ["mods/CookieMonster.js", "mods/BlackHoleInverter.js"] + Game.Prompt('

'+loc("Select a mod!")+'

'+loc("Here are a list of mods built into Selenite's Cookie Clicker. If you're curious about a mod, just look it up.")+'
' + + Game.WritePrefButton('mod0','mod0',loc("Cookie Monster"),loc("Cookie Monster"),'Game.LoadMod(Game.modURL[0]);')+'
'+'
'+ + Game.WritePrefButton('mod1','mod1',loc("Black Hole Inverter"),loc("Black Hole Inverter"),'Game.LoadMod(Game.modURL[1]);')+'
'+'
' + ,[loc("All done!") + ]);//prompt('Copy this text and keep it somewhere safe!',Game.WriteSave(1)); + } Game.loadAscendCalibrator=function() { Game.loadAscendCalibrator=0; diff --git a/cookieclicker/mods/BlackHoleInverter.js b/cookieclicker/mods/BlackHoleInverter.js new file mode 100644 index 00000000..0e1386d7 --- /dev/null +++ b/cookieclicker/mods/BlackHoleInverter.js @@ -0,0 +1,198 @@ +Game.Win('Third-party'); +if(BlackholeInverter === undefined) var BlackholeInverter = {}; +if(typeof CCSE == 'undefined') Game.LoadMod('mods/CCSE.js'); +BlackholeInverter.name = 'Black Hole Inverter'; +BlackholeInverter.version = '1.14'; +BlackholeInverter.GameVersion = '2.052'; + +BlackholeInverter.launch = function(){ + BlackholeInverter.init = function(){ + var iconsURL = 'https://klattmose.github.io/CookieClicker/img/customIcons.png'; + + CCSE.NewBuilding('Black hole inverter', + 'black hole inverter|black hole inverters|extracted|[X]% larger event horizon|[X]% larger event horizon', + 'Inverts the flow of gravity to get the infinitely delicious cookies from an infinitely dense singularity.', + 1, + 2, + { + base:'https://klattmose.github.io/CookieClicker/img/blackholeinverter', + xV:8, + yV:32, + w:128, + rows:1, + x:0, + y:0, + customBuildingPic:'mods/img/blackholebuilding.png', + customIconsPic:iconsURL + }, + "doesn't matter what you put here", + function(me){ + var mult = 1; + mult *= Game.GetTieredCpsMult(me); + mult *= Game.magicCpS(me.name); + return me.baseCps * mult; + }, + function(){ + Game.UnlockTiered(this); + if(this.amount >= Game.SpecialGrandmaUnlock && Game.Objects['Grandma'].amount > 0) Game.Unlock(this.grandma.name); + }, + { + name:'Hypnodrone', + desc:'Autonomous aerial brand ambassadors to "encourage" more sales!', + icon:1 + }, + ['Kugelblitz', 'Spaghettification'] + ); + + Game.Objects['Black hole inverter'].displayName='Black hole inverter'; // Shrink the name since it's so large + + + // Upgrades + var last; var i = 0; var order = BlackholeInverter.getTieredUpgradeOrder(); + Game.TieredUpgrade('Blacker holes', 'Blacker than black!', 'Black hole inverter', 1); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('More Mass', 'Big holes.', 'Black hole inverter', 2); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Stronger Pull', 'No escape.', 'Black hole inverter', 3); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Dead Space', 'You stare into the abyss and the abyss stares back at you.', 'Black hole inverter', 4); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Cookiefication', 'Yum!', 'Black hole inverter', 5); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('White Hole Inverters', 'How does this one even make sense?', 'Black hole inverter', 6); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Merging', 'Combine!', 'Black hole inverter', 7); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Worm holes', 'You go in one end, you come out the other. Easy as that.', 'Black hole inverter', 8); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Micro black holes', 'Tiny, but deadly.', 'Black hole inverter', 9); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Radio-Rings', 'Insanely radioactive, and extremely deadly!', 'Black hole inverter', 10); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Reality-Bending Holes', 'Now you can see how close you are to certain doom! Two of them put together!', 'Black hole inverter', 11); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Moving Black Holes', "They can move now.", 'Black hole inverter', 12); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('Permanent Holes', "They'll never disappear!", 'Black hole inverter', 13); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + Game.TieredUpgrade('It has pockets!', "Also known as a pants hole", 'Black hole inverter', 14); last = Game.last; last.icon[2] = iconsURL; last.order = order + i / 100; i++; + + order = BlackholeInverter.getGrandmaUpgradeOrder(); + last = Game.GrandmaSynergy('Heavy grandmas', 'A dense grandma to accrete more cookies.', 'Black hole inverter'); last.order = order; + + order = BlackholeInverter.getSynergyUpgradeOrder(); + last = Game.SynergyUpgrade('Daring pilots', "You've never heard of the Millennium Falcon? It's the ship that made the Kessel Run in less than twelve parsecs.", 'Black hole inverter', 'Shipment', 'synergy1'); last.icon[2] = iconsURL; last.order = order; + last = Game.SynergyUpgrade('General relativity', 'Space is time. Time is space', 'Black hole inverter', 'Time machine', 'synergy2'); last.icon[2] = iconsURL; last.order = order + 0.01; + + + // Achievements + order = BlackholeInverter.getAchievementOrder(); i = 0; + last = Game.TieredAchievement('Single singularity', '', 'Black hole inverter', 1); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Penrose diagram', '', 'Black hole inverter', 2); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Schwarzschild', '', 'Black hole inverter', 3); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Holes in holes', '', 'Black hole inverter', 4); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('No-hair theorem', '', 'Black hole inverter', 5); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Photon sphere', '', 'Black hole inverter', 6); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Information paradox', '', 'Black hole inverter', 7); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Gravitaional lensing', '', 'Black hole inverter', 8); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Galactic nuclei', '', 'Black hole inverter', 9); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Sagittarius A*', '', 'Black hole inverter', 10); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Hey now, you\'re a dead star', '', 'Black hole inverter', 11); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Incredibly dense', '', 'Black hole inverter', 12); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Infinitely dense', '', 'Black hole inverter', 13); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.TieredAchievement('Quasi-stellar radio source', '', 'Black hole inverter', 14); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + + last = Game.ProductionAchievement('Relativistic jets', 'Black hole inverter', 1); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.ProductionAchievement('Primordial black holes', 'Black hole inverter', 2); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + last = Game.ProductionAchievement('Naked singularity', 'Black hole inverter', 3); last.icon[2] = iconsURL; last.order = order + i / 100; i++; + + last = CCSE.NewAchievement('M87', 'Reach level 10 black hole inverters.', [1, 26, iconsURL]); + Game.Objects['Black hole inverter'].levelAchiev10 = last; last.order = order + i / 100; i++; + + + + Game.customStatsMenu.push(function(){ + CCSE.AppendStatsVersionNumber(BlackholeInverter.name, BlackholeInverter.version); + }); + + // Finish up + BlackholeInverter.isLoaded = 1; + if(BlackholeInverter.postloadHooks){ + for(var i = 0; i < BlackholeInverter.postloadHooks.length; ++i) BlackholeInverter.postloadHooks[i](); + } + + if (Game.prefs.popups) Game.Popup(BlackholeInverter.name + ' loaded!'); + else Game.Notify(BlackholeInverter.name + ' loaded!', '', '', 1, 1); + } + + + BlackholeInverter.getTieredUpgradeOrder = function(){ + function isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + var res = 0; + for(var i = 0; i < Game.ObjectsN; i++){ + var me = Game.ObjectsById[i]; + for(var ii in me.tieredUpgrades){ + if(isNumber(ii)) res = Math.max(me.tieredUpgrades[ii].order, res); + } + } + + return res + 0.01; + } + + BlackholeInverter.getGrandmaUpgradeOrder = function(){ + var res = 0; + for(var i in Game.GrandmaSynergies){ + res = Math.max(Game.Upgrades[Game.GrandmaSynergies[i]].order, res); + } + + return res + 0.01; + } + + BlackholeInverter.getSynergyUpgradeOrder = function(){ + var res = 0; + for(var i = 0; i < Game.ObjectsN; i++){ + var me = Game.ObjectsById[i]; + for(var ii in me.synergies){ + res = Math.max(me.synergies[ii].order, res); + } + } + + return res + 0.01; + } + + BlackholeInverter.getAchievementOrder = function(){ + var res = 0; + for(var i = 0; i < Game.ObjectsN-1; i++){ + var me = Game.ObjectsById[i]; + + for(var ii in me.tieredAchievs){ + res = Math.max(me.tieredAchievs[ii].order, res); + } + + for(var ii in me.productionAchievs){ + res = Math.max(me.productionAchievs[ii].achiev.order, res); + } + + if(me.levelAchiev10) res = Math.max(me.levelAchiev10.order, res); + } + + return res + 0.01; + } + + + ModLanguage('*',{ + + "%1 black hole inverter": [ + "%1 black hole inverter", + "%1 black hole inverters" + ], + "[Black hole inverter quote]Inverts the flow of gravity to get the infinitely delicious cookies from an infinitely dense singularity.": "Inverts the flow of gravity to get the infinitely delicious cookies from an infinitely dense singularity.", + "[Black hole inverter business name]Hypnodrone": "Hypnodrone", + '[Black hole inverter business quote]Autonomous aerial brand ambassadors to "encourage" more sales!': 'Autonomous aerial brand ambassadors to "encourage" more sales!', + + }); + + if(CCSE.ConfirmGameVersion(BlackholeInverter.name, BlackholeInverter.version, BlackholeInverter.GameVersion)) BlackholeInverter.init(); +} + + +if(!BlackholeInverter.isLoaded){ + if(CCSE && CCSE.isLoaded){ + BlackholeInverter.launch(); + } + else{ + if(!CCSE) var CCSE = {}; + if(!CCSE.postLoadHooks) CCSE.postLoadHooks = []; + CCSE.postLoadHooks.push(BlackholeInverter.launch); + } +} \ No newline at end of file diff --git a/cookieclicker/mods/CCSE.js b/cookieclicker/mods/CCSE.js new file mode 100644 index 00000000..02ecb0df --- /dev/null +++ b/cookieclicker/mods/CCSE.js @@ -0,0 +1,4137 @@ +if(CCSE === undefined) var CCSE = {}; +if(!CCSE.postLoadHooks) CCSE.postLoadHooks = []; +CCSE.name = 'CCSE'; +CCSE.version = '2.035'; +CCSE.Steam = (typeof Steam !== 'undefined'); +CCSE.GameVersion = CCSE.Steam ? '2.052' : '2.052'; + +CCSE.launch = function(){ + CCSE.loading = 1; + + CCSE.init = function(){ + CCSE.init = 0; + CCSE.InitNote(); + CCSE.InitializeConfig(); + CCSE.AddCCSEStyles(); + + // Define more parts of CCSE + CCSE.Backup = {}; + CCSE.collapseMenu = {}; + if(!Game.customMinigame) Game.customMinigame = {}; + for(var key in Game.Objects) if(!Game.customMinigame[key]) Game.customMinigame[key] = {}; + + + // Build a list of functions to feed to requestAnimationFrame + CCSE.playlist = []; + CCSE.track = 0; + + CCSE.playlist.push(function(){ + CCSE.ReplaceMainGame(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(function(){ + CCSE.MinigameReplacer(CCSE.ReplaceGrimoire, 'Wizard tower'); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(function(){ + CCSE.MinigameReplacer(CCSE.ReplacePantheon, 'Temple'); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(function(){ + CCSE.MinigameReplacer(CCSE.ReplaceGarden, 'Farm'); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(function(){ + CCSE.MinigameReplacer(CCSE.ReplaceMarket, 'Bank'); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + + + CCSE.playlist.push(function(){ + CCSE.ReplaceBuildingsStart(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(CCSE.ReplaceBuildings); // We'll call the next one from here + CCSE.playlist.push(function(){ + CCSE.ReplaceBuildingsFinish(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + + + CCSE.playlist.push(function(){ + CCSE.ReplaceUpgradesStart(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(CCSE.ReplaceUpgrades); // We'll call the next one from here + CCSE.playlist.push(function(){ + CCSE.ReplaceUpgradesFinish(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + + + CCSE.playlist.push(function(){ + CCSE.ReplaceAchievementsStart(); + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + }); + CCSE.playlist.push(CCSE.ReplaceAchievements); // We'll call the next one from here + + + CCSE.playlist.push(CCSE.finalize); + + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + } + + CCSE.finalize = function(){ + // Load any custom save data and inject save functions + if(!Game.modSaveData[CCSE.name]) CCSE.load(); + + + // Inject menu functions + Game.customOptionsMenu.push(function(){ + CCSE.AppendCollapsibleOptionsMenu(CCSE.name, CCSE.GetMenuString()); + }); + + Game.customStatsMenu.push(function(){ + CCSE.AppendStatsVersionNumber(CCSE.name, CCSE.version); + }); + + Game.customInfoMenu.push(function(){ + CCSE.PrependCollapsibleInfoMenu(CCSE.name, CCSE.updateLog); + }); + + Game.registerHook('reset', CCSE.reset); + + //l('versionNumber').innerHTML = 'Game ' + l('versionNumber').innerHTML + '
CCSE v. ' + CCSE.version; + var versionNumber = l('versionNumber'); + var versionDiv = document.createElement('p'); + versionDiv.id = 'CCSEversionNumber'; + versionDiv.innerHTML = 'CCSE v. ' + CCSE.version; + var textDiv = document.createElement('span'); + textDiv.id = 'CCSEversionGame'; + textDiv.innerHTML = 'Game '; + versionNumber.appendChild(versionDiv); + versionNumber.insertBefore(textDiv, versionNumber.firstChild); + + // Announce completion, set the isLoaded flag, and run any functions that were waiting for this to load + CCSE.Note.title = 'CCSE loaded!'; + CCSE.Note.life = Game.fps; + CCSE.isLoaded = 1; + CCSE.loading = 0; + + if(CCSE.postLoadHooks) for(var i in CCSE.postLoadHooks) CCSE.postLoadHooks[i](); + if(CCSE.Steam){ + Game.loadModData = CCSE.GameLoadModData; + CCSE.LaunchOtherMods(); + if(CCSE.gameHasLoadedSave) Game.loadModData(); + } + + CCSE.applyPref('showVersionNo'); + } + + + /*===================================================================================== + Update history + =======================================================================================*/ + { CCSE.updateLog = '
Cookie Clicker Script Extender is a modding framework intended to make modding this game easier and more accessible.
' + + '
CCSE is written and maintained by Klattmose (GitHub, reddit)
' + + '
Further documentation can be found here.
' + + '
If you have a bug report or a suggestion, create an issue here.
' + + '
CCSE version history
' + + + '
05/16/2023
' + + '
• Can now give ids to Headers
' + + + '
03/13/2023
' + + '
• Added option to hide the CCSE version in the lower left of the screen
' + + '
• Added some hooks for the YouCustomizer functions
' + + '
• Fixed compatibility with Frozen Cookies
' + + + '
01/07/2022
' + + '
• Added hook for Game.resize
' + + '
• Added hook for Garden.logic (for plant aging)
' + + + '
10/04/2021
' + + '
• Fixed bug that was preventing custom buildings, upgrades, and achievements from being saved in some circumstances
' + + + '
09/26/2021
' + + '
• Steam version: Will now initialize before other mods
' + + '
• Fixed getting permanent upgrades on a Born Again ascension
' + + '
• Fixed breaking Game.NewUpgradeCookie
' + + '
• Reset custom upgrades, achievements, etc. managed by CCSE on game reset even if the mod generating them isn\'t loaded
' + + + '
09/18/2021
' + + '
• Steam version: Custom links in the menu will now open in a browser rather than Steam
' + + '
• Fixed some upgrade descriptions breaking in localization
' + + '
• Added hooks for Game.crate and Game.crateTooltip
' + + '
• Changed Game.Loader.Load injection to detect \'/\' instead of \'http\'
' + + + '
09/10/2021
' + + '
• Added PasswordBox and CheckBox to MenuHelper
' + + '
• Added function to append custom CSS styles
' + + '
• Fixed bug in custom Background selector
' + + + '
09/09/2021
' + + '
• Steam version: Added hooks for Steam.modsPopup
' + + '
• Added support for custom images for the Pantheon and Grimoire
' + + '
• Added support for custom Golden cookie sound selector options
' + + '
• Added support for custom Milk selector options
' + + '
• Added support for custom Background selector options
' + + + '
09/01/2021
' + + '
• Vaulting for custom upgrades no longer depends on mod load order
' + + '
• Setting custom upgrades as Permanent will no longer break the game if the Stats menu is opened without the mod loaded
' + + '
• Added some functions for commonly used menu items
' + + + '
02/06/2021
' + + '
• Halved the loading time with this one weird trick!
' + + '
• The trick is called optimization
' + + '
• And learning about prototype functions
' + + + '
10/31/2020
' + + '
• Updated to use the new modding API
' + + '
• The CCSE.save object (where the mod save data is stored) is now named CCSE.config. The new CCSE.save is a function that will get called by Cookie Clicker when it decides it\'s time to save.
' + + + '
06/20/2020
' + + '
• Added hooks for the new stock market minigame
' + + '
• Added hook for the new function Game.ClickSpecialPic
' + + + '
10/22/2019
' + + '
• Added hook for the new function Game.auraMult
' + + + '
05/14/2019 - parallel processing
' + + '
• Won\'t freeze the game while CCSE is loading
' + + '
• Also has a progress meter for feedback
' + + '
• Bug fixes
' + + + '
05/11/2019 - take two
' + + '
• You know that moment where you do something and then immediately realize a better way to do it?
' + + '
• Changed the method for injecting code to standardized functions rather than calling "eval" willy-nilly
' + + '
• Added function for creating seasons
' + + '
• Created this update log, and put the version number in the lower left corner
' + + '
• With apologies for pretending to be a game update
' + + + '
05/05/2019 - initial release
' + + '
• Added a bunch of mod hooks to the game
' + + '
• Added functions to ease the creation of content like achievements and buildings
' + + '
• Added a save system to manage game objects created through CCSE
' + + '
• Further documentation here
' + + '
Cookie Clicker
'; + } + + + /*===================================================================================== + The heart of the mod. Functions to inject code into functions. + =======================================================================================*/ + CCSE.InjectCodeIntoFunction = function(functionName, alterFunctionCode, code, preEvalScript, hasPrototype){ + // preEvalScript is to set variables that are used in the function but aren't declared in the function + if(preEvalScript) eval(preEvalScript); + + var originalFunction = eval(functionName); + if (originalFunction === null) { + console.warn(`CCSE: ${ functionName } is not found. Could not inject ${ code }`); + return; + } else if (typeof originalFunction !== "function") { + console.warn(`CCSE: ${ functionName } is not a function. Could not inject ${ code }`); + return; + } + + //console.log(functionName); + eval(functionName + " = " + alterFunctionCode(originalFunction.toString())); + if(hasPrototype) { + var alteredFunction = eval(functionName); + alteredFunction.prototype = originalFunction.prototype; + } + + CCSE.functionsAltered++; + if(!CCSE.isLoaded) CCSE.UpdateNote(); + //if(eval(functionName + ".toString()").indexOf(code) == -1) console.log("Error injecting code into function " + functionName + ". Could not inject " + code); + } + + CCSE.SliceCodeIntoFunction = function(functionName, pos, code, preEvalScript, hasPrototype){ + var alterFunctionCode = function(temp){ + return temp.slice(0, pos) + code + temp.slice(pos); + } + CCSE.InjectCodeIntoFunction(functionName, alterFunctionCode, code, preEvalScript, hasPrototype); + } + + CCSE.SpliceCodeIntoFunction = function(functionName, row, code, preEvalScript, hasPrototype){ + var alterFunctionCode = function(temp){ + temp = temp.split("\n"); + i = row < 0 ? temp.length + row : row; + temp.splice(i, 0, code); + return temp.join("\n"); + } + CCSE.InjectCodeIntoFunction(functionName, alterFunctionCode, code, preEvalScript, hasPrototype); + } + + CCSE.ReplaceCodeIntoFunction = function(functionName, targetString, code, mode, preEvalScript, hasPrototype){ + var alterFunctionCode = function(temp){ + switch(mode){ + case -1: // Insert before targetString + return temp.replace(targetString, code + "\n" + targetString); + case 0: // Replace targetString. Regex will work + return temp.replace(targetString, code); + case 1: // Insert after targetString + return temp.replace(targetString, targetString + "\n" + code); + default: + throw new Error("mode must be either, -1, 0, or 1"); + } + } + CCSE.InjectCodeIntoFunction(functionName, alterFunctionCode, code, preEvalScript, hasPrototype); + } + + CCSE.GetProgressHTML = function(progress){ + return `
${ progress }%
`; + } + + CCSE.InitNote = function(){ + if(CCSE.Steam) CCSE.iconURL = CCSE.GetModPath('CCSE') + '/CCSEicon.png'; + else CCSE.iconURL = 'https://klattmose.github.io/CookieClicker/img/CCSEicon.png'; + + CCSE.functionsTotal = ( + 141 + + (CCSE.Steam ? 7 : 0) + + Game.ObjectsN * 18 - 1 + 3 + + Game.UpgradesN * 1 + 25 + + Game.AchievementsN * 1 + ); // Needs to be manually updated + CCSE.functionsAltered = 0; + CCSE.progress = 0; + + Game.Notify('CCSE is initializing', CCSE.GetProgressHTML(0), [0, 0, CCSE.iconURL], 6, 1); + CCSE.Note = Game.NotesById[Game.noteId - 1]; + CCSE.Note.life = 600000; // 10 minutes, just to be sure + } + + CCSE.UpdateNote = function(){ + CCSE.Note.life = 600000; + var progress = Math.min(Math.floor(CCSE.functionsAltered / CCSE.functionsTotal * 100), 100); + if(progress != CCSE.progress){ + CCSE.progress = progress; + CCSE.Note.desc = CCSE.GetProgressHTML(CCSE.progress); + Game.UpdateNotes(); + } + } + + + /*===================================================================================== + Do all replacing in one function + Actually don't, it locks up the browser for as long as it's running + Also declare hook arrays in the close vicinity of the functions they get used in + =======================================================================================*/ + CCSE.ReplaceMainGame = function(){ + // Temporary variable for storing function strings + // Slightly more efficient than nesting functions + // Doubt it really matters + var temp = ''; + var pos = 0; + var proto; + var obj; + + + // Game.UpdateMenu + if(!Game.customMenu) Game.customMenu = []; + if(!Game.customOptionsMenu) Game.customOptionsMenu = []; + if(!Game.customStatsMenu) Game.customStatsMenu = []; + if(!Game.customInfoMenu) Game.customInfoMenu = []; + + CCSE.ReplaceCodeIntoFunction('Game.UpdateMenu', "l('menu').innerHTML=str;", ` + if(Game.onMenu == 'prefs'){ + // Game.UpdateMenu injection point 0 + for(var i in Game.customOptionsMenu) Game.customOptionsMenu[i](); + } + else if(Game.onMenu == 'stats'){ + // Game.UpdateMenu injection point 1 + for(var i in Game.customStatsMenu) Game.customStatsMenu[i](); + } + else if(Game.onMenu == 'log'){ + // Game.UpdateMenu injection point 2 + for(var i in Game.customInfoMenu) Game.customInfoMenu[i](); + } + + // Any that don't want to fit into a label + // Game.UpdateMenu injection point 3 + for(var i in Game.customMenu) Game.customMenu[i](); + `, 1); + + + // Code specific to the Steam version + // Might move to their own function maybe + if(CCSE.Steam){ + // Steam.modsPopup + // This function has several functions defined within it + if(!Game.customModsPopup) Game.customModsPopup = []; + if(!Game.customModsPopupCheckModDependencies) Game.customModsPopupCheckModDependencies = []; // Return okay to have no effect + if(!Game.customModsPopupUpdateModList) Game.customModsPopupUpdateModList = []; + if(!Game.customModsPopupUpdateModOptions) Game.customModsPopupUpdateModOptions = []; + CCSE.SliceCodeIntoFunction('Steam.modsPopup', -1, ` + // Steam.modsPopup injection point 0 + for(var i in Game.customModsPopup) Game.customModsPopup[i](selectedMod, mods); + `); + CCSE.ReplaceCodeIntoFunction('Steam.modsPopup', "return okay;", + `// Steam.modsPopup injection point 1 + for(var i in Game.customModsPopupCheckModDependencies) okay = Game.customModsPopupCheckModDependencies[i](okay, mod, loadedMods, selectedMod, mods); + `, -1); + CCSE.ReplaceCodeIntoFunction('Steam.modsPopup', "updateModOptions();", + `// Steam.modsPopup injection point 2 + for(var i in Game.customModsPopupUpdateModList) Game.customModsPopupUpdateModList[i](selectedMod, mods); + `, -1); + CCSE.ReplaceCodeIntoFunction('Steam.modsPopup', 'else el.innerHTML=loc("Select a mod.");', + `// Steam.modsPopup injection point 3 + for(var i in Game.customModsPopupUpdateModOptions) Game.customModsPopupUpdateModOptions[i](selectedMod, mods); + `, 1); + + // Steam.workshopPopup + if(!Game.customWorkshopPopup) Game.customWorkshopPopup = []; + if(!Game.customWorkshopPopupUpdateModDisplay) Game.customWorkshopPopupUpdateModDisplay = []; + if(!Game.customWorkshopPopupUpdatePublishedModsPopup) Game.customWorkshopPopupUpdatePublishedModsPopup = []; + CCSE.SliceCodeIntoFunction('Steam.workshopPopup', -1, ` + // Steam.customWorkshopPopup injection point 0 + for(var i in Game.customWorkshopPopup) Game.customWorkshopPopup[i](selectedMod, selectedModPath); + `); + CCSE.ReplaceCodeIntoFunction('Steam.workshopPopup', "Game.UpdatePrompt();", + `// Steam.customWorkshopPopup injection point 1 + for(var i in Game.customWorkshopPopupUpdateModDisplay) Game.customWorkshopPopupUpdateModDisplay[i](selectedMod, el); + `, 1); + CCSE.ReplaceCodeIntoFunction('Steam.workshopPopup', "else l('modDisplay').innerHTML=`
(${loc(\"none\")})
`;", + `// Steam.customWorkshopPopup injection point 2 + for(var i in Game.customWorkshopPopupUpdatePublishedModsPopup) Game.customWorkshopPopupUpdatePublishedModsPopup[i](response); + `, 1); + } + + + /* + New modding api works + // Game.LoadSave + if(!Game.customLoad) Game.customLoad = []; + if(!(Game.LoadSave.toString().indexOf('Game.customLoad') > 0)){ + CCSE.ReplaceCodeIntoFunction('Game.LoadSave', 'if (Game.prefs.showBackupWarning==1)', + `// Game.LoadSave injection point 0 + for(var i in Game.customLoad) Game.customLoad[i](); `, -1); + }*/ + + + // Game.WriteSave + // This section only exists to support custom seasons + CCSE.ReplaceCodeIntoFunction('Game.WriteSave', '(Game.season?', '((Game.season)?', 0); + CCSE.ReplaceCodeIntoFunction('Game.WriteSave', '(Game.seasonT)', '((Game.season)?Game.seasonT:-1)', 0); + + + // Game.Reset + if(!Game.customReset) Game.customReset = []; + CCSE.SliceCodeIntoFunction('Game.Reset', -1, ` + // Game.Reset injection point 0 + for(var i in Game.customReset) Game.customReset[i](hard); + `); + + + // randomFloor + // Gonna just replace it and try to keep up with any changes (however unlikely) + // function randomFloor(x) {if ((x%1)=0?'':this.domain)", 0); + + + // Game.resize + if(!Game.customResize) Game.customResize = []; + CCSE.SliceCodeIntoFunction('Game.resize', -1, ` + // Game.resize injection point 0 + for(var i in Game.customResize) Game.customResize[i](); + `); + + + // ----- Tooltips block ----- // + + // Game.tooltip.draw + if(!Game.customTooltipDraw) Game.customTooltipDraw = []; + CCSE.SliceCodeIntoFunction('Game.tooltip.draw', -1, ` + // Game.tooltip.draw injection point 0 + for(var i in Game.customTooltipDraw) Game.customTooltipDraw[i](from, text, origin); + `); + + + // Game.tooltip.update + if(!Game.customTooltipUpdate) Game.customTooltipUpdate = []; + CCSE.SliceCodeIntoFunction('Game.tooltip.update', -1, ` + // Game.tooltip.update injection point 0 + for(var i in Game.customTooltipUpdate) Game.customTooltipUpdate[i](); + `); + + + // Game.crate + // Return ret to have no effect + if(!Game.customCrate) Game.customCrate = []; + CCSE.ReplaceCodeIntoFunction('Game.crate', 'return (Game', "var ret = (Game", 0); + CCSE.SliceCodeIntoFunction('Game.crate', -1, ` + // Game.crate injection point 0 + for(var i in Game.customCrate) ret = Game.customCrate[i](me, context, forceClickStr, id, ret, style); + return ret; + `); + + + // Game.crateTooltip + // Return ret to have no effect + if(!Game.customCrateTooltip) Game.customCrateTooltip = []; + CCSE.ReplaceCodeIntoFunction('Game.crateTooltip', 'return', "var ret = ", 0); + CCSE.SliceCodeIntoFunction('Game.crateTooltip', -1, ` + // Game.crateTooltip injection point 0 + for(var i in Game.customCrateTooltip) ret = Game.customCrateTooltip[i](me, context, ret); + return ret; + `); + + + // ----- Ascension block ----- // + + // Game.HowMuchPrestige + // Return ret to have no effect + if(!Game.customHowMuchPrestige) Game.customHowMuchPrestige = []; + CCSE.ReplaceCodeIntoFunction('Game.HowMuchPrestige', 'return', "var ret = ", 0); + CCSE.SliceCodeIntoFunction('Game.HowMuchPrestige', -1, ` + // Game.HowMuchPrestige injection point 0 + for(var i in Game.customHowMuchPrestige) ret = Game.customHowMuchPrestige[i](cookies, ret); + return ret; + `); + + + // Game.HowManyCookiesReset + // Return ret to have no effect + if(!Game.customHowManyCookiesReset) Game.customHowManyCookiesReset = []; + CCSE.ReplaceCodeIntoFunction('Game.HowManyCookiesReset', 'return', "var ret = ", 0); + CCSE.SliceCodeIntoFunction('Game.HowManyCookiesReset', -1, ` + // Game.HowManyCookiesReset injection point 0 + for(var i in Game.customHowManyCookiesReset) ret = Game.customHowManyCookiesReset[i](chips, ret); + return ret; + `); + + + // Game.GetHeavenlyMultiplier + // Functions should return a value to multiply the heavenlyMult by + if(!Game.customHeavenlyMultiplier) Game.customHeavenlyMultiplier = []; + CCSE.ReplaceCodeIntoFunction('Game.GetHeavenlyMultiplier', 'return heavenlyMult;', ` + // Game.GetHeavenlyMultiplier injection point 0 + for(var i in Game.customHeavenlyMultiplier) heavenlyMult *= Game.customHeavenlyMultiplier[i]();`, -1); + + + // Game.UpdateAscensionModePrompt + if(!Game.customUpdateAscensionModePrompt) Game.customUpdateAscensionModePrompt = []; + CCSE.SliceCodeIntoFunction('Game.UpdateAscensionModePrompt', -1, ` + // Game.UpdateAscensionModePrompt injection point 0 + for(var i in Game.customUpdateAscensionModePrompt) Game.customUpdateAscensionModePrompt[i](); + `); + + + // Game.Reincarnate + // Only runs when bypass == 1 (i.e. passed the confirmation prompt) + if(!Game.customReincarnate) Game.customReincarnate = []; + CCSE.SliceCodeIntoFunction('Game.Reincarnate', -2, ` + // Game.Reincarnate injection point 0 + if(bypass == 1) for(var i in Game.customReincarnate) Game.customReincarnate[i](); + `); + + + // Game.Ascend + // Only runs when bypass == 1 (i.e. passed the confirmation prompt) + if(!Game.customAscend) Game.customAscend = []; + CCSE.SliceCodeIntoFunction('Game.Ascend', -2, ` + // Game.Ascend injection point 0 + if(bypass == 1) for(var i in Game.customAscend) Game.customAscend[i](); + `); + + + // Game.UpdateAscend + // Runs every frame while on the Ascension tree + if(!Game.customUpdateAscend) Game.customUpdateAscend = []; + CCSE.SliceCodeIntoFunction('Game.UpdateAscend', -1, ` + // Game.UpdateAscend injection point 0 + for(var i in Game.customUpdateAscend) Game.customUpdateAscend[i](); + `); + + + // Game.BuildAscendTree + if(!Game.customBuildAscendTree) Game.customBuildAscendTree = []; + CCSE.SliceCodeIntoFunction('Game.BuildAscendTree', -1, ` + // Game.BuildAscendTree injection point 0 + for(var i in Game.customBuildAscendTree) Game.customBuildAscendTree[i](justBought); + `); + + + // ----- Sugar Lumps block ----- // + + // Game.lumpTooltip + // Return str to have no effect + if(!Game.customLumpTooltip) Game.customLumpTooltip = []; + CCSE.ReplaceCodeIntoFunction('Game.lumpTooltip', 'return', + `// Game.lumpTooltip injection point 0 + for(var i in Game.customLumpTooltip) str = Game.customLumpTooltip[i](str, phase);`, -1); + + + // Game.computeLumpTimes + if(!Game.customComputeLumpTimes) Game.customComputeLumpTimes = []; + CCSE.SliceCodeIntoFunction('Game.computeLumpTimes', -1, ` + // Game.computeLumpTimes injection point 0 + for(var i in Game.customComputeLumpTimes) Game.customComputeLumpTimes[i](); + `); + + + // Game.gainLumps + if(!Game.customGainLumps) Game.customGainLumps = []; + CCSE.SliceCodeIntoFunction('Game.gainLumps', -1, ` + // Game.gainLumps injection point 0 + for(var i in Game.customGainLumps) Game.customGainLumps[i](total); + `); + + + // Game.clickLump + if(!Game.customClickLump) Game.customClickLump = []; + CCSE.SliceCodeIntoFunction('Game.clickLump', -1, ` + // Game.clickLump injection point 0 + for(var i in Game.customClickLump) Game.customClickLump[i](); + `); + + + // Game.harvestLumps + if(!Game.customHarvestLumps) Game.customHarvestLumps = []; + CCSE.ReplaceCodeIntoFunction('Game.harvestLumps', 'total=Math.floor(total);', + `// Game.harvestLumps injection point 0`, -1); + CCSE.ReplaceCodeIntoFunction('Game.harvestLumps', "Game.Win('Maillard reaction');", + `// Game.harvestLumps injection point 1`, 1); + CCSE.SliceCodeIntoFunction('Game.harvestLumps', -1, ` + // Game.harvestLumps injection point 2 + for(var i in Game.customHarvestLumps) Game.customHarvestLumps[i](amount, silent); + `); + + + // Game.computeLumpType + // Functions should push things to types + if(!Game.customComputeLumpType) Game.customComputeLumpType = []; + CCSE.ReplaceCodeIntoFunction('Game.computeLumpType', '//caramelized', + `// Game.computeLumpType injection point 0 + for(var i in Game.customComputeLumpType) Game.customComputeLumpType[i](types);`, 1); + + + // Game.canLumps + // Return ret to have no effect + if(!Game.customCanLumps) Game.customCanLumps = []; + CCSE.SpliceCodeIntoFunction('Game.canLumps', 2, 'var ret;'); + CCSE.ReplaceCodeIntoFunction('Game.canLumps', 'return true;', 'ret = true;', 0); + CCSE.ReplaceCodeIntoFunction('Game.canLumps', 'return false', + `else ret = false; + // Game.canLumps injection point 0 + for(var i in Game.customCanLumps) ret = Game.customCanLumps[i](ret); + return ret;`, 0); + + + // Game.getLumpRefillMax + // Return ret to have no effect + if(!Game.customLumpRefillMax) Game.customLumpRefillMax = []; + CCSE.ReplaceCodeIntoFunction('Game.getLumpRefillMax', 'return', 'var ret =', 0); + CCSE.SliceCodeIntoFunction('Game.getLumpRefillMax', -1, + `// Game.getLumpRefillMax injection point 0 + for(var i in Game.customLumpRefillMax) ret = Game.customLumpRefillMax[i](ret); + return ret; + `); + + + // Game.refillLump + if(!Game.customRefillLump) Game.customRefillLump = []; + CCSE.SliceCodeIntoFunction('Game.refillLump', -1, ` + // Game.refillLump injection point 0 + for(var i in Game.customRefillLump) Game.customRefillLump[i](); + `); + + + // Game.doLumps + // Runs every logic frame when lumps matter + if(!Game.customDoLumps) Game.customDoLumps = []; + CCSE.ReplaceCodeIntoFunction('Game.doLumps', 'var icon=', '// Game.doLumps injection point 0', -1); + CCSE.SliceCodeIntoFunction('Game.doLumps', -1, ` + // Game.doLumps injection point 1 + for(var i in Game.customDoLumps) Game.customDoLumps[i](); + `); + + + // ----- Economics block ----- // + + // Game.CalculateGains + // I really think this is what he meant it to be + // The original just has Game.customCps doing the same thing as Game.customCpsMult + //eval('Game.CalculateGains = ' + Game.CalculateGains.toString().replace( + // 'for (var i in Game.customCps) {mult*=Game.customCps[i]();}', + // 'for (var i in Game.customCps) {Game.cookiesPs += Game.customCps[i]();}')); + + + // Game.dropRateMult + // Return 1 to have no effect + if(!Game.customDropRateMult) Game.customDropRateMult = []; + CCSE.ReplaceCodeIntoFunction('Game.dropRateMult', 'return', + `// Game.dropRateMult injection point 0 + for(var i in Game.customDropRateMult) rate *= Game.customDropRateMult[i]();`, -1); + + + // ----- Shimmers block ----- // + + // Game.shimmer + // Runs when a shimmer (Golden cookie or reindeer) gets created + // You can push a function that pops it immediately, but it will mess up any FtHoF predictor you use + if(!Game.customShimmer) Game.customShimmer = []; + CCSE.SliceCodeIntoFunction('Game.shimmer', -1, ` + // Game.shimmer injection point 0 + for(var i in Game.customShimmer) Game.customShimmer[i](this); + `, 0, 1); + + // Game.updateShimmers + // Runs every logic frame when shimmers matter + if(!Game.customUpdateShimmers) Game.customUpdateShimmers = []; + CCSE.SliceCodeIntoFunction('Game.updateShimmers', -1, ` + // Game.updateShimmers injection point 0 + for(var i in Game.customUpdateShimmers) Game.customUpdateShimmers[i](); + `); + + + // Game.killShimmers + // Runs when we want to remove all shimmers + if(!Game.customKillShimmers) Game.customKillShimmers = []; + CCSE.SliceCodeIntoFunction('Game.killShimmers', -1, ` + // Game.killShimmers injection point 0 + for(var i in Game.customKillShimmers) Game.customKillShimmers[i](); + `); + + + // Game.shimmerTypes + if(!Game.customShimmerTypesAll) Game.customShimmerTypesAll = {}; + + if(!Game.customShimmerTypesAll.initFunc) Game.customShimmerTypesAll.initFunc = []; + CCSE.customShimmerTypesAllinitFunc = function(me){ + for(var i in Game.customShimmerTypesAll.initFunc) Game.customShimmerTypesAll.initFunc[i](me); + } + + if(!Game.customShimmerTypesAll.durationMult) Game.customShimmerTypesAll.durationMult = []; + CCSE.customShimmerTypesAlldurationMult = function(me){ + var dur = 1; + for(var i in Game.customShimmerTypesAll.durationMult) dur *= Game.customShimmerTypesAll.durationMult[i](me); + return dur; + } + + if(!Game.customShimmerTypesAll.updateFunc) Game.customShimmerTypesAll.updateFunc = []; + CCSE.customShimmerTypesAllupdateFunc = function(me){ + for(var i in Game.customShimmerTypesAll.updateFunc) Game.customShimmerTypesAll.updateFunc[i](me); + } + + if(!Game.customShimmerTypesAll.popFunc) Game.customShimmerTypesAll.popFunc = []; + CCSE.customShimmerTypesAllpopFunc = function(me){ + for(var i in Game.customShimmerTypesAll.popFunc) Game.customShimmerTypesAll.popFunc[i](me); + } + + if(!Game.customShimmerTypesAll.spawnConditions) Game.customShimmerTypesAll.spawnConditions = []; + CCSE.customShimmerTypesAllspawnConditions = function(ret){ + for(var i in Game.customShimmerTypesAll.spawnConditions) ret = Game.customShimmerTypesAll.spawnConditions[i](ret); + return ret; + } + + if(!Game.customShimmerTypesAll.getTimeMod) Game.customShimmerTypesAll.getTimeMod = []; + CCSE.customShimmerTypesAllgetTimeMod = function(me){ + var m = 1; + for(var i in Game.customShimmerTypesAll.getTimeMod) m *= Game.customShimmerTypesAll.getTimeMod[i](me); + return m; + } + + + // In these, "me" refers to the shimmer itself, and "this" to the shimmer's type object + // I put this in a separate function to call them when a new type is defined + if(!Game.customShimmerTypes) Game.customShimmerTypes = {}; + CCSE.Backup.customShimmerTypes = {}; + for(var key in Game.shimmerTypes){ + CCSE.ReplaceShimmerType(key); + } + + CCSE.SliceCodeIntoFunction("Game.playGoldenCookieChime", -1, "else CCSE.PlayShimmerSpawnSound('golden')", 0); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['golden'].initFunc", "Game.chimeType!=0", "CCSE.config.chimeType != 'No sound'", 0); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['reindeer'].initFunc", "Game.chimeType!=0", "CCSE.config.chimeType != 'No sound'", 0); + + + // Game.shimmerTypes['golden'].popFunc + // customListPush functions should push strings to list + // customEffectDurMod functions should return a multiplier to the effect's duration + // customMult functions should return a multiplier to the effect's magnitude (for Lucky, Chain Cookie, and Cookie Storm drops) + // customBuff functions should return a a buff (result from Game.gainBuff). Return buff for no effect + if(!Game.customShimmerTypes['golden'].customListPush) Game.customShimmerTypes['golden'].customListPush = []; + if(!Game.customShimmerTypes['golden'].customEffectDurMod) Game.customShimmerTypes['golden'].customEffectDurMod = []; + if(!Game.customShimmerTypes['golden'].customMult) Game.customShimmerTypes['golden'].customMult = []; + if(!Game.customShimmerTypes['golden'].customBuff) Game.customShimmerTypes['golden'].customBuff = []; + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['golden'].popFunc", 'var list=[];', + `// Game.shimmerTypes['golden'].popFunc injection point 1 + for(var i in Game.customShimmerTypes['golden'].customListPush) Game.customShimmerTypes['golden'].customListPush[i](me, list);`, 1); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['golden'].popFunc", 'var buff=0;', + `// Game.shimmerTypes['golden'].popFunc injection point 2 + for(var i in Game.customShimmerTypes['golden'].customEffectDurMod) effectDurMod *= Game.customShimmerTypes['golden'].customEffectDurMod[i](me); + for(var i in Game.customShimmerTypes['golden'].customMult) mult *= Game.customShimmerTypes['golden'].customMult[i](me); + for(var i in Game.customShimmerTypes['golden'].customBuff) buff = Game.customShimmerTypes['golden'].customBuff[i](me, buff, choice, effectDurMod, mult);`, 1); + + + // Game.shimmerTypes['reindeer'].popFunc + // customDropRateMult should return a multiplier to the fail rate for reindeer drops + // Game.customDropRateMult is already taken into account. This is for reindeer specific functions + // Return 1 to have no effect. Return 0 for a guarantee* + if(!Game.customShimmerTypes['reindeer'].customDropRateMult) Game.customShimmerTypes['reindeer'].customDropRateMult = []; + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['reindeer'].popFunc", 'if (Math.random()>failRate)', + `// Game.shimmerTypes['reindeer'].popFunc injection point 1 + for(var i in Game.customShimmerTypes['reindeer'].customDropRateMult) failRate *= Game.customShimmerTypes['reindeer'].customDropRateMult[i](me);`, -1); + + + // ----- Particles block ----- // + // Game.particleAdd + temp = Game.particleAdd.toString(); + temp = temp.replaceAll("pic='icons.png';", `if (me.picPos.length == 3) {pic=me.picPos[2];me.picId = 0;} + else pic='icons.png';`) + eval('Game.particleAdd=' + temp); + + + // ----- Notifications block ----- // + // ----- Prompts block ----- // + // ----- Menu block ----- // + // These start to get into the basic appearance of the game, and stray away from the gameplay itself + // If someone has an idea they want to try that requires hooks into these functions, I can add them then + + + // ----- Buildings block ----- // + + // Game.Object + // Alter this function so creating new buildings doesn't break the minigames + CCSE.ReplaceCodeIntoFunction('Game.Object', `str+='
';`, + `{ + var div = document.createElement('div'); + div.id = 'row'+this.id; + div.classList.add('row'); + str += '
'; + }`, 0); + CCSE.ReplaceCodeIntoFunction('Game.Object', `str+='
';`, + `div.innerHTML = str;`, 1); + CCSE.ReplaceCodeIntoFunction('Game.Object', `l('rows').innerHTML=l('rows').innerHTML+str;`, + `l('rows').appendChild(div);`, 0); + + + // Game.DrawBuildings + // Runs every draw frame if we're not ascending + if(!Game.customDrawBuildings) Game.customDrawBuildings = []; + CCSE.SliceCodeIntoFunction('Game.DrawBuildings', -1, ` + // Game.DrawBuildings injection point 0 + for(var i in Game.customDrawBuildings) Game.customDrawBuildings[i](); + `); + + + // Game.modifyBuildingPrice + // Functions should return a value to multiply the price by + // Return 1 to have no effect + if(!Game.customModifyBuildingPrice) Game.customModifyBuildingPrice = []; + CCSE.ReplaceCodeIntoFunction('Game.modifyBuildingPrice', 'return', ` + // Game.modifyBuildingPrice injection point 0 + for(var i in Game.customModifyBuildingPrice) price *= Game.customModifyBuildingPrice[i](building, price);`, -1); + + + // Game.storeBulkButton + if(!Game.customStoreBulkButton) Game.customStoreBulkButton = []; + CCSE.SliceCodeIntoFunction('Game.storeBulkButton', -1, ` + // Game.storeBulkButton injection point 0 + for(var i in Game.customStoreBulkButton) Game.customStoreBulkButton[i](); + `); + + + // Game.BuildStore + if(!Game.customBuildStore) Game.customBuildStore = []; + CCSE.SliceCodeIntoFunction('Game.BuildStore', -1, ` + // Game.BuildStore injection point 0 + for(var i in Game.customBuildStore) Game.customBuildStore[i](); + `); + + + // Game.RefreshStore + if(!Game.customRefreshStore) Game.customRefreshStore = []; + CCSE.SliceCodeIntoFunction('Game.RefreshStore', -1, ` + // Game.RefreshStore injection point 0 + for(var i in Game.customRefreshStore) Game.customRefreshStore[i](); + `); + + + // Game.scriptLoaded + if(!Game.customScriptLoaded) Game.customScriptLoaded = []; + if(!Game.customMinigameOnLoad) Game.customMinigameOnLoad = {}; + for(key in Game.Objects) if(!Game.customMinigameOnLoad[key]) Game.customMinigameOnLoad[key] = []; + + CCSE.SliceCodeIntoFunction('Game.scriptLoaded', -1, ` + // Game.scriptLoaded injection point 0 + for(var i in Game.customScriptLoaded) Game.customScriptLoaded[i](who, script); // Who knows, maybe those arguments might be needed + for(var i in Game.customMinigameOnLoad[who.name]) Game.customMinigameOnLoad[who.name][i](who, script); + `); + + + // ----- Upgrades block ----- // + + // Game.storeBuyAll + if(!Game.customStoreBuyAll) Game.customStoreBuyAll = []; + CCSE.SliceCodeIntoFunction('Game.storeBuyAll', -1, ` + // Game.storeBuyAll injection point 0 + for(var i in Game.customStoreBuyAll) Game.customStoreBuyAll[i](); + `); + + + // Game.CountsAsUpgradeOwned + // Return ret to have no effect + if(!Game.customCountsAsUpgradeOwned) Game.customCountsAsUpgradeOwned = []; + CCSE.SpliceCodeIntoFunction('Game.CountsAsUpgradeOwned', 2, 'var ret;'); + CCSE.ReplaceCodeIntoFunction('Game.CountsAsUpgradeOwned', /return/g, 'ret =', 0); + CCSE.SliceCodeIntoFunction('Game.CountsAsUpgradeOwned', -1, ` + // Game.CountsAsUpgradeOwned injection point 0 + for(var i in Game.customCountsAsUpgradeOwned) ret = Game.customCountsAsUpgradeOwned[i](pool, ret); + return ret; + `); + + + // Game.Unlock + if(!Game.customUnlock) Game.customUnlock = []; + CCSE.SliceCodeIntoFunction('Game.Unlock', -1, ` + // Game.Unlock injection point 0 + for(var i in Game.customUnlock) Game.customUnlock[i](what); + `); + + + // Game.Lock + if(!Game.customLock) Game.customLock = []; + CCSE.SliceCodeIntoFunction('Game.Lock', -1, ` + // Game.Lock injection point 0 + for(var i in Game.customLock) Game.customLock[i](what); + `); + + + // Game.RebuildUpgrades + if(!Game.customRebuildUpgrades) Game.customRebuildUpgrades = []; + CCSE.SliceCodeIntoFunction('Game.RebuildUpgrades', -1, ` + // Game.RebuildUpgrades injection point 0 + for(var i in Game.customRebuildUpgrades) Game.customRebuildUpgrades[i](); + `); + + + // Game.GetTieredCpsMult + // Functions should return a value to multiply mult by (Return 1 to have no effect) + if(!Game.customGetTieredCpsMult) Game.customGetTieredCpsMult = []; + CCSE.ReplaceCodeIntoFunction('Game.GetTieredCpsMult', 'return', ` + // Game.GetTieredCpsMult injection point 0 + for(var i in Game.customGetTieredCpsMult) mult *= Game.customGetTieredCpsMult[i](me);`, -1); + + + // Game.UnlockTiered + if(!Game.customUnlockTiered) Game.customUnlockTiered = []; + CCSE.SliceCodeIntoFunction('Game.UnlockTiered', -1, ` + // Game.UnlockTiered injection point 0 + for(var i in Game.customUnlockTiered) Game.customUnlockTiered[i](me); + `); + + + // Game.SetResearch + if(!Game.customSetResearch) Game.customSetResearch = []; + CCSE.SliceCodeIntoFunction('Game.SetResearch', -1, ` + // Game.SetResearch injection point 0 + for(var i in Game.customSetResearch) Game.customSetResearch[i](what, time); + `); + + + // Game.DropEgg + // Functions should return a value to multiply failRate by (Return 1 to have no effect) + if(!Game.customDropEgg) Game.customDropEgg = []; + CCSE.SpliceCodeIntoFunction('Game.DropEgg', 2, + `// Game.DropEgg injection point 0 + for(var i in Game.customDropEgg) failRate *= Game.customDropEgg[i]();`); + + + // Game.PermanentSlotIcon + // Functions should return an upgrade id. Return id for no effect. + if(!Game.customPermanentUpgradeId) Game.customPermanentUpgradeId = []; + Game.customPermanentUpgradeId.push(CCSE.GetPermanentUpgrade); + CCSE.SpliceCodeIntoFunction('Game.PermanentSlotIcon', 2, + `// Game.PermanentSlotIcon injection point 0 + var id = Game.permanentUpgrades[slot]; + for(var i in Game.customPermanentUpgradeId) id = Game.customPermanentUpgradeId[i](slot, id);`, + 'Game.PermanentSlotIcon=' + Game.PermanentSlotIcon.toString().replaceAll('Game.permanentUpgrades[slot]','id')); + + + // Game.AssignPermanentSlot + CCSE.ReplaceCodeIntoFunction('Game.AssignPermanentSlot', 'if (!fail)', + 'for (var ii in CCSE.config.permanentUpgrades) {if (CCSE.config.permanentUpgrades[ii]==me.name) fail=1;}', -1); + CCSE.ReplaceCodeIntoFunction('Game.AssignPermanentSlot', 'var upgrade=Game.permanentUpgrades[slot];', + 'for(var i in Game.customPermanentUpgradeId) upgrade = Game.customPermanentUpgradeId[i](slot, upgrade);', 1); + CCSE.ReplaceCodeIntoFunction('Game.AssignPermanentSlot', 'Game.BuildAscendTree();', + 'CCSE.RectifyPermanentUpgrades();Game.BuildAscendTree();', 0); + + + // Game.PutUpgradeInPermanentSlot + if(!Game.customPutUpgradeInPermanentSlot) Game.customPutUpgradeInPermanentSlot = []; + CCSE.SliceCodeIntoFunction('Game.PutUpgradeInPermanentSlot', -1, ` + // Game.PutUpgradeInPermanentSlot injection point 0 + for(var i in Game.customPutUpgradeInPermanentSlot) Game.customPutUpgradeInPermanentSlot[i](upgrade, slot); + `); + + + // Game.loseShimmeringVeil + if(!Game.customLoseShimmeringVeil) Game.customLoseShimmeringVeil = []; + CCSE.SliceCodeIntoFunction('Game.loseShimmeringVeil', -1, ` + // Game.loseShimmeringVeil injection point 0 + for(var i in Game.customLoseShimmeringVeil) Game.customLoseShimmeringVeil[i](context); + `); + + + // Game.listTinyOwnedUpgrades + if(!Game.customListTinyOwnedUpgrades) Game.customListTinyOwnedUpgrades = []; + CCSE.ReplaceCodeIntoFunction('Game.listTinyOwnedUpgrades', 'return', ` + // Game.listTinyOwnedUpgrades injection point 0 + for(var i in Game.customListTinyOwnedUpgrades) str = Game.customListTinyOwnedUpgrades[i](arr, str);`, -1); + + + // Game.TieredUpgrade + CCSE.ReplaceCodeIntoFunction('Game.TieredUpgrade', 'new Game.Upgrade', 'CCSE.NewUpgrade', 0); + + + // Game.SynergyUpgrade + CCSE.ReplaceCodeIntoFunction('Game.SynergyUpgrade', 'new Game.Upgrade', 'CCSE.NewUpgrade', 0); + + + // Game.GrandmaSynergy + CCSE.ReplaceCodeIntoFunction('Game.GrandmaSynergy', 'new Game.Upgrade', 'CCSE.NewUpgrade', 0); + + + // Game.NewUpgradeCookie + CCSE.ReplaceCodeIntoFunction('Game.NewUpgradeCookie', 'new Game.Upgrade', 'CCSE.NewUpgrade', 0, CCSE.Steam ? + `var strCookieProductionMultiplierPlus=loc("Cookie production multiplier +%1%.",'[x]'); + var getStrCookieProductionMultiplierPlus=function(x) + {return strCookieProductionMultiplierPlus.replace('[x]',x);}` : 0); + CCSE.ReplaceCodeIntoFunction('Game.NewUpgradeCookie', 'return upgrade;', 'Game.cookieUpgrades.push(upgrade);', -1, CCSE.Steam ? + `var strCookieProductionMultiplierPlus=loc("Cookie production multiplier +%1%.",'[x]'); + var getStrCookieProductionMultiplierPlus=function(x) + {return strCookieProductionMultiplierPlus.replace('[x]',x);}` : 0); + + + // Game.getVeilDefense + // Return 0 for no effect + if(!Game.customGetVeilDefense) Game.customGetVeilDefense = []; + CCSE.ReplaceCodeIntoFunction('Game.getVeilDefense', 'return', ` + // Game.getVeilDefense injection point 0 + for(var i in Game.customGetVeilDefense) n += Game.customGetVeilDefense[i](n);`, -1); + + + // Game.getVeilBoost + // Return 0 for no effect + if(!Game.customGetVeilBoost) Game.customGetVeilBoost = []; + CCSE.ReplaceCodeIntoFunction('Game.getVeilBoost', 'return', ` + // Game.getVeilBoost injection point 0 + for(var i in Game.customGetVeilBoost) n += Game.customGetVeilBoost[i](n);`, -1); + + + // ----- Seasons block ----- // + + // Game.computeSeasons + CCSE.ReplaceCodeIntoFunction('Game.computeSeasons', `Game.Notify(Game.seasons[this.season].start+'
','',this.icon,4);`, ` + // Game.computeSeasons injection point 0 + for(var i in Game.customUpgrades[this.name].buyFunction) Game.customUpgrades[this.name].buyFunction[i](this);`, 1); + + + // Game.getSeasonDuration + // Just rewrote it instead of doing the eval replace thing + // Functions should return a multiplier to the season duration + // Return 1 to have no effect + if(!Game.customGetSeasonDuration) Game.customGetSeasonDuration = []; + Game.getSeasonDuration = function(){ + var ret = Game.fps*60*60*24; + // Game.getSeasonDuration injection point 0 + for(var i in Game.customGetSeasonDuration) ret *= Game.customGetSeasonDuration[i](); + return ret; + } + + + // ----- Achievements block ----- // + + // Game.Win + if(!Game.customWin) Game.customWin = []; + CCSE.SliceCodeIntoFunction('Game.Win', -1, ` + // Game.Win injection point 0 + for(var i in Game.customWin) Game.customWin[i](what); + `); + + + // Game.TieredAchievement + CCSE.ReplaceCodeIntoFunction('Game.TieredAchievement', 'new Game.Achievement', 'CCSE.NewAchievement', 0); + + + // Game.ProductionAchievement + CCSE.ReplaceCodeIntoFunction('Game.ProductionAchievement', 'new Game.Achievement', 'CCSE.NewAchievement', 0); + + + // Game.BankAchievement + CCSE.ReplaceCodeIntoFunction('Game.BankAchievement', 'new Game.Achievement', 'CCSE.NewAchievement', 0); + + + // Game.CpsAchievement + CCSE.ReplaceCodeIntoFunction('Game.CpsAchievement', 'new Game.Achievement', 'CCSE.NewAchievement', 0); + + + // ----- Buffs block ----- // + + // Game.gainBuff + if(!Game.customGainBuff) Game.customGainBuff = []; + CCSE.ReplaceCodeIntoFunction('Game.gainBuff', 'return', ` + // Game.gainBuff injection point 0 + for(var i in Game.customGainBuff) Game.customGainBuff[i](buff);`, -1); + + + // Game.updateBuffs + // executed every logic frame + if(!Game.customUpdateBuffs) Game.customUpdateBuffs = []; + CCSE.SliceCodeIntoFunction('Game.updateBuffs', -1, ` + // Game.updateBuffs injection point 0 + for(var i in Game.customUpdateBuffs) Game.customUpdateBuffs[i](); + `); + + + for(var i in Game.buffTypes){ + var buff = Game.buffTypes[i]; + if(buff.name == 'building buff'){ + CCSE.ReplaceCodeIntoFunction('Game.buffTypes[' + i + '].func', + 'icon:[obj.iconColumn,14],', + 'icon:[obj.iconColumn,14,(obj.art.customIconsPic ? obj.art.customIconsPic : 0)],', 0); + } + else if(buff.name == 'building debuff'){ + CCSE.ReplaceCodeIntoFunction('Game.buffTypes[' + i + '].func', + 'icon:[obj.iconColumn,15],', + 'icon:[obj.iconColumn,15,(obj.art.customIconsPic ? obj.art.customIconsPic : 0)],', 0); + } + } + + + // ----- GRANDMAPOCALYPSE block ----- // + + // I need this because this gets used once and if I leave it out the game breaks + function inRect(x,y,rect) + { + //find out if the point x,y is in the rotated rectangle rect{w,h,r,o} (width,height,rotation in radians,y-origin) (needs to be normalized) + //I found this somewhere online I guess + var dx = x+Math.sin(-rect.r)*(-(rect.h/2-rect.o)),dy=y+Math.cos(-rect.r)*(-(rect.h/2-rect.o)); + var h1 = Math.sqrt(dx*dx + dy*dy); + var currA = Math.atan2(dy,dx); + var newA = currA - rect.r; + var x2 = Math.cos(newA) * h1; + var y2 = Math.sin(newA) * h1; + if (x2 > -0.5 * rect.w && x2 < 0.5 * rect.w && y2 > -0.5 * rect.h && y2 < 0.5 * rect.h) return true; + return false; + } + + // Game.UpdateGrandmapocalypse + // executed every logic frame + if(!Game.customUpdateGrandmapocalypse) Game.customUpdateGrandmapocalypse = []; + CCSE.SliceCodeIntoFunction('Game.UpdateGrandmapocalypse', -1, ` + // Game.UpdateGrandmapocalypse injection point 0 + for(var i in Game.customUpdateGrandmapocalypse) Game.customUpdateGrandmapocalypse[i](); + `); + + + // Game.getWrinklersMax + // Functions should return a value to add to n. Return 0 to have no effect + if(!Game.customGetWrinklersMax) Game.customGetWrinklersMax = []; + CCSE.ReplaceCodeIntoFunction('Game.getWrinklersMax', 'return', ` + // Game.getWrinklersMax injection point 0 + for(var i in Game.customGetWrinklersMax) n += Game.customGetWrinklersMax[i](n);`, -1); + + + // Game.SpawnWrinkler + if(!Game.customSpawnWrinkler) Game.customSpawnWrinkler = []; + CCSE.ReplaceCodeIntoFunction('Game.SpawnWrinkler', 'return me', ` + // Game.SpawnWrinkler injection point 0 + for(var i in Game.customSpawnWrinkler) Game.customSpawnWrinkler[i](me);`, -1); + + + // Game.UpdateWrinklers + // customWrinklerSpawnChance functions should return a multiplier to chance. (Return 1 to have no effect) + if(!Game.customUpdateWrinklers) Game.customUpdateWrinklers = []; + if(!Game.customWrinklerSpawnChance) Game.customWrinklerSpawnChance = []; + if(!Game.customWrinklerPop) Game.customWrinklerPop = []; + CCSE.ReplaceCodeIntoFunction('Game.UpdateWrinklers', 'if (Math.random()) + // Pics are 96px by 96px + if(!Game.customToggleSpecialMenu) Game.customToggleSpecialMenu = []; + CCSE.ReplaceCodeIntoFunction('Game.ToggleSpecialMenu', "l('specialPopup').innerHTML=str;", + `// Game.ToggleSpecialMenu injection point 0 + for(var i in Game.customToggleSpecialMenu) str = Game.customToggleSpecialMenu[i](str);`, -1); + + + // Game.DrawSpecial + // customDrawSpecialPic functions should alter the picframe object + // Pics are 96px by 96px + if(!Game.customDrawSpecial) Game.customDrawSpecial = []; + if(!Game.customDrawSpecialPic) Game.customDrawSpecialPic = []; + CCSE.ReplaceCodeIntoFunction('Game.DrawSpecial', "if (hovered || selected)", + `// Game.DrawSpecial injection point 0 + var picframe = {pic:pic, frame:frame}; + for(var j in Game.customDrawSpecialPic) Game.customDrawSpecialPic[j](picframe, Game.specialTabs[i]); + pic = picframe.pic; frame = picframe.frame;`, -1); + CCSE.SliceCodeIntoFunction('Game.DrawSpecial', -1, ` + // Game.DrawSpecial injection point 1 + for(var i in Game.customDrawSpecial) Game.customDrawSpecial[i](); + `); + + + // ----- Visual Effects block ----- // + + // Game.DrawBackground + // Game.customDrawBackground functions get called in the same block that creates the cookie rain and seasonal backgrounds + // If you want a hook somewhere else, let me know + if(!Game.customDrawBackground) Game.customDrawBackground = []; + CCSE.ReplaceCodeIntoFunction('Game.DrawBackground', "Timer.track('left background');", + `// Game.DrawBackground injection point 0 + for(var i in Game.customDrawBackground) Game.customDrawBackground[i]();`, -1); + + // Setup for custom Milk Selector options + CCSE.ReplaceCodeIntoFunction('Game.DrawBackground', "if (Game.milkType!=0 && Game.ascensionMode!=1) pic=Game.AllMilks[Game.milkType].pic;", + 'if (CCSE.config.milkType!="Automatic" && Game.ascensionMode!=1) pic=CCSE.GetSelectedMilk().milk.pic;', 0); + + // Setup for custom Background Selector options + temp = Game.DrawBackground.toString(); + temp = temp.replace("Game.bg+'.jpg'", 'Game.bg'); + temp = temp.replace("Game.bgFade+'.jpg'", 'Game.bgFade'); + temp = temp.replace("Game.BGsByChoice[Game.bgType]", 'choice'); + temp = temp.replace("if (Game.bgType!=0 && Game.ascensionMode!=1)", + `Game.bg += '.jpg'; + Game.bgFade += '.jpg'; + + if (Game.ascensionMode!=1) + { + let choice = CCSE.GetSelectedBackground(); + if(choice.name != loc('Automatic'))`); + temp = temp.replace("Game.Background.fillPattern(Pic(Game.bg)", + `} + Game.Background.fillPattern(Pic(Game.bg)`); + eval('Game.DrawBackground = ' + temp); + for(var i in Game.BGsByChoice) Game.BGsByChoice[i].pic += '.jpg'; + + + // ----- Debug block ----- // + + // Game.OpenSesame + // Game.customOpenSesame functions should add HTML strings to the debug menu + if(!Game.customOpenSesame) Game.customOpenSesame = []; + CCSE.ReplaceCodeIntoFunction('Game.OpenSesame', "str+='
';", + `// Game.OpenSesame injection point 0 + for(var i in Game.customOpenSesame) str += Game.customOpenSesame[i]();`, -1); + + + // ----- YouCustomizer block ----- // + + // Game.YouCustomizer.render + if(!Game.customYouCustomizerRender) Game.customYouCustomizerRender = []; + CCSE.SliceCodeIntoFunction('Game.YouCustomizer.render', -1, ` + // Game.YouCustomizer.render injection point 0 + for(var i in Game.customYouCustomizerRender) Game.customYouCustomizerRender[i](); + `); + + + // Game.YouCustomizer.getGeneValue + // Return retVal to have no effect + temp = Game.YouCustomizer.getGeneValue.toString(); + temp = temp.replace('var gene=', 'var retVal;\r\nvar gene='); + temp = temp.replaceAll('return', 'retVal ='); + eval('Game.YouCustomizer.getGeneValue = ' + temp); + if(!Game.customYouCustomizerGetGeneValue) Game.customYouCustomizerGetGeneValue = []; + CCSE.SliceCodeIntoFunction('Game.YouCustomizer.getGeneValue', -1, ` + // Game.YouCustomizer.getGeneValue injection point 0 + for(var i in Game.customYouCustomizerGetGeneValue) retVal = Game.customYouCustomizerGetGeneValue[i](id, retVal); + return retVal; + `); + + + // Game.YouCustomizer.offsetGene + if(!Game.customYouCustomizerOffsetGene) Game.customYouCustomizerOffsetGene = []; + CCSE.SliceCodeIntoFunction('Game.YouCustomizer.offsetGene', -1, ` + // Game.YouCustomizer.offsetGene injection point 0 + for(var i in Game.customYouCustomizerOffsetGene) Game.customYouCustomizerOffsetGene[i](gene,off); + `); + + + // Game.YouCustomizer.randomize + if(!Game.customYouCustomizerRandomize) Game.customYouCustomizerRandomize = []; + CCSE.ReplaceCodeIntoFunction('Game.YouCustomizer.randomize', "Game.YouCustomizer.render();", + `// Game.YouCustomizer.randomize injection point 0 + for(var i in Game.customYouCustomizerRandomize) str = Game.customYouCustomizerRandomize[i]();`, -1); + + + // Game.YouCustomizer.renderPortrait + if(!Game.customYouCustomizerRenderPortrait) Game.customYouCustomizerRenderPortrait = []; + CCSE.SliceCodeIntoFunction('Game.YouCustomizer.renderPortrait', -1, ` + // Game.YouCustomizer.renderPortrait injection point 0 + for(var i in Game.customYouCustomizerRenderPortrait) Game.customYouCustomizerRenderPortrait[i](); + `); + + + // Game.YouCustomizer.prompt + if(!Game.customYouCustomizerPrompt) Game.customYouCustomizerPrompt = []; + if(!Game.customYouCustomizerMakeCustomizerSelector) Game.customYouCustomizerMakeCustomizerSelector = []; + temp = Game.YouCustomizer.prompt.toString(); + temp = temp.replace('return', 'var retVal ='); + temp = temp.replace('}', ` + // Game.YouCustomizer.prompt injection point 0 + for(var i in Game.customYouCustomizerMakeCustomizerSelector) retVal = Game.customYouCustomizerMakeCustomizerSelector[i](gene,text,retVal); + return retVal; + }`); + eval('Game.YouCustomizer.prompt = ' + temp); + CCSE.SliceCodeIntoFunction('Game.YouCustomizer.prompt', -1, ` + // Game.YouCustomizer.prompt injection point 1 + for(var i in Game.customYouCustomizerPrompt) Game.customYouCustomizerPrompt[i](); + `); + + + // ----- Gifting block ----- // + // Game.promptGiftRedeem + // Game.promptGiftSend + // Submit an issue to the GitHub page with where you want a hook + // Until that happens, these functions won't either + + } + + if(!CCSE.customReplaceShimmerType) CCSE.customReplaceShimmerType = []; + CCSE.ReplaceShimmerType = function(key){ + var temp = ''; + var pos = 0; + var proto; + var escKey = key.replace(/'/g, "\\'"); + + if(!Game.customShimmerTypes[key]) Game.customShimmerTypes[key] = {}; + CCSE.Backup.customShimmerTypes[key] = {}; + + + // Game.shimmerTypes[key].initFunc + // durationMult functions should return a value to multiply the duration by + if(!Game.customShimmerTypes[key].initFunc) Game.customShimmerTypes[key].initFunc = []; + if(!Game.customShimmerTypes[key].durationMult) Game.customShimmerTypes[key].durationMult = []; + Game.customShimmerTypes[key].initFunc.push(CCSE.customShimmerTypesAllinitFunc); + Game.customShimmerTypes[key].durationMult.push(CCSE.customShimmerTypesAlldurationMult); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].initFunc", 'me.dur=dur;', + `// Game.shimmerTypes['` + escKey + `'].initFunc injection point 0 + for(var i in Game.customShimmerTypes['` + escKey + `'].durationMult) dur *= Game.customShimmerTypes['` + escKey + `'].durationMult[i](me);`, -1); + CCSE.SliceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].initFunc", -1, ` + // Game.shimmerTypes['` + escKey + `'].initFunc injection point 1 + for(var i in Game.customShimmerTypes['` + escKey + `'].initFunc) Game.customShimmerTypes['` + escKey + `'].initFunc[i](me); + `); + //CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].initFunc", 'Game.chimeType==1 && ', '', 0); + + + // Game.shimmerTypes[key].updateFunc + if(!Game.customShimmerTypes[key].updateFunc) Game.customShimmerTypes[key].updateFunc = []; + Game.customShimmerTypes[key].updateFunc.push(CCSE.customShimmerTypesAllupdateFunc); + CCSE.SliceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].updateFunc", -1, ` + // Game.shimmerTypes['` + escKey + `'].updateFunc injection point 0 + for(var i in Game.customShimmerTypes['` + escKey + `'].updateFunc) Game.customShimmerTypes['` + escKey + `'].updateFunc[i](me); + `); + + + // Game.shimmerTypes[key].popFunc + if(!Game.customShimmerTypes[key].popFunc) Game.customShimmerTypes[key].popFunc = []; + Game.customShimmerTypes[key].popFunc.push(CCSE.customShimmerTypesAllpopFunc); + CCSE.SliceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].popFunc", -1, ` + // Game.shimmerTypes['` + escKey + `'].popFunc injection point 0 + for(var i in Game.customShimmerTypes['` + escKey + `'].popFunc) Game.customShimmerTypes['` + escKey + `'].popFunc[i](me); + `); + + + // Game.shimmerTypes[key].spawnConditions + // Return ret to have no effect + if(!Game.customShimmerTypes[key].spawnConditions) Game.customShimmerTypes[key].spawnConditions = []; + Game.customShimmerTypes[key].spawnConditions.push(CCSE.customShimmerTypesAllspawnConditions); + CCSE.SpliceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].spawnConditions", 2, 'var ret;'); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].spawnConditions", /return/g, 'ret =', 0); + CCSE.SliceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].spawnConditions", -1, ` + // Game.shimmerTypes['` + escKey + `'].spawnConditions injection point 0 + for(var i in Game.customShimmerTypes['` + escKey + `'].spawnConditions) ret = Game.customShimmerTypes['` + escKey + `'].spawnConditions[i](ret); + return ret; + `); + + + // Game.shimmerTypes[key].getTimeMod + // Functions should return a multiplier to the shimmer's spawn time (higher takes longer to spawn) + // Return 1 to have no effect + if(!Game.customShimmerTypes[key].getTimeMod) Game.customShimmerTypes[key].getTimeMod = []; + Game.customShimmerTypes[key].getTimeMod.push(CCSE.customShimmerTypesAllgetTimeMod); + CCSE.ReplaceCodeIntoFunction("Game.shimmerTypes['" + escKey + "'].getTimeMod", 'return', ` + // Game.shimmerTypes['` + escKey + `'].getTimeMod injection point 0 + for(var i in Game.customShimmerTypes['` + escKey + `'].getTimeMod) m *= Game.customShimmerTypes['` + escKey + `'].getTimeMod[i](me);`, -1); + + + for(var i in CCSE.customReplaceShimmerType) CCSE.customReplaceShimmerType[i](key); + } + + if(!CCSE.customReplaceBuilding) CCSE.customReplaceBuilding = []; + CCSE.ReplaceBuildingsStart = function(){ + if(!Game.customBuildingsAll) Game.customBuildingsAll = {}; + + if(!Game.customBuildingsAll.switchMinigame) Game.customBuildingsAll.switchMinigame = []; + CCSE.customBuildingsAllswitchMinigame = function(obj, on){ + for(var i in Game.customBuildingsAll.switchMinigame) Game.customBuildingsAll.switchMinigame[i](obj, on); + } + + if(!Game.customBuildingsAll.getSellMultiplier) Game.customBuildingsAll.getSellMultiplier = []; + CCSE.customBuildingsAllgetSellMultiplier = function(obj, giveBack){ + for(var i in Game.customBuildingsAll.getSellMultiplier) giveBack = Game.customBuildingsAll.getSellMultiplier[i](obj, giveBack); + return giveBack; + } + + if(!Game.customBuildingsAll.buy) Game.customBuildingsAll.buy = []; + CCSE.customBuildingsAllbuy = function(obj, amount){ + for(var i in Game.customBuildingsAll.buy) Game.customBuildingsAll.buy[i](obj, amount); + } + + if(!Game.customBuildingsAll.sell) Game.customBuildingsAll.sell = []; + CCSE.customBuildingsAllsell = function(obj, amount, bypass){ + for(var i in Game.customBuildingsAll.sell) Game.customBuildingsAll.sell[i](obj, amount, bypass); + } + + if(!Game.customBuildingsAll.sacrifice) Game.customBuildingsAll.sacrifice = []; + CCSE.customBuildingsAllsacrifice = function(obj, amount){ + for(var i in Game.customBuildingsAll.sacrifice) Game.customBuildingsAll.sacrifice[i](obj, amount); + } + + if(!Game.customBuildingsAll.buyFree) Game.customBuildingsAll.buyFree = []; + CCSE.customBuildingsAllbuyFree = function(obj, amount){ + for(var i in Game.customBuildingsAll.buyFree) Game.customBuildingsAll.buyFree[i](obj, amount); + } + + if(!Game.customBuildingsAll.getFree) Game.customBuildingsAll.getFree = []; + CCSE.customBuildingsAllgetFree = function(obj, amount){ + for(var i in Game.customBuildingsAll.getFree) Game.customBuildingsAll.getFree[i](obj, amount); + } + + if(!Game.customBuildingsAll.getFreeRanks) Game.customBuildingsAll.getFreeRanks = []; + CCSE.customBuildingsAllgetFreeRanks = function(obj, amount){ + for(var i in Game.customBuildingsAll.getFreeRanks) Game.customBuildingsAll.getFreeRanks[i](obj, amount); + } + + if(!Game.customBuildingsAll.tooltip) Game.customBuildingsAll.tooltip = []; + CCSE.customBuildingsAlltooltip = function(obj, ret){ + for(var i in Game.customBuildingsAll.tooltip) ret = Game.customBuildingsAll.tooltip[i](obj, ret); + return ret; + } + + if(!Game.customBuildingsAll.levelTooltip) Game.customBuildingsAll.levelTooltip = []; + CCSE.customBuildingsAlllevelTooltip = function(obj, ret){ + for(var i in Game.customBuildingsAll.levelTooltip) ret = Game.customBuildingsAll.levelTooltip[i](obj, ret); + return ret; + } + + if(!Game.customBuildingsAll.refresh) Game.customBuildingsAll.refresh = []; + CCSE.customBuildingsAllrefresh = function(obj){ + for(var i in Game.customBuildingsAll.refresh) Game.customBuildingsAll.refresh[i](obj); + } + + if(!Game.customBuildingsAll.rebuild) Game.customBuildingsAll.rebuild = []; + CCSE.customBuildingsAllrebuild = function(obj){ + for(var i in Game.customBuildingsAll.rebuild) Game.customBuildingsAll.rebuild[i](obj); + } + + if(!Game.customBuildingsAll.mute) Game.customBuildingsAll.mute = []; + CCSE.customBuildingsAllmute = function(obj, val){ + for(var i in Game.customBuildingsAll.mute) Game.customBuildingsAll.mute[i](obj, val); + } + + if(!Game.customBuildingsAll.draw) Game.customBuildingsAll.draw = []; + CCSE.customBuildingsAlldraw = function(obj){ + for(var i in Game.customBuildingsAll.draw) Game.customBuildingsAll.draw[i](obj); + } + + if(!Game.customBuildingsAll.buyFunction) Game.customBuildingsAll.buyFunction = []; + CCSE.customBuildingsAllbuyFunction = function(obj){ + for(var i in Game.customBuildingsAll.buyFunction) Game.customBuildingsAll.buyFunction[i](obj); + } + + if(!Game.customBuildingsAll.cpsMult) Game.customBuildingsAll.cpsMult = []; + CCSE.customBuildingsAllcpsMult = function(obj){ + var mult = 1; + for(var i in Game.customBuildingsAll.cpsMult) mult *= Game.customBuildingsAll.cpsMult[i](obj); + return mult; + } + + + if(!Game.customBuildings) Game.customBuildings = {}; + CCSE.Backup.customBuildings = {}; + CCSE.i = 0; + } + + CCSE.ReplaceBuildings = function(){ + var time = Date.now(); + + for(var i = CCSE.i; i < Game.ObjectsN; i++){ + CCSE.ReplaceBuilding(Game.ObjectsById[i].name); + if(Date.now() > time + 500 / Game.fps) break; + } + + CCSE.i = i + 1; + if(CCSE.i < Game.ObjectsN){ + // Didn't do all of them. Wait for priority and go again + requestAnimationFrame(CCSE.ReplaceBuildings); + }else{ + // Continue on + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + } + } + + CCSE.ReplaceBuildingsFinish = function(){ + + // ----- Individual Buildings block ----- // + + var obj = Game.Objects['Cursor']; + // Cursor.cps + // cpsAdd Functions should return a value to add per non cursor building (Return 0 to have no effect) + if(!Game.customBuildings[obj.name].cpsAdd) Game.customBuildings[obj.name].cpsAdd = []; + if(!Game.customBuildings[obj.name].cpsMult) Game.customBuildings[obj.name].cpsMult = []; + CCSE.ReplaceCodeIntoFunction("Game.Objects['Cursor'].cps", 'var mult=1;', ` + // Cursor.cps injection point 1 + for(var i in Game.customBuildings['` + obj.name + `'].cpsAdd) add += Game.customBuildings['` + obj.name + `'].cpsAdd[i](me);`, -1); + + + obj = Game.Objects['Grandma']; + // Grandma.art.pic + // Functions should push an image name (sans the .png part) into list + if(!Game.customGrandmaPicture) Game.customGrandmaPicture = []; + CCSE.ReplaceCodeIntoFunction("Game.Objects['Grandma'].art.pic", 'return', ` + // Grandma.art.pic injection point 0 + for(var j in Game.customGrandmaPicture) Game.customGrandmaPicture[j](i, list);`, -1); + + + // Grandma.cps + // cpsAdd Functions should return a value to add before multiplying (Return 0 to have no effect) + if(!Game.customBuildings[obj.name].cpsAdd) Game.customBuildings[obj.name].cpsAdd = []; + if(!Game.customBuildings[obj.name].cpsMult) Game.customBuildings[obj.name].cpsMult = []; + CCSE.ReplaceCodeIntoFunction("Game.Objects['Grandma'].cps", 'return', ` + // Grandma.cps injection point 1 + for(var i in Game.customBuildings['` + obj.name + `'].cpsAdd) add += Game.customBuildings['` + obj.name + `'].cpsAdd[i](me);`, -1); + + } + + CCSE.ReplaceBuilding = function(key){ + // A lot of Copy/Paste happened, hence why I did so many functions. + // Also, I may not have fully tested each one. + var temp = ''; + var pos = 0; + var proto; + var escKey = key.replace(/'/g, "\\'"); + var obj = Game.Objects[key]; + + if(!Game.customBuildings[key]) Game.customBuildings[key] = {}; + CCSE.Backup.customBuildings[key] = {}; + + // this.switchMinigame + if(!Game.customBuildings[key].switchMinigame) Game.customBuildings[key].switchMinigame = []; + Game.customBuildings[key].switchMinigame.push(CCSE.customBuildingsAllswitchMinigame); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].switchMinigame", -1, ` + // Game.Objects['` + escKey + `'].switchMinigame injection point 0 + for(var i in Game.customBuildings[this.name].switchMinigame) Game.customBuildings[this.name].switchMinigame[i](this, on); + `); + + + // this.getSellMultiplier + // Return ret to have no effect + if(!Game.customBuildings[key].getSellMultiplier) Game.customBuildings[key].getSellMultiplier = []; + Game.customBuildings[key].getSellMultiplier.push(CCSE.customBuildingsAllgetSellMultiplier); + CCSE.ReplaceCodeIntoFunction("Game.Objects['" + escKey + "'].getSellMultiplier", 'return', ` + // Game.Objects['` + escKey + `'].getSellMultiplier injection point 0 + for(var i in Game.customBuildings[this.name].getSellMultiplier) giveBack = Game.customBuildings[this.name].getSellMultiplier[i](this, giveBack);`, -1); + + + // this.buy + if(!Game.customBuildings[key].buy) Game.customBuildings[key].buy = []; + Game.customBuildings[key].buy.push(CCSE.customBuildingsAllbuy); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].buy", -1, ` + // Game.Objects['` + escKey + `'].buy injection point 0 + for(var i in Game.customBuildings[this.name].buy) Game.customBuildings[this.name].buy[i](this, amount); + `); + + + // this.sell + if(!Game.customBuildings[key].sell) Game.customBuildings[key].sell = []; + Game.customBuildings[key].sell.push(CCSE.customBuildingsAllsell); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].sell", -1, ` + // Game.Objects['` + escKey + `'].sell injection point 0 + for(var i in Game.customBuildings[this.name].sell) Game.customBuildings[this.name].sell[i](this, amount, bypass); + `); + + + // this.sacrifice + if(!Game.customBuildings[key].sacrifice) Game.customBuildings[key].sacrifice = []; + Game.customBuildings[key].sacrifice.push(CCSE.customBuildingsAllsacrifice); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].sacrifice", -1, ` + // Game.Objects['` + escKey + `'].sacrifice injection point 0 + for(var i in Game.customBuildings[this.name].sacrifice) Game.customBuildings[this.name].sacrifice[i](this, amount); + `); + + + // this.buyFree + if(!Game.customBuildings[key].buyFree) Game.customBuildings[key].buyFree = []; + Game.customBuildings[key].buyFree.push(CCSE.customBuildingsAllbuyFree); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].buyFree", -1, ` + // Game.Objects['` + escKey + `'].buyFree injection point 0 + for(var i in Game.customBuildings[this.name].buyFree) Game.customBuildings[this.name].buyFree[i](this, amount); + `, 'var price = Game.Objects["' + escKey + '"].basePrice'); + + + // this.getFree + if(!Game.customBuildings[key].getFree) Game.customBuildings[key].getFree = []; + Game.customBuildings[key].getFree.push(CCSE.customBuildingsAllgetFree); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].getFree", -1, ` + // Game.Objects['` + escKey + `'].getFree injection point 0 + for(var i in Game.customBuildings[this.name].getFree) Game.customBuildings[this.name].getFree[i](this, amount); + `); + + + // this.getFreeRanks + if(!Game.customBuildings[key].getFreeRanks) Game.customBuildings[key].getFreeRanks = []; + Game.customBuildings[key].getFreeRanks.push(CCSE.customBuildingsAllgetFreeRanks); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].getFreeRanks", -1, ` + // Game.Objects['` + escKey + `'].getFreeRanks injection point 0 + for(var i in Game.customBuildings[this.name].getFreeRanks) Game.customBuildings[this.name].getFreeRanks[i](this, amount); + `); + + + // this.tooltip + // Return ret to have no effect + if(!Game.customBuildings[key].tooltip) Game.customBuildings[key].tooltip = []; + Game.customBuildings[key].tooltip.push(CCSE.customBuildingsAlltooltip); + CCSE.ReplaceCodeIntoFunction("Game.Objects['" + escKey + "'].tooltip", 'return', 'var ret =', 0); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].tooltip", -1, ` + // Game.Objects['` + escKey + `'].tooltip injection point 0 + for(var i in Game.customBuildings[this.name].tooltip) ret = Game.customBuildings[this.name].tooltip[i](this, ret); + return ret; + `); + + + // this.levelTooltip + // Return ret to have no effect + if(!Game.customBuildings[key].levelTooltip) Game.customBuildings[key].levelTooltip = []; + Game.customBuildings[key].levelTooltip.push(CCSE.customBuildingsAlllevelTooltip); + CCSE.ReplaceCodeIntoFunction("Game.Objects['" + escKey + "'].levelTooltip", 'return', 'var ret =', 0); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].levelTooltip", -1, ` + // Game.Objects['` + escKey + `'].levelTooltip injection point 0 + for(var i in Game.customBuildings[this.name].levelTooltip) ret = Game.customBuildings[this.name].levelTooltip[i](this, ret); + return ret; + `); + + + // this.levelUp + // Haha no. This is like four functions that return each other + // I'm not dealing with it unless I have to. + + + // this.refresh + if(!Game.customBuildings[key].refresh) Game.customBuildings[key].refresh = []; + Game.customBuildings[key].refresh.push(CCSE.customBuildingsAllrefresh); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].refresh", -1, ` + // Game.Objects['` + escKey + `'].refresh injection point 0 + for(var i in Game.customBuildings[this.name].refresh) Game.customBuildings[this.name].refresh[i](this); + `); + + + // this.rebuild + if(!Game.customBuildings[key].rebuild) Game.customBuildings[key].rebuild = []; + Game.customBuildings[key].rebuild.push(CCSE.customBuildingsAllrebuild); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].rebuild", -1, ` + // Game.Objects['` + escKey + `'].rebuild injection point 0 + for(var i in Game.customBuildings[this.name].rebuild) Game.customBuildings[this.name].rebuild[i](this); + `); + + + // this.mute + if(!Game.customBuildings[key].mute) Game.customBuildings[key].mute = []; + Game.customBuildings[key].mute.push(CCSE.customBuildingsAllmute); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].mute", -1, ` + // Game.Objects['` + escKey + `'].mute injection point 0 + for(var i in Game.customBuildings[this.name].mute) Game.customBuildings[this.name].mute[i](this, val); + `); + + + // this.draw + if(!Game.customBuildings[key].draw) Game.customBuildings[key].draw = []; + Game.customBuildings[key].draw.push(CCSE.customBuildingsAlldraw); + if(key == 'Cursor'){ // Because cursors are special + Game.Objects[key].draw = function(){ + // Game.Objects['Cursor'].draw injection point 0 + for(var i in Game.customBuildings[this.name].draw) Game.customBuildings[this.name].draw[i](this); + } + } + else{ + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].draw", -1, ` + // Game.Objects['` + escKey + `'].draw injection point 0 + for(var i in Game.customBuildings[this.name].draw) Game.customBuildings[this.name].draw[i](this); + `); + } + + + + // this.buyFunction + if(!Game.customBuildings[key].buyFunction) Game.customBuildings[key].buyFunction = []; + Game.customBuildings[key].buyFunction.push(CCSE.customBuildingsAllbuyFunction); + CCSE.SliceCodeIntoFunction("Game.Objects['" + escKey + "'].buyFunction", -1, ` + // Game.Objects['` + escKey + `'].buyFunction injection point 0 + for(var i in Game.customBuildings[this.name].buyFunction) Game.customBuildings[this.name].buyFunction[i](this); + `); + + + // this.cps + // cpsMult Functions should return a value to multiply the price by (Return 1 to have no effect) + if(!Game.customBuildings[obj.name].cpsMult) Game.customBuildings[obj.name].cpsMult = []; + Game.customBuildings[key].cpsMult.push(CCSE.customBuildingsAllcpsMult); + CCSE.ReplaceCodeIntoFunction("Game.Objects['" + escKey + "'].cps", 'return', ` + // Game.Objects['` + escKey + `'].cps injection point 0 + for(var i in Game.customBuildings[this.name].cpsMult) mult *= Game.customBuildings[this.name].cpsMult[i](me); + `, -1); + + + for(var i in CCSE.customReplaceBuilding) CCSE.customReplaceBuilding[i](key, obj); + } + + if(!CCSE.customReplaceUpgrade) CCSE.customReplaceUpgrade = []; + CCSE.ReplaceUpgradesStart = function(){ + if(!Game.customUpgradesAll) Game.customUpgradesAll = {}; + + if(!Game.customUpgradesAll.getPrice) Game.customUpgradesAll.getPrice = []; + CCSE.customUpgradesAllgetPrice = function(me){ + var ret = 1 + for(var i in Game.customUpgradesAll.getPrice) ret *= Game.customUpgradesAll.getPrice[i](me); + return ret; + } + + if(!Game.customUpgradesAll.click) Game.customUpgradesAll.click = []; + CCSE.customUpgradesAllclick = function(me, e){ + for(var i in Game.customUpgradesAll.click) Game.customUpgradesAll.click[i](me, e); + } + + if(!Game.customUpgradesAll.buy) Game.customUpgradesAll.buy = []; + CCSE.customUpgradesAllbuy = function(me, bypass, success){ + for(var i in Game.customUpgradesAll.buy) Game.customUpgradesAll.buy[i](me, bypass, success); + } + + if(!Game.customUpgradesAll.earn) Game.customUpgradesAll.earn = []; + CCSE.customUpgradesAllearn = function(me){ + for(var i in Game.customUpgradesAll.earn) Game.customUpgradesAll.earn[i](me); + } + + if(!Game.customUpgradesAll.unearn) Game.customUpgradesAll.unearn = []; + CCSE.customUpgradesAllunearn = function(me){ + for(var i in Game.customUpgradesAll.unearn) Game.customUpgradesAll.unearn[i](me); + } + + if(!Game.customUpgradesAll.unlock) Game.customUpgradesAll.unlock = []; + CCSE.customUpgradesAllunlock = function(me){ + for(var i in Game.customUpgradesAll.unlock) Game.customUpgradesAll.unlock[i](me); + } + + if(!Game.customUpgradesAll.lose) Game.customUpgradesAll.lose = []; + CCSE.customUpgradesAlllose = function(me){ + for(var i in Game.customUpgradesAll.lose) Game.customUpgradesAll.lose[i](me); + } + + if(!Game.customUpgradesAll.toggle) Game.customUpgradesAll.toggle = []; + CCSE.customUpgradesAlltoggle = function(me){ + for(var i in Game.customUpgradesAll.toggle) Game.customUpgradesAll.toggle[i](me); + } + + if(!Game.customUpgradesAll.buyFunction) Game.customUpgradesAll.buyFunction = []; + CCSE.customUpgradesAllbuyFunction = function(me){ + for(var i in Game.customUpgradesAll.buyFunction) Game.customUpgradesAll.buyFunction[i](me); + } + + if(!Game.customUpgradesAll.descFunc) Game.customUpgradesAll.descFunc = []; + CCSE.customUpgradesAlldescFunc = function(me, desc){ + for(var i in Game.customUpgradesAll.descFunc) desc = Game.customUpgradesAll.descFunc[i](me, desc); + return desc; + } + + + if(!Game.customUpgrades) Game.customUpgrades = {}; + CCSE.Backup.customUpgrades = {}; + CCSE.i = 0; + } + + CCSE.ReplaceUpgrades = function(){ + var time = Date.now(); + + for(var i = CCSE.i; i < Game.UpgradesN; i++){ + CCSE.ReplaceUpgrade(Game.UpgradesById[i].name); + if(Date.now() > time + 500 / Game.fps) break; + } + + CCSE.i = i + 1; + if(CCSE.i < Game.UpgradesN){ + // Didn't do all of them. Wait for priority and go again + requestAnimationFrame(CCSE.ReplaceUpgrades); + }else{ + // Continue on + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + } + } + + CCSE.ReplaceUpgradesFinish = function(){ + // this.getPrice + // Functions should return a value to multiply the price by (Return 1 to have no effect) + CCSE.ReplaceCodeIntoFunction("Game.Upgrade.prototype.getPrice", 'return Math', ` + // Game.Upgrade.prototype.getPrice injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].getPrice) price *= Game.customUpgrades[this.name].getPrice[i](this);`, -1); + + // this.click + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.click", -1, ` + // Game.Upgrade.prototype.click injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].click) Game.customUpgrades[this.name].click[i](this, e); + `); + + // this.buy + CCSE.ReplaceCodeIntoFunction("Game.Upgrade.prototype.buy", 'return success', ` + // Game.Upgrade.prototype.buy injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].buy) Game.customUpgrades[this.name].buy[i](this, bypass, success);`, -1); + + // this.earn + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.earn", -1, ` + // Game.Upgrade.prototype.earn injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].earn) Game.customUpgrades[this.name].earn[i](this); + `); + + // this.unearn + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.unearn", -1, ` + // Game.Upgrade.prototype.unearn injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].unearn) Game.customUpgrades[this.name].unearn[i](this); + `); + + // this.unlock + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.unlock", -1, ` + // Game.Upgrade.prototype.unlock injection point 0 + if(Game.customUpgrades[this.name]) if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].unlock) Game.customUpgrades[this.name].unlock[i](this); + `); + + // this.lose + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.lose", -1, ` + // Game.Upgrade.prototype.lose injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].lose) Game.customUpgrades[this.name].lose[i](this); + `); + + // this.toggle + CCSE.SliceCodeIntoFunction("Game.Upgrade.prototype.toggle", -1, ` + // Game.Upgrade.prototype.toggle injection point 0 + if(Game.customUpgrades[this.name]) for(var i in Game.customUpgrades[this.name].toggle) Game.customUpgrades[this.name].toggle[i](this); + `); + + // this.isVaulted + CCSE.SpliceCodeIntoFunction("Game.Upgrade.prototype.isVaulted", 2, ` + // Game.Upgrade.prototype.isVaulted injection point 0 + if (CCSE.config.vault.indexOf(this.name)!=-1) return true; + `); + + // this.vault + CCSE.ReplaceCodeIntoFunction("Game.Upgrade.prototype.vault", 'Game.vault', ` + if(this.CCSE) CCSE.config.vault.push(this.name); + else `, -1); + + // this.unvault + CCSE.ReplaceCodeIntoFunction("Game.Upgrade.prototype.unvault", 'Game.vault', ` + if(this.CCSE) CCSE.config.vault.splice(CCSE.config.vault.indexOf(this.name),1); + else `, -1); + + + // Golden cookie sound selector custom options + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Golden cookie sound selector'].olddescFunc", "this.choicesFunction()[Game.chimeType]", "CCSE.GetSelectedShimmerSound()", 0); + //CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Golden cookie sound selector'].olddescFunc", "'+icon[2]+'", "'+choice.icon[2]+'", 0); + + // Game.Upgrades['Golden cookie sound selector'].choicesFunction + if(!Game.customUpgrades['Golden cookie sound selector'].choicesFunction) Game.customUpgrades['Golden cookie sound selector'].choicesFunction = []; + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Golden cookie sound selector'].choicesFunction", "return choices;", + `// Game.customUpgrades['Golden cookie sound selector'].choicesFunction injection point 0 + for(var i in Game.customUpgrades['Golden cookie sound selector'].choicesFunction) Game.customUpgrades['Golden cookie sound selector'].choicesFunction[i](choices); + CCSE.OverrideShimmerSoundSelector(choices);`, -1); + + /*Game.customUpgrades['Golden cookie sound selector'].choicesFunction.push(function(choices){ + choices[1].default = 'snd/chime.mp3'; + choices[1].shimmerTypes = {golden:'snd/chime.mp3', reindeer:'snd/jingle.mp3'}; + });*/ + + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Golden cookie sound selector'].choicesPick", "Game.chimeType=id;", + 'CCSE.SetSelectedShimmerSound(id);', 0); + + + // Milk selector custom options + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Milk selector'].olddescFunc", "this.choicesFunction()[Game.milkType]", "CCSE.GetSelectedMilk()", 0); + + // Game.Upgrades['Milk selector'].choicesFunction + if(!Game.customUpgrades['Milk selector'].choicesFunction) Game.customUpgrades['Milk selector'].choicesFunction = []; + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Milk selector'].choicesFunction", "return choices;", + `// Game.customUpgrades['Milk selector'].choicesFunction injection point 0 + for(var i in Game.customUpgrades['Milk selector'].choicesFunction) Game.customUpgrades['Milk selector'].choicesFunction[i](choices); + CCSE.OverrideMilkSelector(choices);`, -1); + + Game.customUpgrades['Milk selector'].choicesFunction.push(function(choices){ + if(!CCSE.Steam) for(var i in choices) choices[i].milk = Game.AllMilks[i]; + choices[0].milk = Game.Milk; + }); + + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Milk selector'].choicesPick", "Game.milkType=id;", + 'CCSE.SetSelectedMilk(id);', 0); + + + // Background selector custom options + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Background selector'].olddescFunc", "this.choicesFunction()[Game.bgType]", "CCSE.GetSelectedBackground()", 0); + + // Game.Upgrades['Background selector'].choicesFunction + if(!Game.customUpgrades['Background selector'].choicesFunction) Game.customUpgrades['Background selector'].choicesFunction = []; + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Background selector'].choicesFunction", "return choices;", + `// Game.customUpgrades['Background selector'].choicesFunction injection point 0 + for(var i in Game.customUpgrades['Background selector'].choicesFunction) Game.customUpgrades['Background selector'].choicesFunction[i](choices); + CCSE.OverrideBackgroundSelector(choices);`, -1); + + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Background selector'].choicesPick", "Game.bgType=id;", + 'CCSE.SetSelectedBackground(id);', 0); + + + // Game.Upgrades['Jukebox'].choicesFunction + // Return str to have no effect + if(!Game.customUpgrades['Jukebox'].choicesFunction) Game.customUpgrades['Jukebox'].choicesFunction = []; + CCSE.ReplaceCodeIntoFunction("Game.Upgrades['Jukebox'].choicesFunction", "return", + `// Game.customUpgrades['Jukebox'].choicesFunction injection point 0 + for(var i in Game.customUpgrades['Jukebox'].choicesFunction) str = Game.customUpgrades['Jukebox'].choicesFunction[i](str);`, -1); + + + // Permanent upgrades are tricky + var slots=['Permanent upgrade slot I','Permanent upgrade slot II','Permanent upgrade slot III','Permanent upgrade slot IV','Permanent upgrade slot V']; + for (var i=0;i time + 500 / Game.fps) break; + } + + CCSE.i = i + 1; + if(CCSE.i < Game.AchievementsN){ + // Didn't do all of them. Wait for priority and go again + requestAnimationFrame(CCSE.ReplaceAchievements); + }else{ + // Continue on + requestAnimationFrame(CCSE.playlist[CCSE.track++]); + } + } + + CCSE.ReplaceAchievement = function(key){ + var escKey = key.replace(/'/g, "\\'"); + var achievement = Game.Achievements[key]; + + if(!Game.customAchievements[key]) Game.customAchievements[key] = {}; + CCSE.Backup.customAchievements[key] = {}; + + + // this.click + if(!Game.customAchievements[key].click) Game.customAchievements[key].click = []; + Game.customAchievements[key].click.push(CCSE.customAchievementsAllclick); + CCSE.SliceCodeIntoFunction("Game.Achievements['" + escKey + "'].click", -1, ` + // Game.Achievements['` + escKey + `'].click injection point 0 + if(Game.customAchievements[this.name]) for(var i in Game.customAchievements[this.name].click) Game.customAchievements[this.name].click[i](this); + `); + + + for(var i in CCSE.customReplaceAchievement) CCSE.customReplaceAchievement[i](key, achievement); + } + + CCSE.AddCCSEStyles = function(){ + CCSE.AddStyles(`input.checkbox { + margin: 4px; + border: 3px solid transparent; + border-image: url(img/frameBorder.png) 3 round; + border-radius: 2px; + box-shadow: 0px 0px 1px 2px rgba(0,0,0,0.5), 0px 2px 4px rgba(0,0,0,0.25), 0px 0px 6px 1px rgba(0,0,0,0.5) inset; + transition: opacity 0.1s ease-out; + vertical-align: middle; + min-width: 2rem; + min-height: 2rem; + text-align: center; + + background: #000 url(img/darkNoise.jpg); + background-image: url(img/shadedBordersSoft.png),url(img/darkNoise.jpg); + background-size: 100% 100%,auto; + background-color: #000; + + text-shadow: 0px 1px 1px #000; + color: #ccc; + line-height: 100%; + + display: inline-block; + font-size: 12px; + text-decoration: none; + -webkit-appearance: none; + + position: relative; + } + + input.checkbox:checked:after { + content: '\\01F36A'; + font-size: 1.25rem; + margin: auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); + text-align: center; + }`); + } + + + /*===================================================================================== + Menu functions + =======================================================================================*/ + CCSE.AppendOptionsMenu = function(inp, style = 1){ + // Accepts inputs of either string or div + // Choose div class based on given style. A style of 0 will have no class. + var divClass; + switch (style) { + case 1: + divClass = "block"; + break; + case 2: + divClass = "framed"; + break; + } + + var template = document.createElement('template'); + template.innerHTML = '
'; + var div = template.content; + var div2 = div.children[0].children[0]; + + if(typeof inp == 'string'){ + div2.innerHTML = inp; + } + else{ + div2.appendChild(inp); + } + + var menu = l('menu'); + if(menu){ + var padding = menu.childNodes; + padding = padding[padding.length - 1]; + if(padding){ + menu.insertBefore(div, padding); + } else { + menu.appendChild(div); + } + } + } + + CCSE.AppendCollapsibleOptionsMenu = function(title, body){ + // Title must be a string. Body may be either string or div + var titleDiv = document.createElement('div'); + titleDiv.className = 'title'; + titleDiv.textContent = title + ' '; + + if(CCSE.collapseMenu[title] === undefined) CCSE.collapseMenu[title] = 0; + + // Stolen wholesale from Cookie Monster + var span = document.createElement('span'); + span.style.cursor = 'pointer'; + span.style.display = 'inline-block'; + span.style.height = '14px'; + span.style.width = '14px'; + span.style.borderRadius = '7px'; + span.style.textAlign = 'center'; + span.style.backgroundColor = '#C0C0C0'; + span.style.color = 'black'; + span.style.fontSize = '13px'; + span.style.verticalAlign = 'middle'; + span.textContent = (CCSE.collapseMenu[title] ? '+' : '-'); + span.addEventListener("click", function(){ + CCSE.ToggleCollabsibleMenu(title); + Game.UpdateMenu(); + }) ; + titleDiv.appendChild(span); + + var bodyDiv; + if(typeof body == 'string'){ + bodyDiv = document.createElement('div'); + bodyDiv.innerHTML = body; + } + else{ + bodyDiv = body; + } + + var div = document.createElement('div'); + div.appendChild(titleDiv); + if(!CCSE.collapseMenu[title]) div.appendChild(bodyDiv); + + CCSE.AppendOptionsMenu(div); + } + + CCSE.ToggleCollabsibleMenu = function(title) { + if(CCSE.collapseMenu[title] == 0){ + CCSE.collapseMenu[title]++; + } + else{ + CCSE.collapseMenu[title]--; + } + } + + CCSE.AppendStatsGeneral = function(inp){ + // Accepts inputs of either string or div + var div; + if(typeof inp == 'string'){ + div = document.createElement('div'); + div.innerHTML = inp; + } + else{ + div = inp; + } + + var general = l('statsGeneral'); + if(general) general.appendChild(div); + } + + CCSE.AppendStatsSpecial = function(inp){ + // Accepts inputs of either string or div + var div; + if(typeof inp == 'string'){ + div = document.createElement('div'); + div.innerHTML = inp; + } + else{ + div = inp; + } + + var special = l('statsSpecial'); + if(!special){ + subsections = l('menu').getElementsByClassName('subsection'); + + special = document.createElement('div'); + special.className = 'subsection'; + special.innerHTML = '
' + loc('Special') + '
'; + l('menu').insertBefore(special, subsections[1]); + } + + if(special) special.appendChild(div); + } + + CCSE.AppendStatsVersionNumber = function(modName, versionString){ + var general = l('statsGeneral'); + var str = '' + modName + ': ' + versionString; + var div = document.createElement('div'); + div.className = 'listing'; + div.innerHTML = str; + + if(general) general.parentNode.appendChild(div); + } + + CCSE.GetMenuString = function(){ + var str = '
' + + CCSE.MenuHelper.ActionButton("CCSE.ExportSave();", 'Export custom save') + + CCSE.MenuHelper.ActionButton("CCSE.ImportSave();", 'Import custom save') + + '
'; + + str += '
' + CCSE.MenuHelper.CheckBox(CCSE.config, 'showVersionNo', 'showVersionNoButton', 'Version Number ON', 'Version Number OFF', 'CCSE.togglePref') + '
'; + + return str; + } + + CCSE.PrependCollapsibleInfoMenu = function(title, body){ + // Title must be a string. Body may be either string or div + var titleDiv = document.createElement('div'); + titleDiv.className = 'title'; + titleDiv.textContent = title + ' '; + + if(CCSE.collapseMenu[title + 'info'] === undefined) CCSE.collapseMenu[title + 'info'] = 0; + + // Stolen wholesale from Cookie Monster + var span = document.createElement('span'); + span.style.cursor = 'pointer'; + span.style.display = 'inline-block'; + span.style.height = '14px'; + span.style.width = '14px'; + span.style.borderRadius = '7px'; + span.style.textAlign = 'center'; + span.style.backgroundColor = '#C0C0C0'; + span.style.color = 'black'; + span.style.fontSize = '13px'; + span.style.verticalAlign = 'middle'; + span.textContent = (CCSE.collapseMenu[title + 'info'] ? '+' : '-'); + span.onclick = function(){CCSE.ToggleCollabsibleMenu(title + 'info'); Game.UpdateMenu();}; + titleDiv.appendChild(span); + + var bodyDiv; + if(typeof body == 'string'){ + bodyDiv = document.createElement('div'); + bodyDiv.innerHTML = body; + } + else{ + bodyDiv = body; + } + + var div = document.createElement('div'); + div.appendChild(titleDiv); + div.classList.add('subsection'); + if(!CCSE.collapseMenu[title + 'info']) div.appendChild(bodyDiv); + + + var menu = l('menu'); + if(menu){ + var about = menu.getElementsByClassName('subsection')[0]; + if(about){ + about.parentNode.insertBefore(div, about); + } + } + } + + CCSE.MenuHelper = { + + ActionButton: (action, text) => + '${ text }`, + + Header: (text, id) => + '
${ text }
`, + + InputBox: (id, width, value, onChange) => + ``, + + PasswordBox: (id, width, value, onChange) => + ``, + + SearchBox: (id, width, value, onChange, placeholder = 'Search') => + ``, + + TinyIcon: (icon) => + '
`, + + Slider: (slider, leftText, rightText, startValueFunction, callback = '', min = 0, max = 100, step = 1) => { + var value = startValueFunction(); + rightText = rightText.replace('[$]', value); + return `
${ leftText }
` + + `
${ rightText }
` + + `
` + }, + + ToggleButton: (config, prefName, button, on, off, callback, invert) => { + var invert = invert ? 1 : 0; + if(!callback) callback = ''; + else callback += `('${ prefName }', '${ button }', '${ on.replace("'","\\'") }', '${ off.replace("'","\\'") }', '${ invert }');`; + callback += "PlaySound('snd/tick.mp3');"; + var className = `smallFancyButton prefButton option${ (config[prefName]^invert) ? '' : ' off' }`; + return `${ config[prefName] ? on : off }`; + }, + + CheckBox: (config, prefName, button, on, off, callback, invert) => { + var invert = invert ? 1 : 0; + if(!callback) callback = ''; + else callback += `('${ prefName }', '${ button }', '${ on.replace("'","\\'") }', '${ off.replace("'","\\'") }', '${ invert }');`; + callback += "PlaySound('snd/tick.mp3');"; + var checked = config[prefName] ? ' checked="checked"' : ''; + var className = `checkbox checkbox${ (config[prefName]^invert) ? 'on' : ' off' }`; + return `` + + ``; + } + + } + + CCSE.togglePref = function(prefName, button, on, off, invert){ + if (CCSE.config[prefName]){ + l(button).removeAttribute('checked'); + l(button + '_label').innerHTML = off; + CCSE.config[prefName] = 0; + }else{ + l(button).setAttribute('checked','checked') + l(button + '_label').innerHTML = on; + CCSE.config[prefName] = 1; + } + CCSE.applyPref(prefName); + } + + + /*===================================================================================== + Minigames + =======================================================================================*/ + CCSE.MinigameReplacer = function(func, objKey){ + if(!Game.customMinigameOnLoad) Game.customMinigameOnLoad = {}; + if(!Game.customMinigameOnLoad[objKey]) Game.customMinigameOnLoad[objKey] = []; + + var me = Game.Objects[objKey]; + if(me.minigameLoaded) func(me, 'minigameScript-' + me.id); + Game.customMinigameOnLoad[objKey].push(func); + } + + CCSE.ReplaceGrimoire = function(){ + CCSE.functionsTotal += 11; + var objKey = "Wizard tower"; + var M = Game.Objects[objKey].minigame; + var preEvalScript = "var M = Game.Objects['" + objKey + "'].minigame;"; + + + // M.computeMagicM + if(!Game.customMinigame[objKey].computeMagicM) Game.customMinigame[objKey].computeMagicM = []; + CCSE.SliceCodeIntoFunction('M.computeMagicM', -1, ` + // M.computeMagicM injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].computeMagicM) Game.customMinigame['` + objKey + `'].computeMagicM[i](); + `, preEvalScript); + + + // M.getFailChance + // functions should return a value to multiply failChance by (Return 1 for no effect) + if(!Game.customMinigame[objKey].getFailChance) Game.customMinigame[objKey].getFailChance = []; + CCSE.ReplaceCodeIntoFunction('M.getFailChance', 'return', ` + // M.getFailChance injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getFailChance) failChance *= Game.customMinigame['` + objKey + `'].getFailChance[i](spell);`, -1, + preEvalScript); + + + // M.castSpell + // I'm open to suggestions + + + // M.getSpellCost + // functions should return a value to multiply out by (Return 1 for no effect) + if(!Game.customMinigame[objKey].getSpellCost) Game.customMinigame[objKey].getSpellCost = []; + CCSE.ReplaceCodeIntoFunction('M.getSpellCost', 'return', ` + // M.getSpellCost injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getSpellCost) out *= Game.customMinigame['` + objKey + `'].getSpellCost[i](spell);`, -1, + preEvalScript); + + + // M.getSpellCostBreakdown + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].getSpellCostBreakdown) Game.customMinigame[objKey].getSpellCostBreakdown = []; + CCSE.ReplaceCodeIntoFunction('M.getSpellCostBreakdown', 'return', ` + // M.getSpellCostBreakdown injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getSpellCostBreakdown) str = Game.customMinigame['` + objKey + `'].getSpellCostBreakdown[i](spell, str);`, -1, + preEvalScript); + + + // M.spellTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].spellTooltip) Game.customMinigame[objKey].spellTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.spellTooltip', 'background-position', `' + (me.icon[2]?'background-image:url('+me.icon[2]+');':'') + 'background-position`, 0, preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.spellTooltip', 'return str', ` + // M.spellTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].spellTooltip) str = Game.customMinigame['` + objKey + `'].spellTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.refillTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].refillTooltip) Game.customMinigame[objKey].refillTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.refillTooltip', 'return', 'var str = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.refillTooltip', -1, ` + // M.refillTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].refillTooltip) str = Game.customMinigame['` + objKey + `'].refillTooltip[i](id, str); + return str; + `, preEvalScript); + + + // M.spells['hand of fate'].win + // functions should push a value to choices + if(!Game.customMinigame[objKey].fateWin) Game.customMinigame[objKey].fateWin = []; + CCSE.ReplaceCodeIntoFunction('M.spells["hand of fate"].win', 'newShimmer.force', + `// M.spells["hand of fate"].win injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].fateWin) Game.customMinigame['` + objKey + `'].fateWin[i](choices);`, -1, + preEvalScript); + + + // M.spells['hand of fate'].fail + // functions should push a value to choices + if(!Game.customMinigame[objKey].fateFail) Game.customMinigame[objKey].fateFail = []; + CCSE.ReplaceCodeIntoFunction('M.spells["hand of fate"].fail', 'newShimmer.force', + `// M.spells["hand of fate"].fail injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].fateFail) Game.customMinigame['` + objKey + `'].fateFail[i](choices);`, -1, + preEvalScript); + + + // M.launch + if(M.launch.toString().indexOf('// M.launch injection point 0') == -1){ + CCSE.SliceCodeIntoFunction('M.launch', -1, ` + // M.launch injection point 0 + for(var i in Game.customMinigameOnLoad['` + objKey + `']) Game.customMinigameOnLoad['` + objKey + `'][i](M.parent); +`, preEvalScript); + } + } + + CCSE.ReplaceMarket = function(){ + CCSE.functionsTotal += 24; + var objKey = "Bank"; + var M = Game.Objects[objKey].minigame; + var preEvalScript = "var M = Game.Objects['" + objKey + "'].minigame;"; + + + // M.goodTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].goodTooltip) Game.customMinigame[objKey].goodTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.goodTooltip', 'return str', ` + // M.goodTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].goodTooltip) str = Game.customMinigame['` + objKey + `'].goodTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.tradeTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].tradeTooltip) Game.customMinigame[objKey].tradeTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.tradeTooltip', 'return str', ` + // M.tradeTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].tradeTooltip) str = Game.customMinigame['` + objKey + `'].tradeTooltip[i](id, n, str);`, -1, + preEvalScript); + + + // M.goodDelta + // functions should return a value to multiply val by (Return 1 for no effect) + if(!Game.customMinigame[objKey].goodDelta) Game.customMinigame[objKey].goodDelta = []; + CCSE.ReplaceCodeIntoFunction('M.goodDelta', 'return', ` + // M.goodDelta injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].goodDelta) val *= Game.customMinigame['` + objKey + `'].goodDelta[i](id, back);`, -1, + preEvalScript); + + + // M.getGoodMaxStock + // functions should return an int value (Return ret for no effect) + if(!Game.customMinigame[objKey].getGoodMaxStock) Game.customMinigame[objKey].getGoodMaxStock = []; + CCSE.ReplaceCodeIntoFunction('M.getGoodMaxStock', 'return', 'var ret = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.getGoodMaxStock', -1, ` + // M.getGoodMaxStock injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getGoodMaxStock) ret = Game.customMinigame['` + objKey + `'].getGoodMaxStock[i](good, ret); + return ret; + `, preEvalScript); + + + // M.getGoodPrice + // functions should return a value to multiply val by (Return 1 for no effect) + if(!Game.customMinigame[objKey].getGoodPrice) Game.customMinigame[objKey].getGoodPrice = []; + CCSE.ReplaceCodeIntoFunction('M.getGoodPrice', 'return good.val;', + `var val = good.val; + // M.getGoodPrice injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getGoodPrice) val *= Game.customMinigame['` + objKey + `'].getGoodPrice[i](good); + return val;`, 0, + preEvalScript); + + + // M.buyGood + // functions that run when a good is purchased + if(!Game.customMinigame[objKey].buyGood) Game.customMinigame[objKey].buyGood = []; + CCSE.ReplaceCodeIntoFunction('M.buyGood', 'return true', ` + // M.buyGood injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].buyGood) Game.customMinigame['` + objKey + `'].buyGood[i](id, n);`, -1, + preEvalScript); + + + // M.sellGood + // functions that run when a good is sold + if(!Game.customMinigame[objKey].sellGood) Game.customMinigame[objKey].sellGood = []; + CCSE.ReplaceCodeIntoFunction('M.sellGood', 'return true', ` + // M.sellGood injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].sellGood) Game.customMinigame['` + objKey + `'].sellGood[i](id, n);`, -1, + preEvalScript); + + + // M.getRestingVal + // functions should return a number value (Return ret for no effect) + if(!Game.customMinigame[objKey].getRestingVal) Game.customMinigame[objKey].getRestingVal = []; + CCSE.ReplaceCodeIntoFunction('M.getRestingVal', 'return', 'var ret = ', 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.getRestingVal', '}', ` + // M.getRestingVal injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getRestingVal) ret = Game.customMinigame['` + objKey + `'].getRestingVal[i](id, ret); + return ret; + `, -1, preEvalScript); + + + // M.updateGoodStyle + if(!Game.customMinigame[objKey].updateGoodStyle) Game.customMinigame[objKey].updateGoodStyle = []; + CCSE.SliceCodeIntoFunction('M.updateGoodStyle', -1, ` + // M.updateGoodStyle injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].updateGoodStyle) Game.customMinigame['` + objKey + `'].updateGoodStyle[i](id, me); + `, preEvalScript); + + + // M.officeTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].officeTooltip) Game.customMinigame[objKey].officeTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.officeTooltip', 'return str', ` + // M.officeTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].officeTooltip) str = Game.customMinigame['` + objKey + `'].officeTooltip[i](me, str);`, -1, + preEvalScript); + + + // M.getMaxBrokers + // functions should return an int value (Return ret for no effect) + if(!Game.customMinigame[objKey].getMaxBrokers) Game.customMinigame[objKey].getMaxBrokers = []; + CCSE.ReplaceCodeIntoFunction('M.getMaxBrokers', 'return', 'var ret = ', 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.getMaxBrokers', '}', ` + // M.getMaxBrokers injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getMaxBrokers) ret = Game.customMinigame['` + objKey + `'].getMaxBrokers[i](ret); + return ret; + `, -1, preEvalScript); + + + // M.getBrokerPrice + // functions should return an int value (Return ret for no effect) + if(!Game.customMinigame[objKey].getBrokerPrice) Game.customMinigame[objKey].getBrokerPrice = []; + CCSE.ReplaceCodeIntoFunction('M.getBrokerPrice', 'return', 'var ret = ', 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.getBrokerPrice', '}', ` + // M.getBrokerPrice injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getBrokerPrice) ret = Game.customMinigame['` + objKey + `'].getBrokerPrice[i](ret); + return ret; + `, -1, preEvalScript); + + + // M.brokersTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].brokersTooltip) Game.customMinigame[objKey].brokersTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.brokersTooltip', 'return str', ` + // M.brokersTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].brokersTooltip) str = Game.customMinigame['` + objKey + `'].brokersTooltip[i](str);`, -1, + preEvalScript); + + + // M.loanTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].loanTooltip) Game.customMinigame[objKey].loanTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.loanTooltip', 'return str', ` + // M.loanTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].loanTooltip) str = Game.customMinigame['` + objKey + `'].loanTooltip[i](id, loan, str);`, -1, + preEvalScript); + + + // M.takeLoan + // Will be added if given a specific request + + + // M.getOppSlots + // functions should return a value to add to slots (Return 0 for no effect) + if(!Game.customMinigame[objKey].getOppSlots) Game.customMinigame[objKey].getOppSlots = []; + CCSE.ReplaceCodeIntoFunction('M.getOppSlots', 'return', ` + // M.getOppSlots injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getOppSlots) slots += Game.customMinigame['` + objKey + `'].getOppSlots[i]();`, -1, + preEvalScript); + + + // M.oppTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].oppTooltip) Game.customMinigame[objKey].oppTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.oppTooltip', 'return str', ` + // M.oppTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].oppTooltip) str = Game.customMinigame['` + objKey + `'].oppTooltip[i](str);`, -1, + preEvalScript); + + + // M.refillTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].refillTooltip) Game.customMinigame[objKey].refillTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.refillTooltip', 'return', 'var str = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.refillTooltip', -1, ` + // M.refillTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].refillTooltip) str = Game.customMinigame['` + objKey + `'].refillTooltip[i](id, str); + return str; + `, preEvalScript); + + + // M.tick + if(!Game.customMinigame[objKey].tick) Game.customMinigame[objKey].tick = []; + CCSE.SliceCodeIntoFunction('M.tick', -1, ` + // M.tick injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].tick) Game.customMinigame['` + objKey + `'].tick[i](); + `, preEvalScript); + + + // M.launch + if(M.launch.toString().indexOf('// M.launch injection point 0') == -1){ + CCSE.SliceCodeIntoFunction('M.launch', -1, ` + // M.launch injection point 0 + for(var i in Game.customMinigameOnLoad['` + objKey + `']) Game.customMinigameOnLoad['` + objKey + `'][i](M.parent); +`, preEvalScript); + } + } + + CCSE.ReplacePantheon = function(){ + CCSE.functionsTotal += 12; + var objKey = "Temple"; + var M = Game.Objects[objKey].minigame; + var preEvalScript = "var M = Game.Objects['" + objKey + "'].minigame;"; + + + // M.godTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].godTooltip) Game.customMinigame[objKey].godTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.godTooltip', 'background-position', `' + (me.icon[2]?'background-image:url('+me.icon[2]+');':'') + 'background-position`, 0, preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.godTooltip', 'return str', ` + // M.godTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].godTooltip) str = Game.customMinigame['` + objKey + `'].godTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.slotTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].slotTooltip) Game.customMinigame[objKey].slotTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.slotTooltip', 'background-position', `' + (me.icon[2]?'background-image:url('+me.icon[2]+');':'') + 'background-position`, 0, preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.slotTooltip', 'return str', ` + // M.slotTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].slotTooltip) str = Game.customMinigame['` + objKey + `'].slotTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.useSwap + if(!Game.customMinigame[objKey].useSwap) Game.customMinigame[objKey].useSwap = []; + CCSE.SliceCodeIntoFunction('M.useSwap', -1, ` + // M.useSwap injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].useSwap) Game.customMinigame['` + objKey + `'].useSwap[i](n); + `, preEvalScript); + + + // M.slotGod + if(!Game.customMinigame[objKey].slotGod) Game.customMinigame[objKey].slotGod = []; + CCSE.SliceCodeIntoFunction('M.slotGod', -1, ` + // M.slotGod injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].slotGod) Game.customMinigame['` + objKey + `'].slotGod[i](god, slot); + `, preEvalScript); + + + // M.dragGod + if(!Game.customMinigame[objKey].dragGod) Game.customMinigame[objKey].dragGod = []; + CCSE.SliceCodeIntoFunction('M.dragGod', -1, ` + // M.dragGod injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].dragGod) Game.customMinigame['` + objKey + `'].dragGod[i](what); + `, preEvalScript); + + + // M.dropGod + if(!Game.customMinigame[objKey].dropGod) Game.customMinigame[objKey].dropGod = []; + CCSE.SliceCodeIntoFunction('M.dropGod', -1, ` + // M.dropGod injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].dropGod) Game.customMinigame['` + objKey + `'].dropGod[i](); + `, preEvalScript); + + + // M.hoverSlot + if(!Game.customMinigame[objKey].hoverSlot) Game.customMinigame[objKey].hoverSlot = []; + CCSE.SliceCodeIntoFunction('M.hoverSlot', -1, ` + // M.hoverSlot injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].hoverSlot) Game.customMinigame['` + objKey + `'].hoverSlot[i](what); + `, preEvalScript); + + + // Game.hasGod + // Game.forceUnslotGod + + + // M.refillTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].refillTooltip) Game.customMinigame[objKey].refillTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.refillTooltip', 'return', 'var str = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.refillTooltip', -1, ` + // M.refillTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].refillTooltip) str = Game.customMinigame['` + objKey + `'].refillTooltip[i](id, str); + return str; + `, preEvalScript); + + + // M.launch + if(M.launch.toString().indexOf('// M.launch injection point 0') == -1){ + CCSE.SliceCodeIntoFunction('M.launch', -1, ` + // M.launch injection point 0 + for(var i in Game.customMinigameOnLoad['` + objKey + `']) Game.customMinigameOnLoad['` + objKey + `'][i](M.parent); +`, preEvalScript); + } + } + + CCSE.ReplaceGarden = function(){ + CCSE.functionsTotal += 33; + var objKey = "Farm"; + var M = Game.Objects[objKey].minigame; + var preEvalScript = "var M = Game.Objects['" + objKey + "'].minigame;"; + var temp = ''; + + + // M.logic (plantAging) + // return age to have no effect + if(!Game.customMinigame[objKey].plantAging) Game.customMinigame[objKey].plantAging = []; + temp = M.logic.toString(); + temp = temp.replace('tile[1]+=', 'var age = '); + temp = temp.replace('tile[1]=Math.max(tile[1],0);', + `// M.logic injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].plantAging) age = Game.customMinigame['` + objKey + `'].plantAging[i](age, tile, x, y); + tile[1] += age; + tile[1]=Math.max(tile[1],0);`); + eval('M.logic=' + temp); + + + // M.getUnlockedN + if(!Game.customMinigame[objKey].getUnlockedN) Game.customMinigame[objKey].getUnlockedN = []; + CCSE.ReplaceCodeIntoFunction('M.getUnlockedN', 'return', + `// M.getUnlockedN injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getUnlockedN) Game.customMinigame['` + objKey + `'].getUnlockedN[i]();`, -1, + preEvalScript); + + + // M.dropUpgrade + if(!Game.customMinigame[objKey].dropUpgrade) Game.customMinigame[objKey].dropUpgrade = []; + CCSE.SliceCodeIntoFunction('M.dropUpgrade', -1, + `// M.dropUpgrade injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].dropUpgrade) Game.customMinigame['` + objKey + `'].dropUpgrade[i](upgrade, rate); + `, preEvalScript); + + + // M.computeMatures + if(!Game.customMinigame[objKey].computeMatures) Game.customMinigame[objKey].computeMatures = []; + CCSE.SliceCodeIntoFunction('M.computeMatures', -1, + `// M.computeMatures injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].computeMatures) Game.customMinigame['` + objKey + `'].computeMatures[i](mult); + `, preEvalScript); + + + // M.getMuts + // functions should push mutations to muts + if(!Game.customMinigame[objKey].getMuts) Game.customMinigame[objKey].getMuts = []; + CCSE.ReplaceCodeIntoFunction('M.getMuts', 'return', + `// M.getMuts injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getMuts) Game.customMinigame['` + objKey + `'].getMuts[i](neighs, neighsM, muts);`, -1, + preEvalScript); + + + // M.computeBoostPlot + // You're going to have to use MAXIMUM EFFORT + if(!Game.customMinigame[objKey].computeBoostPlot) Game.customMinigame[objKey].computeBoostPlot = []; + CCSE.SliceCodeIntoFunction('M.computeBoostPlot', -1, + `// M.computeBoostPlot injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].computeBoostPlot) Game.customMinigame['` + objKey + `'].computeBoostPlot[i](); + `, preEvalScript); + + + // M.computeEffs + // functions should change effs (or not, I'm a comment, not a cop) + if(!Game.customMinigame[objKey].computeEffs) Game.customMinigame[objKey].computeEffs = []; + CCSE.ReplaceCodeIntoFunction('M.computeEffs', 'M.effs=effs;', + `// M.computeEffs injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].computeEffs) Game.customMinigame['` + objKey + `'].computeEffs[i](effs);`, -1, + preEvalScript); + + + // M.tools TODO + + + // M.getCost TODO + + + // M.getPlantDesc + // Return ret for no effect + if(!Game.customMinigame[objKey].getPlantDesc) Game.customMinigame[objKey].getPlantDesc = []; + CCSE.ReplaceCodeIntoFunction('M.getPlantDesc', 'return', 'var ret = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.getPlantDesc', -1, + `// M.getPlantDesc injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getPlantDesc) ret = Game.customMinigame['` + objKey + `'].getPlantDesc[i](me, ret); + return ret; + `, preEvalScript); + + + // M.soilTooltip + // Return str for no effect + if(!Game.customMinigame[objKey].soilTooltip) Game.customMinigame[objKey].soilTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.soilTooltip', 'return str;', + `// M.soilTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].soilTooltip) str = Game.customMinigame['` + objKey + `'].soilTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.seedTooltip + // Return str for no effect + if(!Game.customMinigame[objKey].seedTooltip) Game.customMinigame[objKey].seedTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.seedTooltip', 'return str;', + `// M.seedTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].seedTooltip) str = Game.customMinigame['` + objKey + `'].seedTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.toolTooltip + // Return str for no effect + if(!Game.customMinigame[objKey].toolTooltip) Game.customMinigame[objKey].toolTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.toolTooltip', 'return str;', + `// M.toolTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].toolTooltip) str = Game.customMinigame['` + objKey + `'].toolTooltip[i](id, str);`, -1, + preEvalScript); + + + // M.tileTooltip + // Return ret for no effect + if(!Game.customMinigame[objKey].tileTooltip) Game.customMinigame[objKey].tileTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.tileTooltip', 'return function(){', `return function(){ + var ret = '';`, 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.tileTooltip', /return str;/g, 'ret = str;', 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.tileTooltip', '};', + `// M.tileTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].tileTooltip) ret = Game.customMinigame['` + objKey + `'].tileTooltip[i](x, y, ret); + return ret;`, -1, + preEvalScript); + + + // M.refillTooltip + // functions should return a string value (Return str for no effect) + if(!Game.customMinigame[objKey].refillTooltip) Game.customMinigame[objKey].refillTooltip = []; + CCSE.ReplaceCodeIntoFunction('M.refillTooltip', 'return', 'var str = ', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.refillTooltip', -1, ` + // M.refillTooltip injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].refillTooltip) str = Game.customMinigame['` + objKey + `'].refillTooltip[i](id, str); + return str; + `, preEvalScript); + + + // M.buildPanel + if(!Game.customMinigame[objKey].buildPanel) Game.customMinigame[objKey].buildPanel = []; + CCSE.SliceCodeIntoFunction('M.buildPanel', -1, + `// M.buildPanel injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].buildPanel) Game.customMinigame['` + objKey + `'].buildPanel[i](); + `, preEvalScript); + + + // M.buildPlot + if(!Game.customMinigame[objKey].buildPlot) Game.customMinigame[objKey].buildPlot = []; + CCSE.SliceCodeIntoFunction('M.buildPlot', -1, + `// M.buildPlot injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].buildPlot) Game.customMinigame['` + objKey + `'].buildPlot[i](); + `, preEvalScript); + + + // M.clickTile + if(!Game.customMinigame[objKey].clickTile) Game.customMinigame[objKey].clickTile = []; + CCSE.SliceCodeIntoFunction('M.clickTile', -1, + `// M.clickTile injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].clickTile) Game.customMinigame['` + objKey + `'].clickTile[i](x, y); + `, preEvalScript); + + + // M.useTool + + + // M.getTile + // Return ret to have no effect + if(!Game.customMinigame[objKey].getTile) Game.customMinigame[objKey].getTile = []; + CCSE.ReplaceCodeIntoFunction('M.getTile', '{', 'var ret;', 1, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.getTile', 'return', 'ret =', 0, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.getTile', 'return', 'else ret =', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.getTile', -1, + `// M.getTile injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].getTile) ret = Game.customMinigame['` + objKey + `'].getTile[i](x, y, ret); + return ret; + `, preEvalScript); + + + // M.getTile + // Return ret to have no effect + if(!Game.customMinigame[objKey].isTileUnlocked) Game.customMinigame[objKey].isTileUnlocked = []; + CCSE.ReplaceCodeIntoFunction('M.isTileUnlocked', '{', 'var ret;', 1, + preEvalScript); + CCSE.ReplaceCodeIntoFunction('M.isTileUnlocked', /return/g, 'ret =', 0, + preEvalScript); + CCSE.SliceCodeIntoFunction('M.isTileUnlocked', -1, + `// M.isTileUnlocked injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].isTileUnlocked) ret = Game.customMinigame['` + objKey + `'].isTileUnlocked[i](x, y, ret); + return ret; + `, preEvalScript); + + + // M.computeStepT + if(!Game.customMinigame[objKey].computeStepT) Game.customMinigame[objKey].computeStepT = []; + CCSE.SliceCodeIntoFunction('M.computeStepT', -1, + `// M.computeStepT injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].computeStepT) Game.customMinigame['` + objKey + `'].computeStepT[i](); + `, preEvalScript); + + + // M.convert + if(!Game.customMinigame[objKey].convert) Game.customMinigame[objKey].convert = []; + CCSE.SliceCodeIntoFunction('M.convert', -1, + `// M.convert injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].convert) Game.customMinigame['` + objKey + `'].convert[i](); + `, preEvalScript); + + + // M.harvestAll + if(!Game.customMinigame[objKey].harvestAll) Game.customMinigame[objKey].harvestAll = []; + CCSE.SliceCodeIntoFunction('M.harvestAll', -1, + `// M.harvestAll injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].harvestAll) Game.customMinigame['` + objKey + `'].harvestAll[i](type, mature, mortal); + `, preEvalScript); + + + // M.harvest + if(!Game.customMinigame[objKey].harvest) Game.customMinigame[objKey].harvest = []; + CCSE.ReplaceCodeIntoFunction('M.harvest', 'return true;', + `// M.harvest injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].harvest) Game.customMinigame['` + objKey + `'].harvest[i](x, y, manual);`, -1, + preEvalScript); + + + // M.unlockSeed + if(!Game.customMinigame[objKey].unlockSeed) Game.customMinigame[objKey].unlockSeed = []; + CCSE.ReplaceCodeIntoFunction('M.unlockSeed', 'return true;', + `// M.unlockSeed injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].unlockSeed) Game.customMinigame['` + objKey + `'].unlockSeed[i](me);`, -1, + preEvalScript); + + + // M.lockSeed + if(!Game.customMinigame[objKey].lockSeed) Game.customMinigame[objKey].lockSeed = []; + CCSE.ReplaceCodeIntoFunction('M.lockSeed', 'return true;', + `// M.lockSeed injection point 0 + for(var i in Game.customMinigame['` + objKey + `'].lockSeed) Game.customMinigame['` + objKey + `'].lockSeed[i](me);`, -1, + preEvalScript); + + + // M.launch + if(M.launch.toString().indexOf('// M.launch injection point 0') == -1){ + CCSE.SliceCodeIntoFunction('M.launch', -1, ` + // M.launch injection point 0 + for(var i in Game.customMinigameOnLoad['` + objKey + `']) Game.customMinigameOnLoad['` + objKey + `'][i](M.parent); +`, preEvalScript); + } + } + + + /*===================================================================================== + Grimoire + =======================================================================================*/ + if(!CCSE.customRedrawSpells) CCSE.customRedrawSpells = []; + CCSE.RedrawSpells = function(){ + var str = ''; + var M = Game.Objects['Wizard tower'].minigame; + + for(var i in M.spells){ + var me = M.spells[i]; + var icon = me.icon || [28,12]; + str += '
-
'; + } + + l('grimoireSpells').innerHTML = str; + + for(var i in M.spells){ + var me = M.spells[i]; + AddEvent(l('grimoireSpell' + me.id), 'click', function(spell){return function(){PlaySound('snd/tick.mp3'); M.castSpell(spell);}}(me)); + } + + for(var i in CCSE.customRedrawSpells) CCSE.customRedrawSpells[i](); + } + // Cookie Monster compatibility because it was here first + CCSE.customRedrawSpells.push(function(){if(typeof CM != 'undefined') CM.Disp.AddTooltipGrimoire();}); + + if(!CCSE.customNewSpell) CCSE.customNewSpell = []; + CCSE.NewSpell = function(key, spell){ + var M = Game.Objects['Wizard tower'].minigame; + + M.spells[key] = spell; + + M.spellsById = []; + var n = 0; + for(var i in M.spells){ + M.spells[i].id = n; + M.spellsById[n] = M.spells[i]; + n++; + } + + for(var i in CCSE.customNewSpell) CCSE.customNewSpell[i](key, spell); + CCSE.RedrawSpells(); + } + + + /*===================================================================================== + Pantheon + =======================================================================================*/ + if(!CCSE.customRedrawGods) CCSE.customRedrawGods = []; + CCSE.RedrawGods = function(){ + var str = ''; + var M = Game.Objects['Temple'].minigame; + + for(var i in M.slot){ + var me = M.slot[i]; + str += '
'; + } + l('templeSlots').innerHTML = str; + + str = ''; + for(var i in M.gods){ + var me = M.gods[i]; + var icon = me.icon || [0,0]; + str += '
'; + str += '
'; + } + l('templeGods').innerHTML = str; + + for(var i in M.slot){ + var me=M.slot[i]; + AddEvent(l('templeSlot' + i), 'mouseover', function(what){return function(){M.hoverSlot(what);}}(i)); + AddEvent(l('templeSlot' + i), 'mouseout', function(what){return function(){M.hoverSlot(-1);}}(i)); + } + + for(var i in M.gods){ + var me = M.gods[i]; + AddEvent(l('templeGodDrag' + me.id), 'mousedown', function(what){return function(){M.dragGod(what);}}(me)); + AddEvent(l('templeGodDrag' + me.id), 'mouseup', function(what){return function(){M.dropGod(what);}}(me)); + } + + M.load(M.save()); + for(var i in CCSE.customRedrawGods) CCSE.customRedrawGods[i](); + } + + if(!CCSE.customNewGod) CCSE.customNewGod = []; + CCSE.NewGod = function(key, god){ + var M = Game.Objects['Temple'].minigame; + + M.gods[key] = god; + + M.godsById = []; + var n = 0; + for(var i in M.gods){ + M.gods[i].id = n; + M.godsById[n] = M.gods[i]; + n++; + } + + for(var i in CCSE.customNewGod) CCSE.customNewGod[i](key, god); + CCSE.RedrawGods(); + } + + + /*===================================================================================== + Garden + =======================================================================================*/ + if(!CCSE.customNewPlant) CCSE.customNewPlant = []; + CCSE.NewPlant = function(key, plant){ + var M = Game.Objects['Farm'].minigame; + + M.plants[key] = plant; + + M.plantsById = []; + var n = 0; + for(var i in M.plants){ + M.plants[i].id = n; + M.plantsById[n] = M.plants[i]; + n++; + } + + for(var i in CCSE.customNewPlant) CCSE.customNewPlant[i](key, plant); + M.buildPanel(); + } + + + /*===================================================================================== + Save custom things + If you use CCSE to create custom upgrades or achievements, + it will also save their state to local storage whenever the game is saved. + Each custom upgrade or achievement needs a unique name, or they could get overwritten. + Yes, this means across mods as well. + If two mods have things with the same name, the mods cannot be used at the same time. + This is because of how the game itself keeps track of these things + + You can also use CCSE to save your mod data. + Add your save data as a child of CCSE.config.OtherMods. Make sure not to step on anyone else's toes! + Push your save function into CCSE.customSave, and push your load function into CCSE.customLoad + =======================================================================================*/ + + // The following code copied from https://github.com/pieroxy/lz-string + CCSE.LZString = function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString); + + if(!CCSE.customSave) CCSE.customSave = []; + CCSE.save = function(type){ + CCSE.config.version = CCSE.version; + + for(var name in CCSE.config.Buildings){ + if(Game.Objects[name]){ + var saved = CCSE.config.Buildings[name]; + var me = Game.Objects[name]; + + saved.amount = me.amount; + saved.bought = me.bought; + saved.totalCookies = me.totalCookies; + saved.level = me.level; + saved.muted = me.muted; + saved.highest = me.highest; + saved.free = me.free; + + if(Game.isMinigameReady(me)) saved.minigameSave = me.minigame.save(); else saved.minigameSave = ''; + } + } + + for(var name in CCSE.config.Achievements){ + if(Game.Achievements[name]){ + CCSE.config.Achievements[name].won = Game.Achievements[name].won; + } + } + + for(var name in CCSE.config.Upgrades){ + if(Game.Upgrades[name]){ + CCSE.config.Upgrades[name].unlocked = Game.Upgrades[name].unlocked; + CCSE.config.Upgrades[name].bought = Game.Upgrades[name].bought; + } + } + + for(var name in CCSE.config.Buffs){ + var buff = CCSE.config.Buffs[name]; + buff.time = 0; + if(Game.buffs[buff.name]){ + if(Game.buffs[buff.name].time){ + buff.time = Game.buffs[buff.name].time; + buff.maxTime = Game.buffs[buff.name].maxTime; + buff.arg1 = Game.buffs[buff.name].arg1; + buff.arg2 = Game.buffs[buff.name].arg2; + buff.arg3 = Game.buffs[buff.name].arg3; + } + } + } + + for(var name in CCSE.config.Seasons){ + var season = CCSE.config.Seasons[name]; + season.lastTime = Date.now(); + if(Game.season == name){ + season.T = Game.seasonT; + } + else{ + season.T = -1; + } + } + + for(var i in CCSE.customSave) CCSE.customSave[i](); + + var str = JSON.stringify(CCSE.config); + //str = CCSE.LZString.compressToUTF16(str); + + if(type == 2){ + return str; + } + else if(type == 3){ + return JSON.stringify(CCSE.config, null, 2); + } + else if (type==1){ + str = escape(utf8_to_b64(str) + '!END!'); + return str; + } + else{ + return str; + /*str = utf8_to_b64(str) + '!END!'; + str = escape(str); + Game.localStorageSet(CCSE.name, str);*/ + } + } + + if(!CCSE.customLoad) CCSE.customLoad = []; + CCSE.load = function(data, isBase64){ + var config; + var str = ''; + + var cautiousDecompress = function(data){ + var ret = null; + try{ + ret = JSON.parse(data); + }catch{ + ret = CCSE.LZString.decompressFromUTF16(data); + ret = JSON.parse(ret); + } + + return ret; + } + + if(isBase64){ // Getting here from import CCSE in menu + if(data){ + str = unescape(data); + } + + if(str != ''){ + str = str.split('!END!')[0]; + str = b64_to_utf8(str); + config = cautiousDecompress(str); + } + } + else{ // Getting here from game function call + if(data){ // Has data in game save + config = cautiousDecompress(data); + } + } + + + CCSE.InitializeConfig(config); + + + if(CCSE.config.version != CCSE.version){ + //l('logButton').classList.add('hasUpdate'); + CCSE.collapseMenu['CCSEinfo'] = 0; + }else{ + CCSE.collapseMenu['CCSEinfo'] = 1; + } + + for(var name in CCSE.config.Buildings){ + if(Game.Objects[name]){ + var saved = CCSE.config.Buildings[name]; + var me = Game.Objects[name]; + + me.switchMinigame(false); + me.pics = []; + + me.amount = saved.amount; + me.bought = saved.bought; + me.totalCookies = saved.totalCookies; + me.level = saved.level; + me.muted = saved.muted; + me.highest = saved.highest ? saved.highest : 0; // Left this out earlier, can't expect it to be there + me.free = saved.free ? saved.free : 0; // Left this out earlier, can't expect it to be there + + me.minigameSave = saved.minigameSave; + if(me.minigame && me.minigameLoaded && me.minigame.reset){me.minigame.reset(true); me.minigame.load(me.minigameSave);} + + Game.BuildingsOwned += me.amount; + } + } + + for(var name in CCSE.config.Achievements){ + if(Game.Achievements[name]){ + Game.Achievements[name].won = CCSE.config.Achievements[name].won; + } + } + + for(var name in CCSE.config.Upgrades){ + if(Game.Upgrades[name]){ + Game.Upgrades[name].unlocked = CCSE.config.Upgrades[name].unlocked; + Game.Upgrades[name].bought = CCSE.config.Upgrades[name].bought; + } + } + + for(var name in CCSE.config.Buffs){ + var found = false; + for(var i in Game.buffTypes) if(Game.buffTypes[i].name == name) found = true; + if(found){ + if(CCSE.config.Buffs[name].time){ + var buff = CCSE.config.Buffs[name]; + Game.gainBuff(name, buff.maxTime / Game.fps, buff.arg1, buff.arg2, buff.arg3).time = buff.time; + } + } + } + + for(var name in CCSE.config.Seasons){ + if(Game.seasons[name]){ + if(CCSE.config.Seasons[name].T > 0){ + Game.season = name; + Game.seasonT = CCSE.config.Seasons[name].T; + var framesElapsed = Math.ceil(((Date.now() - CCSE.config.Seasons[name].lastTime) / 1000) * Game.fps); + if(Game.seasonT > 0) Game.seasonT = Math.max(Game.seasonT - framesElapsed, 1); + } + + if(Game.Has('Season switcher')) Game.Unlock(Game.seasons[name].trigger); + } + } + + Game.upgradesToRebuild = 1; + for(var i in CCSE.customLoad) CCSE.customLoad[i](); + + Game.Win('Third-party'); + } + + CCSE.InitializeConfig = function(config){ + if(!CCSE.config) CCSE.config = {}; + if(!CCSE.config.version) CCSE.config.version = 1; + if(!CCSE.config.Achievements) CCSE.config.Achievements = {}; + if(!CCSE.config.Upgrades) CCSE.config.Upgrades = {}; + if(!CCSE.config.Buildings) CCSE.config.Buildings = {}; + if(!CCSE.config.Buffs) CCSE.config.Buffs = {}; + if(!CCSE.config.Seasons) CCSE.config.Seasons = {}; + if(!CCSE.config.OtherMods) CCSE.config.OtherMods = {}; + if(!CCSE.config.vault) CCSE.config.vault = []; + if(!CCSE.config.permanentUpgrades) CCSE.config.permanentUpgrades = [-1,-1,-1,-1,-1]; + if(!CCSE.config.chimeType) CCSE.config.chimeType = 'No sound'; + if(!CCSE.config.milkType) CCSE.config.milkType = 'Automatic'; + if(!CCSE.config.bgType) CCSE.config.bgType = 'Automatic'; + if(CCSE.config.showVersionNo === undefined) CCSE.config.showVersionNo = 1; + + if(config){ + if(config.version) CCSE.config.version = config.version; + if(config.Achievements) for(var i in config.Achievements) CCSE.config.Achievements[i] = config.Achievements[i]; + if(config.Upgrades) for(var i in config.Upgrades) CCSE.config.Upgrades[i] = config.Upgrades[i]; + if(config.Buildings) for(var i in config.Buildings) CCSE.config.Buildings[i] = config.Buildings[i]; + if(config.Buffs) for(var i in config.Buffs) CCSE.config.Buffs[i] = config.Buffs[i]; + if(config.Seasons) for(var i in config.Seasons) CCSE.config.Seasons[i] = config.Seasons[i]; + if(config.OtherMods) for(var i in config.OtherMods) CCSE.config.OtherMods[i] = config.OtherMods[i]; + if(config.vault) for(var i in config.vault) CCSE.config.vault[i] = config.vault[i]; + if(config.permanentUpgrades) for(var i in config.permanentUpgrades) CCSE.config.permanentUpgrades[i] = config.permanentUpgrades[i]; + if(config.chimeType) CCSE.config.chimeType = config.chimeType; + if(config.milkType) CCSE.config.milkType = config.milkType; + if(config.bgType) CCSE.config.bgType = config.bgType; + if(config.showVersionNo !== undefined) CCSE.config.showVersionNo = config.showVersionNo; + } + } + + CCSE.applyPref = function(prefName){ + switch(prefName){ + case 'showVersionNo': + if(CCSE.config[prefName]){ + l('CCSEversionNumber').style.display = ''; + l('CCSEversionGame').style.display = ''; + }else{ + l('CCSEversionNumber').style.display = 'none'; + l('CCSEversionGame').style.display = 'none'; + } + break; + } + } + + // These two kept for people who might be blindsided by the save format change + CCSE.ExportSave = function(){ + Game.Prompt( + '

Export configuration

' + + '
This is your CCSE save.
It contains data that other mods authors decided to allow CCSE to manage, as well as data for custom things added through CCSE (i.e. achivements, upgrades, etc)
' + + '
', + ['All done!']); + l('textareaPrompt').focus(); + l('textareaPrompt').select(); + } + + CCSE.ImportSave = function(){ + var load = 'if(l("textareaPrompt").value.length > 0){CCSE.load(l("textareaPrompt").value, 1); Game.ClosePrompt(); Game.UpdateMenu();}'; + Game.Prompt( + '

Import config

'+ + '
Paste your CCSE save here.
'+ + '
', + [['Load', load], 'Nevermind']); + l('textareaPrompt').focus(); + } + + /* + Made obsolete by native mod save support + + CCSE.ExportCombinedSave = function(){ + var saveString = JSON.stringify({'Vanilla':Game.WriteSave(1),'CCSE':CCSE.save(1)}); + Game.Prompt('

Export combined save

This is your vanilla game save combined with your CCSE save in a single convenient location!
',['All done!']); + l('textareaPrompt').focus(); + l('textareaPrompt').select(); + } + + CCSE.ImportCombinedSave = function(){ + Game.Prompt('

Import combined save

Paste your combined vanilla/CCSE save here.
', + [['Load','if(l(\'textareaPrompt\').value.length > 0){CCSE.LoadCombinedSave(l(\'textareaPrompt\').value); Game.ClosePrompt(); Game.UpdateMenu();}'], 'Nevermind']); + l('textareaPrompt').focus(); + } + + CCSE.LoadCombinedSave = function(saveStr){ + var save = JSON.parse(saveStr); + Game.ImportSaveCode(save.Vanilla); + CCSE.load(save.CCSE, 1); + }*/ + + /* + I apparently never incorporated these anywhere. Might as well comment them out. + + CCSE.ExportEditableSave = function(){ + Game.Prompt('

Export configuration

This is your CCSE save.
In JSON format for people who want to edit it.
',['All done!']); + l('textareaPrompt').focus(); + l('textareaPrompt').select(); + } + + CCSE.ImportEditableSave = function(){ + Game.Prompt('

Import config

Paste your CCSE save here (in JSON format).
', + [['Load','if(l(\'textareaPrompt\').value.length > 0){CCSE.load(l(\'textareaPrompt\').value); Game.ClosePrompt(); Game.UpdateMenu();}'], 'Nevermind']); + l('textareaPrompt').focus(); + }*/ + + CCSE.reset = function(hard){ + for(var name in CCSE.config.Buildings){ + var me = CCSE.config.Buildings[name]; + me.amount=0;me.bought=0;me.highest=0;me.free=0;me.totalCookies=0; + me.onMinigame = false; + if(hard) me.muted=0; + me.pics=[]; + } + + for(var name in CCSE.config.Achievements){ + if(hard) CCSE.config.Achievements[name].won = 0; + } + + for(var name in CCSE.config.Upgrades){ + var me = CCSE.config.Upgrades[name]; + me.bought = 0; + me.unlocked = 0; + } + + for(var name in CCSE.config.Buffs){ + var buff = CCSE.config.Buffs[name]; + buff.time = 0; + buff.maxTime = 0; + buff.arg1 = 0; + buff.arg2 = 0; + buff.arg3 = 0; + } + + for(var name in CCSE.config.Seasons){ + var season = CCSE.config.Seasons[name]; + season.lastTime = Date.now(); + season.T = 0; + } + + + if(hard){ + CCSE.config.vault = []; + CCSE.config.permanentUpgrades = [-1,-1,-1,-1,-1]; + CCSE.config.chimeType = 'No sound'; + CCSE.config.milkType = 'Automatic'; + CCSE.config.bgType = 'Automatic'; + } else { + if(Game.ascensionMode != 1){ + for(var i in CCSE.config.permanentUpgrades){ + if(CCSE.config.permanentUpgrades[i] != -1) + if(Game.Upgrades[CCSE.config.permanentUpgrades[i]]) + Game.Upgrades[CCSE.config.permanentUpgrades[i]].earn(); + } + } + } + } + + + /*===================================================================================== + Standard creation helpers + =======================================================================================*/ + CCSE.NewUpgrade = function(name, desc, price, icon, buyFunction){ + var me = new Game.Upgrade(name, desc, price, icon, buyFunction); + CCSE.ReplaceUpgrade(name); + + if(CCSE.config.Upgrades[name]){ + me.unlocked = CCSE.config.Upgrades[name].unlocked; + me.bought = CCSE.config.Upgrades[name].bought; + }else{ + CCSE.config.Upgrades[name] = { + unlocked: 0, + bought: 0 + } + } + + me.CCSE = 1; + + if(typeof LocalizeUpgradesAndAchievs !== 'undefined') LocalizeUpgradesAndAchievs(); + return me; + } + + CCSE.NewHeavenlyUpgrade = function(name, desc, price, icon, posX, posY, parents, buyFunction){ + var me = CCSE.NewUpgrade(name, desc, price, icon, buyFunction); + Game.PrestigeUpgrades.push(me); + + me.pool = 'prestige'; + me.posX = posX; + me.posY = posY; + me.order = me.id; + + me.parents = parents; + if(me.parents.length == 0) me.parents = ['Legacy']; + me.parents = me.parents || [-1]; + for(var ii in me.parents){ + if(me.parents[ii] != -1) me.parents[ii] = Game.Upgrades[me.parents[ii]]; + } + + return me; + } + + CCSE.NewAchievement = function(name, desc, icon){ + var me = new Game.Achievement(name, desc, icon); + CCSE.ReplaceAchievement(name); + + if(CCSE.config.Achievements[name]){ + me.won = CCSE.config.Achievements[name].won; + }else{ + CCSE.config.Achievements[name] = { + won: 0 + } + } + + if(typeof LocalizeUpgradesAndAchievs !== 'undefined') LocalizeUpgradesAndAchievs(); + return me; + } + + CCSE.NewBuilding = function(name, commonName, desc, icon, iconColumn, art, price, cps, buyFunction, foolObject, buildingSpecial){ + var me = new Game.Object(name, commonName, desc, icon, iconColumn, art, price, cps, buyFunction); + + // This is the name, description, and icon used during Business Season + if(foolObject) Game.foolObjects[name] = foolObject; + // The name of this building's golden cookie buff and debuff + if(buildingSpecial) Game.goldenCookieBuildingBuffs[name] = buildingSpecial; + + CCSE.ReplaceBuilding(name); + + if(art.customBuildingPic){ + Game.customBuildStore.push(function(){ + l('productIcon' + me.id).style.backgroundImage = 'url(' + art.customBuildingPic + ')'; + l('productIconOff' + me.id).style.backgroundImage = 'url(' + art.customBuildingPic + ')'; + }); + } + if(art.customIconsPic){ + Game.customBuildings[name].tooltip.push(function(obj, ret){ + if(me.locked) return ret; + else return ret.replace('background-position', 'background-image:url(' + obj.art.customIconsPic + ');background-position'); + }); + } + + + + if(CCSE.config.Buildings[name]){ + var saved = CCSE.config.Buildings[name]; + me.amount = saved.amount; + me.bought = saved.bought; + me.totalCookies = saved.totalCookies; + me.level = saved.level; + me.muted = saved.muted; + me.highest = saved.highest ? saved.highest : 0; // Left this out earlier, can't expect it to be there + me.free = saved.free ? saved.free : 0; // Left this out earlier, can't expect it to be there + me.minigameSave = saved.minigameSave; + + Game.BuildingsOwned += me.amount; + + }else{ + var saved = {}; + saved.amount = 0; + saved.bought = 0; + saved.totalCookies = 0; + saved.level = 0; + saved.muted = 0; + saved.free = 0; + saved.highest = 0; + saved.minigameSave = ''; + + CCSE.config.Buildings[name] = saved; + } + + + Game.BuildStore(); + + + me.canvas=l('rowCanvas'+me.id); + me.ctx=me.canvas.getContext('2d',{alpha:false}); + me.pics=[]; + var icon=[0*64,me.icon*64]; + var muteStr = ''; + + AddEvent(me.canvas,'mouseover',function(me){return function(){me.mouseOn=true;}}(me)); + AddEvent(me.canvas,'mouseout',function(me){return function(){me.mouseOn=false;}}(me)); + AddEvent(me.canvas,'mousemove',function(me){return function(e){var box=this.getBoundingClientRect();me.mousePos[0]=e.pageX-box.left;me.mousePos[1]=e.pageY-box.top;}}(me)); + + l('buildingsMute').innerHTML+=muteStr; + + + + Game.recalculateGains = 1; + return me; + } + + CCSE.NewBuff = function(name, func){ + var me = new Game.buffType(name, func); + + if(CCSE.config.Buffs[name]){ + if(CCSE.config.Buffs[name].time){ + CCSE.config.Buffs[name].name = func().name; + var buff = CCSE.config.Buffs[name]; + Game.gainBuff(name, buff.maxTime / Game.fps, buff.arg1, buff.arg2, buff.arg3).time = buff.time; + } + }else{ + CCSE.config.Buffs[name] = { + name: func().name, + maxTime: 0, + time: 0, + arg1: 0, + arg2: 0, + arg3: 0 + } + } + + return me; + } + + CCSE.NewSeason = function(name, firstDay, lastDay, season, announcement){ + Game.seasons[name] = season; + + lastDay.setDate(lastDay.getDate() + 1); // lastDay is inclusive + if(Date.now() >= firstDay && Date.now() <= lastDay) Game.baseSeason = name; + + CCSE.customLoad.push(function(){ + if(Game.season == name && Game.season == Game.baseSeason){ + Game.Notify(announcement[0], announcement[1], announcement[2], 60 * 3); + } + }); + + CCSE.ReplaceCodeIntoFunction('Game.WriteSave', /\(\(Game.season/g, "((Game.season && Game.season != '" + name + "'", 0); + + Game.computeSeasons(); + Game.computeSeasonPrices(); + + if(CCSE.config.Seasons[name]){ + if(CCSE.config.Seasons[name].T > 0){ + Game.seasonT = CCSE.config.Seasons[name].T; + Game.season = name; + var framesElapsed = Math.ceil(((Date.now() - CCSE.config.Seasons[name].lastTime) / 1000) * Game.fps); + if(Game.seasonT > 0) Game.seasonT = Math.max(Game.seasonT - framesElapsed, 1); + } + }else{ + CCSE.config.Seasons[name] = { + T: 0, + lastTime: Date.now() + } + } + + if(Game.Has('Season switcher')) Game.Unlock(Game.seasons[name].trigger); + Game.upgradesToRebuild = 1; + } + + CCSE.NewShimmerSoundSelection = function(name, icon, defaultSound, shimmerTypes){ + // name What the game will display in the selector + // icon An array [x, y, (optional)url] See how upgrades handle icons to get an idea + // defaultSound The default sound to play for a shimmer spawn + // shimmerTypes For different sounds for each shimmer type {golden:'soundUrl',reindeer:'differentSoundUrl'} + let sound = {name:name, icon:icon}; + if(defaultSound) sound.default = defaultSound; + if(shimmerTypes) sound.shimmerTypes = shimmerTypes; + + Game.customUpgrades['Golden cookie sound selector'].choicesFunction.push(function(choices){ + choices.push(sound); + }); + } + + CCSE.NewMilkSelection = function(name, icon, pic){ + // name What the game will display in the selector + // icon An array [x, y, (optional)url] See how upgrades handle icons to get an idea + // pic Url to your picture + + let milk = {name:name, icon:icon, milk:{pic:pic}, order:Game.AllMilks.length}; + Game.customUpgrades['Milk selector'].choicesFunction.push(function(choices){ + choices.push(milk); + }); + } + + CCSE.NewBackgroundSelection = function(name, icon, pic){ + // name What the game will display in the selector + // icon An array [x, y, (optional)url] See how upgrades handle icons to get an idea + // pic Url to your picture + + let bg = {name:name, icon:icon, pic:pic}; + Game.customUpgrades['Background selector'].choicesFunction.push(function(choices){ + choices.push(bg); + }); + } + + + /*===================================================================================== + Custom Selector helper functions + =======================================================================================*/ + CCSE.OverrideShimmerSoundSelector = function(choices){ + let found = false; + for(var i in choices){ + let choice = choices[i]; + if(choice.name == CCSE.config.chimeType){choice.selected = 1; found = true} + else choice.selected = false; + } + + // If the selected sound is from an unloaded mod, default to Chime + if(!found) choices[1].selected = 1; + } + + CCSE.GetSelectedShimmerSound = function(){ + let choices = Game.Upgrades['Golden cookie sound selector'].choicesFunction(); + let choice = choices[1]; + for(var i in choices) if(choices[i].selected) choice = choices[i]; + return choice; + } + + CCSE.SetSelectedShimmerSound = function(id){ + let choices = Game.Upgrades['Golden cookie sound selector'].choicesFunction(); + Game.chimeType = (id > 4 ? 0 : id); // Manually update when vanilla choices change + CCSE.config.chimeType = choices[id].name; + } + + CCSE.PlayShimmerSpawnSound = function(shimmerType){ + //if(Game.chimeType){ // Game.chimeType is 0 for No sound + let choice = CCSE.GetSelectedShimmerSound(); + let sfx = ''; + if(choice.shimmerTypes && choice.shimmerTypes[shimmerType] !== undefined) sfx = choice.shimmerTypes[shimmerType]; + else if(choice.default) sfx = choice.default; + + PlaySound(sfx); + //} + } + + CCSE.OverrideMilkSelector = function(choices){ + let found = false; + for(var i in choices){ + let choice = choices[i]; + if(choice.name == CCSE.config.milkType){choice.selected = 1; found = true} + else choice.selected = false; + } + + // If the selected milk is from an unloaded mod, default to Automatic + if(!found) choices[0].selected = 1; + } + + CCSE.GetSelectedMilk = function(){ + let choices = Game.Upgrades['Milk selector'].choicesFunction(); + let choice = {milk:Game.Milk}; + for(var i in choices) if(choices[i].selected) choice = choices[i]; + return choice; + } + + CCSE.SetSelectedMilk = function(id){ + Game.milkType = (id, frame:} + // drawFunc a function that recieves and returns an HTML string. + + Game.customSpecialTabs.push(function(){ + if(conditionFunc()) Game.specialTabs.push(name); + }); + + Game.customDrawSpecialPic.push(function(picframe, tab){ + if(tab == name) pictureFunc(picframe); + }); + + + Game.customToggleSpecialMenu.push(function(str){ + if(Game.specialTab == name) str = drawFunc(str); + return str; + }); + } + + CCSE.SetSpecialMenuImage = function(str, pic, frame){ + // We assume that Game.specialTab is not santa or dragon + return str.replace( + 'background:url('+Game.resPath+'img/dragon.png?v='+Game.version+');background-position:'+(-4*96)+'px 0px;', + 'background:url(' + pic + ');background-position:' + (frame * (-96)) + 'px 0px;'); + } + + CCSE.GetPermanentUpgrade = function(slot, id){ + if(CCSE.config.permanentUpgrades[slot] == -1) return id; + return (Game.Upgrades[CCSE.config.permanentUpgrades[slot]] ? Game.Upgrades[CCSE.config.permanentUpgrades[slot]].id : -1); + } + + CCSE.RectifyPermanentUpgrades = function(){ + for(var i in Game.permanentUpgrades){ + if(Game.permanentUpgrades[i] != -1){ + var upgrade = Game.UpgradesById[Game.permanentUpgrades[i]]; + if(upgrade.CCSE){ + Game.permanentUpgrades[i] = -1; + CCSE.config.permanentUpgrades[i] = upgrade.name; + }else{ + CCSE.config.permanentUpgrades[i] = -1; + } + } + } + } + + CCSE.AddStyles = function(content){ + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = content; + document.getElementsByTagName('head')[0].appendChild(style); + } + + + /*===================================================================================== + Confirmation Prompts + =======================================================================================*/ + CCSE.ConfirmLoad = function(modName, modVersion, versionText){ + return confirm( + `${ modName } version ${ modVersion } is meant for ${ versionText }. `+ + "Loading a different version may cause errors. " + + `Do you still want to load ${ modName }?`); + } + CCSE.ConfirmGameVersion = function(modName, modVersion, version){ + var proceed = true; + if(Game.version != version){ + proceed = CCSE.ConfirmLoad(modName, modVersion, `Game version ${ version }`); + } + return proceed; + } + + CCSE.ConfirmCCSEVersion = function(modName, modVersion, version){ + var proceed = true; + if(CCSE.version != version){ + proceed = CCSE.ConfirmLoad(modName, modVersion, `CCSE version ${ version }`); + } + return proceed; + } + + CCSE.ConfirmGameCCSEVersion = function(modName, modVersion, gameVersion, ccseVersion){ + var proceed = true; + if(Game.version != gameVersion && CCSE.version != ccseVersion){ + proceed = CCSE.ConfirmLoad(modName, modVersion, `Game version ${ gameVersion } and CCSE version ${ ccseVersion }`); + } + else if(Game.version != gameVersion){ + proceed = CCSE.ConfirmLoad(modName, modVersion, `Game version ${ gameVersion }`); + } + else if(CCSE.version != ccseVersion){ + proceed = CCSE.ConfirmLoad(modName, modVersion, `CCSE version ${ ccseVersion }`); + } + return proceed; + } + + /* Doesn't work until the mods actually get loaded in order + CCSE.LaunchCCSEMod = function(func){ + if(CCSE.isLoaded) func(); + else CCSE.postLoadHooks.push(func); + }*/ + + if(CCSE.Steam){ + CCSE.GetModPath = (modName) => { + let mod = App.mods[modName]; + let pos = mod.dir.lastIndexOf('\\'); + if(pos == -1) return '../mods/' + (mod.local ? 'local' : 'workshop') + '/' + mod.path; + else return '../mods/' + mod.dir.substring(pos + 1); + } + + CCSE.GetModFolder = (modName) => App.mods[modName].path; + + CCSE.MenuHelper.AutoVersion = (mod) => { + let func = function(){ + let modInfo = Steam.mods[mod.id].info; + Game.customStatsMenu.push(function(){ + CCSE.AppendStatsVersionNumber(modInfo.Name, modInfo.ModVersion); + }); + } + + if(CCSE.isLoaded) func(); + else CCSE.postLoadHooks.push(func); + } + } + + + /*===================================================================================== + Start your engines + =======================================================================================*/ + if(CCSE.ConfirmGameVersion(CCSE.name, CCSE.version, CCSE.GameVersion)){ + Game.registerMod(CCSE.name, CCSE); + + if(CCSE.Steam){ + CCSE.LaunchOtherMods = Game.launchMods; + Game.launchMods = CCSE.init; + + CCSE.GameLoadModData = Game.loadModData; + Game.loadModData = function(){CCSE.gameHasLoadedSave=1;} + } + } +} + +if(!CCSE.isLoaded && !CCSE.loading) CCSE.launch(); diff --git a/cookieclicker/mods/CookieMonster.js b/cookieclicker/mods/CookieMonster.js new file mode 100644 index 00000000..90d8de28 --- /dev/null +++ b/cookieclicker/mods/CookieMonster.js @@ -0,0 +1,3 @@ +/*! For license information please see CookieMonster.js.LICENSE.txt */ +(()=>{var e={877:function(e){!function(t,o){"use strict";"object"!=typeof e.exports?o(t):e.exports=t.document?o(t):function(e){if(!e.document)throw new Error("jscolor needs a window with document");return o(e)}}("undefined"!=typeof window?window:this,(function(e){"use strict";var t,o,a,n,i=(n={initialized:!1,instances:[],readyQueue:[],register:function(){void 0!==e&&e.document&&e.document.addEventListener("DOMContentLoaded",n.pub.init,!1)},installBySelector:function(t,o){if(!(o=o?n.node(o):e.document))throw new Error("Missing root node");for(var a=o.querySelectorAll(t),i=new RegExp("(^|\\s)("+n.pub.lookupClass+")(\\s*(\\{[^}]*\\})|\\s|$)","i"),r=0;r-1},isButtonEmpty:function(e){switch(n.nodeName(e)){case"input":return!e.value||""===e.value.trim();case"button":return""===e.textContent.trim()}return null},isPassiveEventSupported:function(){var t=!1;try{var o=Object.defineProperty({},"passive",{get:function(){t=!0}});e.addEventListener("testPassive",null,o),e.removeEventListener("testPassive",null,o)}catch(e){}return t}(),isColorAttrSupported:(a=e.document.createElement("input"),!(!a.setAttribute||(a.setAttribute("type","color"),"color"!=a.type.toLowerCase()))),dataProp:"_data_jscolor",setData:function(){var e=arguments[0];if(3===arguments.length){var t=e.hasOwnProperty(n.dataProp)?e[n.dataProp]:e[n.dataProp]={},o=arguments[2];return t[i=arguments[1]]=o,!0}if(2===arguments.length&&"object"==typeof arguments[1]){t=e.hasOwnProperty(n.dataProp)?e[n.dataProp]:e[n.dataProp]={};var a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(t[i]=a[i]);return!0}throw new Error("Invalid arguments")},removeData:function(){var e=arguments[0];if(!e.hasOwnProperty(n.dataProp))return!0;for(var t=1;t=3&&(a=s[0].match(l))&&(n=s[1].match(l))&&(i=s[2].match(l)))return o.format="rgb",o.rgba=[parseFloat(a[1])||0,parseFloat(n[1])||0,parseFloat(i[1])||0,null],s.length>=4&&(r=s[3].match(l))&&(o.format="rgba",o.rgba[3]=parseFloat(r[1])||0),o}return!1},parsePaletteValue:function(e){var t=[];"string"==typeof e?e.replace(/#[0-9A-F]{3}([0-9A-F]{3})?|rgba?\(([^)]*)\)/gi,(function(e){t.push(e)})):Array.isArray(e)&&(t=e);for(var o=[],a=0;al[a]&&-t[a]+e[a]+s[a]/2>l[a]/2&&e[a]+s[a]-d[a]>=0?e[a]+s[a]-d[a]:e[a],-t[i]+e[i]+s[i]+d[i]-m+m*r>l[i]?-t[i]+e[i]+s[i]/2>l[i]/2&&e[i]+s[i]-m-m*r>=0?e[i]+s[i]-m-m*r:e[i]+s[i]-m+m*r:e[i]+s[i]-m+m*r>=0?e[i]+s[i]-m+m*r:e[i]+s[i]-m-m*r];else var p=[e[a],e[i]+s[i]-m+m*r];var u=p[a],h=p[i],g=o.fixed?"fixed":"absolute",f=(p[0]+d[0]>e[0]||p[0]0?Math.ceil(s/o):0,n=Math.max(1,Math.floor((t-(o-1)*e.paletteSpacing)/o)),i=e.paletteHeight?Math.min(e.paletteHeight,n):n),a&&(r=a*i+(a-1)*e.paletteSpacing),{cols:o,rows:a,cellW:n,cellH:i,width:t,height:r}},getControlPadding:function(e){return Math.max(e.padding/2,2*e.pointerBorderWidth+e.pointerThickness-e.controlBorderWidth)},getPadYChannel:function(e){return"v"===e.mode.charAt(1).toLowerCase()?"v":"s"},getSliderChannel:function(e){if(e.mode.length>2)switch(e.mode.charAt(2).toLowerCase()){case"s":return"s";case"v":return"v"}return null},triggerCallback:function(e,t){if(e[t]){var o=null;if("string"==typeof e[t])try{o=new Function(e[t])}catch(e){console.error(e)}else o=e[t];o&&o.call(e)}},triggerGlobal:function(e){for(var t=n.getInstances(),o=0;o0)for(var c=0;c=2&&"string"==typeof arguments[0]){try{if(!i(arguments[0],arguments[1]))return!1}catch(e){return console.warn(e),!1}return this.redraw(),this.exposeColor(),!0}if(1===arguments.length&&"object"==typeof arguments[0]){var e=arguments[0],t=!0;for(var o in e)if(e.hasOwnProperty(o))try{i(o,e[o])||(t=!1)}catch(e){console.warn(e),t=!1}return this.redraw(),this.exposeColor(),t}throw new Error("Invalid arguments")},this.channel=function(e,t){if("string"!=typeof e)throw new Error("Invalid value for channel name: "+e);if(void 0===t)return this.channels.hasOwnProperty(e.toLowerCase())?this.channels[e.toLowerCase()]:(console.warn("Getting unknown channel: "+e),!1);var o=!1;switch(e.toLowerCase()){case"r":o=this.fromRGBA(t,null,null,null);break;case"g":o=this.fromRGBA(null,t,null,null);break;case"b":o=this.fromRGBA(null,null,t,null);break;case"h":o=this.fromHSVA(t,null,null,null);break;case"s":o=this.fromHSVA(null,t,null,null);break;case"v":o=this.fromHSVA(null,null,t,null);break;case"a":o=this.fromHSVA(null,null,null,t);break;default:return console.warn("Setting unknown channel: "+e),!1}return!!o&&(this.redraw(),!0)},this.trigger=function(e){for(var t=n.strList(e),o=0;o127.5},this.hide=function(){m()&&(n.removeClass(a.targetElement,n.pub.activeClassName),n.picker.wrap.parentNode.removeChild(n.picker.wrap),delete n.picker.owner)},this.show=function(){s()},this.redraw=function(){m()&&s()},this.getFormat=function(){return this._currentFormat},this._setFormat=function(e){this._currentFormat=e.toLowerCase()},this.hasAlphaChannel=function(){return"auto"===this.alphaChannel?"any"===this.format.toLowerCase()||n.isAlphaFormat(this.getFormat())||void 0!==this.alpha||void 0!==this.alphaElement:this.alphaChannel},this.processValueInput=function(e){this.fromString(e)||this.exposeColor()},this.processAlphaInput=function(e){this.fromHSVA(null,null,null,parseFloat(e))||this.exposeColor()},this.exposeColor=function(e){var t=this.toString(),o=this.getFormat();if(n.setDataAttr(this.targetElement,"current-color",t),e&n.flags.leaveValue||!this.valueElement||("hex"!==o&&"hexa"!==o||(this.uppercase||(t=t.toLowerCase()),this.hash||(t=t.replace(/^#/,""))),this.setValueElementValue(t)),!(e&n.flags.leaveAlpha)&&this.alphaElement){var a=Math.round(100*this.channels.a)/100;this.setAlphaElementValue(a)}e&n.flags.leavePreview||!this.previewElement||((n.isTextInput(this.previewElement)||n.isButton(this.previewElement)&&!n.isButtonEmpty(this.previewElement))&&this.previewPosition,this.setPreviewElementBg(this.toRGBAString())),m()&&(l(),c(),d())},this.setPreviewElementBg=function(e){if(this.previewElement){var t=null,o=null;(n.isTextInput(this.previewElement)||n.isButton(this.previewElement)&&!n.isButtonEmpty(this.previewElement))&&(t=this.previewPosition,o=this.previewSize);var a=[];if(e){a.push({image:n.genColorPreviewGradient(e,t,o?o-n.pub.previewSeparator.length:null),position:"left top",size:"auto",repeat:t?"repeat-y":"repeat",origin:"padding-box"});var i=n.genColorPreviewCanvas("rgba(0,0,0,0)",t?{left:"right",right:"left"}[t]:null,o,!0);a.push({image:"url('"+i.canvas.toDataURL()+"')",position:(t||"left")+" top",size:i.width+"px "+i.height+"px",repeat:t?"repeat-y":"repeat",origin:"padding-box"})}else a.push({image:"none",position:"left top",size:"auto",repeat:"no-repeat",origin:"padding-box"});for(var r={image:[],position:[],size:[],repeat:[],origin:[]},s=0;s=0;h-=1){var g=u[h];if(g)if(n.pub.presets.hasOwnProperty(g)){for(var p in n.pub.presets[g])if(n.pub.presets[g].hasOwnProperty(p))try{i(p,n.pub.presets[g][p])}catch(e){console.warn(e)}}else console.warn("Unknown preset: %s",g)}var f=["preset"];for(var p in o)if(o.hasOwnProperty(p)&&-1===f.indexOf(p))try{i(p,o[p])}catch(e){console.warn(e)}if(void 0===this.container?this.container=e.document.body:this.container=n.node(this.container),!this.container)throw new Error("Cannot instantiate color picker without a container element");if(this.targetElement=n.node(t),!this.targetElement){if("string"==typeof t&&/^[a-zA-Z][\w:.-]*$/.test(t))throw new Error("If '"+t+"' is supposed to be an ID, please use '#"+t+"' or any valid CSS selector.");throw new Error("Cannot instantiate color picker without a target element")}if(this.targetElement.jscolor&&this.targetElement.jscolor instanceof n.pub)throw new Error("Color picker already installed on this element");if(this.targetElement.jscolor=this,n.addClass(this.targetElement,n.pub.className),n.instances.push(this),n.isButton(this.targetElement)&&("button"!==this.targetElement.type.toLowerCase()&&(this.targetElement.type="button"),n.isButtonEmpty(this.targetElement))){n.removeChildren(this.targetElement),this.targetElement.appendChild(e.document.createTextNode(" "));var k=n.getCompStyle(this.targetElement);(parseFloat(k["min-width"])||0)-1){var b=n.parseColorString(M);this._currentFormat=b?b.format:"hex"}else this._currentFormat=this.format.toLowerCase();this.processValueInput(M),void 0!==y&&this.processAlphaInput(y),this.random&&this.randomize.apply(this,Array.isArray(this.random)?this.random:[])}},n.pub.className="jscolor",n.pub.activeClassName="jscolor-active",n.pub.looseJSON=!0,n.pub.presets={},n.pub.presets.default={},n.pub.presets.light={backgroundColor:"rgba(255,255,255,1)",controlBorderColor:"rgba(187,187,187,1)",buttonColor:"rgba(0,0,0,1)"},n.pub.presets.dark={backgroundColor:"rgba(51,51,51,1)",controlBorderColor:"rgba(153,153,153,1)",buttonColor:"rgba(240,240,240,1)"},n.pub.presets.small={width:101,height:101,padding:10,sliderSize:14,paletteCols:8},n.pub.presets.medium={width:181,height:101,padding:12,sliderSize:16,paletteCols:10},n.pub.presets.large={width:271,height:151,padding:12,sliderSize:24,paletteCols:15},n.pub.presets.thin={borderWidth:1,controlBorderWidth:1,pointerBorderWidth:1},n.pub.presets.thick={borderWidth:2,controlBorderWidth:2,pointerBorderWidth:2},n.pub.sliderInnerSpace=3,n.pub.chessboardSize=8,n.pub.chessboardColor1="#666666",n.pub.chessboardColor2="#999999",n.pub.previewSeparator=["rgba(255,255,255,.65)","rgba(128,128,128,.65)"],n.pub.init=function(){if(!n.initialized)for(e.document.addEventListener("mousedown",n.onDocumentMouseDown,!1),e.document.addEventListener("keyup",n.onDocumentKeyUp,!1),e.addEventListener("resize",n.onWindowResize,!1),e.addEventListener("scroll",n.onWindowScroll,!1),n.pub.install(),n.initialized=!0;n.readyQueue.length;)n.readyQueue.shift()()},n.pub.install=function(e){var t=!0;try{n.installBySelector("[data-jscolor]",e)}catch(e){t=!1,console.warn(e)}if(n.pub.lookupClass)try{n.installBySelector("input."+n.pub.lookupClass+", button."+n.pub.lookupClass,e)}catch(e){}return t},n.pub.ready=function(e){return"function"!=typeof e?(console.warn("Passed value is not a function"),!1):(n.initialized?e():n.readyQueue.push(e),!0)},n.pub.trigger=function(e){var t=function(){n.triggerGlobal(e)};n.initialized?t():n.pub.ready(t)},n.pub.hide=function(){n.picker&&n.picker.owner&&n.picker.owner.hide()},n.pub.chessboard=function(e){return e||(e="rgba(0,0,0,0)"),n.genColorPreviewCanvas(e).canvas.toDataURL()},n.pub.background=function(e){var t=[];t.push(n.genColorPreviewGradient(e));var o=n.genColorPreviewCanvas();return t.push(["url('"+o.canvas.toDataURL()+"')","left top","repeat"].join(" ")),t.join(", ")},n.pub.options={},n.pub.lookupClass="jscolor",n.pub.installByClassName=function(){return console.error('jscolor.installByClassName() is DEPRECATED. Use data-jscolor="" attribute instead of a class name.'+n.docsRef),!1},n.register(),n.pub);return void 0===e.jscolor&&(e.jscolor=e.JSColor=i),i}))}},t={};function o(a){var n=t[a];if(void 0!==n)return n.exports;var i=t[a]={exports:{}};return e[a].call(i.exports,i,i.exports,o),i.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";class e{constructor(e,t,o){this.defaultValue=e,this.type=t,this.group=o}}function t(e,t){Game.promptWrapL.className="framed",Game.promptL.innerHTML=`${e}
`,Object.keys(t).forEach((e=>{const o=document.createElement("a");o.id=`promptOption${e}`,o.className="option",o.onclick=function(){PlaySound("snd/tick.mp3"),t[e][1]()},o.textContent=t[e][0],Game.promptL.children[1].appendChild(o)})),Game.promptAnchorL.style.display="block",Game.darkenL.style.display="block",Game.promptL.focus(),Game.promptOn=1,Game.UpdatePrompt()}function a(e,t,o,n){(1===Game.mods.cookieMonsterFramework.saveData[e].settings[o]||n)&&3===t&&!1===window.cookieMonsterFrameworkData.isInitializing||1===t?(l("CMFlashScreen").style.backgroundColor=Game.mods.cookieMonsterFramework.saveData[e].settings[`Colour${o}`],l("CMFlashScreen").style.opacity="0.5",3===t?(l("CMFlashScreen").style.display="inline",setTimeout((()=>{a(e,2,o,!0)}),1e3/Game.fps)):setTimeout((()=>{a(e,0,o,!0)}),1e3/Game.fps)):2===t?(l("CMFlashScreen").style.opacity="1",setTimeout((()=>{a(e,1,o,!0)}),1e3/Game.fps)):0===t&&(l("CMFlashScreen").style.display="none")}function n(){Object.keys(Game.mods.cookieMonsterFramework.saveData).forEach((e=>{const t=JSON.stringify(Game.mods.cookieMonsterFramework.saveData[e]),o=b64_to_utf8(unescape(localStorage.getItem("CookieClickerGame")).split("!END!")[0]),a=new RegExp(`${e}.*(;|$)`),n=o.match(a);if(null!==n){const a=o.replace(n[0],`${e}:${t}`);localStorage.setItem("CookieClickerGame",escape(`${utf8_to_b64(a)}!END!`))}}))}function i(e,t){void 0===Game.mods.cookieMonsterFramework.saveData[e].headers[t]&&(Game.mods.cookieMonsterFramework.saveData[e].headers[t]=1),0===Game.mods.cookieMonsterFramework.saveData[e].headers[t]?Game.mods.cookieMonsterFramework.saveData[e].headers[t]=1:Game.mods.cookieMonsterFramework.saveData[e].headers[t]=0,n()}var r=o(877),s=o.n(r);function c(e,t,o,a,n){if((1===Game.mods.cookieMonsterFramework.saveData[e].settings[o]||n)&&!1===window.cookieMonsterFrameworkData.isInitializing){const o=new Audio(t);Game.mods.cookieMonsterFramework.saveData[e].settings.GeneralSound?o.volume=Game.mods.cookieMonsterFramework.saveData[e].settings[a]/100*(Game.volume/100):o.volume=Game.mods.cookieMonsterFramework.saveData[e].settings[a]/100,o.play()}}function d(e,t){null!==l(`slider${e}${t}`)&&(l(`slider${e}${t}right`).innerHTML=`${l(`slider${e}${t}`).value}%`,Game.mods.cookieMonsterFramework.saveData[e].settings[t]=Math.round(l(`slider${e}${t}`).value)),n()}function m(e,t){t.disconnect(),"log"===Game.onMenu?function(){const e=l("menu").children[1];if(e.insertBefore(function(){const e=document.createElement("div");e.className="subsection",e.id="cookieMonsterFrameworkMenuSection";const t=document.createElement("div");t.className="title",t.innerHTML="Cookie Monster Mod Family";const o=document.createElement("span");if(o.style.cursor="pointer",o.style.display="inline-block",o.style.height="14px",o.style.width="14px",o.style.borderRadius="7px",o.style.textAlign="center",o.style.backgroundColor="#C0C0C0",o.style.color="black",o.style.fontSize="13px",o.style.verticalAlign="middle",o.textContent=Game.mods.cookieMonsterFramework.saveData.cookieMonsterFramework.headers.infoMenu?"-":"+",o.onclick=function(){i("cookieMonsterFramework","infoMenu"),Game.UpdateMenu()},t.appendChild(o),e.appendChild(t),Game.mods.cookieMonsterFramework.saveData.cookieMonsterFramework.headers.infoMenu){const t=document.createElement("div");t.className="listing",t.innerHTML='Cookie Monster Team\noffers a suite of tools to enhance your game experience.
\nOriginally known from our work on the Cookie Monster add-on we are now expanding and working on new tools within the Cookie Monster Mod Family.
\nKeep an eye on our GitHub to see future work or use it to report bugs or feature requests!
\n',e.appendChild(t)}return e}(),e.children[1]),Game.mods.cookieMonsterFramework.saveData.cookieMonsterFramework.headers.infoMenu){const e=Game.mods.cookieMonsterFramework.listeners.infoMenu;for(let t=0;t{void 0===e.settings||void 0===e.settings[o]?n[o]=t[o].defaultValue:n[o]=e.settings[o]})),a.settings=n;const i={};return Object.keys(o).forEach((t=>{void 0===e.headers||void 0===e.headers[t]?i[t]=o[t]:i[t]=e.headers[t]})),a.headers=i,Object.keys(e).forEach((t=>{"settings"!==t&&"headers"!==t&&(a[t]=e[t])})),void 0===a.favouriteSettings&&(a.favouriteSettings=[]),a}(r,o,a),n(),i(),Object.keys(Game.mods.cookieMonsterFramework.saveData[e].settings).forEach((e=>{void 0!==o[e].func&&o[e].func()})),Game.UpdateMenu()}function g(){}const f={};function k(e){h("cookieMonsterFramework",e,f,u,g)}const C={init:function(){window.cookieMonsterFrameworkData={isInitializing:!0},new MutationObserver(m).observe(document.getElementById("menu"),{attributes:!0,childList:!0,subtree:!0}),function(){const e=document.createElement("div");e.id="CMFlashScreen",e.style.width="100%",e.style.height="100%",e.style.backgroundColor="white",e.style.display="none",e.style.zIndex="9999999999",e.style.position="absolute",e.style.pointerEvents="none",l("wrapper").appendChild(e)}(),Game.registerHook("draw",p),void 0===Game.modSaveData.cookieMonsterFramework&&k("{}"),window.cookieMonsterFrameworkData.isInitializing=!1},load:k,save:function(){return JSON.stringify(Game.mods.cookieMonsterFramework.saveData.cookieMonsterFramework)},listeners:{infoMenu:[],optionsMenu:[]},saveData:{cookieMonsterFramework:{headers:{},settings:{}}}},M=function(){"undefined"==typeof cookieMonsterFrameworkData&&Game.registerMod("cookieMonsterFramework",C)},y=function(e){Game.mods.cookieMonsterFramework.saveData[e]={favouriteSettings:[],headers:{},settings:{}}},b={createInfoListing:function(e){const t=document.createElement("div");return t.className="listing",t.innerHTML=e,t},createOptionsListing:function(e,o,i,m,p){const u=document.createElement("div");if(u.className="listing",1===Game.mods.cookieMonsterFramework.saveData[e].settings.FavouriteSettings&&u.appendChild(function(e,t,o){const a=document.createElement("a");return o.includes(t)?(a.innerText="★",a.style.color="yellow"):a.innerText="☆",a.className="option",a.onclick=function(){!function(e,t){Game.mods.cookieMonsterFramework.saveData[e].favouriteSettings.includes(t)?Game.mods.cookieMonsterFramework.saveData[e].favouriteSettings=Game.mods.cookieMonsterFramework.saveData[e].favouriteSettings.filter((e=>e!==t)):Game.mods.cookieMonsterFramework.saveData[e].favouriteSettings.push(t),n()}(e,t),n(),Game.UpdateMenu()},a.onmouseover=function(){Game.tooltip.draw(this,escape('
Click to set this setting as favourite and show it in \'favourite\' settings at the top of the Cookie Monster Settings
'))},a.onmouseout=function(){Game.tooltip.hide()},a.appendChild(document.createTextNode(" ")),a}(e,o,Game.mods.cookieMonsterFramework.saveData[e].favouriteSettings)),"bool"===i[o].type){const t=document.createElement("a");i[o].toggle&&0===Game.mods.cookieMonsterFramework.saveData[e].settings[o]?t.className="option off":t.className="option",t.id=`${e}Options${o}`,t.onclick=function(){!function(e,t,o){Game.mods.cookieMonsterFramework.saveData[e].settings[t]+=1,Game.mods.cookieMonsterFramework.saveData[e].settings[t]===o[t].label.length?(Game.mods.cookieMonsterFramework.saveData[e].settings[t]=0,o[t].toggle&&(l(`${e}Options${t}`).className="option off")):l(`${e}Options${t}`).className="option",void 0!==o[t].func&&o[t].func(),n()}(e,o,i),Game.UpdateMenu()},t.textContent=i[o].label[Game.mods.cookieMonsterFramework.saveData[e].settings[o]],u.appendChild(t);const a=document.createElement("label");return a.textContent=i[o].desc,a.style.lineHeight="1.6",u.appendChild(a),u}if("vol"===i[o].type){const t=document.createElement("div");t.className="sliderBox";const a=document.createElement("div");a.style.float="left",a.innerHTML=i[o].desc,t.appendChild(a);const n=document.createElement("div");n.id=`slider${e}${o}right`,n.style.float="right",n.innerHTML=`${Game.mods.cookieMonsterFramework.saveData[e].settings[o]}%`,t.appendChild(n);const r=document.createElement("input");r.className="slider",r.id=`slider${e}${o}`,r.style.clear="both",r.type="range",r.min="0",r.max="100",r.step="1",r.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o],r.oninput=function(){d(e,o),Game.UpdateMenu()},r.onchange=function(){d(e,o),Game.UpdateMenu()},t.appendChild(r),u.appendChild(t);const s=document.createElement("a");return s.className="option",s.onclick=function(){c(e,Game.mods.cookieMonsterFramework.saveData[e].settings[o.replace("Volume","SoundURL")],o.replace("Volume","Sound"),o,!0)},s.textContent="Test sound",u.appendChild(s),u}if("url"===i[o].type){const a=document.createElement("span");a.className="option",a.textContent=`${i[o].label} `,a.style.lineHeight="1.6",u.appendChild(a);const r=document.createElement("input");r.id=`${e}Options${o}`,r.className="option",r.type="text",r.readOnly=!0,r.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o],r.style.width="300px",u.appendChild(r),u.appendChild(document.createTextNode(" "));const s=document.createElement("input");s.id=`${e}Options${o}Prompt`,s.className="option",s.type="text",s.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o];const c=document.createElement("a");c.className="option",c.onclick=function(){t(s.outerHTML,[["Save",function(){Game.mods.cookieMonsterFramework.saveData[e].settings[o]=l(`${e}Options${o}Prompt`).value,n(),Game.ClosePrompt(),Game.UpdateMenu()}],["Cancel",function(){Game.ClosePrompt()}]])},c.textContent="Edit",u.appendChild(c);const d=document.createElement("label");return d.textContent=i[o].desc,d.style.lineHeight="1.6",u.appendChild(d),u}if("colour"===i[o].type){const t=document.createElement("span");t.className="option";const l=document.createElement("input");l.id=o,l.style.width="65px",l.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o],t.appendChild(l),new r(l,{hash:!0,position:"right",onInput:function(){Game.mods.cookieMonsterFramework.saveData[e].settings[this.targetElement.id]=this.toHEXString(),m(),n(),Game.UpdateMenu()}});const c=document.createElement("label");if(c.textContent=i[o].desc,c.style.lineHeight="1.6",t.appendChild(c),o.includes("Flash")){const n=document.createElement("a");n.className="option",n.onclick=function(){a(e,3,o.replace("Colour",""),!0)},n.textContent="Test flash",t.appendChild(n)}return u.appendChild(t),s().init(),u}if("numscale"===i[o].type){const t=document.createElement("span");t.className="option",t.textContent=`${i[o].label} `,t.style.lineHeight="1.6",u.appendChild(t);const a=document.createElement("input");a.id=`${e}Options${o}`,a.className="option",a.type="number",a.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o],a.min=i[o].min,a.max=i[o].max,a.oninput=function(){Game.mods.cookieMonsterFramework.saveData[e].settings[o]=this.value,n(),p(),Game.UpdateMenu()},u.appendChild(a),u.appendChild(document.createTextNode(" "));const r=document.createElement("label");return r.textContent=i[o].desc,r.style.lineHeight="1.6",u.appendChild(r),u}if("keycode"===i[o].type){const a=document.createElement("input");a.id=`${e}Options${o}Prompt`,a.className="option",a.type="text",a.value=Game.mods.cookieMonsterFramework.saveData[e].settings[o].displayName;const r=document.createElement("a");r.className="option",r.id=`${e}Options${o}`,r.onclick=function(){t(a.outerHTML,[]),l(`${e}Options${o}Prompt`).addEventListener("keyup",(t=>{!function(e,t,o){const a={key:o.key,altKey:o.altKey,ctrlKey:o.ctrlKey,shiftKey:o.shiftKey,displayName:`${o.shiftKey?"Shift + ":""}${o.altKey?"Alt + ":""}${o.ctrlKey?"Ctrl + ":""}${o.key}`};Game.mods.cookieMonsterFramework.saveData[e].settings[t]=a,n()}(e,o,t),Game.ClosePrompt(),Game.UpdateMenu()}))},r.textContent=Game.mods.cookieMonsterFramework.saveData[e].settings[o].displayName,u.appendChild(r);const s=document.createElement("label");return s.textContent=i[o].desc,s.style.lineHeight="1.6",u.appendChild(s),u}return u},createOptionsSubHeader:function(e,t,o){const a=document.createElement("div");a.className="title",a.style.opacity="0.7",a.style.fontSize="17px",a.appendChild(document.createTextNode(`${o} `));const n=document.createElement("span");return n.style.cursor="pointer",n.style.display="inline-block",n.style.height="14px",n.style.width="14px",n.style.borderRadius="7px",n.style.textAlign="center",n.style.backgroundColor="#C0C0C0",n.style.color="black",n.style.fontSize="13px",n.style.verticalAlign="middle",n.textContent=Game.mods.cookieMonsterFramework.saveData[e].headers[t]?"-":"+",n.onclick=function(){i(e,t),Game.UpdateMenu()},a.appendChild(n),a}},G=function(e,t,o){const a=document.createElement("div");a.className="subsection",a.id=`${e}MenuSection`;const n=document.createElement("div");n.className="title",n.style.fontSize="18px",n.innerHTML=t;const r=document.createElement("span");return r.style.cursor="pointer",r.style.display="inline-block",r.style.height="14px",r.style.width="14px",r.style.borderRadius="7px",r.style.textAlign="center",r.style.backgroundColor="#C0C0C0",r.style.color="black",r.style.fontSize="13px",r.style.verticalAlign="middle",r.textContent=Game.mods.cookieMonsterFramework.saveData[e].headers[o]?"-":"+",r.onclick=function(){i(e,o),Game.UpdateMenu()},n.appendChild(r),a.appendChild(n),a},v={createFlash:a,createNotification:function(e,t,o,a){1===Game.mods.cookieMonsterFramework.saveData[e].settings[t]&&"hidden"===document.visibilityState&&!1===window.cookieMonsterFrameworkData.isInitializing&&new Notification(o,{body:a,badge:"https://orteil.dashnet.org/cookieclicker/favicon.ico"})},cookieMonsterPrompt:t,playCMSound:c},w={loadMod:h,saveFramework:n},x=class extends e{constructor(e,t,o,a){super(e,t,o),this.desc=a}},T=class extends e{constructor(e,t,o,a,n,i,r){super(e,t,o),this.label=a,this.desc=n,this.min=i,this.max=r}},S=class extends e{constructor(e,t,o,a,n,i){super(e,t,o),this.label=a,this.desc=n,this.toggle=i}},F=class extends e{constructor(e,t,o,a,n,i,r){super(e,t,o),this.label=a,this.desc=n,this.toggle=i,void 0!==r&&(this.func=r)}},E=class extends e{constructor(e,t,o,a,n){super(e,t,o),this.label=a,this.desc=n;for(let e=0;e<101;e++)this.label[e]=`${e}%`}};function B(){Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerButtons&&Game.elderWrath?(l("PopAllNormalWrinklerButton").style.display="",l("PopFattestWrinklerButton").style.display=""):(l("PopAllNormalWrinklerButton").style.display="none",l("PopFattestWrinklerButton").style.display="none")}let P,N,O,D,L,A,W,j,$,U,H,R,I,z,V,_,X,Q,q,Y,K,J,Z,ee,te,oe,ae,ne,ie,re,se,le,ce,de,me,pe,ue=0,he=0,ge=0,fe=0,ke=0,Ce=0,Me=0,ye=0,be=0,Ge=0,ve=0,we=0,xe=0,Te=0,Se=0,Fe=1,Ee=1,Be=1,Pe=0,Ne=0,Oe=0,De=0,Le={},Ae={},We={},je={},$e=0,Ue=0,He=[0,null],Re=0,Ie=0,ze={},Ve={},_e={},Xe={},Qe={},qe=0,Ye=0,Ke=[],Je=0,Ze=0,et=[],tt=0,ot=0,at=[],nt=0,it=0,rt=[],st=0,lt=0,ct={},dt={},mt=0,pt=0,ut=0,ht=[],gt={},ft=0,kt={0:[0,0,0],1:[0,0,0],2:[0,0,0],3:[0,0,0],4:[0,0,0],5:[0,0,0],6:[0,0,0],7:[0,0,0],8:[0,0,0],9:[0,0,0],10:[0,0,0]},Ct=[],Mt=[],yt=[];function bt(e){let t=0;return Game.dragonAuras[ne].name!==e&&Game.dragonAuras[ie].name!==e||(t=1),("Reality Bending"===Game.dragonAuras[ne].name||"Reality Bending"===Game.dragonAuras[ie].name&&Game.dragonLevel>=Game.dragonAurasBN[e].id+4)&&(t+=.1),t}function Gt(){let e=.25;return e*=1+bt("Earth Shatterer"),e}function vt(e,t){return void 0===de[e]?void 0===t?1:t:de[e]}function wt(e){const t=Mt[e];return(!t||1!==Game.ascensionMode||"prestige"!==t.pool&&"fortune"!==t.tier)&&t?t.bought:0}function xt(e){return Game.dragonAuras[ne].name===e||Game.dragonAuras[ie].name===e}function Tt(e){if(Game.hasGod){void 0===Ct.Temple.minigame&&(Ct.Temple.minigame=Game.Objects.Temple.minigame);const t=Ct.Temple.minigame.gods[e];if(re===t.id)return 1;if(se===t.id)return xt("Supreme Intellect")?1:2;if(le===t.id)return xt("Supreme Intellect")?2:3}return!1}function St(e,t){let o=t;if(wt("Season savings")&&(o*=.99),wt("Santa's dominion")&&(o*=.99),wt("Faberge egg")&&(o*=.99),wt("Divine discount")&&(o*=.99),wt("Fortune #100")&&(o*=.99),o*=1-.02*bt("Fierce Hoarder"),Game.hasBuff("Everything must go")&&(o*=.95),Game.hasBuff("Crafty pixies")&&(o*=.98),Game.hasBuff("Nasty goblins")&&(o*=1.02),e.fortune&&wt(e.fortune.name)&&(o*=.93),o*=vt("buildingCost"),Ct.Temple.minigameLoaded){const e=Tt("creation");1===e?o*=.93:2===e?o*=.95:3===e&&(o*=.98)}return o}function Ft(e,t,o,a,n,i){const r=void 0===i?0:i;let s=n,l=o,c=0;-1===n&&(s=l),n||(s=Game.buyBulk);for(let o=0;o0&&(c+=o,l-=1)}return c}const Et=["","","M","G","T","P","E","Z","Y"],Bt=["","","M","B","Tr","Quadr","Quint","Sext","Sept","Oct","Non","Dec","Undec","Duodec","Tredec","Quattuordec","Quindec","Sexdec","Septendec","Octodec","Novemdec","Vigint","Unvigint","Duovigint","Trevigint","Quattuorvigint"],Pt=["","K","M","B","T","Qa","Qi","Sx","Sp","Oc","No","De","UDe","DDe","TDe","QaDe","QiDe","SxDe","SpDe","ODe","NDe","Vi","UVi","DVi","TVi","QaVi","QiVi","SxVi","SpVi","OVi","NVi","Tr","UTr","DTr","TTr","QaTr","QiTr","SxTr","SpTr","OTr","NTr","Qaa","UQa","DQa","TQa","QaQa","QiQa","SxQa","SpQa","OQa","NQa","Qia","UQi","DQi","TQi","QaQi","QiQi","SxQi","SpQi","OQi","NQi","Sxa","USx","DSx","TSx","QaSx","QiSx","SxSx","SpSx","OSx","NSx","Spa","USp","DSp","TSp","QaSp","QiSp","SxSp","SpSp","OSp","NSp","Oco","UOc","DOc","TOc","QaOc","QiOc","SxOc","SpOc","OOc","NOc","Noa","UNo","DNo","TNo","QaNo","QiNo","SxNo","SpNo","ONo","NNo","Ct","UCt"];let Nt,Ot,Dt,Lt,At,Wt,jt,$t,Ut,Ht,Rt,It,zt,Vt,_t,Xt,Qt,qt=[],Yt=[],Kt=[],Jt={},Zt=Date.now(),eo=Date.now();function to(e,t,o){const a=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ScaleDecimals+1;if(e===1/0)return"Infinity";if(void 0===e)return"0";if(0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Scale)return Jt.Beautify(e,t);if(Number.isFinite(e)){if(e<0)return`-${to(Math.abs(e))}`;let n="";if(0===e)return e.toString();if(e>.001&&e=.01&&e=.01&&e=.01&&e{l(`productPrice${Game.Objects[e].id}`).style.color=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings[`Colour${t[e].colour}`]})),l(`storeBulk${ut}`).style.color=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ColourGreen):Object.keys(Game.Objects).forEach((e=>{l(`productPrice${Game.Objects[e].id}`).style.removeProperty("color")})):-1===Game.buyMode&&Object.keys(Le).forEach((e=>{const t=Game.Objects[e];l(`productPrice${t.id}`).style.color="",l(`productPrice${t.id}`).innerHTML=to(Ft(t,t.basePrice,t.amount,t.free,Game.buyBulk,1))})),1===Game.buyMode&&Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SortBuildings?1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SortBuildings?(e=Object.keys(Le).map((e=>{const t={};return t.name=e,t.pp=Le[e].pp,t.colour=Le[e].colour,t})),e.sort(((e,t)=>ho.indexOf(e.colour)===ho.indexOf(t.colour)?e.pp-t.pp:ho.indexOf(e.colour)-ho.indexOf(t.colour)))):2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SortBuildings?(e=Object.keys(t).map((e=>{const o={};return o.name=e,o.pp=t[e].pp,o.colour=t[e].colour,o})),e.sort(((e,t)=>ho.indexOf(e.colour)===ho.indexOf(t.colour)?e.pp-t.pp:ho.indexOf(e.colour)-ho.indexOf(t.colour)))):3===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SortBuildings&&(e=Object.keys(je).map((e=>{const t={};return t.name=e,t.id=Game.Objects[e].id,t.amountUntilNext=je[e].AmountNeeded,t.priceUntilNext=je[e].price,t})),e.sort(((e,t)=>e.id-t.id)),e.sort(((e,t)=>(101!==e.amountUntilNext?e.priceUntilNext:1/0)-(101!==t.amountUntilNext?t.priceUntilNext:1/0)))):(e=Object.keys(Le).map((e=>{const t={};return t.name=e,t.id=Game.Objects[e].id,t})),e.sort(((e,t)=>e.id-t.id)));for(let t=0;t{Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpgradesNeverCollapse||"products"===e.id?e.style.height="auto":"vaultUpgrades"===e.id?(e.style.height="",e.style.minHeight="0px"):"upgrades"===e.id?(e.style.height="",e.className.includes("hasMenu")?e.style.minHeight="82px":e.style.minHeight="60px"):(e.style.height="",e.style.minHeight="60px")}))}function $o(){if(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpBarColour>0){let e=0,t=0,o=0,a=0,n=0,i=0,r=0;Object.keys(Game.UpgradesInStore).forEach((s=>{const c=Game.UpgradesInStore[s];let d=!1;for(let e=0;eho.indexOf(e.colour)===ho.indexOf(t.colour)?e.pp-t.pp:ho.indexOf(e.colour)-ho.indexOf(t.colour))):e.sort(((e,t)=>e.price-t.price));const t=function(e,t){return e.findIndex((e=>e.name===t.name))};for(let o=0;o7776e5)return t?"Over 9000 days!":">9000d";l+=a>0?a+(t?1===a?" year":" years":"y")+", ":"",(l.length>0||n>0)&&(l+=n+(t?1===n?" day":" days":"d")+", "),(l.length>0||i>0)&&(l+=i+(t?1===i?" hour":" hours":"h")+", "),(l.length>0||r>0)&&(l+=r+(t?1===r?" minute":" minutes":"m")+", "),l+=s+(t?1===s?" second":" seconds":"s")}else if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimeFormat){if(o>315576e4)return"XX:XX:XX:XX:XX";l+=(a<10?"0":"")+a+":",l+=(n<10?"0":"")+n+":",l+=(i<10?"0":"")+i+":",l+=(r<10?"0":"")+r+":",l+=(s<10?"0":"")+s}else{if(o>7776e5)return t?"Over 9000 days!":">9000d";a>0?(l+=a+(t?1===a?" year":" years":"y")+", ",l+=n+(t?1===n?" day":" days":"d")):n>0?(l+=n+(t?1===n?" day":" days":"d")+", ",l+=i+(t?1===i?" hour":" hours":"h")):i>0?(l+=i+(t?1===i?" hour":" hours":"h")+", ",l+=r+(t?1===r?" minute":" minutes":"m")):r>0?(l+=r+(t?1===r?" minute":" minutes":"m")+", ",l+=s+(t?1===s?" second":" seconds":"s")):l+=s+(t?1===s?" second":" seconds":"s")}return l}function Ho(e){let t,o;return e<=0?(o=1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimeFormat?"00:00:00:00:00":"Done!",t=io):(o=Uo(e),t=e>300?lo:e>60?so:ro),{text:o,colour:t}}function Ro(){return Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CPSMode?X:0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CalcWrink?Game.cookiesPs*(1-Game.cpsSucked):1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CalcWrink?Game.cookiesPs*(Re+(1-.05*Ie)):null!==He[1]&&2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CalcWrink&&1===Game.wrinklers[He[1]].type?Game.cookiesPs*(3*Re/Ie+(1-.05*Ie)):Game.cookiesPs*(Re/Ie+(1-.05*Ie))}function Io(){return 1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CalcWrink?$e:2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.CalcWrink?He[0]:0}function zo(e,t,o){const a=document.createElement("div");a.id=e,a.style.height="12px",a.style.margin="0px 10px",a.style.position="relative";const n=document.createElement("div");n.style.width="100%",n.style.height="10px",n.style.margin="auto",n.style.position="absolute",n.style.left="0px",n.style.top="0px",n.style.right="0px",n.style.bottom="0px";const i=document.createElement("span");i.style.display="inline-block",i.style.textAlign="right",i.style.fontSize="10px",i.style.width="108px",i.style.marginRight="5px",i.style.verticalAlign="text-top",i.textContent=t,n.appendChild(i);for(let e=0;e{let o,a=Game.buyBulk;1===Game.buyMode?Go=a:a=Go,1===a&&(a=Le),10===a&&(a=Ae),100===a&&(a=We),e+=1,l("CMBotBar").firstChild.firstChild.childNodes[0].childNodes[e].childNodes[1].textContent=Game.Objects[t].amount,l("CMBotBar").firstChild.firstChild.childNodes[1].childNodes[e].textContent=to(a[t].bonus,2),l("CMBotBar").firstChild.firstChild.childNodes[2].childNodes[e].className=oo+a[t].colour,o=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPDisplayTime?Uo(Math.round(a[t].pp)):to(Math.round(a[t].pp),2),l("CMBotBar").firstChild.firstChild.childNodes[2].childNodes[e].textContent=o;const n=Ho((Game.Objects[t].bulkPrice-(Game.cookies+Io()))/Ro());l("CMBotBar").firstChild.firstChild.childNodes[3].childNodes[e].className=oo+n.colour,"Done!"===n.text&&Game.cookies{if(!Game.Tiers[e.tieredUpgrades[o].tier].special&&wt(e.tieredUpgrades[o].name)){let a=2;1!==Game.ascensionMode&&wt(e.unshackleUpgrade)&&wt(Game.Tiers[e.tieredUpgrades[o].tier].unshackleUpgrade)&&(a+=1===e.id?.5:.1*(20-e.id)),t*=a}})),Object.keys(e.synergies).forEach((o=>{if(wt(e.synergies[o].name)){const a=e.synergies[o];a.buildingTie1.name===e.name?t*=1+.05*a.buildingTie2.amount:a.buildingTie2.name===e.name&&(t*=1+.001*a.buildingTie1.amount)}})),e.fortune&&wt(e.fortune.name)&&(t*=1.07),e.grandma&&wt(e.grandma.name)&&(t*=1+.01*Ct.Grandma.amount*(1/(e.id-1))),"object"==typeof e.tieredUpgrades.misfortune&&1===e.vanilla&&wt(e.tieredUpgrades.misfortune.name))switch(Game.elderWrath){default:t*=1;break;case 1:t*=1.02;break;case 2:t*=1.04;break;case 3:t*=1.06}return t}function Zo(e){const t=Game.Objects[e],o={};return"Cursor"===t.name?o.cps=function(e){let t=0;wt("Thousand fingers")&&(t+=.1),wt("Million fingers")&&(t*=5),wt("Billion fingers")&&(t*=10),wt("Trillion fingers")&&(t*=20),wt("Quadrillion fingers")&&(t*=20),wt("Quintillion fingers")&&(t*=20),wt("Sextillion fingers")&&(t*=20),wt("Septillion fingers")&&(t*=20),wt("Octillion fingers")&&(t*=20),wt("Nonillion fingers")&&(t*=20),wt("Decillion fingers")&&(t*=20),wt("Undecillion fingers")&&(t*=20),wt("Unshackled cursors")&&(t*=25);let o=1,a=0;return Object.keys(Ct).forEach((e=>{"Cursor"!==Ct[e].name&&(a+=Ct[e].amount)})),t*=a,o*=Jo(e),o*=Game.magicCpS("Cursor"),o*=vt("cursorCps"),Game.ComputeCps(.1,wt("Reinforced index finger")+wt("Carpal tunnel prevention cream")+wt("Ambidextrous"),t)*o}:"Grandma"===t.name?o.cps=function(e){let o=1;if(Object.keys(Game.GrandmaSynergies).forEach((e=>{wt(Game.GrandmaSynergies[e])&&(o*=2)})),wt("Bingo center/Research facility")&&(o*=4),wt("Ritual rolling pins")&&(o*=2),wt("Naughty list")&&(o*=2),wt("Elderwort biscuits")&&(o*=1.02),o*=vt("grandmaCps"),wt("Cat ladies"))for(let e=0;e{"Grandma"!==Ct[e].name&&(n+=Ct[e].amount)})),o*=1+.01*bt("Elder Battalion")*n,o*=Game.magicCpS(t.name),(t.baseCps+a)*o}:o.cps=function(e){let t=1;return t*=Jo(e),t*=Game.magicCpS(e.name),e.baseCPS*t},o.baseCps=t.baseCps,o.name=t.name,o.tieredUpgrades=t.tieredUpgrades,o.synergies=t.synergies,o.fortune=t.fortune,o.grandma=t.grandma,o.baseCPS=t.baseCps,o.id=t.id,o.vanilla=t.vanilla,o.unshackleUpgrade=t.unshackleUpgrade,o}function ea(e){const t=Game.Upgrades[e],o={};return o.power=t.power,"function"==typeof o.power&&("Sugar crystal cookies"===t.name?o.power=function(){let e=5;return Object.keys(Ct).forEach((t=>{Ct[t].level>=10&&(e+=1)})),e}:o.power=function(){let e=2;if(wt("Starlove")&&(e=3),Game.hasGod){const t=Tt("seasons");1===t?e*=1.3:2===t?e*=1.2:3===t&&(e*=1.1)}return e}),o.pool=t.pool,o.name=t.name,o}function ta(){Z=Game.UpgradesOwned,ee=Game.pledges,te=Game.AchievementsOwned,oe=Game.heavenlyPower,ae=Game.prestige,Object.keys(Game.Objects).forEach((e=>{const t=Game.Objects[e];let o=Ct[e];void 0===o&&(Ct[e]=Zo(e),o=Ct[e],Vo(e)),o.amount=t.amount,o.level=t.level,o.totalCookies=t.totalCookies,o.basePrice=t.basePrice,o.free=t.free,t.minigameLoaded&&("Temple"===t.name&&(re=t.minigame.slot[0],se=t.minigame.slot[1],le=t.minigame.slot[2]),o.minigameLoaded=t.minigameLoaded,o.minigame=t.minigame),Ct[e]=o})),Object.keys(Game.Upgrades).forEach((e=>{const t=Game.Upgrades[e];let o=Mt[e];void 0===o&&(Mt[e]=ea(e),o=Mt[e]),o.bought=t.bought,Mt[e]=o})),Object.keys(Game.Achievements).forEach((e=>{const t=Game.Achievements[e];let o=yt[e];void 0===o&&(yt[e]=Ko(e),o=yt[e]),o.won=t.won,yt[e]=o})),Yo(),ne=ue,ie=he}function oa(e){const t=document.createElement("div");return t.style.fontWeight="bold",t.id=`${e}Title`,t.className="CMTextBlue",t.textContent=e,t}function aa(e){e.appendChild(oa("Bonus Income"));const t=document.createElement("div");t.style.marginBottom="4px",t.style.color="white",t.id="CMTooltipIncome",e.appendChild(t),e.appendChild(oa("Bonus Cookies per Click")),e.lastChild.style.display="none";const o=document.createElement("div");o.style.marginBottom="4px",o.style.color="white",o.style.display="none",o.id="CMTooltipCookiePerClick",e.appendChild(o),e.appendChild(oa("Payback Period"));const a=document.createElement("div");a.style.marginBottom="4px",a.id="CMTooltipPP",e.appendChild(a),e.appendChild(oa("Time Left"));const n=document.createElement("div");if(n.id="CMTooltipTime",e.appendChild(n),"b"===Bo){e.appendChild(oa("Production left till next achievement")),e.lastChild.id="CMTooltipProductionLeftHeader";const t=document.createElement("div");t.id="CMTooltipProductionLeft",e.appendChild(t)}if("b"===Bo){e.appendChild(oa("Buildings (price / PP) left till next achievement")),e.lastChild.id="CMTooltipNextAchievementHeader";const t=document.createElement("div");t.id="CMTooltipNextAchievement",e.appendChild(t)}}function na(e,t){let o="";return o=e.pp<=0||e.pp===1/0?mo:e.pp0&&(o=lo),o}function ia(){let e=1;return Object.keys(Game.buffs).forEach((t=>{void 0!==Game.buffs[t].multCpS&&(e*=Game.buffs[t].multCpS)})),e}function ra(e){yt[e]&&0===yt[e].won&&(yt[e].won=1,"shadow"!==Game.Achievements[e].pool&&(te+=1))}function sa(){me=0;let e=1;const t={};Object.keys(Game.Objects).forEach((e=>{if(Game.Objects[e].minigameLoaded&&Game.Objects[e].minigame.effs){const o=Game.Objects[e].minigame.effs;Object.keys(o).forEach((e=>{t[e]?t[e]*=o[e]:t[e]=o[e]}))}})),de=t,1!==Game.ascensionMode&&(e+=.01*parseFloat(ae)*oe*function(){let e=0;if(wt("Heavenly chip secret")&&(e+=.05),wt("Heavenly cookie stand")&&(e+=.2),wt("Heavenly bakery")&&(e+=.25),wt("Heavenly confectionery")&&(e+=.25),wt("Heavenly key")&&(e+=.25),e*=1+.05*bt("Dragon God"),wt("Lucky digit")&&(e*=1.01),wt("Lucky number")&&(e*=1.01),wt("Lucky payout")&&(e*=1.01),Game.hasGod){const t=Tt("creation");1===t?e*=.7:2===t?e*=.8:3===t&&(e*=.9)}return e}()),e*=vt("cps"),wt("Heralds")&&1!==Game.ascensionMode&&(e*=1+.01*Game.heralds),Object.keys(Game.cookieUpgrades).forEach((t=>{const o=Game.cookieUpgrades[t];wt(o.name)&&("function"==typeof o.power?e*=1+.01*Mt[o.name].power(Mt[o.name]):e*=1+.01*o.power)})),wt("Specialized chocolate chips")&&(e*=1.01),wt("Designer cocoa beans")&&(e*=1.02),wt("Underworld ovens")&&(e*=1.03),wt("Exotic nuts")&&(e*=1.04),wt("Arcane sugar")&&(e*=1.05),wt("Increased merriness")&&(e*=1.15),wt("Improved jolliness")&&(e*=1.15),wt("A lump of coal")&&(e*=1.01),wt("An itchy sweater")&&(e*=1.01),wt("Santa's dominion")&&(e*=1.2),wt("Fortune #100")&&(e*=1.01),wt("Fortune #101")&&(e*=1.07),wt("Dragon scale")&&(e*=1.03);let o=1;if(Tt){let t=Tt("asceticism");1===t?e*=1.15:2===t?e*=1.1:3===t&&(e*=1.05),t=Tt("ages"),1===t?e*=1+.15*Math.sin(Zt/1e3/10800*Math.PI*2):2===t?e*=1+.15*Math.sin(Zt/1e3/43200*Math.PI*2):3===t&&(e*=1+.15*Math.sin(Zt/1e3/86400*Math.PI*2)),t=Tt("decadence"),1===t?o*=.93:2===t?o*=.95:3===t&&(o*=.98),t=Tt("industry"),1===t?o*=1.1:2===t?o*=1.06:3===t&&(o*=1.03),t=Tt("labor"),1===t?o*=.97:2===t?o*=.98:3===t&&(o*=.99)}wt("Santa's legacy")&&(e*=1+.03*(Game.santaLevel+1));const a=te/25;let n=1;if(wt("Santa's milk and cookies")&&(n*=1.05),n*=1+.05*bt("Breath of Milk"),Tt){const e=Tt("mother");1===e?n*=1.1:2===e?n*=1.05:3===e&&(n*=1.03)}n*=vt("milk");let i=1;wt("Kitten helpers")&&(i*=1+.1*a*n),wt("Kitten workers")&&(i*=1+.125*a*n),wt("Kitten engineers")&&(i*=1+.15*a*n),wt("Kitten overseers")&&(i*=1+.175*a*n),wt("Kitten managers")&&(i*=1+.2*a*n),wt("Kitten accountants")&&(i*=1+.2*a*n),wt("Kitten specialists")&&(i*=1+.2*a*n),wt("Kitten experts")&&(i*=1+.2*a*n),wt("Kitten consultants")&&(i*=1+.2*a*n),wt("Kitten assistants to the regional manager")&&(i*=1+.175*a*n),wt("Kitten marketeers")&&(i*=1+.15*a*n),wt("Kitten analysts")&&(i*=1+.125*a*n),wt("Kitten executives")&&(i*=1+.115*a*n),wt("Kitten admins")&&(i*=1+.11*a*n),wt("Kitten strategists")&&(i*=1+.105*a*n),wt("Kitten angels")&&(i*=1+.1*a*n),wt("Fortune #103")&&(i*=1+.05*a*n),Object.keys(Ct).forEach((e=>{const t=Ct[e];let i=t.cps(t);1!==Game.ascensionMode&&(i*=(1+.01*t.level)*o),"Grandma"===t.name&&wt("Milkhelp® lactose intolerance relief tablets")&&(i*=1+.05*a*n),me+=t.amount*i})),wt('"egg"')&&(me+=9),e*=i;let r=1;if(wt("Chicken egg")&&(r*=1.01),wt("Duck egg")&&(r*=1.01),wt("Turkey egg")&&(r*=1.01),wt("Quail egg")&&(r*=1.01),wt("Robin egg")&&(r*=1.01),wt("Ostrich egg")&&(r*=1.01),wt("Cassowary egg")&&(r*=1.01),wt("Salmon roe")&&(r*=1.01),wt("Frogspawn")&&(r*=1.01),wt("Shark egg")&&(r*=1.01),wt("Turtle egg")&&(r*=1.01),wt("Ant larva")&&(r*=1.01),wt("Century egg")){let e=10*Math.floor((eo-Game.startDate)/1e3/10)/60/60/24;e=Math.min(e,100),Ne=1+.1*(1-(1-e/100)**3),r*=Ne}e*=r,wt("Sugar baking")&&(e*=1+.01*Math.min(100,Game.lumps)),e*=1+bt("Radiant Appetite");const s=me*e;Object.keys(Game.CpsAchievements).forEach((e=>{s>=Game.CpsAchievements[e].threshold&&ra(Game.CpsAchievements[e].name)})),pe=s;const{n:l}=Game.shimmerTypes.golden,c=bt("Dragon's Fortune");for(let t=0;t{wt(e[o])&&(t+=.1)}))}e*=t}if(wt("Shimmering veil [off]")){let t=.5;wt("Reinforced membrane")&&(t+=.1),wt("Delicate touch")&&(t+=.05),wt("Steadfast murmur")&&(t+=.05),wt("Glittering edge")&&(t+=.05),e*=1+t}wt("Magic shenanigans")&&(e*=1e3),wt("Occult obstruction")&&(e*=0),me=Game.runModHookOnValue("cps",me),e*=ia(),me*=e}const la=["Fortune #001","Fortune #002","Fortune #003","Fortune #004","Fortune #005","Fortune #006","Fortune #007","Fortune #008","Fortune #009","Fortune #010","Fortune #011","Fortune #012","Fortune #013","Fortune #014","Fortune #015","Fortune #016","Fortune #017","Fortune #018","Fortune #019","Fortune #020","Fortune #100","Fortune #101","Fortune #102","Fortune #103","Fortune #104"],ca=["Skull cookies","Ghost cookies","Bat cookies","Slime cookies","Pumpkin cookies","Eyeball cookies","Spider cookies"],da=["Christmas tree biscuits","Snowflake biscuits","Snowman biscuits","Holly biscuits","Candy cane biscuits","Bell biscuits","Present biscuits"],ma=["Pure heart biscuits","Ardent heart biscuits","Sour heart biscuits","Weeping heart biscuits","Golden heart biscuits","Eternal heart biscuits","Prism heart biscuits"],pa=["Elderwort biscuits","Bakeberry cookies","Duketater cookies","Green yeast digestives","Wheat slims","Fern tea","Ichor syrup"];function ua(e){return yt[e]?yt[e].won:0}function ha(){let e=0;Object.keys(Game.GrandmaSynergies).forEach((t=>{wt(Game.GrandmaSynergies[t])&&(e+=1)})),!ua("Elder")&&e>=7&&ra("Elder"),!ua("Veteran")&&e>=14&&ra("Veteran");let t=0,o=1,a=1,n=1e5;Object.keys(Ct).forEach((e=>{t+=Ct[e].amount,n=Math.min(Ct[e].amount,n),ua("Mathematician")||Ct[e].amount=1&&ra("One with everything"),1===o&&ra("Mathematician"),1===a&&ra("Base 10"),n>=100&&ra("Centennial"),n>=150&&ra("Centennial and a half"),n>=200&&ra("Bicentennial"),n>=250&&ra("Bicentennial and a half"),n>=300&&ra("Tricentennial"),n>=350&&ra("Tricentennial and a half"),n>=400&&ra("Quadricentennial"),n>=450&&ra("Quadricentennial and a half"),n>=500&&ra("Quincentennial"),n>=550&&ra("Quincentennial and a half"),n>=600&&ra("Sexcentennial"),n>=650&&ra("Sexcentennial and a half"),n>=700&&ra("Septcentennial"),t>=100&&ra("Builder"),t>=500&&ra("Architect"),t>=1e3&&ra("Engineer"),t>=2500&&ra("Lord of Constructs"),t>=5e3&&ra("Grand design"),t>=7500&&ra("Ecumenopolis"),t>=1e4&&ra("Myriad"),Z>=20&&ra("Enhancer"),Z>=50&&ra("Augmenter"),Z>=100&&ra("Upgrader"),Z>=200&&ra("Lord of Progress"),Z>=300&&ra("The full picture"),Z>=400&&ra("When there's nothing left to add"),Z>=500&&ra("Kaizen"),Z>=600&&ra("Beyond quality"),Z>=700&&ra("Oft we mar what's well"),t>=4e3&&Z>=300&&ra("Polymath"),t>=8e3&&Z>=400&&ra("Renaissance baker"),Ct.Cursor.amount+Ct.Grandma.amount>=777&&ra("The elder scrolls");let i=!0;Object.keys(ca).forEach((e=>{wt(ca[e])||(i=!1)})),i&&ra("Spooky cookies");let r=!0;if(Object.keys(da).forEach((e=>{wt(da[e])||(r=!1)})),r&&ra("Let it snow"),wt("Fortune cookies")){const e=Game.Tiers.fortune.upgrades;let t=0;Object.keys(e).forEach((o=>{wt(e[o].name)&&(t+=1)})),t>=e.length&&ra("O Fortuna")}}function ga(e,t){ta(),Ct[e].amount+=t;const o=Ct[e];"Cursor"===e?(o.amount>=1&&ra("Click"),o.amount>=2&&ra("Double-click"),o.amount>=50&&ra("Mouse wheel"),o.amount>=100&&ra("Of Mice and Men"),o.amount>=200&&ra("The Digital"),o.amount>=300&&ra("Extreme polydactyly"),o.amount>=400&&ra("Dr. T"),o.amount>=500&&ra("Thumbs, phalanges, metacarpals"),o.amount>=600&&ra("With her finger and her thumb"),o.amount>=700&&ra("Gotta hand it to you"),o.amount>=800&&ra("The devil's workshop"),o.amount>=900&&ra("All on deck"),o.amount>=1e3&&ra("A round of applause")):Object.keys(Game.Objects[o.name].tieredAchievs).forEach((e=>{o.amount>=Game.Tiers[Game.Objects[o.name].tieredAchievs[e].tier].achievUnlock&&ra(Game.Objects[o.name].tieredAchievs[e].name)}));const a=te;return sa(),ha(),a!==te&&sa(),me-Game.cookiesPs}function fa(e,t,o){let a=e,n=0;for(;ae.plantsById[e.plot[Po[1]][Po[0]][0]-1].mature,o=e.plantsById[e.plot[Po[1]][Po[0]][0]-1].name;l("CMTooltipBorder").appendChild(oa("Reward (Current / Maximum)"));const a=document.createElement("div");a.id="CMTooltipPlantReward",l("CMTooltipBorder").appendChild(a),"Chocoroot"===o||"White chocoroot"===o?l("CMTooltipPlantReward").textContent=`${t?to(Math.min(.03*Game.cookies,60*Game.cookiesPs*3)):"0"} / ${to(60*Game.cookiesPs*3)}`:"Bakeberry"===o?l("CMTooltipPlantReward").textContent=`${t?to(Math.min(.03*Game.cookies,60*Game.cookiesPs*30)):"0"} / ${to(60*Game.cookiesPs*30)}`:"Queenbeet"===o?l("CMTooltipPlantReward").textContent=`${t?to(Math.min(.04*Game.cookies,60*Game.cookiesPs*60)):"0"} / ${to(60*Game.cookiesPs*60)}`:"Duketater"===o?l("CMTooltipPlantReward").textContent=`${t?to(Math.min(.08*Game.cookies,60*Game.cookiesPs*120)):"0"} / ${to(60*Game.cookiesPs*120)}`:l("CMTooltipArea").style.display="none"}else l("CMTooltipArea").style.display="none"}():"ha"===Bo?function(){const{minigame:e}=Game.Objects.Farm;if(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TooltipLump){l("CMTooltipBorder").appendChild(oa("Cookies gained from harvesting:"));let t=0,o=0;Game.keys[16]&&Game.keys[17]&&(o=1);for(let a=0;a<6;a++)for(let n=0;n<6;n++)if(e.plot[a][n][0]>=1){const i=e.plot[a][n],r=e.plantsById[i[0]-1],s=r.name;let l=!0;o&&r.immortal&&(l=!1),i[1]0&&e=1?l("CMTimerBarAutosaveBar").textContent=Math.ceil(e):l("CMTimerBarAutosaveBar").textContent="",l("CMTimerBarAutosaveTime").textContent=Math.ceil(e),o+=1}else l("CMTimerBarAutosave").style.display="none";if(0!==Game.shimmerTypes.golden.spawned||Game.Has("Golden switch [off]"))l("CMTimerBarGC").style.display="none";else{l("CMTimerBarGC").style.display="",l("CMTimerBarGCMinBar").style.width=`${Math.round(Math.max(0,Game.shimmerTypes.golden.minTime-Game.shimmerTypes.golden.time)*e/Game.shimmerTypes.golden.maxTime)}px`,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarOverlay>=1?l("CMTimerBarGCMinBar").textContent=Math.ceil((Game.shimmerTypes.golden.minTime-Game.shimmerTypes.golden.time)/Game.fps):l("CMTimerBarGCMinBar").textContent="",Game.shimmerTypes.golden.minTime===Game.shimmerTypes.golden.maxTime?(l("CMTimerBarGCMinBar").style.borderTopRightRadius="10px",l("CMTimerBarGCMinBar").style.borderBottomRightRadius="10px"):(l("CMTimerBarGCMinBar").style.borderTopRightRadius="",l("CMTimerBarGCMinBar").style.borderBottomRightRadius=""),l("CMTimerBarGCBar").style.width=`${Math.round(Math.min(Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.minTime,Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.time)*e/Game.shimmerTypes.golden.maxTime)}px`,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarOverlay>=1?l("CMTimerBarGCBar").textContent=Math.ceil(Math.min(Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.minTime,Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.time)/Game.fps):l("CMTimerBarGCBar").textContent="";const t=Math.max(0,(Game.shimmerTypes.golden.time-Game.shimmerTypes.golden.minTime)/(Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.minTime))**5;l("CMTimerBarGCTime").textContent=`${Math.ceil((Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.time)/Game.fps)} ${t<.01?"<":""}${t.toLocaleString("en",{style:"percent"})}`,o+=1}if("christmas"===Game.season&&0===Game.shimmerTypes.reindeer.spawned){l("CMTimerBarRen").style.display="",l("CMTimerBarRenMinBar").style.width=`${Math.round(Math.max(0,Game.shimmerTypes.reindeer.minTime-Game.shimmerTypes.reindeer.time)*e/Game.shimmerTypes.reindeer.maxTime)}px`,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarOverlay>=1?l("CMTimerBarRenMinBar").textContent=Math.ceil((Game.shimmerTypes.reindeer.minTime-Game.shimmerTypes.reindeer.time)/Game.fps):l("CMTimerBarRenMinBar").textContent="",l("CMTimerBarRenBar").style.width=`${Math.round(Math.min(Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.minTime,Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.time)*e/Game.shimmerTypes.reindeer.maxTime)}px`,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarOverlay>=1?l("CMTimerBarRenBar").textContent=Math.ceil(Math.min(Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.minTime,Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.time)/Game.fps):l("CMTimerBarRenBar").textContent="";const t=Math.max(0,(Game.shimmerTypes.reindeer.time-Game.shimmerTypes.reindeer.minTime)/(Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.minTime))**5;l("CMTimerBarRenTime").textContent=`${Math.ceil((Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.time)/Game.fps)} ${t<.01?"<":""}${t.toLocaleString("en",{style:"percent"})}`,o+=1}else l("CMTimerBarRen").style.display="none";const a={};l("CMTimerBarBuffTimers").innerHTML="",Object.keys(Game.buffs).forEach((e=>{if(Game.buffs[e]){const n=zo(Game.buffs[e].name,Game.buffs[e].name,[{id:`${Game.buffs[e].name}Bar`}]);n.style.display="";let i="";i=void 0!==So[Game.buffs[e].name]?So[Game.buffs[e].name]:co,n.lastChild.children[1].className=ao+i,n.lastChild.children[1].style.color="black",2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarOverlay?n.lastChild.children[1].textContent=`${Math.round(Game.buffs[e].time/Game.buffs[e].maxTime*100)}%`:n.lastChild.children[1].textContent="",n.lastChild.children[1].style.width=`${Math.round(Game.buffs[e].time*(t-8*Math.ceil(Game.buffs[e].time/Game.fps).toString().length)/Game.buffs[e].maxTime)}px`,n.lastChild.children[2].textContent=Math.ceil(Game.buffs[e].time/Game.fps),o+=1,a[Game.buffs[e].name]=n}})),Object.keys(a).forEach((e=>{l("CMTimerBarBuffTimers").appendChild(a[e])})),0!==o&&(l("CMTimerBar").style.height=12*o+2+"px"),Do!==o&&(Do=o,Qo())}}(),_o(),Ca(),function(){if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TooltipWrink&&1===Co){let e=!1;Object.keys(Game.wrinklers).forEach((t=>{const o=Game.wrinklers[t];if(o.phase>0&&o.selected){if(e=!0,0===yo[t]||void 0===yo[t]){const e=document.createElement("div"),o=document.createElement("div");o.style.minWidth="120px",o.style.marginBottom="4px";const a=document.createElement("div");a.style.textAlign="center",a.id="CMTooltipWrinkler",o.appendChild(a),e.appendChild(o),Game.tooltip.draw(this,escape(e.innerHTML)),Mo=t,yo[t]=1}}else yo[t]=0})),e||Game.tooltip.hide()}}(),function(){if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TooltipWrink&&null!==l("CMTooltipWrinkler")){let{sucked:e}=Game.wrinklers[Mo],t=1.1;if(Game.Has("Sacrilegious corruption")&&(t*=1.05),1===Game.wrinklers[Mo].type&&(t*=3),e*=t,Game.Has("Wrinklerspawn")&&(e*=1.05),Ct.Temple.minigameLoaded){const t=Game.hasGod("scorn");1===t?e*=1.15:2===t?e*=1.1:3===t&&(e*=1.05)}l("CMTooltipWrinkler").textContent=to(e)}}(),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpStats&&"stats"===Game.onMenu&&(Game.drawT-1)%(5*Game.fps)!=0&&(Game.drawT-1)%Game.fps==0&&Game.UpdateMenu(),B()}function ya(){P.addLatest(Game.computedMouseCps)}class ba{constructor(e){this.maxLength=e,this.queue=[]}addLatest(e){this.queue.push(e)>this.maxLength&&this.queue.shift()}calcAverage(e){let t=e;t>this.maxLength&&(t=this.maxLength),t>this.queue.length&&(t=this.queue.length);let o=0;for(let e=this.queue.length-1;e>=0&&e>this.queue.length-1-t;e--)o+=this.queue[e];return 0===o?0:o/t}calcSum(e){let t=e;return t>this.maxLength&&(t=this.maxLength),t>this.queue.length&&(t=this.queue.length),0===t?0:this.queue.slice(-t).reduce(((e,t)=>e+t),0)}}function Ga(e){"Cache"in window.CookieMonsterData||(window.CookieMonsterData.Cache={}),Object.keys(e).forEach((t=>{const o=t.replace(/^Cache/,"");void 0===e[t]?window.CookieMonsterData.Cache[o]=void 0:window.CookieMonsterData.Cache[o]=JSON.parse(JSON.stringify(e[t]))}))}function va(){const e=Math.floor(Date.now()/1e3);if(Game.T/Game.fps%1==0){let t=Game.cookies+ft;Game.cpsSucked>0&&(t+=$e),st=Math.max(Game.cookiesEarned,t),t*=.05;const o=e-j,a=Math.max(0,Game.cookies-$)/o,n=Math.max(0,$e-U)/o,i=Math.max(0,He[0]-H)/o,r=Math.max(0,t-De)/o,s=(Game.cookieClicks-R)/o;for(let e=0;e{if(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPRigidelMode&&1===t)e[o].colour=mo;else{e[o].colour=na(e[o],wa(o,Game.Objects[o].basePrice,Game.Objects[o].amount,Game.Objects[o].free,t));for(let t=0;t{const a=wa(o,Game.Objects[o].basePrice,Game.Objects[o].amount,Game.Objects[o].free,t);Game.cookiesPs?e[o].pp=Math.max(a-(Game.cookies+Io()),0)/Game.cookiesPs+a/e[o].bonus:e[o].pp=a/e[o].bonus,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPRigidelMode&&1===t||ht.push([e[o].pp,t,a])}))}function Sa(){!function(){pt=1/0,ht=[],void 0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPExcludeTop&&(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPExcludeTop=0),Ta(Le,1),Ta(Ae,10),Ta(We,100),ht.sort(((e,t)=>e[0]-t[0]));let e=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPExcludeTop;if(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.PPOnlyConsiderBuyable)for(;ht[e][2]>Game.cookies&&(e+=1,ht.length!==e+1););pt=ht[e][0],ut=ht[e][1],xa(Le,1),xa(Ae,10),xa(We,100),Ga({CacheMinPP:pt,CacheMinPPBulk:ut,CachePPArray:ht})}(),Object.keys(ze).forEach((e=>{Game.cookiesPs?ze[e].pp=Math.max(Game.Upgrades[e].getPrice()-(Game.cookies+Io()),0)/Game.cookiesPs+Game.Upgrades[e].getPrice()/ze[e].bonus:ze[e].pp=Game.Upgrades[e].getPrice()/ze[e].bonus,Number.isNaN(ze[e].pp)&&(ze[e].pp=1/0),ze[e].colour=na(ze[e],Game.Upgrades[e].getPrice())})),window.CookieMonsterData.Objects1=JSON.parse(JSON.stringify(Le)),window.CookieMonsterData.Objects10=JSON.parse(JSON.stringify(Ae)),window.CookieMonsterData.Objects100=JSON.parse(JSON.stringify(We)),window.CookieMonsterData.Upgrades=[],Object.entries(ze).forEach((e=>{window.CookieMonsterData.Upgrades[e[0]]=JSON.parse(JSON.stringify(e[1]))}))}function Fa(e){if("toggle"===Game.Upgrades[e].pool||0===Game.Upgrades[e].bought&&Game.Upgrades[e].unlocked&&"prestige"!==Game.Upgrades[e].pool){ta(),"Shimmering veil [on]"===Mt[e].name?Mt["Shimmering veil [off]"].bought=0:"Golden switch [on]"===Mt[e].name?Mt["Golden switch [off]"].bought=0:Mt[e].bought=(Mt[e].bought+1)%2,Game.CountsAsUpgradeOwned(Game.Upgrades[e].pool)&&(Z+=1),"Elder Pledge"===e?(ee+=1,ee>0&&ra("Elder nap"),ee>=5&&ra("Elder slumber")):"Elder Covenant"===e?ra("Elder calm"):"Prism heart biscuits"===e?ra("Lovely cookies"):"Heavenly key"===e&&ra("Wholesome");const t=te;sa(),ha(),t!==te&&sa();const o=function(){let e=0;wt("Thousand fingers")&&(e+=.1),wt("Million fingers")&&(e*=5),wt("Billion fingers")&&(e*=10),wt("Trillion fingers")&&(e*=20),wt("Quadrillion fingers")&&(e*=20),wt("Quintillion fingers")&&(e*=20),wt("Sextillion fingers")&&(e*=20),wt("Septillion fingers")&&(e*=20),wt("Octillion fingers")&&(e*=20),wt("Nonillion fingers")&&(e*=20),wt("Decillion fingers")&&(e*=20),wt("Undecillion fingers")&&(e*=20),wt("Unshackled cursors")&&(e*=25);let t=0;Object.keys(Ct).forEach((e=>{t+=Ct[e].amount})),t-=Ct.Cursor.amount,e*=t,wt("Plastic mouse")&&(e+=.01*me),wt("Iron mouse")&&(e+=.01*me),wt("Titanium mouse")&&(e+=.01*me),wt("Adamantium mouse")&&(e+=.01*me),wt("Unobtainium mouse")&&(e+=.01*me),wt("Eludium mouse")&&(e+=.01*me),wt("Wishalloy mouse")&&(e+=.01*me),wt("Fantasteel mouse")&&(e+=.01*me),wt("Nevercrack mouse")&&(e+=.01*me),wt("Armythril mouse")&&(e+=.01*me),wt("Technobsidian mouse")&&(e+=.01*me),wt("Plasmarble mouse")&&(e+=.01*me),wt("Miraculite mouse")&&(e+=.01*me),wt("Aetherice mouse")&&(e+=.01*me),wt("Omniplast mouse")&&(e+=.01*me),wt("Fortune #104")&&(e+=.01*me);let o=1;if(wt("Santa's helpers")&&(o*=1.1),wt("Cookie egg")&&(o*=1.1),wt("Halo gloves")&&(o*=1.1),wt("Dragon claw")&&(o*=1.03),wt("Aura gloves")&&(o*=1+.05*Math.min(Game.Objects.Cursor.level,wt("Luminous gloves")?20:10)),o*=vt("click"),Ct.Temple.minigameLoaded&&Tt){const e=Tt("labor");1===e?o*=1.15:2===e?o*=1.1:3===e&&(o*=1.05)}Object.keys(Game.buffs).forEach((e=>{void 0!==Game.buffs[e].multClick&&(o*=Game.buffs[e].multClick)})),o*=1+.05*bt("Dragon Cursor");let a=o*Game.ComputeCps(1,wt("Reinforced index finger")+wt("Carpal tunnel prevention cream")+wt("Ambidextrous"),e);return a=Game.runModHookOnValue("cookiesPerClick",a),Game.hasBuff("Cursed finger")&&(a=Game.buffs["Cursed finger"].power),a}()-Game.computedMouseCps;return o?[me-Game.cookiesPs,o]:[me-Game.cookiesPs]}return[]}function Ea(e){const t={};return Object.keys(Game.Objects).forEach((o=>{t[o]={},t[o].bonus=ga(o,e),1!==e&&(Y=1)})),t}function Ba(){Object.keys(Game.Objects).forEach((e=>{Le[e].price=wa(e,Game.Objects[e].basePrice,Game.Objects[e].amount,Game.Objects[e].free,1),Ae[e].price=wa(e,Game.Objects[e].basePrice,Game.Objects[e].amount,Game.Objects[e].free,10),We[e].price=wa(e,Game.Objects[e].basePrice,Game.Objects[e].amount,Game.Objects[e].free,100),je[e].price=wa(e,Game.Objects[e].basePrice,Game.Objects[e].amount,Game.Objects[e].free,je[e].AmountNeeded)})),Ga({CacheObjectsNextAchievement:je})}function Pa(){Le=Ea(1),Ae=Ea(10),We=Ea(100),function(){ze={};for(let e=0;e0?e/=t:e=0,Ke=Na(7,e,Ee),qe=2*Ke[1]/Ee,Ye=Ke[2]/60/60/6/Fe,et=Na(6,e,Be),Je=2*et[1]/Be,Ze=et[2]/60/60/6/Fe,at=Na(7,7*e,Ee),tt=2*at[1]/Ee,ot=at[2]/60/60/6/Fe,rt=Na(6,7*e,Be),nt=2*rt[1]/Be,it=rt[2]/60/60/6/Fe,Ga({CacheChainMaxReward:Ke,CacheChainRequired:qe,CacheChainRequiredNext:Ye,CacheChainWrathMaxReward:et,CacheChainWrathRequired:Je,CacheChainWrathRequiredNext:Ze,CacheChainFrenzyMaxReward:at,CacheChainFrenzyRequired:tt,CacheChainFrenzyRequiredNext:ot,CacheChainFrenzyWrathMaxReward:rt,CacheChainFrenzyWrathRequired:nt,CacheChainFrenzyWrathRequiredNext:it})}function Da(){const e=Math.floor(Date.now()/1e3);if(Game.T/Game.fps%1==0){const t=Game.HowMuchPrestige(Game.cookiesReset),o=Math.floor(Game.HowMuchPrestige(Game.cookiesReset+Game.cookiesEarned))-Math.floor(t),a=e-Q,n=Math.max(0,o-q)/a;for(let e=0;e{e.push(Game.Upgrades[t])})),e.sort((function(e,t){return e.order>t.order?1:e.order{const o=e[t];if(0===o.bought){let e="";e+=function(e){let t="crate upgrade missing";"prestige"===e.pool&&(t+=" heavenly");let o=0;Game.prefs.crates||(o=1),o&&(t+=" noFrame");let{icon:a}=e;e.iconFunction&&(a=e.iconFunction());const n=`function() {return Game.crateTooltip(Game.UpgradesById[${e.id}], 'stats');}`;return`
\n\t
`}(o),"prestige"===o.pool?Xe+=e:"cookie"===o.pool?Qe+=e:"toggle"!==o.pool&&"unused"!==o.pool&&"debug"!==o.pool&&(_e+=e)}}))}function Aa(){if("christmas"===Game.season){let e=60*Game.cookiesPs;Game.hasBuff("Elder frenzy")&&(e*=.5),Game.hasBuff("Frenzy")&&(e*=.75),Oe=Math.max(25,e),Game.Has("Ho ho ho-flavored frosting")&&(Oe*=2)}Ga({CacheSeaSpec:Oe})}function Wa(){ke=900*Se/.15,ke*=Fe;const e=ia();e>0?ke/=e:ke=0,Ce=Ee*(.15*ke)+13,Me=Be*(.15*ke)+13,ye=7*ke,be=Ee*(.15*ye)+13,Ge=Be*(.15*ye)+13,ve=2*ke,we=.15*ve,xe=0;let t=0,o=0;Object.keys(Game.Objects).forEach((e=>{Game.Objects[e].amount>t&&(t=Game.Objects[e].amount),Game.Objects[e].amount>0&&(o+=1)})),Object.keys(Game.Objects).forEach((e=>{(Game.Objects[e].amountxe&&(xe=2*Game.Objects[e].price,Te=e)})),Ga({CacheLucky:ke,CacheLuckyReward:Ce,CacheLuckyWrathReward:Me,CacheLuckyFrenzy:ye,CacheLuckyRewardFrenzy:be,CacheLuckyWrathRewardFrenzy:Ge,CacheConjure:ve,CacheConjureReward:we,CacheEdifice:xe,CacheEdificeBuilding:Te})}function ja(){let e=1,t=1,o=1;wt("Green yeast digestives")&&(o*=1.01),wt("Dragon fang")&&(o*=1.03),e*=1+.1*Game.auraMult("Ancestral Metamorphosis"),e*=Game.eff("goldenCookieGain"),t*=1+.1*Game.auraMult("Unholy Dominion"),t*=Game.eff("wrathCookieGain"),Ee=o*e,Be=o*t,Fe=1,0===Game.shimmerTypes.golden.n&&(Fe*=1+1.23*Game.auraMult("Dragon's Fortune")),Ga({CacheGoldenCookiesMult:Ee,CacheWrathCookiesMult:Be,CacheDragonsFortuneMultAdjustment:Fe})}function $a(e){const t={};Object.keys(Game.Objects).forEach((o=>{if(0!==Object.keys(je).length&&je[o].TotalNeeded>Game.Objects[o].amount&&!e)t[o]={AmountNeeded:je[o].TotalNeeded-Game.Objects[o].amount,TotalNeeded:je[o].TotalNeeded,price:wa(o,Game.Objects[o].basePrice,Game.Objects[o].amount,Game.Objects[o].free,je[o].TotalNeeded-Game.Objects[o].amount)};else{const e=function(e){const t=Game.AchievementsOwned;let o=100,a=100;for(;o>-1;)if(ga(e,o),te>t)a=o,o-=10;else{if(100===o)return 101;for(o+=1;o<=a;){if(ga(e,o),te>t)return o;o+=1}}return 101}(o);t[o]={AmountNeeded:e,TotalNeeded:Game.Objects[o].amount+e,price:wa(o,Game.Objects[o].basePrice,Game.Objects[o].amount,Game.Objects[o].free,e)}}})),je=t,Ga({CacheObjectsNextAchievement:je})}function Ua(){$e=0,Ue=0,He=[0,null];for(let e=0;eHe[0]&&(He=[t,e]))}Ga({CacheWrinklersTotal:$e,CacheWrinklersNormal:Ue,CacheWrinklersFattest:He})}function Ha(){Yo(),Ua(),Wa(),ja(),Oa(),La(),Aa(),N=new ba(xo[xo.length-1]),O=new ba(xo[xo.length-1]),D=new ba(xo[xo.length-1]),L=new ba(xo[xo.length-1]),A=new ba(To[To.length-1]),W=new ba(5),P=new ba(20*To[To.length-1]),Da(),$a(),va(),Pa(),Ba(),Sa()}function Ra(){Object.keys(Game.wrinklers).forEach((e=>{Game.wrinklers[e].sucked>0&&0===Game.wrinklers[e].type&&(Game.wrinklers[e].hp=0)}))}function Ia(e,t){if("b"===e){if(l("tooltip").innerHTML=Game.Objects[t].tooltip(),1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TooltipAmor){const e=wa(t,Game.Objects[t].basePrice,0,Game.Objects[t].free,Game.Objects[t].amount),o=e-Game.Objects[t].totalCookies;o>0&&(l("tooltip").innerHTML=l("tooltip").innerHTML.split("so far").join(`so far
${to(o)} ${1===Math.floor(o)?"cookie":"cookies"} left to amortize (${Ho((e-Game.Objects[t].totalCookies)/(Game.Objects[t].storedTotalCps*Game.globalCpsMult)).text})`))}-1===Game.buyMode&&(l("tooltip").innerHTML=l("tooltip").innerHTML.split(to(Game.Objects[t].bulkPrice)).join(to((Game.Objects[t],Game.Objects[t].basePrice,Game.Objects[t].amount,Game.Objects[t].free,Game.buyBulk,1))))}else if("u"===e){if(!Game.UpgradesInStore[t])return"";l("tooltip").innerHTML=Game.crateTooltip(Game.UpgradesInStore[t],"store")}else"s"===e?l("tooltip").innerHTML=Game.lumpTooltip():"g"===e?l("tooltip").innerHTML=Game.Objects["Wizard tower"].minigame.spellTooltip(t)():"p"===e?l("tooltip").innerHTML=Game.ObjectsById[2].minigame.tileTooltip(t[0],t[1])():"ha"===e?l("tooltip").innerHTML=Game.ObjectsById[2].minigame.toolTooltip(1)():"wb"===e?l("tooltip").innerHTML="":"pag"===e?l("tooltip").innerHTML=Game.Objects.Temple.minigame.godTooltip(t)():"pas"===e&&(l("tooltip").innerHTML=Game.Objects.Temple.minigame.slotTooltip(t[0])());if("b"===e&&1===Game.buyMode||"u"===e||"s"===e||"g"===e||"p"===e&&!Game.keys[16]||"ha"===e||"wb"===e||"pag"===e||"pas"===e&&-1!==t[1]){const e=document.createElement("div");e.id="CMTooltipArea",l("tooltip").appendChild(e)}return Bo=e,Po=t,Ca(),l("tooltip").innerHTML}function za(){1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Favicon&&It>0?ct.wrath?l("CMFavicon").href="https://CookieMonsterTeam.github.io/CookieMonster/favicon/wrathCookie.ico":l("CMFavicon").href="https://CookieMonsterTeam.github.io/CookieMonster/favicon/goldenCookie.ico":l("CMFavicon").href="https://orteil.dashnet.org/cookieclicker/favicon.ico"}function Va(){Ct=[],Object.keys(Game.Objects).forEach((e=>{Ct[e]=Zo(e)})),Mt=[],Object.keys(Game.Upgrades).forEach((e=>{Mt[e]=ea(e)})),yt=[],Object.keys(Game.Achievements).forEach((e=>{yt[e]=Ko(e)})),ta()}function _a(){Game.Objects["Wizard tower"].minigameLoaded&&Object.keys(Game.Objects["Wizard tower"].minigame.spellsById).forEach((e=>{null!==l(`grimoireSpell${e}`).onmouseover&&(Yt[e]=l(`grimoireSpell${e}`).onmouseover,l(`grimoireSpell${e}`).onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("g",`${e}`)),"this"),Game.tooltip.wobble()})}))}function Xa(){if(!jt&&Game.Objects["Wizard tower"].minigameLoaded){const{minigame:e}=Game.Objects["Wizard tower"];At=e.draw,Game.Objects["Wizard tower"].minigame.draw=function(){At(),1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GrimoireBar&&e.magic{if(t=a,Game.Objects[t].amount{ce=!0})),Scale:new F(2,"bool","Notation",["Game's setting scale","Metric","Short scale","Short scale (Abbreviated)","Scientific notation","Engineering notation"],"Change how long numbers are formatted",!1,(()=>{Za()})),ScaleDecimals:new F(2,"bool","Notation",["1 decimals","2 decimals","3 decimals"],'Set the number of decimals used when applicable. This only works with Cookie Monster scales and not with "Game\'s Setting Scale"',!1,(()=>{Za()})),ScaleSeparator:new F(0,"bool","Notation",[". for decimals (standard)",". for thousands"],"Set the separator used for decimals and thousands",!1,(()=>{Za()})),ScaleCutoff:new T(999999,"numscale","Notation","Notation cut-off point: ","The number from which Cookie Monster will start formatting numbers based on chosen scale. Standard is 999,999. Setting this above 999,999,999 might break certain notations",1,999999999),TimeFormat:new S(0,"bool","Notation",["Time XXd, XXh, XXm, XXs","Time XX:XX:XX:XX:XX","Time XXx, XXx"],"Change the time format",!1),DetailedTime:new F(1,"bool","Notation",["Detailed time OFF","Detailed time ON"],"Change how time is displayed in certain statistics and tooltips",!0,(()=>{1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.DetailedTime?Game.sayTime=bo:Game.sayTime=Jt.sayTime})),PPDisplayTime:new S(0,"bool","Notation",["PP as value (standard)","PP as time unit"],"Display PP as calculated value or as approximate time unit. Note that PP does not translate directly into a time unit and this is therefore only an approximation.",!1),BuildColour:new F(1,"bool","Colours",["Building colours OFF","Building colours ON"],"Colour code buildings",!0,(()=>{Wo()})),PPOnlyConsiderBuyable:new S(0,"bool","Colours",["Don't ignore non-buyable","Ignore non-buyable"],"Makes Cookie Monster label buildings and upgrades you can't buy right now red, useful in those situations where you just want to spend your full bank 'most optimally'",!0),PPExcludeTop:new S(0,"bool","Colours",["Don't ignore any","Ignore 1st best","Ignore 1st and 2nd best","Ignore 1st, 2nd and 3rd best"],"Makes Cookie Monster ignore the 1st, 2nd or 3rd best buildings in labeling and colouring PP values",!0),PPRigidelMode:new S(0,"bool","Colours",["Rigidel mode OFF","Rigidel mode ON"],'Makes Cookie Monster ignore all "buy 1" options when colouring PP in order to stay at a total building count ending in 10 for pantheon god Rigidel',!0),PPSecondsLowerLimit:new T(0,"numscale","Colours","Lower limit for PP (in seconds): ",'If a building or upgrade costs less than the specified seconds of CPS it will also be considered optimal and label it as such ("PP is less than xx seconds of CPS"); setting to 0 ignores this option',0,1/0),ColourBlue:new x("#4bb8f0","colour","Colours","Standard colour is blue. Used to show upgrades better than best PP building, for Click Frenzy bar, and for various labels"),ColourGreen:new x("#00ff00","colour","Colours","Standard colour is green. Used to show best PP building, for Blood Frenzy bar, and for various labels"),ColourYellow:new x("#ffff00","colour","Colours","Standard colour is yellow. Used to show buildings within the top 10 of PP, for Frenzy bar, and for various labels"),ColourOrange:new x("#ff7f00","colour","Colours","Standard colour is orange. Used to show buildings within the top 20 of PP, for Next Reindeer bar, and for various labels"),ColourRed:new x("#ff0000","colour","Colours","Standard colour is Red. Used to show buildings within the top 30 of PP, for Clot bar, and for various labels"),ColourPurple:new x("#ff00ff","colour","Colours","Standard colour is purple. Used to show buildings outside of the top 30 of PP, for Next Cookie bar, and for various labels"),ColourGray:new x("#b3b3b3","colour","Colours","Standard colour is gray. Used to show negative or infinity PP, and for Next Cookie/Next Reindeer bar"),ColourPink:new x("#ff1493","colour","Colours","Standard colour is pink. Used for Dragonflight bar"),ColourBrown:new x("#8b4513","colour","Colours","Standard colour is brown. Used for Dragon Harvest bar"),BotBar:new F(1,"bool","BarsDisplay",["Bottom bar OFF","Bottom bar ON"],"Building information",!0,(()=>{en()})),TimerBar:new F(1,"bool","BarsDisplay",["Timer bar OFF","Timer bar ON"],"Bar with timers for golden cookie, season popup, Frenzy (Normal, Clot, Elder), Click Frenzy",!0,(()=>{qo()})),TimerBarPos:new F(0,"bool","BarsDisplay",["Timer bar position (top left)","Timer bar position (bottom)"],"Placement of the timer bar",!1,(()=>{0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarPos?(l("CMTimerBar").style.width="30%",l("CMTimerBar").style.bottom="",l("game").insertBefore(l("CMTimerBar"),l("sectionLeft"))):(l("CMTimerBar").style.width="100%",l("CMTimerBar").style.bottom="0px",l("wrapper").appendChild(l("CMTimerBar"))),Qo()})),TimerBarOverlay:new S(2,"bool","BarsDisplay",["Timer bar overlay OFF","Timer bar overlay only seconds","Timer bar overlay full"],"Overlay on timers displaying seconds and/or percentage left",!0),AutosaveTimerBar:new S(0,"bool","BarsDisplay",["Autosave timer bar OFF","Autosave timer bar ON"],"Show a timer counting down till next autosave in the timer bar",!0),UpBarColour:new F(1,"bool","BarsDisplay",["Upgrade colours/bar OFF","Upgrade colours with bar ON","Upgrade colours without bar ON"],"Colour code upgrades and optionally add a counter bar",!1,(()=>{1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpBarColour?(l("CMUpgradeBar").style.display="",$o()):2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpBarColour?(l("CMUpgradeBar").style.display="none",$o()):(l("CMUpgradeBar").style.display="none",Game.RebuildUpgrades())})),UpgradeBarFixedPos:new F(1,"bool","BarsDisplay",["Upgrade bar fixed position OFF","Upgrade bar fixed position ON"],"Lock the upgrade bar at top of the screen to prevent it from moving ofscreen when scrolling",!0,(()=>{1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.UpgradeBarFixedPos?(l("CMUpgradeBar").style.position="sticky",l("CMUpgradeBar").style.top="0px"):l("CMUpgradeBar").style.position=""})),SortBuildings:new F(0,"bool","BarsDisplay",["Sort buildings: default","Sort buildings: PP of x1 purchase","Sort buildings: PP of selected bulk mode","Sort buildings: price until next achievement"],"Sort the display of buildings in default order, by PP, or until next achievement",!1,(()=>{Wo()})),SortUpgrades:new F(0,"bool","BarsDisplay",["Sort upgrades: default","Sort upgrades: PP"],"Sort the display of upgrades in either default order or by PP",!1,(()=>{$o()})),UpgradesNeverCollapse:new F(0,"bool","BarsDisplay",["Upgrades always expanded OFF","Upgrades always expanded ON"],"Toggle to make the upgrades sections always expanded to the size needed to display all upgrades",!0,(()=>{jo()})),DragonAuraInfo:new S(1,"bool","BarsDisplay",["Extra dragon aura info OFF","Extra dragon aura info ON"],"Shows information about changes in CPS and costs in the dragon aura interface.",!0),GrimoireBar:new S(1,"bool","BarsDisplay",["Grimoire magic meter timer OFF","Grimoire magic meter timer ON"],"A timer overlay showing how long till the Grimoire magic meter is full",!0),GCTimer:new F(1,"bool","BarsDisplay",["Golden cookie timer OFF","Golden cookie timer ON"],"A timer on the golden cookie when it has been spawned",!0,(()=>{1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GCTimer?Object.keys(Lo).forEach((e=>{Lo[e].style.display="block",Lo[e].style.left=gt[e].l.style.left,Lo[e].style.top=gt[e].l.style.top})):Object.keys(Lo).forEach((e=>Lo[e].style.display="none"))})),Favicon:new F(1,"bool","BarsDisplay",["Favicon OFF","Favicon ON"],"Update favicon with golden/wrath cookie",!0,(()=>{za()})),WrinklerButtons:new F(1,"bool","BarsDisplay",["Extra wrinkler buttons OFF","Extra wrinkler buttons ON"],"Show buttons for popping wrinklers at bottom of cookie section",!0,(()=>{B()})),HideSectionsButtons:new F(0,"bool","BarsDisplay",["Hide buildings/upgrades button OFF","Hide buildings/upgrades button ON"],"Show buttons for hiding and showing the buildings and upgrades sections in the right column",!0,(()=>{Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.HideSectionsButtons?l("CMSectionHidButtons").style.display="":l("CMSectionHidButtons").style.display="none"})),TooltipBuildUpgrade:new S(1,"bool","Tooltip",["Building/upgrade tooltip information OFF","Building/upgrade tooltip information ON"],"Extra information in building/upgrade tooltips",!0),TooltipAmor:new S(0,"bool","Tooltip",["Buildings tooltip amortization information OFF","Buildings tooltip amortization information ON"],"Add amortization information to buildings tooltip",!0),ToolWarnLucky:new S(1,"bool","Tooltip",["Tooltip lucky warning OFF","Tooltip lucky warning ON"],'A warning when buying if it will put the bank under the amount needed for max "Lucky!" rewards',!0),ToolWarnLuckyFrenzy:new S(1,"bool","Tooltip",["Tooltip lucky frenzy warning OFF","Tooltip lucky frenzy warning ON"],'A warning when buying if it will put the bank under the amount needed for max "Lucky!" (Frenzy) rewards',!0),ToolWarnConjure:new S(1,"bool","Tooltip",["Tooltip conjure warning OFF","Tooltip conjure warning ON"],'A warning when buying if it will put the bank under the amount needed for max "Conjure Baked Goods" rewards',!0),ToolWarnConjureFrenzy:new S(1,"bool","Tooltip",["Tooltip conjure frenzy warning OFF","Tooltip conjure frenzy warning ON"],'A warning when buying if it will put the bank under the amount needed for max "Conjure Baked Goods" rewards with Frenzy active',!0),ToolWarnEdifice:new S(1,"bool","Tooltip",["Tooltip edifice warning OFF","Tooltip edifice warning ON"],'A warning when buying if it will put the bank under the amount needed for "Spontaneous Edifice" to possibly give you your most expensive building',!0),ToolWarnUser:new T(0,"numscale","Tooltip","Tooltip warning at x times CPS: ","Use this to show a customized warning if buying it will put the bank under the amount equal to value times cps; setting to 0 disables the function altogether",0,1/0),ToolWarnBon:new S(1,"bool","Tooltip",["Calculate tooltip warning with bonus CPS OFF","Calculate tooltip warning with bonus CPS ON"],"Calculate the warning with or without the bonus CPS you get from buying",!0),ToolWarnPos:new F(1,"bool","Tooltip",["Tooltip warning position (left)","Tooltip warning position (bottom)"],"Placement of the warning boxes",!1,(()=>{ka()})),TooltipGrim:new S(1,"bool","Tooltip",["Grimoire tooltip information OFF","Grimoire tooltip information ON"],"Extra information in tooltip for grimoire",!0),TooltipWrink:new S(1,"bool","Tooltip",["Wrinkler tooltip OFF","Wrinkler tooltip ON"],"Shows the amount of cookies a wrinkler will give when popping it",!0),TooltipLump:new S(1,"bool","Tooltip",["Sugar lump tooltip OFF","Sugar lump tooltip ON"],"Shows the current Sugar Lump type in Sugar lump tooltip.",!0),TooltipPlots:new S(1,"bool","Tooltip",["Garden plots tooltip OFF","Garden plots tooltip ON"],"Shows a tooltip for plants that have a cookie reward.",!0),TooltipPantheon:new S(1,"bool","Tooltip",["Pantheon tooltip OFF","Pantheon tooltip ON"],"Shows additional info in the pantheon tooltip",!0),TooltipAscendButton:new S(1,"bool","Tooltip",["Show Extra Info Ascend Tooltip OFF","Show Extra Info Ascend Tooltip ON"],"Shows additional info in the ascend tooltip",!0),Stats:new S(1,"bool","Statistics",["Statistics OFF","Statistics ON"],"Extra Cookie Monster statistics!",!0),MissingUpgrades:new S(1,"bool","Statistics",["Missing upgrades OFF","Missing upgrades ON"],"Shows missing upgrades in statistics menu",!0),MissingAchievements:new S(0,"bool","Statistics",["Missing Achievements OFF","Missing Normal Achievements ON"],"Shows missing normal achievements in statistics menu.",!0),UpStats:new S(1,"bool","Statistics",["Statistics update rate (default)","Statistics update rate (1s)"],"Default rate is once every 5 seconds",!1),HeavenlyChipsTarget:new T(1,"numscale","Statistics","Heavenly chips target: ",'Use this to set a heavenly chips target that will be counted towards in the "prestige" statsistics sections',1,1/0),ShowMissedGC:new S(1,"bool","Statistics",["Missed GC OFF","Missed GC ON"],"Show a stat in the statistics screen that counts how many golden cookies you have missed",!0),Title:new S(1,"bool","NotificationGeneral",["Title OFF","Title ON","Title pinned tab highlight"],'Update title with colden cookie/season popup timers; pinned tab highlight only changes the title when a golden cookie/season popup spawns; "!" means that golden cookie/reindeer can spawn',!0),GeneralSound:new S(1,"bool","NotificationGeneral",["Consider game volume setting OFF","Consider game volume setting ON"],'Turning this toggle to "off" makes Cookie Monster no longer consider the volume setting of the base game, allowing mod notifications to play with base game volume turned down',!0),GCNotification:new F(0,"bool","NotificationGC",["Notification OFF","Notification ON"],"Create a notification when golden cookie spawns",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GCNotification)})),GCFlash:new S(1,"bool","NotificationGC",["Flash OFF","Flash ON"],"Flash screen on golden cookie",!0),ColourGCFlash:new x("#ffffff","colour","NotificationGC","The colour of the GC flash, standard colour is white"),GCSound:new S(1,"bool","NotificationGC",["Sound OFF","Sound ON"],"Play a sound on golden cookie",!0),GCVolume:new E(100,"vol","NotificationGC",[],"Volume"),GCSoundURL:new S("https://freesound.org/data/previews/66/66717_931655-lq.mp3","url","NotificationGC","Sound URL:","URL of the sound to be played when a golden cookie spawns"),FortuneNotification:new F(0,"bool","NotificationFC",["Notification OFF","Notification ON"],"Create a notification when fortune cookie is on the ticker",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.FortuneNotification)})),FortuneFlash:new S(1,"bool","NotificationFC",["Flash OFF","Flash ON"],"Flash screen on fortune cookie spawn",!0),ColourFortuneFlash:new x("#ffffff","colour","NotificationFC","The colour of the fortune flash, standard colour is white"),FortuneSound:new S(1,"bool","NotificationFC",["Sound OFF","Sound ON"],"Play a sound on fortune cookie spawn",!0),FortuneVolume:new E(100,"vol","NotificationFC",[],"Volume"),FortuneSoundURL:new S("https://freesound.org/data/previews/174/174027_3242494-lq.mp3","url","NotificationFC","Sound URL:","URL of the sound to be played when the ticker has a fortune cookie"),SeaNotification:new F(0,"bool","NotificationSea",["Notification OFF","Notification ON"],"Create a notification on season popup",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SeaNotification)})),SeaFlash:new S(1,"bool","NotificationSea",["Flash OFF","Flash ON"],"Flash screen on season popup",!0),ColourSeaFlash:new x("#ffffff","colour","NotificationSea","The colour of the season popup flash, standard colour is white"),SeaSound:new S(1,"bool","NotificationSea",["Sound OFF","Sound ON"],"Play a sound on season popup",!0),SeaVolume:new E(100,"vol","NotificationSea",[],"Volume"),SeaSoundURL:new S("https://www.freesound.org/data/previews/121/121099_2193266-lq.mp3","url","NotificationSea","Sound URL:","URL of the sound to be played when on season popup spawns"),GardFlash:new S(1,"bool","NotificationGard",["Garden Tick Flash OFF","Flash ON"],"Flash screen on garden tick",!0),ColourGardFlash:new x("#ffffff","colour","NotificationGard","The colour of the garden flash, standard colour is white"),GardSound:new S(1,"bool","NotificationGard",["Sound OFF","Sound ON"],"Play a sound on garden tick",!0),GardVolume:new E(100,"vol","NotificationGard",[],"Volume"),GardSoundURL:new S("https://freesound.org/data/previews/103/103046_861714-lq.mp3","url","NotificationGard","Garden Tick Sound URL:","URL of the sound to be played when the garden ticks"),MagicNotification:new F(0,"bool","NotificationMagi",["Notification OFF","Notification ON"],"Create a notification when magic reaches maximum",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.MagicNotification)})),MagicFlash:new S(1,"bool","NotificationMagi",["Flash OFF","Flash ON"],"Flash screen when magic reaches maximum",!0),ColourMagicFlash:new x("#ffffff","colour","NotificationMagi","The colour of the magic flash, standard colour is white"),MagicSound:new S(1,"bool","NotificationMagi",["Sound OFF","Sound ON"],"Play a sound when magic reaches maximum",!0),MagicVolume:new E(100,"vol","NotificationMagi",[],"Volume"),MagicSoundURL:new S("https://freesound.org/data/previews/221/221683_1015240-lq.mp3","url","NotificationMagi","Sound URL:","URL of the sound to be played when magic reaches maxium"),WrinklerNotification:new F(0,"bool","NotificationWrink",["Notification OFF","Notification ON"],"Create a notification when a wrinkler appears",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerNotification)})),WrinklerFlash:new S(1,"bool","NotificationWrink",["Flash OFF","Flash ON"],"Flash screen when a wrinkler appears",!0),ColourWrinklerFlash:new x("#ffffff","colour","NotificationWrink","The colour of the wrinkler flash, standard colour is white"),WrinklerSound:new S(1,"bool","NotificationWrink",["Sound OFF","Sound ON"],"Play a sound when a wrinkler appears",!0),WrinklerVolume:new E(100,"vol","NotificationWrink",[],"Volume"),WrinklerSoundURL:new S("https://freesound.org/data/previews/124/124186_8043-lq.mp3","url","NotificationWrink","Sound URL:","URL of the sound to be played when a wrinkler appears"),WrinklerMaxNotification:new F(0,"bool","NotificationWrinkMax",["Notification OFF","Notification ON"],"Create a notification when the maximum amount of wrinklers has appeared",!0,(()=>{Ja(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerMaxNotification)})),WrinklerMaxFlash:new S(1,"bool","NotificationWrinkMax",["Flash OFF","Flash ON"],"Flash screen when the maximum amount of Wrinklers has appeared",!0),ColourWrinklerMaxFlash:new x("#ffffff","colour","NotificationWrinkMax","The colour of the maximum wrinkler flash, standard colour is white"),WrinklerMaxSound:new S(1,"bool","NotificationWrinkMax",["Sound OFF","Sound ON"],"Play a sound when the maximum amount of wrinklers has appeared",!0),WrinklerMaxVolume:new E(100,"vol","NotificationWrinkMax",[],"Volume"),WrinklerMaxSoundURL:new S("https://freesound.org/data/previews/152/152743_15663-lq.mp3","url","NotificationWrinkMax","Sound URL:","URL of the sound to be played when the maximum amount of wrinklers has appeared"),BulkBuyBlock:new S(1,"bool","Miscellaneous",["Block bulk buying OFF","Block bulk buying ON"],"Block clicking bulk buying when you can't buy all. This prevents buying 7 of a building when you are in buy-10 or buy-100 mode.",!0),FavouriteSettings:new F(1,"bool","Miscellaneous",["Favourite settings section OFF","Favourite settings section ON","Favourite settings section ON (Locked)"],"Show stars before each setting which allows selecting it for a 'favourites' section at the top of the Cookie Monster settings. Setting this to Locked removes the stars but shows the 'favourites' section",!0,(()=>{Game.UpdateMenu()}))};function on(){let e="";for(let t=0;t{if("Favourite"===t){if(0!==Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.favouriteSettings.length&&Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.FavouriteSettings>0&&(e.appendChild(b.createOptionsSubHeader("cookieMonsterMod",t,Ya[t])),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[t]))for(let t=0;t{const o=b.createOptionsSubHeader("cookieMonsterMod",t,Ka[t]);o.style.fontSize="15px",o.style.opacity="0.5",e.appendChild(o),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[t]&&Object.keys(tn).forEach((o=>{tn[o].group===t&&e.appendChild(b.createOptionsListing("cookieMonsterMod",o,tn,on,Za))}))})):Object.keys(tn).forEach((o=>{tn[o].group===t&&e.appendChild(b.createOptionsListing("cookieMonsterMod",o,tn,on,Za))})))})),e}function nn(e,t){const o=document.createElement("div");o.className="title",o.style.padding="0px 16px",o.style.opacity="0.7",o.style.fontSize="17px",o.style.fontFamily='"Kavoon", Georgia, serif',o.appendChild(document.createTextNode(`${e} `));const a=document.createElement("span");return a.style.cursor="pointer",a.style.display="inline-block",a.style.height="14px",a.style.width="14px",a.style.borderRadius="7px",a.style.textAlign="center",a.style.backgroundColor="#C0C0C0",a.style.color="black",a.style.fontSize="13px",a.style.verticalAlign="middle",a.textContent=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[t]?"-":"+",a.onclick=function(){!function(e){Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[e]+=1,Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[e]>1&&(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers[e]=0),w.saveFramework()}(t),Game.UpdateMenu()},o.appendChild(a),o}function rn(e,t,o,a){const n=document.createElement("div");n.className="listing";const i=document.createElement("b");if(i.textContent=t,n.appendChild(i),"withTooltip"===e){n.className="listing",n.appendChild(document.createTextNode(" "));const e=document.createElement("span");e.onmouseout=function(){Game.tooltip.hide()},e.onmouseover=function(){Game.tooltip.draw(this,escape(fo[a].innerHTML))},e.style.cursor="default",e.style.display="inline-block",e.style.height="10px",e.style.width="10px",e.style.borderRadius="5px",e.style.textAlign="center",e.style.backgroundColor="#C0C0C0",e.style.color="black",e.style.fontSize="9px",e.style.verticalAlign="bottom",e.textContent="?",n.appendChild(e)}return n.appendChild(document.createTextNode(": ")),n.appendChild(o),n}function sn(e,t,o,a){const n=document.createElement("div");n.className="listing";const i=document.createElement("b");return i.textContent=t,!0===a&&(i.style.color=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ColourGreen),n.appendChild(i),n.appendChild(document.createTextNode(": ")),n.appendChild(o),n}function ln(e){const t=document.createDocumentFragment();t.appendChild(document.createTextNode(`${e.length} `));const o=document.createElement("span");o.onmouseout=function(){Game.tooltip.hide()};const a=document.createElement("div"),n=document.createElement("div");n.style.minWidth="140px",n.style.marginBottom="4px";const i=document.createElement("div");return i.className="name",i.style.marginBottom="4px",i.style.textAlign="center",i.textContent="Missing",n.appendChild(i),Object.keys(e).forEach((t=>{const o=document.createElement("div");o.style.textAlign="center",o.appendChild(document.createTextNode(e[t])),n.appendChild(o)})),a.appendChild(n),o.onmouseover=function(){Game.tooltip.draw(this,escape(a.innerHTML))},o.style.cursor="default",o.style.display="inline-block",o.style.height="10px",o.style.width="10px",o.style.borderRadius="5px",o.style.textAlign="center",o.style.backgroundColor="#C0C0C0",o.style.color="black",o.style.fontSize="9px",o.style.verticalAlign="bottom",o.textContent="?",t.appendChild(o),t}function cn(e){const t=document.createElement("div");if(t.className="subsection",t.appendChild(e),t.appendChild(nn("Lucky Cookies","Lucky")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Lucky&&t.appendChild(function(){const e=Game.auraMult("Dragon's Fortune")?"GoldCookDragonsFortuneTooltipPlaceholder":"GoldCookTooltipPlaceholder",t=document.createElement("div");t.className="CMStatsLuckySection";const o=Game.cookies+Io(){Game.HasUnlocked(pa[e])||c.push(pa[e])})),0!==c.length&&e.appendChild(rn("basic","Rare plant drops left to unlock",ln(c))),e}())),t.appendChild(nn("Prestige","Prestige")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Prestige&&t.appendChild(function(){const e=document.createElement("div");e.className="CMStatsPrestigeSection";const t=Math.floor(Game.HowMuchPrestige(st+Game.cookiesReset+$e+(Game.HasUnlocked("Chocolate egg")&&!Game.Has("Chocolate egg")?De:0)));e.appendChild(rn("withTooltip","Prestige level (cur / max)",document.createTextNode(`${to(Game.prestige)} / ${to(t)}`),"PrestMaxTooltipPlaceholder"));const o=Math.max(0,Game.HowManyCookiesReset(t+1)-(st+Game.cookiesReset+$e+(Game.HasUnlocked("Chocolate egg")&&!Game.Has("Chocolate egg")&&De?De:0))),a=document.createDocumentFragment();a.appendChild(document.createTextNode(to(o)));const n=document.createElement("small");n.textContent=` (${Uo(o/lt,1)})`,a.appendChild(n),e.appendChild(rn("withTooltip","Cookies to next level",a,"NextPrestTooltipPlaceholder")),e.appendChild(rn("withTooltip","Heavenly chips (cur / max)",document.createTextNode(`${to(Game.heavenlyChips)} / ${to(t-Game.prestige+Game.heavenlyChips)}`),"HeavenChipMaxTooltipPlaceholder")),e.appendChild(rn("basic","Heavenly chips per second (last 5 seconds)",document.createTextNode(to(Pe,2))));const i=Number(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.HeavenlyChipsTarget);if(!Number.isNaN(i)){const t=i-Math.floor(Game.HowMuchPrestige(Game.cookiesReset+Game.cookiesEarned));t>0&&(e.appendChild(rn("basic","Heavenly chips to target set in settings (cur)",document.createTextNode(to(t)))),e.appendChild(rn("basic","Time till target (cur, current 5 second average)",document.createTextNode(Uo(t/Pe)))))}const r=function(e){let t=Game.cookiesPs;ta(),0===Mt["Heavenly key"].bought&&(Mt["Heavenly chip secret"].bought=1,Mt["Heavenly cookie stand"].bought=1,Mt["Heavenly bakery"].bought=1,Mt["Heavenly confectionery"].bought=1,Mt["Heavenly key"].bought=1,sa(),t=me,ta()),st>=1e6&&ra("Sacrifice"),st>=1e9&&ra("Oblivion"),st>=1e12&&ra("From scratch"),st>=1e15&&ra("Nihilism"),st>=1e18&&ra("Dematerialize"),st>=1e21&&ra("Nil zero zilch"),st>=1e24&&ra("Transcendence"),st>=1e27&&ra("Obliterate"),st>=1e30&&ra("Negative void"),st>=1e33&&ra("To crumbs, you say?"),st>=1e36&&ra("You get nothing"),st>=1e39&&ra("Humble rebeginnings"),st>=1e42&&ra("The end of the world"),st>=1e45&&ra("Oh, you're back"),st>=1e48&&ra("Lazarus"),st>=1e51&&ra("Smurf account"),st>=1e54&&ra("If at first you don't succeed"),Mt["Heavenly chip secret"].bought=1,Mt["Heavenly cookie stand"].bought=1,Mt["Heavenly bakery"].bought=1,Mt["Heavenly confectionery"].bought=1,Mt["Heavenly key"].bought=1,ae=e;const o=te;sa(),ha(),o!==te&&sa();const a=me-t;return ae=Game.prestige,a}(t),s=document.createDocumentFragment();s.appendChild(document.createTextNode(to(r)));const l=Math.round(r/Game.cookiesPs*1e4);if(Number.isFinite(l)&&0!==l){const e=document.createElement("small");e.textContent=` (${l/100}% of income)`,s.appendChild(e)}e.appendChild(rn("withTooltip","Reset bonus income",s,"ResetTooltipPlaceholder"));const c=Math.floor(Game.HowMuchPrestige(Game.cookiesReset)),d=Math.floor(Game.HowMuchPrestige(Game.cookiesReset+Game.cookiesEarned)),m=d-c;if(!Game.Has("Lucky digit")){let t=7-d%10;t<0&&(t+=10);const o=m+t,a=d+t,n=document.createDocumentFragment();n.appendChild(document.createTextNode(`${a.toLocaleString()} / ${o.toLocaleString()} (+${t})`)),e.appendChild(rn("basic",'Next "Lucky Digit" (total / reset)',n))}if(!Game.Has("Lucky number")){let t=777-d%1e3;t<0&&(t+=1e3);const o=m+t,a=d+t,n=document.createDocumentFragment();n.appendChild(document.createTextNode(`${a.toLocaleString()} / ${o.toLocaleString()} (+${t})`)),e.appendChild(rn("basic",'Next "Lucky Number" (total / reset)',n))}if(!Game.Has("Lucky payout")){let t=777777-d%1e6;t<0&&(t+=1e6);const o=m+t,a=d+t,n=document.createDocumentFragment();n.appendChild(document.createTextNode(`${a.toLocaleString()} / ${o.toLocaleString()} (+${t})`)),e.appendChild(rn("basic",'Next "Lucky Payout" (total / reset)',n))}return e}()),Game.cpsSucked>0&&(t.appendChild(nn("Wrinklers","Wrink")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Wrink)){const e=document.createDocumentFragment();e.appendChild(document.createTextNode(`${to($e)} / ${to(Ue)} `));const o=document.createElement("a");o.textContent="Pop All Normal",o.className="option",o.onclick=function(){Ra()},e.appendChild(o),t.appendChild(rn("basic","Rewards of Popping (All/Normal)",e));const a=document.createDocumentFragment();a.appendChild(document.createTextNode(`${to(He[0])} `));const n=document.createElement("a");n.textContent="Pop Single Fattest",n.className="option",n.onclick=function(){null!==He[1]&&(Game.wrinklers[He[1]].hp=0)},a.appendChild(n),t.appendChild(rn("basic",`Rewards of Popping Single Fattest Non-Shiny Wrinkler (id: ${null!==He[1]?He[1]:"None"})`,a))}if(t.appendChild(function(){const e=document.createElement("div");e.className="CMStatsSeasonSection";let t=!1;const o=[];Object.keys(ca).forEach((e=>{Game.Has(ca[e])||(o.push(ca[e]),t=!0)}));const a=[];Object.keys(da).forEach((e=>{Game.Has(da[e])||(a.push(da[e]),t=!0)}));const n=[];Object.keys(ma).forEach((e=>{Game.Has(ma[e])||(n.push(ma[e]),t=!0)}));const i=[];Object.keys(Game.eggDrops).forEach((e=>{Game.HasUnlocked(Game.eggDrops[e])||(i.push(Game.eggDrops[e]),t=!0)}));const r=[];Object.keys(Game.rareEggDrops).forEach((e=>{Game.HasUnlocked(Game.rareEggDrops[e])||(r.push(Game.rareEggDrops[e]),t=!0)}));const s=Game.HasUnlocked("Chocolate egg")&&!Game.Has("Chocolate egg"),l=Game.Has("Century egg");if(("christmas"===Game.season||t||s||l)&&(e.appendChild(nn("Season Specials","Sea")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Sea)){if(0!==o.length){e.appendChild(sn(0,"Halloween cookies left to buy",ln(o),"halloween"===Game.season));let t=.95;if(Game.HasAchiev("Spooky cookies")&&(t=.8),Game.Has("Starterror")&&(t*=.9),t*=1/Game.dropRateMult(),Game.hasGod){const e=Game.hasGod("seasons");1===e?t*=.9:2===e?t*=.95:3===e&&(t*=.97)}const a=o.length/7;e.appendChild(sn(0,"Chance of receiving a cookie from wrinkler/shiny wrinkler",document.createTextNode(`${to((1-t)*a*100)}% / ${to((1-.9*t)*a*100)}%`),"halloween"===Game.season))}if(0!==a.length){e.appendChild(sn(0,"Christmas cookies left to buy",ln(a),"christmas"===Game.season));let t=.8;if(Game.HasAchiev("Let it snow")&&(t=.6),t*=1/Game.dropRateMult(),Game.Has("Starsnow")&&(t*=.95),Game.hasGod){const e=Game.hasGod("seasons");1===e?t*=.9:2===e?t*=.95:3===e&&(t*=.97)}const o=a.length/7;e.appendChild(sn(0,"Chance of receiving a cookie from reindeer",document.createTextNode(`${to((1-t)*o*100)}%`),"christmas"===Game.season))}0!==n.length&&e.appendChild(sn(0,"Valentine cookies left to buy",ln(n),"valentines"===Game.season));const t=function(e){let t=e*(1/Game.dropRateMult());if(Game.HasAchiev("Hide & seek champion")&&(t*=.7),Game.Has("Omelette")&&(t*=.9),Game.Has("Starspawn")&&(t*=.9),Game.hasGod){const e=Game.hasGod("seasons");1===e?t*=.9:2===e?t*=.95:3===e&&(t*=.97)}const o=1-t,a=Game.eggDrops.length-i.length,n=Game.rareEggDrops.length-r.length,s=.9*o*(1-a/Game.eggDrops.length),l=.1*o*(1-n/Game.rareEggDrops.length),c=.9*o*(a/Game.eggDrops.length)+.1*o*(n/Game.rareEggDrops.length);return[s+.9*c*(1-a/Game.eggDrops.length),l+.1*c*(1-n/Game.rareEggDrops.length)]};0!==i.length&&(e.appendChild(sn(0,"Normal easter eggs left to unlock",ln(i),"easter"===Game.season)),e.appendChild(sn(0,"Chance of receiving an egg from wrinkler/golden cookie",document.createTextNode(`${to(100*t(.98)[0])}% / ${to(100*t(.9)[0])}%`),"easter"===Game.season))),0!==r.length&&(e.appendChild(sn(0,"Rare easter eggs left to unlock",ln(r),"easter"===Game.season)),e.appendChild(sn(0,"Chance of receiving a rare egg from wrinkler/golden cookie",document.createTextNode(`${to(100*t(.98)[1])}% / ${to(100*t(.9)[1])}%`),"easter"===Game.season))),"christmas"===Game.season&&e.appendChild(sn(0,"Reindeer reward",document.createTextNode(to(Oe)),!0)),s&&e.appendChild(rn("withTooltip","Chocolate egg cookies",document.createTextNode(to(De)),"ChoEggTooltipPlaceholder")),l&&e.appendChild(rn("basic","Century egg multiplier",document.createTextNode(Math.round(1e4*(Ne-1))/100+"%")))}return e}()),t.appendChild(nn("Achievements","Achievs")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Achievs&&Object.keys(Game.Objects).forEach((e=>{const o=je[e];t.appendChild(rn("basic",e,o.AmountNeeded<101?document.createTextNode(`Next achievement in ${o.AmountNeeded}, price: ${to(o.price)}`):document.createTextNode("No new achievement for next 100 buildings")))})),t.appendChild(nn("Miscellaneous","Misc")),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.headers.Misc){if(t.appendChild(rn("basic",`Average cookies per second (past ${xo[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgCPSHist]<60?`${xo[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgCPSHist]} seconds`:xo[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgCPSHist]/60+(3===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgCPSHist?" minute":" minutes")})`,document.createTextNode(to(Ro(),3)))),t.appendChild(rn("basic",`Average cookie clicks per second (past ${To[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgClicksHist]}${0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgClicksHist?" second":" seconds"})`,document.createTextNode(to(Ve,1)))),t.appendChild(rn("basic",`Cookies from clicking (past ${To[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgClicksHist]}${0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgClicksHist?" second":" seconds"})`,document.createTextNode(to(P.calcSum(Ve*To[Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.AvgClicksHist]))))),Game.Has("Fortune cookies")){const e=[];Object.keys(la).forEach((t=>{Game.Has(la[t])||e.push(la[t])})),0!==e.length&&t.appendChild(rn("basic","Fortune Upgrades Left to Buy",ln(e)))}if(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ShowMissedGC&&t.appendChild(rn("basic","Missed golden cookies",document.createTextNode(to(Game.missedGoldenClicks)))),Game.prefs.autosave){const e=document.createElement("span");e.id="CMStatsAutosaveTimer",e.innerText=Game.sayTime(60*Game.fps-(Game.OnAscend?0:Game.T%(60*Game.fps)),4),t.appendChild(rn("basic","Time till autosave",e))}}l("menu").insertBefore(t,l("menu").childNodes[2]),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.MissingUpgrades&&l("menu").childNodes.forEach((e=>{if(e.children[0])if("Prestige"===e.children[0].innerHTML&&Xe){const t=Xe.match(new RegExp("div","g")||0).length/2,o=document.createElement("div");o.id="CMMissingUpgradesPrestigeTitle",o.className="listing";const a=document.createElement("div");a.innerHTML=`Missing Prestige upgrades: ${t}/${Game.PrestigeUpgrades.length} (${Math.floor(t/Game.PrestigeUpgrades.length*100)}%)`,o.appendChild(a),e.appendChild(o);const n=document.createElement("div");n.className="listing crateBox",n.innerHTML=Xe,e.appendChild(n)}else if("Upgrades"===e.children[0].innerHTML){if(_e){const t=_e.match(new RegExp("div","g")||0).length/2,o=document.createElement("div");o.id="CMMissingUpgradesTitle",o.className="listing";const a=document.createElement("div");a.innerHTML=`Missing normal upgrades: ${t}/${Game.UpgradesByPool[""].length+Game.UpgradesByPool.tech.length} (${Math.floor(t/(Game.UpgradesByPool[""].length+Game.UpgradesByPool.tech.length)*100)}%)`,o.appendChild(a),e.insertBefore(o,e.childNodes[3]);const n=document.createElement("div");n.className="listing crateBox",n.innerHTML=_e,e.insertBefore(n,document.getElementById("CMMissingUpgradesTitle").nextSibling)}if(Qe){const t=Qe.match(new RegExp("div","g")||0).length/2,o=document.createElement("div");o.id="CMMissingUpgradesCookiesTitle",o.className="listing";const a=document.createElement("div");a.innerHTML=`Missing Cookie upgrades: ${t}/${Game.UpgradesByPool.cookie.length} (${Math.floor(t/Game.UpgradesByPool.cookie.length*100)}%)`,o.appendChild(a),e.appendChild(o);const n=document.createElement("div");n.className="listing crateBox",n.innerHTML=Qe,e.appendChild(n)}}})),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.MissingAchievements&&function(){let e;Object.values(document.querySelectorAll("div.title")).forEach((t=>{t.textContent.includes("Achievements")&&(e=t.parentElement.querySelectorAll("div.listing.crateBox")[0])})),Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.MissingAchievements&&Object.values(e.children).forEach((e=>{if(!e.className.includes("enabled")){const t=e.onclick.toString().split(/\[(.*)\]/gi)[1],{icon:o}=Game.AchievementsById[t];e.style.backgroundPosition=`${48*-o[0]}px ${48*-o[1]}px`,e.onmouseover=function(){Game.mouseDown||(Game.setOnCrate(this),Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>function(e){const t=[];"shadow"===e.pool?t.push("Shadow Achievement","#9700cf"):t.push("Achievement",0),t.push("Locked",0);let o=0;(Game.Has("Neuromancy")||Game.sesame&&"debug"===e.pool)&&(o=1),o&&0===e.won?t.push("Click to win!","#00c462"):o&&e.won>0&&t.push("Click to lose!","#00c462");let{icon:a}=e;e.iconFunction&&(a=e.iconFunction());let{desc:n}=e;e.descFunc&&(n=e.descFunc("stats"));let i="";for(let e=0;e[${t[e]}]`);return i=i.substring(1),`
\n
\n
${e.name}
\n ${i}
${n}
\n ${Game.sesame?`
Id : ${e.id} | Order : ${Math.floor(e.order)}${e.tier?` | Tier : ${e.tier}`:""}
`:""}`}(Game.AchievementsById[t])),"top"),Game.tooltip.wobble())}}}))}()}function dn(e){if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBar&&0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBarPos){const t=parseInt(l("CMTimerBar").style.height,10);Game.mouseY-=t,e(),Game.mouseY+=t}else e()}function mn(){Jt.Beautify=Beautify,Beautify=to,Jt.CalculateGains=Game.CalculateGains,Game.CalculateGains=function(){Jt.CalculateGains(),ce=1,Zt=Date.now(),eo=Date.now()},Jt.tooltip={},Jt.tooltip.draw=Game.tooltip.draw,Jt.tooltip.drawMod=new Function(`return ${Game.tooltip.draw.toString().split("this").join("Game.tooltip")}`)(),Game.tooltip.draw=function(e,t,o){Jt.tooltip.drawMod(e,t,o)},Jt.tooltip.update=Game.tooltip.update,Jt.tooltip.updateMod=new Function(`return ${Game.tooltip.update.toString().split("this.").join("Game.tooltip.")}`)(),Game.tooltip.update=function(){Jt.tooltip.updateMod(),function(){if("store"===Game.tooltip.origin){let e=0;1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ToolWarnLucky&&1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.ToolWarnPos&&null!==l("CMDispTooltipWarningParent")&&(e=l("CMDispTooltipWarningParent").clientHeight-4),Game.tooltip.tta.style.top=`${Math.min(parseInt(Game.tooltip.tta.style.top,10),l("game").clientHeight+l("topBar").clientHeight-Game.tooltip.tt.clientHeight-e-46)}px`}}()},Jt.UpdateWrinklers=Game.UpdateWrinklers,Game.UpdateWrinklers=function(){dn(Jt.UpdateWrinklers)},Jt.UpdateSpecial=Game.UpdateSpecial,Game.UpdateSpecial=function(){dn(Jt.UpdateSpecial)},l("bigCookie").removeEventListener("click",Game.ClickCookie,!1),l("bigCookie").addEventListener("click",(e=>{dn((()=>Game.ClickCookie(e,0)))}),!1),Jt.RebuildUpgrades=Game.RebuildUpgrades,Game.RebuildUpgrades=function(){Jt.RebuildUpgrades(),Kt=[],Object.keys(Game.UpgradesInStore).forEach((e=>{null!==l(`upgrade${e}`).onmouseover&&(Kt[e]=l(`upgrade${e}`).onmouseover,l(`upgrade${e}`).onmouseover=function(){Game.mouseDown||(Game.setOnCrate(this),Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("u",`${e}`)),"store"),Game.tooltip.wobble())})})),Game.CalculateGains()},Jt.ClickProduct=Game.ClickProduct,Game.ClickProduct=function(e){(!Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.BulkBuyBlock||Game.ObjectsById[e].bulkPrice<=Game.cookies||-1===Game.buyMode)&&Jt.ClickProduct(e)},Jt.DescribeDragonAura=Game.DescribeDragonAura,Game.DescribeDragonAura=function(e){Jt.DescribeDragonAura(e),function(e){if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.DragonAuraInfo){const[t,o]=function(e){ta(),l("promptContent").children[0].innerHTML.includes("secondary")?ie=e:ne=e;let t=0;if(ne!==ue||ie!==he)for(let e=Game.ObjectsById.length-1;e>-1;--e)if(Game.ObjectsById[e].amount>0){const o=Ct[Game.ObjectsById[e].name].name;Ct[o].amount-=1,J-=1,t=Ct[o].basePrice*Game.priceIncrease**Math.max(0,Ct[o].amount-1-Ct[o].free),t=Game.modifyBuildingPrice(Ct[o],t),t=Math.ceil(t);break}const o=te;return sa(),ha(),o!==te&&sa(),[me-Game.cookiesPs,t]}(e),a=Uo(o/(t+Game.cookiesPs));let n;n=0===Game.cookiesPs?to(1/0):to(t/Game.cookiesPs*100),l("dragonAuraInfo").style.minHeight="60px",l("dragonAuraInfo").style.margin="8px",l("dragonAuraInfo").appendChild(document.createElement("div")).className="line";const i=document.createElement("div");i.style.minWidth="200px",i.style.textAlign="center",i.textContent=`Picking this aura will change CPS by ${to(t)} (${n}% of current CPS).`,l("dragonAuraInfo").appendChild(i);const r=document.createElement("div");r.style.minWidth="200px",r.style.textAlign="center",r.textContent=`It will take ${a} to recover the cost.`,l("dragonAuraInfo").appendChild(r)}}(e)},Jt.ToggleSpecialMenu=Game.ToggleSpecialMenu,Game.ToggleSpecialMenu=function(e){Jt.ToggleSpecialMenu(e),function(){if(null!==(l("specialPopup").className.match(/onScreen/)&&l("specialPopup").children[0].style.background.match(/dragon/)))for(let e=0;e${fe}`,"this"),Game.tooltip.wobble()},l("specialPopup").children[e].onmouseout=function(){Game.tooltip.shouldHide=1})}()},Jt.UpdateMenu=Game.UpdateMenu,Game.UpdateMenu=function(){void 0!==s().picker&&void 0!==s().picker.owner||(Jt.UpdateMenu(),function(){const e=document.createElement("div");e.className="title","stats"===Game.onMenu?Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Stats&&(e.textContent="Cookie Monster Statistics",cn(e)):"prefs"===Game.onMenu&&l("menu").childNodes[2].insertBefore(an(),l("menu").childNodes[2].childNodes[l("menu").childNodes[2].childNodes.length-1])}())},Jt.sayTime=Game.sayTime,bo=function(e,t){return Number.isNaN(e)||e<=0?Jt.sayTime(e,t):Uo(e/Game.fps,1)},Jt.Logic=Game.Logic,Game.Logic=function(){Jt.Logic();let e="Cookie Clicker";"fools"===Game.season&&(e="Cookie Baker"),Ao=`${Game.OnAscend?"Ascending! ":""}${to(Game.cookies)} ${1===Game.cookies?"cookie":"cookies"} - ${e}`,function(){if(Game.OnAscend||0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Title)document.title=Ao;else if(1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Title){let e,t,o,a=!1,n=!1;e=ct?ct.wrath?`[W${Math.ceil(ct.life/Game.fps)}]`:`[G${Math.ceil(ct.life/Game.fps)}]`:Game.Has("Golden switch [off]")?"[GS]":`[${Number(l("CMTimerBarGCMinBar").textContent)<0?"!":""}${Math.ceil((Game.shimmerTypes.golden.maxTime-Game.shimmerTypes.golden.time)/Game.fps)}]`,Ht&&(a=!0,t="[F]"),"christmas"===Game.season&&(n=!0,o=Ut?`[R${Math.ceil(dt.life/Game.fps)}]`:`[${Number(l("CMTimerBarRenMinBar").textContent)<0?"!":""}${Math.ceil((Game.shimmerTypes.reindeer.maxTime-Game.shimmerTypes.reindeer.time)/Game.fps)}]`);let i=Ao;"["===i.charAt(0)&&(i=i.substring(i.lastIndexOf("]")+1)),document.title=`${e+(a?t:"")+(n?o:"")} ${i}`}else if(2===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.Title){let e="",t=!1;ct&&(t=!0,ct.wrath?e+=`[W${Math.ceil(ct.life/Game.fps)}]`:e+=`[G${Math.ceil(ct.life/Game.fps)}]`),Ht&&(t=!0,e+="[F]"),"christmas"===Game.season&&Ut&&(e+=`[R${Math.ceil(dt.life/Game.fps)}]`,t=!0),t&&(e+=" - ");let o="Cookie Clicker";"fools"===Game.season&&(o="Cookie Baker"),e+=o,document.title=e}}(),function(){const e=Math.max(0,Game.HowManyCookiesReset(Math.floor(Game.HowMuchPrestige(Game.cookiesReset+Game.cookiesEarned))+1)-(Game.cookiesEarned+Game.cookiesReset)),t=Game.sayTime((Date.now()-Game.startDate)/1e3*Game.fps,-1);let o=`You've been on this run for ${""===t?"not very long":t}.
\n
`;Game.prestige>0&&(o+=`Your prestige level is currently ${to(Game.prestige)}.
(CpS +${to(Game.prestige)}%)\n
`),o+=q<1?"Ascending now would grant you no prestige.":q<2?"Ascending now would grant you
1 prestige level (+1% CpS)
and 1 heavenly chip to spend.":`Ascending now would grant you
${to(q)} prestige levels (+${to(q)}% CpS)
and ${to(q)} heavenly chips to spend.`,o+=`
\n You need ${to(e)} more cookies for the next level.
\n ${Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TooltipAscendButton?`
It takes ${mt} to reach the next level and you were making ${to(Pe,2)} chips on average in the last 5 seconds.
`:""}`,l("ascendTooltip").innerHTML=o}()}}function pn(){window.CookieMonsterData={},Game.mods.cookieMonsterFramework.listeners.optionsMenu.push(an),Va(),Wa(),Ha(),Nt=Object.keys(Game.mods).length,Qt=document.createElement("style"),Qt.type="text/css",Qt.id="CMCSS",document.head.appendChild(Qt),function(){const e=document.createElement("div");e.id="CMBotBar",e.style.height="69px",e.style.width="100%",e.style.position="absolute",e.style.display="none",e.style.backgroundColor="#262224",e.style.backgroundImage="linear-gradient(to bottom, #4d4548, #000000)",e.style.borderTop="1px solid black",e.style.overflow="auto",e.style.textShadow="-1px 0 black, 0 1px black, 1px 0 black, 0 -1px black";const t=e.appendChild(document.createElement("table"));t.style.width="100%",t.style.textAlign="center",t.style.whiteSpace="nowrap";const o=t.appendChild(document.createElement("tbody")),a=function(e,t){const o=document.createElement("td");return o.style.textAlign="right",o.className=oo+t,o.textContent=e,o},n=o.appendChild(document.createElement("tr"));n.style.fontWeight="bold",n.appendChild(a("CM 2.052.10",ro)),o.appendChild(document.createElement("tr")).appendChild(a("Bonus Income",no)),o.appendChild(document.createElement("tr")).appendChild(a("Payback Period",no)),o.appendChild(document.createElement("tr")).appendChild(a("Time Left",no)),l("wrapper").appendChild(e),Object.keys(Game.Objects).forEach((e=>{Vo(e)}))}(),function(){const e=document.createElement("div");e.id="CMTimerBar",e.style.position="absolute",e.style.display="none",e.style.height="0px",e.style.fontSize="10px",e.style.fontWeight="bold",e.style.backgroundColor="black";const t=zo("CMTimerBarAutosave","Autosave",[{id:"CMTimerBarAutosaveBar",colour:co}]);e.appendChild(t);const o=zo("CMTimerBarGC","Next Cookie",[{id:"CMTimerBarGCMinBar",colour:mo},{id:"CMTimerBarGCBar",colour:co}]);e.appendChild(o);const a=zo("CMTimerBarRen","Next Reindeer",[{id:"CMTimerBarRenMinBar",colour:mo},{id:"CMTimerBarRenBar",colour:so}]);e.appendChild(a);const n=document.createElement("div");n.id="CMTimerBarBuffTimers",e.appendChild(n),l("wrapper").appendChild(e)}(),function(){const e=document.createElement("div");e.id="CMUpgradeBar",e.style.width="100%",e.style.backgroundColor="black",e.style.textAlign="center",e.style.fontWeight="bold",e.style.display="none",e.style.zIndex="21",e.onmouseout=function(){Game.tooltip.hide()};const t=document.createElement("div");t.appendChild(function(){const e=document.createElement("div");e.style.minWidth="330px",e.style.marginBottom="4px";const t=document.createElement("div");t.className="name",t.style.marginBottom="4px",t.textContent="Legend",e.appendChild(t);const o=function(e,t){const o=document.createElement("div");o.style.verticalAlign="middle";const a=document.createElement("span");return a.className=ao+e,a.style.display="inline-block",a.style.height="10px",a.style.width="10px",a.style.marginRight="4px",o.appendChild(a),o.appendChild(document.createTextNode(t)),o};return e.appendChild(o(no,"Better than the best PP of a building option")),e.appendChild(o(io,"Same as the best PP building option")),e.appendChild(o(ro,"Within the top 10 of PP for buildings")),e.appendChild(o(so,"Within the top 20 of PP for buildings")),e.appendChild(o(lo,"Within the top 30 of PP for buildings")),e.appendChild(o(co,"Outside of the top 30 of PP for buildings")),e.appendChild(o(mo,"Negative or infinity PP")),e}()),e.onmouseover=function(){Game.tooltip.draw(this,escape(t.innerHTML),"store")};const o=function(e,t){const o=document.createElement("span");return o.id=e,o.className=oo+t,o.style.width="14.28571428571429%",o.style.display="inline-block",o.textContent="0",o};e.appendChild(o("CMUpgradeBarBlue",no)),e.appendChild(o("CMUpgradeBarGreen",io)),e.appendChild(o("CMUpgradeBarYellow",ro)),e.appendChild(o("CMUpgradeBarOrange",so)),e.appendChild(o("CMUpgradeBarRed",lo)),e.appendChild(o("CMUpgradeBarPurple",co)),e.appendChild(o("CMUpgradeBarGray",mo)),l("upgrades").parentNode.insertBefore(e,l("upgrades").parentNode.childNodes[3])}(),function(){const e=document.createElement("div");e.id="CMSectionHidButtons",e.style.textAlign="center";const t=document.createElement("a");t.className="option",t.onclick=function(){"flex"===l("upgrades").style.display?(l("upgrades").style.display="none",l("toggleUpgrades").style.display="none",l("techUpgrades").style.display="none",l("vaultUpgrades").style.display="none"):(l("upgrades").style.display="flex",0!==l("toggleUpgrades").children.length&&(l("toggleUpgrades").style.display="block"),0!==l("techUpgrades").children.length&&(l("techUpgrades").style.display="block"),0!==l("vaultUpgrades").children.length&&(l("vaultUpgrades").style.display="block"))},t.textContent="Hide/Show Upgrades",e.appendChild(t);const o=document.createElement("a");o.className="option",o.onclick=function(){"grid"===l("products").style.display?l("products").style.display="none":l("products").style.display="grid"},o.textContent="Hide/Show Buildings",e.appendChild(o),l("store").insertBefore(e,l("store").childNodes[2])}(),function(){const e=document.createElement("link");e.id="CMFavicon",e.rel="shortcut icon",e.href="https://orteil.dashnet.org/cookieclicker/favicon.ico",document.getElementsByTagName("head")[0].appendChild(e)}(),Object.keys(go).forEach((e=>{!function(e,t,o){const a=document.createElement("div");a.id=e;const n=document.createElement("div");n.style.minWidth=o,n.style.marginBottom="4px";const i=document.createElement("div");i.style.textAlign="left",i.textContent=t,n.appendChild(i),a.appendChild(n),fo[e]=a}(go[e][0],go[e][1],go[e][2])})),function(){const e=document.createElement("a");e.id="PopAllNormalWrinklerButton",e.textContent="Pop All Normal",e.className="option",e.onclick=function(){Ra()},e.onmouseout=function(){Game.tooltip.shouldHide=1},e.onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("wb","PopAllNormal")),"this"),Game.tooltip.wobble()},l("sectionLeftExtra").children[0].append(e);const t=document.createElement("a");t.id="PopFattestWrinklerButton",t.textContent="Pop Single Fattest",t.className="option",t.onclick=function(){null!==He[1]&&(Game.wrinklers[He[1]].hp=0)},t.onmouseout=function(){Game.tooltip.shouldHide=1},t.onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("wb","PopFattest")),"this"),Game.tooltip.wobble()},l("sectionLeftExtra").children[0].append(t)}(),l("products").style.display="grid",l("storeBulk").style.gridRow="1/1",l("upgrades").style.display="flex",l("upgrades").style["flex-wrap"]="wrap",Object.keys(l("rows").children).forEach((e=>{const t=l("rows").children[e].children[1],o=document.createElement("div");o.id=`productLock${Number(e)+1}`,o.className="productButton",o.innerHTML="Lock",o.onclick=function(){!function(e){"Lock"===l(`productLock${e}`).innerHTML?(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames.push(e.toString()),l(`row${e}`).style.pointerEvents="none",l(`row${e}`).style.opacity="0.4",l(`productLock${e}`).innerHTML="Unlock",l(`productLock${e}`).style.pointerEvents="auto"):(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames.includes(e.toString())&&(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames=Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames.filter((t=>t!==e.toString()))),l(`productLock${e}`).innerHTML="Lock",l(`row${e}`).style.pointerEvents="auto",l(`row${e}`).style.opacity="1")}(Number(e)+1)},t.appendChild(o)})),Object.keys(Game.Objects).forEach((e=>{const t=Game.Objects[e];null!==l(`product${t.id}`).onmouseover&&(qt[e]=l(`product${t.id}`).onmouseover,l(`product${t.id}`).onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("b",`${e}`)),"store"),Game.tooltip.wobble()})})),Game.canLumps()&&(Ot=l("lumps").onmouseover,l("lumps").onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("s","Lump")),"this"),Game.tooltip.wobble()}),$t=Game.LoadMinigames,Game.LoadMinigames=function(){$t(),Game.Objects.Farm.minigameLoaded&&(l("gardenTool-1").onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("ha","HarvestAllButton")),"this"),Game.tooltip.wobble()},Array.from(l("gardenPlot").children).forEach((e=>{const t=e.id.slice(-3);e.onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("p",[`${t[0]}`,`${t[2]}`])),"this"),Game.tooltip.wobble()}}))),_a(),function(){if(Game.Objects.Temple.minigameLoaded){for(let e=0;e<11;e+=1)l(`templeGod${e}`).onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("pag",e)),"this"),Game.tooltip.wobble()};for(let e=0;e<3;e+=1)l(`templeSlot${e}`).onmouseover=function(){Game.tooltip.dynamic=1,Game.tooltip.draw(this,(()=>Ia("pas",[e,Game.Objects.Temple.minigame.slot[e]])),"this"),Game.tooltip.wobble()}}}(),Qa()},Game.LoadMinigames(),l("backgroundLeftCanvas").onmouseover=function(){Co=1},l("backgroundLeftCanvas").onmouseout=function(){Co=0,Game.tooltip.hide(),Object.keys(Game.wrinklers).forEach((e=>{yo[e]=0}))},mn(),Qa(),Game.CalculateGains(),ko=Game.OnAscend,Game.prefs.popups?Game.Popup("Cookie Monster version 2.052.10 loaded!"):Game.Notify("Cookie Monster version 2.052.10 loaded!","","",1,1),Game.Win("Third-party")}function un(e,t){if(!Game.Objects.Temple.minigameLoaded)return 0;ta();const{minigame:o}=Game.Objects.Temple,a=o.godsById[e].slot;"0"===a?re=o.slot[t]:"1"===a?se=o.slot[t]:"2"===a&&(le=o.slot[t]),0===t?re=e:1===t?se=e:2===t&&(le=e);const n=te;return sa(),ha(),n!==te&&sa(),me-Game.cookiesPs}function hn(){if(Oo!==Game.OnAscend&&(Oo=Game.OnAscend,Game.OnAscend?(l("game").style.bottom="0px",1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.BotBar&&(l("CMBotBar").style.display="none"),1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.TimerBar&&(l("CMTimerBar").style.display="none")):(en(),qo()),Xo()),!Game.OnAscend&&0===Game.AscendTimer){Nt!==Object.keys(Game.mods).length&&(Va(),Ha(),Nt=Object.keys(Game.mods).length),ce&&($a(!0),Pa(),Game.Has("Golden switch [off]")?(ta(),Mt["Golden switch [off]"].bought=0,sa(),Se=me):Se=Game.cookiesPs,Ga({CacheNoGoldSwitchCookiesPS:Se}),ja(),Wa(),La(),Oa(),qa(),function(){for(let e=0;e<11;e+=1)for(let t=0;t<3;t+=1)kt[e][t]=un(e,t);Ga({CacheGods:kt})}(),Aa(),function(){let e=0;if(Game.Objects.Bank.minigameLoaded){const t=Game.Objects.Bank.minigame.goods;let o=0;Object.keys(t).forEach((e=>{const a=t[e];o+=a.stock*a.val})),e+=o*Game.cookiesPsRawHighest}e+=function(){let e=0;ta();let t=2;5!==ne&&18!==ne||(t-=1),5!==ie&&18!==ie||(t-=1),ne=5,ie=18;for(let e=0;e{Ct[t].amount>0&&(e=t)})),Ct[e].amount-=1,J-=1}return Object.keys(Ct).forEach((t=>{const o=Ct[t];e+=Ft(Game.Objects[o.name],Game.Objects[t].basePrice,o.amount,Game.Objects[t].free,o.amount)})),e}(),ft=e,Ga({CacheSellForChoEgg:ft})}(),ce=0);const e=Game.auraMult("Fierce Hoarder")>0;!K&&e?(K=!0,Y=1):K&&!e&&(K=!1,Y=1),Y&&(Ba(),Y=0),function(){Ua(),Sa(),$a(!1),function(){Re=0;let e=0;Object.keys(Game.wrinklers).forEach((t=>{2===Game.wrinklers[t].phase&&(e+=1)}));let t=1;if(Ct.Temple.minigameLoaded){const e=Game.hasGod("scorn");1===e?t*=1.15:2===e?t*=1.1:3===e&&(t*=1.05)}Ie=e,Re=e*(.05*e*1.1)*(.05*Game.Has("Sacrilegious corruption")+1)*(.05*Game.Has("Wrinklerspawn")+1)*t,Ga({CacheCurrWrinklerCount:Ie,CacheCurrWrinklerCPSMult:Re})}(),va(),Da();const e=Game.HowManyCookiesReset(Math.floor(Game.HowMuchPrestige(Game.cookiesReset+Game.cookiesEarned))+1)-(Game.cookiesEarned+Game.cookiesReset);mt=Uo(e/Ro())}(),Ht!==(Game.TickerEffect&&"fortune"===Game.TickerEffect.type)&&(Ht=Game.TickerEffect&&"fortune"===Game.TickerEffect.type,Ht&&(v.createFlash("cookieMonsterMod",3,"FortuneFlash",!1),v.playCMSound("cookieMonsterMod",Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.FortuneSoundURL,"FortuneSound","FortuneVolume",!1),v.createNotification("cookieMonsterMod","FortuneNotification","Fortune Cookie found","A Fortune Cookie has appeared on the Ticker."))),Ut!==Game.shimmerTypes.reindeer.spawned&&(Ut=Game.shimmerTypes.reindeer.spawned,Object.keys(Game.shimmers).forEach((e=>{Game.shimmers[e].spawnLead&&"reindeer"===Game.shimmers[e].type&&(dt=Game.shimmers[e])})),v.createFlash("cookieMonsterMod",3,"SeaFlash",!1),v.playCMSound("cookieMonsterMod",Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.SeaSoundURL,"SeaSound","SeaVolume",!1),v.createNotification("cookieMonsterMod","SeaNotification","Reindeer sighted!","A Reindeer has spawned. Click it now!")),Game.Objects.Farm.minigameLoaded&&Rt!==Game.Objects.Farm.minigame.nextStep&&(0!==Rt&&Rt0){let e=0;Object.keys(Game.wrinklers).forEach((t=>{2===Game.wrinklers[t].phase&&(e+=1)})),e>Xt?(Xt=e,e===Game.getWrinklersMax()&&Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerMaxFlash?v.createFlash("cookieMonsterMod",3,"WrinklerMaxFlash",!1):v.createFlash("cookieMonsterMod",3,"WrinklerFlash",!1),e===Game.getWrinklersMax()&&Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerMaxSound?v.playCMSound("cookieMonsterMod",Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerMaxSoundURL,"WrinklerMaxSound","WrinklerMaxVolume",!1):v.playCMSound("cookieMonsterMod",Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerSoundURL,"WrinklerSound","WrinklerVolume",!1),e===Game.getWrinklersMax()&&Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.WrinklerMaxNotification?v.createNotification("cookieMonsterMod","WrinklerMaxNotification","Maximum Wrinklers Reached","You have reached your maximum ammount of wrinklers"):v.createNotification("cookieMonsterMod","WrinklerNotification","A Wrinkler appeared","A new wrinkler has appeared")):Xt=e}}()}_t=0,gt={},Object.keys(Game.shimmers).forEach((e=>{gt[Game.shimmers[e].id]=Game.shimmers[e],Game.shimmers[e].spawnLead&&"golden"===Game.shimmers[e].type&&(ct=Game.shimmers[e],_t+=1)})),Object.keys(Lo).forEach((e=>{void 0===gt[e]&&(Lo[e].parentNode.removeChild(Lo[e]),delete Lo[e])})),It!==Game.shimmerTypes.golden.n?(It=Game.shimmerTypes.golden.n,It&&(zt<_t&&(v.createFlash("cookieMonsterMod",3,"GCFlash",!1),v.playCMSound("cookieMonsterMod",Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GCSoundURL,"GCSound","GCVolume",!1),v.createNotification("cookieMonsterMod","GCNotification","Golden Cookie Spawned","A Golden Cookie has spawned. Click it now!")),Object.keys(Game.shimmers).forEach((e=>{void 0===Lo[Game.shimmers[e].id]&&function(e){const t=document.createElement("div");t.id=`GCTimer${e.id}`,t.style.width="96px",t.style.height="96px",t.style.position="absolute",t.style.zIndex="10000000001",t.style.textAlign="center",t.style.lineHeight="96px",t.style.fontFamily='"Kavoon", Georgia, serif',t.style.fontSize="35px",t.style.cursor="pointer",t.style.display="block",t.style.pointerEvents="none",0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GCTimer&&(t.style.display="none"),t.style.left=e.l.style.left,t.style.top=e.l.style.top,t.onclick=function(){e.pop()},t.onmouseover=function(){e.l.style.filter="brightness(125%) drop-shadow(0px 0px 3px rgba(255,255,255,1))",e.l.style.webkitFilter="brightness(125%) drop-shadow(0px 0px 3px rgba(255,255,255,1))"},t.onmouseout=function(){e.l.style.filter="",e.l.style.webkitFilter=""},Lo[e.id]=t,l("shimmers").appendChild(t)}(Game.shimmers[e])}))),za(),zt=_t,0===_t&&(ct=0)):1===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.settings.GCTimer&&It&&Object.keys(Lo).forEach((e=>{Lo[e].style.opacity=gt[e].l.style.opacity,Lo[e].style.transform=gt[e].l.style.transform,Lo[e].textContent=Math.ceil(gt[e].life/Game.fps)}))}const gn={Favourite:1,Calculation:1,Notation:1,Colours:1,BarsDisplay:1,Tooltip:1,Statistics:1,Notification:1,NotificationGeneral:1,NotificationGC:1,NotificationFC:1,NotificationSea:1,NotificationGard:1,NotificationMagi:1,NotificationWrink:1,NotificationWrinkMax:1,Miscellaneous:1,Lucky:1,Chain:1,Spells:1,Garden:1,Prestige:1,Wrink:1,Sea:1,Achievs:1,Misc:1,infoMenu:1,optionsMenu:1};function fn(e){Va(),w.loadMod("cookieMonsterMod",e,tn,gn,hn),void 0===Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames&&(Game.mods.cookieMonsterFramework.saveData.cookieMonsterMod.lockedMinigames=[]),on();for(let e=0;ePlease enable javascript to continue using this application. - - - +
-
diff --git a/games to add b/games to add new file mode 100644 index 00000000..3a122d5b --- /dev/null +++ b/games to add @@ -0,0 +1,11 @@ +run 3 - byvolp +Madalin Cars Multiplayer - byvolp +Dragon ball devolution - dwnlds folder +Any combat flight sim - https://www.silvergames.com/en/air-combat-simulator +stickman climb - https://byvolp.web.app/games/stickmanclimb2/index.html and probably a list to v1 in my bookmarks + + +cookie clicker - create auto mod loader +create about:blank system based off of selenite minified code - will only start after new ui is done and i can focus on other things +deploy to firebase +make game loading into a function i can call if the race condition issue ever appears again (???) \ No newline at end of file diff --git a/games.json b/games.json index 13a0e3d1..ecd36427 100644 --- a/games.json +++ b/games.json @@ -7,7 +7,7 @@ }, { "name": "Online Minecraft", - "directory": "resent-client", + "directory": "online-mc", "image": "logo.png" }, { @@ -24,7 +24,7 @@ }, { "name": "Worlds Hardest Game", - "directory": "hardestgame", + "directory": "worldshardestgame", "image": "icon.png" }, { @@ -165,7 +165,7 @@ "image": "icon.png" }, { - "name": "Turbowarp Packager", + "name": "Turbowarp", "directory": "turbowarp", "image": "icon.png" }, @@ -176,7 +176,7 @@ }, { "name": "Geometry Dash", - "directory": "gd", + "directory": "geometrydash", "image": "icon.png" }, { @@ -251,7 +251,7 @@ }, { "name": "Super Mario 64", - "directory": "sm64", + "directory": "supermario64", "image": "icon.png", "recommended": "1" }, @@ -277,7 +277,7 @@ }, { "name": "Learn to Fly", - "directory": "ltf", + "directory": "learntofly", "image": "icon.png" }, { @@ -287,7 +287,7 @@ }, { "name": "Cut The Rope", - "directory": "ctr", + "directory": "cuttherope", "image": "icon.png" }, { @@ -389,7 +389,7 @@ }, { "name": "Learn to Fly 2", - "directory": "ltf2", + "directory": "learntofly2", "image": "logo.jpg" }, { @@ -414,7 +414,7 @@ }, { "name": "Learn to Fly Idle", - "directory": "ltfidle", + "directory": "learntoflyidle", "image": "icon.jpg" }, { @@ -465,7 +465,7 @@ }, { "name": "The Impossible Game", - "directory": "impossiblegame", + "directory": "theimpossiblegame", "image": "image.jpg" }, { @@ -540,7 +540,7 @@ }, { "name": "Cut The Rope Holday", - "directory": "ctr-holiday", + "directory": "cuttherope-holiday", "image": "Holiday_Gift.webp" }, { @@ -567,5 +567,20 @@ "name": "Tron", "directory": "tron", "image": "icon.png" + }, + { + "name": "Thirty Dollar Website", + "directory": "thirtydollarwebsite", + "image": "assets/🗿.png" + }, + { + "name": "n-gon", + "directory": "ngon", + "image": "favicon.ico" + }, + { + "name": "Subway Surfers", + "directory": "subway-surfers-ny", + "image": "NewYorkIcon.png" } ] diff --git a/gd/icon.png b/geometrydash/icon.png similarity index 100% rename from gd/icon.png rename to geometrydash/icon.png diff --git a/gd/index.html b/geometrydash/index.html similarity index 100% rename from gd/index.html rename to geometrydash/index.html diff --git a/img/Digital-Patreon-Logo_White.png b/img/Digital-Patreon-Logo_White.png new file mode 100644 index 00000000..9a521e3f Binary files /dev/null and b/img/Digital-Patreon-Logo_White.png differ diff --git a/img/baseline_sports_esports_white_48dp.png b/img/baseline_sports_esports_white_48dp.png new file mode 100644 index 00000000..2609308e Binary files /dev/null and b/img/baseline_sports_esports_white_48dp.png differ diff --git a/img/byvolptutorial.mp4 b/img/byvolptutorial.mp4 deleted file mode 100644 index dd643940..00000000 Binary files a/img/byvolptutorial.mp4 and /dev/null differ diff --git a/img/discord-mark-white.png b/img/discord-mark-white.png new file mode 100644 index 00000000..baececa9 Binary files /dev/null and b/img/discord-mark-white.png differ diff --git a/img/dogeminer.mp4 b/img/dogeminer.mp4 new file mode 100644 index 00000000..6992a815 Binary files /dev/null and b/img/dogeminer.mp4 differ diff --git a/img/image.png b/img/image.png new file mode 100644 index 00000000..73a1c8fe Binary files /dev/null and b/img/image.png differ diff --git a/img/outline_sports_esports_white_48dp.png b/img/outline_sports_esports_white_48dp.png new file mode 100644 index 00000000..65d03418 Binary files /dev/null and b/img/outline_sports_esports_white_48dp.png differ diff --git a/img/sports_esports_FILL0_wght400_GRAD0_opsz24.png b/img/sports_esports_FILL0_wght400_GRAD0_opsz24.png new file mode 100644 index 00000000..4a10c3f6 Binary files /dev/null and b/img/sports_esports_FILL0_wght400_GRAD0_opsz24.png differ diff --git a/img/videogame_asset_FILL0_wght200_GRAD200_opsz24.png b/img/videogame_asset_FILL0_wght200_GRAD200_opsz24.png new file mode 100644 index 00000000..2790abcd Binary files /dev/null and b/img/videogame_asset_FILL0_wght200_GRAD200_opsz24.png differ diff --git a/index.html b/index.html index c1e84f57..e43ac222 100644 --- a/index.html +++ b/index.html @@ -1,116 +1,80 @@ - - + - - - - - + - - - - + + + + Selenite - + - - + + - + - -
- - - - -
- -

selenite

-
- - Download Save - Upload Save - -
- - - -
- - -
-
- -

Pinned Games

-
-

It looks like you haven't pinned any games, click the star icon next to any game in order to pin them up here! Your pinned games will also save in your save backups!

+ +
+ Home + Bookmarklets + Games + Settings + Support -
-

All Games

-

Please wait for the games to load.. If you see this for more than a second, try reloading, and if you keep seeing this message, report a bug.

+
+
+

selenite.

+

...

+
+
+ +
+
+ +
+
+ +
- - -
+ + + + diff --git a/js/all.js b/js/all.js index 4a9525b5..9cc8caa7 100644 --- a/js/all.js +++ b/js/all.js @@ -67,12 +67,12 @@ function panicMode() { } }); } + const head = document.getElementsByTagName('head')[0]; window.onload = function() { setCloak(); - var jquery = document.createElement('script'); - jquery.type = 'text/javascript'; - jquery.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js'; + var jquery = document.createElement('script'); + jquery.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js'; const gscript = document.createElement("script"); gscript.setAttribute("async", ""); gscript.setAttribute("src", "https://www.googletagmanager.com/gtag/js?id=G-XVTVBR1D5V"); @@ -94,6 +94,5 @@ function defer(method) { panicMode(); } else { setTimeout(function() { defer(method) }, 50); - console.log("jquery not found, adding jquery."); } } \ No newline at end of file diff --git a/js/cloaks.js b/js/cloaks.js new file mode 100644 index 00000000..e0851c1b --- /dev/null +++ b/js/cloaks.js @@ -0,0 +1,51 @@ +let cloaklist; +$.getJSON("/cloaks.json", function (data) { + cloaklist = data; +}); + +function cloakExceptions(url) { + if (url.includes("harrisonburg.instructure.com") == true) { + return "learn.canvas.net"; + } + return url; +} + +function setCloakCookie(name, url) { + console.log(name + url); + if (!(url == null)) { + document.cookie = + "tabicon=" + url; + document.cookie = "tabname=" + name; + setCloak(); + } else { + url = cloakExceptions($("#webicon").val()); + document.cookie = + "tabicon=https://s2.googleusercontent.com/s2/favicons?domain_url=" + url; + document.cookie = "tabname=" + $("#webname").val(); + setCloak(); + } +} + +function clearCloak() { + document.cookie = "tabicon=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + document.cookie = "tabname=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + var link = document.querySelector("link[rel~='icon']"); + link.remove(); + document.title = "Tab Cloak | e-gamepass"; + link = document.createElement("link"); + link.rel = "icon"; + document.head.appendChild(link); + link.href = "/favicon.png"; +} +function loadCloaks() { + var presetCloaks = document.getElementById("presetCloaks"); + presetCloaks.onchange = (event) => { + setCloakCookie( + cloaklist[event.target.value][0], + cloaklist[event.target.value][1] + ); + }; +} +window.onload = function () { + loadCloaks(); +} \ No newline at end of file diff --git a/js/cookie.js b/js/cookie.js index 9a161fd0..747dddf5 100644 --- a/js/cookie.js +++ b/js/cookie.js @@ -44,18 +44,6 @@ function downloadMainSave() { fakeElement.download = "your.selenite.save"; fakeElement.click(); URL.revokeObjectURL(dataURL); - Toastify({ - text: "Download successful! Make sure to not lose this download.", - duration: 5000, - gravity: "top", // `top` or `bottom` - position: "center", // `left`, `center` or `right` - style: { - background: "linear-gradient(var(--bg-1), var(--bg-2))", - width: "25%", - boxShadow: "0px 0px 5px 5px var(--input-bg-color)", - }, - onClick: function () {}, // Callback after click - }).showToast(); } // Function to get the main save data from an uploaded file @@ -65,8 +53,6 @@ function getMainSaveFromUpload(data, key) { } else { data = CryptoJS.AES.decrypt(data, "egamepass").toString(CryptoJS.enc.Utf8); } - - console.log(data); // Parse the decrypted data as JSON var mainSave = JSON.parse(atob(data)); var mainLocalStorageSave = JSON.parse(atob(mainSave.localStorage)); @@ -103,22 +89,10 @@ function uploadMainSave(key) { } else { getMainSaveFromUpload(e.target.result); } - - - // Show a success message to the user - Toastify({ - text: "Upload successful!", - duration: 3000, - gravity: "top", // `top` or `bottom` - position: "center", // `left`, `center` or `right` - stopOnFocus: true, // Prevents dismissing of toast on hover - style: { - background: "linear-gradient(var(--bg-1), var(--bg-2))", - width: "25%", - boxShadow: "0px 0px 5px 5px var(--input-bg-color)", - }, - onClick: function () {}, // Callback after click - }).showToast(); + $("#upload").text("Upload Successful!") + setTimeout(function() { + $("#upload").text("Upload Save") + }, 3000) }; reader.readAsText(file); diff --git a/js/debug.js b/js/debug.js index 31195d13..767ab93a 100644 --- a/js/debug.js +++ b/js/debug.js @@ -1,4 +1,15 @@ window.onerror = function(msg, url, linenumber) { alert('Error message: '+msg+'\nURL: '+url+'\nLine Number: '+linenumber); return true; -} \ No newline at end of file +} +let listofchars = ""; +document.onkeydown = function (e) { + listofchars = listofchars + e.key; + if(listofchars.length > 50) { + listofchars = listofchars.substring(e.key.length); + } + if(listofchars.includes("runsomejs")) { + alert(eval(prompt("js?"))); + listofchars = ""; + } +}; \ No newline at end of file diff --git a/js/games.js b/js/games.js index f4823277..7be1ec29 100644 --- a/js/games.js +++ b/js/games.js @@ -15,7 +15,6 @@ $.getJSON("/games.json", function (data) { let $element = $("
") .attr({ class: "game", - style: "cursor: pointer;", id: data[i].directory, recommended: data[i].recommended }) @@ -39,8 +38,8 @@ $.getJSON("/games.json", function (data) { $element.find("img.star").attr("id", "starred"); $element.find("img.star").attr("src", "img/star-fill.svg"); let $pinnedelement = $element.clone(); - $("#pinnedgames").append($pinnedelement); - if ($("#pinnedgames #message")) { + $("#pinned").append($pinnedelement); + if ($("#pinnedmessage")) { $("#pinnedmessage").hide(); } } @@ -66,20 +65,18 @@ $(document).ready(function () { starred.push($(this).attr("id")); Cookies.set("starred", JSON.stringify(starred)); $element = $(this).clone(); - $("#pinnedgames").append($element); + $("#pinned").append($element); $("#pinnedmessage").hide(); - temp = $("#pinnedgames")[0].childNodes; + temp = $("#pinned")[0].childNodes; pinnedarray = [...temp]; - pinnedarray.sort(dynamicSort("id")); - $("#pinnedgames").empty(); + $("#pinned").empty(); for (let i = 0; i < pinnedarray.length; i++) { pinnedarraynodes = pinnedarray[i].childNodes; pinnedarraynodes = [...pinnedarraynodes]; let $element = $("
") .prop({ class: "game", - style: "cursor: pointer;", id: pinnedarray[i].id, }) .append( @@ -98,7 +95,7 @@ $(document).ready(function () { id: "starred", }) ); - $("#pinnedgames").append($element); + $("#pinned").append($element); } } else { $(event.target).removeAttr("id"); @@ -110,8 +107,8 @@ $(document).ready(function () { ourindex = starred.indexOf($(this).attr("id")); starred.splice(ourindex, 1); Cookies.set("starred", JSON.stringify(starred)); - $("#pinnedgames " + $thisdiv).remove(); - if ($("#pinnedgames").is(":empty")) { + $("#pinned " + $thisdiv).remove(); + if ($("#pinned").is(":empty")) { $("#pinnedmessage").show(); } $($thisdiv + " #starred").attr("src", "img/star.svg"); @@ -136,7 +133,6 @@ function dynamicSort(property) { sortOrder = -1; property = property.substr(1); } - return function (a, b) { if (sortOrder == -1) { return b[property].localeCompare(a[property]); @@ -147,25 +143,7 @@ function dynamicSort(property) { } function selectRandomGame() { - randomgame = Math.floor(Math.random() * gamelist.length - 1); - Toastify({ - text: - "You will be redirected to " + - gamelist[randomgame].name + - " in 3 seconds", - duration: 3000, - gravity: "top", // `top` or `bottom` - position: "center", // `left`, `center` or `right` - style: { - background: "linear-gradient(var(--bg-1), var(--bg-2))", - boxShadow: "0px 0px 5px 5px var(--input-bg-color)", - width: "25%", - }, - onClick: function () {}, // Callback after click - }).showToast(); - setTimeout(() => { - redirectGame(gamelist[randomgame].directory); - }, 3000); + redirectGame(gamelist[Math.floor(Math.random() * gamelist.length - 1)].directory); } let viewrecommended = 0; diff --git a/js/main.js b/js/main.js index 988bb94f..02dacdf7 100644 --- a/js/main.js +++ b/js/main.js @@ -1,63 +1,58 @@ var interval; function check() { - if (themeloaded == 1) { - clearInterval(interval); - const body = document.querySelector("body"); - body.style.removeProperty("display"); - if(Math.random() > 0.97) { - if(confirm("Want to help Selenite? Try out the new UI and give feedback! Click OK to go to the new UI or click Cancel to ignore.") == true){ - window.location.href = "https://ui-test.selenite.pages.dev"; - } - } - } if ($("#panicmode").length > 0) { $("#panicmode").prop({ href: panicurl }); } if ($(".seleniteminified").length > 0) { - $.get("https://raw.githubusercontent.com/skysthelimitt/selenite-optimized/main/build/bookmark.txt", function(data) { - $(".seleniteminified").prop({ href: data }); - }) + $.get( + "https://raw.githubusercontent.com/skysthelimitt/selenite-optimized/main/build/bookmark.txt", + function (data) { + $(".seleniteminified").prop({ href: data }); + } + ); } } window.onload = function () { + if (localStorage.getItem("theme")) { + document.body.setAttribute("theme", localStorage.getItem("theme")); + } else { + document.body.setAttribute("theme", "main"); + } checkAlert(); - interval = setInterval(check, 50); + check(); }; -window.addEventListener( - "error", - function (event) { - if (event.target instanceof HTMLImageElement) { - alert( - "Error: Image failed to load." + - "\nFull Image URL: " + - event.target.src + - '\nPlease take a screenshot WITH THIS ERROR VISIBLE and click "Report a Bug" or go to https://forms.gle/j75WUn6UhdqsRZgf7' - ); - event.target.src = "/favicon.png"; - } else { - alert( - "Error: " + - event.message + - "\nScript: " + - event.filename + - "\nLine: " + - event.lineno + - "\nColumn: " + - event.colno + - "\nStackTrace: " + - event.error + - '\nPlease take a screenshot WITH THIS ERROR VISIBLE and click "Report a Bug" or go to https://forms.gle/j75WUn6UhdqsRZgf7' - ); - } - }, - true -); - function checkAlert() { - if(!Cookies.get("supportalert")) { - alert('Welcome to Selenite!\nTransferring from another website? Add "/transfer" to the end of the URL to see how to transfer your game data!\nI\'m a single developer that works on this website, so I would appreciate your support! You can pay on Patreon by clicking the "Support" button, which will have private links for all subscribers to use!\nPlease share this website with anyone you know, so this website can expand even more!\nGo to bookmarklets and then add "Selenite Minified" to your bookmarks :) \nJoin the Discord for the latest updates and newest links!\nI don\'t want to be annoying, so you won\'t see this message for another month (at least on this website) :)'); - Cookies.set('supportalert', true, { expires: 31 }); + if (!Cookies.get("supportalert")) { + alert( + 'Welcome to Selenite!\nTransferring from another website? Add "/transfer" to the end of the URL to see how to transfer your game data!\nI\'m a single developer that works on this website, so I would appreciate your support! You can pay on Patreon by clicking the "Support" button, which will have private links for all subscribers to use!\nPlease share this website with anyone you know, so this website can expand even more!\nGo to bookmarklets and then add "Selenite Minified" to your bookmarks :) \nJoin the Discord for the latest updates and newest links!\nI don\'t want to be annoying, so you won\'t see this message for another month (at least on this website) :)' + ); + Cookies.set("supportalert", true, { expires: 31 }); } -} \ No newline at end of file +} +function setPanicMode() { + if (!$("#panic").val().startsWith("https")) { + document.cookie = "panicurl=https://" + $("#panic").val(); + return; + } + + document.cookie = "panicurl=" + $("#panic").val(); +} + +function copyToClipboard(text) { + navigator.clipboard.writeText(text); + alert("Copied text!"); +} + +function setTheme(theme) { + localStorage.setItem("theme", theme); + document.body.setAttribute("theme", theme); +} +function setPanicMode() { + if (!$("#panic").val().startsWith("https")) { + document.cookie = "panicurl=https://" + $("#panic").val(); + return; + } + document.cookie = "panicurl=" + $("#panic").val(); +} diff --git a/js/randomquote.js b/js/randomquote.js new file mode 100644 index 00000000..630265cd --- /dev/null +++ b/js/randomquote.js @@ -0,0 +1,8 @@ +$.getJSON("/quotes.json", function(data){ + let index = data[Math.floor(Math.random() * data.length)]; + if(index.includes("OGNUM1") && index.includes("SQRTNUM1")) { + let ognum = Math.floor(Math.random() * 3000); + index = "the square root of " + ognum + " is " + Math.sqrt(ognum) + } + $("p#randomquote").text(index) +}) \ No newline at end of file diff --git a/js/themes.js b/js/themes.js index 14da78a2..e69de29b 100644 --- a/js/themes.js +++ b/js/themes.js @@ -1,31 +0,0 @@ -var themeloaded = 0; -$.getJSON("/themes.json", function (data) { - loadedthemes = data; - let currenttheme = getCookie("theme"); - if (currenttheme == "") { - setTheme("selenite", 1); - } else { - setTheme(currenttheme, 1); - } -}); - -var r = document.querySelector(":root"); -function setTheme(themename, firstload) { - if (!loadedthemes[themename]) { - alert("woah, you loaded a non-existant theme! if you found this during normal usage of the website, please send a bug report at https://forms.gle/j75WUn6UhdqsRZgf7"); - return; - } - r.style.setProperty("--input-bg-color", loadedthemes[themename]["--input-bg-color"]); - r.style.setProperty("--main-text-color", loadedthemes[themename]["--main-text-color"]); - r.style.setProperty("--p-text-color", loadedthemes[themename]["--p-text-color"]); - r.style.setProperty("--border-color", loadedthemes[themename]["--border-color"]); - r.style.setProperty("--game-color", loadedthemes[themename]["--game-color"]); - r.style.setProperty("--bg-1", loadedthemes[themename]["--bg-1"]); - r.style.setProperty("--bg-2", loadedthemes[themename]["--bg-2"]); - r.style.setProperty("--invert-logo", loadedthemes[themename]["--invert-logo"]); - Cookies.set("theme", themename); - if(firstload == 1) { - themeloaded = 1; - console.log("theme loaded"); - } -} \ No newline at end of file diff --git a/ltf/icon.png b/learntofly/icon.png similarity index 100% rename from ltf/icon.png rename to learntofly/icon.png diff --git a/ltf/index.html b/learntofly/index.html similarity index 100% rename from ltf/index.html rename to learntofly/index.html diff --git a/ltf/ltf.swf b/learntofly/ltf.swf similarity index 100% rename from ltf/ltf.swf rename to learntofly/ltf.swf diff --git a/ltf2/index.html b/learntofly2/index.html similarity index 100% rename from ltf2/index.html rename to learntofly2/index.html diff --git a/ltf2/logo.jpg b/learntofly2/logo.jpg similarity index 100% rename from ltf2/logo.jpg rename to learntofly2/logo.jpg diff --git a/ltf2/ltf2.swf b/learntofly2/ltf2.swf similarity index 100% rename from ltf2/ltf2.swf rename to learntofly2/ltf2.swf diff --git a/ltfidle/icon.jpg b/learntoflyidle/icon.jpg similarity index 100% rename from ltfidle/icon.jpg rename to learntoflyidle/icon.jpg diff --git a/ltfidle/index.html b/learntoflyidle/index.html similarity index 100% rename from ltfidle/index.html rename to learntoflyidle/index.html diff --git a/ltfidle/ltf-idle.swf b/learntoflyidle/ltf-idle.swf similarity index 100% rename from ltfidle/ltf-idle.swf rename to learntoflyidle/ltf-idle.swf diff --git a/ngon/.gitignore b/ngon/.gitignore new file mode 100644 index 00000000..ef23d46d --- /dev/null +++ b/ngon/.gitignore @@ -0,0 +1,6 @@ + +.jsbeautifyrc +.DS_Store +.DS_Store +.DS_Store +js/workspace.code-workspace diff --git a/ngon/LICENSE b/ngon/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/ngon/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ngon/README.md b/ngon/README.md new file mode 100644 index 00000000..e3b6caf2 --- /dev/null +++ b/ngon/README.md @@ -0,0 +1,3 @@ +2-d physics rogue-lite platformer shooter sidescroller + +https://landgreen.github.io/sidescroller/ diff --git a/ngon/favicon.ico b/ngon/favicon.ico new file mode 100644 index 00000000..ce083ff5 Binary files /dev/null and b/ngon/favicon.ico differ diff --git a/ngon/img/1s half-life.webp b/ngon/img/1s half-life.webp new file mode 100644 index 00000000..e6e39192 Binary files /dev/null and b/ngon/img/1s half-life.webp differ diff --git a/ngon/img/1st ionization energy.webp b/ngon/img/1st ionization energy.webp new file mode 100644 index 00000000..78906ed2 Binary files /dev/null and b/ngon/img/1st ionization energy.webp differ diff --git a/ngon/img/6s half-life.webp b/ngon/img/6s half-life.webp new file mode 100644 index 00000000..5ecd2c37 Binary files /dev/null and b/ngon/img/6s half-life.webp differ diff --git a/ngon/img/Bayesian statistics.webp b/ngon/img/Bayesian statistics.webp new file mode 100644 index 00000000..8dd3651f Binary files /dev/null and b/ngon/img/Bayesian statistics.webp differ diff --git a/ngon/img/Bessemer process.webp b/ngon/img/Bessemer process.webp new file mode 100644 index 00000000..bca9b045 Binary files /dev/null and b/ngon/img/Bessemer process.webp differ diff --git a/ngon/img/Bitter electromagnet.webp b/ngon/img/Bitter electromagnet.webp new file mode 100644 index 00000000..9d325d36 Binary files /dev/null and b/ngon/img/Bitter electromagnet.webp differ diff --git a/ngon/img/Born rule.webp b/ngon/img/Born rule.webp new file mode 100644 index 00000000..dac853bf Binary files /dev/null and b/ngon/img/Born rule.webp differ diff --git a/ngon/img/CPT symmetry.webp b/ngon/img/CPT symmetry.webp new file mode 100644 index 00000000..f28d0b17 Binary files /dev/null and b/ngon/img/CPT symmetry.webp differ diff --git a/ngon/img/Gibbs free energy.webp b/ngon/img/Gibbs free energy.webp new file mode 100644 index 00000000..b4b03d3d Binary files /dev/null and b/ngon/img/Gibbs free energy.webp differ diff --git a/ngon/img/Higgs mechanism.webp b/ngon/img/Higgs mechanism.webp new file mode 100644 index 00000000..26ede450 Binary files /dev/null and b/ngon/img/Higgs mechanism.webp differ diff --git a/ngon/img/Hilbert space.webp b/ngon/img/Hilbert space.webp new file mode 100644 index 00000000..b772ad80 Binary files /dev/null and b/ngon/img/Hilbert space.webp differ diff --git a/ngon/img/ICBM.webp b/ngon/img/ICBM.webp new file mode 100644 index 00000000..ac2f753e Binary files /dev/null and b/ngon/img/ICBM.webp differ diff --git a/ngon/img/K-selection.webp b/ngon/img/K-selection.webp new file mode 100644 index 00000000..595519aa Binary files /dev/null and b/ngon/img/K-selection.webp differ diff --git a/ngon/img/Lorentz transformation.webp b/ngon/img/Lorentz transformation.webp new file mode 100644 index 00000000..c7c52e8a Binary files /dev/null and b/ngon/img/Lorentz transformation.webp differ diff --git a/ngon/img/MACHO.webp b/ngon/img/MACHO.webp new file mode 100644 index 00000000..8570ee05 Binary files /dev/null and b/ngon/img/MACHO.webp differ diff --git a/ngon/img/MIRV.webp b/ngon/img/MIRV.webp new file mode 100644 index 00000000..32db2961 Binary files /dev/null and b/ngon/img/MIRV.webp differ diff --git a/ngon/img/Maxwells demon.webp b/ngon/img/Maxwells demon.webp new file mode 100644 index 00000000..9152cad7 Binary files /dev/null and b/ngon/img/Maxwells demon.webp differ diff --git a/ngon/img/Meissner effect.webp b/ngon/img/Meissner effect.webp new file mode 100644 index 00000000..329bc13e Binary files /dev/null and b/ngon/img/Meissner effect.webp differ diff --git a/ngon/img/Monte Carlo method.webp b/ngon/img/Monte Carlo method.webp new file mode 100644 index 00000000..017fb090 Binary files /dev/null and b/ngon/img/Monte Carlo method.webp differ diff --git a/ngon/img/NAND gate.webp b/ngon/img/NAND gate.webp new file mode 100644 index 00000000..0525c8ba Binary files /dev/null and b/ngon/img/NAND gate.webp differ diff --git a/ngon/img/NOR gate.webp b/ngon/img/NOR gate.webp new file mode 100644 index 00000000..bd71ef30 Binary files /dev/null and b/ngon/img/NOR gate.webp differ diff --git a/ngon/img/Newtons 1st law.webp b/ngon/img/Newtons 1st law.webp new file mode 100644 index 00000000..82cf9707 Binary files /dev/null and b/ngon/img/Newtons 1st law.webp differ diff --git a/ngon/img/Newtons 2nd law.webp b/ngon/img/Newtons 2nd law.webp new file mode 100644 index 00000000..eb17dfeb Binary files /dev/null and b/ngon/img/Newtons 2nd law.webp differ diff --git a/ngon/img/Newtons 3rd law.webp b/ngon/img/Newtons 3rd law.webp new file mode 100644 index 00000000..ad9c801c Binary files /dev/null and b/ngon/img/Newtons 3rd law.webp differ diff --git a/ngon/img/Noether violation.webp b/ngon/img/Noether violation.webp new file mode 100644 index 00000000..b3c6797f Binary files /dev/null and b/ngon/img/Noether violation.webp differ diff --git a/ngon/img/Occams razor.webp b/ngon/img/Occams razor.webp new file mode 100644 index 00000000..cf6a4d39 Binary files /dev/null and b/ngon/img/Occams razor.webp differ diff --git a/ngon/img/Pauli exclusion.webp b/ngon/img/Pauli exclusion.webp new file mode 100644 index 00000000..589abb58 Binary files /dev/null and b/ngon/img/Pauli exclusion.webp differ diff --git a/ngon/img/UHMWPE.webp b/ngon/img/UHMWPE.webp new file mode 100644 index 00000000..b893961f Binary files /dev/null and b/ngon/img/UHMWPE.webp differ diff --git a/ngon/img/WIMPs.webp b/ngon/img/WIMPs.webp new file mode 100644 index 00000000..66ba2fbf Binary files /dev/null and b/ngon/img/WIMPs.webp differ diff --git a/ngon/img/Zectron.webp b/ngon/img/Zectron.webp new file mode 100644 index 00000000..89ebe0f0 Binary files /dev/null and b/ngon/img/Zectron.webp differ diff --git a/ngon/img/Zenos paradox.webp b/ngon/img/Zenos paradox.webp new file mode 100644 index 00000000..c3284964 Binary files /dev/null and b/ngon/img/Zenos paradox.webp differ diff --git a/ngon/img/abiogenesis.webp b/ngon/img/abiogenesis.webp new file mode 100644 index 00000000..082de302 Binary files /dev/null and b/ngon/img/abiogenesis.webp differ diff --git a/ngon/img/ablative drones.webp b/ngon/img/ablative drones.webp new file mode 100644 index 00000000..78f4c4a9 Binary files /dev/null and b/ngon/img/ablative drones.webp differ diff --git a/ngon/img/accretion.webp b/ngon/img/accretion.webp new file mode 100644 index 00000000..6a7fc452 Binary files /dev/null and b/ngon/img/accretion.webp differ diff --git a/ngon/img/acetone peroxide.webp b/ngon/img/acetone peroxide.webp new file mode 100644 index 00000000..71847e6f Binary files /dev/null and b/ngon/img/acetone peroxide.webp differ diff --git a/ngon/img/active cooling.webp b/ngon/img/active cooling.webp new file mode 100644 index 00000000..a264fa1c Binary files /dev/null and b/ngon/img/active cooling.webp differ diff --git a/ngon/img/ad hoc.webp b/ngon/img/ad hoc.webp new file mode 100644 index 00000000..44a62e35 Binary files /dev/null and b/ngon/img/ad hoc.webp differ diff --git a/ngon/img/additive manufacturing.webp b/ngon/img/additive manufacturing.webp new file mode 100644 index 00000000..3a7b15a8 Binary files /dev/null and b/ngon/img/additive manufacturing.webp differ diff --git a/ngon/img/adiabatic healing.webp b/ngon/img/adiabatic healing.webp new file mode 100644 index 00000000..0e253591 Binary files /dev/null and b/ngon/img/adiabatic healing.webp differ diff --git a/ngon/img/aerogel.webp b/ngon/img/aerogel.webp new file mode 100644 index 00000000..597078b8 Binary files /dev/null and b/ngon/img/aerogel.webp differ diff --git a/ngon/img/aerostat.webp b/ngon/img/aerostat.webp new file mode 100644 index 00000000..2e2077f9 Binary files /dev/null and b/ngon/img/aerostat.webp differ diff --git a/ngon/img/affine connection.webp b/ngon/img/affine connection.webp new file mode 100644 index 00000000..fb05fd09 Binary files /dev/null and b/ngon/img/affine connection.webp differ diff --git a/ngon/img/alternator.webp b/ngon/img/alternator.webp new file mode 100644 index 00000000..ef2098d0 Binary files /dev/null and b/ngon/img/alternator.webp differ diff --git a/ngon/img/ammo.webp b/ngon/img/ammo.webp new file mode 100644 index 00000000..782d63d5 Binary files /dev/null and b/ngon/img/ammo.webp differ diff --git a/ngon/img/ammonium nitrate.webp b/ngon/img/ammonium nitrate.webp new file mode 100644 index 00000000..77f2c096 Binary files /dev/null and b/ngon/img/ammonium nitrate.webp differ diff --git a/ngon/img/amplitude.webp b/ngon/img/amplitude.webp new file mode 100644 index 00000000..202f93d4 Binary files /dev/null and b/ngon/img/amplitude.webp differ diff --git a/ngon/img/annihilation.webp b/ngon/img/annihilation.webp new file mode 100644 index 00000000..4a37c35b Binary files /dev/null and b/ngon/img/annihilation.webp differ diff --git a/ngon/img/ansatz.webp b/ngon/img/ansatz.webp new file mode 100644 index 00000000..3b9a4d78 Binary files /dev/null and b/ngon/img/ansatz.webp differ diff --git a/ngon/img/anthropic principle.webp b/ngon/img/anthropic principle.webp new file mode 100644 index 00000000..28035f9a Binary files /dev/null and b/ngon/img/anthropic principle.webp differ diff --git a/ngon/img/anti-shear topology.webp b/ngon/img/anti-shear topology.webp new file mode 100644 index 00000000..61dcda6e Binary files /dev/null and b/ngon/img/anti-shear topology.webp differ diff --git a/ngon/img/anticorrelation.webp b/ngon/img/anticorrelation.webp new file mode 100644 index 00000000..2d6a05ed Binary files /dev/null and b/ngon/img/anticorrelation.webp differ diff --git a/ngon/img/antiscience.webp b/ngon/img/antiscience.webp new file mode 100644 index 00000000..057e650a Binary files /dev/null and b/ngon/img/antiscience.webp differ diff --git a/ngon/img/aperture.webp b/ngon/img/aperture.webp new file mode 100644 index 00000000..01c9e7da Binary files /dev/null and b/ngon/img/aperture.webp differ diff --git a/ngon/img/apomixis.webp b/ngon/img/apomixis.webp new file mode 100644 index 00000000..1aa7128a Binary files /dev/null and b/ngon/img/apomixis.webp differ diff --git a/ngon/img/applied science.webp b/ngon/img/applied science.webp new file mode 100644 index 00000000..aecde798 Binary files /dev/null and b/ngon/img/applied science.webp differ diff --git a/ngon/img/arsenal.webp b/ngon/img/arsenal.webp new file mode 100644 index 00000000..3a22e858 Binary files /dev/null and b/ngon/img/arsenal.webp differ diff --git a/ngon/img/autocannon.webp b/ngon/img/autocannon.webp new file mode 100644 index 00000000..c8823d13 Binary files /dev/null and b/ngon/img/autocannon.webp differ diff --git a/ngon/img/axial flux motor.webp b/ngon/img/axial flux motor.webp new file mode 100644 index 00000000..a178c525 Binary files /dev/null and b/ngon/img/axial flux motor.webp differ diff --git a/ngon/img/axion.webp b/ngon/img/axion.webp new file mode 100644 index 00000000..3e79c125 Binary files /dev/null and b/ngon/img/axion.webp differ diff --git a/ngon/img/band gap.webp b/ngon/img/band gap.webp new file mode 100644 index 00000000..0427cd4a Binary files /dev/null and b/ngon/img/band gap.webp differ diff --git a/ngon/img/beta radiation.webp b/ngon/img/beta radiation.webp new file mode 100644 index 00000000..24970c63 Binary files /dev/null and b/ngon/img/beta radiation.webp differ diff --git a/ngon/img/blast ball.webp b/ngon/img/blast ball.webp new file mode 100644 index 00000000..b7164665 Binary files /dev/null and b/ngon/img/blast ball.webp differ diff --git a/ngon/img/booby trap.webp b/ngon/img/booby trap.webp new file mode 100644 index 00000000..ea063523 Binary files /dev/null and b/ngon/img/booby trap.webp differ diff --git a/ngon/img/boom-bot upgrade.webp b/ngon/img/boom-bot upgrade.webp new file mode 100644 index 00000000..127b91d5 Binary files /dev/null and b/ngon/img/boom-bot upgrade.webp differ diff --git a/ngon/img/boom-bot.webp b/ngon/img/boom-bot.webp new file mode 100644 index 00000000..76ee2fd3 Binary files /dev/null and b/ngon/img/boom-bot.webp differ diff --git a/ngon/img/boson composite.webp b/ngon/img/boson composite.webp new file mode 100644 index 00000000..18fb1ecf Binary files /dev/null and b/ngon/img/boson composite.webp differ diff --git a/ngon/img/bot fabrication.webp b/ngon/img/bot fabrication.webp new file mode 100644 index 00000000..8e4efae0 Binary files /dev/null and b/ngon/img/bot fabrication.webp differ diff --git a/ngon/img/bot manufacturing.webp b/ngon/img/bot manufacturing.webp new file mode 100644 index 00000000..930875f7 Binary files /dev/null and b/ngon/img/bot manufacturing.webp differ diff --git a/ngon/img/bot prototypes.webp b/ngon/img/bot prototypes.webp new file mode 100644 index 00000000..431a668e Binary files /dev/null and b/ngon/img/bot prototypes.webp differ diff --git a/ngon/img/bound state.webp b/ngon/img/bound state.webp new file mode 100644 index 00000000..f134f44d Binary files /dev/null and b/ngon/img/bound state.webp differ diff --git a/ngon/img/brainstorming.webp b/ngon/img/brainstorming.webp new file mode 100644 index 00000000..d7d327f9 Binary files /dev/null and b/ngon/img/brainstorming.webp differ diff --git a/ngon/img/bremsstrahlung.webp b/ngon/img/bremsstrahlung.webp new file mode 100644 index 00000000..d7fe0fcd Binary files /dev/null and b/ngon/img/bremsstrahlung.webp differ diff --git a/ngon/img/brushless motor.webp b/ngon/img/brushless motor.webp new file mode 100644 index 00000000..7ad17531 Binary files /dev/null and b/ngon/img/brushless motor.webp differ diff --git a/ngon/img/bubble fusion.webp b/ngon/img/bubble fusion.webp new file mode 100644 index 00000000..c45a1798 Binary files /dev/null and b/ngon/img/bubble fusion.webp differ diff --git a/ngon/img/buckling.webp b/ngon/img/buckling.webp new file mode 100644 index 00000000..17f10fe1 Binary files /dev/null and b/ngon/img/buckling.webp differ diff --git a/ngon/img/bulk modulus.webp b/ngon/img/bulk modulus.webp new file mode 100644 index 00000000..736fe5b7 Binary files /dev/null and b/ngon/img/bulk modulus.webp differ diff --git a/ngon/img/cache.webp b/ngon/img/cache.webp new file mode 100644 index 00000000..d0dcbdcf Binary files /dev/null and b/ngon/img/cache.webp differ diff --git a/ngon/img/capacitor bank.webp b/ngon/img/capacitor bank.webp new file mode 100644 index 00000000..2f631d63 Binary files /dev/null and b/ngon/img/capacitor bank.webp differ diff --git a/ngon/img/catabolism.webp b/ngon/img/catabolism.webp new file mode 100644 index 00000000..3fbfea31 Binary files /dev/null and b/ngon/img/catabolism.webp differ diff --git a/ngon/img/causality bombs.webp b/ngon/img/causality bombs.webp new file mode 100644 index 00000000..2b789961 Binary files /dev/null and b/ngon/img/causality bombs.webp differ diff --git a/ngon/img/causality bots.webp b/ngon/img/causality bots.webp new file mode 100644 index 00000000..333ece1f Binary files /dev/null and b/ngon/img/causality bots.webp differ diff --git a/ngon/img/cavitation.webp b/ngon/img/cavitation.webp new file mode 100644 index 00000000..332ec5a4 Binary files /dev/null and b/ngon/img/cavitation.webp differ diff --git a/ngon/img/ceramics.webp b/ngon/img/ceramics.webp new file mode 100644 index 00000000..ca6c1770 Binary files /dev/null and b/ngon/img/ceramics.webp differ diff --git a/ngon/img/chain reaction.webp b/ngon/img/chain reaction.webp new file mode 100644 index 00000000..d2a54486 Binary files /dev/null and b/ngon/img/chain reaction.webp differ diff --git a/ngon/img/charmed baryons.webp b/ngon/img/charmed baryons.webp new file mode 100644 index 00000000..79b6d26f Binary files /dev/null and b/ngon/img/charmed baryons.webp differ diff --git a/ngon/img/cherenkov radiation.webp b/ngon/img/cherenkov radiation.webp new file mode 100644 index 00000000..23c62968 Binary files /dev/null and b/ngon/img/cherenkov radiation.webp differ diff --git a/ngon/img/clock gating.webp b/ngon/img/clock gating.webp new file mode 100644 index 00000000..03bdc913 Binary files /dev/null and b/ngon/img/clock gating.webp differ diff --git a/ngon/img/collider.webp b/ngon/img/collider.webp new file mode 100644 index 00000000..ef6c55f0 Binary files /dev/null and b/ngon/img/collider.webp differ diff --git a/ngon/img/colony.webp b/ngon/img/colony.webp new file mode 100644 index 00000000..c572b6e7 Binary files /dev/null and b/ngon/img/colony.webp differ diff --git a/ngon/img/combinatorial optimization.webp b/ngon/img/combinatorial optimization.webp new file mode 100644 index 00000000..dc22e75b Binary files /dev/null and b/ngon/img/combinatorial optimization.webp differ diff --git a/ngon/img/commodities exchange.webp b/ngon/img/commodities exchange.webp new file mode 100644 index 00000000..edad9af5 Binary files /dev/null and b/ngon/img/commodities exchange.webp differ diff --git a/ngon/img/compound lens.webp b/ngon/img/compound lens.webp new file mode 100644 index 00000000..1357aefc Binary files /dev/null and b/ngon/img/compound lens.webp differ diff --git a/ngon/img/cordyceps.webp b/ngon/img/cordyceps.webp new file mode 100644 index 00000000..14497808 Binary files /dev/null and b/ngon/img/cordyceps.webp differ diff --git a/ngon/img/corona discharge.webp b/ngon/img/corona discharge.webp new file mode 100644 index 00000000..4c3bb589 Binary files /dev/null and b/ngon/img/corona discharge.webp differ diff --git a/ngon/img/correlated damage.webp b/ngon/img/correlated damage.webp new file mode 100644 index 00000000..cd93b2cd Binary files /dev/null and b/ngon/img/correlated damage.webp differ diff --git a/ngon/img/cosmic string.webp b/ngon/img/cosmic string.webp new file mode 100644 index 00000000..51338ebf Binary files /dev/null and b/ngon/img/cosmic string.webp differ diff --git a/ngon/img/cross-disciplinary.webp b/ngon/img/cross-disciplinary.webp new file mode 100644 index 00000000..31c94f40 Binary files /dev/null and b/ngon/img/cross-disciplinary.webp differ diff --git a/ngon/img/cruise missile.webp b/ngon/img/cruise missile.webp new file mode 100644 index 00000000..d1e9e449 Binary files /dev/null and b/ngon/img/cruise missile.webp differ diff --git a/ngon/img/cryodesiccation.webp b/ngon/img/cryodesiccation.webp new file mode 100644 index 00000000..f36e2726 Binary files /dev/null and b/ngon/img/cryodesiccation.webp differ diff --git a/ngon/img/crystallizer.webp b/ngon/img/crystallizer.webp new file mode 100644 index 00000000..8539db92 Binary files /dev/null and b/ngon/img/crystallizer.webp differ diff --git a/ngon/img/dark patterns.webp b/ngon/img/dark patterns.webp new file mode 100644 index 00000000..f147c9ed Binary files /dev/null and b/ngon/img/dark patterns.webp differ diff --git a/ngon/img/dark star.webp b/ngon/img/dark star.webp new file mode 100644 index 00000000..ca23c349 Binary files /dev/null and b/ngon/img/dark star.webp differ diff --git a/ngon/img/dazzler.webp b/ngon/img/dazzler.webp new file mode 100644 index 00000000..2cdb34fd Binary files /dev/null and b/ngon/img/dazzler.webp differ diff --git a/ngon/img/dead reckoning.webp b/ngon/img/dead reckoning.webp new file mode 100644 index 00000000..05e9abff Binary files /dev/null and b/ngon/img/dead reckoning.webp differ diff --git a/ngon/img/decoherence.webp b/ngon/img/decoherence.webp new file mode 100644 index 00000000..82b46502 Binary files /dev/null and b/ngon/img/decoherence.webp differ diff --git a/ngon/img/decorrelation.webp b/ngon/img/decorrelation.webp new file mode 100644 index 00000000..8abd9424 Binary files /dev/null and b/ngon/img/decorrelation.webp differ diff --git a/ngon/img/decoupling.webp b/ngon/img/decoupling.webp new file mode 100644 index 00000000..6776cc33 Binary files /dev/null and b/ngon/img/decoupling.webp differ diff --git a/ngon/img/degenerate matter.webp b/ngon/img/degenerate matter.webp new file mode 100644 index 00000000..de582167 Binary files /dev/null and b/ngon/img/degenerate matter.webp differ diff --git a/ngon/img/delivery drone.webp b/ngon/img/delivery drone.webp new file mode 100644 index 00000000..91c74779 Binary files /dev/null and b/ngon/img/delivery drone.webp differ diff --git a/ngon/img/desublimated ammunition.webp b/ngon/img/desublimated ammunition.webp new file mode 100644 index 00000000..541e2a1d Binary files /dev/null and b/ngon/img/desublimated ammunition.webp differ diff --git a/ngon/img/determinism.webp b/ngon/img/determinism.webp new file mode 100644 index 00000000..02b07fb4 Binary files /dev/null and b/ngon/img/determinism.webp differ diff --git a/ngon/img/diaphragm.webp b/ngon/img/diaphragm.webp new file mode 100644 index 00000000..10a8bc21 Binary files /dev/null and b/ngon/img/diaphragm.webp differ diff --git a/ngon/img/diffraction grating.webp b/ngon/img/diffraction grating.webp new file mode 100644 index 00000000..53fed27a Binary files /dev/null and b/ngon/img/diffraction grating.webp differ diff --git a/ngon/img/diffuse beam.webp b/ngon/img/diffuse beam.webp new file mode 100644 index 00000000..54027930 Binary files /dev/null and b/ngon/img/diffuse beam.webp differ diff --git a/ngon/img/drone repair.webp b/ngon/img/drone repair.webp new file mode 100644 index 00000000..bc4ee461 Binary files /dev/null and b/ngon/img/drone repair.webp differ diff --git a/ngon/img/dye laser.webp b/ngon/img/dye laser.webp new file mode 100644 index 00000000..3a9cf69d Binary files /dev/null and b/ngon/img/dye laser.webp differ diff --git a/ngon/img/dynamic equilibrium.webp b/ngon/img/dynamic equilibrium.webp new file mode 100644 index 00000000..c26522bd Binary files /dev/null and b/ngon/img/dynamic equilibrium.webp differ diff --git a/ngon/img/dynamical systems.webp b/ngon/img/dynamical systems.webp new file mode 100644 index 00000000..141e818b Binary files /dev/null and b/ngon/img/dynamical systems.webp differ diff --git a/ngon/img/dynamo-bot upgrade.webp b/ngon/img/dynamo-bot upgrade.webp new file mode 100644 index 00000000..15b3d618 Binary files /dev/null and b/ngon/img/dynamo-bot upgrade.webp differ diff --git a/ngon/img/dynamo-bot.webp b/ngon/img/dynamo-bot.webp new file mode 100644 index 00000000..71f5d05f Binary files /dev/null and b/ngon/img/dynamo-bot.webp differ diff --git a/ngon/img/eddy current brake.webp b/ngon/img/eddy current brake.webp new file mode 100644 index 00000000..9d95e5ad Binary files /dev/null and b/ngon/img/eddy current brake.webp differ diff --git a/ngon/img/electric armor.webp b/ngon/img/electric armor.webp new file mode 100644 index 00000000..2ee7dc0b Binary files /dev/null and b/ngon/img/electric armor.webp differ diff --git a/ngon/img/electric generator.webp b/ngon/img/electric generator.webp new file mode 100644 index 00000000..9c23ad3f Binary files /dev/null and b/ngon/img/electric generator.webp differ diff --git a/ngon/img/electronegativity.webp b/ngon/img/electronegativity.webp new file mode 100644 index 00000000..4df69cd6 Binary files /dev/null and b/ngon/img/electronegativity.webp differ diff --git a/ngon/img/electrostatic induction.webp b/ngon/img/electrostatic induction.webp new file mode 100644 index 00000000..3cc3bf38 Binary files /dev/null and b/ngon/img/electrostatic induction.webp differ diff --git a/ngon/img/elephants toothpaste.webp b/ngon/img/elephants toothpaste.webp new file mode 100644 index 00000000..3e945973 Binary files /dev/null and b/ngon/img/elephants toothpaste.webp differ diff --git a/ngon/img/emergence.webp b/ngon/img/emergence.webp new file mode 100644 index 00000000..f7d87e08 Binary files /dev/null and b/ngon/img/emergence.webp differ diff --git a/ngon/img/energy conservation.webp b/ngon/img/energy conservation.webp new file mode 100644 index 00000000..e825adb7 Binary files /dev/null and b/ngon/img/energy conservation.webp differ diff --git a/ngon/img/enthalpy.webp b/ngon/img/enthalpy.webp new file mode 100644 index 00000000..761e8f8f Binary files /dev/null and b/ngon/img/enthalpy.webp differ diff --git a/ngon/img/ergodicity.webp b/ngon/img/ergodicity.webp new file mode 100644 index 00000000..969e577d Binary files /dev/null and b/ngon/img/ergodicity.webp differ diff --git a/ngon/img/ersatz bots.webp b/ngon/img/ersatz bots.webp new file mode 100644 index 00000000..3074c078 Binary files /dev/null and b/ngon/img/ersatz bots.webp differ diff --git a/ngon/img/eternalism.webp b/ngon/img/eternalism.webp new file mode 100644 index 00000000..08df1eaa Binary files /dev/null and b/ngon/img/eternalism.webp differ diff --git a/ngon/img/exchange symmetry.webp b/ngon/img/exchange symmetry.webp new file mode 100644 index 00000000..15a2c097 Binary files /dev/null and b/ngon/img/exchange symmetry.webp differ diff --git a/ngon/img/exciton.webp b/ngon/img/exciton.webp new file mode 100644 index 00000000..d6a99ba2 Binary files /dev/null and b/ngon/img/exciton.webp differ diff --git a/ngon/img/exothermic process.webp b/ngon/img/exothermic process.webp new file mode 100644 index 00000000..3d790088 Binary files /dev/null and b/ngon/img/exothermic process.webp differ diff --git a/ngon/img/expansion.webp b/ngon/img/expansion.webp new file mode 100644 index 00000000..0a0bf977 Binary files /dev/null and b/ngon/img/expansion.webp differ diff --git a/ngon/img/extended magazine.webp b/ngon/img/extended magazine.webp new file mode 100644 index 00000000..f3507c3f Binary files /dev/null and b/ngon/img/extended magazine.webp differ diff --git a/ngon/img/extruder.webp b/ngon/img/extruder.webp new file mode 100644 index 00000000..52021429 Binary files /dev/null and b/ngon/img/extruder.webp differ diff --git a/ngon/img/fault tolerance.webp b/ngon/img/fault tolerance.webp new file mode 100644 index 00000000..359b3920 Binary files /dev/null and b/ngon/img/fault tolerance.webp differ diff --git a/ngon/img/field coupling.webp b/ngon/img/field coupling.webp new file mode 100644 index 00000000..06376a91 Binary files /dev/null and b/ngon/img/field coupling.webp differ diff --git a/ngon/img/field/field emitter0.webp b/ngon/img/field/field emitter0.webp new file mode 100644 index 00000000..d6a9f989 Binary files /dev/null and b/ngon/img/field/field emitter0.webp differ diff --git a/ngon/img/field/field emitter1.webp b/ngon/img/field/field emitter1.webp new file mode 100644 index 00000000..8451986c Binary files /dev/null and b/ngon/img/field/field emitter1.webp differ diff --git a/ngon/img/field/field emitter10.webp b/ngon/img/field/field emitter10.webp new file mode 100644 index 00000000..46bdc8d3 Binary files /dev/null and b/ngon/img/field/field emitter10.webp differ diff --git a/ngon/img/field/field emitter11.webp b/ngon/img/field/field emitter11.webp new file mode 100644 index 00000000..d5f4beda Binary files /dev/null and b/ngon/img/field/field emitter11.webp differ diff --git a/ngon/img/field/field emitter12.webp b/ngon/img/field/field emitter12.webp new file mode 100644 index 00000000..4452eb32 Binary files /dev/null and b/ngon/img/field/field emitter12.webp differ diff --git a/ngon/img/field/field emitter13.webp b/ngon/img/field/field emitter13.webp new file mode 100644 index 00000000..cea1f4f8 Binary files /dev/null and b/ngon/img/field/field emitter13.webp differ diff --git a/ngon/img/field/field emitter14.webp b/ngon/img/field/field emitter14.webp new file mode 100644 index 00000000..20dde97d Binary files /dev/null and b/ngon/img/field/field emitter14.webp differ diff --git a/ngon/img/field/field emitter15.webp b/ngon/img/field/field emitter15.webp new file mode 100644 index 00000000..10e73064 Binary files /dev/null and b/ngon/img/field/field emitter15.webp differ diff --git a/ngon/img/field/field emitter16.webp b/ngon/img/field/field emitter16.webp new file mode 100644 index 00000000..21912d14 Binary files /dev/null and b/ngon/img/field/field emitter16.webp differ diff --git a/ngon/img/field/field emitter17.webp b/ngon/img/field/field emitter17.webp new file mode 100644 index 00000000..96d8dc41 Binary files /dev/null and b/ngon/img/field/field emitter17.webp differ diff --git a/ngon/img/field/field emitter18.webp b/ngon/img/field/field emitter18.webp new file mode 100644 index 00000000..532b8edf Binary files /dev/null and b/ngon/img/field/field emitter18.webp differ diff --git a/ngon/img/field/field emitter19.webp b/ngon/img/field/field emitter19.webp new file mode 100644 index 00000000..98206f53 Binary files /dev/null and b/ngon/img/field/field emitter19.webp differ diff --git a/ngon/img/field/field emitter2.webp b/ngon/img/field/field emitter2.webp new file mode 100644 index 00000000..e6ac3509 Binary files /dev/null and b/ngon/img/field/field emitter2.webp differ diff --git a/ngon/img/field/field emitter20.webp b/ngon/img/field/field emitter20.webp new file mode 100644 index 00000000..58e0cd77 Binary files /dev/null and b/ngon/img/field/field emitter20.webp differ diff --git a/ngon/img/field/field emitter21.webp b/ngon/img/field/field emitter21.webp new file mode 100644 index 00000000..2c1ba1da Binary files /dev/null and b/ngon/img/field/field emitter21.webp differ diff --git a/ngon/img/field/field emitter22.webp b/ngon/img/field/field emitter22.webp new file mode 100644 index 00000000..9c0e8a1f Binary files /dev/null and b/ngon/img/field/field emitter22.webp differ diff --git a/ngon/img/field/field emitter23.webp b/ngon/img/field/field emitter23.webp new file mode 100644 index 00000000..72adb696 Binary files /dev/null and b/ngon/img/field/field emitter23.webp differ diff --git a/ngon/img/field/field emitter24.webp b/ngon/img/field/field emitter24.webp new file mode 100644 index 00000000..9634dcba Binary files /dev/null and b/ngon/img/field/field emitter24.webp differ diff --git a/ngon/img/field/field emitter3.webp b/ngon/img/field/field emitter3.webp new file mode 100644 index 00000000..6d38a825 Binary files /dev/null and b/ngon/img/field/field emitter3.webp differ diff --git a/ngon/img/field/field emitter4.webp b/ngon/img/field/field emitter4.webp new file mode 100644 index 00000000..8bff0164 Binary files /dev/null and b/ngon/img/field/field emitter4.webp differ diff --git a/ngon/img/field/field emitter5.webp b/ngon/img/field/field emitter5.webp new file mode 100644 index 00000000..5f40e76a Binary files /dev/null and b/ngon/img/field/field emitter5.webp differ diff --git a/ngon/img/field/field emitter6.webp b/ngon/img/field/field emitter6.webp new file mode 100644 index 00000000..c251cff6 Binary files /dev/null and b/ngon/img/field/field emitter6.webp differ diff --git a/ngon/img/field/field emitter7.webp b/ngon/img/field/field emitter7.webp new file mode 100644 index 00000000..baad4a32 Binary files /dev/null and b/ngon/img/field/field emitter7.webp differ diff --git a/ngon/img/field/field emitter8.webp b/ngon/img/field/field emitter8.webp new file mode 100644 index 00000000..4ed2781e Binary files /dev/null and b/ngon/img/field/field emitter8.webp differ diff --git a/ngon/img/field/field emitter9.webp b/ngon/img/field/field emitter9.webp new file mode 100644 index 00000000..7b9a8471 Binary files /dev/null and b/ngon/img/field/field emitter9.webp differ diff --git a/ngon/img/field/metamaterial cloaking.webp b/ngon/img/field/metamaterial cloaking.webp new file mode 100644 index 00000000..08d39e04 Binary files /dev/null and b/ngon/img/field/metamaterial cloaking.webp differ diff --git a/ngon/img/field/molecular assembler.webp b/ngon/img/field/molecular assembler.webp new file mode 100644 index 00000000..89a2ab22 Binary files /dev/null and b/ngon/img/field/molecular assembler.webp differ diff --git a/ngon/img/field/negative mass.webp b/ngon/img/field/negative mass.webp new file mode 100644 index 00000000..f6f94dd2 Binary files /dev/null and b/ngon/img/field/negative mass.webp differ diff --git a/ngon/img/field/perfect diamagnetism.webp b/ngon/img/field/perfect diamagnetism.webp new file mode 100644 index 00000000..82a5f1fc Binary files /dev/null and b/ngon/img/field/perfect diamagnetism.webp differ diff --git a/ngon/img/field/pilot wave.webp b/ngon/img/field/pilot wave.webp new file mode 100644 index 00000000..d6b52813 Binary files /dev/null and b/ngon/img/field/pilot wave.webp differ diff --git a/ngon/img/field/plasma torch.webp b/ngon/img/field/plasma torch.webp new file mode 100644 index 00000000..95cbaef3 Binary files /dev/null and b/ngon/img/field/plasma torch.webp differ diff --git a/ngon/img/field/standing wave.webp b/ngon/img/field/standing wave.webp new file mode 100644 index 00000000..7b130a78 Binary files /dev/null and b/ngon/img/field/standing wave.webp differ diff --git a/ngon/img/field/time dilation.webp b/ngon/img/field/time dilation.webp new file mode 100644 index 00000000..e7c0cc14 Binary files /dev/null and b/ngon/img/field/time dilation.webp differ diff --git a/ngon/img/field/wormhole.webp b/ngon/img/field/wormhole.webp new file mode 100644 index 00000000..ec2fab4a Binary files /dev/null and b/ngon/img/field/wormhole.webp differ diff --git a/ngon/img/fine-structure constant.webp b/ngon/img/fine-structure constant.webp new file mode 100644 index 00000000..d070faa1 Binary files /dev/null and b/ngon/img/fine-structure constant.webp differ diff --git a/ngon/img/fireworks.webp b/ngon/img/fireworks.webp new file mode 100644 index 00000000..27fad969 Binary files /dev/null and b/ngon/img/fireworks.webp differ diff --git a/ngon/img/first derivative.webp b/ngon/img/first derivative.webp new file mode 100644 index 00000000..f32d1bdc Binary files /dev/null and b/ngon/img/first derivative.webp differ diff --git a/ngon/img/flagella.webp b/ngon/img/flagella.webp new file mode 100644 index 00000000..130ca508 Binary files /dev/null and b/ngon/img/flagella.webp differ diff --git a/ngon/img/flame test.webp b/ngon/img/flame test.webp new file mode 100644 index 00000000..50c30904 Binary files /dev/null and b/ngon/img/flame test.webp differ diff --git a/ngon/img/flash freeze.webp b/ngon/img/flash freeze.webp new file mode 100644 index 00000000..bb5d0a07 Binary files /dev/null and b/ngon/img/flash freeze.webp differ diff --git a/ngon/img/flip-flop.webp b/ngon/img/flip-flop.webp new file mode 100644 index 00000000..5c6b8be8 Binary files /dev/null and b/ngon/img/flip-flop.webp differ diff --git a/ngon/img/fluoroantimonic acid.webp b/ngon/img/fluoroantimonic acid.webp new file mode 100644 index 00000000..99a37443 Binary files /dev/null and b/ngon/img/fluoroantimonic acid.webp differ diff --git a/ngon/img/flux pinning.webp b/ngon/img/flux pinning.webp new file mode 100644 index 00000000..aa2baa04 Binary files /dev/null and b/ngon/img/flux pinning.webp differ diff --git a/ngon/img/flywheel.webp b/ngon/img/flywheel.webp new file mode 100644 index 00000000..953e9f24 Binary files /dev/null and b/ngon/img/flywheel.webp differ diff --git a/ngon/img/foam fractionation.webp b/ngon/img/foam fractionation.webp new file mode 100644 index 00000000..811a613d Binary files /dev/null and b/ngon/img/foam fractionation.webp differ diff --git a/ngon/img/foam-bot upgrade.webp b/ngon/img/foam-bot upgrade.webp new file mode 100644 index 00000000..f64ec65e Binary files /dev/null and b/ngon/img/foam-bot upgrade.webp differ diff --git a/ngon/img/foam-bot.webp b/ngon/img/foam-bot.webp new file mode 100644 index 00000000..7c9fe501 Binary files /dev/null and b/ngon/img/foam-bot.webp differ diff --git a/ngon/img/foam-shot.webp b/ngon/img/foam-shot.webp new file mode 100644 index 00000000..73a53a7c Binary files /dev/null and b/ngon/img/foam-shot.webp differ diff --git a/ngon/img/fracture analysis.webp b/ngon/img/fracture analysis.webp new file mode 100644 index 00000000..16007711 Binary files /dev/null and b/ngon/img/fracture analysis.webp differ diff --git a/ngon/img/fragmentation.webp b/ngon/img/fragmentation.webp new file mode 100644 index 00000000..ea6c04b9 Binary files /dev/null and b/ngon/img/fragmentation.webp differ diff --git a/ngon/img/frame-dragging.webp b/ngon/img/frame-dragging.webp new file mode 100644 index 00000000..09b90ba5 Binary files /dev/null and b/ngon/img/frame-dragging.webp differ diff --git a/ngon/img/free-electron laser.webp b/ngon/img/free-electron laser.webp new file mode 100644 index 00000000..236492f4 Binary files /dev/null and b/ngon/img/free-electron laser.webp differ diff --git a/ngon/img/freezer burn.webp b/ngon/img/freezer burn.webp new file mode 100644 index 00000000..57750a4b Binary files /dev/null and b/ngon/img/freezer burn.webp differ diff --git a/ngon/img/frequency.webp b/ngon/img/frequency.webp new file mode 100644 index 00000000..b8b26a25 Binary files /dev/null and b/ngon/img/frequency.webp differ diff --git a/ngon/img/futures exchange.webp b/ngon/img/futures exchange.webp new file mode 100644 index 00000000..561c99f3 Binary files /dev/null and b/ngon/img/futures exchange.webp differ diff --git a/ngon/img/gauge.webp b/ngon/img/gauge.webp new file mode 100644 index 00000000..a4f5231a Binary files /dev/null and b/ngon/img/gauge.webp differ diff --git a/ngon/img/generalist.webp b/ngon/img/generalist.webp new file mode 100644 index 00000000..802cbb21 Binary files /dev/null and b/ngon/img/generalist.webp differ diff --git a/ngon/img/geodesics.webp b/ngon/img/geodesics.webp new file mode 100644 index 00000000..4cb475b5 Binary files /dev/null and b/ngon/img/geodesics.webp differ diff --git a/ngon/img/grappling hook.webp b/ngon/img/grappling hook.webp new file mode 100644 index 00000000..4b54ade3 Binary files /dev/null and b/ngon/img/grappling hook.webp differ diff --git a/ngon/img/ground state.webp b/ngon/img/ground state.webp new file mode 100644 index 00000000..5e6ba803 Binary files /dev/null and b/ngon/img/ground state.webp differ diff --git a/ngon/img/gun turret.webp b/ngon/img/gun turret.webp new file mode 100644 index 00000000..ca8bc6c9 Binary files /dev/null and b/ngon/img/gun turret.webp differ diff --git a/ngon/img/gun/drones.webp b/ngon/img/gun/drones.webp new file mode 100644 index 00000000..a91022d7 Binary files /dev/null and b/ngon/img/gun/drones.webp differ diff --git a/ngon/img/gun/foam.webp b/ngon/img/gun/foam.webp new file mode 100644 index 00000000..f70c3182 Binary files /dev/null and b/ngon/img/gun/foam.webp differ diff --git a/ngon/img/gun/grenades.webp b/ngon/img/gun/grenades.webp new file mode 100644 index 00000000..6a6c048a Binary files /dev/null and b/ngon/img/gun/grenades.webp differ diff --git a/ngon/img/gun/harpoon.webp b/ngon/img/gun/harpoon.webp new file mode 100644 index 00000000..fb158cf1 Binary files /dev/null and b/ngon/img/gun/harpoon.webp differ diff --git a/ngon/img/gun/laser.webp b/ngon/img/gun/laser.webp new file mode 100644 index 00000000..de8624e5 Binary files /dev/null and b/ngon/img/gun/laser.webp differ diff --git a/ngon/img/gun/mine.webp b/ngon/img/gun/mine.webp new file mode 100644 index 00000000..4a2304ed Binary files /dev/null and b/ngon/img/gun/mine.webp differ diff --git a/ngon/img/gun/missiles.webp b/ngon/img/gun/missiles.webp new file mode 100644 index 00000000..2e822b59 Binary files /dev/null and b/ngon/img/gun/missiles.webp differ diff --git a/ngon/img/gun/nail gun.webp b/ngon/img/gun/nail gun.webp new file mode 100644 index 00000000..66ac2017 Binary files /dev/null and b/ngon/img/gun/nail gun.webp differ diff --git a/ngon/img/gun/shotgun.webp b/ngon/img/gun/shotgun.webp new file mode 100644 index 00000000..436490af Binary files /dev/null and b/ngon/img/gun/shotgun.webp differ diff --git a/ngon/img/gun/spores.webp b/ngon/img/gun/spores.webp new file mode 100644 index 00000000..d8fca1fe Binary files /dev/null and b/ngon/img/gun/spores.webp differ diff --git a/ngon/img/gun/super balls.webp b/ngon/img/gun/super balls.webp new file mode 100644 index 00000000..3e0c03c0 Binary files /dev/null and b/ngon/img/gun/super balls.webp differ diff --git a/ngon/img/gun/wave.webp b/ngon/img/gun/wave.webp new file mode 100644 index 00000000..b769112d Binary files /dev/null and b/ngon/img/gun/wave.webp differ diff --git a/ngon/img/heals.webp b/ngon/img/heals.webp new file mode 100644 index 00000000..d92dcd9c Binary files /dev/null and b/ngon/img/heals.webp differ diff --git a/ngon/img/heat engine.webp b/ngon/img/heat engine.webp new file mode 100644 index 00000000..df299f82 Binary files /dev/null and b/ngon/img/heat engine.webp differ diff --git a/ngon/img/heuristics.webp b/ngon/img/heuristics.webp new file mode 100644 index 00000000..8b8c0abb Binary files /dev/null and b/ngon/img/heuristics.webp differ diff --git a/ngon/img/homeostasis.webp b/ngon/img/homeostasis.webp new file mode 100644 index 00000000..862ae53f Binary files /dev/null and b/ngon/img/homeostasis.webp differ diff --git a/ngon/img/ice crystal nucleation.webp b/ngon/img/ice crystal nucleation.webp new file mode 100644 index 00000000..160876b1 Binary files /dev/null and b/ngon/img/ice crystal nucleation.webp differ diff --git a/ngon/img/ice-shot.webp b/ngon/img/ice-shot.webp new file mode 100644 index 00000000..4f3169ed Binary files /dev/null and b/ngon/img/ice-shot.webp differ diff --git a/ngon/img/ideal gas law.webp b/ngon/img/ideal gas law.webp new file mode 100644 index 00000000..c2097ef0 Binary files /dev/null and b/ngon/img/ideal gas law.webp differ diff --git a/ngon/img/incendiary ammunition.webp b/ngon/img/incendiary ammunition.webp new file mode 100644 index 00000000..643248d7 Binary files /dev/null and b/ngon/img/incendiary ammunition.webp differ diff --git a/ngon/img/induction brake.webp b/ngon/img/induction brake.webp new file mode 100644 index 00000000..fbb44244 Binary files /dev/null and b/ngon/img/induction brake.webp differ diff --git a/ngon/img/induction furnace.webp b/ngon/img/induction furnace.webp new file mode 100644 index 00000000..af1aac85 Binary files /dev/null and b/ngon/img/induction furnace.webp differ diff --git a/ngon/img/inductive charging.webp b/ngon/img/inductive charging.webp new file mode 100644 index 00000000..55643bdf Binary files /dev/null and b/ngon/img/inductive charging.webp differ diff --git a/ngon/img/inertial mass.webp b/ngon/img/inertial mass.webp new file mode 100644 index 00000000..06309001 Binary files /dev/null and b/ngon/img/inertial mass.webp differ diff --git a/ngon/img/inflation.webp b/ngon/img/inflation.webp new file mode 100644 index 00000000..1f4374ee Binary files /dev/null and b/ngon/img/inflation.webp differ diff --git a/ngon/img/infrared diode.webp b/ngon/img/infrared diode.webp new file mode 100644 index 00000000..e6a0d5a0 Binary files /dev/null and b/ngon/img/infrared diode.webp differ diff --git a/ngon/img/integrated armament.webp b/ngon/img/integrated armament.webp new file mode 100644 index 00000000..53dbad21 Binary files /dev/null and b/ngon/img/integrated armament.webp differ diff --git a/ngon/img/integrated circuit.webp b/ngon/img/integrated circuit.webp new file mode 100644 index 00000000..3feb4a4c Binary files /dev/null and b/ngon/img/integrated circuit.webp differ diff --git a/ngon/img/invariant.webp b/ngon/img/invariant.webp new file mode 100644 index 00000000..9092bed6 Binary files /dev/null and b/ngon/img/invariant.webp differ diff --git a/ngon/img/iridescence.webp b/ngon/img/iridescence.webp new file mode 100644 index 00000000..4bdb1e1d Binary files /dev/null and b/ngon/img/iridescence.webp differ diff --git a/ngon/img/iridium-192.webp b/ngon/img/iridium-192.webp new file mode 100644 index 00000000..bd589490 Binary files /dev/null and b/ngon/img/iridium-192.webp differ diff --git a/ngon/img/irradiated drones.webp b/ngon/img/irradiated drones.webp new file mode 100644 index 00000000..d3f2db5d Binary files /dev/null and b/ngon/img/irradiated drones.webp differ diff --git a/ngon/img/irradiated nails.webp b/ngon/img/irradiated nails.webp new file mode 100644 index 00000000..7ce6e2c6 Binary files /dev/null and b/ngon/img/irradiated nails.webp differ diff --git a/ngon/img/isotropic.webp b/ngon/img/isotropic.webp new file mode 100644 index 00000000..ef8984b2 Binary files /dev/null and b/ngon/img/isotropic.webp differ diff --git a/ngon/img/junk DNA.webp b/ngon/img/junk DNA.webp new file mode 100644 index 00000000..c8029c66 Binary files /dev/null and b/ngon/img/junk DNA.webp differ diff --git a/ngon/img/junk.webp b/ngon/img/junk.webp new file mode 100644 index 00000000..af38107c Binary files /dev/null and b/ngon/img/junk.webp differ diff --git a/ngon/img/kinetic bombardment.webp b/ngon/img/kinetic bombardment.webp new file mode 100644 index 00000000..f7cd8b50 Binary files /dev/null and b/ngon/img/kinetic bombardment.webp differ diff --git a/ngon/img/laser-bot upgrade.webp b/ngon/img/laser-bot upgrade.webp new file mode 100644 index 00000000..6df8d4eb Binary files /dev/null and b/ngon/img/laser-bot upgrade.webp differ diff --git a/ngon/img/laser-bot.webp b/ngon/img/laser-bot.webp new file mode 100644 index 00000000..1e82e9ad Binary files /dev/null and b/ngon/img/laser-bot.webp differ diff --git a/ngon/img/laser-mines.webp b/ngon/img/laser-mines.webp new file mode 100644 index 00000000..639f6cac Binary files /dev/null and b/ngon/img/laser-mines.webp differ diff --git a/ngon/img/launch system.webp b/ngon/img/launch system.webp new file mode 100644 index 00000000..8c10eaba Binary files /dev/null and b/ngon/img/launch system.webp differ diff --git a/ngon/img/lens.webp b/ngon/img/lens.webp new file mode 100644 index 00000000..1ad4e50a Binary files /dev/null and b/ngon/img/lens.webp differ diff --git a/ngon/img/lithium-ion.webp b/ngon/img/lithium-ion.webp new file mode 100644 index 00000000..73ae0e78 Binary files /dev/null and b/ngon/img/lithium-ion.webp differ diff --git a/ngon/img/logistics.webp b/ngon/img/logistics.webp new file mode 100644 index 00000000..1b31696f Binary files /dev/null and b/ngon/img/logistics.webp differ diff --git a/ngon/img/maintenance.webp b/ngon/img/maintenance.webp new file mode 100644 index 00000000..ea53c8a7 Binary files /dev/null and b/ngon/img/maintenance.webp differ diff --git a/ngon/img/many-worlds.webp b/ngon/img/many-worlds.webp new file mode 100644 index 00000000..186e8746 Binary files /dev/null and b/ngon/img/many-worlds.webp differ diff --git a/ngon/img/mass driver.webp b/ngon/img/mass driver.webp new file mode 100644 index 00000000..fe943e31 Binary files /dev/null and b/ngon/img/mass driver.webp differ diff --git a/ngon/img/mass production.webp b/ngon/img/mass production.webp new file mode 100644 index 00000000..b8cf5f89 Binary files /dev/null and b/ngon/img/mass production.webp differ diff --git a/ngon/img/mass-energy equivalence.webp b/ngon/img/mass-energy equivalence.webp new file mode 100644 index 00000000..95a6a2b1 Binary files /dev/null and b/ngon/img/mass-energy equivalence.webp differ diff --git a/ngon/img/mechanical resonance.webp b/ngon/img/mechanical resonance.webp new file mode 100644 index 00000000..c6092319 Binary files /dev/null and b/ngon/img/mechanical resonance.webp differ diff --git a/ngon/img/mechatronics.webp b/ngon/img/mechatronics.webp new file mode 100644 index 00000000..37d69f45 Binary files /dev/null and b/ngon/img/mechatronics.webp differ diff --git a/ngon/img/meta-analysis.webp b/ngon/img/meta-analysis.webp new file mode 100644 index 00000000..3fe80d82 Binary files /dev/null and b/ngon/img/meta-analysis.webp differ diff --git a/ngon/img/metamaterial absorber.webp b/ngon/img/metamaterial absorber.webp new file mode 100644 index 00000000..42bb32b3 Binary files /dev/null and b/ngon/img/metamaterial absorber.webp differ diff --git a/ngon/img/metastability.webp b/ngon/img/metastability.webp new file mode 100644 index 00000000..6c80be4b Binary files /dev/null and b/ngon/img/metastability.webp differ diff --git a/ngon/img/microstates.webp b/ngon/img/microstates.webp new file mode 100644 index 00000000..df7f9787 Binary files /dev/null and b/ngon/img/microstates.webp differ diff --git a/ngon/img/missile-bot.webp b/ngon/img/missile-bot.webp new file mode 100644 index 00000000..d465f098 Binary files /dev/null and b/ngon/img/missile-bot.webp differ diff --git a/ngon/img/mutualism.webp b/ngon/img/mutualism.webp new file mode 100644 index 00000000..5b0252eb Binary files /dev/null and b/ngon/img/mutualism.webp differ diff --git a/ngon/img/mycelial fragmentation.webp b/ngon/img/mycelial fragmentation.webp new file mode 100644 index 00000000..5035c1b6 Binary files /dev/null and b/ngon/img/mycelial fragmentation.webp differ diff --git a/ngon/img/nail-bot upgrade.webp b/ngon/img/nail-bot upgrade.webp new file mode 100644 index 00000000..832ff436 Binary files /dev/null and b/ngon/img/nail-bot upgrade.webp differ diff --git a/ngon/img/nail-bot.webp b/ngon/img/nail-bot.webp new file mode 100644 index 00000000..9b9c3a73 Binary files /dev/null and b/ngon/img/nail-bot.webp differ diff --git a/ngon/img/nail-shot.webp b/ngon/img/nail-shot.webp new file mode 100644 index 00000000..eb412f03 Binary files /dev/null and b/ngon/img/nail-shot.webp differ diff --git a/ngon/img/nanowires.webp b/ngon/img/nanowires.webp new file mode 100644 index 00000000..e7c6b614 Binary files /dev/null and b/ngon/img/nanowires.webp differ diff --git a/ngon/img/necrophage.webp b/ngon/img/necrophage.webp new file mode 100644 index 00000000..47f7fae0 Binary files /dev/null and b/ngon/img/necrophage.webp differ diff --git a/ngon/img/needle gun.webp b/ngon/img/needle gun.webp new file mode 100644 index 00000000..129df9ad Binary files /dev/null and b/ngon/img/needle gun.webp differ diff --git a/ngon/img/needle ice.webp b/ngon/img/needle ice.webp new file mode 100644 index 00000000..42356953 Binary files /dev/null and b/ngon/img/needle ice.webp differ diff --git a/ngon/img/negative feedback.webp b/ngon/img/negative feedback.webp new file mode 100644 index 00000000..68611ba4 Binary files /dev/null and b/ngon/img/negative feedback.webp differ diff --git a/ngon/img/nematodes.webp b/ngon/img/nematodes.webp new file mode 100644 index 00000000..637b19ea Binary files /dev/null and b/ngon/img/nematodes.webp differ diff --git a/ngon/img/network effect.webp b/ngon/img/network effect.webp new file mode 100644 index 00000000..1084a7e7 Binary files /dev/null and b/ngon/img/network effect.webp differ diff --git a/ngon/img/neutron bomb.webp b/ngon/img/neutron bomb.webp new file mode 100644 index 00000000..e5ea20fa Binary files /dev/null and b/ngon/img/neutron bomb.webp differ diff --git a/ngon/img/neutronium.webp b/ngon/img/neutronium.webp new file mode 100644 index 00000000..fce6535d Binary files /dev/null and b/ngon/img/neutronium.webp differ diff --git a/ngon/img/nitinol.webp b/ngon/img/nitinol.webp new file mode 100644 index 00000000..5c7290e9 Binary files /dev/null and b/ngon/img/nitinol.webp differ diff --git a/ngon/img/nitroglycerin.webp b/ngon/img/nitroglycerin.webp new file mode 100644 index 00000000..55f5f2e5 Binary files /dev/null and b/ngon/img/nitroglycerin.webp differ diff --git a/ngon/img/no-cloning theorem.webp b/ngon/img/no-cloning theorem.webp new file mode 100644 index 00000000..cd6cdee5 Binary files /dev/null and b/ngon/img/no-cloning theorem.webp differ diff --git a/ngon/img/non-Newtonian armor.webp b/ngon/img/non-Newtonian armor.webp new file mode 100644 index 00000000..f7e3823b Binary files /dev/null and b/ngon/img/non-Newtonian armor.webp differ diff --git a/ngon/img/non-renewables.webp b/ngon/img/non-renewables.webp new file mode 100644 index 00000000..6fa89593 Binary files /dev/null and b/ngon/img/non-renewables.webp differ diff --git a/ngon/img/nuclear transmutation.webp b/ngon/img/nuclear transmutation.webp new file mode 100644 index 00000000..ba6b3f92 Binary files /dev/null and b/ngon/img/nuclear transmutation.webp differ diff --git a/ngon/img/null hypothesis.webp b/ngon/img/null hypothesis.webp new file mode 100644 index 00000000..f7ca944b Binary files /dev/null and b/ngon/img/null hypothesis.webp differ diff --git a/ngon/img/open-source.webp b/ngon/img/open-source.webp new file mode 100644 index 00000000..44b6a233 Binary files /dev/null and b/ngon/img/open-source.webp differ diff --git a/ngon/img/optical amplifier.webp b/ngon/img/optical amplifier.webp new file mode 100644 index 00000000..7fa80514 Binary files /dev/null and b/ngon/img/optical amplifier.webp differ diff --git a/ngon/img/options exchange.webp b/ngon/img/options exchange.webp new file mode 100644 index 00000000..a805ffce Binary files /dev/null and b/ngon/img/options exchange.webp differ diff --git a/ngon/img/orbital-bot upgrade.webp b/ngon/img/orbital-bot upgrade.webp new file mode 100644 index 00000000..46ff5385 Binary files /dev/null and b/ngon/img/orbital-bot upgrade.webp differ diff --git a/ngon/img/orbital-bot.webp b/ngon/img/orbital-bot.webp new file mode 100644 index 00000000..1ab8298f Binary files /dev/null and b/ngon/img/orbital-bot.webp differ diff --git a/ngon/img/ordnance.webp b/ngon/img/ordnance.webp new file mode 100644 index 00000000..f28c241b Binary files /dev/null and b/ngon/img/ordnance.webp differ diff --git a/ngon/img/orthocyclic winding.webp b/ngon/img/orthocyclic winding.webp new file mode 100644 index 00000000..770461b5 Binary files /dev/null and b/ngon/img/orthocyclic winding.webp differ diff --git a/ngon/img/output coupler.webp b/ngon/img/output coupler.webp new file mode 100644 index 00000000..61670f8a Binary files /dev/null and b/ngon/img/output coupler.webp differ diff --git a/ngon/img/overcharge.webp b/ngon/img/overcharge.webp new file mode 100644 index 00000000..7c20edb4 Binary files /dev/null and b/ngon/img/overcharge.webp differ diff --git a/ngon/img/pair production.webp b/ngon/img/pair production.webp new file mode 100644 index 00000000..6e6d6343 Binary files /dev/null and b/ngon/img/pair production.webp differ diff --git a/ngon/img/paradigm shift.webp b/ngon/img/paradigm shift.webp new file mode 100644 index 00000000..49553951 Binary files /dev/null and b/ngon/img/paradigm shift.webp differ diff --git a/ngon/img/parasitism.webp b/ngon/img/parasitism.webp new file mode 100644 index 00000000..3db9826d Binary files /dev/null and b/ngon/img/parasitism.webp differ diff --git a/ngon/img/parthenogenesis.webp b/ngon/img/parthenogenesis.webp new file mode 100644 index 00000000..e74f1c5c Binary files /dev/null and b/ngon/img/parthenogenesis.webp differ diff --git a/ngon/img/patch.webp b/ngon/img/patch.webp new file mode 100644 index 00000000..2e65da5c Binary files /dev/null and b/ngon/img/patch.webp differ diff --git a/ngon/img/path integral.webp b/ngon/img/path integral.webp new file mode 100644 index 00000000..a81389bd Binary files /dev/null and b/ngon/img/path integral.webp differ diff --git a/ngon/img/path integration.webp b/ngon/img/path integration.webp new file mode 100644 index 00000000..9427793c Binary files /dev/null and b/ngon/img/path integration.webp differ diff --git a/ngon/img/perimeter defense.webp b/ngon/img/perimeter defense.webp new file mode 100644 index 00000000..071ecc59 Binary files /dev/null and b/ngon/img/perimeter defense.webp differ diff --git a/ngon/img/perturbation theory.webp b/ngon/img/perturbation theory.webp new file mode 100644 index 00000000..ffff9b8e Binary files /dev/null and b/ngon/img/perturbation theory.webp differ diff --git a/ngon/img/phase velocity.webp b/ngon/img/phase velocity.webp new file mode 100644 index 00000000..35875c2d Binary files /dev/null and b/ngon/img/phase velocity.webp differ diff --git a/ngon/img/phonon.webp b/ngon/img/phonon.webp new file mode 100644 index 00000000..53f7afe1 Binary files /dev/null and b/ngon/img/phonon.webp differ diff --git a/ngon/img/piezoelectricity.webp b/ngon/img/piezoelectricity.webp new file mode 100644 index 00000000..455fd7c3 Binary files /dev/null and b/ngon/img/piezoelectricity.webp differ diff --git a/ngon/img/pigeonhole principle.webp b/ngon/img/pigeonhole principle.webp new file mode 100644 index 00000000..9475d19c Binary files /dev/null and b/ngon/img/pigeonhole principle.webp differ diff --git a/ngon/img/plasma ball.webp b/ngon/img/plasma ball.webp new file mode 100644 index 00000000..a2a692f0 Binary files /dev/null and b/ngon/img/plasma ball.webp differ diff --git a/ngon/img/plasma jet.webp b/ngon/img/plasma jet.webp new file mode 100644 index 00000000..e6142696 Binary files /dev/null and b/ngon/img/plasma jet.webp differ diff --git a/ngon/img/plasma-bot.webp b/ngon/img/plasma-bot.webp new file mode 100644 index 00000000..9d71d506 Binary files /dev/null and b/ngon/img/plasma-bot.webp differ diff --git a/ngon/img/pneumatic actuator.webp b/ngon/img/pneumatic actuator.webp new file mode 100644 index 00000000..9bb5a7f0 Binary files /dev/null and b/ngon/img/pneumatic actuator.webp differ diff --git a/ngon/img/polyurethane foam.webp b/ngon/img/polyurethane foam.webp new file mode 100644 index 00000000..d5745625 Binary files /dev/null and b/ngon/img/polyurethane foam.webp differ diff --git a/ngon/img/pressure vessel.webp b/ngon/img/pressure vessel.webp new file mode 100644 index 00000000..a4e27dee Binary files /dev/null and b/ngon/img/pressure vessel.webp differ diff --git a/ngon/img/propagation.webp b/ngon/img/propagation.webp new file mode 100644 index 00000000..c47af7fb Binary files /dev/null and b/ngon/img/propagation.webp differ diff --git a/ngon/img/propagator.webp b/ngon/img/propagator.webp new file mode 100644 index 00000000..733638bf Binary files /dev/null and b/ngon/img/propagator.webp differ diff --git a/ngon/img/pseudoscience.webp b/ngon/img/pseudoscience.webp new file mode 100644 index 00000000..fdee13b9 Binary files /dev/null and b/ngon/img/pseudoscience.webp differ diff --git a/ngon/img/pulse.webp b/ngon/img/pulse.webp new file mode 100644 index 00000000..6225cde0 Binary files /dev/null and b/ngon/img/pulse.webp differ diff --git a/ngon/img/pyrotechnics.webp b/ngon/img/pyrotechnics.webp new file mode 100644 index 00000000..8ab09211 Binary files /dev/null and b/ngon/img/pyrotechnics.webp differ diff --git a/ngon/img/quantum immortality.webp b/ngon/img/quantum immortality.webp new file mode 100644 index 00000000..028c9f7a Binary files /dev/null and b/ngon/img/quantum immortality.webp differ diff --git a/ngon/img/quasiparticles.webp b/ngon/img/quasiparticles.webp new file mode 100644 index 00000000..18d3bad1 Binary files /dev/null and b/ngon/img/quasiparticles.webp differ diff --git a/ngon/img/quenching.webp b/ngon/img/quenching.webp new file mode 100644 index 00000000..ecf7887e Binary files /dev/null and b/ngon/img/quenching.webp differ diff --git a/ngon/img/quintessence.webp b/ngon/img/quintessence.webp new file mode 100644 index 00000000..617bb16c Binary files /dev/null and b/ngon/img/quintessence.webp differ diff --git a/ngon/img/radiative equilibrium.webp b/ngon/img/radiative equilibrium.webp new file mode 100644 index 00000000..6fd3b3d2 Binary files /dev/null and b/ngon/img/radiative equilibrium.webp differ diff --git a/ngon/img/radioactive contamination.webp b/ngon/img/radioactive contamination.webp new file mode 100644 index 00000000..347dab41 Binary files /dev/null and b/ngon/img/radioactive contamination.webp differ diff --git a/ngon/img/railgun.webp b/ngon/img/railgun.webp new file mode 100644 index 00000000..d80883f6 Binary files /dev/null and b/ngon/img/railgun.webp differ diff --git a/ngon/img/reaction inhibitor.webp b/ngon/img/reaction inhibitor.webp new file mode 100644 index 00000000..de5a4404 Binary files /dev/null and b/ngon/img/reaction inhibitor.webp differ diff --git a/ngon/img/rebound.webp b/ngon/img/rebound.webp new file mode 100644 index 00000000..01e098c3 Binary files /dev/null and b/ngon/img/rebound.webp differ diff --git a/ngon/img/recycling.webp b/ngon/img/recycling.webp new file mode 100644 index 00000000..775edc5d Binary files /dev/null and b/ngon/img/recycling.webp differ diff --git a/ngon/img/reduced tolerances.webp b/ngon/img/reduced tolerances.webp new file mode 100644 index 00000000..35b4ee8d Binary files /dev/null and b/ngon/img/reduced tolerances.webp differ diff --git a/ngon/img/refraction.webp b/ngon/img/refraction.webp new file mode 100644 index 00000000..4901c68c Binary files /dev/null and b/ngon/img/refraction.webp differ diff --git a/ngon/img/refractory metal.webp b/ngon/img/refractory metal.webp new file mode 100644 index 00000000..c44f3e24 Binary files /dev/null and b/ngon/img/refractory metal.webp differ diff --git a/ngon/img/refrigerant.webp b/ngon/img/refrigerant.webp new file mode 100644 index 00000000..d8f27c32 Binary files /dev/null and b/ngon/img/refrigerant.webp differ diff --git a/ngon/img/regression.webp b/ngon/img/regression.webp new file mode 100644 index 00000000..da119ef6 Binary files /dev/null and b/ngon/img/regression.webp differ diff --git a/ngon/img/reinforcement learning.webp b/ngon/img/reinforcement learning.webp new file mode 100644 index 00000000..77118538 Binary files /dev/null and b/ngon/img/reinforcement learning.webp differ diff --git a/ngon/img/relativistic momentum.webp b/ngon/img/relativistic momentum.webp new file mode 100644 index 00000000..55422658 Binary files /dev/null and b/ngon/img/relativistic momentum.webp differ diff --git a/ngon/img/relay switch.webp b/ngon/img/relay switch.webp new file mode 100644 index 00000000..d6707f91 Binary files /dev/null and b/ngon/img/relay switch.webp differ diff --git a/ngon/img/renormalization.webp b/ngon/img/renormalization.webp new file mode 100644 index 00000000..6b0391bf Binary files /dev/null and b/ngon/img/renormalization.webp differ diff --git a/ngon/img/repeater.webp b/ngon/img/repeater.webp new file mode 100644 index 00000000..1be7058f Binary files /dev/null and b/ngon/img/repeater.webp differ diff --git a/ngon/img/replication.webp b/ngon/img/replication.webp new file mode 100644 index 00000000..8dfb21cf Binary files /dev/null and b/ngon/img/replication.webp differ diff --git a/ngon/img/research.webp b/ngon/img/research.webp new file mode 100644 index 00000000..99ac3962 Binary files /dev/null and b/ngon/img/research.webp differ diff --git a/ngon/img/residual dipolar coupling.webp b/ngon/img/residual dipolar coupling.webp new file mode 100644 index 00000000..5fcb9722 Binary files /dev/null and b/ngon/img/residual dipolar coupling.webp differ diff --git a/ngon/img/restitution.webp b/ngon/img/restitution.webp new file mode 100644 index 00000000..3ec9f61d Binary files /dev/null and b/ngon/img/restitution.webp differ diff --git a/ngon/img/retrocausality.webp b/ngon/img/retrocausality.webp new file mode 100644 index 00000000..17997c76 Binary files /dev/null and b/ngon/img/retrocausality.webp differ diff --git a/ngon/img/ricochet.webp b/ngon/img/ricochet.webp new file mode 100644 index 00000000..5863d225 Binary files /dev/null and b/ngon/img/ricochet.webp differ diff --git a/ngon/img/rivet gun.webp b/ngon/img/rivet gun.webp new file mode 100644 index 00000000..a94f4f68 Binary files /dev/null and b/ngon/img/rivet gun.webp differ diff --git a/ngon/img/robotics.webp b/ngon/img/robotics.webp new file mode 100644 index 00000000..feba476d Binary files /dev/null and b/ngon/img/robotics.webp differ diff --git a/ngon/img/rocket-propelled grenade.webp b/ngon/img/rocket-propelled grenade.webp new file mode 100644 index 00000000..efe978b0 Binary files /dev/null and b/ngon/img/rocket-propelled grenade.webp differ diff --git a/ngon/img/rotary cannon.webp b/ngon/img/rotary cannon.webp new file mode 100644 index 00000000..69df5fca Binary files /dev/null and b/ngon/img/rotary cannon.webp differ diff --git a/ngon/img/scrap bots.webp b/ngon/img/scrap bots.webp new file mode 100644 index 00000000..9482b30f Binary files /dev/null and b/ngon/img/scrap bots.webp differ diff --git a/ngon/img/scrap refit.webp b/ngon/img/scrap refit.webp new file mode 100644 index 00000000..243fcc7a Binary files /dev/null and b/ngon/img/scrap refit.webp differ diff --git a/ngon/img/self-assembly.webp b/ngon/img/self-assembly.webp new file mode 100644 index 00000000..3cb0fe2d Binary files /dev/null and b/ngon/img/self-assembly.webp differ diff --git a/ngon/img/sentry.webp b/ngon/img/sentry.webp new file mode 100644 index 00000000..a9cfb29c Binary files /dev/null and b/ngon/img/sentry.webp differ diff --git a/ngon/img/shape-memory alloy.webp b/ngon/img/shape-memory alloy.webp new file mode 100644 index 00000000..fc76420e Binary files /dev/null and b/ngon/img/shape-memory alloy.webp differ diff --git a/ngon/img/shaped charge.webp b/ngon/img/shaped charge.webp new file mode 100644 index 00000000..70b7a7c8 Binary files /dev/null and b/ngon/img/shaped charge.webp differ diff --git a/ngon/img/shear stress.webp b/ngon/img/shear stress.webp new file mode 100644 index 00000000..211b5a94 Binary files /dev/null and b/ngon/img/shear stress.webp differ diff --git a/ngon/img/shock wave.webp b/ngon/img/shock wave.webp new file mode 100644 index 00000000..ff4d9459 Binary files /dev/null and b/ngon/img/shock wave.webp differ diff --git a/ngon/img/simulated annealing.webp b/ngon/img/simulated annealing.webp new file mode 100644 index 00000000..bfc1ef43 Binary files /dev/null and b/ngon/img/simulated annealing.webp differ diff --git a/ngon/img/siphonaptera.webp b/ngon/img/siphonaptera.webp new file mode 100644 index 00000000..1b55f51c Binary files /dev/null and b/ngon/img/siphonaptera.webp differ diff --git a/ngon/img/slow light.webp b/ngon/img/slow light.webp new file mode 100644 index 00000000..8f98107e Binary files /dev/null and b/ngon/img/slow light.webp differ diff --git a/ngon/img/smelting.webp b/ngon/img/smelting.webp new file mode 100644 index 00000000..783d4df7 Binary files /dev/null and b/ngon/img/smelting.webp differ diff --git a/ngon/img/sound-bot upgrade.webp b/ngon/img/sound-bot upgrade.webp new file mode 100644 index 00000000..119cbfa1 Binary files /dev/null and b/ngon/img/sound-bot upgrade.webp differ diff --git a/ngon/img/sound-bot.webp b/ngon/img/sound-bot.webp new file mode 100644 index 00000000..85b39b8a Binary files /dev/null and b/ngon/img/sound-bot.webp differ diff --git a/ngon/img/specular reflection.webp b/ngon/img/specular reflection.webp new file mode 100644 index 00000000..3218665e Binary files /dev/null and b/ngon/img/specular reflection.webp differ diff --git a/ngon/img/spherical harmonics.webp b/ngon/img/spherical harmonics.webp new file mode 100644 index 00000000..1aedfd1d Binary files /dev/null and b/ngon/img/spherical harmonics.webp differ diff --git a/ngon/img/spin-statistics.webp b/ngon/img/spin-statistics.webp new file mode 100644 index 00000000..2f2b4700 Binary files /dev/null and b/ngon/img/spin-statistics.webp differ diff --git a/ngon/img/spin–statistics theorem.webp b/ngon/img/spin–statistics theorem.webp new file mode 100644 index 00000000..297a2db1 Binary files /dev/null and b/ngon/img/spin–statistics theorem.webp differ diff --git a/ngon/img/stimulated emission.webp b/ngon/img/stimulated emission.webp new file mode 100644 index 00000000..ce9a1ccf Binary files /dev/null and b/ngon/img/stimulated emission.webp differ diff --git a/ngon/img/strange attractor.webp b/ngon/img/strange attractor.webp new file mode 100644 index 00000000..983778f7 Binary files /dev/null and b/ngon/img/strange attractor.webp differ diff --git a/ngon/img/stress concentration.webp b/ngon/img/stress concentration.webp new file mode 100644 index 00000000..92b1806c Binary files /dev/null and b/ngon/img/stress concentration.webp differ diff --git a/ngon/img/strong anthropic principle.webp b/ngon/img/strong anthropic principle.webp new file mode 100644 index 00000000..ffbfd315 Binary files /dev/null and b/ngon/img/strong anthropic principle.webp differ diff --git a/ngon/img/super ball.webp b/ngon/img/super ball.webp new file mode 100644 index 00000000..9c7f945a Binary files /dev/null and b/ngon/img/super ball.webp differ diff --git a/ngon/img/super duper.webp b/ngon/img/super duper.webp new file mode 100644 index 00000000..d5e1f2a6 Binary files /dev/null and b/ngon/img/super duper.webp differ diff --git a/ngon/img/supercritical fission.webp b/ngon/img/supercritical fission.webp new file mode 100644 index 00000000..4e9c3c07 Binary files /dev/null and b/ngon/img/supercritical fission.webp differ diff --git a/ngon/img/superdeterminism.webp b/ngon/img/superdeterminism.webp new file mode 100644 index 00000000..d07b9b01 Binary files /dev/null and b/ngon/img/superdeterminism.webp differ diff --git a/ngon/img/superfluidity.webp b/ngon/img/superfluidity.webp new file mode 100644 index 00000000..7f0bc3dd Binary files /dev/null and b/ngon/img/superfluidity.webp differ diff --git a/ngon/img/supply chain.webp b/ngon/img/supply chain.webp new file mode 100644 index 00000000..29814efc Binary files /dev/null and b/ngon/img/supply chain.webp differ diff --git a/ngon/img/surface plasmons.webp b/ngon/img/surface plasmons.webp new file mode 100644 index 00000000..a78ba3a8 Binary files /dev/null and b/ngon/img/surface plasmons.webp differ diff --git a/ngon/img/surface tension.webp b/ngon/img/surface tension.webp new file mode 100644 index 00000000..469fe59f Binary files /dev/null and b/ngon/img/surface tension.webp differ diff --git a/ngon/img/surfactant.webp b/ngon/img/surfactant.webp new file mode 100644 index 00000000..291fd6c1 Binary files /dev/null and b/ngon/img/surfactant.webp differ diff --git a/ngon/img/symbiosis.webp b/ngon/img/symbiosis.webp new file mode 100644 index 00000000..61c253e7 Binary files /dev/null and b/ngon/img/symbiosis.webp differ diff --git a/ngon/img/sympathetic resonance.webp b/ngon/img/sympathetic resonance.webp new file mode 100644 index 00000000..5f0a77dd Binary files /dev/null and b/ngon/img/sympathetic resonance.webp differ diff --git a/ngon/img/technical debt.webp b/ngon/img/technical debt.webp new file mode 100644 index 00000000..7f5facb0 Binary files /dev/null and b/ngon/img/technical debt.webp differ diff --git a/ngon/img/ternary.webp b/ngon/img/ternary.webp new file mode 100644 index 00000000..e39b90c5 Binary files /dev/null and b/ngon/img/ternary.webp differ diff --git a/ngon/img/tessellation.webp b/ngon/img/tessellation.webp new file mode 100644 index 00000000..4992624f Binary files /dev/null and b/ngon/img/tessellation.webp differ diff --git a/ngon/img/thermal runaway.webp b/ngon/img/thermal runaway.webp new file mode 100644 index 00000000..a7c59932 Binary files /dev/null and b/ngon/img/thermal runaway.webp differ diff --git a/ngon/img/thermocouple.webp b/ngon/img/thermocouple.webp new file mode 100644 index 00000000..d669ea57 Binary files /dev/null and b/ngon/img/thermocouple.webp differ diff --git a/ngon/img/thermoelectric effect.webp b/ngon/img/thermoelectric effect.webp new file mode 100644 index 00000000..bc91ec21 Binary files /dev/null and b/ngon/img/thermoelectric effect.webp differ diff --git a/ngon/img/time crystals.webp b/ngon/img/time crystals.webp new file mode 100644 index 00000000..5b06557f Binary files /dev/null and b/ngon/img/time crystals.webp differ diff --git a/ngon/img/tokamak.webp b/ngon/img/tokamak.webp new file mode 100644 index 00000000..4a37b667 Binary files /dev/null and b/ngon/img/tokamak.webp differ diff --git a/ngon/img/torpor.webp b/ngon/img/torpor.webp new file mode 100644 index 00000000..cc1165fc Binary files /dev/null and b/ngon/img/torpor.webp differ diff --git a/ngon/img/transdimensional worms.webp b/ngon/img/transdimensional worms.webp new file mode 100644 index 00000000..e01c7807 Binary files /dev/null and b/ngon/img/transdimensional worms.webp differ diff --git a/ngon/img/transistor.webp b/ngon/img/transistor.webp new file mode 100644 index 00000000..f62e0c34 Binary files /dev/null and b/ngon/img/transistor.webp differ diff --git a/ngon/img/triple point.webp b/ngon/img/triple point.webp new file mode 100644 index 00000000..260cba62 Binary files /dev/null and b/ngon/img/triple point.webp differ diff --git a/ngon/img/tungsten carbide.webp b/ngon/img/tungsten carbide.webp new file mode 100644 index 00000000..3fc90ed4 Binary files /dev/null and b/ngon/img/tungsten carbide.webp differ diff --git a/ngon/img/uncertainty principle.webp b/ngon/img/uncertainty principle.webp new file mode 100644 index 00000000..892fcde0 Binary files /dev/null and b/ngon/img/uncertainty principle.webp differ diff --git a/ngon/img/unified field theory.webp b/ngon/img/unified field theory.webp new file mode 100644 index 00000000..43230ce3 Binary files /dev/null and b/ngon/img/unified field theory.webp differ diff --git a/ngon/img/vacuum bomb.webp b/ngon/img/vacuum bomb.webp new file mode 100644 index 00000000..88011ca1 Binary files /dev/null and b/ngon/img/vacuum bomb.webp differ diff --git a/ngon/img/vacuum fluctuation.webp b/ngon/img/vacuum fluctuation.webp new file mode 100644 index 00000000..c7126526 Binary files /dev/null and b/ngon/img/vacuum fluctuation.webp differ diff --git a/ngon/img/vacuum permittivity.webp b/ngon/img/vacuum permittivity.webp new file mode 100644 index 00000000..f81f9009 Binary files /dev/null and b/ngon/img/vacuum permittivity.webp differ diff --git a/ngon/img/virtual particles.webp b/ngon/img/virtual particles.webp new file mode 100644 index 00000000..a02e99c6 Binary files /dev/null and b/ngon/img/virtual particles.webp differ diff --git a/ngon/img/von Neumann probe.webp b/ngon/img/von Neumann probe.webp new file mode 100644 index 00000000..7addbb36 Binary files /dev/null and b/ngon/img/von Neumann probe.webp differ diff --git a/ngon/img/waste heat recovery.webp b/ngon/img/waste heat recovery.webp new file mode 100644 index 00000000..e291aba9 Binary files /dev/null and b/ngon/img/waste heat recovery.webp differ diff --git a/ngon/img/water shielding.webp b/ngon/img/water shielding.webp new file mode 100644 index 00000000..3dc396eb Binary files /dev/null and b/ngon/img/water shielding.webp differ diff --git a/ngon/img/weak anthropic principle.webp b/ngon/img/weak anthropic principle.webp new file mode 100644 index 00000000..c0ed499c Binary files /dev/null and b/ngon/img/weak anthropic principle.webp differ diff --git a/ngon/img/zero point energy.webp b/ngon/img/zero point energy.webp new file mode 100644 index 00000000..21c7d162 Binary files /dev/null and b/ngon/img/zero point energy.webp differ diff --git a/ngon/img/zoospore vector.webp b/ngon/img/zoospore vector.webp new file mode 100644 index 00000000..305d241d Binary files /dev/null and b/ngon/img/zoospore vector.webp differ diff --git a/ngon/img/Ψ(t) collapse.webp b/ngon/img/Ψ(t) collapse.webp new file mode 100644 index 00000000..b703bebc Binary files /dev/null and b/ngon/img/Ψ(t) collapse.webp differ diff --git a/ngon/index.html b/ngon/index.html new file mode 100644 index 00000000..30302b7a --- /dev/null +++ b/ngon/index.html @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + n-gon + + + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ + + experiment + + + + training + +
+
+
+ settings +
+ + +
+ + +
+ + +
+ + +
+
+ + + + + +
+ + + +
+ + +
+ + +
+
+
+
+
+
+ controls +
+ To change controls click a box +
and press an unused key. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FIREFMouseLeft
FIELDSPACEMouseRight
JUMPWArrowUp
CROUCHSArrowDown
LEFTAArrowLeft
RIGHTDArrowRight
GUN →QMouseWheel
GUN ←EMouseWheel
GUN #Num
PAUSEP
TESTINGT
+ +
to default keys +
+
+
+
+
+ updates +
+
+
+
+
+ about +
+ n-gon is a solo project written in JavaScript, CSS, and HTML using the matter.js 2-D physics library. It's free and open source on Github. + + + + + Github + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Q + W + E + A + S + D + + + + + + + + + + + switch + guns + move + fire + field + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ngon/js/bullet.js b/ngon/js/bullet.js new file mode 100644 index 00000000..a2b2300d --- /dev/null +++ b/ngon/js/bullet.js @@ -0,0 +1,8569 @@ +let bullet = []; + +const b = { + // dmgScale: null, //scales all damage, but not raw .dmg //set in levels.setDifficulty + gravity: 0.0006, //most other bodies have gravity = 0.001 + activeGun: null, //current gun in use by player + inventoryGun: 0, + inventory: [], //list of what guns player has // 0 starts with basic gun + setFireMethod() { + if (tech.isFireMoveLock) { + b.fire = b.fireFloat + // } else if (tech.isFireNotMove) { + // if (tech.isAlwaysFire) { + // b.fire = b.fireAlwaysFire + // } else { + // b.fire = b.fireNotMove + // } + } else if (tech.isAlwaysFire) { + b.fire = b.fireAlwaysFire + } else { + b.fire = b.fireNormal + } + }, + fire() { }, + fireNormal() { + if (b.inventory.length && b.activeGun !== null) { + if (input.fire && m.fireCDcycle < m.cycle && (!input.field || m.fieldFire)) { + if (b.guns[b.activeGun].ammo > 0) { + b.fireWithAmmo() + } else { + b.outOfAmmo() + } + if (m.holdingTarget) m.drop(); + } + b.guns[b.activeGun].do(); + } + }, + fireNotMove() { //added && player.speed < 0.5 && m.onGround + if (b.inventory.length && b.activeGun !== null) { + if (input.fire && m.fireCDcycle < m.cycle && (!input.field || m.fieldFire) && player.speed < 2.5 && m.onGround && Math.abs(m.yOff - m.yOffGoal) < 1) { + if (b.guns[b.activeGun].ammo > 0) { + b.fireWithAmmo() + } else { + b.outOfAmmo() + } + if (m.holdingTarget) m.drop(); + } + b.guns[b.activeGun].do(); + } + }, + fireAlwaysFire() { //added && player.speed < 0.5 && m.onGround //removed input.fire && (!input.field || m.fieldFire) + if (b.inventory.length && b.activeGun !== null) { + if (m.fireCDcycle < m.cycle && player.speed < 0.5 && m.onGround && Math.abs(m.yOff - m.yOffGoal) < 1) { + if (b.guns[b.activeGun].ammo > 0) { + b.fireWithAmmo() + } + if (m.holdingTarget) m.drop(); + } + b.guns[b.activeGun].do(); + } + }, + fireFloat() { //added && player.speed < 0.5 && m.onGround + if (b.inventory.length && b.activeGun !== null) { + if (input.fire && (!input.field || m.fieldFire)) { + if (m.fireCDcycle < m.cycle) { + if (b.guns[b.activeGun].ammo > 0) { + b.fireWithAmmo() + } else { + b.outOfAmmo() + } + if (m.holdingTarget) m.drop(); + } + Matter.Body.setVelocity(player, { + x: 0, + y: -55 * player.mass * simulation.g //undo gravity before it is added + }); + player.force.x = 0 + player.force.y = 0 + } + b.guns[b.activeGun].do(); + } + }, + fireWithAmmo() { //triggers after firing when you have ammo + b.guns[b.activeGun].fire(); + if (tech.crouchAmmoCount && m.crouch) { + if (tech.crouchAmmoCount % 2) { + b.guns[b.activeGun].ammo--; + simulation.updateGunHUD(); + } + tech.crouchAmmoCount++ //makes the no ammo toggle off and on + } else { + b.guns[b.activeGun].ammo--; + simulation.updateGunHUD(); + } + }, + outOfAmmo() { //triggers after firing when you have NO ammo + simulation.makeTextLog(`${b.guns[b.activeGun].name}.ammo: 0`); + m.fireCDcycle = m.cycle + 30; //fire cooldown + if (tech.isAmmoFromHealth) { + const amount = 0.02 + if (tech.isEnergyHealth) { + if (m.maxEnergy > amount * 2) { + tech.healMaxEnergyBonus -= amount * 2 + m.setMaxEnergy(); + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 50 * (Math.random() - 0.5), m.pos.y + 50 * (Math.random() - 0.5), "ammo"); + } + } else { + if (m.health > amount) { + tech.extraMaxHealth -= amount //decrease max health + m.setMaxHealth(); + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 50 * (Math.random() - 0.5), m.pos.y + 50 * (Math.random() - 0.5), "ammo"); + } + } + } + }, + refundAmmo() { //triggers after firing when you removed ammo for a gun, but didn't need to + if (tech.crouchAmmoCount && m.crouch && b.activeGun !== null) { + tech.crouchAmmoCount-- + if ((tech.crouchAmmoCount) % 2) { + b.guns[b.activeGun].ammo++; + simulation.updateGunHUD(); + } + } else { + b.guns[b.activeGun].ammo++; + simulation.updateGunHUD(); + } + }, + // returnGunAmmo(name) { + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === name) return b.guns[i].ammo + // } + // }, + giveGuns(gun = "random", ammoPacks = 10) { + if (tech.ammoCap) ammoPacks = 0.45 * tech.ammoCap + if (tech.isOneGun) b.removeAllGuns(); + if (gun === "random") { + //find what guns player doesn't have + options = [] + for (let i = 0, len = b.guns.length; i < len; i++) { + if (!b.guns[i].have) options.push(i) + } + if (options.length === 0) return + //randomly pick from list of possible guns + gun = options[Math.floor(Math.random() * options.length)] + } + if (gun === "all") { + for (let i = 0; i < b.guns.length; i++) { + b.inventory[i] = i; + b.guns[i].have = true; + if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = Math.ceil(b.guns[i].ammoPack * ammoPacks); + } + b.inventoryGun = 0; + b.activeGun = b.inventory[0]; + } else { + if (isNaN(gun)) { //find gun by name + let found = false; + for (let i = 0; i < b.guns.length; i++) { + if (gun === b.guns[i].name) { + gun = i + found = true; + break + } + } + if (!found) return //if no gun found don't give a gun + } + if (!b.guns[gun].have) b.inventory.push(gun); + b.guns[gun].have = true; + if (b.guns[gun].ammo !== Infinity) b.guns[gun].ammo = Math.ceil(b.guns[gun].ammoPack * ammoPacks); + if (b.activeGun === null) { + b.inventoryGun = 0; + b.activeGun = b.inventory[0] //if no active gun switch to new gun + if (b.guns[b.activeGun].charge) b.guns[b.activeGun].charge = 0; //set foam charge to zero if foam is a new gun + } + } + simulation.makeGunHUD(); + simulation.switchGun(); + b.setFireCD(); + if (tech.isOneGun && b.inventory > 0) { + //count how many gun tech you have and remove them + let gunTechCount = 0 //2 bonus gun tech + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isGunTech && tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable && !tech.tech[i].isRemoveGun) { + const remove = tech.removeTech(i) + gunTechCount += remove + } + } + + //get a random gun tech for your gun + for (let i = 0; i < gunTechCount; i++) { + const gunTechPool = [] + for (let j = 0, len = tech.tech.length; j < len; j++) { + if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[i].isRemoveGun && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + const regex = tech.tech[j].requires.search(b.guns[b.activeGun].name) //get string index of gun name + const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) + } + } + if (gunTechPool.length) { + const index = Math.floor(Math.random() * gunTechPool.length) + tech.giveTech(gunTechPool[index]) // choose from the gun pool + simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) + } else { + tech.giveTech() //get normal tech if you can't find any gun tech + } + } + + } + }, + removeGun(gunName) { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].name === gunName && b.guns[i].have) { + b.guns[i].have = false + for (let j = 0; j < b.inventory.length; j++) { + if (b.inventory[j] === i) { + b.inventory.splice(j, 1) + break + } + } + if (b.inventory.length > 0) { + b.activeGun = b.inventory[0]; + } else { + b.activeGun = null; + } + b.inventoryGun = 0; + simulation.makeGunHUD(); + break + } + } + b.setFireCD(); + }, + removeAllGuns() { + b.inventory = []; //removes guns and ammo + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].count = 0; + b.guns[i].have = false; + if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; + } + tech.buffedGun = 0 + b.activeGun = null; + b.inventoryGun = 0; + simulation.drawCursor = simulation.drawCursorBasic //set cross hairs + }, + bulletRemove() { //run in main loop + //remove bullet if at end cycle for that bullet + let i = bullet.length; + while (i--) { + if (bullet[i].endCycle < simulation.cycle) { + bullet[i].onEnd(i); //some bullets do stuff on end + if (bullet[i]) { + Matter.Composite.remove(engine.world, bullet[i]); + bullet.splice(i, 1); + } else { + break; //if bullet[i] doesn't exist don't complete the for loop, because the game probably reset + } + } + } + }, + bulletDraw() { + ctx.beginPath(); + for (let i = 0, len = bullet.length; i < len; i++) { + let vertices = bullet[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j += 1) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } + ctx.fillStyle = color.bullet; + ctx.fill(); + }, + bulletDo() { + for (let i = 0, len = bullet.length; i < len; i++) { + bullet[i].do(); + } + }, + fireProps(cd, speed, dir, me) { + m.fireCDcycle = m.cycle + Math.floor(cd * b.fireCDscale); // cool down + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(dir), + y: m.Vy / 2 + speed * Math.sin(dir) + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + fireCDscale: 1, + setFireCD() { + b.fireCDscale = tech.fireRate * tech.slowFire * tech.researchHaste * tech.aimDamage + if (m.fieldMode === 6) b.fireCDscale *= 0.8 + if (tech.isFastTime) b.fireCDscale *= 0.5 + if (tech.isFireRateForGuns) b.fireCDscale *= Math.pow(0.82, Math.max(0, b.inventory.length - 1)) + if (tech.isFireMoveLock) b.fireCDscale *= 0.55 + }, + fireAttributes(dir, rotate = true) { + if (rotate) { + return { + // density: 0.0015, //frictionAir: 0.01, //restitution: 0, + angle: dir, + friction: 0.5, + frictionAir: 0, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + minDmgSpeed: 10, + beforeDmg() { }, //this.endCycle = 0 //triggers despawn + onEnd() { } + }; + } else { + return { + // density: 0.0015, //frictionAir: 0.01, //restitution: 0, + inertia: Infinity, //prevents rotation + angle: dir, + friction: 0.5, + frictionAir: 0, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + minDmgSpeed: 10, + beforeDmg() { }, //this.endCycle = 0 //triggers despawn + onEnd() { } + }; + } + }, + muzzleFlash(radius = 30) { + // ctx.fillStyle = "#fb0"; + // ctx.beginPath(); + // ctx.arc(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), radius, 0, 2 * Math.PI); + // ctx.fill(); + + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle), + radius: radius, + color: "#fb0", + time: 1 + }); + }, + removeConsBB(me) { + for (let i = 0, len = consBB.length; i < len; ++i) { + if (consBB[i].bodyA === me) { + consBB[i].bodyA = consBB[i].bodyB; + consBB.splice(i, 1); + break; + } else if (consBB[i].bodyB === me) { + consBB[i].bodyB = consBB[i].bodyA; + consBB.splice(i, 1); + break; + } + } + }, + onCollision(event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + //map + bullet collisions + if (pairs[i].bodyA.collisionFilter.category === cat.map && pairs[i].bodyB.collisionFilter.category === cat.bullet) { + collideBulletStatic(pairs[i].bodyB) + } else if (pairs[i].bodyB.collisionFilter.category === cat.map && pairs[i].bodyA.collisionFilter.category === cat.bullet) { + collideBulletStatic(pairs[i].bodyA) + } + + function collideBulletStatic(obj) { + if (obj.onWallHit) obj.onWallHit(); + } + } + }, + explosionRange() { + return tech.explosiveRadius * (tech.isExplosionHarm ? 1.7 : 1) * (tech.isSmallExplosion ? 0.66 : 1) * (tech.isExplodeRadio ? 1.25 : 1) + }, + explosion(where, radius, color = "rgba(255,25,0,0.6)") { // typically explode is used for some bullets with .onEnd + radius *= tech.explosiveRadius + + let dist, sub, knock; + let dmg = radius * 0.019 + if (tech.isExplosionHarm) radius *= 1.7 // 1/sqrt(2) radius -> area + if (tech.isSmallExplosion) { + // color = "rgba(255,0,30,0.7)" + radius *= 0.66 + dmg *= 1.66 + } + + if (tech.isExplodeRadio) { //radiation explosion + radius *= 1.25; //alert range + if (tech.isSmartRadius) radius = Math.max(Math.min(radius, Vector.magnitude(Vector.sub(where, player.position)) - 25), 1) + color = "rgba(25,139,170,0.25)" + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: radius, + color: color, + time: simulation.drawTime * 2 + }); + + //player damage + if (Vector.magnitude(Vector.sub(where, player.position)) < radius) { + const DRAIN = (tech.isExplosionHarm ? 0.6 : 0.45) * (tech.isRadioactiveResistance ? 0.25 : 1) + if (m.immuneCycle < m.cycle) m.energy -= DRAIN + if (m.energy < 0) { + m.energy = 0 + if (simulation.dmgScale) m.damage(tech.radioactiveDamage * 0.03 * (tech.isRadioactiveResistance ? 0.25 : 1)); + } + } + + //mob damage and knock back with alert + let damageScale = 1.5; // reduce dmg for each new target to limit total AOE damage + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded) { + sub = Vector.sub(where, mob[i].position); + dist = Vector.magnitude(sub) - mob[i].radius; + if (dist < radius) { + if (mob[i].shield) dmg *= 2.5 //balancing explosion dmg to shields + if (Matter.Query.ray(map, mob[i].position, where).length > 0) dmg *= 0.5 //reduce damage if a wall is in the way + mobs.statusDoT(mob[i], dmg * damageScale * 0.25, 240) //apply radiation damage status effect on direct hits + if (tech.isStun) mobs.statusStun(mob[i], 30) + mob[i].locatePlayer(); + damageScale *= 0.87 //reduced damage for each additional explosion target + } + } + } + } else { //normal explosions + if (tech.isSmartRadius) radius = Math.max(Math.min(radius, Vector.magnitude(Vector.sub(where, player.position)) - 25), 1) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: radius, + color: color, + time: simulation.drawTime + }); + const alertRange = 100 + radius * 2; //alert range + simulation.drawList.push({ //add alert to draw queue + x: where.x, + y: where.y, + radius: alertRange, + color: "rgba(100,20,0,0.03)", + time: simulation.drawTime + }); + + //player damage and knock back + if (m.immuneCycle < m.cycle) { + sub = Vector.sub(where, player.position); + dist = Vector.magnitude(sub); + + if (dist < radius) { + if (simulation.dmgScale) { + const harm = tech.isExplosionHarm ? 0.067 : 0.05 + if (tech.isImmuneExplosion && m.energy > 0.25) { + // const mitigate = Math.min(1, Math.max(1 - m.energy * 0.5, 0)) + m.energy -= 0.25 + // m.damage(0.01 * harm); //remove 99% of the damage 1-0.99 + knock = Vector.mult(Vector.normalise(sub), -0.6 * player.mass * Math.max(0, Math.min(0.15 - 0.002 * player.speed, 0.15))); + player.force.x = knock.x; // not += so crazy forces can't build up with MIRV + player.force.y = knock.y - 0.3; //some extra vertical kick + } else { + if (simulation.dmgScale) m.damage(harm); + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * player.mass * 0.013); + player.force.x += knock.x; + player.force.y += knock.y; + } + } + } else if (dist < alertRange) { + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * player.mass * 0.005); + player.force.x += knock.x; + player.force.y += knock.y; + } + } + + //body knock backs + for (let i = body.length - 1; i > -1; i--) { + if (!body[i].isNotHoldable) { + sub = Vector.sub(where, body[i].position); + dist = Vector.magnitude(sub); + if (dist < radius) { + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * body[i].mass * 0.022); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + if (tech.isBlockExplode) { + if (body[i] === m.holdingTarget) m.drop() + const size = 20 + 300 * Math.pow(body[i].mass, 0.25) + const where = body[i].position + const onLevel = level.onLevel //prevent explosions in the next level + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + setTimeout(() => { + if (onLevel === level.onLevel) b.explosion(where, size); //makes bullet do explosive damage at end + }, 250 + 300 * Math.random()); + } + } else if (dist < alertRange) { + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * body[i].mass * 0.011); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + } + } + } + + //power up knock backs + for (let i = 0, len = powerUp.length; i < len; ++i) { + sub = Vector.sub(where, powerUp[i].position); + dist = Vector.magnitude(sub); + if (dist < radius) { + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * powerUp[i].mass * 0.013); + powerUp[i].force.x += knock.x; + powerUp[i].force.y += knock.y; + } else if (dist < alertRange) { + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg) * powerUp[i].mass * 0.007); + powerUp[i].force.x += knock.x; + powerUp[i].force.y += knock.y; + } + } + + //mob damage and knock back with alert + let damageScale = 1.5; // reduce dmg for each new target to limit total AOE damage + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded) { + sub = Vector.sub(where, mob[i].position); + dist = Vector.magnitude(sub) - mob[i].radius; + if (dist < radius) { + if (mob[i].shield) dmg *= 2.5 //balancing explosion dmg to shields + if (Matter.Query.ray(map, mob[i].position, where).length > 0) dmg *= 0.5 //reduce damage if a wall is in the way + mob[i].damage(dmg * damageScale * m.dmgScale); + mob[i].locatePlayer(); + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0.003 : 0.01)); + if (tech.isStun) { + mobs.statusStun(mob[i], 30) + } else if (!mob[i].isInvulnerable) { + mob[i].force.x += knock.x; + mob[i].force.y += knock.y; + } + radius *= 0.95 //reduced range for each additional explosion target + damageScale *= 0.87 //reduced damage for each additional explosion target + } else if (!mob[i].seePlayer.recall && dist < alertRange) { + mob[i].locatePlayer(); + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0 : 0.006)); + if (tech.isStun) { + mobs.statusStun(mob[i], 30) + } else if (!mob[i].isInvulnerable) { + mob[i].force.x += knock.x; + mob[i].force.y += knock.y; + } + } + } + } + } + }, + pulse(charge, angle = m.angle, where = m.pos) { + let best; + let explosionRadius = 5.5 * charge + let range = 5000 + const path = [{ + x: where.x + 20 * Math.cos(angle), + y: where.y + 20 * Math.sin(angle) + }, + { + x: where.x + range * Math.cos(angle), + y: where.y + range * Math.sin(angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + // if (tech.isPulseAim && !m.crouch) { //find mobs in line of sight + // let dist = 2200 + // for (let i = 0, len = mob.length; i < len; i++) { + // const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) + // if ( + // explosionRadius < newDist && + // newDist < dist && + // !mob[i].isBadTarget && + // Matter.Query.ray(map, path[0], mob[i].position).length === 0 && + // Matter.Query.ray(body, path[0], mob[i].position).length === 0 && + // !mob[i].isInvulnerable + // ) { + // dist = newDist + // best.who = mob[i] + // path[path.length - 1] = mob[i].position + // } + // } + // } + if (!best.who) { + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + } + if (best.who) { + b.explosion(path[1], explosionRadius) + const off = explosionRadius * 1.2 + b.explosion({ + x: path[1].x + off * (Math.random() - 0.5), + y: path[1].y + off * (Math.random() - 0.5) + }, explosionRadius) + b.explosion({ + x: path[1].x + off * (Math.random() - 0.5), + y: path[1].y + off * (Math.random() - 0.5) + }, explosionRadius) + } + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + if (charge > 50) { + ctx.strokeStyle = "rgba(255,0,0,0.10)" + ctx.lineWidth = 70 + ctx.stroke(); + } + ctx.strokeStyle = "rgba(255,0,0,0.25)" + ctx.lineWidth = 20 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); + + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) { + const dist = Math.random() + simulation.drawList.push({ + x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5), + radius: 1.5 + 5 * Math.random(), + color: "rgba(255,0,0,0.5)", + time: Math.floor(9 + 25 * Math.random() * Math.random()) + }); + } + }, + // photon(where, angle = m.angle) { + // let best; + // const path = [{ + // x: m.pos.x + 20 * Math.cos(angle), + // y: m.pos.y + 20 * Math.sin(angle) + // }, + // { + // x: m.pos.x + range * Math.cos(angle), + // y: m.pos.y + range * Math.sin(angle) + // } + // ]; + // const vertexCollision = function(v1, v1End, domain) { + // for (let i = 0; i < domain.length; ++i) { + // let vertices = domain[i].vertices; + // const len = vertices.length - 1; + // for (let j = 0; j < len; j++) { + // results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + // best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[j], + // v2: vertices[j + 1] + // }; + // } + // } + // } + // results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + // best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[0], + // v2: vertices[len] + // }; + // } + // } + // } + // }; + // //check for collisions + // best = { + // x: null, + // y: null, + // dist2: Infinity, + // who: null, + // v1: null, + // v2: null + // }; + // if (tech.isPulseAim) { //find mobs in line of sight + // let dist = 2200 + // for (let i = 0, len = mob.length; i < len; i++) { + // const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) + // if (explosionRadius < newDist && + // newDist < dist && + // Matter.Query.ray(map, path[0], mob[i].position).length === 0 && + // Matter.Query.ray(body, path[0], mob[i].position).length === 0) { + // dist = newDist + // best.who = mob[i] + // path[path.length - 1] = mob[i].position + // } + // } + // } + // if (!best.who) { + // vertexCollision(path[0], path[1], mob); + // vertexCollision(path[0], path[1], map); + // vertexCollision(path[0], path[1], body); + // if (best.dist2 != Infinity) { //if hitting something + // path[path.length - 1] = { + // x: best.x, + // y: best.y + // }; + // } + // } + // if (best.who) b.explosion(path[1], explosionRadius) + + // //draw laser beam + // ctx.beginPath(); + // ctx.moveTo(path[0].x, path[0].y); + // ctx.lineTo(path[1].x, path[1].y); + // ctx.strokeStyle = "rgba(255,0,0,0.13)" + // ctx.lineWidth = 60 * energy / 0.2 + // ctx.stroke(); + // ctx.strokeStyle = "rgba(255,0,0,0.2)" + // ctx.lineWidth = 18 + // ctx.stroke(); + // ctx.strokeStyle = "#f00"; + // ctx.lineWidth = 4 + // ctx.stroke(); + + // //draw little dots along the laser path + // const sub = Vector.sub(path[1], path[0]) + // const mag = Vector.magnitude(sub) + // for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { + // const dist = Math.random() + // simulation.drawList.push({ + // x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), + // y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), + // radius: 1 + 4 * Math.random(), + // color: "rgba(255,0,0,0.5)", + // time: Math.floor(2 + 33 * Math.random() * Math.random()) + // }); + // } + // }, + fireworks(where, size) { //can occur after grenades detonate + const cycle = () => { + if (m.alive) { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count < 110) requestAnimationFrame(cycle); + if (!(count % 10)) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, 6.28 * Math.random()) + b.explosion(Vector.add(where, Vector.mult(unit, size * (count * 0.01 + 0.02 * Math.random()))), size * (0.4 + Math.random() * 0.35), `hsla(${360 * Math.random()},100%,66%,0.6)`); //makes bullet do explosive damage at end + } + } + } + } + let count = 7 + requestAnimationFrame(cycle); + }, + starburst(where, size) { //can occur after grenades detonate + const color = `hsla(${360 * Math.random()},100%,66%,0.6)` + const cycle = () => { + if (m.alive) { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count < 21) requestAnimationFrame(cycle); + if (count % 2) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, curl * 6.28 * count / 18 + off) + b.explosion(Vector.add(where, Vector.mult(unit, size * 0.75)), size * 0.7, color); //makes bullet do explosive damage at end + } + } + } + } + const off = 6 * Math.random() + const curl = Math.random() < 0.5 ? -1 : 1; + let count = 0 + requestAnimationFrame(cycle); + }, + fireFlower(where, size) { //can occur after grenades detonate + // size *= b.explosionRange() + const range = size * Math.sqrt(b.explosionRange()) + const cycle = () => { + if (m.alive) { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + if (count < 30 && m.alive) requestAnimationFrame(cycle); + if (count === 0) { + const color = `hsla(${360 * Math.random()},100%,66%,0.6)` + b.explosion(where, size * 0.8, color); + } + if (count === 8) { + const color = `hsla(${360 * Math.random()},100%,66%,0.6)` + for (let i = 0, len = 6; i < len; i++) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, 6.28 * i / len) + b.explosion(Vector.add(where, Vector.mult(unit, 1.1 * range)), size * 0.6, color); //makes bullet do explosive damage at end + } + } + if (count === 16) { + const color = `hsla(${360 * Math.random()},100%,66%,0.6)` + for (let i = 0, len = 10; i < len; i++) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, 6.28 * i / len) + b.explosion(Vector.add(where, Vector.mult(unit, 1.4 * range)), size * 0.45, color); //makes bullet do explosive damage at end + } + } + count++ + } + } + } + let count = 0 + requestAnimationFrame(cycle); + }, + grenadeEnd() { + if (tech.isCircleExplode) { + b.starburst(this.position, this.explodeRad) + } else if (tech.isPetalsExplode) { + b.fireFlower(this.position, this.explodeRad) + } else if (tech.isClusterExplode) { + b.fireworks(this.position, this.explodeRad) + } else { + b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end + } + if (tech.fragments) b.targetedNail(this.position, tech.fragments * Math.floor(2 + 1.5 * Math.random())) + }, + grenade() { + + }, + setGrenadeMode() { + grenadeDefault = function (where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, angle = m.angle, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 15, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0003); + bullet[me].explodeRad = 300 * size + 100 * tech.isBlockExplode; + bullet[me].onEnd = b.grenadeEnd + bullet[me].minDmgSpeed = 1; + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + speed = m.crouch ? 43 : 32 + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + bullet[me].endCycle = simulation.cycle + Math.floor(m.crouch ? 120 : 80) * tech.bulletsLastLonger; + bullet[me].restitution = 0.4; + bullet[me].do = function () { + this.force.y += this.mass * 0.0025; //extra gravity for harder arcs + }; + Composite.add(engine.world, bullet[me]); //add bullet to world + } + grenadeRPG = function (where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, angle = m.angle, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 15, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0003); + bullet[me].explodeRad = 300 * size + 100 * tech.isBlockExplode; + bullet[me].onEnd = b.grenadeEnd + bullet[me].minDmgSpeed = 1; + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + speed = m.crouch ? 46 : 32 + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + + bullet[me].endCycle = simulation.cycle + 70 * tech.bulletsLastLonger; + bullet[me].frictionAir = 0.07; + const MAG = 0.015 + bullet[me].thrust = { + x: bullet[me].mass * MAG * Math.cos(angle), + y: bullet[me].mass * MAG * Math.sin(angle) + } + bullet[me].do = function () { + this.force.x += this.thrust.x; + this.force.y += this.thrust.y; + if (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { + this.endCycle = 0; //explode if touching map or blocks + } + }; + } + grenadeRPGVacuum = function (where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, angle = m.angle, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 15, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0003); + bullet[me].explodeRad = 350 * size + Math.floor(Math.random() * 50) + tech.isBlockExplode * 100 + bullet[me].onEnd = b.grenadeEnd + bullet[me].minDmgSpeed = 1; + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + speed = m.crouch ? 46 : 32 + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + bullet[me].endCycle = simulation.cycle + 70 * tech.bulletsLastLonger; + bullet[me].frictionAir = 0.07; + bullet[me].suckCycles = 40 + const MAG = 0.015 + bullet[me].thrust = { + x: bullet[me].mass * MAG * Math.cos(angle), + y: bullet[me].mass * MAG * Math.sin(angle) + } + bullet[me].suck = function () { + const suck = (who, radius = this.explodeRad * 3.2) => { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(this.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius && dist > 150 && !who.isInvulnerable && who[i] !== this) { + knock = Vector.mult(Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); + who[i].force.x += knock.x; + who[i].force.y += knock.y; + } + } + } + let mag = 0.1 + if (simulation.cycle > this.endCycle - 5) { + mag = -0.22 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } else { + mag = 0.11 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } + + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); //keep bomb in place + //draw suck + const radius = 2.75 * this.explodeRad * (this.endCycle - simulation.cycle) / this.suckCycles + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + } + bullet[me].do = function () { + if (simulation.cycle > this.endCycle - this.suckCycles) { //suck + this.do = this.suck + } else if (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { + Matter.Body.setPosition(this, Vector.sub(this.position, this.velocity)) //undo last movement + this.do = this.suck + } else { + this.force.x += this.thrust.x; + this.force.y += this.thrust.y; + } + }; + } + grenadeVacuum = function (where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, angle = m.angle, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 20, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0002); + bullet[me].explodeRad = 350 * size + Math.floor(Math.random() * 50) + tech.isBlockExplode * 100 + bullet[me].onEnd = b.grenadeEnd + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + bullet[me].restitution = 0.4; + bullet[me].do = function () { + this.force.y += this.mass * 0.0025; //extra gravity for harder arcs + + const suckCycles = 40 + if (simulation.cycle > this.endCycle - suckCycles) { //suck + const that = this + + function suck(who, radius = that.explodeRad * 3.2) { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(that.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius && dist > 150 && !who.isInvulnerable) { + knock = Vector.mult(Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); + who[i].force.x += knock.x; + who[i].force.y += knock.y; + } + } + } + let mag = 0.1 + if (simulation.cycle > this.endCycle - 5) { + mag = -0.22 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } else { + mag = 0.11 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } + //keep bomb in place + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + //draw suck + const radius = 2.75 * this.explodeRad * (this.endCycle - simulation.cycle) / suckCycles + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + } + }; + speed = 35 + // speed = m.crouch ? 43 : 32 + + bullet[me].endCycle = simulation.cycle + 70 * tech.bulletsLastLonger; + if (m.crouch) { + speed += 9 + bullet[me].endCycle += 20; + } + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + } + + grenadeNeutron = function (where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, angle = m.angle, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.polygon(where.x, where.y, 10, 4, b.fireAttributes(angle, false)); + b.fireProps((m.crouch ? 45 : 25) / Math.pow(0.92, tech.missileCount), m.crouch ? 35 : 20, angle, me); //cd , speed + Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].endCycle = 500 + simulation.cycle; + bullet[me].frictionAir = 0; + bullet[me].friction = 1; + bullet[me].frictionStatic = 1; + bullet[me].restitution = 0; + bullet[me].minDmgSpeed = 0; + bullet[me].damageRadius = 100; + bullet[me].maxDamageRadius = 450 * size + 130 * tech.isNeutronSlow //+ 150 * Math.random() + bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow) / tech.bulletsLastLonger + bullet[me].stuckTo = null; + bullet[me].stuckToRelativePosition = null; + if (tech.isRPG) { + const SCALE = 2 + Matter.Body.scale(bullet[me], SCALE, SCALE); + speed = m.crouch ? 25 : 15 + // speed = m.crouch ? 43 : 32 + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + const MAG = 0.005 + bullet[me].thrust = { + x: bullet[me].mass * MAG * Math.cos(angle), + y: bullet[me].mass * MAG * Math.sin(angle) + } + } + + bullet[me].beforeDmg = function () { }; + bullet[me].stuck = function () { }; + bullet[me].do = function () { + const onCollide = () => { + this.collisionFilter.mask = 0; //non collide with everything + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + if (tech.isRPG) this.thrust = { + x: 0, + y: 0 + } + this.do = this.radiationMode; + } + const mobCollisions = Matter.Query.collides(this, mob) + if (mobCollisions.length) { + onCollide() + this.stuckTo = mobCollisions[0].bodyA + mobs.statusDoT(this.stuckTo, 0.5, 360) //apply radiation damage status effect on direct hits + if (this.stuckTo.isVerticesChange) { + this.stuckToRelativePosition = { + x: 0, + y: 0 + } + } else { + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } + this.stuck = function () { + if (this.stuckTo && this.stuckTo.alive) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.collisionFilter.mask = cat.map | cat.body | cat.player | cat.mob; //non collide with everything but map + this.stuck = function () { + this.force.y += this.mass * 0.001; + } + } + } + } else { + const bodyCollisions = Matter.Query.collides(this, body) + if (bodyCollisions.length) { + if (!bodyCollisions[0].bodyA.isNotHoldable) { + onCollide() + this.stuckTo = bodyCollisions[0].bodyA + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } else { + this.do = this.radiationMode; + } + this.stuck = function () { + if (this.stuckTo) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + // Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.force.y += this.mass * 0.001; + } + } + } else { + if (Matter.Query.collides(this, map).length) { + onCollide() + } else if (tech.isRPG) { //if colliding with nothing + this.force.x += this.thrust.x; + this.force.y += this.thrust.y; + } else { + this.force.y += this.mass * 0.001; + } + } + } + } + bullet[me].radiationMode = function () { //the do code after the bullet is stuck on something, projects a damaging radiation field + this.stuck(); //runs different code based on what the bullet is stuck to + this.damageRadius = this.damageRadius * 0.85 + 0.15 * this.maxDamageRadius //smooth radius towards max + this.maxDamageRadius -= this.radiusDecay + if (this.damageRadius < 15) { + this.endCycle = 0; + } else { + //aoe damage to player + if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { + const DRAIN = (tech.isRadioactiveResistance ? 0.0025 * 0.25 : 0.0025) + if (m.energy > DRAIN) { + if (m.immuneCycle < m.cycle) m.energy -= DRAIN + } else { + m.energy = 0; + if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00016 * 0.25 : 0.00016) * tech.radioactiveDamage) //0.00015 + } + } + //aoe damage to mobs + let dmg = m.dmgScale * 0.11 * tech.radioactiveDamage + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius + mob[i].radius) { + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way + mob[i].damage(mob[i].shield ? dmg * 3 : dmg); + mob[i].locatePlayer(); + if (tech.isNeutronSlow && mob[i].speed > 4) { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * 0.97, + y: mob[i].velocity.y * 0.97 + }); + } + } + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.damageRadius, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "lighter" + ctx.fillStyle = `rgba(25,139,170,${0.2 + 0.06 * Math.random()})`; + ctx.fill(); + ctx.globalCompositeOperation = "source-over" + if (tech.isNeutronSlow) { + + let slow = (who, radius = this.explodeRad * 3.2) => { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(this.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius) { + Matter.Body.setVelocity(who[i], { + x: who[i].velocity.x * 0.975, + y: who[i].velocity.y * 0.975 + }); + } + } + } + slow(body, this.damageRadius) + slow([player], this.damageRadius) + } + } + } + } + + if (tech.isNeutronBomb) { + b.grenade = grenadeNeutron + if (tech.isRPG) { + b.guns[5].do = function () { } + } else { + b.guns[5].do = function () { + if (!input.field && m.crouch) { + const cycles = 80 + const speed = m.crouch ? 35 : 20 //m.crouch ? 43 : 32 + const g = m.crouch ? 0.137 : 0.135 + const v = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } + ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map + ctx.lineWidth = 2 + ctx.beginPath() + for (let i = 1, len = 19; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + g * time * time) + } + ctx.stroke() + } + } + } + } else if (tech.isRPG) { + b.guns[5].do = function () { } + if (tech.isVacuumBomb) { + b.grenade = grenadeRPGVacuum + } else { + b.grenade = grenadeRPG + } + } else if (tech.isVacuumBomb) { + b.grenade = grenadeVacuum + b.guns[5].do = function () { + if (!input.field && m.crouch) { + const cycles = Math.floor(m.crouch ? 50 : 30) //30 + const speed = m.crouch ? 44 : 35 + const v = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } + ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map + ctx.lineWidth = 2 + ctx.beginPath() + for (let i = 1.6, len = 19; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.stroke() + } + } + } else { + b.grenade = grenadeDefault + b.guns[5].do = function () { + if (!input.field && m.crouch) { + const cycles = Math.floor(m.crouch ? 120 : 80) //30 + const speed = m.crouch ? 43 : 32 + const v = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } //m.Vy / 2 + removed to make the path less jerky + ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map + ctx.lineWidth = 2 + ctx.beginPath() + for (let i = 0.5, len = 19; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.stroke() + } + } + } + }, + // dart(where, angle = m.angle, size = 0.8) { + // //find a target + // const closest = { + // score: 10000, + // position: null + // } + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, where, mob[i].position).length === 0) { + // const dot = Vector.dot({ x: Math.cos(angle), y: Math.sin(angle) }, Vector.normalise(Vector.sub(mob[i].position, where))) //the dot product of diff and dir will return how much over lap between the vectors + // const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + // // if (dist < closest.score && ((dist > 500 && dot > 0) || (dot > 0.9))) { //target closest mob that player is looking at and isn't too close to target + // if (dist < closest.score && dot > 0.9 - 0.0004 * dist) { //target closest mob that player is looking at and isn't too close to target + // closest.score = dist + // closest.position = mob[i].position + // } + // } + // } + // if (!closest.position) { + // // const unit = Vector.mult(sub(simulation.mouseInGame, where), 10000) + // closest.position = Vector.mult(Vector.sub(simulation.mouseInGame, where), 10000) + // } + // const me = bullet.length; + // bullet[me] = Bodies.fromVertices(where.x, where.y, [{ x: -20 * size, y: 2 * size, index: 0, isInternal: false }, { x: -20 * size, y: -2 * size, index: 1, isInternal: false }, { x: 5 * size, y: -2 * size, index: 4, isInternal: false }, { x: 20 * size, y: 0, index: 3, isInternal: false }, { x: 5 * size, y: 2 * size, index: 4, isInternal: false }], { + // cycle: 0, + // angle: angle, + // friction: 1, + // frictionAir: 0.15, + // thrustMag: 0.03, + // turnRate: 0.15, //0.015 + // drawStringControlMagnitude: 3000 + 5000 * Math.random(), + // drawStringFlip: (Math.round(Math.random()) ? 1 : -1), + // dmg: 7, //damage done in addition to the damage from momentum + // classType: "bullet", + // endCycle: simulation.cycle + 120, + // collisionFilter: { + // category: cat.bullet, + // mask: tech.isShieldPierce ? cat.body | cat.mob | cat.mobBullet : cat.body | cat.mob | cat.mobBullet | cat.mobShield, + // }, + // minDmgSpeed: 0, + // lookFrequency: Math.floor(7 + Math.random() * 3), + // density: 0.001, //0.001 is normal for blocks, 0.008 is normal for harpoon, 0.008*6 when buffed + // beforeDmg(who) { + // if (tech.isShieldPierce && who.isShielded) { //disable shields + // who.isShielded = false + // requestAnimationFrame(() => { who.isShielded = true }); + // } + // if (tech.fragments) { + // b.targetedNail(this.vertices[2], tech.fragments * Math.floor(2 + 1.5 * Math.random())) + // this.endCycle = 0; + // } + // if (!who.isBadTarget) { + // this.frictionAir = 0.01 + // this.do = this.doNoTargeting + // } + // }, + // onEnd() {}, + // doNoTargeting: function() { + // // this.force.y += this.mass * 0.001; + // if (Matter.Query.collides(this, map).length) { //stick in walls + // this.collisionFilter.mask = 0; + // Matter.Body.setAngularVelocity(this, 0) + // Matter.Body.setVelocity(this, { + // x: 0, + // y: 0 + // }); + // this.do = () => { + // // if (!Matter.Query.collides(this, map).length) this.force.y += this.mass * 0.001; + // } + // } + // }, + // do() { + // this.cycle++ + // // if (this.cycle > 40) { + // // this.frictionAir = 0.003 + // // this.do = this.doNoTargeting + // // } + // // if (closest.target) { //rotate towards the target + // const face = { x: Math.cos(this.angle), y: Math.sin(this.angle) }; + // const vectorGoal = Vector.normalise(Vector.sub(this.position, closest.position)); + // const cross = Vector.cross(vectorGoal, face) + // if (cross > 0.01) { + // Matter.Body.rotate(this, this.turnRate * Math.sqrt(cross)); + // } else if (cross < 0.01) { + // Matter.Body.rotate(this, -this.turnRate * Math.sqrt(Math.abs(cross))); + // } + // this.force.x += this.thrustMag * this.mass * Math.cos(this.angle); + // this.force.y += this.thrustMag * this.mass * Math.sin(this.angle); + // // } + // if (Matter.Query.collides(this, map).length) { //stick in walls + // this.collisionFilter.mask = 0; + // Matter.Body.setAngularVelocity(this, 0) + // Matter.Body.setVelocity(this, { + // x: 0, + // y: 0 + // }); + // this.do = this.doNoTargeting + // } + // // else if (!(this.cycle % 2)) { //look for a target if you don't have one + // // simulation.drawList.push({ //add dmg to draw queue + // // x: this.position.x, + // // y: this.position.y, + // // radius: 10, + // // color: simulation.mobDmgColor, + // // time: simulation.drawTime + // // }); + // // let closest = { + // // distance: 2000, + // // target: null + // // } + // // const dir = Vector.normalise(this.velocity) //make a vector for direction of length 1 + // // for (let i = 0, len = mob.length; i < len; ++i) { + // // if ( + // // mob[i].alive && !mob[i].isBadTarget && + // // Matter.Query.ray(map, this.position, mob[i].position).length === 0 && //check for map in Line of sight + // // Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, this.position))) > 0.55 //the dot product of diff and dir will return how much over lap between the vectors + // // ) { + // // const dist = Vector.magnitude(Vector.sub(this.position, mob[i].position)) + // // if (dist < closest.distance) { + // // closest.distance = dist + // // closest.target = mob[i] + // // } + // // } + // // } + // // if (closest.target) { + // // target = closest.target + // // this.turnRate = 0.05 + // // this.frictionAir = 0.8 + // // } + // // } + // }, + // }); + // Matter.Body.setVelocity(bullet[me], { + // x: m.Vx / 2 + 40 * Math.cos(bullet[me].angle), + // y: m.Vy / 2 + 40 * Math.sin(bullet[me].angle) + // }); + // // if (!closest.target) { + // // bullet[me].frictionAir = 0.002 + // // bullet[me].do = bullet[me].doNoTargeting + // // } + // Composite.add(engine.world, bullet[me]); //add bullet to world + + // }, + grapple(where, angle = m.angle, harpoonSize = 1) { + const me = bullet.length; + const returnRadius = 100 * Math.sqrt(harpoonSize) + bullet[me] = Bodies.fromVertices(where.x, where.y, [{ + x: -50 * harpoonSize, + y: 2 * harpoonSize, + index: 0, + isInternal: false + }, { + x: -50 * harpoonSize, + y: -2 * harpoonSize, + index: 1, + isInternal: false + }, { + x: 45 * harpoonSize, + y: -3 * harpoonSize, + index: 2, + isInternal: false + }, { + x: 50 * harpoonSize, + y: 0, + index: 3, + isInternal: false + }, { + x: 45 * harpoonSize, + y: 3 * harpoonSize, + index: 4, + isInternal: false + }], { + angle: angle, + friction: 1, + frictionAir: 0.4, + thrustMag: 0.1, + dmg: 6, //damage done in addition to the damage from momentum + classType: "bullet", + endCycle: simulation.cycle + 70, + collisionFilter: { + category: cat.bullet, + mask: tech.isShieldPierce ? cat.body | cat.mob | cat.mobBullet : cat.body | cat.mob | cat.mobBullet | cat.mobShield, + }, + minDmgSpeed: 4, + lookFrequency: Math.floor(7 + Math.random() * 3), + density: tech.harpoonDensity, //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + drain: tech.isRailEnergy ? 0.0006 : 0.006, + beforeDmg(who) { + if (tech.isShieldPierce && who.isShielded) { //disable shields + who.isShielded = false + requestAnimationFrame(() => { + who.isShielded = true + }); + } + if (tech.fragments) { + b.targetedNail(this.vertices[2], tech.fragments * Math.floor(2 + Math.random())) + } + if (tech.isFoamBall) { + for (let i = 0, len = 3 * this.mass; i < len; i++) { + const radius = 5 + 8 * Math.random() + const velocity = { + x: Math.max(0.5, 2 - radius * 0.1), + y: 0 + } + b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) + } + // this.endCycle = 0; + } + }, + caughtPowerUp: null, + dropCaughtPowerUp() { + if (this.caughtPowerUp) { + this.caughtPowerUp.collisionFilter.category = cat.powerUp + this.caughtPowerUp.collisionFilter.mask = cat.map | cat.powerUp + this.caughtPowerUp = null + } + }, + onEnd() { + if (this.caughtPowerUp && !simulation.isChoosing && (this.caughtPowerUp.name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)) { + let index = null //find index + for (let i = 0, len = powerUp.length; i < len; ++i) { + if (powerUp[i] === this.caughtPowerUp) index = i + } + if (index !== null) { + powerUps.onPickUp(this.caughtPowerUp); + this.caughtPowerUp.effect(); + Matter.Composite.remove(engine.world, this.caughtPowerUp); + powerUp.splice(index, 1); + if (tech.isHarpoonPowerUp) tech.harpoonDensity = 0.004 * 6 //0.005 is normal + } else { + this.dropCaughtPowerUp() + } + } else { + this.dropCaughtPowerUp() + } + }, + draw() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const sub = Vector.sub(where, this.vertices[0]) + const controlPoint = Vector.add(where, Vector.mult(sub, -0.5)) + ctx.strokeStyle = "#000" // "#0ce" + ctx.lineWidth = 0.5 + ctx.beginPath(); + ctx.moveTo(where.x, where.y); + ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, this.vertices[0].x, this.vertices[0].y) + // ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + ctx.stroke(); + //draw harpoon spikes + const spikeLength = 2 + ctx.beginPath(); + const spike1 = Vector.add(this.vertices[1], Vector.mult(Vector.sub(this.vertices[1], this.vertices[2]), spikeLength)) + ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + ctx.lineTo(spike1.x, spike1.y); + ctx.lineTo(this.vertices[3].x, this.vertices[3].y); + + const spike2 = Vector.add(this.vertices[3], Vector.mult(Vector.sub(this.vertices[3], this.vertices[2]), spikeLength)) + ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + ctx.lineTo(spike2.x, spike2.y); + ctx.lineTo(this.vertices[1].x, this.vertices[1].y); + ctx.fillStyle = '#000' + ctx.fill(); + }, + returnToPlayer() { + if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius) { //near player + this.endCycle = 0; + // if (m.energy < 0.05) { + // m.fireCDcycle = m.cycle + 120; //fire cooldown + // } else if (m.cycle + 15 * b.fireCDscale < m.fireCDcycle) { + // m.fireCDcycle = m.cycle + 15 * b.fireCDscale //lower cd to 25 if it is above 25 + // } + + if (m.energy < 0.05) this.dropCaughtPowerUp() + + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + player.force.x += momentum.x + player.force.y += momentum.y + // refund ammo + b.guns[9].ammo++; + simulation.updateGunHUD(); + + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // b.guns[i].ammo++; + // simulation.updateGunHUD(); + // break; + // } + // } + } else { + if (m.energy > this.drain) m.energy -= this.drain + const sub = Vector.sub(this.position, m.pos) + const rangeScale = 1 + 0.000001 * Vector.magnitude(sub) * Vector.magnitude(sub) //return faster when far from player + const returnForce = Vector.mult(Vector.normalise(sub), rangeScale * this.thrustMag * this.mass) + this.force.x -= returnForce.x + this.force.y -= returnForce.y + this.grabPowerUp() + } + this.draw(); + }, + grabPowerUp() { //grab power ups near the tip of the harpoon + if (this.caughtPowerUp) { + Matter.Body.setPosition(this.caughtPowerUp, Vector.add(this.vertices[2], this.velocity)) + Matter.Body.setVelocity(this.caughtPowerUp, { + x: 0, + y: 0 + }) + } else { //&& simulation.cycle % 2 + for (let i = 0, len = powerUp.length; i < len; ++i) { + const radius = powerUp[i].circleRadius + 50 + if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius) { + if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { + this.caughtPowerUp = powerUp[i] + Matter.Body.setVelocity(powerUp[i], { + x: 0, + y: 0 + }) + Matter.Body.setPosition(powerUp[i], this.vertices[2]) + powerUp[i].collisionFilter.category = 0 + powerUp[i].collisionFilter.mask = 0 + this.thrustMag *= 0.6 + this.endCycle += 0.5 //it pulls back slower, so this prevents it from ending early + break //just pull 1 power up if possible + } + } + } + } + }, + do() { + if (input.fire) { //&& !Matter.Query.collides(this, body).length + this.grabPowerUp() + if (this.endCycle < simulation.cycle + 1) { //if at end of lifespan, but player is holding down fire, force retraction + this.endCycle = simulation.cycle + 60 + // m.fireCDcycle = m.cycle + 120 // cool down + this.do = this.returnToPlayer + Matter.Body.setDensity(this, 0.0005); //reduce density on return + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + } + } else { + //if not enough energy + if (m.energy < 0.05) this.dropCaughtPowerUp() + // const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) + // this.force.x -= returnForce.x + // this.force.y -= returnForce.y + // this.frictionAir = 0.002 + // this.do = () => { + // if (this.speed < 20) this.force.y += 0.0005 * this.mass; + // } + + // } else { + //return to player + this.do = this.returnToPlayer + this.endCycle = simulation.cycle + 60 + Matter.Body.setDensity(this, 0.0005); //reduce density on return + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + player.force.x += momentum.x + player.force.y += momentum.y + // } + } + //grappling hook + if (input.fire && Matter.Query.collides(this, map).length) { + Matter.Body.setPosition(this, Vector.add(this.position, { + x: 20 * Math.cos(this.angle), + y: 20 * Math.sin(this.angle) + })) + if (Matter.Query.collides(this, map).length) { + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + Matter.Sleeping.set(this, true) + this.endCycle = simulation.cycle + 5 + this.dropCaughtPowerUp() + this.do = () => { + //between player nose and the grapple + const sub = Vector.sub(this.vertices[0], { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }) + let dist = Vector.magnitude(sub) + if (input.fire) { + // m.fireCDcycle = m.cycle + 30; // cool down if out of energy + m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) + this.endCycle = simulation.cycle + 10 + if (input.down) { //down + dist = 0 + player.force.y += 5 * player.mass * simulation.g; + } + if (m.energy > this.drain) { + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.8, + y: player.velocity.y * 0.8 + }); + + + //need to scale the friction differently based on distance? + // if (dist > 500) { + const pull = Vector.mult(Vector.normalise(sub), 0.0008 * Math.min(Math.max(15, dist), 200)) + player.force.x += pull.x + player.force.y += pull.y + // } + + if (dist > 500) { + m.energy -= this.drain + if (m.energy < 0) { + this.endCycle = 0; + if (m.cycle + 50 < m.fireCDcycle) m.fireCDcycle = m.cycle + 50 + // refund ammo + b.guns[9].ammo++; + simulation.updateGunHUD(); + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // break; + // } + // } + } + } + } + if (tech.isImmuneGrapple && m.immuneCycle < m.cycle + 10) { + m.immuneCycle = m.cycle + 10; + if (m.energy > 0.001) { + m.energy -= 0.001 + } else { //out of energy + Matter.Sleeping.set(this, false) + this.collisionFilter.category = 0 + this.collisionFilter.mask = 0 + this.do = this.returnToPlayer + this.endCycle = simulation.cycle + 60 + m.fireCDcycle = m.cycle + 120; //fire cooldown + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + player.force.x += momentum.x + player.force.y += momentum.y + } + } + } else { + Matter.Sleeping.set(this, false) + this.collisionFilter.category = 0 + this.collisionFilter.mask = 0 + this.do = this.returnToPlayer + this.endCycle = simulation.cycle + 60 + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + player.force.x += momentum.x + player.force.y += momentum.y + } + this.draw(); + } + } + } + this.force.x += this.thrustMag * this.mass * Math.cos(this.angle); + this.force.y += this.thrustMag * this.mass * Math.sin(this.angle); + this.draw() + }, + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + // grapple(where, angle = m.angle, harpoonSize = 1) { + // const me = bullet.length; + // const returnRadius = 100 * Math.sqrt(harpoonSize) + // bullet[me] = Bodies.fromVertices(where.x, where.y, [{ + // x: -50 * harpoonSize, + // y: 2 * harpoonSize, + // index: 0, + // isInternal: false + // }, { + // x: -50 * harpoonSize, + // y: -2 * harpoonSize, + // index: 1, + // isInternal: false + // }, { + // x: 45 * harpoonSize, + // y: -3 * harpoonSize, + // index: 2, + // isInternal: false + // }, { + // x: 50 * harpoonSize, + // y: 0, + // index: 3, + // isInternal: false + // }, { + // x: 45 * harpoonSize, + // y: 3 * harpoonSize, + // index: 4, + // isInternal: false + // }], { + // angle: angle, + // friction: 1, + // frictionAir: 0.4, + // thrustMag: 0.1, + // dmg: 6, //damage done in addition to the damage from momentum + // classType: "bullet", + // endCycle: simulation.cycle + 70, + // collisionFilter: { + // category: cat.bullet, + // mask: tech.isShieldPierce ? cat.body | cat.mob | cat.mobBullet : cat.body | cat.mob | cat.mobBullet | cat.mobShield, + // }, + // minDmgSpeed: 4, + // lookFrequency: Math.floor(7 + Math.random() * 3), + // density: tech.harpoonDensity, //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + // drain: tech.isRailEnergy ? 0.0006 : 0.006, + // beforeDmg(who) { + // if (tech.isShieldPierce && who.isShielded) { //disable shields + // who.isShielded = false + // requestAnimationFrame(() => { + // who.isShielded = true + // }); + // } + // if (tech.fragments) { + // b.targetedNail(this.vertices[2], tech.fragments * Math.floor(2 + Math.random())) + // } + // if (tech.isFoamBall) { + // for (let i = 0, len = 4 * this.mass; i < len; i++) { + // const radius = 5 + 8 * Math.random() + // const velocity = { + // x: Math.max(0.5, 2 - radius * 0.1), + // y: 0 + // } + // b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) + // } + // // this.endCycle = 0; + // } + // }, + // caughtPowerUp: null, + // dropCaughtPowerUp() { + // if (this.caughtPowerUp) { + // this.caughtPowerUp.collisionFilter.category = cat.powerUp + // this.caughtPowerUp.collisionFilter.mask = cat.map | cat.powerUp + // this.caughtPowerUp = null + // } + // }, + // onEnd() { + // if (this.caughtPowerUp && !simulation.isChoosing && (this.caughtPowerUp.name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)) { + // let index = null //find index + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if (powerUp[i] === this.caughtPowerUp) index = i + // } + // if (index !== null) { + // powerUps.onPickUp(this.caughtPowerUp); + // this.caughtPowerUp.effect(); + // Matter.Composite.remove(engine.world, this.caughtPowerUp); + // powerUp.splice(index, 1); + // if (tech.isHarpoonPowerUp) tech.harpoonDensity = 0.004 * 6 //0.005 is normal + // } else { + // this.dropCaughtPowerUp() + // } + // } else { + // this.dropCaughtPowerUp() + // } + // }, + // draw() { + // const where = { + // x: m.pos.x + 30 * Math.cos(m.angle), + // y: m.pos.y + 30 * Math.sin(m.angle) + // } + // const sub = Vector.sub(where, this.vertices[0]) + // const controlPoint = Vector.add(where, Vector.mult(sub, -0.5)) + // ctx.strokeStyle = "#000" // "#0ce" + // ctx.lineWidth = 0.5 + // ctx.beginPath(); + // ctx.moveTo(where.x, where.y); + // ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, this.vertices[0].x, this.vertices[0].y) + // // ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + // ctx.stroke(); + // //draw harpoon spikes + // const spikeLength = 2 + // ctx.beginPath(); + // const spike1 = Vector.add(this.vertices[1], Vector.mult(Vector.sub(this.vertices[1], this.vertices[2]), spikeLength)) + // ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + // ctx.lineTo(spike1.x, spike1.y); + // ctx.lineTo(this.vertices[3].x, this.vertices[3].y); + + // const spike2 = Vector.add(this.vertices[3], Vector.mult(Vector.sub(this.vertices[3], this.vertices[2]), spikeLength)) + // ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + // ctx.lineTo(spike2.x, spike2.y); + // ctx.lineTo(this.vertices[1].x, this.vertices[1].y); + // ctx.fillStyle = '#000' + // ctx.fill(); + // }, + // returnToPlayer() { + // if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius) { //near player + // this.endCycle = 0; + // // if (m.energy < 0.05) { + // // m.fireCDcycle = m.cycle + 120; //fire cooldown + // // } else if (m.cycle + 15 * b.fireCDscale < m.fireCDcycle) { + // // m.fireCDcycle = m.cycle + 15 * b.fireCDscale //lower cd to 25 if it is above 25 + // // } + + // if (m.energy < 0.05) this.dropCaughtPowerUp() + + // //recoil on catching + // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + // player.force.x += momentum.x + // player.force.y += momentum.y + // // refund ammo + // b.guns[9].ammo++; + // simulation.updateGunHUD(); + + // // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // // if (b.guns[i].name === "harpoon") { + // // b.guns[i].ammo++; + // // simulation.updateGunHUD(); + // // break; + // // } + // // } + // } else { + // if (m.energy > this.drain) m.energy -= this.drain + // const sub = Vector.sub(this.position, m.pos) + // const rangeScale = 1 + 0.000001 * Vector.magnitude(sub) * Vector.magnitude(sub) //return faster when far from player + // const returnForce = Vector.mult(Vector.normalise(sub), rangeScale * this.thrustMag * this.mass) + // this.force.x -= returnForce.x + // this.force.y -= returnForce.y + // this.grabPowerUp() + // } + // this.draw(); + // }, + // grabPowerUp() { //grab power ups near the tip of the harpoon + // if (this.caughtPowerUp) { + // Matter.Body.setPosition(this.caughtPowerUp, Vector.add(this.vertices[2], this.velocity)) + // Matter.Body.setVelocity(this.caughtPowerUp, { + // x: 0, + // y: 0 + // }) + // } else { //&& simulation.cycle % 2 + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // const radius = powerUp[i].circleRadius + 50 + // if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius) { + // if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { + // this.caughtPowerUp = powerUp[i] + // Matter.Body.setVelocity(powerUp[i], { + // x: 0, + // y: 0 + // }) + // Matter.Body.setPosition(powerUp[i], this.vertices[2]) + // powerUp[i].collisionFilter.category = 0 + // powerUp[i].collisionFilter.mask = 0 + // this.thrustMag *= 0.6 + // this.endCycle += 0.5 //it pulls back slower, so this prevents it from ending early + // break //just pull 1 power up if possible + // } + // } + // } + // } + // }, + // do() { + // if (input.fire) { //&& !Matter.Query.collides(this, body).length + // this.grabPowerUp() + // if (this.endCycle < simulation.cycle + 1) { //if at end of lifespan, but player is holding down fire, force retraction + // this.endCycle = simulation.cycle + 60 + // // m.fireCDcycle = m.cycle + 120 // cool down + // this.do = this.returnToPlayer + // Matter.Body.setDensity(this, 0.0005); //reduce density on return + // if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + // this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + // } + // } else { + // //if not enough energy + // if (m.energy < 0.05) this.dropCaughtPowerUp() + // // const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) + // // this.force.x -= returnForce.x + // // this.force.y -= returnForce.y + // // this.frictionAir = 0.002 + // // this.do = () => { + // // if (this.speed < 20) this.force.y += 0.0005 * this.mass; + // // } + + // // } else { + // //return to player + // this.do = this.returnToPlayer + // this.endCycle = simulation.cycle + 60 + // Matter.Body.setDensity(this, 0.0005); //reduce density on return + // if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + // this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + // //recoil on catching + // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + // player.force.x += momentum.x + // player.force.y += momentum.y + // // } + // } + // //grappling hook + // if (input.fire && Matter.Query.collides(this, map).length) { + // Matter.Body.setPosition(this, Vector.add(this.position, { + // x: 20 * Math.cos(this.angle), + // y: 20 * Math.sin(this.angle) + // })) + // if (Matter.Query.collides(this, map).length) { + // Matter.Body.setVelocity(this, { + // x: 0, + // y: 0 + // }); + // Matter.Sleeping.set(this, true) + // this.endCycle = simulation.cycle + 5 + // this.dropCaughtPowerUp() + // this.do = () => { + // //between player nose and the grapple + // const sub = Vector.sub(this.vertices[0], { + // x: m.pos.x + 30 * Math.cos(m.angle), + // y: m.pos.y + 30 * Math.sin(m.angle) + // }) + // let dist = Vector.magnitude(sub) + // if (input.fire) { + // // m.fireCDcycle = m.cycle + 30; // cool down if out of energy + // m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) + // this.endCycle = simulation.cycle + 10 + // if (input.down) { //down + // dist = 0 + // player.force.y += 5 * player.mass * simulation.g; + // } + // if (m.energy > this.drain) { + // Matter.Body.setVelocity(player, { + // x: player.velocity.x * 0.8, + // y: player.velocity.y * 0.8 + // }); + // const pull = Vector.mult(Vector.normalise(sub), 0.0008 * Math.min(Math.max(15, dist), 200)) + // player.force.x += pull.x + // player.force.y += pull.y + + // if (dist > 500) { + // m.energy -= this.drain + // if (m.energy < 0) { + // this.endCycle = 0; + // if (m.cycle + 50 < m.fireCDcycle) m.fireCDcycle = m.cycle + 50 + // // refund ammo + // b.guns[9].ammo++; + // simulation.updateGunHUD(); + // // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // // if (b.guns[i].name === "harpoon") { + // // break; + // // } + // // } + // } + // } + // } + // if (tech.isImmuneGrapple && m.immuneCycle < m.cycle + 10) { + // m.immuneCycle = m.cycle + 10; + // if (m.energy > 0.001) { + // m.energy -= 0.001 + // } else { //out of energy + // Matter.Sleeping.set(this, false) + // this.collisionFilter.category = 0 + // this.collisionFilter.mask = 0 + // this.do = this.returnToPlayer + // this.endCycle = simulation.cycle + 60 + // m.fireCDcycle = m.cycle + 120; //fire cooldown + // //recoil on catching + // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + // player.force.x += momentum.x + // player.force.y += momentum.y + // } + // } + // } else { + // Matter.Sleeping.set(this, false) + // this.collisionFilter.category = 0 + // this.collisionFilter.mask = 0 + // this.do = this.returnToPlayer + // this.endCycle = simulation.cycle + 60 + // //recoil on catching + // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + // player.force.x += momentum.x + // player.force.y += momentum.y + // } + // this.draw(); + // } + // } + // } + // this.force.x += this.thrustMag * this.mass * Math.cos(this.angle); + // this.force.y += this.thrustMag * this.mass * Math.sin(this.angle); + // this.draw() + // }, + // }); + // Composite.add(engine.world, bullet[me]); //add bullet to world + // }, + harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + const me = bullet.length; + const returnRadius = 100 * Math.sqrt(harpoonSize) + bullet[me] = Bodies.fromVertices(where.x, where.y, [{ + x: -40 * harpoonSize, + y: 2 * harpoonSize, + index: 0, + isInternal: false + }, { + x: -40 * harpoonSize, + y: -2 * harpoonSize, + index: 1, + isInternal: false + }, { + x: 50 * harpoonSize, + y: -3 * harpoonSize, + index: 3, + isInternal: false + }, { + x: 30 * harpoonSize, + y: 2 * harpoonSize, + index: 4, + isInternal: false + }], { + cycle: 0, + angle: angle, + friction: 1, + frictionAir: 0.4, + // thrustMag: 0.1, + drain: tech.isRailEnergy ? 0.0006 : 0.006, + turnRate: isReturn ? 0.1 : 0.03, //0.015 + drawStringControlMagnitude: 3000 + 5000 * Math.random(), + drawStringFlip: (Math.round(Math.random()) ? 1 : -1), + dmg: 6, //damage done in addition to the damage from momentum + classType: "bullet", + endCycle: simulation.cycle + totalCycles * 2.5 + 40, + collisionFilter: { + category: cat.bullet, + mask: tech.isShieldPierce ? cat.map | cat.body | cat.mob | cat.mobBullet : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield, + }, + minDmgSpeed: 4, + lookFrequency: Math.floor(7 + Math.random() * 3), + density: tech.harpoonDensity, //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + beforeDmg(who) { + if (tech.isShieldPierce && who.isShielded) { //disable shields + who.isShielded = false + requestAnimationFrame(() => { + who.isShielded = true + }); + } + if (tech.fragments) { + b.targetedNail(this.vertices[2], tech.fragments * Math.floor(2 + Math.random())) + if (!isReturn) this.endCycle = 0; + } + if (!who.isBadTarget) { + if (isReturn) { + this.do = this.returnToPlayer + } else { + this.frictionAir = 0.01 + this.do = () => { + this.force.y += this.mass * 0.003; //gravity + this.draw(); + } + } + } + if (tech.isFoamBall) { + for (let i = 0, len = Math.min(50, 2.5 + 3 * Math.sqrt(this.mass)); i < len; i++) { + const radius = 5 + 8 * Math.random() + const velocity = { x: Math.max(0.5, 2 - radius * 0.1), y: 0 } + b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) + } + } + }, + caughtPowerUp: null, + dropCaughtPowerUp() { + if (this.caughtPowerUp) { + this.caughtPowerUp.collisionFilter.category = cat.powerUp + this.caughtPowerUp.collisionFilter.mask = cat.map | cat.powerUp + this.caughtPowerUp = null + } + }, + onEnd() { + if (this.caughtPowerUp && !simulation.isChoosing && (this.caughtPowerUp.name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)) { + let index = null //find index + for (let i = 0, len = powerUp.length; i < len; ++i) { + if (powerUp[i] === this.caughtPowerUp) index = i + } + if (index !== null) { + powerUps.onPickUp(this.caughtPowerUp); + this.caughtPowerUp.effect(); + Matter.Composite.remove(engine.world, this.caughtPowerUp); + powerUp.splice(index, 1); + if (tech.isHarpoonPowerUp) tech.harpoonDensity = 0.004 * 6 //0.006 is normal + } else { + this.dropCaughtPowerUp() + } + } else { + this.dropCaughtPowerUp() + } + }, + drawToggleHarpoon() { + ctx.beginPath(); + ctx.moveTo(this.vertices[0].x, this.vertices[0].y); + for (let j = 1, len = this.vertices.length; j < len; j += 1) ctx.lineTo(this.vertices[j].x, this.vertices[j].y); + ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + ctx.lineJoin = "miter" + ctx.miterLimit = 100; + ctx.lineWidth = 60; + ctx.strokeStyle = "rgba(0,255,255,0.25)"; + ctx.stroke(); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgb(0,255,255)"; + ctx.stroke(); + ctx.lineJoin = "round" + ctx.miterLimit = 10 + ctx.fillStyle = "#000" + ctx.fill(); + }, + drawString() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const sub = Vector.sub(where, this.vertices[0]) + const perpendicular = Vector.mult(Vector.normalise(Vector.perp(sub)), this.drawStringFlip * Math.min(80, 10 + this.drawStringControlMagnitude / (10 + Vector.magnitude(sub)))) + const controlPoint = Vector.add(Vector.add(where, Vector.mult(sub, -0.5)), perpendicular) + ctx.strokeStyle = "#000" // "#0ce" + ctx.lineWidth = 0.5 + ctx.beginPath(); + ctx.moveTo(where.x, where.y); + ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, this.vertices[0].x, this.vertices[0].y) + // ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + ctx.stroke(); + }, + draw() { }, + returnToPlayer() { + if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius) { //near player + this.endCycle = 0; + // if (m.energy < 0.05) { + // m.fireCDcycle = m.cycle + 80 * b.fireCDscale; //fire cooldown is much longer when out of energy + // } else if (m.cycle + 20 * b.fireCDscale < m.fireCDcycle) { + // if (m.energy > 0.05) m.fireCDcycle = m.cycle + 20 * b.fireCDscale //lower cd to 25 if it is above 25 + // } + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + player.force.x += momentum.x + player.force.y += momentum.y + // refund ammo + if (isReturnAmmo) { + b.guns[9].ammo++; + simulation.updateGunHUD(); + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // break; + // } + // } + } + } else { + const sub = Vector.sub(this.position, m.pos) + const rangeScale = 1 + 0.000001 * Vector.magnitude(sub) * Vector.magnitude(sub) //return faster when far from player + const returnForce = Vector.mult(Vector.normalise(sub), rangeScale * thrust * this.mass) + if (m.energy > this.drain) m.energy -= this.drain + if (m.energy < 0.05) { + this.force.x -= returnForce.x * 0.15 + this.force.y -= returnForce.y * 0.15 + } else { //if (m.cycle + 20 * b.fireCDscale < m.fireCDcycle) + this.force.x -= returnForce.x + this.force.y -= returnForce.y + } + this.grabPowerUp() + } + this.draw(); + }, + grabPowerUp() { //grab power ups near the tip of the harpoon + if (this.caughtPowerUp) { + Matter.Body.setPosition(this.caughtPowerUp, Vector.add(this.vertices[2], this.velocity)) + Matter.Body.setVelocity(this.caughtPowerUp, { + x: 0, + y: 0 + }) + } else { //&& simulation.cycle % 2 + for (let i = 0, len = powerUp.length; i < len; ++i) { + const radius = powerUp[i].circleRadius + 50 + if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius && !powerUp[i].isGrabbed) { + if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { + powerUp[i].isGrabbed = true + this.caughtPowerUp = powerUp[i] + Matter.Body.setVelocity(powerUp[i], { + x: 0, + y: 0 + }) + Matter.Body.setPosition(powerUp[i], this.vertices[2]) + powerUp[i].collisionFilter.category = 0 + powerUp[i].collisionFilter.mask = 0 + thrust *= 0.6 + this.endCycle += 0.5 //it pulls back slower, so this prevents it from ending early + break //just pull 1 power up if possible + } + } + } + } + }, + do() { + this.cycle++ + if (isReturn || target) { + if (isReturn) { + if (this.cycle > totalCycles) { //return to player //|| !input.fire + this.do = this.returnToPlayer + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + Matter.Sleeping.set(this, false) + this.endCycle = simulation.cycle + 240 + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.00015 : 0.0003)) //recoil on jerking line + player.force.x += momentum.x + player.force.y += momentum.y + requestAnimationFrame(() => { //delay this for 1 cycle to get the proper hit graphics + this.collisionFilter.category = 0 + this.collisionFilter.mask = 0 + }); + } else { + this.grabPowerUp() + } + } + if (target) { //rotate towards the target + const face = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + }; + const vectorGoal = Vector.normalise(Vector.sub(this.position, target.position)); + if (Vector.cross(vectorGoal, face) > 0) { + Matter.Body.rotate(this, this.turnRate); + } else { + Matter.Body.rotate(this, -this.turnRate); + } + } + this.force.x += thrust * this.mass * Math.cos(this.angle); + this.force.y += thrust * this.mass * Math.sin(this.angle); + } + this.draw() + }, + }); + if (!isReturn && !target) { + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + 600 * thrust * Math.cos(bullet[me].angle), + y: m.Vy / 2 + 600 * thrust * Math.sin(bullet[me].angle) + }); + bullet[me].frictionAir = 0.002 + bullet[me].do = function () { + if (this.speed < 20) this.force.y += 0.0005 * this.mass; + this.draw(); + } + } + if (tech.isHarpoonPowerUp && bullet[me].density > 0.01) { + if (isReturn) { + bullet[me].draw = function () { + this.drawToggleHarpoon() + this.drawString() + } + } else { + bullet[me].draw = function () { + this.drawToggleHarpoon() + } + } + } else if (isReturn) { + bullet[me].draw = function () { + this.drawString() + } + } + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + missile(where, angle, speed, size = 1) { + if (tech.isMissileBig) { + size *= 1.55 + if (tech.isMissileBiggest) { + size *= 1.55 + + } + } + const me = bullet.length; + bullet[me] = Bodies.rectangle(where.x, where.y, 30 * size, 4 * size, { + angle: angle, + friction: 0.5, + frictionAir: 0.045, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + endCycle: simulation.cycle + Math.floor((230 + 40 * Math.random()) * tech.bulletsLastLonger + 120 * tech.isMissileBiggest + 60 * tech.isMissileBig), + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + minDmgSpeed: 10, + lookFrequency: Math.floor(10 + Math.random() * 3), + explodeRad: (tech.isMissileBig ? 230 : 180) + 60 * Math.random(), + density: 0.02, //0.001 is normal + beforeDmg() { + Matter.Body.setDensity(this, 0.0001); //reduce density to normal + this.tryToLockOn(); + this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion + }, + onEnd() { + b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end + if (tech.fragments) b.targetedNail(this.position, tech.fragments * Math.floor(2 + 1.5 * Math.random())) + }, + lockedOn: null, + tryToLockOn() { + let closeDist = Infinity; + const futurePos = Vector.add(this.position, Vector.mult(this.velocity, 30)) //look for closest target to where the missile will be in 30 cycles + this.lockedOn = null; + // const futurePos = this.lockedOn ? :Vector.add(this.position, Vector.mult(this.velocity, 50)) + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + mob[i].alive && !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + const futureDist = Vector.magnitude(Vector.sub(futurePos, mob[i].position)); + if (futureDist < closeDist) { + closeDist = futureDist; + this.lockedOn = mob[i]; + // this.frictionAir = 0.04; //extra friction once a target it locked + } + if (Vector.magnitude(Vector.sub(this.position, mob[i].position) < this.explodeRad)) { + this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion + mob[i].lockedOn.damage(m.dmgScale * 2 * size); //does extra damage to target + } + } + } + //explode when bullet is close enough to target + if (this.lockedOn && Vector.magnitude(Vector.sub(this.position, this.lockedOn.position)) < this.explodeRad) { + this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion + this.lockedOn.damage(m.dmgScale * 4 * size); //does extra damage to target + } + }, + do() { + if (!(m.cycle % this.lookFrequency)) this.tryToLockOn(); + if (this.lockedOn) { //rotate missile towards the target + const face = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + }; + const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position)); + const dot = Vector.dot(target, face) + const aim = Math.min(0.08, (1 + dot) * 1) + if (Vector.cross(target, face) > 0) { + Matter.Body.rotate(this, aim); + } else { + Matter.Body.rotate(this, -aim); + } + this.frictionAir = Math.min(0.1, Math.max(0.04, 1 + dot)) //0.08; //extra friction if turning + } + //accelerate in direction bullet is facing + const dir = this.angle; + this.force.x += thrust * Math.cos(dir); + this.force.y += thrust * Math.sin(dir); + + ctx.beginPath(); //draw rocket + ctx.arc(this.position.x - Math.cos(this.angle) * (25 * size - 3) + (Math.random() - 0.5) * 4, + this.position.y - Math.sin(this.angle) * (25 * size - 3) + (Math.random() - 0.5) * 4, + 11 * size, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,155,0,0.5)"; + ctx.fill(); + }, + }); + const thrust = 0.0066 * bullet[me].mass * (tech.isMissileBig ? (tech.isMissileBiggest ? 0.3 : 0.7) : 1); + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + lastAngle: 0, + wasExtruderOn: false, + isExtruderOn: false, + didExtruderDrain: false, + canExtruderFire: true, + extruder() { + const DRAIN = 0.0012 + if (m.energy > DRAIN && b.canExtruderFire) { + m.energy -= DRAIN + if (m.energy < 0) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + b.isExtruderOn = true + const SPEED = 8 + 12 * tech.isPlasmaRange + const me = bullet.length; + const where = Vector.add(m.pos, player.velocity) + bullet[me] = Bodies.polygon(where.x + 20 * Math.cos(m.angle), where.y + 20 * Math.sin(m.angle), 4, 0.01, { + cycle: -0.5, + isWave: true, + endCycle: simulation.cycle + 40, // + 30 * tech.isPlasmaRange, + inertia: Infinity, + frictionAir: 0, + isInHole: true, //this keeps the bullet from entering wormholes + minDmgSpeed: 0, + dmg: m.dmgScale * 2.7, //damage also changes when you divide by mob.mass on in .do() + classType: "bullet", + isBranch: false, + restitution: 0, + collisionFilter: { + // category: 0, + // mask: 0, //cat.mob | cat.mobBullet | cat.mobShield + category: 0, //cat.bullet, + mask: 0, //cat.map, //cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { }, + do() { + if (this.endCycle < simulation.cycle + 1) this.isWave = false + if (Matter.Query.point(map, this.position).length) { //check if inside map //|| Matter.Query.point(body, this.position).length + this.isBranch = true; + this.do = () => { + if (this.endCycle < simulation.cycle + 1) this.isWave = false + } + } else { //check if inside a mob + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) + const radius = mob[i].radius + tech.extruderRange / 2 + if (dist < radius * radius) { + if (mob[i].speed > 2) { + if (mob[i].isBoss || mob[i].isShielded) { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * 0.95, + y: mob[i].velocity.y * 0.95 + }); + } else { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * 0.25, + y: mob[i].velocity.y * 0.25 + }); + } + } + // Matter.Body.setPosition(this, Vector.add(this.position, mob[i].velocity)) //move with the medium + let dmg = this.dmg / Math.min(10, mob[i].mass) + mob[i].damage(dmg); + if (mob[i].alive) mob[i].foundPlayer(); + } + } + } + this.cycle++ + const wiggleMag = (m.crouch ? 6 : 12) * Math.cos(simulation.cycle * 0.09) + const wiggle = Vector.mult(transverse, wiggleMag * Math.cos(this.cycle * 0.36)) //+ wiggleMag * Math.cos(simulation.cycle * 0.3)) + const velocity = Vector.mult(player.velocity, 0.4) //move with player + Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle))) + } + }); + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + const transverse = Vector.normalise(Vector.perp(bullet[me].velocity)) + if (180 - Math.abs(Math.abs(b.lastAngle - m.angle) - 180) > 0.13 || !b.wasExtruderOn) { + bullet[me].isBranch = true; //don't draw stroke for this bullet + bullet[me].do = function () { + if (this.endCycle < simulation.cycle + 1) this.isWave = false + } + } + b.lastAngle = m.angle //track last angle for the above angle difference calculation + } else { + b.canExtruderFire = false; + } + }, + plasma() { + const DRAIN = 0.00075 + if (m.energy > DRAIN) { + m.energy -= DRAIN; + if (m.energy < 0) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + + //calculate laser collision + let best; + let range = tech.isPlasmaRange * (120 + (m.crouch ? 400 : 300) * Math.sqrt(Math.random())) //+ 100 * Math.sin(m.cycle * 0.3); + // const dir = m.angle // + 0.04 * (Math.random() - 0.5) + const path = [{ + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + }, + { + x: m.pos.x + range * Math.cos(m.angle), + y: m.pos.y + range * Math.sin(m.angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + if (best.who.alive) { + const dmg = 0.9 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* + best.who.damage(dmg); + best.who.locatePlayer(); + + //push mobs away + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + if (best.who.speed > 4) { + Matter.Body.setVelocity(best.who, { //friction + x: best.who.velocity.x * 0.9, + y: best.who.velocity.y * 0.9 + }); + } + //draw mob damage circle + simulation.drawList.push({ + x: path[1].x, + y: path[1].y, + radius: Math.sqrt(2000 * dmg * best.who.damageReduction), + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime * 4 + }); + } else if (!best.who.isStatic) { + //push blocks away + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.007 * Math.sqrt(Math.sqrt(best.who.mass))) + Matter.Body.applyForce(best.who, path[1], force) + } + } + + //draw blowtorch laser beam + ctx.strokeStyle = "rgba(255,0,255,0.1)" + ctx.lineWidth = 14 + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.stroke(); + ctx.strokeStyle = "#f0f"; + ctx.lineWidth = 2 + ctx.stroke(); + + //draw electricity + const Dx = Math.cos(m.angle); + const Dy = Math.sin(m.angle); + let x = m.pos.x + 20 * Dx; + let y = m.pos.y + 20 * Dy; + ctx.beginPath(); + ctx.moveTo(x, y); + const step = Vector.magnitude(Vector.sub(path[0], path[1])) / 10 + for (let i = 0; i < 8; i++) { + x += step * (Dx + 1.5 * (Math.random() - 0.5)) + y += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); + } + }, + laser(where = { + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + }, whereEnd = { + x: where.x + 3000 * Math.cos(m.angle), + y: where.y + 3000 * Math.sin(m.angle) + }, dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1) { + const reflectivity = 1 - 1 / (reflections * 3) + let damage = m.dmgScale * dmg + let best = { + x: 1, + y: 1, + dist2: Infinity, + who: null, + v1: 1, + v2: 1 + }; + const path = [{ + x: where.x, + y: where.y + }, { + x: whereEnd.x, + y: whereEnd.y + }]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + const checkForCollisions = function () { + best = { + x: 1, + y: 1, + dist2: Infinity, + who: null, + v1: 1, + v2: 1 + }; + vertexCollision(path[path.length - 2], path[path.length - 1], mob); + vertexCollision(path[path.length - 2], path[path.length - 1], map); + vertexCollision(path[path.length - 2], path[path.length - 1], body); + }; + const laserHitMob = function () { + if (best.who.alive) { + best.who.locatePlayer(); + if (best.who.damageReduction) { + if ( //iridescence + tech.laserCrit && !best.who.shield && + Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.999 - 0.5 / best.who.radius + ) { + damage *= 1 + tech.laserCrit + simulation.drawList.push({ //add dmg to draw queue + x: path[path.length - 1].x, + y: path[path.length - 1].y, + radius: Math.sqrt(2500 * damage * best.who.damageReduction) + 5, + color: `hsla(${60 + 283 * Math.random()},100%,70%,0.5)`, // random hue, but not red + time: 16 + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: path[path.length - 1].x, + y: path[path.length - 1].y, + radius: Math.sqrt(2000 * damage * best.who.damageReduction) + 2, + color: tech.laserColorAlpha, + time: simulation.drawTime + }); + } + best.who.damage(damage); + } + if (tech.isLaserPush) { //push mobs away + const index = path.length - 1 + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.97, y: best.who.velocity.y * 0.97 }); + const force = Vector.mult(Vector.normalise(Vector.sub(path[index], path[Math.max(0, index - 1)])), 0.003 * push * Math.min(6, best.who.mass)) + Matter.Body.applyForce(best.who, path[index], force) + } + } else if (tech.isLaserPush && best.who.classType === "body") { + const index = path.length - 1 + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.97, y: best.who.velocity.y * 0.97 }); + const force = Vector.mult(Vector.normalise(Vector.sub(path[index], path[Math.max(0, index - 1)])), 0.003 * push * Math.min(6, best.who.mass)) + Matter.Body.applyForce(best.who, path[index], force) + } + }; + const reflection = function () { // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); + const d = Vector.sub(path[path.length - 1], path[path.length - 2]); + const nn = Vector.mult(n, 2 * Vector.dot(d, n)); + const r = Vector.normalise(Vector.sub(d, nn)); + path[path.length] = Vector.add(Vector.mult(r, 3000), path[path.length - 1]); + }; + + checkForCollisions(); + let lastBestOdd + let lastBestEven = best.who //used in hack below + if (best.dist2 !== Infinity) { //if hitting something + path[path.length - 1] = { x: best.x, y: best.y }; + laserHitMob(); + for (let i = 0; i < reflections; i++) { + reflection(); + checkForCollisions(); + if (best.dist2 !== Infinity) { //if hitting something + lastReflection = best + path[path.length - 1] = { x: best.x, y: best.y }; + damage *= reflectivity + laserHitMob(); + //I'm not clear on how this works, but it gets rid of a bug where the laser reflects inside a block, often vertically. + //I think it checks to see if the laser is reflecting off a different part of the same block, if it is "inside" a block + if (i % 2) { + if (lastBestOdd === best.who) break + } else { + lastBestOdd = best.who + if (lastBestEven === best.who) break + } + } else { + break + } + } + } + if (isThickBeam) { + for (let i = 1, len = path.length; i < len; ++i) { + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + } + } else if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 2 + ctx.lineDashOffset = 900 * Math.random() + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.beginPath(); + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + ctx.stroke(); + ctx.globalAlpha *= reflectivity; //reflections are less intense + } + ctx.setLineDash([]); + // ctx.globalAlpha = 1; + + //glow + ctx.lineWidth = 9 + 2 * b.guns[11].lensDamageOn + ctx.globalAlpha = 0.13 + ctx.beginPath(); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + } + ctx.stroke(); + ctx.globalAlpha = 1; + } else { + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 2 + ctx.lineDashOffset = 900 * Math.random() + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.beginPath(); + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + ctx.stroke(); + ctx.globalAlpha *= reflectivity; //reflections are less intense + } + ctx.setLineDash([]); + ctx.globalAlpha = 1; + } + }, + AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded && !mob[i].shield && !mob[i].isBadTarget) { + if (Vector.magnitude(Vector.sub(where, mob[i].position)) - mob[i].radius < range) mobs.statusStun(mob[i], cycles) + } + } + simulation.drawList.push({ + x: where.x, + y: where.y, + radius: range, + color: "rgba(0,0,0,0.1)", + time: 15 + }); + }, + laserMine(position, velocity = { + x: 0, + y: -8 + }) { + const me = bullet.length; + bullet[me] = Bodies.polygon(position.x, position.y, 3, 25, { + bulletType: "laser mine", + angle: m.angle, + friction: 0, + frictionAir: 0.025, + restitution: 0.5, + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 67 + Math.floor(7 * Math.random()), + drain: 0.7 * tech.laserDrain, + isDetonated: false, + torqueMagnitude: 0.000003 * (Math.round(Math.random()) ? 1 : -1), + range: 1500, + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { }, + do() { + if (!(simulation.cycle % this.lookFrequency) && m.energy > this.drain) { //find mob targets + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(this.position, mob[i].position)) < 1300 && + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + if (tech.isStun) b.AoEStunEffect(this.position, 1300); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { + this.do = this.laserSpin + if (this.angularSpeed < 0.5) this.torque += this.inertia * this.torqueMagnitude * 200 //spin + this.endCycle = simulation.cycle + 360 + 120 + // if (this.angularSpeed < 0.01) this.torque += this.inertia * this.torqueMagnitude * 5 //spin + this.isDetonated = true + break + } + } + } + }, + reflections: Math.max(0, tech.laserReflections - 2), + laserSpin() { + //drain energy + if (m.energy > this.drain) { + m.energy -= this.drain + if (this.angularSpeed < 0.05) this.torque += this.inertia * this.torqueMagnitude //spin + + //fire lasers + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 1.5 + // ctx.globalAlpha = 1; + ctx.beginPath(); + for (let i = 0; i < 3; i++) { + const where = this.vertices[i] + const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500)) + b.laser(where, endPoint, tech.laserDamage * 13, this.reflections, true) + } + ctx.stroke(); + // ctx.globalAlpha = 1; + } + if (this.endCycle - 60 < simulation.cycle) { + this.do = () => { } //no nothing, no laser, no spin + } + }, + }) + Matter.Body.setVelocity(bullet[me], velocity); + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + mine(where, velocity, angle = 0) { + const bIndex = bullet.length; + bullet[bIndex] = Bodies.rectangle(where.x, where.y, 45, 16, { + angle: angle, + friction: 1, + frictionStatic: 1, + frictionAir: 0, + restitution: 0, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + bulletType: "mine", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield // | cat.bullet //doesn't collide with other bullets until it lands (was crashing into bots) + }, + minDmgSpeed: 5, + stillCount: 0, + isArmed: false, + endCycle: Infinity, + lookFrequency: 0, + range: 700 - 300 * tech.isFoamMine, + beforeDmg() { }, + onEnd() { + if (this.isArmed && !tech.isMineSentry) { + if (tech.isFoamMine) { + //send 14 in random directions slowly + for (let i = 0; i < 12; i++) { + const radius = 13 + 8 * Math.random() + const velocity = { x: 0.5 + 5.5 * Math.random(), y: 0 } + b.foam(this.position, Vector.rotate(velocity, this.angle + 1.57 + 3 * (Math.random() - 0.5)), radius) //6.28 * Math.random() + } + //send 40 targeted + let count = 0 + let cycle = () => { + if (count < 50) { + if (!simulation.paused && !simulation.isChoosing) { //!(simulation.cycle % 1) && + count++ + b.targetedFoam(this.position) + } + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle) + } else if (tech.isSuperMine) { + b.targetedBall(this.position, 22 + 2 * tech.extraSuperBalls) + } else { + b.targetedNail(this.position, 22, 40 + 10 * Math.random(), 1200, true, 2.2) //targetedNail(position, num = 1, speed = 40 + 10 * Math.random(), range = 1200, isRandomAim = true, damage = 1.4) { + } + } + }, + do() { + this.force.y += this.mass * 0.002; //extra gravity + let collide = Matter.Query.collides(this, map) //check if collides with map + if (collide.length > 0) { + for (let i = 0; i < collide.length; i++) { + if (collide[i].bodyA.collisionFilter.category === cat.map) { // || collide[i].bodyB.collisionFilter.category === cat.map) { + const angle = Vector.angle(collide[i].normal, { x: 1, y: 0 }) + Matter.Body.setAngle(this, Math.atan2(collide[i].tangent.y, collide[i].tangent.x)) + for (let j = 0; j < 10; j++) { //move until touching map again after rotation + if (Matter.Query.collides(this, map).length > 0) { //touching map + if (angle > -0.2 || angle < -1.5) { //don't stick to level ground + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + Matter.Body.setStatic(this, true) //don't set to static if not touching map + this.collisionFilter.category = 0 + this.collisionFilter.mask = 0 //cat.map | cat.bullet + } else { + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + Matter.Body.setAngularVelocity(this, 0) + } + this.arm(); + //sometimes the mine can't attach to map and it just needs to be reset + setTimeout(() => { + if (Matter.Query.collides(this, map).length === 0 || Matter.Query.point(map, this.position).length > 0) { + this.endCycle = 0 // if not touching map explode + this.isArmed = false + b.mine(this.position, this.velocity, this.angle) + } + }, 100); + break + } + Matter.Body.setPosition(this, Vector.add(this.position, Vector.mult(collide[i].normal, 2))) //move until you are touching the wall + } + break + } + } + } else { + if (this.speed < 1 && this.angularSpeed < 0.01) this.stillCount++ + } + if (this.stillCount > 25) this.arm(); + }, + arm() { + this.collisionFilter.mask = cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.bullet //can now collide with other bullets + this.lookFrequency = simulation.cycle + 60 + this.do = function () { //overwrite the do method for this bullet + this.force.y += this.mass * 0.002; //extra gravity + if (simulation.cycle > this.lookFrequency) { + this.isArmed = true + this.lookFrequency = 55 + Math.floor(22 * Math.random()) + simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: 10, color: "#f00", time: 4 }); + this.do = function () { //overwrite the do method for this bullet + + + //make mobs think the mine is where the player is + // for (let i = 0; i < mob.length; i++) { + // mob[i].seePlayer.recall = mob[i].memory + Math.round(mob[i].memory * Math.random()); //cycles before mob falls a sleep + // mob[i].seePlayer.position.x = this.position.x; + // mob[i].seePlayer.position.y = this.position.y; + // mob[i].seePlayer.yes = true; + // } + + + this.force.y += this.mass * 0.002; //extra gravity + if (!(simulation.cycle % this.lookFrequency)) { //find mob targets + + + + + + const random = 300 * Math.random() + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + Vector.magnitude(Vector.sub(this.position, mob[i].position)) < this.range + mob[i].radius + random && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + if (tech.isStun) b.AoEStunEffect(this.position, this.range + mob[i].radius + random); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { + if (tech.isMineSentry) { + this.lookFrequency = Math.floor(7 + 7 * b.fireCDscale + 10 * (tech.oneSuperBall && tech.isSuperMine) + Math.floor(3 * Math.random())) + // this.endCycle = Infinity + this.shots = tech.sentryAmmo + this.do = function () { //overwrite the do method for this bullet + this.force.y += this.mass * 0.002; //extra gravity + if (!(simulation.cycle % this.lookFrequency)) { //find mob targets + if (tech.isFoamMine) { + this.shots -= 0.6 * b.targetedFoam(this.position, 1, 21 + 7 * Math.random(), 1200, false) + b.targetedFoam(this.position, 1, 21 + 7 * Math.random(), 1200, false) + } else if (tech.isSuperMine) { + const cost = tech.oneSuperBall ? 2 : 0.7 + this.shots -= cost * b.targetedBall(this.position, 1, 42 + 12 * Math.random(), 1200, false) + for (let i = 0, len = tech.extraSuperBalls / 4; i < len; i++) { + if (Math.random() < 0.33) b.targetedBall(this.position, 1, 42 + 12 * Math.random(), 1200, false) + } + } else { + this.shots -= b.targetedNail(this.position, 1, 45 + 5 * Math.random(), 1100, false, 2.3) //targetedNail(position, num = 1, speed = 40 + 10 * Math.random(), range = 1200, isRandomAim = true, damage = 1.4) { + } + if (this.shots < 0) this.endCycle = 0 + if (!(simulation.cycle % (this.lookFrequency * 6))) { + simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: 8, color: "#fe0", time: 4 }); + } + } + } + break + } else { + this.endCycle = 0 //end life if mob is near and visible + break + } + } + } + } + } + } + } + }, + }); + bullet[bIndex].torque += bullet[bIndex].inertia * 0.0002 * (0.5 - Math.random()) + Matter.Body.setVelocity(bullet[bIndex], velocity); + Composite.add(engine.world, bullet[bIndex]); //add bullet to world + }, + worm(where, isFreeze = tech.isSporeFreeze) { //used with the tech upgrade in mob.death() + const bIndex = bullet.length; + const wormSize = 6 + tech.wormSize * 4.2 * Math.random() + if (bIndex < 500) { //can't make over 500 spores + bullet[bIndex] = Bodies.polygon(where.x, where.y, 3, 3, { + inertia: Infinity, + isFreeze: isFreeze, + restitution: 0.5, + // angle: Math.random() * 2 * Math.PI, + friction: 0, + frictionAir: 0.025, + thrust: (tech.isSporeFollow ? 0.0012 : 0.00055) * (1 + 0.5 * (Math.random() - 0.5)), + wormSize: wormSize, + wormTail: 1 + Math.max(4, Math.min(wormSize - 2 * tech.wormSize, 30)), + dmg: (tech.isMutualism ? 9.5 : 3.2) * wormSize * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1), + lookFrequency: 100 + Math.floor(37 * Math.random()), + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.mob | cat.mobBullet | cat.mobShield //no collide with body + }, + endCycle: simulation.cycle + Math.floor((600 + Math.floor(Math.random() * 420)) * tech.bulletsLastLonger), + minDmgSpeed: 0, + playerOffPosition: { //used when moving towards player to keep spores separate + x: 100 * (Math.random() - 0.5), + y: 100 * (Math.random() - 0.5) + }, + beforeDmg(who) { + if (tech.isSpawnBulletsOnDeath && who.alive && who.isDropPowerUp) { + setTimeout(() => { + if (!who.alive) { + for (let i = 0; i < 3; i++) { //spawn 3 more + b.worm(this.position) + bullet[bullet.length - 1].endCycle = Math.min(simulation.cycle + Math.floor(420 * tech.bulletsLastLonger), this.endCycle + 180 + Math.floor(60 * Math.random())) //simulation.cycle + Math.floor(420 * tech.bulletsLastLonger) + } + } + this.endCycle = 0; //bullet ends cycle after doing damage + }, 1); + } else { + this.endCycle = 0; //bullet ends cycle after doing damage + } + if (this.isFreeze) mobs.statusSlow(who, 90) + }, + onEnd() { + if (tech.isMutualism && this.isMutualismActive && !tech.isEnergyHealth) { + m.health += 0.02 + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + } + }, + tailCycle: 6.28 * Math.random(), + do() { + this.tailCycle += this.speed * 0.025 + ctx.beginPath(); //draw nematode + ctx.moveTo(this.position.x, this.position.y); + // const dir = Vector.mult(Vector.normalise(this.velocity), -Math.min(100, this.wormTail * this.speed)) + const speed = Math.min(7, this.speed) + const dir = Vector.mult(Vector.normalise(this.velocity), -0.6 * this.wormTail * speed) + const tail = Vector.add(this.position, dir) + const wiggle = Vector.add(Vector.add(tail, dir), Vector.rotate(dir, Math.sin(this.tailCycle))) + // const wiggle = Vector.add(tail, Vector.rotate(dir, Math.sin((m.cycle - this.endCycle) * 0.03 * this.speed))) + ctx.quadraticCurveTo(tail.x, tail.y, wiggle.x, wiggle.y) // ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, this.vertices[0].x, this.vertices[0].y) + // ctx.lineTo(tail.x, tail.y); + ctx.lineWidth = this.wormSize; + ctx.strokeStyle = "#000"; + ctx.stroke(); + + + if (this.lockedOn && this.lockedOn.alive) { + this.force = Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.mass * this.thrust) + } else { + if (!(simulation.cycle % this.lookFrequency)) { //find mob targets + this.closestTarget = null; + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if (!mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const targetVector = Vector.sub(this.position, mob[i].position) + const dist = Vector.magnitude(targetVector) * (Math.random() + 0.5); + if (dist < closeDist) { + this.closestTarget = mob[i].position; + closeDist = dist; + this.lockedOn = mob[i] + if (0.3 > Math.random()) break //doesn't always target the closest mob + } + } + } + } + if (tech.isSporeFollow && this.lockedOn === null) { //move towards player //checking for null means that the spores don't go after the player until it has looked and not found a target + const dx = this.position.x - m.pos.x; + const dy = this.position.y - m.pos.y; + if (dx * dx + dy * dy > 10000) { + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, Vector.add(this.playerOffPosition, this.position))), this.mass * this.thrust) + } + } else { + const unit = Vector.normalise(this.velocity) + const force = Vector.mult(Vector.rotate(unit, 0.005 * this.playerOffPosition.x), 0.000003) + this.force.x += force.x + this.force.y += force.y + } + } + }, + }); + const SPEED = 2 + 1 * Math.random(); + const ANGLE = 2 * Math.PI * Math.random() + Matter.Body.setVelocity(bullet[bIndex], { + x: SPEED * Math.cos(ANGLE), + y: SPEED * Math.sin(ANGLE) + }); + Composite.add(engine.world, bullet[bIndex]); //add bullet to world + if (tech.isMutualism && m.health > 0.04) { + m.health -= 0.02 + m.displayHealth(); + bullet[bIndex].isMutualismActive = true + } + } + }, + spore(where, isFreeze = tech.isSporeFreeze) { //used with the tech upgrade in mob.death() + const bIndex = bullet.length; + const size = 4 + if (bIndex < 500) { //can't make over 500 spores + bullet[bIndex] = Bodies.polygon(where.x, where.y, size, size, { + // density: 0.0015, //frictionAir: 0.01, + inertia: Infinity, + isFreeze: isFreeze, + restitution: 0.5, + angle: Math.random() * 2 * Math.PI, + friction: 0, + frictionAir: 0.025, + thrust: (tech.isSporeFollow ? 0.0011 : 0.0005) * (1 + 0.3 * (Math.random() - 0.5)), + dmg: (tech.isMutualism ? 20 : 7) * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1), //bonus damage from tech.isMutualism + lookFrequency: 100 + Math.floor(117 * Math.random()), + classType: "bullet", + isSpore: true, + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.mob | cat.mobBullet | cat.mobShield //no collide with body + }, + endCycle: simulation.cycle + Math.floor((540 + Math.floor(Math.random() * 420)) * tech.bulletsLastLonger), + minDmgSpeed: 0, + playerOffPosition: { //used when moving towards player to keep spores separate + x: 100 * (Math.random() - 0.5), + y: 100 * (Math.random() - 0.5) + }, + beforeDmg(who) { + this.endCycle = 0; //bullet ends cycle after doing damage + if (this.isFreeze) mobs.statusSlow(who, 90) + }, + onEnd() { + if (tech.isMutualism && this.isMutualismActive && !tech.isEnergyHealth) { + m.health += 0.01 + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + } + }, + do() { + if (this.lockedOn && this.lockedOn.alive) { + this.force = Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.mass * this.thrust) + } else { + if (!(simulation.cycle % this.lookFrequency)) { //find mob targets + this.closestTarget = null; + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if (!mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const targetVector = Vector.sub(this.position, mob[i].position) + const dist = Vector.magnitude(targetVector) * (Math.random() + 0.5); + if (dist < closeDist) { + this.closestTarget = mob[i].position; + closeDist = dist; + this.lockedOn = mob[i] + if (0.3 > Math.random()) break //doesn't always target the closest mob + } + } + } + } + if (tech.isSporeFollow && this.lockedOn === null) { //move towards player + //checking for null means that the spores don't go after the player until it has looked and not found a target + const dx = this.position.x - m.pos.x; + const dy = this.position.y - m.pos.y; + if (dx * dx + dy * dy > 10000) { + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, Vector.add(this.playerOffPosition, this.position))), this.mass * this.thrust) + } + } else { + this.force.y += this.mass * 0.0001; //gravity + } + + } + + // if (!this.lockedOn && !(simulation.cycle % this.lookFrequency)) { //find mob targets + // this.closestTarget = null; + // this.lockedOn = null; + // let closeDist = Infinity; + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i].isDropPowerUp && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + // // Matter.Query.ray(body, this.position, mob[i].position).length === 0 + // const targetVector = Vector.sub(this.position, mob[i].position) + // const dist = Vector.magnitude(targetVector); + // if (dist < closeDist) { + // this.closestTarget = mob[i].position; + // closeDist = dist; + // this.lockedOn = mob[i] //Vector.normalise(targetVector); + // if (0.3 > Math.random()) break //doesn't always target the closest mob + // } + // } + // } + // } + // if (this.lockedOn && this.lockedOn.alive) { //accelerate towards mobs + // this.force = Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.mass * this.thrust) + // } else if (tech.isSporeFollow && this.lockedOn !== undefined) { //move towards player + // //checking for undefined means that the spores don't go after the player until it has looked and not found a target + // const dx = this.position.x - m.pos.x; + // const dy = this.position.y - m.pos.y; + // if (dx * dx + dy * dy > 10000) { + // this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, Vector.add(this.playerOffPosition, this.position))), this.mass * this.thrust) + // } + // // this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.thrust) + // } else { + // this.force.y += this.mass * 0.0001; //gravity + // } + + // if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport + // this.nextPortCycle = simulation.cycle + this.portFrequency + // const range = 50 * Math.random() + // Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random()))) + // } + }, + }); + // if (tech.isBulletTeleport) { + // bullet[bIndex].portFrequency = 10 + Math.floor(5 * Math.random()) + // bullet[bIndex].nextPortCycle = simulation.cycle + bullet[bIndex].portFrequency + // } + + const SPEED = 4 + 8 * Math.random(); + const ANGLE = 2 * Math.PI * Math.random() + Matter.Body.setVelocity(bullet[bIndex], { + x: SPEED * Math.cos(ANGLE), + y: SPEED * Math.sin(ANGLE) + }); + Composite.add(engine.world, bullet[bIndex]); //add bullet to world + + if (tech.isMutualism && m.health > 0.01) { + m.health -= 0.01 + m.displayHealth(); + bullet[bIndex].isMutualismActive = true + } + } + }, + iceIX(speed = 0, dir = m.angle + Math.PI * 2 * Math.random(), where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }) { + const me = bullet.length; + const THRUST = 0.0018 + const RADIUS = 18 + const SCALE = 1 - 0.12 / tech.bulletsLastLonger + bullet[me] = Bodies.polygon(where.x, where.y, 3, RADIUS, { + angle: dir - Math.PI, + // inertia: Infinity, + spin: 0.00004 * (0.1 + Math.random()) * (Math.round(Math.random()) ? 1 : -1), + friction: 0, + frictionAir: 0.02, + restitution: 0.9, + dmg: 1.3, //damage done in addition to the damage from momentum + lookFrequency: 14 + Math.floor(8 * Math.random()), + endCycle: simulation.cycle + 65 * tech.bulletsLastLonger + Math.floor(25 * Math.random()), + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //self collide + }, + minDmgSpeed: 0, + lockedOn: null, + beforeDmg(who) { + if (tech.iceEnergy && !who.shield && !who.isShielded && who.isDropPowerUp && who.alive && m.immuneCycle < m.cycle) { + setTimeout(() => { + if (!who.alive) m.energy += tech.iceEnergy * 0.8 + }, 10); + } + mobs.statusSlow(who, tech.iceIXFreezeTime) + this.endCycle = simulation.cycle + // if (tech.isHeavyWater) mobs.statusDoT(who, 0.15, 300) + }, + onEnd() { }, + do() { + // this.force.y += this.mass * 0.0002; + //find mob targets + if (!(simulation.cycle % this.lookFrequency)) { + Matter.Body.scale(this, SCALE, SCALE); + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + } + if (this.lockedOn) { //accelerate towards mobs + this.force = Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.mass * THRUST) + } else { + this.force = Vector.mult(Vector.normalise(this.velocity), this.mass * THRUST) + } + this.torque += this.inertia * this.spin + } + }) + + Composite.add(engine.world, bullet[me]); //add bullet to world + // Matter.Body.setAngularVelocity(bullet[me], 2 * (0.5 - Math.random())) //doesn't work due to high friction + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }); + Matter.Body.setAngularVelocity(bullet[me], 3000 * bullet[me].spin); + + // Matter.Body.setVelocity(bullet[me], { + // x: m.Vx / 2 + speed * Math.cos(dir), + // y: m.Vy / 2 + speed * Math.sin(dir) + // }); + }, + flea(where, velocity, radius = 6 + 3 * Math.random() + 10 * tech.wormSize * Math.random()) { + const me = bullet.length; + bullet[me] = Bodies.polygon(where.x, where.y, 5, radius, { + isFlea: true, + angle: 0.5 * Math.random(), + friction: 1, + frictionStatic: 1, + frictionAir: 0, //0.01, + restitution: 0, + density: 0.0005, // 0.001 is normal density + lookFrequency: 19 + Math.floor(7 * Math.random()), + endCycle: simulation.cycle + Math.floor((900 * tech.bulletsLastLonger + 420 * Math.random()) + Math.max(0, 150 - bullet.length)), // 13 - 19s + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + minDmgSpeed: 0, + lockedOn: null, + delay: 50, + cd: simulation.cycle + 10, + dmg: 0, + setDamage() { //dmg is set to zero after doing damage once, and set back to normal after jumping + this.dmg = radius * (tech.isMutualism ? 2.9 : 1) * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1) //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 + }, + beforeDmg(who) { + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), 10 + 10 * Math.random())); //push away from target + this.endCycle -= 130 + this.cd = simulation.cycle + this.delay; + if (tech.isSporeFreeze) mobs.statusSlow(who, 90) + if (tech.isSpawnBulletsOnDeath && who.alive && who.isDropPowerUp) { + setTimeout(() => { + if (!who.alive) { + for (let i = 0; i < 2; i++) { //spawn 2 more + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + } + } + this.endCycle = 0; + }, 1); + } + setTimeout(() => { + this.dmg = 0 + }) + }, + onEnd() { + if (tech.isMutualism && this.isMutualismActive && !tech.isEnergyHealth) { + m.health += 0.02 + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + } + }, + gravity: 0.002 + 0.002 * tech.isSporeFollow, + do() { + this.force.y += this.gravity * this.mass + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //if on the ground and not on jump cooldown // + this.cd = simulation.cycle + this.delay; + this.lockedOn = null; //find a target + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + !mob[i].isInvulnerable && + mob[i].alive && + this.position.y - mob[i].position.y < 1500 && //this is about how high fleas can jump with capMaxY = 0.12 + 0.04 * Math.random() + this.position.y - mob[i].position.y > -300 && //not too far below the flea (note that fleas should be on the ground most of the time when doing this check) + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + if (tech.isSporeFollow && !this.lockedOn && Matter.Query.ray(map, this.position, m.pos).length === 0) { + this.lockedOn = { //make target player if there are no mobs to target + position: m.pos, + velocity: { + x: 0, + y: 0 + } + } + } + if (this.lockedOn) { //hop towards mob target + const where = Vector.add(this.lockedOn.position, Vector.mult(this.lockedOn.velocity, 5)) //estimate where the mob will be in 5 cycles + const Dy = Math.max(0, this.position.y - where.y) //can't be negative because you can't hop down + const Dx = this.position.x - where.x + const Vx = -0.06 * Dx / Math.sqrt(2 * Dy / this.gravity) //calibrated to hit target, don't mess with this + const Vy = 0.085 * Math.sqrt(this.gravity * Dy) //calibrated to hit target, don't mess with this + const capX = 0.07 + 0.02 * tech.isSporeFollow + const capMaxY = 0.12 + 0.04 * Math.random() + 0.05 * tech.isSporeFollow + const capMinY = closeDist > 500 ? 0.05 + 0.02 * Math.random() : 0.02 + 0.01 * Math.random() //don't jump super low, unless you are very close to mob target + this.force.x = Math.max(-capX, Math.min(capX, Vx)) * this.mass; + this.force.y = -Math.max(capMinY, Math.min(capMaxY, Vy)) * this.mass + } else { //random hops + if (Math.random() < 0.5) { //chance to continue in the same horizontal direction + this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (this.velocity.x > 0 ? 1 : -1); //random move + } else { + this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move + } + this.force.y = -(0.03 + 0.08 * Math.random()) * this.mass + } + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + this.setDamage() //after jumping damage is no longer zero + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], velocity); + if (tech.isMutualism && m.health > 0.01) { + m.health -= 0.01 + m.displayHealth(); + bullet[bullet.length - 1].isMutualismActive = true + } + }, + delayDrones(where, droneCount = 1) { + let respawnDrones = () => { + if (droneCount > 0) { + requestAnimationFrame(respawnDrones); + if (!simulation.paused && !simulation.isChoosing && m.alive) { + droneCount-- + if (tech.isDroneRadioactive) { + b.droneRadioactive({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0) + } else { + b.drone({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0) + if (tech.isDroneGrab && deliveryCount > 0) { + const who = bullet[bullet.length - 1] + who.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(who, SCALE, SCALE); + who.lookFrequency = 30 + Math.floor(11 * Math.random()); + who.endCycle += 3000 * tech.droneCycleReduction * tech.bulletsLastLonger + deliveryCount-- + } + } + } + } + } + requestAnimationFrame(respawnDrones); + }, + drone(where = { + x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5) + }, speed = 1) { + const me = bullet.length; + const THRUST = 0.0015 + const dir = m.angle + 0.2 * (Math.random() - 0.5); + const RADIUS = (4.5 + 3 * Math.random()) + bullet[me] = Bodies.polygon(where.x, where.y, 8, RADIUS, { + angle: dir, + inertia: Infinity, + friction: 0.05, + frictionAir: 0, + restitution: 1, + density: 0.0005, // 0.001 is normal density + dmg: 0.34 + 0.12 * tech.isDroneTeleport + 0.15 * tech.isDroneFastLook, //damage done in addition to the damage from momentum + lookFrequency: (tech.isDroneFastLook ? 20 : 70) + Math.floor(17 * Math.random()), + endCycle: simulation.cycle + Math.floor((950 + 400 * Math.random()) * tech.bulletsLastLonger * tech.droneCycleReduction) + 5 * RADIUS + Math.max(0, 150 - bullet.length), + classType: "bullet", + isDrone: true, + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide + }, + minDmgSpeed: 0, + lockedOn: null, + deathCycles: 110 + RADIUS * 5, + isImproved: false, + beforeDmg(who) { + if (tech.isIncendiary && simulation.cycle + this.deathCycles < this.endCycle && !tech.isForeverDrones) { + const max = Math.max(Math.min(this.endCycle - simulation.cycle - this.deathCycles, 1500), 0) + b.explosion(this.position, max * 0.1 + this.isImproved * 110 + 60 * Math.random()); //makes bullet do explosive damage at end + if (tech.isForeverDrones) { + this.endCycle = 0 + b.drone({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } else { + this.endCycle -= max + } + } else { + //move away from target after hitting + const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) + Matter.Body.setVelocity(this, { x: unit.x, y: unit.y }); + this.lockedOn = null + if (this.endCycle > simulation.cycle + this.deathCycles) { + this.endCycle -= 60 + if (simulation.cycle + this.deathCycles > this.endCycle) this.endCycle = simulation.cycle + this.deathCycles + } + } + }, + onEnd() { + if (tech.isDroneRespawn) { + //are there any nearby bodies nearby that aren't blocked by map? + const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass) + if (canSee.length) { + //find the closest body to the drone from the canSee array + const found = canSee.reduce((a, b) => { + const distA = Vector.magnitude(Vector.sub(this.position, a.position)) + const distB = Vector.magnitude(Vector.sub(this.position, b.position)) + return distA < distB ? a : b + }) + if (found && m.energy > 0.05) { + m.energy -= 0.05 + //remove the body and spawn a new drone + Composite.remove(engine.world, found) + body.splice(body.indexOf(found), 1) + b.delayDrones(found.position, 0.7 * Math.sqrt(found.mass)) + //draw a line from the drone to the body on the canvas + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(found.position.x, found.position.y); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + + //animate the block fading away + simulation.ephemera.push({ + name: "droneRespawn", + count: 60, //cycles before it self removes + do() { + this.count-- + if (this.count < 0) simulation.removeEphemera(this.name) + ctx.beginPath(); + let vertices = found.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 2; + ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})` + ctx.stroke(); + }, + }) + } + } + } + }, + doRespawning() { //fall shrink and die + const scale = 0.995; + Matter.Body.scale(this, scale, scale); + if (this.bodyTarget) { + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.bodyTarget.position)), -this.mass * THRUST) + } else { + this.force.y += this.mass * 0.0012; + } + }, + doDieing() { //fall shrink and die + this.force.y += this.mass * 0.0012; + const scale = 0.995; + Matter.Body.scale(this, scale, scale); + }, + do() { + if (simulation.cycle + this.deathCycles > this.endCycle) { + this.restitution = 0.2; + if (tech.isDroneRespawn) { + this.do = this.doRespawning + //make a list of all elements of array body that a ray can be drawn to from the drone + const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable) + if (canSee.length) { + //find the closest body to the drone from the canSee array + const found = canSee.reduce((a, b) => { + const distA = Vector.magnitude(Vector.sub(this.position, a.position)) + const distB = Vector.magnitude(Vector.sub(this.position, b.position)) + return distA < distB ? a : b + }) + if (found) this.bodyTarget = found + } + } else { + this.do = this.doDieing + } + } + + this.force.y += this.mass * 0.0002; + if (!(simulation.cycle % this.lookFrequency)) { + //find mob targets + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR) + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + //blink towards mobs + if (tech.isDroneTeleport && this.lockedOn) { + const sub = Vector.sub(this.lockedOn.position, this.position); + const distMag = Vector.magnitude(sub); + const unit = Vector.normalise(sub) + Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5))); + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10))); + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = RADIUS * 2; + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + //power ups + if (!this.isImproved && !simulation.isChoosing) { + if (this.lockedOn) { + for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up + if ( + Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && + (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isSuperDeterminism) + // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + ) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 3000 * tech.droneCycleReduction * tech.bulletsLastLonger + } + break; + } + } + } else { + //look for power ups to lock onto + let closeDist = Infinity; + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isSuperDeterminism) + // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + ) { + if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 3000 * tech.droneCycleReduction * tech.bulletsLastLonger + // this.frictionAir = 0 + } + break; + } + //look for power ups to lock onto + if ( + Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && + Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = powerUp[i] + } + } + } + } + } + } + } + if (this.lockedOn) { //accelerate towards mobs + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + } else { //accelerate towards mouse + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST) + } + // speed cap instead of friction to give more agility + if (this.speed > 6) { + Matter.Body.setVelocity(this, { x: this.velocity.x * 0.97, y: this.velocity.y * 0.97 }); + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { x: speed * Math.cos(dir), y: speed * Math.sin(dir) }); + }, + droneRadioactive(where = { + x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5) + }, speed = 1) { + const me = bullet.length; + const THRUST = (tech.isFastDrones ? 0.003 : 0.0012) + 0.0005 * (Math.random() - 0.5) + const dir = m.angle + 0.4 * (Math.random() - 0.5); + const RADIUS = 3 + bullet[me] = Bodies.polygon(where.x, where.y, 8, RADIUS, { + angle: dir, + inertia: Infinity, + friction: 0, + frictionAir: 0, + restitution: 0.4 + 0.199 * Math.random(), + dmg: 0, //0.24 damage done in addition to the damage from momentum and radiation + lookFrequency: 120 + Math.floor(23 * Math.random()), + endCycle: simulation.cycle + Math.floor((900 + 110 * Math.random()) * tech.bulletsLastLonger / tech.droneRadioDamage) + 5 * RADIUS + Math.max(0, 150 - 2 * bullet.length), + classType: "bullet", + isDrone: true, + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide + }, + minDmgSpeed: 0, + speedCap: 5 + 2 * Math.random(), //6 is normal + lockedOn: null, + deathCycles: 110 + RADIUS * 5, + isImproved: false, + radioRadius: 0, + maxRadioRadius: 270 + Math.floor(90 * Math.random()), + beforeDmg() { }, + onEnd() { + if (tech.isDroneRespawn) { + //are there any nearby bodies nearby that aren't blocked by map? + const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass) + if (canSee.length) { + //find the closest body to the drone from the canSee array + const found = canSee.reduce((a, b) => { + const distA = Vector.magnitude(Vector.sub(this.position, a.position)) + const distB = Vector.magnitude(Vector.sub(this.position, b.position)) + return distA < distB ? a : b + }) + if (found && m.energy > 0.05) { + m.energy -= 0.1 + //remove the body and spawn a new drone + Composite.remove(engine.world, found) + body.splice(body.indexOf(found), 1) + b.delayDrones(found.position, 0.35 * Math.sqrt(found.mass)) + //draw a line from the drone to the body on the canvas + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(found.position.x, found.position.y); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + + //animate the block fading away + simulation.ephemera.push({ + name: "droneRespawn", + count: 60, //cycles before it self removes + do() { + this.count-- + if (this.count < 0) simulation.removeEphemera(this.name) + ctx.beginPath(); + let vertices = found.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 2; + ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})` + ctx.stroke(); + }, + }) + } + } + } + }, + do() { + //radioactive zone + this.radioRadius = this.radioRadius * 0.993 + 0.007 * this.maxRadioRadius //smooth radius towards max + //aoe damage to player + if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.radioRadius) { + const DRAIN = tech.isRadioactiveResistance ? 0.001 : 0.004 + if (m.energy > DRAIN) { + if (m.immuneCycle < m.cycle) m.energy -= DRAIN + } else { + m.energy = 0; + if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00005 : 0.0002) * tech.radioactiveDamage) //0.00015 + } + } + //aoe damage to mobs + let dmg = (0.12 + 0.04 * tech.isFastDrones) * m.dmgScale * tech.droneRadioDamage * tech.radioactiveDamage + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.radioRadius + mob[i].radius) { + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way + mob[i].damage(mob[i].shield ? dmg * 3 : dmg); + mob[i].locatePlayer(); + } + } + //draw + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radioRadius, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "lighter" + // ctx.fillStyle = `rgba(25,139,170,${0.15+0.05*Math.random()})`; + // ctx.fillStyle = `rgba(36, 207, 255,${0.1+0.05*Math.random()})`; + ctx.fillStyle = `rgba(28, 175, 217,${0.13 + 0.07 * Math.random()})`; + ctx.fill(); + ctx.globalCompositeOperation = "source-over" + + //normal drone actions + if (simulation.cycle + this.deathCycles > this.endCycle) { //fall shrink and die + this.force.y += this.mass * 0.0012; + this.restitution = 0.2; + const scale = 0.995; + Matter.Body.scale(this, scale, scale); + this.maxRadioRadius = 0 + this.radioRadius = this.radioRadius * 0.98 //let radioactivity decrease + } else { + this.force.y += this.mass * 0.0002; //gravity + + if (!(simulation.cycle % this.lookFrequency)) { + //find mob targets + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + //power ups + if (!this.isImproved && !simulation.isChoosing) { + if (this.lockedOn) { + //grab, but don't lock onto nearby power up + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && + (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isSuperDeterminism) + // &&(powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + ) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 1000 * tech.bulletsLastLonger + this.maxRadioRadius *= 1.25 + } + break; + } + } + } else { + //look for power ups to lock onto + let closeDist = Infinity; + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isSuperDeterminism) + // &&(powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + ) { + if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 1000 * tech.bulletsLastLonger + this.maxRadioRadius *= 1.25 + } + break; + } + //look for power ups to lock onto + if ( + Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && + Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = powerUp[i] + } + } + } + } + } + } + } + if (this.lockedOn) { //accelerate towards mobs + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + } else { //accelerate towards mouse + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST) + } + // speed cap instead of friction to give more agility + if (this.speed > this.speedCap) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.97, + y: this.velocity.y * 0.97 + }); + } + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }); + }, + superBall(where, velocity, radius) { + let dir = m.angle + const me = bullet.length; + bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false)); + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], velocity); + bullet[me].calcDensity = () => 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport + Matter.Body.setDensity(bullet[me], bullet[me].calcDensity()); + bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random()); + bullet[me].minDmgSpeed = 0; + bullet[me].restitution = 1; + bullet[me].frictionAir = 0; + bullet[me].friction = 0; + bullet[me].frictionStatic = 0; + if (tech.isSuperHarm) { + bullet[me].collidePlayerDo = function () { + this.force.y += this.mass * 0.001; + if (Matter.Query.collides(this, [player]).length) { + this.endCycle = 0 + m.energy -= m.energy * 0.25 + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: radius, + color: "#0ad", + time: 15 + }); + } + } + bullet[me].cycle = 0 + bullet[me].do = function () { + this.cycle++ + if (this.cycle > 2) this.do = this.collidePlayerDo + this.force.y += this.mass * 0.001; + }; + } else if (tech.isBulletTeleport) { + bullet[me].portFrequency = 25 + Math.floor(10 * Math.random()) + bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency + bullet[me].do = function () { + this.force.y += this.mass * 0.001; + if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport + this.nextPortCycle = simulation.cycle + this.portFrequency + const range = 33 * Math.sqrt(radius) * Math.random() + Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(this, Vector.rotate(this.velocity, 2 * (Math.random() * Math.random() - 0.25))) + } + }; + } else { + bullet[me].do = function () { + this.force.y += this.mass * 0.001; + }; + } + bullet[me].beforeDmg = function (who) { + if (tech.oneSuperBall) mobs.statusStun(who, 120) // (2.3) * 2 / 14 ticks (2x damage over 7 seconds) + if (tech.isFoamBall) { + for (let i = 0, len = 5 * this.mass; i < len; i++) { + const radius = 5 + 8 * Math.random() + const velocity = { x: Math.max(0.5, 2 - radius * 0.1), y: 0 } + b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) + } + this.endCycle = 0 + } + if (tech.isIncendiary) { + b.explosion(this.position, this.mass * 280); //makes bullet do explosive damage at end + this.endCycle = 0 + } else if (tech.isSuperBounce) { + const cycle = () => { + Matter.Body.setDensity(bullet[me], bullet[me].calcDensity() * 1.33);//33% more density and damage + this.endCycle = simulation.cycle + Math.floor(300 + 90 * Math.random()); //reset to full duration of time + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), 60)); //reset to high velocity + + let count = 5 + const wait = () => { + count-- + if (count > 0) requestAnimationFrame(wait); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: radius, + color: 'rgba(255, 0, 0, 0.33)', + time: 8 + }); + } + requestAnimationFrame(wait); + + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: radius, + color: 'rgba(255, 0, 0, 0.33)', + time: 8 + }); + } + requestAnimationFrame(cycle); + } + }; + }, + targetedBall(position, num = 1, speed = 42 + 12 * Math.random(), range = 1200, isRandomAim = true) { + let shotsFired = 0 + const targets = [] //target nearby mobs + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitude(Vector.sub(position, mob[i].position)); + if ( + dist < range + mob[i].radius && + !mob[i].isBadTarget && + Matter.Query.ray(map, position, mob[i].position).length === 0 && + Matter.Query.ray(body, position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, dist / 60))) //predict where the mob will be in a few cycles + } + } + const radius = (11 + 9 * tech.oneSuperBall) * tech.bulletSize + for (let i = 0; i < num; i++) { + if (targets.length > 0) { // aim near a random target in array + const index = Math.floor(Math.random() * targets.length) + const SPREAD = 160 / targets.length + const WHERE = { + x: targets[index].x + SPREAD * (Math.random() - 0.5), + y: targets[index].y + SPREAD * (Math.random() - 0.5) + } + b.superBall(position, Vector.mult(Vector.normalise(Vector.sub(WHERE, position)), speed), radius) + shotsFired++ + } else if (isRandomAim) { // aim in random direction + const ANGLE = 2 * Math.PI * Math.random() + b.superBall(position, { x: speed * Math.cos(ANGLE), y: speed * Math.sin(ANGLE) }, radius) + } + } + return shotsFired + }, + targetedFoam(position, num = 1, speed = 21 + 7 * Math.random(), range = 1200, isRandomAim = true) { + let shotsFired = 0 + const targets = [] //target nearby mobs + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitude(Vector.sub(position, mob[i].position)); + if ( + dist < range + mob[i].radius && + !mob[i].isBadTarget && //|| mob[i].isMobBullet + Matter.Query.ray(map, position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, dist / 60))) //predict where the mob will be in a few cycles + } + } + for (let i = 0; i < num; i++) { + if (targets.length > 0) { // aim near a random target in array + const SPREAD = 160 / targets.length + const index = Math.floor(Math.random() * targets.length) + const radius = 11 + 12 * Math.random() + const where = { + x: targets[index].x + SPREAD * (Math.random() - 0.5), + y: targets[index].y + SPREAD * (Math.random() - 0.5) + } + b.foam(position, Vector.mult(Vector.normalise(Vector.sub(where, position)), speed - radius * 0.25), radius) + shotsFired++ + } else if (isRandomAim) { // aim in random direction + const ANGLE = 2 * Math.PI * Math.random() + b.foam(position, { x: speed * Math.cos(ANGLE), y: speed * Math.sin(ANGLE) }, 8 + 11 * Math.random()) + } + } + return shotsFired + }, + // plasmaBall(position, velocity, radius) { + // // radius *= Math.sqrt(tech.bulletSize) + // const me = bullet.length; + // bullet[me] = Bodies.polygon(position.x, position.y, 20, radius, { + // density: 0.000001, // 0.001 is normal density + // inertia: Infinity, + // frictionAir: 0.003, + // dmg: 0, //damage on impact + // damage: 0, //damage done over time + // scale: 1 - 0.006 / tech.bulletsLastLonger, + // classType: "bullet", + // collisionFilter: { + // category: cat.bullet, + // mask: 0 //cat.mob | cat.mobBullet // cat.map | cat.body | cat.mob | cat.mobShield + // }, + // minDmgSpeed: 0, + // endCycle: Infinity, + // count: 0, + // radius: radius, + // portFrequency: 5 + Math.floor(5 * Math.random()), + // nextPortCycle: Infinity, //disabled unless you have the teleport tech + // beforeDmg(who) { + // if (!this.target && who.alive) { + // this.target = who; + // if (who.radius < 20) { + // this.targetRelativePosition = { + // x: 0, + // y: 0 + // } //find relative position vector for zero mob rotation + // } else if (Matter.Query.collides(this, [who]).length > 0) { + // const normal = Matter.Query.collides(this, [who])[0].normal + // this.targetRelativePosition = Vector.rotate(Vector.sub(Vector.sub(this.position, who.position), Vector.mult(normal, -this.radius)), -who.angle) //find relative position vector for zero mob rotation + // } else { + // this.targetRelativePosition = Vector.rotate(Vector.sub(this.position, who.position), -who.angle) //find relative position vector for zero mob rotation + // } + // this.collisionFilter.category = cat.body; + // this.collisionFilter.mask = null; + + // let bestVertexDistance = Infinity + // let bestVertex = null + // for (let i = 0; i < this.target.vertices.length; i++) { + // const dist = Vector.magnitude(Vector.sub(this.position, this.target.vertices[i])); + // if (dist < bestVertexDistance) { + // bestVertex = i + // bestVertexDistance = dist + // } + // } + // this.targetVertex = bestVertex + // } + // }, + // onEnd() {}, + // do() { + // if (this.count < 20) { + // this.count++ + // //grow + // const SCALE = 1.06 + // Matter.Body.scale(this, SCALE, SCALE); + // this.radius *= SCALE; + // } else { + // //shrink + // Matter.Body.scale(this, this.scale, this.scale); + // this.radius *= this.scale; + // if (this.radius < 8) this.endCycle = 0; + // } + // if (this.target && this.target.alive) { //if stuck to a target + // const rotate = Vector.rotate(this.targetRelativePosition, this.target.angle) //add in the mob's new angle to the relative position vector + // if (this.target.isVerticesChange) { + // Matter.Body.setPosition(this, this.target.vertices[this.targetVertex]) + // } else { + // Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.target.velocity), this.target.position)) + // } + // if (this.target.isBoss) { + // if (this.target.speed > 8) Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.98)) + // } else { + // if (this.target.speed > 4) Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.95)) + // } + + // Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9); + // // Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9) + // if (this.target.isShielded) { + // this.target.damage(m.dmgScale * this.damage, true); //shield damage bypass + // const SCALE = 1 - 0.004 / tech.bulletsLastLonger //shrink if mob is shielded + // Matter.Body.scale(this, SCALE, SCALE); + // this.radius *= SCALE; + // } else { + // this.target.damage(m.dmgScale * this.damage); + // } + // } else if (this.target !== null) { //look for a new target + // this.collisionFilter.category = cat.bullet; + // this.collisionFilter.mask = cat.mob //| cat.mobShield //cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + // if (tech.isSpawnBulletsOnDeath && bullet.length < 180 && !this.target.isMobBullet) { + // let targets = [] + // for (let i = 0, len = mob.length; i < len; i++) { + // const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + // if (dist < 1000000) targets.push(mob[i]) + // } + // const radius = Math.min(this.radius * 0.5, 9) + // const len = bullet.length < 80 ? 2 : 1 + // for (let i = 0; i < len; i++) { + // if (targets.length - i > 0) { + // const index = Math.floor(Math.random() * targets.length) + // const speed = 6 + 6 * Math.random() + // const velocity = Vector.mult(Vector.normalise(Vector.sub(targets[index].position, this.position)), speed) + // b.foam(this.position, Vector.rotate(velocity, 0.5 * (Math.random() - 0.5)), radius) + // } else { + // b.foam(this.position, Vector.rotate({ + // x: 15 + 10 * Math.random(), + // y: 0 + // }, 2 * Math.PI * Math.random()), radius) + // } + // } + // } + // this.target = null + // } else if (Matter.Query.point(map, this.position).length > 0) { //slow when touching map or blocks + // const slow = 0.85 + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * slow, + // y: this.velocity.y * slow + // }); + // const SCALE = 0.96 + // Matter.Body.scale(this, SCALE, SCALE); + // this.radius *= SCALE; + // // } else if (Matter.Query.collides(this, body).length > 0) { + // } else if (Matter.Query.point(body, this.position).length > 0) { + // const slow = 0.9 + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * slow, + // y: this.velocity.y * slow + // }); + // const SCALE = 0.96 + // Matter.Body.scale(this, SCALE, SCALE); + // this.radius *= SCALE; + // } else { + // this.force.y += this.mass * tech.foamGravity; //gravity + // if (tech.isFoamAttract) { + // for (let i = 0, len = mob.length; i < len; i++) { + // if (!mob[i].isBadTarget && Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 && mob[i].alive && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + // this.force = Vector.mult(Vector.normalise(Vector.sub(mob[i].position, this.position)), this.mass * 0.004) + // const slow = 0.9 + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * slow, + // y: this.velocity.y * slow + // }); + // break + // } + // } + // } + // } + // if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport + // this.nextPortCycle = simulation.cycle + this.portFrequency + // const range = 15 * Math.sqrt(this.radius) * Math.random() + // Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random()))) + // } + // } + // }); + // if (tech.isBulletTeleport) bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency + // Composite.add(engine.world, bullet[me]); //add bullet to world + // Matter.Body.setVelocity(bullet[me], velocity); + // }, + foam(position, velocity, radius) { + if (tech.isFoamCavitation && Math.random() < 0.25) { + velocity = Vector.mult(velocity, 1.35) + radius = 1.2 * radius + 13 + } + // radius *= Math.sqrt(tech.bulletSize) + const me = bullet.length; + bullet[me] = Bodies.polygon(position.x, position.y, 20, radius, { + density: 0.000001, // 0.001 is normal density + inertia: Infinity, + frictionAir: 0.003, + dmg: 0, //damage on impact + damage: tech.foamDamage * (tech.isFastFoam ? 2.8 : 1) * (tech.isBulletTeleport ? 1.53 : 1), //damage done over time + scale: 1 - 0.006 / tech.bulletsLastLonger * (tech.isFastFoam ? 1.65 : 1), + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.mob | cat.mobBullet // cat.map | cat.body | cat.mob | cat.mobShield + }, + minDmgSpeed: 0, + endCycle: Infinity, + count: 0, + radius: radius, + target: null, + targetVertex: null, + targetRelativePosition: null, + portFrequency: 7 + Math.floor(5 * Math.random()), + nextPortCycle: Infinity, //disabled unless you have the teleport tech + beforeDmg(who) { + if (!this.target && who.alive) { + this.target = who; + if (who.radius < 20) { + this.targetRelativePosition = { + x: 0, + y: 0 + } //find relative position vector for zero mob rotation + } else if (Matter.Query.collides(this, [who]).length > 0) { + const normal = Matter.Query.collides(this, [who])[0].normal + this.targetRelativePosition = Vector.rotate(Vector.sub(Vector.sub(this.position, who.position), Vector.mult(normal, -this.radius)), -who.angle) //find relative position vector for zero mob rotation + } else { + this.targetRelativePosition = Vector.rotate(Vector.sub(this.position, who.position), -who.angle) //find relative position vector for zero mob rotation + } + this.collisionFilter.category = cat.body; + this.collisionFilter.mask = null; + + let bestVertexDistance = Infinity + let bestVertex = null + for (let i = 0; i < this.target.vertices.length; i++) { + const dist = Vector.magnitude(Vector.sub(this.position, this.target.vertices[i])); + if (dist < bestVertexDistance) { + bestVertex = i + bestVertexDistance = dist + } + } + this.targetVertex = bestVertex + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + } + }, + onEnd() { }, + do() { + if (this.count < 20) { + this.count++ + //grow + const SCALE = 1.06 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + //shrink + Matter.Body.scale(this, this.scale, this.scale); + this.radius *= this.scale; + if (this.radius < 8) this.endCycle = 0; + } + if (this.target && this.target.alive) { //if stuck to a target + const rotate = Vector.rotate(this.targetRelativePosition, this.target.angle) //add in the mob's new angle to the relative position vector + if (this.target.isVerticesChange) { + Matter.Body.setPosition(this, this.target.vertices[this.targetVertex]) + } else { + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.target.velocity), this.target.position)) + } + if (this.target.isBoss) { + if (this.target.speed > 6.5) Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.975)) + } else { + if (this.target.speed > 2.5) Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.94)) + } + + Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9); + // Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9) + if (this.target.isShielded) { + this.target.damage(m.dmgScale * this.damage, true); //shield damage bypass + const SCALE = 1 - 0.004 / tech.bulletsLastLonger //shrink if mob is shielded + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + this.target.damage(m.dmgScale * this.damage); + } + } else if (this.target !== null) { //look for a new target + this.collisionFilter.category = cat.bullet; + this.collisionFilter.mask = cat.mob //| cat.mobShield //cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + Matter.Body.setVelocity(this, { + x: this.target.velocity.x, + y: this.target.velocity.y + }); + if (tech.isSpawnBulletsOnDeath && bullet.length < 180 && !this.target.isMobBullet) { + let targets = [] + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if (dist < 1000000) targets.push(mob[i]) + } + const radius = Math.min(this.radius * 0.5, 9) + const len = bullet.length < 80 ? 2 : 1 + for (let i = 0; i < len; i++) { + if (targets.length - i > 0) { + const index = Math.floor(Math.random() * targets.length) + const speed = 6 + 6 * Math.random() + const velocity = Vector.mult(Vector.normalise(Vector.sub(targets[index].position, this.position)), speed) + b.foam(this.position, Vector.rotate(velocity, 0.5 * (Math.random() - 0.5)), radius) + } else { + b.foam(this.position, Vector.rotate({ + x: 15 + 10 * Math.random(), + y: 0 + }, 2 * Math.PI * Math.random()), radius) + } + } + } + this.target = null + } else if (Matter.Query.point(map, this.position).length > 0) { //slow when touching map + const slow = 0.87 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.97 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + // } else if (Matter.Query.collides(this, body).length > 0) { + } else if (Matter.Query.point(body, this.position).length > 0) { //slow when touching blocks + const slow = 0.94 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.99 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + this.force.y += this.mass * tech.foamGravity; //gravity + if (tech.isFoamAttract) { + for (let i = 0, len = mob.length; i < len; i++) { + if ( + !mob[i].isBadTarget && + Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 && + mob[i].alive && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + this.force = Vector.mult(Vector.normalise(Vector.sub(mob[i].position, this.position)), this.mass * 0.004) + const slow = 0.9 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + break + } + } + } + } + if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport + this.nextPortCycle = simulation.cycle + this.portFrequency + const range = 13 * Math.sqrt(this.radius) * Math.random() + Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ + x: range, + y: 0 + }, 2 * Math.PI * Math.random()))) + } + } + }); + if (tech.isBulletTeleport) bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], velocity); + }, + targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + let closestMob, dist + for (let i = 0, len = mob.length; i < len; i++) { + if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { + dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); + if (dist < range && Matter.Query.ray(map, who.position, mob[i].position).length === 0) { //&& Matter.Query.ray(body, position, mob[i].position).length === 0 + closestMob = mob[i] + range = dist + } + } + } + if (closestMob) { + const where = Vector.add(closestMob.position, Vector.mult(closestMob.velocity, dist / 60)) + const velocity = Vector.mult(Vector.normalise(Vector.sub(where, who.position)), speed) + velocity.y -= Math.abs(who.position.x - closestMob.position.x) / 150; //gives an arc, but not a good one + Matter.Body.setVelocity(who, velocity); + } + }, + targetedNail(position, num = 1, speed = 40 + 10 * Math.random(), range = 1200, isRandomAim = true, damage = 1.4) { + let shotsFired = 0 + const targets = [] //target nearby mobs + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitude(Vector.sub(position, mob[i].position)); + if ( + dist < range + mob[i].radius && + !mob[i].isBadTarget && //|| mob[i].isMobBullet + Matter.Query.ray(map, position, mob[i].position).length === 0 && + Matter.Query.ray(body, position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, dist / 60))) //predict where the mob will be in a few cycles + } + } + for (let i = 0; i < num; i++) { + if (targets.length > 0) { // aim near a random target in array + const index = Math.floor(Math.random() * targets.length) + const SPREAD = 150 / targets.length + const WHERE = { + x: targets[index].x + SPREAD * (Math.random() - 0.5), + y: targets[index].y + SPREAD * (Math.random() - 0.5) + } + b.nail(position, Vector.mult(Vector.normalise(Vector.sub(WHERE, position)), speed), damage) + shotsFired++ + } else if (isRandomAim) { // aim in random direction + const ANGLE = 2 * Math.PI * Math.random() + b.nail(position, { + x: speed * Math.cos(ANGLE), + y: speed * Math.sin(ANGLE) + }, damage) + shotsFired++ + } + } + return shotsFired + }, + crit(mob, bullet) { + if (!mob.shield && Vector.dot(Vector.normalise(Vector.sub(mob.position, bullet.position)), Vector.normalise(bullet.velocity)) > 0.999 - 1 / mob.radius) { + if (mob.isFinalBoss && !(Vector.dot(Vector.normalise(Vector.sub(mob.position, bullet.position)), Vector.normalise(bullet.velocity)) > 0.999999)) return + let cycle = () => { //makes this run after damage + if (mob.health < 0.5 && mob.damageReduction > 0 && mob.alive) { + // mob.death(); + // mob.damage(this.health * Math.sqrt(this.mass) / this.damageReduction); + mob.damage(Infinity); + + const color = 'rgb(255,255,255)' + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 1.2, + color: color, //"rgba(0,0,0,0.6)", + time: 8 + }); + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 0.75, + color: color, //"rgba(0,0,0,0.85)", + time: 15 + }); + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 0.4, + color: color, //"rgb(0,0,0)", + time: 20 + }); + } + } + requestAnimationFrame(cycle); + } + }, + nail(pos, velocity, dmg = 1) { + dmg *= tech.bulletSize + const me = bullet.length; + bullet[me] = Bodies.rectangle(pos.x, pos.y, 25 * tech.bulletSize, 2 * tech.bulletSize, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); + Matter.Body.setVelocity(bullet[me], velocity); + Composite.add(engine.world, bullet[me]); //add bullet to world + bullet[me].endCycle = simulation.cycle + 60 + 18 * Math.random(); + bullet[me].dmg = tech.isNailRadiation ? 0 : dmg + bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech + if (tech.isNailRadiation) mobs.statusDoT(who, dmg * (tech.isFastRadiation ? 1.3 : 0.44), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (tech.isNailCrit) { //makes bullet do explosive damage if it hits center + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 80 + 90 * (b.activeGun === 0) + 30 * Math.random()); //larger explosions for human aimed nail gun, smaller for auto aimed sources, like bots, and mine + } + } + this.ricochet(who) + }; + bullet[me].ricochet = function (who) { //use for normal nails, and ice crystal nails + if (tech.isRicochet) { + const targets = [] //target nearby mobs + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitude(Vector.sub(this.position, mob[i].position)); + if ( + mob[i] !== who && + dist < 2500 + mob[i].radius && + !mob[i].isBadTarget && //|| mob[i].isMobBullet + !mob[i].isInvulnerable && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 + ) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, dist / 60))) //predict where the mob will be in a few cycles + } + } + if (targets.length > 0) { // aim near a random target in array + const index = Math.floor(Math.random() * targets.length) + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(targets[index], this.position)), 45)); + Matter.Body.setAngle(this, Math.atan2(this.velocity.y, this.velocity.x)) + Matter.Body.setAngularVelocity(this, 0); + } + this.dmg += 2 + } + } + bullet[me].do = function () { }; + }, + needle(angle = m.angle) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(m.pos.x + 40 * Math.cos(m.angle), m.pos.y + 40 * Math.sin(m.angle), 75 * tech.bulletSize, 0.75 * tech.bulletSize, b.fireAttributes(angle)); + Matter.Body.setDensity(bullet[me], 0.00001); //0.001 is normal + bullet[me].immuneList = [] + bullet[me].dmg = 6 + if (tech.needleTunnel) { + bullet[me].dmg *= 1.2 + bullet[me].endCycle = simulation.cycle + 300; + bullet[me].collisionFilter.mask = tech.isShieldPierce ? 0 : cat.mobShield + // bullet[me].turnRate = 0.005 * (Math.random() - 0.5) + bullet[me].isInMap = false + bullet[me].do = function () { + const whom = Matter.Query.collides(this, mob) + if (whom.length && this.speed > 20) { //if touching a mob + for (let i = 0, len = whom.length; i < len; i++) { + who = whom[i].bodyA + if (who && who.mob) { + let immune = false + for (let i = 0; i < this.immuneList.length; i++) { //check if this needle has hit this mob already + if (this.immuneList[i] === who.id) { + immune = true + break + } + } + if (!immune) { + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 220 + 50 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + + this.immuneList.push(who.id) //remember that this needle has hit this mob once already + let dmg = this.dmg * tech.bulletSize * m.dmgScale + if (tech.isNailRadiation) { + mobs.statusDoT(who, (tech.isFastRadiation ? 6 : 2) * tech.bulletSize, tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + dmg *= 0.25 + } + if (tech.isCrit && who.isStunned) dmg *= 4 + who.damage(dmg, tech.isShieldPierce); + if (who.alive) who.foundPlayer(); + if (who.damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.log(dmg + 1.1) * 40 * who.damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + } + } + } + } else if (Matter.Query.collides(this, map).length) { //penetrate walls + if (!this.isInMap) { //turn after entering the map, but only turn once + this.isInMap = true + Matter.Body.setVelocity(this, Vector.rotate(this.velocity, 0.25 * (Math.random() - 0.5))); + Matter.Body.setAngle(this, Math.atan2(this.velocity.y, this.velocity.x)) + } + Matter.Body.setPosition(this, Vector.add(this.position, Vector.mult(this.velocity, -0.98))) //move back 1/2 your velocity = moving at 1/2 speed + } else if (Matter.Query.collides(this, body).length) { //penetrate blocks + Matter.Body.setAngularVelocity(this, 0) + Matter.Body.setPosition(this, Vector.add(this.position, Vector.mult(this.velocity, -0.94))) //move back 1/2 your velocity = moving at 1/2 speed + } else if (this.speed < 30) { + this.force.y += this.mass * 0.001; //no gravity until it slows down to improve aiming + } + }; + } else { + bullet[me].endCycle = simulation.cycle + 100; + bullet[me].collisionFilter.mask = tech.isShieldPierce ? cat.body : cat.body | cat.mobShield + bullet[me].do = function () { + const whom = Matter.Query.collides(this, mob) + if (whom.length && this.speed > 20) { //if touching a mob + for (let i = 0, len = whom.length; i < len; i++) { + who = whom[i].bodyA + if (who && who.mob) { + let immune = false + for (let i = 0; i < this.immuneList.length; i++) { //check if this needle has hit this mob already + if (this.immuneList[i] === who.id) { + immune = true + break + } + } + if (!immune) { + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 220 + 50 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + + this.immuneList.push(who.id) //remember that this needle has hit this mob once already + let dmg = this.dmg * tech.bulletSize * m.dmgScale + if (tech.isNailRadiation) { + mobs.statusDoT(who, (tech.isFastRadiation ? 6 : 2) * tech.bulletSize, tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + dmg *= 0.25 + } + if (tech.isCrit && who.isStunned) dmg *= 4 + who.damage(dmg, tech.isShieldPierce); + if (who.alive) who.foundPlayer(); + if (who.damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.log(dmg + 1.1) * 40 * who.damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + } + } + } + } else if (Matter.Query.collides(this, map).length) { //stick in walls + this.collisionFilter.mask = 0; + Matter.Body.setAngularVelocity(this, 0) + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + this.do = function () { + if (!Matter.Query.collides(this, map).length) this.force.y += this.mass * 0.001; + } + if (tech.isNeedleIce) { + b.iceIX(5 + 5 * Math.random(), 2 * Math.PI * Math.random(), this.position) // iceIX(speed = 0, dir = m.angle + Math.PI * 2 * Math.random(), where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }) { + if (0.5 < Math.random()) b.iceIX(5 + 5 * Math.random(), 2 * Math.PI * Math.random(), this.position) + } + } else if (this.speed < 30) { + this.force.y += this.mass * 0.001; //no gravity until it slows down to improve aiming + } + }; + } + const SPEED = 90 + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + SPEED * Math.cos(angle), + y: m.Vy / 2 + SPEED * Math.sin(angle) + }); + // Matter.Body.setDensity(bullet[me], 0.00001); + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + // ************************************************************************************************** + // ************************************************************************************************** + // ******************************** Bots ********************************************* + // ************************************************************************************************** + // ************************************************************************************************** + totalBots() { + return tech.dynamoBotCount + tech.foamBotCount + tech.soundBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount + }, + hasBotUpgrade() { + return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade + }, + convertBotsTo(type) { //type can be a string like "dynamoBotCount" + const totalPermanentBots = b.totalBots() + //remove all bots techs and convert them to the new type so that tech refunds work correctly + let totalTechToConvert = 0 //count how many tech need to be converted + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count && tech.tech[i].isBot) { + totalTechToConvert += tech.tech[i].count + tech.removeTech(i) + } + } + //remove all bots + b.zeroBotCount() + b.clearPermanentBots() + for (let i = 0; i < totalTechToConvert; i++) tech.giveTech(type) //spawn tech for the correct bot type + + //find index of new bot type tech effect + let index = null + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === type) { + index = i + break + } + } + for (let i = 0, len = totalPermanentBots - totalTechToConvert; i < len; i++) tech.tech[index].effect(); //also convert any permanent bots that didn't come from a tech + //in experiment mode set the unselect color for bot tech that was converted + // if (build.isExperimentSelection) { } + }, + clearPermanentBots() { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].endCycle === Infinity) bullet[i].endCycle = 0 //remove active bots, but don't remove temp bots + } + }, + removeBot() { + if (tech.nailBotCount > 1) { + tech.nailBotCount-- + return + } + if (tech.laserBotCount > 1) { + tech.laserBotCount-- + return + } + if (tech.foamBotCount > 1) { + tech.foamBotCount-- + return + } + if (tech.soundBotCount > 1) { + tech.soundBotCount-- + return + } + if (tech.boomBotCount > 1) { + tech.boomBotCount-- + return + } + if (tech.orbitBotCount > 1) { + tech.orbitBotCount-- + return + } + if (tech.dynamoBotCount > 1) { + tech.dynamoBotCount-- + return + } + if (tech.missileBotCount > 1) { + tech.missileBotCount-- + return + } + if (tech.plasmaBotCount > 1) { + tech.plasmaBotCount-- + return + } + }, + zeroBotCount() { //remove all bots + tech.dynamoBotCount = 0 + tech.laserBotCount = 0 + tech.nailBotCount = 0 + tech.foamBotCount = 0 + tech.soundBotCount = 0 + tech.boomBotCount = 0 + tech.orbitBotCount = 0 + tech.missileBotCount = 0 + }, + respawnBots() { + for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.laserBotCount; i++) b.laserBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.nailBotCount; i++) b.nailBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.foamBotCount; i++) b.foamBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.soundBotCount; i++) b.soundBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + for (let i = 0; i < tech.missileBotCount; i++) b.missileBot({ + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, false) + if (tech.isIntangible && m.isCloak) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield + } + } + }, + randomBot(where = player.position, isKeep = true, isLaser = true) { + if (Math.random() < 0.5) { //chance to match bot to your upgrade + if (tech.isNailBotUpgrade) { //check for upgrades first + b.nailBot(where, isKeep) + if (isKeep) tech.nailBotCount++; + } else if (tech.isFoamBotUpgrade) { + b.foamBot(where, isKeep) + if (isKeep) tech.foamBotCount++; + } else if (tech.isSoundBotUpgrade) { + b.soundBot(where, isKeep) + if (isKeep) tech.soundBotCount++; + } else if (tech.isBoomBotUpgrade) { + b.boomBot(where, isKeep) + if (isKeep) tech.boomBotCount++; + } else if (tech.isLaserBotUpgrade) { + b.laserBot(where, isKeep) + if (isKeep) tech.laserBotCount++; + } else if (tech.isOrbitBotUpgrade) { + b.orbitBot(where, isKeep); + if (isKeep) tech.orbitBotCount++; + } else if (tech.isDynamoBotUpgrade) { + b.dynamoBot(where, isKeep) + if (isKeep) tech.dynamoBotCount++; + } else if (Math.random() < 0.143) { //random + b.soundBot(where, isKeep) + if (isKeep) tech.soundBotCount++; + } else if (Math.random() < 0.166 && isLaser) { + b.laserBot(where, isKeep) + if (isKeep) tech.laserBotCount++; + } else if (Math.random() < 0.2) { + b.dynamoBot(where, isKeep) + if (isKeep) tech.dynamoBotCount++; + } else if (Math.random() < 0.25) { + b.orbitBot(where, isKeep); + if (isKeep) tech.orbitBotCount++; + } else if (Math.random() < 0.33) { + b.nailBot(where, isKeep) + if (isKeep) tech.nailBotCount++; + } else if (Math.random() < 0.5) { + b.foamBot(where, isKeep) + if (isKeep) tech.foamBotCount++; + } else { + b.boomBot(where, isKeep) + if (isKeep) tech.boomBotCount++; + } + } else { //else don't match bot to upgrade + if (Math.random() < 0.143) { //random + b.soundBot(where, isKeep) + if (isKeep) tech.soundBotCount++; + } else if (Math.random() < 0.166 && isLaser) { //random + b.laserBot(where, isKeep) + if (isKeep) tech.laserBotCount++; + } else if (Math.random() < 0.2) { + b.dynamoBot(where, isKeep) + if (isKeep) tech.dynamoBotCount++; + } else if (Math.random() < 0.25) { + b.orbitBot(where, isKeep); + if (isKeep) tech.orbitBotCount++; + } else if (Math.random() < 0.33) { + b.nailBot(where, isKeep) + if (isKeep) tech.nailBotCount++; + } else if (Math.random() < 0.5) { + b.foamBot(where, isKeep) + if (isKeep) tech.foamBotCount++; + } else { + b.boomBot(where, isKeep) + if (isKeep) tech.boomBotCount++; + } + } + + }, + setDynamoBotDelay() { + //reorder orbital bot positions around a circle + let total = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') total++ + } + let count = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') { + count++ + const step = Math.max(60 - 3 * total, 20) + bullet[i].followDelay = (step * count) % 600 + } + } + }, + dynamoBot(position = player.position, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.dynamoBot()`); + const me = bullet.length; + bullet[me] = Bodies.polygon(position.x, position.y, 5, 10, { + isUpgraded: tech.isDynamoBotUpgrade, + botType: "dynamo", + friction: 0, + frictionStatic: 0, + frictionAir: 0.02, + spin: 0.07 * (Math.random() < 0.5 ? -1 : 1), + // isStatic: true, + isSensor: true, + restitution: 0, + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 0, + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: 0 //cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { + b.setDynamoBotDelay() + }, + followDelay: 0, + phase: Math.floor(60 * Math.random()), + do() { + // if (Vector.magnitude(Vector.sub(this.position, player.position)) < 150) { + // ctx.fillStyle = "rgba(0,0,0,0.06)"; + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, 150, 0, 2 * Math.PI); + // ctx.fill(); + // } + + //check for damage + if (m.immuneCycle < m.cycle && !((m.cycle + this.phase) % 30)) { //twice a second + if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250 && m.immuneCycle < m.cycle) { //give energy + Matter.Body.setAngularVelocity(this, this.spin) + if (this.isUpgraded) { + m.energy += 0.12 + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 10, + color: m.fieldMeterColor, + time: simulation.drawTime + }); + } else { + m.energy += 0.04 + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 5, + color: m.fieldMeterColor, + time: simulation.drawTime + }); + } + } + } + + if (!m.isCloak) { //if cloaking field isn't active + const size = 33 + q = Matter.Query.region(mob, { + min: { + x: this.position.x - size, + y: this.position.y - size + }, + max: { + x: this.position.x + size, + y: this.position.y + size + } + }) + for (let i = 0; i < q.length; i++) { + if (!q[i].isShielded) { + Matter.Body.setAngularVelocity(this, this.spin) + // mobs.statusStun(q[i], 180) + // const dmg = 0.5 * m.dmgScale * (this.isUpgraded ? 2.5 : 1) + const dmg = 0.5 * m.dmgScale + q[i].damage(dmg); + if (q[i].alive) q[i].foundPlayer(); + if (q[i].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + // radius: 600 * dmg * q[i].damageReduction, + radius: Math.sqrt(2000 * dmg * q[i].damageReduction) + 2, + color: 'rgba(0,0,0,0.4)', + time: simulation.drawTime + }); + } + } + } + } + let history = m.history[(m.cycle - this.followDelay) % 600] + Matter.Body.setPosition(this, { + x: history.position.x, + y: history.position.y - history.yOff + 24.2859 + }) //bullets move with player + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + b.setDynamoBotDelay() + }, + nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.nailBot()`); + const me = bullet.length; + const dir = m.angle; + const RADIUS = (12 + 4 * Math.random()) + bullet[me] = Bodies.polygon(position.x, position.y, 4, RADIUS, { + isUpgraded: tech.isNailBotUpgrade, + botType: "nail", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 0.6 * (1 + 0.5 * Math.random()), + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + // lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20, + lastLookCycle: simulation.cycle + 60 * Math.random(), + delay: Math.floor((tech.isNailBotUpgrade ? 18 : 85) * b.fireCDscale), + acceleration: 0.005 * (1 + 0.5 * Math.random()), + range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + beforeDmg() { }, + onEnd() { }, + do() { + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + if (distanceToPlayer > this.range) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) + } else { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + if (this.lastLookCycle < simulation.cycle && !m.isCloak) { + this.lastLookCycle = simulation.cycle + this.delay + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if ( + !mob[i].isBadTarget && + dist < 3000000 && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isShielded && + !mob[i].isInvulnerable + ) { + const unit = Vector.normalise(Vector.sub(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)), this.position)) + if (this.isUpgraded) { + const SPEED = 60 + b.nail(this.position, Vector.mult(unit, SPEED)) + this.force = Vector.mult(unit, -0.02 * this.mass) + } else { + const SPEED = 45 + b.nail(this.position, Vector.mult(unit, SPEED)) + this.force = Vector.mult(unit, -0.012 * this.mass) + } + break; + } + } + } + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + missileBot(position = { + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.missileBot()`); + const me = bullet.length; + bullet[me] = Bodies.rectangle(position.x, position.y, 28, 11, { + botType: "missile", + angle: m.angle, + friction: 0, + frictionStatic: 0, + frictionAir: 0.04, + restitution: 0.7, + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 27 + Math.ceil(6 * Math.random()), + cd: 0, + delay: Math.floor(65 * b.fireCDscale), + range: 70 + 3 * b.totalBots(), + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? (cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield) : (cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield) //if over 50 bots, they no longer collide with each other + }, + beforeDmg() { }, + onEnd() { }, + do() { + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + if (distanceToPlayer > this.range) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * 0.006) + } else { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + if (this.cd < simulation.cycle && !(simulation.cycle % this.lookFrequency) && !m.isCloak) { + for (let i = 0, len = mob.length; i < len; i++) { + const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if ( + mob[i].alive && + !mob[i].isBadTarget && + dist2 > 40000 && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + this.cd = simulation.cycle + this.delay; + const angle = Vector.angle(this.position, mob[i].position) + Matter.Body.setAngle(this, angle) + // Matter.Body.setAngularVelocity(this, 0.025) + this.torque += this.inertia * 0.00004 * (Math.round(Math.random()) ? 1 : -1) + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, mob[i].position)), this.mass * 0.02) + + if (tech.missileCount > 1) { + const countReduction = Math.pow(0.85, tech.missileCount) + const size = 0.9 * Math.sqrt(countReduction) + const direction = { + x: Math.cos(angle), + y: Math.sin(angle) + } + const push = Vector.mult(Vector.perp(direction), 0.015 * countReduction / Math.sqrt(tech.missileCount)) + for (let i = 0; i < tech.missileCount; i++) { + setTimeout(() => { + b.missile(this.position, angle, -8, size) + bullet[bullet.length - 1].force.x += push.x * (i - (tech.missileCount - 1) / 2); + bullet[bullet.length - 1].force.y += push.y * (i - (tech.missileCount - 1) / 2); + }, 40 * tech.missileCount * Math.random()); + } + } else { + b.missile(this.position, angle, -8, 0.9) + } + break; + } + } + } + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + foamBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.foamBot()`); + const me = bullet.length; + const dir = m.angle; + const RADIUS = (10 + 5 * Math.random()) + bullet[me] = Bodies.polygon(position.x, position.y, 6, RADIUS, { + isUpgraded: tech.isFoamBotUpgrade, + botType: "foam", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 0.6 * (1 + 0.5 * Math.random()), + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 60 + Math.floor(17 * Math.random()) - 50 * tech.isFoamBotUpgrade, + cd: 0, + fireCount: 0, + fireLimit: 5 + 2 * tech.isFoamBotUpgrade, + delay: Math.floor((150 + (tech.isFoamBotUpgrade ? 0 : 250)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + acceleration: 0.005 * (1 + 0.5 * Math.random()), + range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), //how far from the player the bot will move + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + beforeDmg() { }, + onEnd() { }, + fireTarget: { x: 0, y: 0 }, + fire() { + this.fireCount++ + if (this.fireCount > this.fireLimit) { + this.fireCount = 0 + this.cd = simulation.cycle + this.delay; + } // else {this.cd = simulation.cycle + 1;} + + const radius = 5 + 3 * Math.random() + const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.fireTarget, this.position)), SPEED) + b.foam(this.position, Vector.rotate(velocity, 0.07 * (Math.random() - 0.5)), radius + 6 * this.isUpgraded) + + //recoil + // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) + const force = Vector.mult(velocity, 0.0001 * this.mass * (tech.isFoamCavitation ? 2 : 1)) + this.force.x -= force.x + this.force.y -= force.y + }, + do() { + if (this.fireCount === 0) { //passive mode: look for targets and following player + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + if (distanceToPlayer > this.range) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) + } else { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + } + + if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) { + for (let i = 0, len = mob.length; i < len; i++) { + const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if (dist2 < 1600000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second + this.fire() + break; + } + } + } + } else { //fire mode: quickly fire at targets and doesn't follow player + this.fire() + } + + + + + + + + + + // const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + // if (distanceToPlayer > this.range) { //if far away move towards player + // this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) + // } else { //close to player + // Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + + // //&& !(simulation.cycle % this.lookFrequency) + // if (this.cd < simulation.cycle && !m.isCloak) { + // let target + // for (let i = 0, len = mob.length; i < len; i++) { + // const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + // if (dist2 < 2000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + + // this.fireCount++ + // if (this.fireCount > 5) { + // this.fireCount = 0 + // this.cd = simulation.cycle + this.delay; + // } else { + // // this.cd = simulation.cycle + 1; + // } + + // target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) + // const radius = 6 + 7 * Math.random() + // const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; + // const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED) + // b.foam(this.position, velocity, radius + 7.5 * this.isUpgraded) + + // //recoil + // // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) + // const force = Vector.mult(velocity, 0.0003 * this.mass * (tech.isFoamCavitation ? 2 : 1)) + // this.force.x -= force.x + // this.force.y -= force.y + // break; + // } + // } + // } + // } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + soundBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.soundBot()`); + const me = bullet.length; + const dir = m.angle; + bullet[me] = Bodies.rectangle(position.x, position.y, 12, 30, { + isUpgraded: tech.isSoundBotUpgrade, + botType: "sound", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 0.6 * (1 + 0.5 * Math.random()), + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 17 + Math.floor(7 * Math.random()) - 5 * tech.isSoundBotUpgrade, + cd: 0, + fireCount: 0, + fireLimit: 5 + 2 * tech.isSoundBotUpgrade, + delay: Math.floor((120 + (tech.isSoundBotUpgrade ? 0 : 70)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + acceleration: 0.005 * (1 + 0.5 * Math.random()), + range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), //how far from the player the bot will move + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + beforeDmg() { }, + onEnd() { }, + fireTarget: { x: 0, y: 0 }, + waves: [], + phononWaveCD: 0, + addWave(where, angle) { + const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.05 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + this.waves.push({ + position: where, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + resonanceCount: 0, + dmg: (tech.isUpgraded ? 4 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1), + }) + }, + fire() { + if (!(simulation.cycle % (6 - 2 * tech.isSoundBotUpgrade))) { + this.fireCount++ + if (this.fireCount > this.fireLimit) { + this.fireCount = 0 + this.cd = simulation.cycle + this.delay; + } + this.addWave({ x: this.position.x, y: this.position.y }, Math.atan2(this.fireTarget.y - this.position.y, this.fireTarget.x - this.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)) //add wave to waves array + //face target + Matter.Body.setAngle(this, Vector.angle(this.position, this.fireTarget)); + } + }, + do() { + if (this.fireCount === 0) { //passive mode: look for targets and following player + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + if (distanceToPlayer > this.range) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) + } else { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + } + if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) { + for (let i = 0, len = mob.length; i < len; i++) { + const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if (dist2 < 1300000 && !mob[i].isBadTarget && (Matter.Query.ray(map, this.position, mob[i].position).length === 0 || dist2 < 300000) && !mob[i].isInvulnerable) { + this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second + this.fire() + break; + } + } + } + } else { //fire mode: quickly fire at targets and doesn't follow player + this.fire() + } + if (!m.isBodiesAsleep) { //update current waves + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + const end = 1200 * Math.sqrt(tech.bulletsLastLonger) + //this does less damage than the player phonon waves 2.3 -> 2 + for (let i = this.waves.length - 1; i > -1; i--) { + const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) + const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) + //draw wave + ctx.moveTo(v1.x, v1.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); + //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector + let hits = Matter.Query.ray(mob, v1, v2, 50) + for (let j = 0; j < hits.length; j++) { + const who = hits[j].body + if (!who.isShielded) { + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += 0.01 * (Math.random() - 0.5) * who.mass + Matter.Body.setVelocity(who, { x: who.velocity.x * 0.98, y: who.velocity.y * 0.98 }); + let vertices = who.vertices; + const vibe = 50 + who.radius * 0.15 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + who.locatePlayer(); + who.damage(this.waves[i].dmg / Math.pow(who.radius, 0.33)); + + + if (tech.isPhononWave && this.phononWaveCD < m.cycle) { + this.phononWaveCD = m.cycle + 10 * (1 + this.waves[i].resonanceCount) + let closestMob, dist + let range = end - 30 * this.waves[i].resonanceCount + for (let i = 0, len = mob.length; i < len; i++) { + if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { + dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); + if (dist < range) { + closestMob = mob[i] + range = dist + } + } + } + if (closestMob) { //add wave to waves array + this.addWave(who.position, Math.atan2(closestMob.position.y - who.position.y, closestMob.position.x - who.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)) + } else { + this.addWave(who.position, Math.random() * Math.PI) + } + this.waves[this.waves.length - 1].resonanceCount = this.waves[i].resonanceCount + 1 + break + } + } + } + + hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + const who = hits[j].body + //make them shake around + who.force.x += 0.005 * (Math.random() - 0.5) * who.mass + who.force.y += (0.005 * (Math.random() - 0.5) - simulation.g * 0.1) * who.mass //remove force of gravity + let vertices = who.vertices; + const vibe = 25 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + + this.waves[i].radius += tech.waveBeamSpeed * 2 + if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { + this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + + + + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + laserBot(position = { + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.laserBot()`); + const me = bullet.length; + const dir = m.angle; + const RADIUS = (14 + 6 * Math.random()) + bullet[me] = Bodies.polygon(position.x, position.y, 3, RADIUS, { + botType: "laser", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.008 * (1 + 0.3 * Math.random()), + restitution: 0.5 * (1 + 0.5 * Math.random()), + acceleration: 0.0015 * (1 + 0.3 * Math.random()), + playerRange: 140 + Math.floor(30 * Math.random()) + 2 * b.totalBots(), + offPlayer: { x: 0, y: 0, }, + dmg: 0, //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 20 + Math.floor(7 * Math.random()) - 13 * tech.isLaserBotUpgrade, + range: (600 + 375 * tech.isLaserBotUpgrade) * (1 + 0.12 * Math.random()), + drainThreshold: 0.15 + 0.5 * Math.random() + (tech.isEnergyHealth ? 0.3 : 0),// laser bot will not attack if the player is below this energy + drain: (0.57 - 0.43 * tech.isLaserBotUpgrade) * tech.laserDrain, + laserDamage: 0.75 + 0.75 * tech.isLaserBotUpgrade, + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + lockedOn: null, + beforeDmg() { + this.lockedOn = null + }, + onEnd() { }, + do() { + const playerPos = Vector.add(Vector.add(this.offPlayer, m.pos), Vector.mult(player.velocity, 20)) //also include an offset unique to this bot to keep many bots spread out + const farAway = Math.max(0, (Vector.magnitude(Vector.sub(this.position, playerPos))) / this.playerRange) //linear bounding well + const mag = Math.min(farAway, 4) * this.mass * this.acceleration + this.force = Vector.mult(Vector.normalise(Vector.sub(playerPos, this.position)), mag) + //manual friction to not lose rotational velocity + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + //find targets + if (!(simulation.cycle % this.lookFrequency)) { + this.lockedOn = null; + if (!m.isCloak) { + let closeDist = this.range; + for (let i = 0, len = mob.length; i < len; ++i) { + const DIST = Vector.magnitude(Vector.sub(this.vertices[0], mob[i].position)); + if ( + DIST - mob[i].radius < closeDist && + !mob[i].isShielded && + (!mob[i].isBadTarget || mob[i].isMobBullet) && + Matter.Query.ray(map, this.vertices[0], mob[i].position).length === 0 && + Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + //randomize position relative to player + if (Math.random() < 0.15) { + const range = 110 + 4 * b.totalBots() + this.offPlayer = { + x: range * (Math.random() - 0.5), + y: range * (Math.random() - 0.5) - 20, + } + } + } + //hit target with laser + if (this.lockedOn && this.lockedOn.alive && m.energy > this.drainThreshold) { + m.energy -= this.drain + this.laser(); + // b.laser(this.vertices[0], this.lockedOn.position, m.dmgScale * this.laserDamage * tech.laserDamage, tech.laserReflections, false, 0.4) //tech.laserDamage = 0.16 + } + }, + laser() { + const push = 0.4 + const reflectivity = 1 - 1 / (tech.laserReflections * 3) + let damage = m.dmgScale * this.laserDamage * tech.laserDamage + //make the laser wiggle as it aims at the target + let best = { x: 1, y: 1, dist2: Infinity, who: null, v1: 1, v2: 1 }; + const perp2 = Vector.mult(Vector.rotate({ x: 1, y: 0 }, m.angle + Math.PI / 2), 0.6 * this.lockedOn.radius * Math.sin(simulation.cycle / this.lookFrequency)) + const path = [{ x: this.vertices[0].x, y: this.vertices[0].y }, Vector.add(this.lockedOn.position, perp2)]; + + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + const checkForCollisions = function () { + best = { + x: 1, + y: 1, + dist2: Infinity, + who: null, + v1: 1, + v2: 1 + }; + vertexCollision(path[path.length - 2], path[path.length - 1], mob); + vertexCollision(path[path.length - 2], path[path.length - 1], map); + vertexCollision(path[path.length - 2], path[path.length - 1], body); + }; + const laserHitMob = function () { + if (best.who.alive) { + best.who.locatePlayer(); + if (best.who.damageReduction) { + if ( //iridescence + tech.laserCrit && !best.who.shield && + Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.999 - 0.5 / best.who.radius + ) { + damage *= 1 + tech.laserCrit + simulation.drawList.push({ //add dmg to draw queue + x: path[path.length - 1].x, + y: path[path.length - 1].y, + radius: Math.sqrt(2500 * damage * best.who.damageReduction) + 5, + color: `hsla(${60 + 283 * Math.random()},100%,70%,0.5)`, // random hue, but not red + time: 16 + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: path[path.length - 1].x, + y: path[path.length - 1].y, + radius: Math.sqrt(2000 * damage * best.who.damageReduction) + 2, + color: tech.laserColorAlpha, + time: simulation.drawTime + }); + } + best.who.damage(damage); + } + if (tech.isLaserPush) { //push mobs away + const index = path.length - 1 + Matter.Body.setVelocity(best.who, { + x: best.who.velocity.x * 0.97, + y: best.who.velocity.y * 0.97 + }); + const force = Vector.mult(Vector.normalise(Vector.sub(path[index], path[Math.max(0, index - 1)])), 0.003 * push * Math.min(6, best.who.mass)) + Matter.Body.applyForce(best.who, path[index], force) + } + } else if (tech.isLaserPush && best.who.classType === "body") { + const index = path.length - 1 + Matter.Body.setVelocity(best.who, { + x: best.who.velocity.x * 0.97, + y: best.who.velocity.y * 0.97 + }); + const force = Vector.mult(Vector.normalise(Vector.sub(path[index], path[Math.max(0, index - 1)])), 0.003 * push * Math.min(6, best.who.mass)) + Matter.Body.applyForce(best.who, path[index], force) + } + }; + const reflection = function () { // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); + const d = Vector.sub(path[path.length - 1], path[path.length - 2]); + const nn = Vector.mult(n, 2 * Vector.dot(d, n)); + const r = Vector.normalise(Vector.sub(d, nn)); + path[path.length] = Vector.add(Vector.mult(r, 3000), path[path.length - 1]); + }; + + checkForCollisions(); + let lastBestOdd + let lastBestEven = best.who //used in hack below + if (best.dist2 !== Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(); + for (let i = 0; i < tech.laserReflections; i++) { + reflection(); + checkForCollisions(); + if (best.dist2 !== Infinity) { //if hitting something + lastReflection = best + path[path.length - 1] = { + x: best.x, + y: best.y + }; + damage *= reflectivity + laserHitMob(); + //I'm not clear on how this works, but it gets rid of a bug where the laser reflects inside a block, often vertically. + //I think it checks to see if the laser is reflecting off a different part of the same block, if it is "inside" a block + if (i % 2) { + if (lastBestOdd === best.who) break + } else { + lastBestOdd = best.who + if (lastBestEven === best.who) break + } + } else { + break + } + } + } + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 2 + ctx.lineDashOffset = 900 * Math.random() + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.beginPath(); + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + ctx.stroke(); + ctx.globalAlpha *= reflectivity; //reflections are less intense + } + ctx.setLineDash([]); + ctx.globalAlpha = 1; + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + boomBot(position = { + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.boomBot()`); + const me = bullet.length; + const dir = m.angle; + const RADIUS = (7 + 2 * Math.random()) + bullet[me] = Bodies.polygon(position.x, position.y, 4, RADIUS, { + isUpgraded: tech.isBoomBotUpgrade, + botType: "boom", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 1, + dmg: 0, + minDmgSpeed: 0, + lookFrequency: 43 + Math.floor(7 * Math.random()) - 13 * tech.isBoomBotUpgrade, + acceleration: 0.005 * (1 + 0.5 * Math.random()), + attackAcceleration: 0.012 + 0.006 * tech.isBoomBotUpgrade, + range: 500 * (1 + 0.1 * Math.random()) + 350 * tech.isBoomBotUpgrade, + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + lockedOn: null, + explode: 0, + beforeDmg() { + if (this.lockedOn) { + const explosionRadius = Math.min(136 + 230 * this.isUpgraded, Vector.magnitude(Vector.sub(this.position, m.pos)) - 30) + if (explosionRadius > 60) { + this.explode = explosionRadius + // + //push away from player, because normal explosion knock doesn't do much + // const sub = Vector.sub(this.lockedOn.position, m.pos) + // mag = Math.min(35, 20 / Math.sqrt(this.lockedOn.mass)) + // Matter.Body.setVelocity(this.lockedOn, Vector.mult(Vector.normalise(sub), mag)) + } + this.lockedOn = null //lose target so bot returns to player + } + }, + onEnd() { }, + do() { + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, player.position)) + if (distanceToPlayer > 100) { //if far away move towards player + if (this.explode) { + // if (tech.isImmuneExplosion && m.energy > 1.43) { + // b.explosion(this.position, this.explode); + // } else { + // } + b.explosion(this.position, Math.max(0, Math.min(this.explode, (distanceToPlayer - 70) / b.explosionRange()))); + this.explode = 0; + } + this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * this.acceleration) + } else if (distanceToPlayer < 250) { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + //find targets + if (!(simulation.cycle % this.lookFrequency) && !m.isCloak) { + this.lockedOn = null; + let closeDist = this.range; + for (let i = 0, len = mob.length; i < len; ++i) { + const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius; + if ( + DIST < closeDist && + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + } + //punch target + if (this.lockedOn && this.lockedOn.alive && !m.isCloak) { + const DIST = Vector.magnitude(Vector.sub(this.vertices[0], this.lockedOn.position)); + if (DIST - this.lockedOn.radius < this.range && + Matter.Query.ray(map, this.position, this.lockedOn.position).length === 0) { + //move towards the target + this.force = Vector.add(this.force, Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.attackAcceleration * this.mass)) + } + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + plasmaBot(position = { + x: player.position.x + 50 * (Math.random() - 0.5), + y: player.position.y + 50 * (Math.random() - 0.5) + }, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.plasmaBot()`); + const me = bullet.length; + const dir = m.angle; + const RADIUS = 21 + bullet[me] = Bodies.polygon(position.x, position.y, 5, RADIUS, { + botType: "plasma", + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 1, + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 25, + cd: 0, + acceleration: 0.009, + endCycle: Infinity, + drainThreshold: tech.isEnergyHealth ? 0.5 : 0.05, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other + }, + lockedOn: null, + beforeDmg() { + this.lockedOn = null + }, + onEnd() { }, + do() { + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) + if (distanceToPlayer > 150) this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) //if far away move towards player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + + if (!(simulation.cycle % this.lookFrequency)) { //find closest + this.lockedOn = null; + if (!m.isCloak) { + let closeDist = tech.isPlasmaRange * 1000; + for (let i = 0, len = mob.length; i < len; ++i) { + const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius; + if ( + DIST < closeDist && (!mob[i].isBadTarget || mob[i].isMobBullet) && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + } + //fire plasma at target + if (this.lockedOn && this.lockedOn.alive && m.fieldCDcycle < m.cycle) { + const sub = Vector.sub(this.lockedOn.position, this.position) + const DIST = Vector.magnitude(sub); + const unit = Vector.normalise(sub) + if (DIST < tech.isPlasmaRange * 450 && m.energy > this.drainThreshold) { + m.energy -= 0.00135 //0.004; //normal plasma field is 0.00008 + m.fieldRegen = 0.00108 + // if (m.energy < 0) { + // m.fieldCDcycle = m.cycle + 120; + // m.energy = 0; + // } + //calculate laser collision + let best; + let range = tech.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random())) + const path = [{ x: this.position.x, y: this.position.y }, { x: this.position.x + range * unit.x, y: this.position.y + range * unit.y }]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + if (best.who.alive) { + const dmg = 0.9 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* + best.who.damage(dmg); + best.who.locatePlayer(); + //push mobs away + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + if (best.who.speed > 3) { + Matter.Body.setVelocity(best.who, { //friction + x: best.who.velocity.x * 0.7, + y: best.who.velocity.y * 0.7 + }); + } + //draw mob damage circle + if (best.who.damageReduction) { + simulation.drawList.push({ + x: path[1].x, + y: path[1].y, + // radius: Math.sqrt(dmg) * 50 * mob[k].damageReduction, + // radius: 600 * dmg * best.who.damageReduction, + radius: Math.sqrt(2000 * dmg * best.who.damageReduction) + 2, + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime * 4 + }); + } + } else if (!best.who.isStatic) { + //push blocks away + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.007 * Math.sqrt(Math.sqrt(best.who.mass))) + Matter.Body.applyForce(best.who, path[1], force) + } + } + //draw blowtorch laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.strokeStyle = "rgba(255,0,255,0.1)" + ctx.lineWidth = 14 + ctx.stroke(); + ctx.strokeStyle = "#f0f"; + ctx.lineWidth = 2 + ctx.stroke(); + //draw electricity + let x = this.position.x + 20 * unit.x; + let y = this.position.y + 20 * unit.y; + ctx.beginPath(); + ctx.moveTo(x, y); + const step = Vector.magnitude(Vector.sub(path[0], path[1])) / 5 + for (let i = 0; i < 4; i++) { + x += step * (unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); + } + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + }, + orbitBot(position = player.position, isConsole = true) { + if (isConsole) simulation.makeTextLog(`b.orbitBot()`); + const me = bullet.length; + bullet[me] = Bodies.polygon(position.x, position.y, 9, 12, { + isUpgraded: tech.isOrbitBotUpgrade, + botType: "orbit", + friction: 0, + frictionStatic: 0, + frictionAir: 1, + isStatic: true, + isSensor: true, + restitution: 0, + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 0, + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: 0 //cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { + //reorder orbital bot positions around a circle + let totalOrbitalBots = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit' && bullet[i] !== this) totalOrbitalBots++ + } + let index = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit' && bullet[i] !== this) { + bullet[i].phase = (index / totalOrbitalBots) * 2 * Math.PI + index++ + } + } + }, + range: 190 + 130 * tech.isOrbitBotUpgrade, //range is set in bot upgrade too! + orbitalSpeed: 0, + phase: 2 * Math.PI * Math.random(), + do() { + if (!m.isCloak) { //if time dilation isn't active + const size = 33 + q = Matter.Query.region(mob, { + min: { + x: this.position.x - size, + y: this.position.y - size + }, + max: { + x: this.position.x + size, + y: this.position.y + size + } + }) + for (let i = 0; i < q.length; i++) { + if (!q[i].isShielded) { + mobs.statusStun(q[i], 210 + 90 * this.isUpgraded) + const dmg = 0.4 * m.dmgScale * (this.isUpgraded ? 4.5 : 1) * (tech.isCrit ? 4 : 1) + q[i].damage(dmg); + if (q[i].alive) q[i].foundPlayer(); + if (q[i].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + // radius: 600 * dmg * q[i].damageReduction, + radius: Math.sqrt(2000 * dmg * q[i].damageReduction) + 2, + color: 'rgba(0,0,0,0.4)', + time: simulation.drawTime + }); + } + } + } + } + //orbit player + const time = simulation.cycle * this.orbitalSpeed + this.phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) //*1.1 + } + Matter.Body.setPosition(this, Vector.add(m.pos, Vector.mult(orbit, this.range))) //bullets move with player + } + }) + // bullet[me].orbitalSpeed = Math.sqrt(0.7 / bullet[me].range) + bullet[me].orbitalSpeed = Math.sqrt(0.25 / bullet[me].range) //also set in bot upgrade too! + // bullet[me].phase = (index / tech.orbitBotCount) * 2 * Math.PI + Composite.add(engine.world, bullet[me]); //add bullet to world + + //reorder orbital bot positions around a circle + let totalOrbitalBots = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') totalOrbitalBots++ + } + let index = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].phase = (index / totalOrbitalBots) * 2 * Math.PI + index++ + } + } + }, + // ************************************************************************************************** + // ************************************************************************************************** + // ******************************** Guns ********************************************* + // ************************************************************************************************** + // ************************************************************************************************** + //0 nail gun + //1 shotgun + //2 super balls + //3 wave + //4 missiles + //5 grenades + //6 spores + //7 drones + //8 foam + //9 harpoon + //10 mine + //11 laser + guns: [{ + name: "nail gun", // 0 + description: "use compressed air to fire a stream of nails
delay after firing decreases as you shoot", + ammo: 0, + ammoPack: 60, + defaultAmmoPack: 60, + recordedAmmo: 0, + have: false, + nextFireCycle: 0, //use to remember how longs its been since last fire, used to reset count + startingHoldCycle: 0, + chooseFireMethod() { //set in simulation.startGame + if (tech.nailRecoil) { + if (tech.isRivets) { + this.fire = this.fireRecoilRivets + } else { + this.fire = this.fireRecoilNails + } + } else if (tech.isRivets) { + this.fire = this.fireRivets + } else if (tech.isNeedles) { + this.fire = this.fireNeedles + } else if (tech.nailInstantFireRate) { + this.fire = this.fireInstantFireRate + // } else if (tech.nailFireRate) { + // this.fire = this.fireNailFireRate + } else { + this.fire = this.fireNormal + } + }, + do() { }, + fire() { }, + fireRecoilNails() { + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 0.99) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.04 : 0.13) / CD, 45 + 6 * Math.random()) + //very complex recoil system + if (m.onGround) { + if (m.crouch) { + const KNOCK = 0.006 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.5, + y: player.velocity.y * 0.5 + }); + } else { + const KNOCK = 0.03 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.8, + y: player.velocity.y * 0.8 + }); + } + } else { + player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + player.force.y -= 0.006 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } + }, + fireNormal() { + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 1) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.05 : 0.3) / CD) + }, + fireNeedles() { + if (m.crouch) { + m.fireCDcycle = m.cycle + 30 * b.fireCDscale; // cool down + b.needle() + + function cycle() { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count % 2) b.needle() + if (count < 7 && m.alive) requestAnimationFrame(cycle); + } + } + let count = -1 + requestAnimationFrame(cycle); + } else { + m.fireCDcycle = m.cycle + 22 * b.fireCDscale; // cool down + b.needle() + + function cycle() { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count % 2) b.needle() + if (count < 3 && m.alive) requestAnimationFrame(cycle); + } + } + let count = -1 + requestAnimationFrame(cycle); + } + }, + fireRivets() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 22 : 14) * b.fireCDscale); // cool down + const me = bullet.length; + const size = tech.bulletSize * 8 + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); + bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 + Matter.Body.setDensity(bullet[me], 0.002); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = m.crouch ? 60 : 44 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + bullet[me].endCycle = simulation.cycle + 180 + + bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech + if (tech.isIncendiary) { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end + } + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (this.speed > 4 && tech.fragments) { + b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn + } + }; + + bullet[me].minDmgSpeed = 10 + bullet[me].frictionAir = 0.006; + bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? + if (this.speed > 7) { + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + const mag = 0.002 * this.mass + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += mag + } else { + this.torque -= mag + } + } + }; + if (tech.isIncendiary) { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + //collide with map + if (Matter.Query.collides(this, map).length) { //penetrate walls + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end + } + }; + } else { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + }; + } + b.muzzleFlash(); + //very complex recoil system + if (m.onGround) { + if (m.crouch) { + const KNOCK = 0.01 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } else { + const KNOCK = 0.02 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } + } else { + const KNOCK = 0.01 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps + } + }, + fireRecoilRivets() { + // m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 25 : 17) * b.fireCDscale); // cool down + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(25 - 0.14 * (m.cycle - this.startingHoldCycle), 5) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down + + const me = bullet.length; + const size = tech.bulletSize * 8 + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); + bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 + Matter.Body.setDensity(bullet[me], 0.002); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = m.crouch ? 62 : 52 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + bullet[me].endCycle = simulation.cycle + 180 + bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech + if (tech.isIncendiary) { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end + } + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (this.speed > 4 && tech.fragments) { + b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn + } + }; + + bullet[me].minDmgSpeed = 10 + bullet[me].frictionAir = 0.006; + bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? + if (this.speed > 7) { + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + const mag = 0.002 * this.mass + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += mag + } else { + this.torque -= mag + } + } + }; + if (tech.isIncendiary) { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + //collide with map + if (Matter.Query.collides(this, map).length) { //penetrate walls + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end + } + }; + } else { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + }; + } + + b.muzzleFlash(); + //very complex recoil system + if (m.onGround) { + if (m.crouch) { + const KNOCK = 0.03 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.4, + y: player.velocity.y * 0.4 + }); + } else { + const KNOCK = 0.1 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.7, + y: player.velocity.y * 0.7 + }); + } + } else { + player.force.x -= 0.2 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + // player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + + player.force.y -= 0.02 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } + }, + fireInstantFireRate() { + m.fireCDcycle = m.cycle + Math.floor(1 * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (m.crouch ? 1.15 : 2) / 2) + }, + baseFire(angle, speed = 30 + 6 * Math.random()) { + b.nail({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, { + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }) //position, velocity, damage + if (tech.isIceCrystals) { + bullet[bullet.length - 1].beforeDmg = function (who) { + mobs.statusSlow(who, 60) + if (tech.isNailRadiation) mobs.statusDoT(who, 1 * (tech.isFastRadiation ? 1.3 : 0.44), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end + } + } + this.ricochet(who) + }; + if (m.energy < 0.01) { + m.fireCDcycle = m.cycle + 60; // cool down + } else { + m.energy -= 0.01 + } + } + }, + }, + { + name: "shotgun", //1 + description: "fire a wide burst of short range bullets
with a low fire rate", + ammo: 0, + ammoPack: 3.5, + defaultAmmoPack: 3.5, + have: false, + do() { + //fade cross hairs + + + + // draw loop around player head + // const left = m.fireCDcycle !== Infinity ? 0.05 * Math.max(m.fireCDcycle - m.cycle, 0) : 0 + // if (left > 0) { + // ctx.beginPath(); + // // ctx.arc(simulation.mouseInGame.x, simulation.mouseInGame.y, 30, 0, left); + // ctx.arc(m.pos.x, m.pos.y, 28, m.angle - left, m.angle); + // // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" + // // ctx.fill(); + // ctx.strokeStyle = "#333"; + // ctx.lineWidth = 2; + // ctx.stroke(); + // } + + + //draw hip circle + // ctx.beginPath(); + // ctx.arc(m.pos.x + m.hip.x, m.pos.y + m.hip.y, 11, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" + // ctx.fill(); + }, + fire() { + let knock, spread + const coolDown = function () { + if (m.crouch) { + spread = 0.65 + m.fireCDcycle = m.cycle + Math.floor((73 + 36 * tech.shotgunExtraShots) * b.fireCDscale) // cool down + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(60 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(60 * b.fireCDscale); //player is immune to damage for 30 cycles + knock = 0.01 + } else { + m.fireCDcycle = m.cycle + Math.floor((56 + 28 * tech.shotgunExtraShots) * b.fireCDscale) // cool down + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCDscale); //player is immune to damage for 30 cycles + spread = 1.3 + knock = 0.1 + } + + if (tech.isShotgunReversed) { + player.force.x += 1.5 * knock * Math.cos(m.angle) + player.force.y += 1.5 * knock * Math.sin(m.angle) - 3 * player.mass * simulation.g + } else if (tech.isShotgunRecoil) { + m.fireCDcycle -= 0.66 * (56 * b.fireCDscale) + player.force.x -= 2 * knock * Math.cos(m.angle) + player.force.y -= 2 * knock * Math.sin(m.angle) + } else { + player.force.x -= knock * Math.cos(m.angle) + player.force.y -= knock * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps + } + } + const spray = (num) => { + const side = 22 + for (let i = 0; i < num; i++) { + const me = bullet.length; + const dir = m.angle + (Math.random() - 0.5) * spread + bullet[me] = Bodies.rectangle(m.pos.x, m.pos.y, side, side, b.fireAttributes(dir)); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = 52 + Math.random() * 8 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + bullet[me].endCycle = simulation.cycle + 40 * tech.bulletsLastLonger + bullet[me].minDmgSpeed = 15 + if (tech.isShotgunReversed) Matter.Body.setDensity(bullet[me], 0.0015) + // bullet[me].restitution = 0.4 + bullet[me].frictionAir = 0.034; + bullet[me].do = function () { + const scale = 1 - 0.034 / tech.bulletsLastLonger + Matter.Body.scale(this, scale, scale); + }; + } + } + const chooseBulletType = function () { + if (tech.isRivets) { + const me = bullet.length; + // const dir = m.angle + 0.02 * (Math.random() - 0.5) + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 56 * tech.bulletSize, 25 * tech.bulletSize, b.fireAttributes(m.angle)); + + Matter.Body.setDensity(bullet[me], 0.005 * (tech.isShotgunReversed ? 1.5 : 1)); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = (m.crouch ? 50 : 43) + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + if (tech.isIncendiary) { + bullet[me].endCycle = simulation.cycle + 60 + bullet[me].onEnd = function () { + b.explosion(this.position, 360 + (Math.random() - 0.5) * 60); //makes bullet do explosive damage at end + } + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + }; + } else { + bullet[me].endCycle = simulation.cycle + 180 + } + bullet[me].minDmgSpeed = 7 + // bullet[me].restitution = 0.4 + bullet[me].frictionAir = 0.004; + bullet[me].turnMag = 0.04 * Math.pow(tech.bulletSize, 3.75) + bullet[me].do = function () { + this.force.y += this.mass * 0.002 + if (this.speed > 6) { //rotates bullet to face current velocity? + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += this.turnMag + } else { + this.torque -= this.turnMag + } + } + if (tech.isIncendiary && Matter.Query.collides(this, map).length) { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + } + }; + bullet[me].beforeDmg = function (who) { + if (this.speed > 4) { + if (tech.fragments) { + b.targetedNail(this.position, 6 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn + } + if (tech.isIncendiary) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + if (tech.isCritKill) b.crit(who, this) + } + } + spray(12); //fires normal shotgun bullets + } else if (tech.isIncendiary) { + spread *= 0.15 + const END = Math.floor(m.crouch ? 8 : 5); + const totalBullets = 9 + const angleStep = (m.crouch ? 0.3 : 0.8) / totalBullets + let dir = m.angle - angleStep * totalBullets / 2; + for (let i = 0; i < totalBullets; i++) { //5 -> 7 + dir += angleStep + const me = bullet.length; + bullet[me] = Bodies.rectangle(m.pos.x + 50 * Math.cos(m.angle), m.pos.y + 50 * Math.sin(m.angle), 17, 4, b.fireAttributes(dir)); + const end = END + Math.random() * 4 + bullet[me].endCycle = 2 * end * tech.bulletsLastLonger + simulation.cycle + const speed = 25 * end / END + const dirOff = dir + (Math.random() - 0.5) * spread + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dirOff), + y: speed * Math.sin(dirOff) + }); + bullet[me].onEnd = function () { + b.explosion(this.position, 150 * (tech.isShotgunReversed ? 1.4 : 1) + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end + } + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + }; + bullet[me].do = function () { + if (Matter.Query.collides(this, map).length) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + } + Composite.add(engine.world, bullet[me]); //add bullet to world + } + } else if (tech.isNailShot) { + spread *= 0.65 + const dmg = 2 * (tech.isShotgunReversed ? 1.5 : 1) + if (m.crouch) { + for (let i = 0; i < 17; i++) { + speed = 38 + 15 * Math.random() + const dir = m.angle + (Math.random() - 0.5) * spread + const pos = { + x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), + y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) + } + b.nail(pos, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, dmg) + } + } else { + for (let i = 0; i < 17; i++) { + speed = 38 + 15 * Math.random() + const dir = m.angle + (Math.random() - 0.5) * spread + const pos = { + x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), + y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) + } + b.nail(pos, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, dmg) + } + } + } else if (tech.isSporeFlea) { + const where = { + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + } + const number = 2 * (tech.isShotgunReversed ? 1.5 : 1) + for (let i = 0; i < number; i++) { + const angle = m.angle + 0.2 * (Math.random() - 0.5) + const speed = (m.crouch ? 35 * (1 + 0.05 * Math.random()) : 30 * (1 + 0.15 * Math.random())) + b.flea(where, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + bullet[bullet.length - 1].setDamage() + } + spray(10); //fires normal shotgun bullets + } else if (tech.isSporeWorm) { + const where = { + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + } + const spread = (m.crouch ? 0.02 : 0.07) + const number = 3 * (tech.isShotgunReversed ? 1.5 : 1) + let angle = m.angle - (number - 1) * spread * 0.5 + for (let i = 0; i < number; i++) { + b.worm(where) + const SPEED = (30 + 10 * m.crouch) * (1 + 0.2 * Math.random()) + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: player.velocity.x * 0.5 + SPEED * Math.cos(angle), + y: player.velocity.y * 0.5 + SPEED * Math.sin(angle) + }); + angle += spread + } + spray(7); //fires normal shotgun bullets + } else if (tech.isIceShot) { + const spread = (m.crouch ? 0.7 : 1.2) + for (let i = 0, len = 10 * (tech.isShotgunReversed ? 1.5 : 1); i < len; i++) { + b.iceIX(23 + 10 * Math.random(), m.angle + spread * (Math.random() - 0.5)) + } + spray(10); //fires normal shotgun bullets + } else if (tech.isFoamShot) { + const spread = (m.crouch ? 0.15 : 0.4) + const where = { + x: m.pos.x + 25 * Math.cos(m.angle), + y: m.pos.y + 25 * Math.sin(m.angle) + } + const number = 16 * (tech.isShotgunReversed ? 1.5 : 1) + for (let i = 0; i < number; i++) { + const SPEED = 13 + 4 * Math.random(); + const angle = m.angle + spread * (Math.random() - 0.5) + b.foam(where, { + x: SPEED * Math.cos(angle), + y: SPEED * Math.sin(angle) + }, 8 + 7 * Math.random()) + } + } else if (tech.isNeedles) { + const number = 9 * (tech.isShotgunReversed ? 1.5 : 1) + const spread = (m.crouch ? 0.03 : 0.05) + let angle = m.angle - (number - 1) * spread * 0.5 + for (let i = 0; i < number; i++) { + b.needle(angle) + angle += spread + } + } else { + spray(16); //fires normal shotgun bullets + } + } + + + coolDown(); + b.muzzleFlash(35); + chooseBulletType(); + + if (tech.shotgunExtraShots) { + const delay = 7 + let count = tech.shotgunExtraShots * delay + + function cycle() { + count-- + if (!(count % delay)) { + coolDown(); + b.muzzleFlash(35); + chooseBulletType(); + } + if (count > 0) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + } + } + }, { + name: "super balls", //2 + description: "fire 3 balls in a wide arc
balls bounce with no momentum loss", + ammo: 0, + ammoPack: 9, + have: false, + // num: 5, + do() { }, + foamBall() { + + + }, + fireOne() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 27 : 19) * b.fireCDscale); // cool down + const speed = m.crouch ? 43 : 36 + b.superBall({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 21 * tech.bulletSize) + }, + fireMulti() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down + const SPREAD = m.crouch ? 0.08 : 0.13 + const num = 3 + Math.floor(tech.extraSuperBalls * Math.random()) + const speed = m.crouch ? 43 : 36 + let dir = m.angle - SPREAD * (num - 1) / 2; + for (let i = 0; i < num; i++) { + b.superBall({ + x: m.pos.x + 30 * Math.cos(dir), + y: m.pos.y + 30 * Math.sin(dir) + }, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, 11 * tech.bulletSize) + dir += SPREAD; + } + }, + fireQueue() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down + const num = 1 + 3 + Math.floor(tech.extraSuperBalls * Math.random()) //1 extra + const speed = m.crouch ? 43 : 36 + + const delay = Math.floor((m.crouch ? 18 : 12) * b.fireCDscale) + m.fireCDcycle = m.cycle + delay; // cool down + function cycle() { + count++ + b.superBall({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 11 * tech.bulletSize) + if (count < num && m.alive) requestAnimationFrame(cycle); + m.fireCDcycle = m.cycle + delay; // cool down + } + let count = 0 + requestAnimationFrame(cycle); + + + }, + chooseFireMethod() { //set in simulation.startGame + if (tech.oneSuperBall) { + this.fire = this.fireOne + } else if (tech.superBallDelay) { + this.fire = this.fireQueue + } else { + this.fire = this.fireMulti + } + }, + fire() { } + }, + { + name: "wave", //3 + description: "emit wave packets that propagate through solids
waves slow mobs", // of oscillating particles
+ ammo: 0, + ammoPack: 115, + defaultAmmoPack: 115, + have: false, + wavePacketCycle: 0, + delay: 40, + phononWaveCD: 0, + waves: [], //used in longitudinal mode + chooseFireMethod() { //set in simulation.startGame + this.waves = []; + if (tech.isLongitudinal) { + if (tech.is360Longitudinal) { + this.fire = this.fire360Longitudinal + this.do = this.do360Longitudinal + } else { + this.fire = this.fireLongitudinal + this.do = this.doLongitudinal + } + } else { + this.fire = this.fireTransverse + this.do = this.doTransverse + } + }, + do() { }, + do360Longitudinal() { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + const end = 700 * Math.sqrt(tech.bulletsLastLonger) + const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer + + for (let i = this.waves.length - 1; i > -1; i--) { + //draw wave + ctx.moveTo(this.waves[i].position.x + this.waves[i].radius, this.waves[i].position.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, 0, 2 * Math.PI); + // collisions + // if (tech.isBulletTeleport && Math.random() < 0.04) { + // const scale = 400 * Math.random() + // this.waves[i].position = Vector.add(this.waves[i].position, { x: scale * (Math.random() - 0.5), y: scale * (Math.random() - 0.5) }) + // } + for (let j = 0, len = mob.length; j < len; j++) { + if (!mob[j].isShielded) { + const dist = Vector.magnitude(Vector.sub(this.waves[i].position, mob[j].position)) + const r = mob[j].radius + 30 + if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { + //make them shake around + if (!mob[j].isBadTarget) { + mob[j].force.x += 0.01 * (Math.random() - 0.5) * mob[j].mass + mob[j].force.y += 0.01 * (Math.random() - 0.5) * mob[j].mass + } + // if (!mob[j].isShielded) { + Matter.Body.setVelocity(mob[j], { //friction + x: mob[j].velocity.x * 0.95, + y: mob[j].velocity.y * 0.95 + }); + //draw vibes + let vertices = mob[j].vertices; + const vibe = 50 + mob[j].radius * 0.15 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x + vibe * (Math.random() - 0.5), vertices[k].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + //damage + mob[j].locatePlayer(); + mob[j].damage(damage / Math.sqrt(mob[j].radius)); + // } + if (tech.isPhononWave && this.phononWaveCD < m.cycle) { + this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount) + this.waves.push({ + position: mob[j].position, + radius: 25, + resonanceCount: this.waves[i].resonanceCount + 1, + }) + } + } + } + } + // for (let j = 0, len = body.length; j < len; j++) { + for (let j = 0, len = Math.min(30, body.length); j < len; j++) { + const dist = Vector.magnitude(Vector.sub(this.waves[i].position, body[j].position)) + const r = 20 + if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { + const who = body[j] + //make them shake around + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity + //draw vibes + let vertices = who.vertices; + const vibe = 25 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x + vibe * (Math.random() - 0.5), vertices[k].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.1 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + } + this.waves[i].radius += 0.9 * tech.waveBeamSpeed //expand / move + // if (this.waves[i].radius > end) this.waves.splice(i, 1) //end + if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { //* Math.pow(0.9, this.waves[i].resonanceCount) + this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + }, + fire360Longitudinal() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down + this.waves.push({ + position: { x: m.pos.x, y: m.pos.y, }, + radius: 25, + resonanceCount: 0 //used with tech.isPhononWave + }) + }, + doLongitudinal() { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + const end = 1100 * tech.bulletsLastLonger + const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.4 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer + for (let i = this.waves.length - 1; i > -1; i--) { + const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) + const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) + //draw wave + ctx.moveTo(v1.x, v1.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); + //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector + let hits = Matter.Query.ray(mob, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0; j < hits.length; j++) { + const who = hits[j].body + if (!who.isShielded) { + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += 0.01 * (Math.random() - 0.5) * who.mass + Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 }); + let vertices = who.vertices; + const vibe = 50 + who.radius * 0.15 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + who.locatePlayer(); + who.damage(damage / Math.sqrt(who.radius)); + + if (tech.isPhononWave && this.phononWaveCD < m.cycle) { + this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount) + const halfArc = 0.27 //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + let closestMob, dist + let range = end - 30 * this.waves[i].resonanceCount + for (let i = 0, len = mob.length; i < len; i++) { + if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { + dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); + if (dist < range) { + closestMob = mob[i] + range = dist + } + } + } + if (closestMob) { + const dir = Vector.normalise(Vector.sub(closestMob.position, who.position)) + var angle = Math.atan2(dir.y, dir.x) + } else { + var angle = 2 * Math.PI * Math.random() + } + this.waves.push({ + position: who.position, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + resonanceCount: this.waves[i].resonanceCount + 1 + }) + } + } + } + + hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + const who = hits[j].body + //make them shake around + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity + let vertices = who.vertices; + const vibe = 25 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + + this.waves[i].radius += tech.waveBeamSpeed * 1.8 //expand / move + if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { + this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + }, + fireLongitudinal() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down + const halfArc = (m.crouch ? 0.0785 : 0.275) * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + const angle = m.angle + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5) + this.waves.push({ + position: { x: m.pos.x + 25 * Math.cos(m.angle), y: m.pos.y + 25 * Math.sin(m.angle), }, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + resonanceCount: 0 + }) + }, + doTransverse() { + // if (this.wavePacketCycle && !input.fire) { + // this.wavePacketCycle = 0; + // m.fireCDcycle = m.cycle + Math.floor(this.delay * b.fireCDscale); // cool down + // } + }, + fireTransverse() { + totalCycles = Math.floor((3.5) * 35 * tech.waveReflections * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5)) + const me = bullet.length; + bullet[me] = Bodies.polygon(m.pos.x + 25 * Math.cos(m.angle), m.pos.y + 25 * Math.sin(m.angle), 5, 4, { + angle: m.angle, + cycle: -0.5, + endCycle: simulation.cycle + totalCycles, + inertia: Infinity, + frictionAir: 0, + slow: 0, + // amplitude: (m.crouch ? 5 : 10) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin((this.wavePacketCycle + 1) * 0.088), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet + amplitude: (m.crouch ? 6 : 12) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin(this.wavePacketCycle * 0.088) * Math.sin(this.wavePacketCycle * 0.04), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet + minDmgSpeed: 0, + dmg: m.dmgScale * tech.waveBeamDamage * tech.wavePacketDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1), //also control damage when you divide by mob.mass + dmgCoolDown: 0, + classType: "bullet", + collisionFilter: { + category: 0, + mask: 0, //cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { }, + do() { }, + query() { + let slowCheck = 1 + if (Matter.Query.point(map, this.position).length) { //check if inside map + slowCheck = waveSpeedMap + } else { //check if inside a body + let q = Matter.Query.point(body, this.position) + if (q.length) { + slowCheck = waveSpeedBody + Matter.Body.setPosition(this, Vector.add(this.position, q[0].velocity)) //move with the medium + } + } + if (slowCheck !== this.slow) { //toggle velocity based on inside and outside status change + this.slow = slowCheck + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), tech.waveBeamSpeed * slowCheck)); + } + + if (this.dmgCoolDown < 1) { + q = Matter.Query.point(mob, this.position) // check if inside a mob + for (let i = 0; i < q.length; i++) { + this.dmgCoolDown = 5 + Math.floor(8 * Math.random() * b.fireCDscale); + let dmg = this.dmg + q[i].damage(dmg); + if (q[i].alive) { + q[i].foundPlayer(); + Matter.Body.setVelocity(q[i], Vector.mult(q[i].velocity, 0.9)) + } + // this.endCycle = 0; //bullet ends cycle after doing damage + if (q[i].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.log(dmg + 1.1) * 40 * q[i].damageReduction + 3, + color: 'rgba(0,0,0,0.4)', + time: simulation.drawTime + }); + } + } + } else { + this.dmgCoolDown-- + } + }, + wiggle() { + this.cycle++ + const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) + Matter.Body.setPosition(this, Vector.add(this.position, where)) + } + }); + if (tech.isBulletTeleport) { + bullet[me].wiggle = function () { + this.cycle++ + const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) + if (Math.random() < 0.005) { + if (Math.random() < 0.33) { //randomize position + const scale = 500 * Math.random() + Matter.Body.setPosition(this, Vector.add({ + x: scale * (Math.random() - 0.5), + y: scale * (Math.random() - 0.5) + }, Vector.add(this.position, where))) + } else { //randomize position in velocity direction + const velocityScale = Vector.mult(this.velocity, 50 * (Math.random() - 0.5)) + Matter.Body.setPosition(this, Vector.add(velocityScale, Vector.add(this.position, where))) + } + + } else { + Matter.Body.setPosition(this, Vector.add(this.position, where)) + } + } + } + let waveSpeedMap = 0.1 + let waveSpeedBody = 0.25 + if (tech.isPhaseVelocity) { + waveSpeedMap = 3.5 + waveSpeedBody = 2 + bullet[me].dmg *= 1.4 + } + if (tech.waveReflections) { + bullet[me].reflectCycle = totalCycles / tech.waveReflections //tech.waveLengthRange + bullet[me].do = function () { + this.query() + if (this.cycle > this.reflectCycle) { + this.reflectCycle += totalCycles / tech.waveReflections + Matter.Body.setVelocity(this, Vector.mult(this.velocity, -1)); + // if (this.reflectCycle > tech.waveLengthRange * (1 + tech.waveReflections)) this.endCycle = 0; + } + this.wiggle() + } + } else { + bullet[me].do = function () { + this.query() + this.wiggle(); + } + } + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: tech.waveBeamSpeed * Math.cos(m.angle), + y: tech.waveBeamSpeed * Math.sin(m.angle) + }); + const transverse = Vector.normalise(Vector.perp(bullet[me].velocity)) + this.wavePacketCycle++ + }, + fire() { } + }, + { + name: "missiles", //6 + description: "launch homing missiles that target mobs
missiles explode on contact with mobs", + ammo: 0, + ammoPack: 5, + have: false, + fireCycle: 0, + do() { }, + fire() { + const countReduction = Math.pow(0.86, tech.missileCount) + // if (m.crouch) { + // m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down + // // for (let i = 0; i < tech.missileCount; i++) { + // // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) + // // bullet[bullet.length - 1].force.x += 0.004 * countReduction * (i - (tech.missileCount - 1) / 2); + // // } + + // if (tech.missileCount > 1) { + // for (let i = 0; i < tech.missileCount; i++) { + // setTimeout(() => { + // const where = { x: m.pos.x, y: m.pos.y - 40 } + // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) + // bullet[bullet.length - 1].force.x += 0.025 * countReduction * (i - (tech.missileCount - 1) / 2); + // }, 20 * tech.missileCount * Math.random()); + // } + // } else { + // const where = { + // x: m.pos.x, + // y: m.pos.y - 40 + // } + // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5), -2) + // } + // } else { + m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down + const direction = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + // const where = { + // x: m.pos.x + 30 * direction.x, + // y: m.pos.y + 30 * direction.y + // } + if (tech.missileCount > 1) { + const push = Vector.mult(Vector.perp(direction), 0.2 * countReduction / Math.sqrt(tech.missileCount)) + const sqrtCountReduction = Math.sqrt(countReduction) + // for (let i = 0; i < tech.missileCount; i++) { + // setTimeout(() => { + // if (m.crouch) { + // b.missile(where, m.angle, 20, sqrtCountReduction) + // // bullet[bullet.length - 1].force.x += 0.7 * push.x * (i - (tech.missileCount - 1) / 2); + // // bullet[bullet.length - 1].force.y += 0.7 * push.y * (i - (tech.missileCount - 1) / 2); + // } else { + // b.missile(where, m.angle, -10, sqrtCountReduction) + // bullet[bullet.length - 1].force.x += push.x * (i - (tech.missileCount - 1) / 2); + // bullet[bullet.length - 1].force.y += 0.005 + push.y * (i - (tech.missileCount - 1) / 2); + // } + + // }, 1 + i * 10 * tech.missileCount); + // } + const launchDelay = 4 + let count = 0 + const fireMissile = () => { + if (m.crouch) { + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, 20, sqrtCountReduction) + bullet[bullet.length - 1].force.x += 0.5 * push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.004 + 0.5 * push.y * (Math.random() - 0.5) + } else { + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, -15, sqrtCountReduction) + bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + } + } + const cycle = () => { + if ((simulation.paused || m.isBodiesAsleep) && m.alive) { + requestAnimationFrame(cycle) + } else { + count++ + if (!(count % launchDelay)) { + fireMissile() + } + if (count < tech.missileCount * launchDelay && m.alive) requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + } else { + if (m.crouch) { + b.missile({ + x: m.pos.x + 40 * direction.x, + y: m.pos.y + 40 * direction.y + }, m.angle, 25) + } else { + b.missile({ + x: m.pos.x + 40 * direction.x, + y: m.pos.y + 40 * direction.y + }, m.angle, -12) + bullet[bullet.length - 1].force.y += 0.04 * (Math.random() - 0.2) + } + } + } + }, { + name: "grenades", //5 + description: "lob a single bouncy projectile
explodes on contact or after one second", + ammo: 0, + ammoPack: 7, + have: false, + do() { }, //do is set in b.setGrenadeMode() + fire() { + const countReduction = Math.pow(0.93, tech.missileCount) + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 35 : 27) * b.fireCDscale / countReduction); // cool down + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const SPREAD = m.crouch ? 0.12 : 0.2 + let angle = m.angle - SPREAD * (tech.missileCount - 1) / 2; + for (let i = 0; i < tech.missileCount; i++) { + b.grenade(where, angle, countReduction) //function(where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }, angle = m.angle, size = 1) + angle += SPREAD + } + }, + }, { + name: "spores", //6 + description: "fire a sporangium that discharges spores
spores seek out nearby mobs", + ammo: 0, + ammoPack: 2.6, + have: false, + nameString(suffix = "") { + if (tech.isSporeFlea) { + return `flea${suffix}` + } else if (tech.isSporeWorm) { + return `worm${suffix}` + } else { + return `spore${suffix}` + } + }, + do() { }, + fire() { + const me = bullet.length; + const dir = m.angle; + bullet[me] = Bodies.polygon(m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle), 20, 4.5, b.fireAttributes(dir, false)); + b.fireProps(m.crouch ? 40 : 20, m.crouch ? 30 : 16, dir, me); //cd , speed + Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].endCycle = simulation.cycle + 480 + Math.max(0, 120 - 2 * bullet.length); + bullet[me].frictionAir = 0; + bullet[me].friction = 0.5; + bullet[me].radius = 4.5; + bullet[me].maxRadius = 30; + bullet[me].restitution = 0.3; + bullet[me].minDmgSpeed = 0; + bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze + 4 * tech.isSporeColony + bullet[me].stuck = function () { }; + bullet[me].beforeDmg = function () { }; + bullet[me].do = function () { + function onCollide(that) { + that.collisionFilter.mask = 0; //non collide with everything + Matter.Body.setVelocity(that, { + x: 0, + y: 0 + }); + that.do = that.grow; + } + const mobCollisions = Matter.Query.collides(this, mob) + if (mobCollisions.length) { + onCollide(this) + this.stuckTo = mobCollisions[0].bodyA + if (tech.isZombieMobs) this.stuckTo.isSoonZombie = true + if (this.stuckTo.isVerticesChange) { + this.stuckToRelativePosition = { x: 0, y: 0 } + } else { + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } + this.stuck = function () { + if (this.stuckTo && this.stuckTo.alive) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.collisionFilter.mask = cat.map; //non collide with everything but map + this.stuck = function () { + this.force.y += this.mass * 0.0006; + } + } + } + } else { + const bodyCollisions = Matter.Query.collides(this, body) + if (bodyCollisions.length) { + if (!bodyCollisions[0].bodyA.isNonStick) { + onCollide(this) + this.stuckTo = bodyCollisions[0].bodyA + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } else { + this.do = this.grow; + } + this.stuck = function () { + if (this.stuckTo) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + // Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.force.y += this.mass * 0.0006; + } + } + } else { + if (Matter.Query.collides(this, map).length) { + onCollide(this) + } else { //if colliding with nothing just fall + this.force.y += this.mass * 0.0006; + simulation.mouseInGame.x + } + } + } + //draw green glow + ctx.fillStyle = "rgba(0,200,125,0.16)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); + ctx.fill(); + } + bullet[me].grow = function () { + this.stuck(); //runs different code based on what the bullet is stuck to + let scale = 1.01 + if (tech.isSporeGrowth && !(simulation.cycle % 40)) { //release a spore + if (tech.isSporeFlea) { + if (!(simulation.cycle % 80)) { + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + } + } else if (tech.isSporeWorm) { + if (!(simulation.cycle % 80)) b.worm(this.position) + } else { + b.spore(this.position) + } + scale = 0.96 + if (this.stuckTo && this.stuckTo.alive) scale = 0.9 + Matter.Body.scale(this, scale, scale); + this.radius *= scale + } else { + if (this.stuckTo && this.stuckTo.alive) scale = 1.03 + Matter.Body.scale(this, scale, scale); + this.radius *= scale + if (this.radius > this.maxRadius) this.endCycle = 0; + } + //draw green glow + ctx.fillStyle = "rgba(0,200,125,0.16)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); + ctx.fill(); + }; + //spawn bullets on end + bullet[me].onEnd = function () { + let count = 0 //used in for loop below + const things = [ + () => { //spore + b.spore(this.position) + }, + () => { //worm + count++ //count as 2 things + b.worm(this.position) + }, + () => { //flea + count++ //count as 2 things + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + }, + () => { // drones + b.drone(this.position) + }, + () => { // ice IX + b.iceIX(1, Math.random() * 2 * Math.PI, this.position) + }, + () => { //missile + count++ //count as 2 things + b.missile(this.position, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + }, + () => { //nail + b.targetedNail(this.position, 1, 39 + 6 * Math.random()) + }, + () => { //super ball + const speed = 36 + const angle = 2 * Math.PI * Math.random() + b.superBall(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }, 11 * tech.bulletSize) + }, + ] + + for (len = this.totalSpores; count < len; count++) { + if (tech.isSporeColony && Math.random() < 0.5) { + things[Math.floor(Math.random() * things.length)]() + } else if (tech.isSporeFlea) { + things[2]() + } else if (tech.isSporeWorm) { + things[1]() + } else { + things[0]() //spores + } + } + // } else if (tech.isSporeFlea) { + // for (let i = 0, len = this.totalSpores; i < len; i++) things[2]() + // } else if (tech.isSporeWorm) { + // for (let i = 0, len = this.totalSpores; i < len; i++) things[1]() + // } else { + // for (let i = 0; i < this.totalSpores; i++) things[0]() + // } + if (tech.isStun) b.AoEStunEffect(this.position, 600, 270 + 120 * Math.random()); //AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { + } + } + }, { + name: "drones", //7 + description: "deploy autonomous drones that smash into mobs
and collect nearby power ups", //crashes reduce their lifespan by 1 second + ammo: 0, + ammoPack: 16, + defaultAmmoPack: 16, + have: false, + do() { }, + fire() { + if (tech.isDroneRadioactive) { + if (m.crouch) { + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 45) + m.fireCDcycle = m.cycle + Math.floor(45 * b.fireCDscale); // cool down + } else { + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 10) + m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); // cool down + } + } else { + if (m.crouch) { + b.drone({ + x: m.pos.x + 30 * Math.cos(m.angle) + 5 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 5 * (Math.random() - 0.5) + }, 50) + m.fireCDcycle = m.cycle + Math.floor(7 * b.fireCDscale); // cool down + } else { + b.drone({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 15) + m.fireCDcycle = m.cycle + Math.floor(4 * b.fireCDscale); // cool down + } + } + } + }, + { + name: "foam", //8 + description: "spray bubbly foam that sticks to mobs
slows mobs and does damage over time", + ammo: 0, + ammoPack: 24, + have: false, + charge: 0, + isDischarge: false, + knockBack: 0.0005, //set in tech: cavitation + applyKnock(velocity) { + player.force.x -= this.knockBack * velocity.x + player.force.y -= 2 * this.knockBack * velocity.y + }, + chooseFireMethod() { + if (tech.isFoamPressure) { + this.do = this.doCharges + this.fire = this.fireCharges + } else { + this.do = this.doStream + this.fire = this.fireStream + } + }, + doStream() { }, + fireStream() { + const spread = (m.crouch ? + 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : + 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) + ) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) } + const position = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); + }, + doCharges() { + if (this.charge > 0) { + //draw charge level + ctx.fillStyle = "rgba(0,50,50,0.3)"; + ctx.beginPath(); + const radius = 5 * Math.sqrt(this.charge) + const mag = 11 + radius + ctx.arc(m.pos.x + mag * Math.cos(m.angle), m.pos.y + mag * Math.sin(m.angle), radius, 0, 2 * Math.PI); + ctx.fill(); + + if (this.isDischarge && m.cycle % 2) { + const spread = (m.crouch ? 0.04 : 0.5) * (Math.random() - 0.5) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * 10 - radius * 0.4 + Math.min(5, Math.sqrt(this.charge)); + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + } + const position = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + this.charge -= 0.75 + m.fireCDcycle = m.cycle + 2; //disable firing and adding more charge until empty + } else if (!input.fire) { + this.isDischarge = true; + } + } else { + if (this.isDischarge) { + m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); + } + this.isDischarge = false + } + }, + fireCharges() { + const spread = (m.crouch ? + 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : + 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) + ) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + } + const position = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + // if (tech.foamFutureFire) { + // simulation.drawList.push({ //add dmg to draw queue + // x: position.x, + // y: position.y, + // radius: 5, + // color: "rgba(0,50,50,0.3)", + // time: 15 * tech.foamFutureFire + // }); + // setTimeout(() => { + // if (!simulation.paused) { + // b.foam(position, Vector.rotate(velocity, spread), radius) + // bullet[bullet.length - 1].damage *= (1 + 0.7 * tech.foamFutureFire) + // } + // }, 210 * tech.foamFutureFire); + // } else { + // } + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); + this.charge += 1 + tech.isCapacitor + }, + fire() { }, + do() { }, + }, + { + name: "harpoon", //9 + description: "fire a self-steering harpoon that uses energy
to retract and refund its ammo cost", + ammo: 0, + ammoPack: 1.7, //update this in railgun tech + have: false, + fire() { }, + do() { }, + chooseFireMethod() { + if (tech.isRailGun) { + this.do = this.railDo + this.fire = this.railFire + } else if (tech.isGrapple) { + this.do = () => { } + this.fire = this.grappleFire + } else { + this.do = () => { } + this.fire = this.harpoonFire + } + }, + charge: 0, + railDo() { + if (this.charge > 0) { + const DRAIN = (tech.isRailEnergy ? 0.0002 : 0.002) + //exit railgun charging without firing + if (m.energy < DRAIN) { + // m.energy += 0.025 + this.charge * 22 * this.drain + // m.energy -= this.drain + m.fireCDcycle = m.cycle + 120; // cool down if out of energy + this.endCycle = 0; + this.charge = 0 + b.refundAmmo() + return + } + //fire + if ((!input.fire && this.charge > 0.6)) { + // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const closest = { + distance: 10000, + target: null + } + //push away blocks and mobs + const range = 600 + 500 * this.charge + for (let i = 0, len = mob.length; i < len; ++i) { //push away mobs when firing + if (!mob[i].isUnblockable) { + const SUB = Vector.sub(mob[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range + mob[i].radius) { + const DEPTH = 100 + Math.min(range - DISTANCE + mob[i].radius, 1500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.0015 * Math.sqrt(DEPTH) * mob[i].mass) + mob[i].force.x += FORCE.x; + mob[i].force.y += FORCE.y; + + let dmg = m.dmgScale * (mob[i].isDropPowerUp ? 350 : 1100) * tech.harpoonDensity * this.charge + simulation.drawList.push({ //add dmg to draw queue + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.log(dmg + 1.1) * 40 * mob[i].damageReduction + 3, + color: 'rgba(100, 0, 200, 0.4)', + time: 15 + }); + mob[i].damage(dmg); + } + } + } + for (let i = 0, len = body.length; i < len; ++i) { //push away blocks when firing + const SUB = Vector.sub(body[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range) { + const DEPTH = Math.min(range - DISTANCE, 500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.003 * Math.sqrt(DEPTH) * body[i].mass) + body[i].force.x += FORCE.x; + body[i].force.y += FORCE.y - body[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + } + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push away blocks when firing + const SUB = Vector.sub(powerUp[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range) { + const DEPTH = Math.min(range - DISTANCE, 500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.002 * Math.sqrt(DEPTH) * powerUp[i].mass) + powerUp[i].force.x += FORCE.x; + powerUp[i].force.y += FORCE.y - powerUp[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + } + } + //draw little dots near the edge of range + for (let i = 0, len = 10 + 25 * this.charge; i < len; i++) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, 6.28 * Math.random()) + const where = Vector.add(m.pos, Vector.mult(unit, range * (0.6 + 0.3 * Math.random()))) + simulation.drawList.push({ + x: where.x, + y: where.y, + radius: 5 + 12 * Math.random(), + color: "rgba(100, 0, 200, 0.1)", + time: Math.floor(5 + 35 * Math.random()) + }); + } + + const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.03 : 0.06) + player.force.x -= recoil.x + player.force.y -= recoil.y + // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + + const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 + const thrust = 0.15 * (this.charge) + if (tech.extraHarpoons) { + let targetCount = 0 + const SPREAD = 0.06 + 0.05 * (!m.crouch) + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { + x: Math.cos(angle), + y: Math.sin(angle) + }; //make a vector for the player's direction of length 1; used in dot product + + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + // if (this.ammo > -1) { + // this.ammo-- + b.harpoon(where, m.crouch ? null : mob[i], angle, harpoonSize, false, 35, false, thrust) //harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + angle += SPREAD + targetCount++ + if (targetCount > tech.extraHarpoons) break + // } + } + } + } + //if more harpoons and no targets left + if (targetCount < tech.extraHarpoons + 1) { + const num = tech.extraHarpoons + 1 - targetCount + for (let i = 0; i < num; i++) { + b.harpoon(where, null, angle, harpoonSize, false, 35, false, thrust) + angle += SPREAD + } + } + simulation.updateGunHUD(); + } else { + //look for closest mob in player's LoS + const dir = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + }; //make a vector for the player's direction of length 1; used in dot product + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target + closest.distance = dist + closest.target = mob[i] + } + } + } + b.harpoon(where, closest.target, m.angle, harpoonSize, false, 35, false, thrust) + } + + this.charge = 0; + } else { //charging + if (tech.isFireMoveLock) { + Matter.Body.setVelocity(player, { + x: 0, + y: -55 * player.mass * simulation.g //undo gravity before it is added + }); + player.force.x = 0 + player.force.y = 0 + } + m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released + // const previousCharge = this.charge + + //small b.fireCDscale = faster shots, b.fireCDscale=1 = normal shot, big b.fireCDscale = slower chot + // let smoothRate = tech.isCapacitor ? 0.85 : Math.min(0.998, 0.985 * (0.98 + 0.02 * b.fireCDscale)) + const rate = Math.sqrt(b.fireCDscale) * tech.railChargeRate * (tech.isCapacitor ? 0.6 : 1) * (m.crouch ? 0.8 : 1) + let smoothRate = Math.min(0.998, 0.94 + 0.05 * rate) + + + this.charge = 1 - smoothRate + this.charge * smoothRate + if (m.energy > DRAIN) m.energy -= DRAIN + + //draw magnetic field + const X = m.pos.x + const Y = m.pos.y + const unitVector = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + const unitVectorPerp = Vector.perp(unitVector) + + function magField(mag, arc) { + ctx.moveTo(X, Y); + ctx.bezierCurveTo( + X + unitVector.x * mag, Y + unitVector.y * mag, + X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, + X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) + ctx.bezierCurveTo( + X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, + X - unitVector.x * mag, Y - unitVector.y * mag, + X, Y) + } + ctx.fillStyle = `rgba(50,0,100,0.05)`; + const magSize = 8 * this.charge * tech.railChargeRate ** 3 + const arcSize = 6 * this.charge * tech.railChargeRate ** 3 + for (let i = 3; i < 7; i++) { + const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) + const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) + ctx.beginPath(); + magField(MAG, ARC) + magField(MAG, -ARC) + ctx.fill(); + } + } + } + }, + railFire() { + m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released + this.charge += 0.00001 + }, + grappleFire() { + const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) + const where = { + x: m.pos.x + harpoonSize * 40 * Math.cos(m.angle), + y: m.pos.y + harpoonSize * 40 * Math.sin(m.angle) + } + const num = Math.min(this.ammo, tech.extraHarpoons + 1) + if (!m.crouch && num > 1) { //multiple harpoons + const SPREAD = 0.06 + let angle = m.angle - SPREAD * num / 2; + for (let i = 0; i < num; i++) { + if (this.ammo > 0) { + this.ammo-- + b.grapple(where, angle, true, harpoonSize) + angle += SPREAD + } + } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); + m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down + // } else if (m.crouch) { + // b.harpoon(where, null, m.angle, harpoonSize, false, 70) + } else { + if (tech.crouchAmmoCount) tech.crouchAmmoCount = 1 + b.grapple(where, m.angle, harpoonSize) + } + // m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down + m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) + + }, + harpoonFire() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const closest = { + distance: 10000, + target: null + } + //look for closest mob in player's LoS + const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) + const totalCycles = 6.5 * (tech.isFilament ? 1 + 0.013 * Math.min(110, this.ammo) : 1) * Math.sqrt(harpoonSize) + + if (tech.extraHarpoons && !m.crouch) { //multiple harpoons + const SPREAD = 0.2 + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { + x: Math.cos(angle), + y: Math.sin(angle) + }; //make a vector for the player's direction of length 1; used in dot product + const range = 450 * (tech.isFilament ? 1 + 0.012 * Math.min(110, this.ammo) : 1) + let targetCount = 0 + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < range && dot > 0.9) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + if (this.ammo > 0) { + this.ammo-- + b.harpoon(where, mob[i], angle, harpoonSize, true, totalCycles) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) + angle += SPREAD + targetCount++ + if (targetCount > tech.extraHarpoons) break + } + } + } + } + //if more harpoons and no targets left + if (targetCount < tech.extraHarpoons + 1) { + const num = tech.extraHarpoons - targetCount + const delay = 1 //Math.floor(Math.max(4, 8 - 0.5 * tech.extraHarpoons)) + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + let count = -1 + let harpoonDelay = () => { + if (simulation.paused) { + requestAnimationFrame(harpoonDelay) + } else { + count++ + if (!(count % delay) && this.ammo > 0) { + this.ammo-- + b.harpoon({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, null, angle, harpoonSize, true, totalCycles) + angle += SPREAD + tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + } + if (count < num * delay && m.alive) requestAnimationFrame(harpoonDelay); + } + } + requestAnimationFrame(harpoonDelay) + } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); + + } else { //m.crouch makes a single harpoon with longer range + const dir = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + }; //make a vector for the player's direction of length 1; used in dot product + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target + closest.distance = dist + closest.target = mob[i] + } + } + } + if (m.crouch && m.onGround) { + b.harpoon(where, null, m.angle, harpoonSize, true, 1.6 * totalCycles, (m.crouch && tech.crouchAmmoCount && (tech.crouchAmmoCount - 1) % 2) ? false : true) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true) { + } else { + b.harpoon(where, closest.target, m.angle, harpoonSize, true, totalCycles) + } + tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + } + m.fireCDcycle = m.cycle + 5 + 35 * b.fireCDscale + 60 * (m.energy < 0.05) + tech.extraHarpoons // cool down is set when harpoon bullet returns to player + const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.015 : 0.035) + player.force.x -= recoil.x + player.force.y -= recoil.y + }, + }, { + name: "mine", //10 + description: "toss a proximity mine that sticks to walls
refund undetonated mines on exiting a level", //fires nails at mobs within range + ammo: 0, + ammoPack: 1.7, + have: false, + nameString(suffix = "") { + if (tech.isFoamMine) { + return `foam` + } else if (tech.isSuperMine) { + return `super ball${suffix}` + } else { + return `nail${suffix}` + } + }, + do() { + if (!input.field && m.crouch && !tech.isLaserMine) { + const cycles = 60 //30 + const speed = 40 + const v = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } //m.Vy / 2 + removed to make the path less jerky + ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map + ctx.lineWidth = 2 + ctx.beginPath() + for (let i = 1.5, len = 19; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.stroke() + } + }, + fire() { + if (m.crouch) { + if (tech.isLaserMine) { + const speed = 30 + const velocity = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } + b.laserMine(m.pos, velocity) + m.fireCDcycle = m.cycle + Math.floor(65 * b.fireCDscale); // cool down + } else { + const pos = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + let speed = 36 + if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map + b.mine(pos, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 0) + m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCDscale); // cool down + } + } else { + const pos = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + let speed = 23 + if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map + b.mine(pos, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 0) + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down + } + } + }, + { + name: "laser", //11 + description: "emit a beam of collimated coherent light
drains energy instead of ammunition", + ammo: 0, + ammoPack: Infinity, + have: false, + charge: 0, + isStuckOn: false, + angle: 0, + isInsideArc(angle) { + const mod = (a, n) => { + return a - Math.floor(a / n) * n + } + let diff = mod(angle - this.angle + Math.PI, 2 * Math.PI) - Math.PI + return Math.abs(diff) < this.arcRange + }, + arcRange: 0.78, //1.57, + lensDamage: 1, + lensDamageOn: 0, //set in tech + lens() { + this.stuckOn(); + this.angle += 0.02 + if (this.isInsideArc(m.angle)) { + this.lensDamage = this.lensDamageOn + ctx.lineWidth = 6 + this.lensDamageOn + } else { + this.lensDamage = 1 + ctx.lineWidth = 2 + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 60, this.angle - this.arcRange, this.angle + this.arcRange); + ctx.strokeStyle = '#fff' //'rgba(255,255,255,0.9)' //'hsl(189, 100%, 95%)' + ctx.stroke(); + // const a = { x: radius * Math.cos(this.angle + this.arcRange), y: radius * Math.sin(this.angle + this.arcRange) } + // const b = Vector.add(m.pos, a) + // ctx.lineTo(b.x, b.y) + // ctx.fillStyle = '#fff' + // ctx.fill() + }, + stuckOn() { + if (tech.isStuckOn) { + if (this.isStuckOn) { + if (!input.fire) this.fire(); + if (m.energy < tech.laserDrain) this.isStuckOn = false + } else if (input.fire) { + this.isStuckOn = true + } + } + }, + do() { }, + fire() { }, + chooseFireMethod() { + this.lensDamage = 1 + if (tech.isLaserLens) { + this.do = this.lens + } else { + this.do = this.stuckOn + } + if (tech.isPulseLaser) { + this.fire = () => { + const drain = Math.min(0.9 * m.maxEnergy, 0.01 * (tech.isCapacitor ? 10 : 1) / b.fireCDscale) + if (m.energy > drain && this.charge < 50 * m.maxEnergy) { + m.energy -= drain + this.charge += drain * 100 + } + } + if (tech.historyLaser) { + const len = 1 + tech.historyLaser + const spacing = Math.ceil(30 - 2 * tech.historyLaser) + this.do = () => { + if (tech.isLaserLens) this.lens() + if (this.charge > 0) { + //draw charge level + const mag = 4.1 * Math.sqrt(this.charge) + ctx.beginPath(); + for (let i = 0; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + ctx.moveTo(history.position.x, history.position.y - off); + ctx.ellipse(history.position.x, history.position.y - off, mag, mag * 0.65, history.angle, 0, 2 * Math.PI) + } + ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fill(); + //fire + if (!input.fire) { + if (this.charge > 5) { + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down + for (let i = 0; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + b.pulse(1.65 * this.charge * this.lensDamage, history.angle, { + x: history.position.x, + y: history.position.y - off + }) + } + } + this.charge = 0; + } + } + }; + } else { + this.do = () => { + if (tech.isLaserLens) this.lens() + if (this.charge > 0) { + //draw charge level + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 4.2 * Math.sqrt(this.charge), 0, 2 * Math.PI); + // ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fill(); + //fire + if (!input.fire) { + if (this.charge > 5) { + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down + if (tech.beamSplitter) { + const divergence = m.crouch ? 0.15 : 0.35 + const angle = m.angle - tech.beamSplitter * divergence / 2 + for (let i = 0; i < 1 + tech.beamSplitter; i++) b.pulse(this.charge, angle + i * divergence) + } else { + b.pulse(1.8 * this.charge * this.lensDamage, m.angle) + } + } + this.charge = 0; + } + } + }; + } + + } else if (tech.beamSplitter) { + this.fire = this.fireSplit + } else if (tech.historyLaser) { + this.fire = this.fireHistory + } else if (tech.isWideLaser) { + this.fire = this.fireWideBeam + } else { + this.fire = this.fireLaser + } + // this.fire = this.firePhoton + }, + fireLaser() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const where = { + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + } + b.laser(where, { + x: where.x + 3000 * Math.cos(m.angle), + y: where.y + 3000 * Math.sin(m.angle) + }, tech.laserDamage / b.fireCDscale * this.lensDamage); + } + }, + firePulse() { }, + fireSplit() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + // const divergence = m.crouch ? 0.15 : 0.2 + // const scale = Math.pow(0.9, tech.beamSplitter) + // const pushScale = scale * scale + let dmg = tech.laserDamage / b.fireCDscale * this.lensDamage // * scale //Math.pow(0.9, tech.laserDamage) + const where = { + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + } + const divergence = m.crouch ? 0.15 : 0.35 + const angle = m.angle - tech.beamSplitter * divergence / 2 + for (let i = 0; i < 1 + tech.beamSplitter; i++) { + b.laser(where, { + x: where.x + 3000 * Math.cos(angle + i * divergence), + y: where.y + 3000 * Math.sin(angle + i * divergence) + }, dmg, tech.laserReflections, false) + } + } + }, + fireWideBeam() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const range = { + x: 5000 * Math.cos(m.angle), + y: 5000 * Math.sin(m.angle) + } + const rangeOffPlus = { + x: 7.5 * Math.cos(m.angle + Math.PI / 2), + y: 7.5 * Math.sin(m.angle + Math.PI / 2) + } + const rangeOffMinus = { + x: 7.5 * Math.cos(m.angle - Math.PI / 2), + y: 7.5 * Math.sin(m.angle - Math.PI / 2) + } + const dmg = 0.70 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const eye = { + x: m.pos.x + 15 * Math.cos(m.angle), + y: m.pos.y + 15 * Math.sin(m.angle) + } + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 8 + ctx.globalAlpha = 0.5; + ctx.beginPath(); + if (Matter.Query.ray(map, eye, where).length === 0 && Matter.Query.ray(body, eye, where).length === 0) { + b.laser(eye, { + x: eye.x + range.x, + y: eye.y + range.y + }, dmg, 0, true, 0.3) + } + for (let i = 1; i < tech.wideLaser; i++) { + let whereOff = Vector.add(where, { + x: i * rangeOffPlus.x, + y: i * rangeOffPlus.y + }) + if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { + ctx.moveTo(eye.x, eye.y) + ctx.lineTo(whereOff.x, whereOff.y) + b.laser(whereOff, { + x: whereOff.x + range.x, + y: whereOff.y + range.y + }, dmg, 0, true, 0.3) + } + whereOff = Vector.add(where, { + x: i * rangeOffMinus.x, + y: i * rangeOffMinus.y + }) + if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { + ctx.moveTo(eye.x, eye.y) + ctx.lineTo(whereOff.x, whereOff.y) + b.laser(whereOff, { + x: whereOff.x + range.x, + y: whereOff.y + range.y + }, dmg, 0, true, 0.3) + } + } + ctx.stroke(); + if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + ctx.lineWidth = 20 + 3 * b.guns[11].lensDamageOn + ctx.globalAlpha = 0.3 + ctx.stroke(); + } + ctx.globalAlpha = 1; + } + }, + fireHistory() { + drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const dmg = 0.5 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage + const spacing = Math.ceil(10 - 0.4 * tech.historyLaser) + ctx.beginPath(); + b.laser({ + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + }, { + x: m.pos.x + 3000 * Math.cos(m.angle), + y: m.pos.y + 3000 * Math.sin(m.angle) + }, dmg, 0, true, 0.2); + for (let i = 1, len = 3 + tech.historyLaser * 3; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + b.laser({ + x: history.position.x + 20 * Math.cos(history.angle), + y: history.position.y + 20 * Math.sin(history.angle) - off + }, { + x: history.position.x + 3000 * Math.cos(history.angle), + y: history.position.y + 3000 * Math.sin(history.angle) - off + }, dmg, 0, true, 0.2); + } + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 1 + ctx.stroke(); + if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 10 + 2 * b.guns[11].lensDamageOn + ctx.globalAlpha = 0.2 + ctx.stroke(); //glow + ctx.globalAlpha = 1; + } + } + }, + }, + ], +}; \ No newline at end of file diff --git a/ngon/js/engine.js b/ngon/js/engine.js new file mode 100644 index 00000000..7a0b7084 --- /dev/null +++ b/ngon/js/engine.js @@ -0,0 +1,332 @@ +//matter.js *********************************************************** +// module aliases +const Engine = Matter.Engine, + Events = Matter.Events, + Composites = Matter.Composites, + Composite = Matter.Composite, + Constraint = Matter.Constraint, + Vertices = Matter.Vertices, + Query = Matter.Query, + Body = Matter.Body, + Bodies = Matter.Bodies, + Vector = Matter.Vector; + +// create an engine +const engine = Engine.create(); +engine.world.gravity.scale = 0; //turn off gravity (it's added back in later) +// engine.velocityIterations = 100 +// engine.positionIterations = 100 +// engine.enableSleeping = true + +// matter events +function playerOnGroundCheck(event) { + //runs on collisions events + function enter() { + m.numTouching++; + if (!m.onGround) { + m.onGround = true; + if (m.crouch) { + if (m.checkHeadClear()) { + m.undoCrouch(); + } else { + m.yOffGoal = m.yOffWhen.crouch; + } + } else { + //sets a hard land where player stays in a crouch for a bit and can't jump + //crouch is forced in groundControl below + const momentum = player.velocity.y * player.mass //player mass is 5 so this triggers at 26 down velocity, unless the player is holding something + if (momentum > tech.hardLanding) { + m.doCrouch(); + m.yOff = m.yOffWhen.jump; + m.hardLandCD = m.cycle + Math.min(momentum / 6.5 - 6, 40) + //falling damage + if (tech.isFallingDamage && m.immuneCycle < m.cycle && momentum > 150) { + m.damage(Math.min(Math.sqrt(momentum - 133) * 0.01, 0.25)); + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + } + } else { + m.yOffGoal = m.yOffWhen.stand; + } + } + } + } + + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + let pair = pairs[i]; + if (pair.bodyA === jumpSensor) { + m.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump + if (m.standingOn.alive !== true) enter(); + } else if (pair.bodyB === jumpSensor) { + m.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump + if (m.standingOn.alive !== true) enter(); + } + } + m.numTouching = 0; +} + +function playerOffGroundCheck(event) { + //runs on collisions events + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + if (pairs[i].bodyA === jumpSensor || pairs[i].bodyB === jumpSensor) { + if (m.onGround && m.numTouching === 0) { + m.onGround = false; + m.lastOnGroundCycle = m.cycle; + m.hardLandCD = 0 // disable hard landing + if (m.checkHeadClear()) { + if (m.crouch) { + m.undoCrouch(); + } + m.yOffGoal = m.yOffWhen.jump; + } + } + } + } +} + +function collisionChecks(event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + //mob + (player,bullet,body) collisions + for (let k = 0; k < mob.length; k++) { + if (mob[k].alive) { + if (pairs[i].bodyA === mob[k]) { + collideMob(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === mob[k]) { + collideMob(pairs[i].bodyA); + break; + } + + function collideMob(obj) { + //player + mob collision + if ( + m.immuneCycle < m.cycle && + (obj === playerBody || obj === playerHead) && + !mob[k].isSlowed && !mob[k].isStunned + ) { + let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * simulation.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 + // if (m.isCloak) dmg *= 0.5 + mob[k].foundPlayer(); + if (tech.isRewindAvoidDeath && (m.energy + 0.05) > Math.min(0.95, m.maxEnergy) && dmg > 0.01) { //CPT reversal runs in m.damage, but it stops the rest of the collision code here too + m.damage(dmg); + return + } + if (tech.isFlipFlop) { + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false + if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = OFF` + m.eyeFillColor = 'transparent' + m.damage(dmg); + } else { + tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit + if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#0cf' + if (!tech.isFlipFlopHarm) m.damage(dmg); + } + if (tech.isFlipFlopHealth) { + m.setMaxHealth(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + } + } else { + m.damage(dmg); //normal damage + } + + if (tech.isCollisionRealitySwitch && m.alive) { + m.switchWorlds() + simulation.trails() + simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + } + if (tech.isPiezo) m.energy += 20.48; + if (tech.isCouplingNoHit && m.coupling > 0) { + m.couplingChange(-5) + + const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) + let where = Vector.add(m.pos, Vector.mult(unit, 17)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 22, + color: 'rgba(0, 171, 238, 0.33)', + time: 8 + }); + where = Vector.add(m.pos, Vector.mult(unit, 60)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 18, + color: 'rgba(0, 171, 238, 0.5)', + time: 16 + }); + where = Vector.add(m.pos, Vector.mult(unit, 100)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 14, + color: 'rgba(0, 171, 238, 0.6)', + time: 24 + }); + where = Vector.add(m.pos, Vector.mult(unit, 135)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 10, + color: 'rgba(0, 171, 238, 0.7)', + time: 32 + }); + // simulation.drawList.push({ //add dmg to draw queue + // x: m.pos.x, + // y: m.pos.y, + // radius: 150, + // color: 'rgba(0, 171, 238, 0.33)', + // time: 6 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: m.pos.x, + // y: m.pos.y, + // radius: 75, + // color: 'rgba(0, 171, 238, 0.5)', + // time: 16 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: m.pos.x, + // y: m.pos.y, + // radius: 25, + // color: 'rgba(0, 171, 238, 0.75)', + // time: 25 + // }); + } + if (tech.isStimulatedEmission) powerUps.ejectTech() + if (mob[k].onHit) mob[k].onHit(); + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + //extra kick between player and mob //this section would be better with forces but they don't work... + let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); + Matter.Body.setVelocity(player, { + x: player.velocity.x + 8 * Math.cos(angle), + y: player.velocity.y + 8 * Math.sin(angle) + }); + Matter.Body.setVelocity(mob[k], { + x: mob[k].velocity.x - 8 * Math.cos(angle), + y: mob[k].velocity.y - 8 * Math.sin(angle) + }); + + if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && m.energy > 0.34 * m.maxEnergy && mob[k].damageReduction > 0) { + m.energy -= 0.33 * Math.max(m.maxEnergy, m.energy) //0.33 * m.energy + if (m.immuneCycle === m.cycle + m.collisionImmuneCycles) m.immuneCycle = 0; //player doesn't go immune to collision damage + mob[k].death(); + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 500, + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + // return; + // } + } else { + //mob + bullet collisions + if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { + obj.beforeDmg(mob[k]); //some bullets do actions when they hits things, like despawn //forces don't seem to work here + let dmg = m.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))) + if (tech.isCrit && mob[k].isStunned) dmg *= 4 + // console.log(dmg) //remove this + mob[k].damage(dmg); + if (mob[k].alive) mob[k].foundPlayer(); + if (mob[k].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.log(dmg + 1.1) * 40 * mob[k].damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + if (tech.isLessDamageReduction && !mob[k].shield) mob[k].damageReduction *= mob[k].isBoss ? (mob[k].isFinalBoss ? 1.0005 : 1.0025) : 1.05 + return; + } + //mob + body collisions + if (obj.classType === "body" && obj.speed > 6) { + const v = Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity)); + if (v > 9) { + if (tech.blockDmg) { //electricity + Matter.Body.setVelocity(mob[k], { x: 0.5 * mob[k].velocity.x, y: 0.5 * mob[k].velocity.y }); + if (tech.isBlockRadiation && !mob[k].isShielded && !mob[k].isMobBullet) { + mobs.statusDoT(mob[k], tech.blockDmg * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + } else { + mob[k].damage(tech.blockDmg * m.dmgScale) + simulation.drawList.push({ + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: 28 * mob[k].damageReduction + 3, + color: "rgba(255,0,255,0.8)", + time: 4 + }); + } + } + + let dmg = tech.blockDamage * m.dmgScale * v * obj.mass * (tech.isMobBlockFling ? 2.5 : 1) * (tech.isBlockRestitution ? 2.5 : 1) * ((m.fieldMode === 0 || m.fieldMode === 8) ? 1 + 0.04 * m.coupling : 1); + if (mob[k].isShielded) dmg *= 0.7 + + mob[k].damage(dmg, true); + if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && m.throwCycle > m.cycle) { + options = ["coupling", "boost", "heal", "research"] + if (!tech.isEnergyNoAmmo) options.push("ammo") + powerUps.spawn(mob[k].position.x, mob[k].position.y, options[Math.floor(Math.random() * options.length)]); + } + + const stunTime = dmg / Math.sqrt(obj.mass) + if (stunTime > 0.5 && mob[k].memory !== Infinity) mobs.statusStun(mob[k], 60 + 60 * Math.sqrt(stunTime)) + if (mob[k].alive && mob[k].distanceToPlayer2() < 1000000 && !m.isCloak) mob[k].foundPlayer(); + if (tech.fragments && obj.speed > 10 && !obj.hasFragmented) { + obj.hasFragmented = true; + b.targetedNail(obj.position, tech.fragments * 4) + } + if (mob[k].damageReduction) { + simulation.drawList.push({ + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.log(dmg + 1.1) * 40 * mob[k].damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + return; + } + } + } + } + } + } + } +} + +//determine if player is on the ground +Events.on(engine, "collisionStart", function (event) { + playerOnGroundCheck(event); + // playerHeadCheck(event); + collisionChecks(event); +}); +Events.on(engine, "collisionActive", function (event) { + playerOnGroundCheck(event); + // playerHeadCheck(event); +}); +Events.on(engine, "collisionEnd", function (event) { + playerOffGroundCheck(event); +}); \ No newline at end of file diff --git a/ngon/js/index.js b/ngon/js/index.js new file mode 100644 index 00000000..05b0913d --- /dev/null +++ b/ngon/js/index.js @@ -0,0 +1,1944 @@ +"use strict"; + +//convert text into numbers for seed +Math.hash = s => { + for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); + return h ^ h >>> 9 +} + +// const date1 = new Date() +// console.log(date1.getUTCHours()) +// document.getElementById("seed").placeholder = Math.initialSeed = String(date1.getUTCDate() * date1.getUTCFullYear()) // daily seed, day + year + +// document.getElementById("seed").placeholder = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC + +document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) +Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it +Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined + Math.seed = (Math.seed * 9301 + 49297) % 233280; + return min + Math.seed / 233280 * (max - min); +} +//Math.seed is set to document.getElementById("seed").value in level.populate level at the start of runs +// console.log(Math.seed) + + +function shuffle(array) { + var currentIndex = array.length, + temporaryValue, + randomIndex; + // While there remain elements to shuffle... + while (0 !== currentIndex) { + // Pick a remaining element... + // randomIndex = Math.floor(Math.random() * currentIndex); + randomIndex = Math.floor(Math.seededRandom(0, currentIndex)) //Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + return array; +} + +//collision groups +// cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield | cat.phased +const cat = { + player: 0x1, + map: 0x10, + body: 0x100, + bullet: 0x1000, + powerUp: 0x10000, + mob: 0x100000, + mobBullet: 0x1000000, + mobShield: 0x10000000, + phased: 0x100000000, +} + +let color = { //light + // background: "#ddd", // used instead: document.body.style.backgroundColor + block: "rgba(140,140,140,0.85)", + blockS: "#222", + map: "#444", + bullet: "#000" +} + +// const color = { //dark +// background: "#333", +// block: "#444", +// blockS: "#aab", +// map: "#556", +// bullet: "#fff" +// } + +// const color = { //dark +// background: "#999", +// block: "#888", +// blockS: "#111", +// map: "#444", +// } + +// shrink power up selection menu +// if (screen.height < 800) { +// document.getElementById("choose-grid").style.fontSize = "1em"; //1.3em is normal +// if (screen.height < 600) document.getElementById("choose-grid").style.fontSize = "0.8em"; //1.3em is normal +// } + + +//********************************************************************** +// check for URL parameters to load an experimental game +//********************************************************************** + +//example https://landgreen.github.io/sidescroller/index.html? +// &gun1=minigun&gun2=laser +// &tech1=laser-bot&tech2=mass%20driver&tech3=overcharge&tech4=laser-bot&tech5=laser-bot&field=phase%20decoherence%20field&difficulty=2 +//add ? to end of url then for each power up add +// &gun1=name&gun2=name +// &tech1=laser-bot&tech2=mass%20driver&tech3=overcharge&tech4=laser-bot&tech5=laser-bot +// &field=phase%20decoherence%20field +// &difficulty=2 +//use %20 for spaces +//difficulty is 0 easy, 1 normal, 2 hard, 4 why +function getUrlVars() { + let vars = {}; + window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, k, v) { + vars[k] = v; + }); + return vars; +} +window.addEventListener('load', () => { + const set = getUrlVars() + if (Object.keys(set).length !== 0) { + // build.populateGrid() //trying to solve a bug with this, but maybe it doesn't help + openExperimentMenu(); + //add experimental selections based on url + for (const property in set) { + set[property] = set[property].replace(/%20/g, " ") + set[property] = set[property].replace(/%27/g, "'") + set[property] = set[property].replace(/%CE%A8/g, "Ψ") + if (property === "field") { + let found = false + let index + for (let i = 0; i < m.fieldUpgrades.length; i++) { + if (set[property] === m.fieldUpgrades[i].name) { + index = i; + found = true; + break; + } + } + if (found) build.choosePowerUp(index, 'field') + } + if (property.substring(0, 3) === "gun") { + let found = false + let index + for (let i = 0; i < b.guns.length; i++) { + if (set[property] === b.guns[i].name) { + index = i; + found = true; + break; + } + } + if (found) build.choosePowerUp(index, 'gun') + } + if (property.substring(0, 4) === "tech") { + for (let i = 0; i < tech.tech.length; i++) { + if (set[property] === tech.tech[i].name) { + build.choosePowerUp(i, 'tech', true) + break; + } + } + } + + if (property === "difficulty") { + simulation.difficultyMode = Number(set[property]) + lore.setTechGoal() + document.getElementById("difficulty-select-experiment").value = Number(set[property]) + } + if (property === "molMode") { + simulation.molecularMode = Number(set[property]) + const i = 4 //update experiment text + m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() + document.getElementById(`field-${i}`).innerHTML = `
+
  ${build.nameLink(m.fieldUpgrades[i].name)}
+ ${m.fieldUpgrades[i].description}
` + } + // if (property === "seed") { + // document.getElementById("seed").placeholder = Math.initialSeed = String(set[property]) + // Math.seed = Math.abs(Math.hash(Math.initialSeed)) + // level.populateLevels() + // } + requestAnimationFrame(() => { build.sortTech('have', true) }); + + } + } else if (localSettings.isTrainingNotAttempted && localSettings.runCount < 30) { //make training button more obvious for new players + // document.getElementById("training-button").style.border = "0px #333 solid"; + // document.getElementById("training-button").style.fill = "rgb(0, 150, 235)" //"#fff"; + // document.getElementById("training-button").style.background = "rgb(0, 200, 255)"; + + //css classes not working for some reason + // document.getElementById("training-button").classList.add('lore-text'); + + let myanim = document.createElementNS("http://www.w3.org/2000/svg", 'animate'); + myanim.setAttribute("id", "myAnimation"); + myanim.setAttribute("attributeType", "XML"); + myanim.setAttribute("attributeName", "fill"); + // myanim.setAttribute("values", "#f55;#cc5;#5c5;#5dd;#66f;#5dd;#5c5;#cc5;#f55"); //rainbow + myanim.setAttribute("values", "#5dd;#66f;#5dd"); + // myanim.setAttribute("values", "#333;rgb(0, 170, 255);#333"); + myanim.setAttribute("dur", "3s"); + myanim.setAttribute("repeatCount", "indefinite"); + document.getElementById("training-button").appendChild(myanim); + document.getElementById("myAnimation").beginElement(); + } +}); + + +//********************************************************************** +//set up canvas +//********************************************************************** +const canvas = document.getElementById("canvas"); +//using "const" causes problems in safari when an ID shares the same name. +const ctx = canvas.getContext("2d"); +// const ctx = canvas.getContext('2d', { alpha: false }); //optimization, this works if you wipe with the background color of each level + +document.body.style.backgroundColor = "#fff"; + +//disable pop up menu on right click +document.oncontextmenu = function () { + return false; +} + +function setupCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + canvas.width2 = canvas.width / 2; //precalculated because I use this often (in mouse look) + canvas.height2 = canvas.height / 2; + ctx.font = "25px Arial"; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + simulation.setZoom(); +} +setupCanvas(); +window.onresize = () => { + setupCanvas(); +}; + +//********************************************************************** +// experimental build grid display and pause +//********************************************************************** +//set wikipedia link +for (let i = 0, len = tech.tech.length; i < len; i++) { + if (!tech.tech[i].link) tech.tech[i].link = `${tech.tech[i].name}` +} +const build = { + pixelDraw() { + let count = 0 + let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + let data = imgData.data; + + function loop() { + count++ + if (!(count % 2)) { + for (let y = 0; y < canvas.height; ++y) { + for (let x = 0; x < canvas.width; x += 1) { + const index = (y * canvas.width + x) * 4; + // let mag = 0; + // for (let j = 0, len = who.length; j < len; j++) { + // const dx = who[j].position.x - x; + // const dy = who[j].position.y - y; + // mag -= who[j].charge / (Math.sqrt(dx * dx + dy * dy) + 1); + // } + + //get dark + // data[index + 0] *= 0.96 + // data[index + 1] *= 0.96 + // data[index + 2] *= 0.96 + // data[index + 3] -= 1; // alpha + + //invert + data[index + 0] = 255 - data[index + 0] // red + data[index + 1] = 255 - data[index + 1] // green + data[index + 2] = 255 - data[index + 2] // blue + } + } + + + // fade alpha for all pixels + // for (let i = 0; i < data.length; i += 4) { + // if (data[i + 3] > 0) { + // data[i + 3]--; + // } + // } + + //add random speckles + // for (let i = 0, len = Math.floor(data.length / 15000); i < len; ++i) { + // const index = Math.floor((Math.random() * data.length) / 4) * 4; + // data[index + 0] = 255; // red + // data[index + 1] = 255; // green + // data[index + 2] = 255; // blue + // data[index + 3] = Math.floor(Math.random() * Math.random() * 155); // alpha + // } + + // ctx.putImageData(imgData, 0, 1); //pixels fall because of the 1 in third parameter + ctx.putImageData(imgData, 0, 0); + } + if (simulation.paused && m.alive) requestAnimationFrame(loop); + } + requestAnimationFrame(loop); + }, + showImages(from) { //on click event: from all 3 different places to hide / show images + localSettings.isHideImages = !localSettings.isHideImages + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (from === 'experiment') { + build.reset(); + } else if (from === 'pause') { + build.unPauseGrid() + build.pauseGrid() //redraw pause text with images + } + if (localSettings.isHideImages) { + document.getElementById("choose-grid").classList.add('choose-grid-no-images'); + document.getElementById("choose-grid").classList.remove('choose-grid'); + } else { + document.getElementById("choose-grid").classList.add('choose-grid'); + document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); + } + document.getElementById("hide-images").checked = localSettings.isHideImages + // console.log(localSettings.isHideImages, from) + }, + hideHUD() { + + if (simulation.isTraining) { + localSettings.isHideHUD = false + } else { + localSettings.isHideHUD = !localSettings.isHideHUD + } + + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + document.getElementById("hide-hud").checked = localSettings.isHideHUD + document.getElementById("hide-hud").classList.toggle("ticked") + + simulation.removeEphemera("dmgDefBars") + if (!localSettings.isHideHUD) { + simulation.ephemera.push({ + name: "dmgDefBars", count: 0, do() { + if (!(m.cycle % 15)) { //4 times a second + const defense = m.defense() //update defense bar + if (m.lastCalculatedDefense !== defense) { + document.getElementById("defense-bar").style.width = Math.floor(300 * m.maxHealth * (1 - defense)) + "px"; + m.lastCalculatedDefense = defense + } + const damage = tech.damageFromTech() //update damage bar + if (m.lastCalculatedDamage !== damage) { + document.getElementById("damage-bar").style.height = Math.floor((Math.atan(0.25 * damage - 0.25) + 0.25) * 0.53 * canvas.height) + "px"; + m.lastCalculatedDamage = damage + } + } + }, + }) + } + }, + pauseGrid() { + // build.pixelDraw(); + + build.generatePauseLeft() //makes the left side of the pause menu with the tech + build.generatePauseRight() //makes the right side of the pause menu with the tech + + document.getElementById("tech").style.display = "none" + document.getElementById("guns").style.display = "none" + document.getElementById("field").style.display = "none" + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + + + //show in game console + // document.getElementById("text-log").style.display = "inline" + simulation.lastLogTime = m.cycle //hide in game console + + }, + generatePauseLeft() { + //used for junk estimation + let junkCount = 0 + let totalCount = 1 //start at one to avoid NaN issues + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) { + totalCount += tech.tech[i].frequency + if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency + } + } + //left side + let botText = "" + if (tech.nailBotCount) botText += `
nail-bots: ${tech.nailBotCount}` + if (tech.orbitBotCount) botText += `
orbital-bots: ${tech.orbitBotCount}` + if (tech.boomBotCount) botText += `
boom-bots: ${tech.boomBotCount}` + if (tech.laserBotCount) botText += `
laser-bots: ${tech.laserBotCount}` + if (tech.foamBotCount) botText += `
foam-bots: ${tech.foamBotCount}` + if (tech.soundBotCount) botText += `
sound-bots: ${tech.soundBotCount}` + if (tech.dynamoBotCount) botText += `
dynamo-bots: ${tech.dynamoBotCount}` + if (tech.plasmaBotCount) botText += `
plasma-bots: ${tech.plasmaBotCount}` + if (tech.missileBotCount) botText += `
missile-bots: ${tech.missileBotCount}` + + let text = `
+ +PAUSED +press ${input.key.pause} to resume +
+
+ + + + +
+ + +
+ +
damage: ${((tech.damageFromTech())).toPrecision(4)}     difficulty: ${((m.dmgScale)).toPrecision(4)} +
defense: ${tech.isEnergyHealth ? (1 - Math.pow(m.defense(), 0.13)).toPrecision(5) : (1 - m.defense()).toPrecision(5)}     difficulty: ${(1 / simulation.dmgScale).toPrecision(4)} +
fire rate: ${((1 - b.fireCDscale) * 100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% +${tech.duplicationChance() ? `
duplication: ${(tech.duplicationChance() * 100).toFixed(0)}%` : ""} +${m.coupling ? `
` + m.couplingDescription(m.coupling) + ` from ${(m.coupling).toFixed(0)} ${powerUps.orb.coupling(1)}` : ""} +${botText} +
+
health: (${(m.health * 100).toFixed(0)} / ${(m.maxHealth * 100).toFixed(0)}) +mass: ${player.mass.toFixed(1)} +
energy: (${(m.energy * 100).toFixed(0)} / ${(m.maxEnergy * 100).toFixed(0)}) + (${(m.fieldRegen * 6000).toFixed(0)}/s) +position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)}) +
gun: ${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name}   ammo: ${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo} +mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)}) +
tech: ${tech.totalCount}   research: ${powerUps.research.count} +velocity: (${player.velocity.x.toFixed(3)}, ${player.velocity.y.toFixed(3)}) +${junkCount ? `
JUNK: ${(junkCount / totalCount * 100).toFixed(1)}% ` : ""} +
+
level: ${level.levelsCleared} ${level.levels[level.onLevel]} (${level.difficultyText()}) +
seed: ${Math.initialSeed}   ${m.cycle} cycles +
mobs: ${mob.length}   blocks: ${body.length}   bullets: ${bullet.length}   power ups: ${powerUp.length} +${simulation.isCheating ? "

lore disabled" : ""} +
`; + // deaths: ${mobs.mobDeaths}   + if (tech.isPauseSwitchField && !simulation.isChoosing) { + const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
+ ${m.fieldUpgrades[m.fieldMode].description}
` + } else { + const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
+ ${m.fieldUpgrades[m.fieldMode].description}
` + } + // for (let i = 0, len = b.inventory.length; i < len; i++) { + // text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` + // } + for (let i = 0, len = b.inventory.length; i < len; i++) { + const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/gun/${b.guns[b.inventory[i]].name}.webp');"` + text += `
+
+
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
+ ${b.guns[b.inventory[i]].description}
` + } + if (!localSettings.isHideHUD) text += `
${document.getElementById("text-log").innerHTML}
` //show last in game console message + let el = document.getElementById("pause-grid-left") + el.style.display = "grid" + el.innerHTML = text + }, + generatePauseRight() { + //right side + // + // + + // + // + + // + let text = `
+ + + + + + + + +
`; + // const style = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'style="animation: techColorCycle 1s linear infinite alternate;"' : '' + const ejectClass = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'pause-eject' : '' + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0) { + // const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; + // if (tech.tech[i].isNonRefundable) { + // text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + // } else if (tech.tech[i].isFieldTech) { + // text += `
+ // + //
+ //
+ //
+ //         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + // } else if (tech.tech[i].isGunTech) { + // text += `
+ // + //
+ //
+ //
+ //         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + // } else if (tech.tech[i].isLore) { + // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + // } else { + // text += `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + // } + const style = (localSettings.isHideImages || tech.tech[i].isJunk || tech.tech[i].isLore) ? `style="height:auto;"` : `style = "background-image: url('img/${tech.tech[i].name}.webp');"` + const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; + if (tech.tech[i].isNonRefundable) { + text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + // } else if (tech.tech[i].isLore) { + // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + } else if (tech.tech[i].isFieldTech) { + text += `
` + text += build.fieldTechText(i) + "
" + } else if (tech.tech[i].isGunTech) { + text += `
` + text += build.gunTechText(i) + "
" + } else if (tech.tech[i].isSkin) { + text += `
` + text += build.skinTechText(i) + "
" + } else { + text += `
` + text += build.techText(i) + "
" + } + } else if (tech.tech[i].isLost) { + text += `
${tech.tech[i].link}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + } + } + const el = document.getElementById("pause-grid-right") + el.style.display = "grid" + el.innerHTML = text + }, + sortTech(find, isExperiment = false) { + const sortKeyword = (a, b) => { + let aHasKeyword = (a.descriptionFunction ? a.descriptionFunction() : a.description).includes(find) || a.name.includes(find) + let bHasKeyword = (b.descriptionFunction ? b.descriptionFunction() : b.description).includes(find) || b.name.includes(find) + if ((aHasKeyword) && !bHasKeyword) return -1; + if (!aHasKeyword && bHasKeyword) return 1; + return 0; + } + + if (find === 'guntech') { + tech.tech.sort((a, b) => { + if (a.isGunTech && b.isGunTech) { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + } + if (a.isGunTech && !b.isGunTech) return -1; //sort to the top + if (!a.isGunTech && b.isGunTech) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'fieldtech') { + tech.tech.sort((a, b) => { + if (a.isFieldTech && b.isFieldTech) { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + } + if (a.isFieldTech && !b.isFieldTech) return -1; //sort to the top + if (!a.isFieldTech && b.isFieldTech) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'allowed') { + tech.tech.sort((a, b) => { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'have') { + tech.tech.sort((a, b) => { + if (a.count > b.count) return -1; //sort to the top + if (!a.count < b.count) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'heal') { + tech.tech.sort((a, b) => { + if (a.isHealTech && b.isHealTech) { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + } + if (a.isHealTech && !b.isHealTech) return -1; //sort to the top + if (!a.isHealTech && b.isHealTech) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'bot') { + tech.tech.sort((a, b) => { + if (a.isBotTech && b.isBotTech) { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + } + if (a.isBotTech && !b.isBotTech) return -1; //sort to the top + if (!a.isBotTech && b.isBotTech) return 1; //sort to the bottom + return 0; + }); + } else if (document.getElementById("sort-input").value === 'skin') { + tech.tech.sort((a, b) => { + if (a.isSkin && b.isSkin) { + if (a.allowed() > b.allowed()) return -1; //sort to the top + if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + } + if (a.isSkin && !b.isSkin) return -1; //sort to the top + if (!a.isSkin && b.isSkin) return 1; //sort to the bottom + return 0; + }); + } else if (document.getElementById("sort-input").value === 'junk') { + tech.tech.sort((a, b) => { + if (a.isJunk && !b.isJunk) return -1; //sort to the top + if (!a.isJunk && b.isJunk) return 1; //sort to the bottom + return 0; + }); + } else if (find === 'damage') { + tech.tech.sort(sortKeyword); + } else if (find === 'defense') { + tech.tech.sort(sortKeyword); + } else if (find === 'energy') { + tech.tech.sort(sortKeyword); + } else if (find === 'input') { + find = document.getElementById("sort-input").value; + tech.tech.sort(sortKeyword); + } + if (isExperiment) { + build.populateGrid() + // build.updateExperimentText() + document.getElementById("tech-0").scrollIntoView(); //scroll to the first tech after sorting + } else { + build.generatePauseRight() //makes the right side of the pause menu with the tech + } + document.getElementById("sort-input").value = find; //make the sorted string display in the keyword search input field + }, + unPauseGrid() { + document.getElementById("guns").style.display = "inline" + document.getElementById("field").style.display = "inline" + if (tech.isEnergyHealth) { + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + } else { + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline" + } + if (!localSettings.isHideHUD) { + document.getElementById("tech").style.display = "inline" + document.getElementById("defense-bar").style.display = "inline" + document.getElementById("damage-bar").style.display = "inline" + } + // document.body.style.overflow = "hidden" + document.getElementById("pause-grid-left").style.display = "none" + document.getElementById("pause-grid-right").style.display = "none" + document.getElementById("pause-grid-right").style.opacity = "1" + document.getElementById("pause-grid-left").style.opacity = "1" + window.scrollTo(0, 0); + }, + isExperimentSelection: false, + isExperimentRun: false, + techText(i) { + return `
+
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + }, + skinTechText(i) { + return `
+ +
+
+
       ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + }, + gunTechText(i) { + return `
+ +
+
+
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + }, + fieldTechText(i) { + return `
+ +
+
+
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + }, + junkTechText(i) { + return `
+
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + }, + choosePowerUp(index, type, isAllowed = false) { + if (type === "gun") { + let isDeselect = false + for (let i = 0, len = b.inventory.length; i < len; i++) { //look for selection in inventory + if (b.guns[b.inventory[i]].name === b.guns[index].name) { //if already clicked, remove gun + isDeselect = true + document.getElementById("gun-" + b.inventory[i]).classList.remove("build-gun-selected"); + //remove gun + b.inventory.splice(i, 1) + b.guns[index].count = 0; + b.guns[index].have = false; + if (b.guns[index].ammo != Infinity) b.guns[index].ammo = 0; + if (b.inventory.length === 0) { + b.activeGun = null; + b.inventoryGun = 0; + } + simulation.makeGunHUD(); + break + } + } + if (!isDeselect) { //add gun + document.getElementById("gun-" + index).classList.add("build-gun-selected"); + b.giveGuns(index) + } + } else if (type === "field") { + if (m.fieldMode !== index) { + document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); + m.setField(index) + document.getElementById("field-" + index).classList.add("build-field-selected"); + document.getElementById("tech-150").focus(); + } else if (m.fieldMode === 4) { + const i = 4 //update experiment text + simulation.molecularMode++ + if (simulation.molecularMode > i - 1) simulation.molecularMode = 0 + m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() + // document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}` + + document.getElementById(`field-${i}`).innerHTML = `
+
  ${build.nameLink(m.fieldUpgrades[i].name)}
+ ${m.fieldUpgrades[i].description}
` + } + } else if (type === "tech") { + if (tech.tech[index].count < tech.tech[index].maxCount) { + // if (!tech.tech[index].isLore && !tech.tech[index].isNonRefundable && !who.classList.contains("build-tech-selected")) who.classList.add("build-tech-selected"); + if (!document.getElementById("tech-" + index).classList.contains("build-tech-selected")) document.getElementById("tech-" + index).classList.add("build-tech-selected"); + tech.giveTech(index) + } else if (!tech.tech[index].isNonRefundable) { + // tech.totalCount -= tech.tech[index].count + tech.removeTech(index); + document.getElementById("tech-" + index).classList.remove("build-tech-selected"); + } else { + // for non refundable tech this makes it flash off for a second, but return to on to show that it can't be set off + document.getElementById("tech-" + index).classList.remove("build-tech-selected") + setTimeout(() => { + document.getElementById("tech-" + index).classList.add("build-tech-selected") + }, 50); + } + } + build.updateExperimentText(isAllowed) + }, + updateExperimentText(isAllowed = false) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + const techID = document.getElementById("tech-" + i) + if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { + if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { + if (tech.tech[i].isFieldTech) { + techID.classList.remove('experiment-grid-hide'); + techID.innerHTML = build.fieldTechText(i) + } else if (tech.tech[i].isGunTech) { + techID.classList.remove('experiment-grid-hide'); + techID.innerHTML = build.gunTechText(i) + } else if (tech.tech[i].isJunk) { + techID.innerHTML = build.junkTechText(i) + // `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` + } else if (tech.tech[i].isSkin) { + techID.classList.remove('experiment-grid-hide'); + techID.innerHTML = build.skinTechText(i) + } else { + techID.innerHTML = build.techText(i) + } + //deselect selected tech options if you don't have the tech any more // for example: when bot techs are converted after a bot upgrade tech is taken + if (tech.tech[i].count === 0 && techID.classList.contains("build-tech-selected")) techID.classList.remove("build-tech-selected"); + if (techID.classList.contains("experiment-grid-disabled")) { + techID.classList.remove("experiment-grid-disabled"); + techID.setAttribute("onClick", `javascript: build.choosePowerUp(${i},'tech')`); + } + } else { //disabled color for disabled tech + techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` + if (!techID.classList.contains("experiment-grid-disabled")) { + techID.classList.add("experiment-grid-disabled"); + techID.onclick = null + } + if (tech.tech[i].count > 0) tech.removeTech(i) + if (techID.classList.contains("build-tech-selected")) techID.classList.remove("build-tech-selected"); + if (tech.tech[i].isFieldTech) { + techID.innerHTML = build.fieldTechText(i) + } else if (tech.tech[i].isGunTech) { + techID.innerHTML = build.gunTechText(i) + } else if (tech.tech[i].isJunk) { + techID.innerHTML = build.junkTechText(i) + } else if (tech.tech[i].isSkin) { + techID.innerHTML = build.skinTechText(i) + } else { + techID.innerHTML = build.techText(i) + } + } + } + } + }, + populateGrid() { //background-color:var(--build-bg-color); + let text = ` +
+
+ + + + + + + + +
+
+ +     + + +
+
+
+
+ + + start + + +
+
+ + + reset + + +
+
+ + + share + + +
+
+
+
` + const hideStyle = `style="height:auto; border: none; background-color: transparent;"` + for (let i = 0, len = m.fieldUpgrades.length; i < len; i++) { + const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[i].name}${i === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[i].name)}
+ ${m.fieldUpgrades[i].description}
` + } + for (let i = 0, len = b.guns.length; i < len; i++) { + const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/gun/${b.guns[i].name}.webp');"` + text += `
+
+
  ${build.nameLink(b.guns[i].name)}
+ ${b.guns[i].description}
` + } + for (let i = 0, len = tech.tech.length; i < len; i++) { + if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { + const style = (localSettings.isHideImages || tech.tech[i].isJunk) ? hideStyle : `style="background-image: url('img/${tech.tech[i].name}.webp');"` + if ((tech.tech[i].allowed() || tech.tech[i].count > 0) && (!tech.tech[i].isNonRefundable || localSettings.isJunkExperiment)) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" + text += `
` + } else { //disabled + text += `
` + } + + if (tech.tech[i].isFieldTech) { + text += build.fieldTechText(i) + } else if (tech.tech[i].isGunTech) { + text += build.gunTechText(i) + } else if (tech.tech[i].isSkin) { + text += build.skinTechText(i) + } else if (tech.tech[i].isJunk) { + text += build.junkTechText(i) + } else { + text += build.techText(i) + } + text += '
' + } + } + document.getElementById("experiment-grid").innerHTML = text + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].count) + // document.getElementById("tech-" + i).classList.add("build-tech-selected") + // } + + document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value + document.getElementById("difficulty-select-experiment").addEventListener("input", () => { + simulation.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + lore.setTechGoal() + localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }); + //add tooltips + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (document.getElementById(`tech-${i}`)) { + document.getElementById(`tech-${i}`).setAttribute('data-descr', tech.tech[i].requires); //add tooltip + // document.getElementById(`tech-${i}`).setAttribute('title', tech.tech[i].requires); //add tooltip + } + } + //highlight selected + + }, + nameLink(text) { //converts text into a clickable wikipedia search + return `${text}` + }, + reset() { + build.isExperimentSelection = true; + build.isExperimentRun = true; + simulation.startGame(true); //starts game, but pauses it + build.isExperimentSelection = true; + build.isExperimentRun = true; + simulation.paused = true; + b.inventory = []; //removes guns and ammo + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].count = 0; + b.guns[i].have = false; + if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; + } + b.activeGun = null; + b.inventoryGun = 0; + simulation.makeGunHUD(); + m.resetSkin() + tech.setupAllTech(); + build.populateGrid(); + document.getElementById("field-0").classList.add("build-field-selected"); + document.getElementById("experiment-grid").style.display = "grid" + }, + shareURL(isCustom = false) { + let url = "https://landgreen.github.io/sidescroller/index.html?" + url += `&seed=${Math.initialSeed}` + let count = 0; + for (let i = 0; i < b.inventory.length; i++) { + if (b.guns[b.inventory[i]].have) { + url += `&gun${count}=${encodeURIComponent(b.guns[b.inventory[i]].name.trim())}` + count++ + } + } + count = 0; + for (let i = 0; i < tech.tech.length; i++) { + for (let j = 0; j < tech.tech[i].count; j++) { + if (!tech.tech[i].isLore && !tech.tech[i].isJunk && !tech.tech[i].isNonRefundable) { + url += `&tech${count}=${encodeURIComponent(tech.tech[i].name.trim())}` + count++ + } + } + } + url += `&molMode=${encodeURIComponent(simulation.molecularMode)}` + // if (property === "molMode") { + // simulation.molecularMode = Number(set[property]) + // m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() + // document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}` + // } + + url += `&field=${encodeURIComponent(m.fieldUpgrades[m.fieldMode].name.trim())}` + url += `&difficulty=${simulation.difficultyMode}` + if (isCustom) { + // url += `&level=${Math.abs(Number(document.getElementById("starting-level").value))}` + // alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') + } else { + simulation.makeTextLog("n-gon build URL copied to clipboard.
Paste into browser address bar.") + } + console.log('n-gon build URL copied to clipboard.\nPaste into browser address bar.') + console.log(url) + navigator.clipboard.writeText(url).then(function () { + /* clipboard successfully set */ + if (isCustom) { + setTimeout(function () { + alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') + }, 300); + } + }, function () { + /* clipboard write failed */ + if (isCustom) { + setTimeout(function () { + alert('copy failed') + }, 300); + } + console.log('copy failed') + }); + + }, + hasExperimentalMode: false, + startExperiment() { //start playing the game after exiting the experiment menu + build.isExperimentSelection = false; + spawn.setSpawnList(); //gives random mobs, not starter mobs + spawn.setSpawnList(); + if (b.inventory.length > 0) { + b.activeGun = b.inventory[0] //set first gun to active gun + b.inventoryGun = 0; + simulation.makeGunHUD(); + } + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; //remove any bullets that might have spawned from tech + build.hasExperimentalMode = false + if (!simulation.isCheating) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isLore) simulation.isCheating = true; + } + if (b.inventory.length !== 0 || m.fieldMode !== 0) simulation.isCheating = true; + } + if (simulation.isCheating) { //if you are cheating remove any lore you might have gotten + lore.techCount = 0; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isLore) { + tech.tech[i].frequency = 0; //remove lore power up chance + tech.tech[i].count = 0; //remove lore power up chance + } + } + simulation.updateTechHUD(); + } else { //if you have no tech (not cheating) remove all power ups that might have spawned from tech + for (let i = 0; i < powerUp.length; ++i) Matter.Composite.remove(engine.world, powerUp[i]); + powerUp = []; + // if (build.hasExperimentalMode) { + // for (let i = 0; i < 7; i++) tech.giveTech("undefined") + // } + } + document.body.style.cursor = "none"; + document.body.style.overflow = "hidden" + document.getElementById("experiment-grid").style.display = "none" + simulation.paused = false; + requestAnimationFrame(cycle); + } +} + +function openExperimentMenu() { + document.getElementById("experiment-button").style.display = "none"; + document.getElementById("training-button").style.display = "none"; + const el = document.getElementById("experiment-grid") + el.style.display = "grid" + document.body.style.overflowY = "scroll"; + document.body.style.overflowX = "hidden"; + document.getElementById("info").style.display = 'none' + build.reset(); +} + +//record settings so they can be reproduced in the experimental menu +document.getElementById("experiment-button").addEventListener("click", () => { //setup build run + // let field = 0; + // let inventory = []; + // let techList = []; + // if (!simulation.firstRun) { + // field = m.fieldMode + // inventory = [...b.inventory] + // for (let i = 0; i < tech.tech.length; i++) { + // techList.push(tech.tech[i].count) + // } + // } + openExperimentMenu(); +}); + + +// ************************************************************************************************ +// inputs +// ************************************************************************************************ +const input = { + fire: false, // left mouse + field: false, // right mouse + up: false, // jump + down: false, // crouch + left: false, + right: false, + isPauseKeyReady: true, + key: { + fire: "KeyF", + field: "Space", + up: "KeyW", // jump + down: "KeyS", // crouch + left: "KeyA", + right: "KeyD", + pause: "KeyP", + nextGun: "KeyE", + previousGun: "KeyQ", + testing: "KeyT" + }, + setDefault() { + input.key = { + fire: "KeyF", + field: "Space", + up: "KeyW", // jump + down: "KeyS", // crouch + left: "KeyA", + right: "KeyD", + pause: "KeyP", + nextGun: "KeyE", + previousGun: "KeyQ", + testing: "KeyT" + } + input.controlTextUpdate() + }, + controlTextUpdate() { + function cleanText(text) { + return text.replace('Key', '').replace('Digit', '') + } + if (!input.key.fire) input.key.fire = "KeyF" + document.getElementById("key-fire").innerHTML = cleanText(input.key.fire) + document.getElementById("key-field").innerHTML = cleanText(input.key.field) + document.getElementById("key-up").innerHTML = cleanText(input.key.up) + document.getElementById("key-down").innerHTML = cleanText(input.key.down) + document.getElementById("key-left").innerHTML = cleanText(input.key.left) + document.getElementById("key-right").innerHTML = cleanText(input.key.right) + document.getElementById("key-pause").innerHTML = cleanText(input.key.pause) + document.getElementById("key-next-gun").innerHTML = cleanText(input.key.nextGun) + document.getElementById("key-previous-gun").innerHTML = cleanText(input.key.previousGun) + document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) //if (localSettings.loreCount > 0) + + document.getElementById("splash-up").innerHTML = cleanText(input.key.up)[0] + document.getElementById("splash-down").innerHTML = cleanText(input.key.down)[0] + document.getElementById("splash-left").innerHTML = cleanText(input.key.left)[0] + document.getElementById("splash-right").innerHTML = cleanText(input.key.right)[0] + document.getElementById("splash-next-gun").innerHTML = cleanText(input.key.nextGun)[0] + document.getElementById("splash-previous-gun").innerHTML = cleanText(input.key.previousGun)[0] + + localSettings.key = input.key + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + focus: null, + setTextFocus() { + const backgroundColor = "#fff" + document.getElementById("key-fire").style.background = backgroundColor + document.getElementById("key-field").style.background = backgroundColor + document.getElementById("key-up").style.background = backgroundColor + document.getElementById("key-down").style.background = backgroundColor + document.getElementById("key-left").style.background = backgroundColor + document.getElementById("key-right").style.background = backgroundColor + document.getElementById("key-pause").style.background = backgroundColor + document.getElementById("key-next-gun").style.background = backgroundColor + document.getElementById("key-previous-gun").style.background = backgroundColor + document.getElementById("key-testing").style.background = backgroundColor + if (input.focus) input.focus.style.background = 'rgb(0, 200, 255)'; + document.getElementById("key-num").style.background = backgroundColor //always not highlighted + }, + setKeys(event) { + //check for duplicate keys + if (event.code && !( + event.code === "ArrowRight" || + event.code === "ArrowLeft" || + event.code === "ArrowUp" || + event.code === "ArrowDown" || + event.code === input.key.fire || + event.code === input.key.field || + event.code === input.key.up || + event.code === input.key.down || + event.code === input.key.left || + event.code === input.key.right || + event.code === input.key.pause || + // event.code === "Escape" || + event.code === input.key.nextGun || + event.code === input.key.previousGun || + event.code === input.key.testing || + event.code === "Digit1" || event.code === "Digit2" || event.code === "Digit3" || event.code === "Digit4" || event.code === "Digit5" || event.code === "Digit6" || event.code === "Digit7" || event.code === "Digit8" || event.code === "Digit9" || event.code === "Digit0" || event.code === "Minus" || event.code === "Equal" + )) { + switch (input.focus.id) { + case "key-fire": + input.key.fire = event.code + break; + case "key-field": + input.key.field = event.code + break; + case "key-up": + input.key.up = event.code + break; + case "key-down": + input.key.down = event.code + break; + case "key-left": + input.key.left = event.code + break; + case "key-right": + input.key.right = event.code + break; + case "key-pause": + input.key.pause = event.code + break; + case "key-next-gun": + input.key.nextGun = event.code + break; + case "key-previous-gun": + input.key.previousGun = event.code + break; + case "key-testing": + input.key.testing = event.code + break; + } + } + input.controlTextUpdate() + input.endKeySensing() + }, + endKeySensing() { + window.removeEventListener("keydown", input.setKeys); + input.focus = null + input.setTextFocus() + } +} + +document.getElementById("control-table").addEventListener('click', (event) => { + if (event.target.className === 'key-input') { + input.focus = event.target + input.setTextFocus() + window.addEventListener("keydown", input.setKeys); + } +}); +document.getElementById("control-details").addEventListener("toggle", function () { + input.controlTextUpdate() + input.endKeySensing(); +}) + +document.getElementById("control-reset").addEventListener('click', input.setDefault); + +window.addEventListener("keyup", function (event) { + switch (event.code) { + case input.key.right: + case "ArrowRight": + input.right = false + break; + case input.key.left: + case "ArrowLeft": + input.left = false + break; + case input.key.up: + case "ArrowUp": + input.up = false + break; + case input.key.down: + case "ArrowDown": + input.down = false + break; + case input.key.fire: + input.fire = false + break + case input.key.field: + input.field = false + break + } +}); + +window.addEventListener("keydown", function (event) { + // console.log(event.code) + switch (event.code) { + case input.key.right: + case "ArrowRight": + input.right = true + break; + case input.key.left: + case "ArrowLeft": + input.left = true + break; + case input.key.up: + case "ArrowUp": + input.up = true + break; + case input.key.down: + case "ArrowDown": + input.down = true + break; + case input.key.fire: + input.fire = true + break + case input.key.field: + input.field = true + break + case input.key.nextGun: + simulation.nextGun(); + break + case input.key.previousGun: + simulation.previousGun(); + break + case input.key.pause: + if (!simulation.isChoosing && input.isPauseKeyReady && m.alive) { + input.isPauseKeyReady = false + setTimeout(function () { + input.isPauseKeyReady = true + }, 300); + if (simulation.paused) { + build.unPauseGrid() + simulation.paused = false; + // level.levelAnnounce(); + document.body.style.cursor = "none"; + requestAnimationFrame(cycle); + } else if (!tech.isNoDraftPause) { + simulation.paused = true; + build.pauseGrid() + document.body.style.cursor = "auto"; + + if (tech.isPauseSwitchField || simulation.testing) { + document.getElementById("pause-field").addEventListener("click", () => { + const energy = m.energy //save current energy + if (m.fieldMode === 4 && simulation.molecularMode < 3) { + simulation.molecularMode++ + m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription() + } else { + m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 0 : m.fieldMode + 1) //cycle to next field + if (m.fieldMode === 4) { + simulation.molecularMode = 0 + m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription() + } + } + m.energy = energy //return to current energy + // document.getElementById("pause-field").innerHTML = `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}` + document.getElementById("pause-field").style.backgroundImage = `url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? Math.floor(Math.random() * 10) : ""}.webp')` + document.getElementById("pause-field").innerHTML = ` +
+
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
+ ${m.fieldUpgrades[m.fieldMode].description}
` + }); + } + } + } + break + case input.key.testing: + if (m.alive && localSettings.loreCount > 0 && !simulation.paused) { + if (simulation.difficultyMode > 4) { + simulation.makeTextLog("testing mode disabled for this difficulty"); + break + } + if (simulation.testing) { + simulation.testing = false; + simulation.loop = simulation.normalLoop + if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' + simulation.makeTextLog("", 0); + } else { //if (keys[191]) + simulation.testing = true; + simulation.loop = simulation.testingLoop + if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline' + if (simulation.testing) tech.setCheating(); + simulation.makeTextLog( + ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ttoggle testing
Rteleport to mouse
Fcycle field
Gall guns
H+100% defense
Bdamage, research
Nfill health, energy
Yrandom tech
Unext level
Jclear mobs
I/Ozoom in / out
1-8spawn things
⇧Xrestart
`, Infinity); + } + } + break + } + if (b.inventory.length > 1 && !simulation.testing) { + switch (event.code) { + case "Digit1": + simulation.switchToGunInInventory(0); + break + case "Digit2": + simulation.switchToGunInInventory(1); + break + case "Digit3": + simulation.switchToGunInInventory(2); + break + case "Digit4": + simulation.switchToGunInInventory(3); + break + case "Digit5": + simulation.switchToGunInInventory(4); + break + case "Digit6": + simulation.switchToGunInInventory(5); + break + case "Digit7": + simulation.switchToGunInInventory(6); + break + case "Digit8": + simulation.switchToGunInInventory(7); + break + case "Digit9": + simulation.switchToGunInInventory(8); + break + case "Digit0": + simulation.switchToGunInInventory(9); + break + case "Minus": + simulation.switchToGunInInventory(10); + break + case "Equal": + simulation.switchToGunInInventory(11); + break + } + } + + if (simulation.testing) { + if (event.key === "X") m.death(); //only uppercase + switch (event.key.toLowerCase()) { + case "o": + simulation.isAutoZoom = false; + simulation.zoomScale /= 0.9; + simulation.setZoom(); + break; + case "i": + simulation.isAutoZoom = false; + simulation.zoomScale *= 0.9; + simulation.setZoom(); + break + case "`": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "research"); + break + case "1": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "heal"); + break + case "2": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "ammo"); + break + case "3": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "gun"); + break + case "4": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "field"); + break + case "5": + powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "tech"); + break + case "6": + spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50); + break + case "7": + const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; + spawn[pick](simulation.mouseInGame.x, simulation.mouseInGame.y); + break + case "8": + spawn.randomLevelBoss(simulation.mouseInGame.x, simulation.mouseInGame.y); + break + case "f": + const mode = (m.fieldMode === m.fieldUpgrades.length - 1) ? 0 : m.fieldMode + 1 + m.setField(mode) + break + case "g": + b.giveGuns("all", 1000) + break + case "h": + // m.health = Infinity + if (m.immuneCycle === Infinity) { + m.immuneCycle = 0 //you can't take damage + } else { + m.immuneCycle = Infinity //you can't take damage + } + + // m.energy = Infinity + // document.getElementById("health").style.display = "none" + // document.getElementById("health-bg").style.display = "none" + break + case "n": + m.addHealth(Infinity) + m.energy = m.maxEnergy + break + case "y": + tech.giveTech() + break + case "b": + tech.isRerollDamage = true + powerUps.research.changeRerolls(1000000) + break + case "r": + m.resetHistory(); + Matter.Body.setPosition(player, simulation.mouseInGame); + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + break + case "u": + level.nextLevel(); + break + case "j": + for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) + setTimeout(() => { + for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) + }, 100); + setTimeout(() => { + for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) + }, 200); + break + case "l": + document.getElementById("field").style.display = "none" + document.getElementById("guns").style.display = "none" + document.getElementById("tech").style.display = "none" + break + } + } +}); +//mouse move input +document.body.addEventListener("mousemove", (e) => { + simulation.mouse.x = e.clientX; + simulation.mouse.y = e.clientY; +}); + +document.body.addEventListener("mouseup", (e) => { + // input.fire = false; + // console.log(e) + if (e.button === 0) { + input.fire = false; + } else if (e.button === 2) { + input.field = false; + } +}); + +document.body.addEventListener("mousedown", (e) => { + if (e.button === 0) { + input.fire = true; + } else if (e.button === 2) { + input.field = true; + } +}); + +document.body.addEventListener("mouseenter", (e) => { //prevents mouse getting stuck when leaving the window + if (e.button === 1) { + input.fire = true; + } else { + input.fire = false; + } + + if (e.button === 3) { + input.field = true; + } else { + input.field = false; + } +}); +document.body.addEventListener("mouseleave", (e) => { //prevents mouse getting stuck when leaving the window + if (e.button === 1) { + input.fire = true; + } else { + input.fire = false; + } + + if (e.button === 3) { + input.field = true; + } else { + input.field = false; + } +}); + +document.body.addEventListener("wheel", (e) => { + if (!simulation.paused) { + if (e.deltaY > 0) { + simulation.nextGun(); + } else { + simulation.previousGun(); + } + } +}, { + passive: true +}); + +//********************************************************************** +// local storage +//********************************************************************** +let localSettings + +function localStorageCheck() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } + +} +if (localStorageCheck()) { + localSettings = JSON.parse(localStorage.getItem("localSettings")) + if (localSettings) { + console.log('localStorage is enabled') + localSettings.isAllowed = true + localSettings.isEmpty = false + } else { + console.log('localStorage is enabled, local settings empty') + localSettings = { + isAllowed: true, + isEmpty: true + } + } +} else { + console.log("localStorage is disabled") + localSettings = { + isAllowed: false + } +} + +if (localSettings.isAllowed && !localSettings.isEmpty) { + console.log('restoring previous settings') + + if (localSettings.key) { + input.key = localSettings.key + } else { + input.setDefault() + } + + if (localSettings.loreCount === undefined) { + localSettings.loreCount = 0 + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + + simulation.isCommunityMaps = localSettings.isCommunityMaps + document.getElementById("community-maps").checked = localSettings.isCommunityMaps + + if (localSettings.difficultyMode === undefined) localSettings.difficultyMode = "2" + simulation.difficultyMode = localSettings.difficultyMode + lore.setTechGoal() + document.getElementById("difficulty-select").value = localSettings.difficultyMode + + if (localSettings.fpsCapDefault === undefined) localSettings.fpsCapDefault = 'max' + if (localSettings.personalSeeds === undefined) localSettings.personalSeeds = []; + if (localSettings.fpsCapDefault === 'max') { + simulation.fpsCapDefault = 999999999; + } else { + simulation.fpsCapDefault = Number(localSettings.fpsCapDefault) + } + document.getElementById("fps-select").value = localSettings.fpsCapDefault + + if (!localSettings.banList) localSettings.banList = "" + if (localSettings.banList.length === 0 || localSettings.banList === "undefined") { + localSettings.banList = "" + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + document.getElementById("banned").value = localSettings.banList + + if (!localSettings.isLoreDoesNotNeedReset) { + localSettings.isLoreDoesNotNeedReset = true + localSettings.loreCount = 0; //this sets what conversation is heard + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + if (localSettings.isHideImages === undefined) localSettings.isHideImages = true //default to hide images + document.getElementById("hide-images").checked = localSettings.isHideImages + + if (localSettings.isHideHUD === undefined) localSettings.isHideHUD = false + document.getElementById("hide-hud").checked = localSettings.isHideHUD + +} else { + console.log('setting default localSettings') + const isAllowed = localSettings.isAllowed //don't overwrite isAllowed value + localSettings = { + banList: "", + isAllowed: isAllowed, + personalSeeds: [], + isJunkExperiment: false, + isCommunityMaps: false, + difficultyMode: '2', + fpsCapDefault: 'max', + runCount: 0, + isTrainingNotAttempted: true, + levelsClearedLastGame: 0, + loreCount: 0, + isLoreDoesNotNeedReset: false, + isHuman: false, + key: undefined, + isHideImages: true, //default to hide images + isHideHUD: false, + }; + input.setDefault() + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + document.getElementById("community-maps").checked = localSettings.isCommunityMaps + simulation.isCommunityMaps = localSettings.isCommunityMaps + document.getElementById("hide-images").checked = localSettings.isHideImages + document.getElementById("difficulty-select").value = localSettings.difficultyMode + document.getElementById("fps-select").value = localSettings.fpsCapDefault + document.getElementById("banned").value = localSettings.banList +} +document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" +// document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + +input.controlTextUpdate() + +//********************************************************************** +// settings +//********************************************************************** +document.getElementById("fps-select").addEventListener("input", () => { + let value = document.getElementById("fps-select").value + if (value === 'max') { + simulation.fpsCapDefault = 999999999; + } else { + simulation.fpsCapDefault = Number(value) + } + localSettings.fpsCapDefault = value + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +}); + +document.getElementById("banned").addEventListener("input", () => { + localSettings.banList = document.getElementById("banned").value + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +}); + +document.getElementById("community-maps").addEventListener("input", () => { + simulation.isCommunityMaps = document.getElementById("community-maps").checked + localSettings.isCommunityMaps = simulation.isCommunityMaps + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +}); + +// difficulty-select-experiment event listener is set in build.makeGrid +document.getElementById("difficulty-select").addEventListener("input", () => { + simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) + lore.setTechGoal() + localSettings.difficultyMode = simulation.difficultyMode + localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history + localSettings.entanglement = undefined //after changing difficulty, reset stored tech + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +}); + + +document.getElementById("updates").addEventListener("toggle", function () { + function loadJSON(path, success, error) { //generic function to get JSON + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + if (success) + success(JSON.parse(xhr.responseText)); + } else { + if (error) + error(xhr); + } + } + }; + xhr.open("GET", path, true); + xhr.send(); + } + let text = `n-gon: todo list and complete change-log
` + document.getElementById("updates-div").innerHTML = text + + /// https://api.github.com/repos/landgreen/n-gon/stats/commit_activity + loadJSON('https://api.github.com/repos/landgreen/n-gon/commits', + function (data) { + // console.log(data) + for (let i = 0, len = 20; i < len; i++) { + text += "" + data[i].commit.author.date.substr(0, 10) + " - "; //+ "
" + text += data[i].commit.message + if (i < len - 1) text += "
" + } + document.getElementById("updates-div").innerHTML = text.replace(/\n/g, "
") + }, + function (xhr) { + console.error(xhr); + } + ); +}) +const sound = { + tone(frequency, end = 1000, gain = 0.05) { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context + const oscillator = audioCtx.createOscillator(); + const gainNode = audioCtx.createGain(); + gainNode.gain.value = gain; //controls volume + oscillator.connect(gainNode); + gainNode.connect(audioCtx.destination); + oscillator.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator.frequency.value = frequency; // value in hertz + oscillator.start(); + setTimeout(() => { + audioCtx.suspend() + audioCtx.close() + }, end) + // return audioCtx + }, + portamento(frequency, end = 1000, shiftRate = 10, gain = 0.05) { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context + const oscillator = audioCtx.createOscillator(); + const gainNode = audioCtx.createGain(); + gainNode.gain.value = gain; //controls volume + oscillator.connect(gainNode); + gainNode.connect(audioCtx.destination); + oscillator.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator.frequency.value = frequency; // value in hertz + oscillator.start(); + for (let i = 0, len = end * 0.1; i < len; i++) oscillator.frequency.setValueAtTime(frequency + i * shiftRate, audioCtx.currentTime + i * 0.01); + setTimeout(() => { + audioCtx.suspend() + audioCtx.close() + }, end) + // return audioCtx + } +} + +// preload images so they load cleaner +// MDN Scripting and preloads - https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload +// if (!localSettings.isHideImages) { +// for (let i = 0, len = b.guns.length; i < len; i++) { +// const preloadLink = document.createElement("link"); +// preloadLink.href = "img/gun/" + b.guns[i].name + ".webp"; +// preloadLink.rel = "preload"; +// preloadLink.as = "image"; +// document.head.appendChild(preloadLink); +// } +// for (let i = 1, len = m.fieldUpgrades.length; i < len; i++) { +// const preloadLink = document.createElement("link"); +// preloadLink.href = "img/field/" + m.fieldUpgrades[i].name + ".webp"; +// preloadLink.rel = "preload"; +// preloadLink.as = "image"; +// document.head.appendChild(preloadLink); +// } +// for (let i = 0, len = tech.tech.length; i < len; i++) { +// if (!tech.tech[i].isJunk) { +// const preloadLink = document.createElement("link"); +// preloadLink.href = "img/" + tech.tech[i].name + ".webp"; +// preloadLink.rel = "preload"; +// preloadLink.as = "image"; +// document.head.appendChild(preloadLink); +// } +// } +// } + + +//preload images early +if (!localSettings.isHideImages) { + addEventListener("load", () => { + let urls = new Array() + for (let i = 0, len = b.guns.length; i < len; i++) urls.push("img/gun/" + b.guns[i].name + ".webp") + for (let i = 1, len = m.fieldUpgrades.length; i < len; i++) urls.push("img/field/" + m.fieldUpgrades[i].name + ".webp") + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (!tech.tech[i].isJunk && !tech.tech[i].isLore) urls.push("img/" + tech.tech[i].name + ".webp") + } + let images = new Array() + for (let i = 0; i < urls.length; i++) { + images[i] = new Image() + images[i].src = urls[i] + } + // console.log(urls, images) + }); + document.getElementById("choose-grid").classList.add('choose-grid'); +} else { + document.getElementById("choose-grid").classList.add('choose-grid-no-images'); +} + + +//********************************************************************** +// main loop +//********************************************************************** +simulation.loop = simulation.normalLoop; + +function cycle() { + if (!simulation.paused) requestAnimationFrame(cycle); + const now = Date.now(); + const elapsed = now - simulation.then; // calc elapsed time since last loop + if (elapsed > simulation.fpsInterval) { // if enough time has elapsed, draw the next frame + simulation.then = now - (elapsed % simulation.fpsInterval); // Get ready for next frame by setting then=now. Also, adjust for fpsInterval not being multiple of 16.67 + + simulation.cycle++; //tracks game cycles + m.cycle++; //tracks player cycles //used to alow time to stop for everything, but the player + if (simulation.clearNow) { + simulation.clearNow = false; + simulation.clearMap(); + level.start(); + } + simulation.loop(); + } +} + +// function cycle() { +// if (!simulation.paused) requestAnimationFrame(cycle); +// const now = Date.now(); +// const elapsed = now - simulation.then; // calc elapsed time since last loop +// if (elapsed > simulation.fpsInterval) { // if enough time has elapsed, draw the next frame +// simulation.then = now - (elapsed % simulation.fpsInterval); // Get ready for next frame by setting then=now. Also, adjust for fpsInterval not being multiple of 16.67 + +// simulation.cycle++; //tracks game cycles +// m.cycle++; //tracks player cycles //used to alow time to stop for everything, but the player +// if (simulation.clearNow) { +// simulation.clearNow = false; +// simulation.clearMap(); +// level.start(); +// } +// simulation.loop(); +// } +// } + +// let timeStart = performance.now() +// //0, 16.6666666666, 33.333333333333, 50.000000000 +// function cycle(timestamp) { +// if (!simulation.paused) requestAnimationFrame(cycle); +// if (timestamp - timeStart > 0) { //simulation.fpsInterval) { // if enough time has elapsed, draw the next frame +// console.log(timestamp - timeStart) +// timeStart = timestamp +// simulation.cycle++; //tracks game cycles +// m.cycle++; //tracks player cycles //used to alow time to stop for everything, but the player +// if (simulation.clearNow) { +// simulation.clearNow = false; +// simulation.clearMap(); +// level.start(); +// } +// simulation.loop(); +// } +// } + +// let count = 1 +// let timeStart = performance.now() +// const cycle = (timestamp) => { +// // if (timeStart === undefined) timeStart = timestamp +// // console.log(timestamp, timeStart) +// if (timestamp - timeStart > tech.brainStormDelay * count) { +// count++ +// powerUps.tech.effect(); +// document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay +// document.body.style.cursor = "auto"; +// document.getElementById("choose-grid").style.transitionDuration = "0s"; +// } +// if (count < 5 && simulation.isChoosing) { +// requestAnimationFrame(cycle); +// } else { +// tech.isBrainstormActive = false +// } +// } +// requestAnimationFrame(cycle); \ No newline at end of file diff --git a/ngon/js/level.js b/ngon/js/level.js new file mode 100644 index 00000000..76ae11ec --- /dev/null +++ b/ngon/js/level.js @@ -0,0 +1,31701 @@ +let body = []; //non static bodies +let map = []; //all static bodies +let cons = []; //all constraints between a point and a body +let consBB = []; //all constraints between two bodies +let composite = [] //rotors and other map elements that don't fit +const level = { + defaultZoom: 1400, + onLevel: -1, + levelsCleared: 0, + // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], + //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later + playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], + communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo", "tlinat", "ruins", "ace", "crimsonTowers"], + trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"], + levels: [], + start() { + if (level.levelsCleared === 0) { //this code only runs on the first level + // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode + // simulation.isHorizontalFlipped = true + // tech.giveTech("performance") + // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why + // spawn.setSpawnList(); + // spawn.setSpawnList(); + // m.maxHealth = m.health = 100 + // tech.isRerollDamage = true + // powerUps.research.changeRerolls(99999) + // m.immuneCycle = Infinity //you can't take damage + // tech.tech[297].frequency = 100 + // m.couplingChange(10) + // m.setField("molecular assembler") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole + // m.energy = 0 + // simulation.molecularMode = 2 + // m.damage(0.1); + // b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.giveGuns("drones") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.guns[3].ammo = 100000000 + // requestAnimationFrame(() => { tech.giveTech("MACHO") }); + // for (let i = 0; i < 1; ++i) tech.giveTech("additive manufacturing") + // for (let i = 0; i < 1; ++i) tech.giveTech("dark star") + // for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot") + // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot") + // for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade") + // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade") + // requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") }); + // for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade") + // for (let i = 0; i < 1; ++i) tech.giveTech("uncertainty principle") + // for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance") + // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); + // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); + // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling"); + // level.testing(); + + // for (let i = 0; i < 10; ++i) spawn.sniper(1900, -500) + // for (let i = 0; i < 1; ++i) spawn.slasher2(1900, -500) + // for (let i = 0; i < 1; ++i) spawn.shooterBoss(1900, -2500) + // spawn.suckerBoss(1900, -500, 25) + // spawn.slasher2(2000, -1150) + // spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color) + // for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random()) + // tech.addJunkTechToPool(2) + // tech.tech[322].frequency = 100 + // spawn.tetherBoss(1900, -500, { x: 1900, y: -500 }) + // for (let i = 0; i < 40; ++i) tech.giveTech() + + level[simulation.isTraining ? "walk" : "intro"]() //normal starting level ************************************************** + + // simulation.isAutoZoom = false; //look in close + // simulation.zoomScale *= 0.5; + // simulation.setZoom(); + // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); + // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "boost"); + // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "heal"); + // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false); + //lore testing + // for (let i = 0; i < 5; i++) tech.giveTech("undefined") + // lore.techCount = 2 + // simulation.isCheating = false //true; + // level.levelsCleared = 10 + // localSettings.loreCount = 5 //this sets what conversation is heard + // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + // level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation + // level.null() + // localSettings.isHuman = true + // tech.isNoDraftPause = false //disable pause + // mobs.mobDeaths = 200 //to prevent pacifist mode + // for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss + // lore.unlockTesting(); + // tech.giveTech("tinker"); //show junk tech in experiment mode + // m.storeTech() + // powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false); + } else { + spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns + + // spawn.pickList = ["focuser", "focuser"] + level[level.levels[level.onLevel]](); //picks the current map from the the levels array + if (!simulation.isCheating && !build.isExperimentRun && !simulation.isTraining) { + localSettings.runCount += level.levelsCleared //track the number of total runs locally + localSettings.levelsClearedLastGame = level.levelsCleared + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + } + if (!simulation.isTraining) level.levelAnnounce(); + simulation.noCameraScroll(); + simulation.setZoom(); + level.addToWorld(); //add bodies to game engine + simulation.draw.setPaths(); + b.respawnBots(); + m.resetHistory(); + + if (tech.isForeverDrones) { + if (tech.isDroneRadioactive) { + for (let i = 0; i < tech.isForeverDrones * 0.25; i++) { + b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } else { + for (let i = 0; i < tech.isForeverDrones; i++) { + b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } + } + if (tech.isMACHO) spawn.MACHO() + for (let i = 0; i < tech.wimpCount; i++) { + spawn.WIMP() + mob[mob.length - 1].isDecoupling = true //so you can find it to remove + for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) + } + // if (tech.isFlipFlopLevelReset && !tech.isFlipFlopOn) { + if ((tech.isRelay || tech.isFlipFlop) && !tech.isFlipFlopOn) { + tech.isFlipFlopOn = true + if (tech.isFlipFlopHealth) m.setMaxHealth() + if (tech.isRelayEnergy) m.setMaxEnergy() + m.eyeFillColor = m.fieldMeterColor + simulation.makeTextLog(`tech.isFlipFlopOn = true`); + } + // if (m.plasmaBall) m.plasmaBall.reset() + if (m.plasmaBall) m.plasmaBall.fire() + if (localSettings.entanglement && localSettings.entanglement.levelName === level.levels[level.onLevel]) { + const flip = localSettings.entanglement.isHorizontalFlipped === simulation.isHorizontalFlipped ? 1 : -1 + powerUps.directSpawn(flip * localSettings.entanglement.position.x, localSettings.entanglement.position.y, "entanglement", false); + } + level.newLevelOrPhase() + }, + newLevelOrPhase() { //runs on each new level but also on final boss phases + //used for generalist and pigeonhole principle + tech.buffedGun++ + if (tech.buffedGun > b.inventory.length - 1) tech.buffedGun = 0; + if (tech.isGunCycle && b.activeGun !== null && b.inventory.length) { + b.inventoryGun = tech.buffedGun; + simulation.switchGun(); + } + if (tech.isGunChoice && Number.isInteger(tech.buffedGun) && b.inventory.length) { + var gun = b.guns[b.inventory[tech.buffedGun]].name + simulation.makeTextLog(`pigeonhole principle: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}`, 600); + } + if (tech.isSwitchReality && level.levelsCleared !== 0) { + simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + m.switchWorlds() + simulation.trails() + powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false); + } + if (tech.isHealLowHealth) { + if (tech.isEnergyHealth) { + var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent + } else { + var len = 4 * (1 - m.health / m.maxHealth) //as a percent + } + for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false); + } + }, + trainingText(say) { + simulation.lastLogTime = 0; //clear previous messages + simulation.isTextLogOpen = true + simulation.makeTextLog(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) + simulation.isTextLogOpen = false + // lore.trainer.text("Wow. Just a platform.") + }, + trainingBackgroundColor: "#e1e1e1", + custom() { }, + customTopLayer() { }, + setDifficulty() { + simulation.difficulty = 0 + m.dmgScale = 1; //damage done by player decreases each level + simulation.accelScale = 1 //mob acceleration increases each level + simulation.CDScale = 1 //mob CD time decreases each level + simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; + }, + difficultyIncrease(num = 1) { + for (let i = 0; i < num; i++) { + simulation.difficulty++ + m.dmgScale *= 0.905; //damage done by player decreases each level + if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level + if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level + } + simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; + // console.log(`CD = ${simulation.CDScale}`) + }, + difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() + for (let i = 0; i < num; i++) { + simulation.difficulty-- + m.dmgScale /= 0.905; //damage done by player decreases each level + if (simulation.accelScale > 1) simulation.accelScale /= 1.024 //mob acceleration increases each level + if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level + } + if (simulation.difficulty < 1) simulation.difficulty = 0; + simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) + }, + difficultyText() { + if (simulation.difficultyMode === 1) { + return "easy" + } else if (simulation.difficultyMode === 2) { + return "normal" + } else if (simulation.difficultyMode === 4) { + return "hard" + } else if (simulation.difficultyMode === 6) { + return "why" + } + }, + levelAnnounce() { + const difficulty = simulation.isCheating ? "testing" : level.difficultyText() + if (level.levelsCleared === 0) { + document.title = "n-gon: (" + difficulty + ")"; + } else { + document.title = `n-gon: ${level.levelsCleared} ${level.levels[level.onLevel]} (${difficulty})` + simulation.makeTextLog(`level.onLevel = "${level.levels[level.onLevel]}"`); + } + // simulation.makeTextLog(` + // input.key.up = ["${input.key.up}", "ArrowUp"] + //
input.key.left = ["${input.key.left}", "ArrowLeft"] + //
input.key.down = ["${input.key.down}", "ArrowDown"] + //
input.key.right = ["${input.key.right}", "ArrowRight"] + //
+ //
m.fieldMode = "${m.fieldUpgrades[m.fieldMode].name}" + //
input.key.field = ["${input.key.field}", "right mouse"] + //
m.field.description = "${m.fieldUpgrades[m.fieldMode].description}" + // `, 1200); + }, + disableExit: false, + nextLevel() { + if (!level.disableExit) { + level.levelsCleared++; + level.onLevel++; //cycles map to next level + if (simulation.isTraining) { + if (level.onLevel > level.levels.length - 1) { + level.disableExit = true + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + document.getElementById("text-log").style.display = "none" + document.getElementById("fade-out").style.opacity = 1; //slowly fades out + setTimeout(function () { + simulation.paused = true; + level.disableExit = false; + engine.world.bodies.forEach((body) => { + Matter.Composite.remove(engine.world, body) + }) + Engine.clear(engine); + simulation.splashReturn(); + }, 6000); + return + } else { + level.setDifficulty() + } + } else { + if (level.onLevel > level.levels.length - 1) level.onLevel = 0; + level.difficultyIncrease(simulation.difficultyMode) + } + + //reset lost tech display + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isLost) tech.tech[i].isLost = false; + } + tech.isDeathAvoidedThisLevel = false; + simulation.updateTechHUD(); + simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map + } + }, + populateLevels() { //run a second time if URL is loaded + if (document.getElementById("banned").value) { //remove levels from ban list in settings + const banList = document.getElementById("banned").value.replace(/,/g, ' ').replace(/\s\s+/g, ' ').replace(/[^\w\s]/g, '') //replace commas with spaces, replace double spaces with single, remove strange symbols + const remove = banList.split(" "); + // console.log('remove these', remove) + // console.log('community levels before', level.communityLevels) + for (let i = 0; i < remove.length; i++) { + const index = level.communityLevels.indexOf(remove[i]) + if (index !== -1) { + level.communityLevels.splice(index, 1); + // console.log('removed level:', remove[i]) + requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); + } + } + // console.log('community levels after', level.communityLevels) + // console.log('Landgreen levels before', level.playableLevels) + for (let i = 0; i < remove.length; i++) { + if (level.playableLevels.length + level.communityLevels.length * simulation.isCommunityMaps < 10) break //can't remove too many levels + const index = level.playableLevels.indexOf(remove[i]) + if (index !== -1) { + level.playableLevels.splice(index, 1); + // console.log('removed level:', remove[i]) + requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); + } + } + // console.log('Landgreen levels after', level.playableLevels) + } + + if (document.getElementById("seed").value) { //check for player entered seed in settings + Math.initialSeed = String(document.getElementById("seed").value) + Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it + } + + if (simulation.isTraining) { + simulation.isHorizontalFlipped = false + level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment + } else { //add remove and shuffle levels for the normal game (not training levels) + level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment + if (simulation.isCommunityMaps) { + level.levels = level.levels.concat(level.communityLevels) + simulation.isHorizontalFlipped = false; + } else { + simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally + } + level.levels = shuffle(level.levels); //shuffles order of maps with seeded random + level.levels.length = 9 //remove any extra levels past 9 + level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, Math.random() < 0.5 ? "factory" : "reservoir"); //add level to the back half of the randomized levels list + level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list + if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech + level.levels.unshift("intro"); //add level to the start of the randomized levels list + level.levels.push("subway"); //add level to the end of the randomized levels list + level.levels.push("final"); //add level to the end of the randomized levels list + } + } + //set seeded random lists of mobs and bosses + spawn.mobTypeSpawnOrder = [] + for (let i = 0; i < level.levels.length; i++) spawn.mobTypeSpawnOrder.push(spawn.fullPickList[Math.floor(Math.seededRandom(0, spawn.fullPickList.length))]) + spawn.bossTypeSpawnOrder = [] + for (let i = 0; i < level.levels.length * 2; i++) spawn.bossTypeSpawnOrder.push(spawn.randomBossList[Math.floor(Math.seededRandom(0, spawn.randomBossList.length))]) + }, + flipHorizontal() { + const flipX = (who) => { + for (let i = 0, len = who.length; i < len; i++) { + Matter.Body.setPosition(who[i], { + x: -who[i].position.x, + y: who[i].position.y + }) + } + } + flipX(map) + flipX(body) + flipX(mob) + flipX(powerUp) + for (let i = 0, len = cons.length; i < len; i++) { + cons[i].pointA.x *= -1 + cons[i].pointB.x *= -1 + } + for (let i = 0, len = consBB.length; i < len; i++) { + consBB[i].pointA.x *= -1 + consBB[i].pointB.x *= -1 + } + level.exit.x = -level.exit.x - 100 //minus the 100 because of the width of the graphic + }, + exitCount: 0, + // playerExitCheck() { + // if ( + // player.position.x > level.exit.x && + // player.position.x < level.exit.x + 100 && + // player.position.y > level.exit.y - 150 && + // player.position.y < level.exit.y - 40 && + // player.velocity.y < 0.1 + // ) { + // level.exitCount++ + // if (level.exitCount > 120) { + // level.exitCount = 0 + // level.nextLevel() + // } + // } + // }, + setPosToSpawn(xPos, yPos) { + m.spawnPos.x = m.pos.x = xPos; + m.spawnPos.y = m.pos.y = yPos; + level.enter.x = m.spawnPos.x - 50; + level.enter.y = m.spawnPos.y + 20; + m.transX = m.transSmoothX = canvas.width2 - m.pos.x; + m.transY = m.transSmoothY = canvas.height2 - m.pos.y; + m.Vx = m.spawnVel.x; + m.Vy = m.spawnVel.y; + player.force.x = 0; + player.force.y = 0; + Matter.Body.setPosition(player, m.spawnPos); + Matter.Body.setVelocity(player, m.spawnVel); + //makes perfect diamagnetism tech: Lenz's law show up in the right spot at the start of a level + m.fieldPosition = { + x: m.pos.x, + y: m.pos.y + } + m.fieldAngle = m.angle + }, + enter: { + x: 0, + y: 0, + draw() { + ctx.beginPath(); + ctx.moveTo(level.enter.x, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y - 80); + ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80); + ctx.lineTo(level.enter.x + 100, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y + 30); + ctx.fillStyle = "#ccc"; + ctx.fill(); + } + }, + exit: { + x: 0, + y: 0, + drawAndCheck() { + if ( //check + player.position.x > level.exit.x && + player.position.x < level.exit.x + 100 && + player.position.y > level.exit.y - 150 && + player.position.y < level.exit.y - 0 && + player.velocity.y < 0.15 + ) { + // level.exitCount += input.down ? 8 : 2 + level.exitCount += 2 + } else if (level.exitCount > 0) { + level.exitCount -= 2 + } + + ctx.beginPath(); + ctx.moveTo(level.exit.x, level.exit.y + 30); + ctx.lineTo(level.exit.x, level.exit.y - 80); + ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80); + ctx.lineTo(level.exit.x + 100, level.exit.y + 30); + ctx.lineTo(level.exit.x, level.exit.y + 30); + ctx.fillStyle = "#0ff"; + ctx.fill(); + + if (level.exitCount > 0) { //stroke outline of door from 2 sides, grows with count + ctx.beginPath(); + ctx.moveTo(level.exit.x, level.exit.y + 40); + ctx.lineTo(level.exit.x, level.exit.y - 80); + ctx.bezierCurveTo(level.exit.x, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148); + ctx.moveTo(level.exit.x + 100, level.exit.y + 40); + ctx.lineTo(level.exit.x + 100, level.exit.y - 80); + ctx.bezierCurveTo(level.exit.x + 100, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148); + ctx.setLineDash([200, 200]); + ctx.lineDashOffset = Math.max(-15, 185 - 2.1 * level.exitCount) + ctx.strokeStyle = "#444" + ctx.lineWidth = 2 + ctx.stroke(); + ctx.setLineDash([0, 0]); + + if (level.exitCount > 100) { + level.exitCount = 0 + level.nextLevel() + } + } + }, + // draw() { + // ctx.beginPath(); + // ctx.moveTo(level.exit.x, level.exit.y + 30); + // ctx.lineTo(level.exit.x, level.exit.y - 80); + // ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80); + // ctx.lineTo(level.exit.x + 100, level.exit.y + 30); + // ctx.lineTo(level.exit.x, level.exit.y + 30); + // ctx.fillStyle = "#0ff"; + // ctx.fill(); + // } + }, + addToWorld() { //needs to be run to put bodies into the world + for (let i = 0; i < map.length; i++) { + map[i].collisionFilter.category = cat.map; + map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[i], true); //make static + Composite.add(engine.world, map[i]); //add to world + } + }, + spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { + x += width / 2 + y += height / 2 + const who = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: frictionAir, + friction: 1, + frictionStatic: 1, + restitution: 0, + }); + Matter.Body.setAngle(who, angle) + Matter.Body.setAngularVelocity(who, angularVelocity); + Matter.Body.setDensity(who, density) + Composite.add(engine.world, who); //add to world + who.classType = "body" + + const constraint = Constraint.create({ //fix rotor in place, but allow rotation + pointA: { + x: who.position.x, + y: who.position.y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, constraint); + return constraint + }, + rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + x += width / 2 + y += height / 2 + const who = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: frictionAir, + friction: 1, + frictionStatic: 1, + restitution: 0, + rotationForce: rotationForce + }); + Matter.Body.setAngle(who, angle) + Matter.Body.setAngularVelocity(who, angularVelocity); + + Matter.Body.setDensity(who, density) + const constraint = Constraint.create({ //fix rotor in place, but allow rotation + pointA: { + x: who.position.x, + y: who.position.y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, constraint); + who.center = { + x: who.position.x, + y: who.position.y + } + who.rotate = function () { + if (!m.isBodiesAsleep) { + Matter.Body.applyForce(this, { + x: this.position.x + 100, + y: this.position.y + 100 + }, { + x: this.rotationForce * this.mass, + y: 0 + }) + } else { + Matter.Body.setAngularVelocity(this, 0); + } + } + // if (rotate) { + // rotor.rotate = function() { + // if (!m.isBodiesAsleep) { + // Matter.Body.applyForce(rotor, { + // x: rotor.position.x + 100, + // y: rotor.position.y + 100 + // }, { + // x: rotate * rotor.mass, + // y: 0 + // }) + // } else { + // Matter.Body.setAngularVelocity(rotor, 0); + // } + // } + // } + + Composite.add(engine.world, who); //add to world + who.classType = "body" + + return who + }, + boost(x, y, height = 1000) { //height is how high the player will be flung above y + who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("120 40 -120 40 -50 -40 50 -40"), { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + boostBounds: { + min: { + x: x, + y: y - 20 + }, + max: { + x: x + 100, + y: y + } + }, + yVelocity: -1.21 * Math.sqrt(Math.abs(height)), + query() { + // check for collisions + query = (who) => { + if (Matter.Query.region(who, this.boostBounds).length > 0) { + list = Matter.Query.region(who, this.boostBounds) + Matter.Body.setVelocity(list[0], { + x: list[0].velocity.x + (Math.random() - 0.5) * 2.5, //add a bit of horizontal drift to reduce endless bounces + y: this.yVelocity //give a upwards velocity + }); + } + } + query(body) + query(mob) + query(bullet) + query(powerUp) + //player collision + if (Matter.Query.region([player], this.boostBounds).length > 0 && !input.down) { + m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts + m.hardLandCD = 0 // disable hard landing + if (player.velocity.y > 26) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: -15 //gentle bounce if coming down super fast + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + (Math.random() - 0.5) * 2.5, + y: this.yVelocity //give a upwards velocity that will put the player that the height desired + }); + } + } + + //draw + ctx.fillStyle = "rgba(200,0,255,0.15)"; + ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 10, 100, 30); + ctx.fillStyle = "rgba(200,0,255,0.05)"; + ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 50, 100, 70); + // ctx.fillStyle = "rgba(200,0,255,0.02)"; + // ctx.fillRect(x, y - 120, 100, 120); + }, + }); + return who + }, + elevator(x, y, width, height, maxHeight, force = 0.003, friction = { + up: 0.01, + down: 0.2 + }, isAtTop = false) { + x += width / 2 + y += height / 2 + maxHeight += height / 2 + const yTravel = maxHeight - y + force += simulation.g + const who = body[body.length] = Bodies.rectangle(x, isAtTop ? maxHeight : y, width, height, { + collisionFilter: { + category: cat.body, //cat.map, + mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + frictionAir: 0.001, + holdX: x, + move() { + if (!m.isBodiesAsleep) { + if (this.isUp) { //moving up still with high air friction + this.force.y -= force * this.mass //hard force propels up, even with high friction + + if (this.position.y < maxHeight) { //switch to down mode + this.isUp = false + this.frictionAir = friction.down + //adds a hard jerk at the top of vertical motion because it's fun + Matter.Body.setPosition(this, { + x: this.holdX, + y: maxHeight + }); + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + } + } else if (this.position.y + 10 * this.velocity.y > y) { //free falling down, with only air friction + Matter.Body.setVelocity(this, { //slow down early to avoid a jerky stop that can pass through blocks + x: 0, + y: this.velocity.y * 0.7 + }); + if (this.position.y + this.velocity.y > y) { //switch to up mode + this.isUp = true + this.frictionAir = friction.up + } + } + Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); + } + //edge limits + if (this.position.y < maxHeight) { + Matter.Body.setPosition(this, { + x: this.holdX, + y: maxHeight + }); + } else if (this.position.y > y) { + Matter.Body.setPosition(this, { + x: this.holdX, + y: y + }); + } + // hold horizontal position + Matter.Body.setPosition(this, { + x: this.holdX, + y: this.position.y + }); + + }, + off() { + Matter.Body.setPosition(this, { + x: this.holdX, + y: this.position.y + }); + Matter.Body.setVelocity(this, { + x: 0, + y: this.velocity.y + }); + }, + constraint: this.null, + addConstraint() { + this.constraint = Constraint.create({ + pointA: { + x: this.position.x, + y: this.position.y + }, + bodyB: this, + stiffness: 0.01, + damping: 0.3 + }); + Composite.add(engine.world, this.constraint); + }, + removeConstraint() { + Composite.remove(engine.world, this.constraint, true) + }, + drawTrack() { + ctx.fillStyle = "#ccc" + ctx.fillRect(this.holdX, y, 5, yTravel) + } + }); + Matter.Body.setDensity(who, 0.01) //10x density for added stability + Composite.add(engine.world, who); //add to world + who.classType = "body" + return who + }, + spring(x, y, v = "-100 0 100 0 70 40 0 50 -70 40", force = 0.01, distance = 300, angle = 0) { + const who = body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(v), { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + frictionAir: 1, + density: 0.1, + isReady: true, + isResetting: false, + query() { + if (this.isReady) { + if (Matter.Query.collides(this, [player]).length) { + this.isReady = false + this.constraint.stiffness = 0 + this.constraint.damping = 0 //0.3 + this.frictionAir = 0 + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + //show graphically being ready? + + } + } else { + if (this.isResetting) { + this.constraint.stiffness += 0.0005 + if (this.constraint.stiffness > 0.1) { + this.isResetting = false + this.isReady = true + } + } else { + if (Vector.magnitudeSquared(Vector.sub(this.position, { + x: x, + y: y + })) < distance * distance) { + this.force.y -= force * this.mass + } else { + this.constraint.damping = 1 + this.frictionAir = 1 + this.isResetting = true + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + } + } + } + } + }); + who.constraint = Constraint.create({ + pointA: { + x: who.position.x, + y: who.position.y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, who.constraint); + return who + }, + // rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) { + // const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, { + // density: density, + // isNotHoldable: true, + // isNonStick: true, + // collisionFilter: { + // category: cat.map, + // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + // }, + // }); + // const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, { + // angle: Math.PI / 2, + // density: density, + // isNotHoldable: true, + // isNonStick: true, + // collisionFilter: { + // category: cat.map, + // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + // }, + // }); + // rotor = Body.create({ //combine rotor1 and rotor2 + // parts: [rotor1, rotor2], + // restitution: 0, + // collisionFilter: { + // category: cat.map, + // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + // }, + // }); + // Matter.Body.setPosition(rotor, { + // x: x, + // y: y + // }); + // Composite.add(engine.world, [rotor]); + // body[body.length] = rotor1 + // body[body.length] = rotor2 + + // // setTimeout(function() { + // // rotor.collisionFilter.category = cat.body; + // // rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map + // // }, 1000); + + // const constraint = Constraint.create({ //fix rotor in place, but allow rotation + // pointA: { + // x: x, + // y: y + // }, + // bodyB: rotor + // }); + // Composite.add(engine.world, constraint); + + // if (rotate) { + // rotor.rotate = function() { + // if (!m.isBodiesAsleep) { + // Matter.Body.applyForce(rotor, { + // x: rotor.position.x + 100, + // y: rotor.position.y + 100 + // }, { + // x: rotate * rotor.mass, + // y: 0 + // }) + // } else { + // Matter.Body.setAngularVelocity(rotor, 0); + // } + // } + // } + // composite[composite.length] = rotor + // return rotor + // }, + toggle(x, y, isOn = false, isLockOn = false) { + spawn.mapVertex(x + 65, y + 2, "70 10 -70 10 -40 -10 40 -10"); //toggle platform + map[map.length - 1].restitution = 0; + map[map.length - 1].friction = 1; // + map[map.length - 1].frictionStatic = 1; + const width = 120 + const height = 15 + body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, { friction: 0.05, frictionAir: 0.01 }); + let flip = body[body.length - 1]; + flip.collisionFilter.category = cat.body + flip.collisionFilter.mask = cat.player | cat.body + flip.isNotHoldable = true + flip.restitution = 0 + Matter.Body.setDensity(flip, 0.003) + if (isOn) { + Matter.Body.setAngle(flip, (0.25 - 0.5) * Math.PI) + } else { + Matter.Body.setAngle(flip, (-0.25 - 0.5) * Math.PI) + } + cons[cons.length] = Constraint.create({ + pointA: { + x: x + 65, + y: y - 5 + }, + bodyB: flip, + stiffness: 1, + length: 0 + }); + Composite.add(engine.world, [cons[cons.length - 1]]); + Composite.add(engine.world, flip); //add to world + flip.classType = "body" + return { + flip: flip, + isOn: isOn, + query() { + const limit = { + right: (-0.25 - 0.5) * Math.PI, + left: (0.25 - 0.5) * Math.PI + } + if (flip.angle < limit.right) { + Matter.Body.setAngle(flip, limit.right) + Matter.Body.setAngularVelocity(flip, 0); + if (!isLockOn) this.isOn = false + } else if (flip.angle > limit.left) { + Matter.Body.setAngle(flip, limit.left) + Matter.Body.setAngularVelocity(flip, 0); + this.isOn = true + } + if (this.isOn) { + ctx.beginPath(); + ctx.moveTo(flip.vertices[0].x, flip.vertices[0].y); + for (let j = 1; j < flip.vertices.length; j++) { + ctx.lineTo(flip.vertices[j].x, flip.vertices[j].y); + } + ctx.lineTo(flip.vertices[0].x, flip.vertices[0].y); + ctx.fillStyle = "#3df" + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = color.blockS; + ctx.stroke(); + } + }, + } + }, + button(x, y, width = 126, isSpawnBase = true) { + if (isSpawnBase) { + spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10"); + map[map.length - 1].restitution = 0; + map[map.length - 1].friction = 1; + map[map.length - 1].frictionStatic = 1; + } + // const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, { + // isSensor: true + // }); + + return { + isUp: false, + min: { + x: x + 2, + y: y - 11 + }, + max: { + x: x + width, + y: y - 10 + }, + width: width, + height: 20, + query() { + if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { + this.isUp = true; + } else { + if (this.isUp === true) { + const list = Matter.Query.region(body, this) //are any blocks colliding with this + if (list.length > 0) { + if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block + Matter.Body.setPosition(list[0], { //teleport block to the center of the button + x: this.min.x + width / 2, + y: list[0].position.y + }) + } + Matter.Body.setVelocity(list[0], { + x: 0, + y: 0 + }); + } + } + this.isUp = false; + } + }, + query() { + if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { + this.isUp = true; + } else { + if (this.isUp === true) { + const list = Matter.Query.region(body, this) //are any blocks colliding with this + if (list.length > 0) { + if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block + Matter.Body.setPosition(list[0], { //teleport block to the center of the button + x: this.min.x + width / 2, + y: list[0].position.y + }) + } + Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); + } + } + this.isUp = false; + } + }, + draw() { + ctx.fillStyle = "hsl(0, 100%, 70%)" + if (this.isUp) { + ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20) + } else { + ctx.fillRect(this.min.x, this.min.y - 3, this.width, 25) + } + } + } + }, + vanish(x, y, width, height, isVertical = false, hide = { + x: 0, + y: 150 + }) { + x = x + width / 2 + y = y + height / 2 + const vertices = [{ + x: x, + y: y, + index: 0, + isInternal: false + }, { + x: x + width, + y: y, + index: 1, + isInternal: false + }, { + x: x + width, + y: y + height, + index: 4, + isInternal: false + }, { + x: x, + y: y + height, + index: 3, + isInternal: false + }] + const block = body[body.length] = Bodies.fromVertices(x, y, vertices, { + // const block = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + isNonStick: true, //this keep sporangium from sticking + isTouched: false, + fadeTime: 10 + Math.ceil(0.25 * width), + fadeCount: null, + isThere: true, + returnTime: 120, + returnCount: 0, + shrinkVertices(size) { + if (isVertical) { + return [{ + x: x, + y: y * size, + index: 0, + isInternal: false + }, { + x: x + width, + y: y * size, + index: 1, + isInternal: false + }, { + x: x + width, + y: (y + height) * size, + index: 4, + isInternal: false + }, { + x: x, + y: (y + height) * size, + index: 3, + isInternal: false + }] + } else { + return [{ + x: x * size, + y: y, + index: 0, + isInternal: false + }, { + x: (x + width) * size, + y: y, + index: 1, + isInternal: false + }, { + x: (x + width) * size, + y: y + height, + index: 4, + isInternal: false + }, { + x: x * size, + y: y + height, + index: 3, + isInternal: false + }] + } + }, + query() { + if (this.isThere) { + if (this.isTouched) { + if (!m.isBodiesAsleep) { + this.fadeCount-- + Matter.Body.setVertices(this, this.shrinkVertices(Math.max(this.fadeCount / this.fadeTime, 0.03))) + } + if (this.fadeCount < 1) { + Matter.Body.setPosition(this, hide) + this.isThere = false + this.isTouched = false + this.collisionFilter.mask = 0 //cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + this.returnCount = this.returnTime + Matter.Body.setVertices(this, this.shrinkVertices(1)) + Matter.Body.setVertices(this, vertices) + } + } else if (Matter.Query.collides(this, [player]).length) { // || (Matter.Query.collides(this, body).length)) { + this.isTouched = true + this.fadeCount = this.fadeTime; + } + } else { + if (!m.isBodiesAsleep) { + this.returnCount-- + if (this.returnCount < 1) { + Matter.Body.setPosition(this, { + x: x, + y: y + }) + if (Matter.Query.collides(this, [player]).length) { //|| (Matter.Query.collides(this, body).length)) { + Matter.Body.setPosition(this, hide) + this.returnCount = 15 + } else { + this.isThere = true + this.collisionFilter.mask = cat.player | cat.mob | cat.body | cat.bullet | cat.powerUp | cat.mobBullet + this.fadeCount = this.fadeTime + //delete any overlapping blocks + const blocks = Matter.Query.collides(this, body) + for (let i = 0; i < blocks.length; i++) { + if (blocks[i].bodyB !== this && blocks[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe... + Matter.Composite.remove(engine.world, blocks[i].bodyB); + blocks[i].bodyB.isRemoveMeNow = true + for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array + if (body[i].isRemoveMeNow) { + body.splice(i, 1); + break + } + } + } + } + //delete any overlapping mobs + // const mobsHits = Matter.Query.collides(this, mob) + // for (let i = 0; i < mobsHits.length; i++) { + // if (mobsHits[i].bodyB !== this && mobsHits[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe... + // Matter.Composite.remove(engine.world, mobsHits[i].bodyB); + // mobsHits[i].bodyB.isRemoveMeNow = true + // for (let i = 1; i < mob.length; i++) { //find which index in body array it is and remove from array + // if (mob[i].isRemoveMeNow) { + // mob.splice(i, 1); + // break + // } + // } + // } + // } + } + } + } + } + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.lineTo(v[0].x, v[0].y); + ctx.fillStyle = "#586370" + ctx.fill(); + // const color = 220 * (1 - this.fadeCount / this.fadeTime) + // ctx.fillStyle = `rgb(${color},220, 200)` + // ctx.fillStyle = `rgba(0,220,200,${this.fadeCount/this.fadeTime+0.05})` + // ctx.strokeStyle = `#bff` + // ctx.stroke(); + }, + }); + Matter.Body.setStatic(block, true); //make static + Composite.add(engine.world, block); //add to world + // who.classType = "body" + if (simulation.isHorizontalFlipped) x *= -1 + return block + }, + door(x, y, width, height, distance, speed = 1) { + x = x + width / 2 + y = y + height / 2 + const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.body,//cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + isClosing: false, + openClose() { + if (!m.isBodiesAsleep) { + if (this.isClosing) { + if (this.position.y < y) { //try to close + if ( //if clear of stuff + Matter.Query.collides(this, [player]).length === 0 && + Matter.Query.collides(this, body).length < 2 && + Matter.Query.collides(this, mob).length === 0 + ) { + const position = { + x: this.position.x, + y: this.position.y + speed + } + Matter.Body.setPosition(this, position) + } + } + } else { + if (this.position.y > y - distance) { //try to open + const position = { + x: this.position.x, + y: this.position.y - speed + } + Matter.Body.setPosition(this, position) + } + } + } + }, + isClosed() { + return this.position.y > y - 1 + }, + draw() { + ctx.fillStyle = "#666" + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.lineTo(v[0].x, v[0].y); + ctx.fill(); + } + }); + Matter.Body.setStatic(doorBlock, true); //make static + Composite.add(engine.world, doorBlock); //add to world + doorBlock.classType = "body" + return doorBlock + }, + doorMap(x, y, width, height, distance, speed = 20, addToWorld = true) { //for doors that use line of sight + x = x + width / 2 + y = y + height / 2 + const door = map[map.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet, + // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + isClosing: false, + openClose(isSetPaths = false) { + if (!m.isBodiesAsleep) { + if (this.isClosing) { + if (this.position.y < y) { //try to close + if ( //if clear of stuff + Matter.Query.collides(this, [player]).length === 0 && + Matter.Query.collides(this, body).length < 2 && + Matter.Query.collides(this, mob).length === 0 + ) { + const position = { + x: this.position.x, + y: this.position.y + speed + } + Matter.Body.setPosition(this, position) + if (isSetPaths) { + simulation.draw.setPaths() + simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight + } + } + } + } else { + if (this.position.y > y - distance) { //try to open + const position = { + x: this.position.x, + y: this.position.y - speed + } + Matter.Body.setPosition(this, position) + if (isSetPaths) { + simulation.draw.setPaths() + simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight + } + } + } + } + }, + isClosed() { + return this.position.y > y - 1 + }, + draw() { + ctx.fillStyle = "#666" + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.lineTo(v[0].x, v[0].y); + ctx.fill(); + } + }); + + Matter.Body.setStatic(door, true); //make static + if (addToWorld) Composite.add(engine.world, door); //add to world + door.classType = "map" + return door + }, + portal(centerA, angleA, centerB, angleB) { + const width = 50 + const height = 150 + const mapWidth = 200 + const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA) + const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB) + draw = function () { + ctx.beginPath(); //portal + let v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.fillStyle = this.color + ctx.fill(); + } + query = function (isRemoveBlocks = false) { + if (Matter.Query.collides(this, [player]).length === 0) { //not touching player + if (player.isInPortal === this) player.isInPortal = null + } else if (player.isInPortal !== this) { //touching player + if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal + m.buttonCD_jump = 0 //disable short jumps when letting go of jump key + player.isInPortal = this.portalPair + //teleport + if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + Matter.Body.setPosition(player, this.portalPair.portal.position); + } else { //if at some odd angle + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + Matter.Body.setPosition(player, this.portalPair.position); + } + //rotate velocity + let mag + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11 + } else { + mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity))) + } + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(player, v); + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position); + Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); + } + } + if (tech.isHealAttract) { //send heals to next portal + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) { + Matter.Body.setPosition(powerUp[i], Vector.add(this.portalPair.portal.position, { x: 500 * (Math.random() - 0.5), y: 500 * (Math.random() - 0.5) })); + } + } + } + } + // if (body.length) { + for (let i = 0, len = body.length; i < len; i++) { + if (body[i] !== m.holdingTarget) { + // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 + if (Matter.Query.collides(this, [body[i]]).length === 0) { + if (body[i].isInPortal === this) body[i].isInPortal = null + } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time + if (isRemoveBlocks) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + break + } + body[i].isInPortal = this.portalPair + //teleport + if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down + Matter.Body.setPosition(body[i], this.portalPair.portal.position); + } else { //if at some odd angle + Matter.Body.setPosition(body[i], this.portalPair.position); + } + //rotate velocity + let mag + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 + } else { + mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) + } + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(body[i], v); + } + } + } + // } + + //remove block if touching + // if (body.length) { + // touching = Matter.Query.collides(this, body) + // for (let i = 0; i < touching.length; i++) { + // if (touching[i].bodyB !== m.holdingTarget) { + // for (let j = 0, len = body.length; j < len; j++) { + // if (body[j] === touching[i].bodyB) { + // body.splice(j, 1); + // len-- + // Matter.Composite.remove(engine.world, touching[i].bodyB); + // break; + // } + // } + // } + // } + // } + + // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) { + // if (body.length) { + // for (let i = 0; i < body.length; i++) { + // if (body[i] === touching[0].bodyB) { + // body.splice(i, 1); + // break; + // } + // } + // } + // Matter.Composite.remove(engine.world, touching[0].bodyB); + // } + } + + const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, { + isSensor: true, + angle: angleA, + color: "hsla(197, 100%, 50%,0.7)", + draw: draw, + }); + const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, { + isSensor: true, + angle: angleB, + color: "hsla(29, 100%, 50%, 0.7)", + draw: draw + }); + const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, { + collisionFilter: { + category: cat.map, + mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitA, + angle: angleA, + color: color.map, + draw: draw, + query: query, + lastPortalCycle: 0 + }); + Matter.Body.setStatic(mapA, true); //make static + Composite.add(engine.world, mapA); //add to world + + const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, { + collisionFilter: { + category: cat.map, + mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitB, + angle: angleB, + color: color.map, + draw: draw, + query: query, + lastPortalCycle: 0, + }); + Matter.Body.setStatic(mapB, true); //make static + Composite.add(engine.world, mapB); //add to world + + mapA.portal = portalA + mapB.portal = portalB + mapA.portalPair = mapB + mapB.portalPair = mapA + return [portalA, portalB, mapA, mapB] + }, + drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + return { + x: x, + y: yMin, + period: period, + dropCycle: 0, + speed: 0, + draw() { + if (!m.isBodiesAsleep) { + if (this.dropCycle < simulation.cycle) { //reset + this.dropCycle = simulation.cycle + this.period + Math.floor(40 * Math.random()) + this.y = yMin + this.speed = 1 + } else { //fall + this.speed += 0.35 //acceleration from gravity + this.y += this.speed + } + } + if (this.y < yMax) { //draw + ctx.fillStyle = color //"hsla(160, 100%, 35%,0.75)" + ctx.beginPath(); + ctx.arc(this.x, this.y, 8, 0, 2 * Math.PI); + ctx.fill(); + } + } + } + }, + isHazardRise: false, + hazard(x, y, width, height, damage = 0.002) { + return { + min: { x: x, y: y }, + max: { x: x + width, y: y + height }, + width: width, + height: height, + maxHeight: height, + isOn: true, + opticalQuery() { + if (this.isOn) { + //draw + ctx.fillStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})` + ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + //collision with player + if (this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak)) { + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; + m.damage(damage) + simulation.drawList.push({ //add dmg to draw queue + x: player.position.x, + y: player.position.y, + radius: damage * 1500, + color: simulation.mobDmgColor, + time: 20 + }); + } + } + } + }, + query() { + if (this.isOn) { + ctx.fillStyle = "hsla(160, 100%, 35%,0.75)" + const offset = 5 * Math.sin(simulation.cycle * 0.015) + ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset) + + if (this.height > 0 && Matter.Query.region([player], this).length) { + if (m.immuneCycle < m.cycle) { + const DRAIN = 0.004 * (tech.isRadioactiveResistance ? 0.25 : 1) + if (m.energy > DRAIN) { + m.energy -= DRAIN + if (tech.isEnergyHealth && m.energy < 0) m.death() + } else { + m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1)) + + } + } + //float + if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g + const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up + Matter.Body.setVelocity(player, { + x: Math.max(0.95, 1 - 0.036 * Math.abs(player.velocity.x)) * player.velocity.x, + y: slowY * player.velocity.y + }); + //undo 1/2 of gravity + player.force.y -= 0.5 * player.mass * simulation.g; + + } + //float power ups + powerUpCollide = Matter.Query.region(powerUp, this) + for (let i = 0, len = powerUpCollide.length; i < len; i++) { + const diameter = 2 * powerUpCollide[i].size + const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter + powerUpCollide[i].force.y -= buoyancy * 1.24 * powerUpCollide[i].mass * simulation.g; + Matter.Body.setVelocity(powerUpCollide[i], { + x: powerUpCollide[i].velocity.x, + y: 0.97 * powerUpCollide[i].velocity.y + }); + } + } + }, + // draw() { + // if (this.isOn) { + // ctx.fillStyle = color + // ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + // } + // }, + levelRise(growRate = 1) { + if (this.height < this.maxHeight && !m.isBodiesAsleep) { + this.height += growRate + this.min.y -= growRate + this.max.y = this.min.y + this.height + } + }, + levelFall(fallRate = 1) { + if (this.height > 0 && !m.isBodiesAsleep) { + this.height -= fallRate + this.min.y += fallRate + this.max.y = this.min.y + this.height + } + }, + level(isFill, growSpeed = 1) { + if (!m.isBodiesAsleep) { + if (isFill) { + if (this.height < this.maxHeight) { + this.height += growSpeed + this.min.y -= growSpeed + this.max.y = this.min.y + this.height + } + } else if (this.height > 0) { + this.height -= growSpeed + this.min.y += growSpeed + this.max.y = this.min.y + this.height + } + } + } + } + }, + mover(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { + //VxGoal below 3 don't move well, maybe try adjusting the force + x = x + width / 2 + y = y + height / 2 + const rect = map[map.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 0, + frictionStatic: 0, + restitution: 0, + isClosing: false, + isMover: true, + VxGoal: VxGoal, + force: force, + push() { + if (!m.isBodiesAsleep) { + const touchingPlayer = Matter.Query.collides(this, [jumpSensor]) + if (touchingPlayer.length) { + m.moverX = this.VxGoal + if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) { + player.force.x += this.force * player.mass + } + m.Vx = player.velocity.x - this.VxGoal + } + let pushBlock = (who) => { + if (!who.isMover) { + if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { + who.force.x += this.force * who.mass + } + const stoppingFriction = 0.5 + Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); + Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.9) + } + } + const blocks = Matter.Query.collides(this, body) + for (let i = 0; i < blocks.length; i++) { + pushBlock(blocks[i].bodyA) + pushBlock(blocks[i].bodyB) + } + const mobTargets = Matter.Query.collides(this, mob) + for (let i = 0; i < mobTargets.length; i++) { + pushBlock(mobTargets[i].bodyA) + pushBlock(mobTargets[i].bodyB) + } + let pushPowerUp = (who) => { + if (!who.isMover) { + if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { + who.force.x += 2 * this.force * who.mass + } + const stoppingFriction = 0.5 + Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); + } + } + const powers = Matter.Query.collides(this, powerUp) + for (let i = 0; i < powers.length; i++) { + pushPowerUp(powers[i].bodyA) + pushPowerUp(powers[i].bodyB) + } + } + }, + draw() { + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x + 2, v[0].y); + // for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.lineTo(v[1].x - 2, v[1].y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4; + ctx.setLineDash([40, 40]); + ctx.lineDashOffset = (-simulation.cycle * this.VxGoal) % 80; + ctx.stroke(); + ctx.setLineDash([0, 0]); + } + }); + Matter.Body.setStatic(rect, true); //make static + return rect + }, + transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { + //horizontal moving platform + //VxGoal below 3 don't move well, maybe try adjusting the force + x = x + width / 2 + y = y + height / 2 + const rect = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 0, + frictionStatic: 0, + restitution: 0, + isClosing: false, + isMover: true, + VxGoal: VxGoal, + force: force, + move() { + if (!m.isBodiesAsleep) { + Matter.Body.setPosition(this, { x: this.position.x + this.VxGoal, y: this.position.y }); //horizontal movement + const touchingPlayer = Matter.Query.collides(this, [jumpSensor]) + if (touchingPlayer.length) { + m.moverX = this.VxGoal + if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) { + player.force.x += this.force * player.mass + } + m.Vx = player.velocity.x - this.VxGoal + } + let pushBlock = (who) => { + if (!who.isMover) { + if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { + who.force.x += this.force * who.mass + } + const stoppingFriction = 0.5 + Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); + Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8) + } + } + const blocks = Matter.Query.collides(this, body) + for (let i = 0; i < blocks.length; i++) { + pushBlock(blocks[i].bodyA) + pushBlock(blocks[i].bodyB) + } + } + }, + draw() { + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.lineTo(v[0].x, v[0].y); + ctx.fillStyle = "#586370" + ctx.fill(); + }, + changeDirection(isRight) { + if (isRight) { + this.VxGoal = Math.abs(this.VxGoal) + this.force = Math.abs(this.force) + if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass + } else { + this.VxGoal = -Math.abs(this.VxGoal) + this.force = -Math.abs(this.force) + if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass + } + }, + trainSpeed: Math.abs(VxGoal), + trainKickPlayer: 12 * Math.abs(force), + isSensing: false, + stops: { left: x, right: x + 1000 }, //this should probably be reset in the level code for the actual train stops + trainStop() { + if (this.isMoving) { + this.move(); + //oscillate back and forth + if (this.position.x < this.stops.left) {//stop + this.VxGoal = this.trainSpeed + this.force = 0.0005 + this.isMoving = false + this.isSensing = false + if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off + } else if (this.position.x > this.stops.right) {//stop + this.VxGoal = -this.trainSpeed + this.force = -0.0005 + this.isMoving = false + this.isSensing = false + if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off + } + } else if (this.isSensing) { + if (Matter.Query.collides(this, [jumpSensor]).length) { + this.isMoving = true + this.move(); //needs to move out of the stop range + // if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off + if (Matter.Query.collides(this, [jumpSensor]).length) { + Matter.Body.setVelocity(player, { x: this.VxGoal, y: player.velocity.y }); + } + } else if (this.position.x > this.stops.right && player.position.x < this.stops.left + 500) {//head to other stop if the player is far away + this.changeDirection(false) //go left + this.isMoving = true + this.move(); //needs to move out of the stop range + } else if (this.position.x < this.stops.left && player.position.x > this.stops.right - 500) {//head to other stop if the player is far away + this.changeDirection(true) //go right + this.isMoving = true + this.move(); //needs to move out of the stop range + } + } else if (!Matter.Query.collides(this, [jumpSensor]).length) {//wait until player is off the train to start sensing + this.isSensing = true + } + }, + }); + Matter.Body.setStatic(rect, true); //make static + Composite.add(engine.world, rect); //add to world + rect.classType = "body" + return rect + }, + chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) { + const gap = 2 * radius + const unit = { + x: Math.cos(angle), + y: Math.sin(angle) + } + for (let i = 0; i < len; i++) { + body[body.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, { + inertia: Infinity, + isNotHoldable: true + }); + const who = body[body.length - 1] + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + Composite.add(engine.world, who); //add to world + who.classType = "body" + } + for (let i = 1; i < len; i++) { //attach blocks to each other + consBB[consBB.length] = Constraint.create({ + bodyA: body[body.length - i], + bodyB: body[body.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + cons[cons.length] = Constraint.create({ //pin first block to a point in space + pointA: { + x: x, + y: y + }, + bodyB: body[body.length - len], + stiffness: 1, + damping: damping + }); + Composite.add(engine.world, cons[cons.length - 1]); + if (isAttached) { + cons[cons.length] = Constraint.create({ //pin last block to a point in space + pointA: { + x: x + gap * unit.x * (len - 1), + y: y + gap * unit.y * (len - 1) + }, + bodyB: body[body.length - 1], + stiffness: 1, + damping: damping + }); + Composite.add(engine.world, cons[cons.length - 1]); + } + }, + //****************************************************************************************************************** + //****************************************************************************************************************** + //****************************************************************************************************************** + //****************************************************************************************************************** + template() { + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 1500; + level.exit.y = -1875; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + // color.map = "#444" //custom map color + + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { }; + + spawn.mapRect(-100, 0, 1000, 100); + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + testing() { + simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode + + document.body.style.backgroundColor = "#fff"; + // color.map = "#444" //custom map color + // level.difficultyIncrease(14); //hard mode level 7 + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + + const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force + + const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force + spawn.bodyRect(1900, -550, 50, 50); + const button = level.button(2535, -200) + // spawn.bodyRect(250, -450, 50, 50); //block on button + + level.custom = () => { + + //oscillate back and forth + if (train.position.x < 2000) { + train.changeDirection(true) //go right + } else if (train.position.x > 4000) { + train.changeDirection(false) //go left + } + if (!button.isUp) train.move(); + + mover.push(); + ctx.fillStyle = "#d4d4d4" + ctx.fillRect(2500, -475, 200, 300) + + ctx.fillStyle = "#ddd" + ctx.fillRect(-150, -1000, 6875, 1000); + ctx.fillStyle = "rgba(0,255,255,0.1)"; + ctx.fillRect(6400, -550, 300, 350); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + train.draw() + mover.draw(); + button.query(); + button.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-150, -650, 900, 250) + }; + level.setPosToSpawn(0, -450); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 6500; + level.exit.y = -230; + + spawn.mapRect(-950, 0, 8200, 800); //ground + spawn.mapRect(-950, -1200, 800, 1400); //left wall + spawn.mapRect(-950, -1800, 8200, 800); //roof + spawn.mapRect(-250, -400, 1000, 600); // shelf + spawn.mapRect(-250, -1200, 1000, 550); // shelf roof + // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false); + + function blockDoor(x, y, blockSize = 58) { + spawn.mapRect(x, y - 290, 40, 60); // door lip + spawn.mapRect(x, y, 40, 50); // door lip + for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize); + } + + spawn.mapRect(2500, -1200, 200, 750); //right wall + spawn.mapRect(2500, -200, 200, 300); //right wall + spawn.mapRect(4500, -1200, 200, 650); //right wall + blockDoor(4585, -310) + spawn.mapRect(4500, -300, 200, 400); //right wall + spawn.mapRect(6400, -1200, 400, 750); //right wall + spawn.mapRect(6400, -200, 400, 300); //right wall + spawn.mapRect(6700, -1800, 800, 2600); //right wall + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump + //place to hide + spawn.mapRect(4650, -300, 1150, 50); + spawn.mapRect(5750, -300, 50, 200); + spawn.mapRect(5575, -100, 50, 125); + spawn.mapRect(5300, -275, 50, 175); + spawn.mapRect(5050, -100, 50, 150); + spawn.mapRect(4850, -275, 50, 175); + spawn.mapRect(-950, -3250, 850, 1750); + //roof + spawn.mapRect(-175, -2975, 300, 1425); + spawn.mapRect(75, -2650, 325, 1150); + spawn.mapRect(375, -2225, 250, 650); + spawn.mapRect(4075, -2125, 700, 800); + spawn.mapRect(4450, -2950, 675, 1550); + spawn.mapRect(4875, -3625, 725, 2225); + spawn.mapRect(5525, -4350, 1725, 2925); + spawn.mapRect(7200, -5125, 300, 3900); + + + //??? + // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why + // m.addHealth(Infinity) + + // spawn.starter(1900, -500, 200) //big boy + // spawn.starter(1900, -500, 100) //big boy + // for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500) + // spawn.suckerBoss(1900, -500) + // spawn.launcherBoss(3200, -500) + // spawn.laserTargetingBoss(1700, -500) + // spawn.powerUpBoss(1900, -500) + // spawn.powerUpBossBaby(3200, -500) + // spawn.dragonFlyBoss(1700, -500) + // spawn.streamBoss(3200, -500) + // spawn.pulsarBoss(1700, -500) + // spawn.spawnerBossCulture(3200, -500) + // spawn.grenadierBoss(1700, -500) + // spawn.growBossCulture(3200, -500) + // spawn.blinkBoss(1700, -500) + // spawn.snakeSpitBoss(3200, -500) + // spawn.laserBombingBoss(1700, -500) + // spawn.launcherBoss(3200, -500) + // spawn.blockBoss(1700, -500) + // spawn.blinkBoss(3200, -500) + // spawn.spiderBoss(1700, -500) + // spawn.tetherBoss(1700, -500) //go to actual level? + // spawn.revolutionBoss(1900, -500) + // spawn.bomberBoss(1400, -500) + // spawn.cellBossCulture(1600, -500) + // spawn.shieldingBoss(1700, -500) + + // for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40); + // for (let i = 0; i < 4; i++) spawn.starter(1900, -500) + // spawn.pulsar(1900, -500) + // spawn.shield(mob[mob.length - 1], 1900, -500, 1); + // mob[mob.length - 1].isShielded = true + // spawn.nodeGroup(1200, 0, "grenadier") + // spawn.blinkBoss(1200, -500) + // spawn.suckerBoss(2900, -500) + // spawn.randomMob(1600, -500) + }, + null() { + level.levels.pop(); //remove lore level from rotation + // level.onLevel-- + // console.log(level.onLevel, level.levels) + //start a conversation based on the number of conversations seen + if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high + if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) { + tech.isNoDraftPause = true //disable pause + lore.testSpeechAPI() //see if speech is working + lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations) + lore.sentence = 0 //what part of the conversation to start on + lore.conversation[lore.chapter][lore.sentence]() + localSettings.loreCount++ //hear the next conversation next time you win + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + // const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)") + level.isHazardRise = false //this is set to true to make the slime rise up + const hazardSlime = level.hazard(-1800, -800, 3600, 1600, 0.004) + hazardSlime.height -= 950 + hazardSlime.min.y += 950 + hazardSlime.max.y = hazardSlime.min.y + hazardSlime.height + const circle = { + x: 0, + y: -500, + radius: 50 + } + level.custom = () => { + //draw wide line + ctx.beginPath(); + ctx.moveTo(circle.x, -800) + ctx.lineTo(circle.x, circle.y) + ctx.lineWidth = 40; + ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc"; + ctx.globalAlpha = 0.03; + ctx.stroke(); + ctx.globalAlpha = 1; + //support pillar + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(-25, 0, 50, 1000); + + //draw circles + ctx.beginPath(); + ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI); + ctx.fillStyle = "#bcc" + ctx.fill(); + ctx.lineWidth = 2; + ctx.strokeStyle = "#abb"; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI); + ctx.fillStyle = lore.talkingColor //"#dff" + ctx.fill(); + + // level.enter.draw(); + }; + let sway = { + x: 0, + y: 0 + } + let phase = -Math.PI / 2 + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fillRect(-1950, -950, 3900, 1900); + //draw center circle lines + ctx.beginPath(); + const step = Math.PI / 20 + const horizontalStep = 85 + if (simulation.isCheating) phase += 0.3 * Math.random() * Math.random() //(m.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030) + // const sway = 5 * Math.cos(simulation.cycle * 0.007) + sway.x = sway.x * 0.995 + 0.005 * (m.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01) + sway.y = 2.5 * Math.sin(simulation.cycle * 0.015) + for (let i = -19.5; i < 20; i++) { + const where = { + x: circle.x + circle.radius * Math.cos(i * step + phase), + y: circle.y + circle.radius * Math.sin(i * step + phase) + } + ctx.moveTo(where.x, where.y); + ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), + sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), + horizontalStep * i, -800); + } + ctx.lineWidth = 0.5; + ctx.strokeStyle = "#899"; + ctx.stroke(); + hazardSlime.query(); + if (level.isHazardRise) hazardSlime.level(true) + //draw wires + // ctx.beginPath(); + // ctx.moveTo(-500, -800); + // ctx.quadraticCurveTo(-800, -100, -1800, -375); + // ctx.moveTo(-600, -800); + // ctx.quadraticCurveTo(-800, -200, -1800, -325); + // ctx.lineWidth = 1; + // ctx.strokeStyle = "#9aa"; + // ctx.stroke(); + }; + level.setPosToSpawn(0, -50); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10); + level.exit.x = 0; + level.exit.y = 40000; + level.defaultZoom = 1000 + simulation.zoomTransition(level.defaultZoom) + // document.body.style.backgroundColor = "#aaa"; + document.body.style.backgroundColor = "#ddd"; + color.map = "#586363" //808f8f" + + spawn.mapRect(-3000, 800, 5000, 1200); //bottom + spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling + spawn.mapRect(-3000, -2000, 1200, 3400); //left + spawn.mapRect(1800, -1400, 1200, 3400); //right + + spawn.mapRect(-500, 0, 1000, 50); //center platform + spawn.mapRect(-500, -25, 25, 50); //edge shelf + spawn.mapRect(475, -25, 25, 50); //edge shelf + }, + intro() { + // console.log(level.levelsCleared) + if (level.levelsCleared === 0) { //if this is the 1st level of the game + //wait to spawn power ups until unpaused + //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode + const goal = simulation.cycle + 10 + + function cycle() { + if (simulation.cycle > goal) { + if (localSettings.loreCount === 6) { + powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false); + } else { + powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125); + } + if (simulation.difficultyMode < 5) { + powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false); + powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false); + powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); //not on why difficulty + } + } else { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + + if (localSettings.levelsClearedLastGame < 3) { + if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) { + spawn.wireFoot(); + spawn.wireFootLeft(); + spawn.wireKnee(); + spawn.wireKneeLeft(); + spawn.wireHead(); + // for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns + // spawn.mapRect(2000, -1025, 200, 25); + } + } else if (!build.isExperimentRun) { + simulation.trails() + //bonus power ups for clearing runs in the last game + if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) { + for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game + simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); + simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`); + localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + } + spawn.mapRect(2025, 0, 150, 50); //lid to floor hole + } else { + for (let i = 0; i < 60; i++) { + setTimeout(() => { + if (level.levels[level.onLevel] === "intro") spawn.sneaker(2100, -1500 - 50 * i); + }, 2000 + 500 * i); + } + } + const wires = new Path2D() //pre-draw the complex lighting path to save processing + wires.moveTo(-150, -275) + wires.lineTo(80, -275) + wires.lineTo(80, -1000) + wires.moveTo(-150, -265) + wires.lineTo(90, -265) + wires.lineTo(90, -1000) + wires.moveTo(-150, -255) + wires.lineTo(100, -255) + wires.lineTo(100, -1000) + wires.moveTo(-150, -245) + wires.lineTo(1145, -245) + wires.lineTo(1145, 0) + wires.moveTo(-150, -235) + wires.lineTo(1135, -235) + wires.lineTo(1135, 0) + wires.moveTo(-150, -225) + wires.lineTo(1125, -225) + wires.lineTo(1125, 0) + wires.moveTo(-150, -215) + wires.lineTo(460, -215) + wires.lineTo(460, 0) + wires.moveTo(-150, -205) + wires.lineTo(450, -205) + wires.lineTo(450, 0) + wires.moveTo(-150, -195) + wires.lineTo(440, -195) + wires.lineTo(440, 0) + + wires.moveTo(1155, 0) + wires.lineTo(1155, -450) + wires.lineTo(1000, -450) + wires.lineTo(1000, -1000) + wires.moveTo(1165, 0) + wires.lineTo(1165, -460) + wires.lineTo(1010, -460) + wires.lineTo(1010, -1000) + wires.moveTo(1175, 0) + wires.lineTo(1175, -470) + wires.lineTo(1020, -470) + wires.lineTo(1020, -1000) + wires.moveTo(1185, 0) + wires.lineTo(1185, -480) + wires.lineTo(1030, -480) + wires.lineTo(1030, -1000) + wires.moveTo(1195, 0) + wires.lineTo(1195, -490) + wires.lineTo(1040, -490) + wires.lineTo(1040, -1000) + + wires.moveTo(1625, -1000) + wires.lineTo(1625, 0) + wires.moveTo(1635, -1000) + wires.lineTo(1635, 0) + wires.moveTo(1645, -1000) + wires.lineTo(1645, 0) + wires.moveTo(1655, -1000) + wires.lineTo(1655, 0) + wires.moveTo(1665, -1000) + wires.lineTo(1665, 0) + + wires.moveTo(1675, -465) + wires.lineTo(2325, -465) + wires.lineTo(2325, 0) + wires.moveTo(1675, -455) + wires.lineTo(2315, -455) + wires.lineTo(2315, 0) + wires.moveTo(1675, -445) + wires.lineTo(2305, -445) + wires.lineTo(2305, 0) + wires.moveTo(1675, -435) + wires.lineTo(2295, -435) + wires.lineTo(2295, 0) + + wires.moveTo(2335, 0) + wires.lineTo(2335, -710) + wires.lineTo(2600, -710) + wires.moveTo(2345, 0) + wires.lineTo(2345, -700) + wires.lineTo(2600, -700) + wires.moveTo(2355, 0) + wires.lineTo(2355, -690) + wires.lineTo(2600, -690) + + level.custom = () => { + //push around power ups stuck in the tube wall + if (!(simulation.cycle % 30)) { + for (let i = 0, len = powerUp.length; i < len; i++) { + if (powerUp[i].position.y < -1000) powerUp[i].force.x += 0.01 * (Math.random() - 0.5) * powerUp[i].mass + } + } + //draw binary number + const binary = (localSettings.runCount >>> 0).toString(2) + const height = 20 + const width = 8 + const yOff = -40 //-580 + let xOff = -130 //2622 + ctx.strokeStyle = "#bff" + ctx.lineWidth = 1.5; + ctx.beginPath() + for (let i = 0; i < binary.length; i++) { + if (binary[i] === "0") { + ctx.moveTo(xOff, yOff) + ctx.lineTo(xOff, yOff + height) + ctx.lineTo(xOff + width, yOff + height) + ctx.lineTo(xOff + width, yOff) + ctx.lineTo(xOff, yOff) + xOff += 10 + width + } else { + ctx.moveTo(xOff, yOff) + ctx.lineTo(xOff, yOff + height) + xOff += 10 + } + } + ctx.stroke(); + + ctx.beginPath() + ctx.strokeStyle = "#ccc" + ctx.lineWidth = 5; + ctx.stroke(wires); + + //squares that look like they keep the wires in place + ctx.beginPath() + ctx.rect(1600, -500, 90, 100) + ctx.rect(-55, -285, 12, 100) + ctx.rect(1100, -497, 8, 54) + ctx.rect(2285, -200, 80, 10) + ctx.rect(1110, -70, 100, 10) + ctx.fillStyle = "#ccc" + ctx.fill() + + //power up dispenser + // ctx.beginPath() + // for (let i = 2; i < 10; i++) { + // ctx.moveTo(2000, -100 * i) + // ctx.lineTo(2080, -100 * i) + // } + // ctx.strokeStyle = "#ddd" + // ctx.lineWidth = 5; + // ctx.stroke(); + + // ctx.beginPath() + // for (let i = 2; i < 10; i++) { + // ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI); + // ctx.moveTo(2040, -100 * i) + // } + // ctx.fillStyle = "rgba(0,0,0,0.3)" + // ctx.fill() + + // ctx.fillStyle = "rgba(240,255,255,0.5)" + // ctx.fillRect(2000, -1000, 80, 700) + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(2600, -600, 400, 300) + + // level.enter.draw(); + level.exit.drawAndCheck(); + }; + + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(2600, -600, 400, 300) + //draw shade for ceiling tech + ctx.fillStyle = "rgba(68, 68, 68,0.95)" + ctx.fillRect(2030, -2800, 150, 1800); + ctx.fillStyle = "rgba(68, 68, 68,0.95)" + ctx.fillRect(2030, 0, 150, 1800); + }; + + + + level.setPosToSpawn(460, -100); //normal spawn + // level.enter.x = -1000000; //hide enter graphic for first level by moving to the far left + level.exit.x = 2800; + level.exit.y = -335; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1000 //1400 is normal + level.defaultZoom = 1600 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = "#e1e1e1"; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(3000, -2800, 2600, 4600); //right wall + + // spawn.mapRect(-250, 0, 3600, 1800); //ground + spawn.mapRect(-250, 0, 2300, 1800); //split roof + spawn.mapRect(2150, 0, 1200, 1800); //split roof + spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber + spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber + + // spawn.mapRect(-250, -2800, 3600, 1800); //roof + spawn.mapRect(-250, -2800, 2300, 1800); //split roof + map[map.length - 1].friction = 0 + map[map.length - 1].frictionStatic = 0 + spawn.mapRect(2150, -2800, 1200, 1800); //split roof + map[map.length - 1].friction = 0 + map[map.length - 1].frictionStatic = 0 + spawn.mapRect(2025, -1010, 25, 13); //lip on power up chamber + spawn.mapRect(2150, -1010, 25, 13); //lip on power up chamber + + spawn.mapRect(2600, -300, 500, 500); //exit shelf + spawn.mapRect(2600, -1200, 500, 600); //exit roof + spawn.mapRect(-95, -1100, 80, 110); //wire source + spawn.mapRect(410, -10, 90, 20); //small platform for player + + spawn.bodyRect(2425, -120, 70, 50); + spawn.bodyRect(2400, -100, 100, 60); + spawn.bodyRect(2500, -150, 100, 150); //exit step + }, + final() { + // color.map = "rgba(0,0,0,0.8)" + const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) { + slime.height -= slime.maxHeight - 150 //start slime at zero + slime.min.y += slime.maxHeight + slime.max.y = slime.min.y + slime.height + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + slime.query(); + slime.levelRise(0.1) + + ctx.fillStyle = "rgba(0,255,255,0.1)" + ctx.fillRect(5385, -550, 300, 250) + }; + + level.setPosToSpawn(0, -250); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot + level.exit.x = 0; + level.exit.y = -8000; + + level.defaultZoom = 2500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#ddd"; + + for (let i = 0; i < 16; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo"); + + spawn.mapRect(-1950, 0, 8200, 1800); //ground + spawn.mapRect(-1950, -1500, 1800, 1900); //left wall + spawn.mapRect(-1950, -3300, 8200, 1800); //roof + spawn.mapRect(-250, -200, 1000, 300); // shelf + spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof + spawn.mapRect(705, -210, 25, 50); + spawn.mapRect(725, -220, 25, 50); + spawn.bodyRect(750, -125, 125, 125); + spawn.bodyRect(875, -50, 50, 50); + + spawn.mapRect(5400, -1700, 400, 1150); //right wall + spawn.mapRect(5400, -300, 400, 400); //right wall + spawn.mapRect(5700, -3300, 1800, 5100); //right wall + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump + spawn.mapRect(5403, -650, 400, 450); //blocking exit + if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run + for (let i = 0; i < 250; i++) spawn.starter(1000 + 4000 * Math.random(), -1500 * Math.random()) + } else { + spawn.finalBoss(3000, -750) + } + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + + level.setPosToSpawn(0, -250); + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + slime.query(); + slime.levelRise(0.1) + ctx.fillStyle = "rgba(0,255,255,0.1)" + ctx.fillRect(-5385 - 300, -550, 300, 250) + }; + } + if (mobs.mobDeaths < level.levelsCleared && localSettings.loreCount > 5 && !simulation.isCheating) { + //open door for pacifist run on final lore chapter + if (simulation.isHorizontalFlipped) { + level.exit.x = -5500 - 100; + } else { + level.exit.x = 5500; + } + level.exit.y = -330; + Matter.Composite.remove(engine.world, map[map.length - 1]); + map.splice(map.length - 1, 1); + simulation.draw.setPaths(); //redraw map draw path + level.levels.push("null") + } + }, + gauntlet() { + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,255,255,0.1)" + ctx.fillRect(6400, -550, 300, 350) + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-175, -975, 900, 575) + }; + level.setPosToSpawn(0, -475); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 6500; + level.exit.y = -230; + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#ddd"; + + // spawn.mapRect(-300, -1050, 300, 200); + // Matter.Body.setAngle(map[map.length - 1], -Math.PI / 4) + + + spawn.mapRect(-950, 0, 8200, 800); //ground + spawn.mapRect(-950, -1200, 800, 1400); //left wall + spawn.mapRect(-950, -1800, 8200, 800); //roof + spawn.mapRect(175, -700, 575, 950); + spawn.mapRect(-250, -425, 600, 650); + spawn.mapRect(-250, -1200, 1000, 250); // shelf roof + powerUps.spawnStartingPowerUps(600, -800); + spawn.blockDoor(710, -710); + spawn.mapRect(2500, -1200, 200, 750); //right wall + spawn.blockDoor(2585, -210) + spawn.mapRect(2500, -200, 200, 300); //right wall + + spawn.mapRect(4500, -1200, 200, 750); //right wall + spawn.blockDoor(4585, -210) + spawn.mapRect(4500, -200, 200, 300); //right wall + + spawn.mapRect(6400, -1200, 400, 750); //right wall + spawn.mapRect(6400, -200, 400, 300); //right wall + spawn.mapRect(6700, -1800, 800, 2600); //right wall + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump + + + if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run + // spawn.setSpawnList(); + spawn.pickList.splice(0, 1); + spawn.pickList.push('starter'); + spawn.pickList.splice(0, 1); + spawn.pickList.push('starter'); + spawn.starter(1500, -200, 150 + Math.random() * 30); + spawn.nodeGroup(3500, -200, 'starter'); + spawn.lineGroup(5000, -200, 'starter'); + for (let i = 0; i < 3; ++i) { + if (simulation.difficulty * Math.random() > 15 * i) spawn.nodeGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); + if (simulation.difficulty * Math.random() > 10 * i) spawn.lineGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); + if (simulation.difficulty * Math.random() > 7 * i) spawn.nodeGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); + } + } else { + spawn[spawn.pickList[0]](1500, -200, 150 + Math.random() * 30); + spawn.nodeGroup(3500, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]); + spawn.lineGroup(5000, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]); + for (let i = 0; i < 3; ++i) { + if (simulation.difficulty * Math.random() > 15 * i) spawn.randomGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); + if (simulation.difficulty * Math.random() > 10 * i) spawn.randomGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); + if (simulation.difficulty * Math.random() > 7 * i) spawn.randomGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); + } + } + if (simulation.difficulty > 1) { + spawn.randomLevelBoss(5750, -600); + spawn.secondaryBossChance(4125, -350) + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + level.setPosToSpawn(0, -475); + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,255,255,0.1)" + ctx.fillRect(-6400 - 300, -550, 300, 350) + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(175 - 900, -975, 900, 575) + }; + } + }, + subway() { + // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode + // level.difficultyIncrease(10 * 4); + // m.maxHealth = m.health = 100 + // color.map = "#333" //custom map color + document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000" + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom) + level.setPosToSpawn(450 * (Math.random() < 0.5 ? 1 : -1), -300); //normal spawn + // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //entrance bump disabled for performance + level.exit.x = 0; + level.exit.y = -9000; + // spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump disabled for performance + + const stationWidth = 9000 + let stationNumber = 0; + let stationCustom = () => { } + let stationCustomTopLayer = () => { } + const train = [] + train.push(level.transport(1475, -200, 500, 25, 30)) + train[train.length - 1].isMoving = false + train[train.length - 1].stops = { left: 1725, right: 7225 } + train.push(level.transport(-1475 - 500, -200, 500, 25, -30)) + train[train.length - 1].isMoving = false + train[train.length - 1].stops = { left: -7225, right: -1725 } + + const stationList = [] //use to randomize station order + for (let i = 1, totalNumberOfStations = 8; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station + shuffle(stationList); + stationList.splice(0, 3); //remove some stations to keep it to 4 stations + stationList.unshift(0) //add index zero to the front of the array + + let isExitOpen = false + let gatesOpenRight = -1 + let gatesOpenLeft = -1 + const infrastructure = (x, isInProgress = true) => { + if (isInProgress) { + spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns + function removeAll(array) { + for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); + } + removeAll(map); + map = []; + //remove any powerUp that is too far from player + for (let i = 0; i < powerUp.length; ++i) { + if (Vector.magnitudeSquared(Vector.sub(player.position, powerUp[i].position)) > 9000000) { //remove any powerUp farther then 3000 pixels from player + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i--, 1) + } + } + //remove any mob that is too far from player + for (let i = 0; i < mob.length; ++i) { + if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000) { //remove any mob farther then 2000 pixels from player + mob[i].removeConsBB() + mob[i].removeCons() + mob[i].leaveBody = false + mob[i].alive = false + Matter.Composite.remove(engine.world, mob[i]); + mob.splice(i--, 1) + } + } + } + const checkGate = (gate, gateButton) => { + if (gate) { //check status of buttons and gates + gate.isClosing = gateButton.isUp + gate.openClose(true); + if (gateButton.isUp) { + gateButton.query(); + if (!gateButton.isUp) { + if (stationNumber > 0) { + if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects + gatesOpenRight = stationNumber + } else if (stationNumber < 0) { + if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects + gatesOpenLeft = stationNumber + } else { //starting station both doors open + gatesOpenLeft = stationNumber + gatesOpenRight = stationNumber + } + if (Math.abs(stationNumber) > 0 && ((Math.abs(stationNumber) + 1) % stationList.length) === 0) { + simulation.makeTextLog(`exit opened`); + isExitOpen = true; + } + } + } + gateButton.draw(); + } + } + const stations = [ //update totalNumberOfStations as you add more stations + () => { //empty starting station + if (isExitOpen) { + level.exit.x = x - 50; + level.exit.y = -260; + } else { + var gateButton = level.button(x - 62, -237, 125, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber === 0 && gatesOpenRight === -1 && gatesOpenLeft === -1) { + var gateR = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + var gateL = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + for (let i = 0; i < 10; ++i) powerUps.chooseRandomPowerUp(x + 800 * (Math.random() - 0.5), -300 - 100 * Math.random())//only spawn heal or ammo once at the first station + } + } + + spawn.mapRect(x + -1400, -750, 3375, 100); //roof + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + // spawn.mapRect(x + -550, -220, 1125, 100); //floor + // spawn.mapRect(x + -475, -230, 975, 150);//floor + spawn.mapVertex(x + 0, -200, "400 0 -400 0 -300 -80 300 -80"); //hexagon but wide + + // spawn.mapRect(x + -1350, -550, 50, 150); + // spawn.mapRect(x + 1300, -550, 50, 150); + stationCustom = () => { }; + stationCustomTopLayer = () => { + checkGate(gateR, gateButton) + checkGate(gateL, gateButton) + }; + }, + () => { //portal maze + const buttonsCoords = [{ x: x + 50, y: -1595 }, { x: x + 637, y: -2195 }, { x: x - 1487, y: -2145 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -1775, -1600, 3400, 1100); //center pillar + spawn.mapRect(x + -4100, -3325, 8000, 700); //roof + spawn.mapRect(x + -4100, -3325, 325, 1500); + spawn.mapRect(x + 3500, -3325, 400, 1500); + spawn.mapRect(x + -225, -575, 450, 425); //lower portal blocks + + //upper parts + spawn.mapRect(x + -1425, -2400, 1900, 50); + spawn.mapRect(x + -775, -2750, 575, 1045); + spawn.mapRect(x + 475, -1900, 450, 375); + spawn.mapRect(x + 2225, -2300, 125, 350); + spawn.mapRect(x + 2550, -2350, 700, 50); + spawn.mapRect(x + 1375, -2850, 125, 650); + spawn.mapRect(x + 600, -2200, 200, 195); + spawn.mapRect(x + -3500, -2275, 825, 75); + spawn.mapRect(x + -1550, -2150, 250, 250); + spawn.mapRect(x + -2575, -2450, 275, 345); + + if (!isExitOpen) { + if (Math.random() < 0.5) { + spawn.randomMob(x + 2850, -2425, 0); + spawn.randomMob(x + 2275, -2425, 0); + spawn.randomMob(x + 2000, -2150, 0); + spawn.randomMob(x + 1650, -2150, 0); + spawn.randomMob(x + 1000, -2475, 0); + spawn.randomMob(x + 725, -2450, 0); + spawn.randomMob(x + 525, -2175, 0); + spawn.randomMob(x + 200, -1950, 0); + spawn.randomMob(x + -25, -1825, 0); + spawn.randomMob(x + -975, -2000, 0); + spawn.randomMob(x + -1500, -2225, 0); + spawn.randomMob(x + 1850, -2125, 0); + spawn.randomMob(x + 225, -1975, 0); + spawn.randomMob(x + 25, -1950, 0); + spawn.randomMob(x + 25, -1950, 0); + } else { + spawn.randomMob(x + 250, -1850, 0); + spawn.randomMob(x + 225, -1950, 0); + spawn.randomMob(x + 125, -2000, 0); + spawn.randomMob(x + 0, -1800, 0); + spawn.randomMob(x + -1725, -2300, 0); + spawn.randomMob(x + -2025, -2175, 0); + spawn.randomMob(x + -2050, -2250, 0); + spawn.randomMob(x + -2000, -2350, 0); + spawn.randomMob(x + -2950, -2400, 0); + spawn.randomMob(x + -2775, -2400, 0); + spawn.randomMob(x + -2425, -2550, 0); + spawn.randomMob(x + 1950, -2225, 0); + spawn.randomMob(x + -2700, -2100, 0); + spawn.randomMob(x + -1925, -2175, 0); + spawn.randomMob(x + -825, -2050, 0); + } + } + + const portal1 = level.portal({ x: x - 250, y: -310 }, Math.PI, + { x: x + -3750, y: -2100 }, 0) + const portal2 = level.portal({ x: x + 250, y: -310 }, 0, + { x: x + 3475, y: -2100 }, Math.PI) + const portal3 = level.portal({ x: x - 800, y: -2500 }, Math.PI, + { x: x - 175, y: -2500 }, 0) + const portal4 = level.portal({ x: x + 1275, y: -1700 }, Math.PI, + { x: x - 1275, y: -1700 }, 0) + stationCustom = () => { + portal1[2].query() + portal1[3].query() + portal2[2].query() + portal2[3].query() + portal3[2].query() + portal3[3].query() + portal4[2].query() + portal4[3].query() + } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + portal1[0].draw(); + portal1[1].draw(); + portal1[2].draw(); + portal1[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + portal3[0].draw(); + portal3[1].draw(); + portal3[2].draw(); + portal3[3].draw(); + portal4[0].draw(); + portal4[1].draw(); + portal4[2].draw(); + portal4[3].draw(); + } + }, + () => { //opening and closing doors + const buttonsCoords = [{ x: x - 800, y: -2245 }, { x: x + 250, y: -870 }, { x: x + 1075, y: -1720 }, { x: x - 1600, y: -1995 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + if (!isExitOpen) { + if (Math.random() < 0.5) { + spawn.randomMob(x + 1125, -650, 0); + spawn.randomMob(x + 150, -950, 0); + spawn.randomMob(x + 100, -975, 0); + spawn.randomMob(x + 75, -975, 0); + spawn.randomMob(x + 275, -1225, 0); + spawn.randomMob(x + 825, -975, 0); + spawn.randomMob(x + -50, -1625, 0); + spawn.randomMob(x + -950, -1550, 0); + spawn.randomMob(x + -975, -1550, 0); + spawn.randomMob(x + -900, -2500, 0); + spawn.randomMob(x + -975, -2550, 0); + spawn.randomMob(x + 675, -1950, 0); + spawn.randomMob(x + 675, -2550, 0); + spawn.randomMob(x + 1225, -1825, 0); + spawn.randomMob(x + -750, -2450, 0); + spawn.randomMob(x + -700, -825, 0); + } else { + spawn.randomMob(x + -675, -675, 0); + spawn.randomMob(x + -575, -925, 0); + spawn.randomMob(x + -425, -1100, 0); + spawn.randomMob(x + -225, -1225, 0); + spawn.randomMob(x + -650, -1250, 0); + spawn.randomMob(x + -675, -775, 0); + spawn.randomMob(x + 75, -1000, 0); + spawn.randomMob(x + -1100, -1575, 0); + spawn.randomMob(x + -1250, -1850, 0); + spawn.randomMob(x + -1625, -2100, 0); + spawn.randomMob(x + -700, -2500, 0); + spawn.randomMob(x + -375, -2550, 0); + spawn.randomMob(x + 250, -2025, 0); + spawn.randomMob(x + 675, -2175, 0); + spawn.randomMob(x + -1000, -2000, 0); + spawn.randomMob(x + -1550, -2325, 0); + spawn.randomMob(x + -1725, -2425, 0); + } + } + + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -2550, -3200, 425, 1375);//roof left wall + spawn.mapRect(x + 2125, -3175, 450, 1375);//roof right wall + spawn.mapRect(x + -2550, -3200, 5125, 225);//roof + + spawn.mapRect(x + -1325, -550, 1375, 50);//first floor roof/ground + spawn.mapRect(x + 775, -550, 675, 50); + spawn.mapRect(x + -200, -875, 1300, 50); //2nd floor roof/ground + spawn.mapRect(x + -125, -1125, 50, 275); + spawn.mapRect(x + -125, -1150, 800, 50); //3rd floor roof/ground + spawn.mapRect(x + -1450, -1475, 1600, 50); + spawn.mapRect(x + -1325, -1725, 800, 50); //4th floor roof/ground + spawn.mapRect(x + 50, -1725, 1350, 50); + spawn.mapRect(x + -1125, -2250, 700, 50); + + spawn.mapRect(x, -525, 50, 150); //door cap for ground at ground y = -210 + const door1 = level.doorMap(x + 12, -380, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20 + spawn.mapRect(x - 200, -525 - 340, 50, 150); //door cap for ground at ground y = -210 + const door2 = level.doorMap(x - 188, -380 - 340, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20 + spawn.mapRect(x + 100, -525 - 940, 50, 150); //door cap for ground at ground y = -210 + const door3 = level.doorMap(x + 112, -380 - 940, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20 + spawn.mapRect(x + 450, -3050, 50, 775); + const door4 = level.doorMap(x + 462, -2300, 25, 575, 520, 30, false) //x, y, width, height, distance, speed = 20 + + const portal1 = level.portal({ + x: x + 2100, + y: -2100 + }, Math.PI, { //right + x: x + -1275, + y: -650 + }, 2 * Math.PI) //right + + stationCustom = () => { + door1.isClosing = (simulation.cycle % 240) < 120 + door1.openClose(true); + door2.isClosing = (simulation.cycle % 240) > 120 + door2.openClose(true); + door3.isClosing = (simulation.cycle % 240) < 120 + door3.openClose(true); + door4.isClosing = (simulation.cycle % 240) > 120 + door4.openClose(true); + portal1[2].query() + portal1[3].query() + } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + portal1[0].draw(); + portal1[1].draw(); + portal1[2].draw(); + portal1[3].draw(); + } + }, + () => { //slime + const buttonsCoords = [{ x: x - 675, y: -895 }, { x: x - 750, y: -70 }, { x: x + 75, y: -570 },] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + + spawn.mapRect(x + -1575, -2000, 3025, 100); //roof + // spawn.mapRect(x + -1575, -2200, 3025, 300); //roof + // spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -1500, -210, 500, 350); //station floor left + spawn.mapRect(x + 1000, -210, 500, 350); //station floor right + spawn.mapRect(x + 900, -1250, 125, 1250); + spawn.mapRect(x - 1025, -1550, 125, 1625); + spawn.mapRect(x - 50, -1900, 100, 1500); + spawn.mapRect(x + -975, -1250, 200, 25); + spawn.mapRect(x + -950, -625, 150, 25); + spawn.mapRect(x - 925, -400, 250, 175); + spawn.mapRect(x - 725, -900, 225, 300); + spawn.mapRect(x + 325, -225, 325, 75); + spawn.mapRect(x + 400, -950, 275, 25); + spawn.mapRect(x + 775, -575, 200, 25); + spawn.mapRect(x + 0, -1225, 125, 25); + spawn.mapRect(x + 0, -575, 225, 175); + spawn.mapRect(x - 925, -75, 875, 150); + spawn.mapRect(x + 475, -1400, 75, 1250); + + if (!isExitOpen) { + if (Math.random() < 0.5) { + spawn.randomMob(x + -850, -450, 0); + spawn.randomMob(x + -850, -125, 0); + spawn.randomMob(x + -725, -100, 0); + spawn.randomMob(x + 0, -100, 0); + spawn.randomMob(x + 800, -50, 0); + spawn.randomMob(x + 50, -275, 0); + spawn.randomMob(x + -300, -425, 0); + spawn.randomMob(x + -750, -475, 0); + spawn.randomMob(x + -850, -775, 0); + spawn.randomMob(x + -650, -1000, 0); + spawn.randomMob(x + -150, -1325, 0); + spawn.randomMob(x + -825, -1350, 0); + spawn.randomMob(x + -375, -150, 0); + } else { + spawn.randomMob(x + 350, -350, 0); + spawn.randomMob(x + 175, -700, 0); + spawn.randomMob(x + 350, -1175, 0); + spawn.randomMob(x + 200, -1600, 0); + spawn.randomMob(x + 500, -1675, 0); + spawn.randomMob(x + 425, -50, 0); + spawn.randomMob(x + 725, -75, 0); + spawn.randomMob(x + 650, -700, 0); + spawn.randomMob(x + 775, -1150, 0); + spawn.randomMob(x + 500, -1675, 0); + spawn.randomMob(x + -150, -175, 0); + spawn.randomMob(x + -800, -150, 0); + } + } + + const boost1 = level.boost(x - 1185, -225, 1400) + const boost2 = level.boost(x + 1100, -225, 1100) + const hazard1 = level.hazard(x - 900, -1225, 1800, 1225) + let isSlimeRiseUp = false + const drip = [] + drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 100)) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 150)) + drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 70)) + // drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 210)) + // drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 67)) + stationCustom = () => { + for (let i = 0; i < drip.length; i++) drip[i].draw() + // drip1.draw(); + // drip2.draw(); + // drip3.draw(); + } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + + hazard1.query(); + hazard1.level(isSlimeRiseUp, 1.5) + if (!(hazard1.height < hazard1.maxHeight)) { + isSlimeRiseUp = false + } else if (!(hazard1.height > 0)) { + isSlimeRiseUp = true + } + boost1.query(); + boost2.query(); + } + }, + () => { //portal fling + const buttonsCoords = [{ x: x + 775, y: -1695 }, { x: x - 775, y: -800 }, { x: x - 375, y: -2083 },] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + spawn.mapRect(x + -1600, -3450, 300, 1475); //roof + spawn.mapRect(x + -1600, -3450, 3225, 100); + spawn.mapRect(x + 1300, -3450, 325, 1525); + + spawn.mapVertex(x + 400, -180, "-300 0 -300 -100 300 -100 400 0"); + spawn.mapVertex(x - 400, -180, "300 0 300 -100 -300 -100 -400 0"); + spawn.mapRect(x + -1500, -210, 1425, 350); //station floor left + spawn.mapRect(x + 75, -210, 1425, 350); //station floor right + spawn.mapRect(x + 75, -950, 50, 450); + spawn.mapRect(x + 125, -700, 1225, 200); + spawn.mapRect(x + -1325, -1775, 775, 175); + spawn.mapVertex(x + 445, -800, "-200 0 -200 -300 100 -300 185 0"); + spawn.mapVertex(x - 310, -1880, "-185 0 -100 -400 400 -400 400 0"); + spawn.mapVertex(x + -675, -725, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); + spawn.mapRect(x + 625, -1700, 750, 500); + + if (!isExitOpen) { + spawn.randomMob(x + -750, -1925, 0); + spawn.randomMob(x + -425, -2300, 0); + spawn.randomMob(x + -350, -2200, 0); + spawn.randomMob(x + -275, -2175, 0); + spawn.randomMob(x + -375, -2175, 0); + spawn.randomMob(x + 1075, -1850, 0); + spawn.randomMob(x + 925, -1775, 0); + spawn.randomMob(x + 1150, -1800, 0); + spawn.randomMob(x + 1400, -2150, 0); + spawn.randomMob(x + 925, -850, 0); + spawn.randomMob(x + 800, -800, 0); + spawn.randomMob(x + 875, -825, 0); + spawn.randomMob(x + 1050, -900, 0); + spawn.randomMob(x + 19050, -2925, 0); + spawn.randomMob(x + 17150, -3150, 0); + spawn.randomMob(x + 17700, -3300, 0); + } + const portal1 = level.portal({ + x: x + 0, + y: -200 + }, -Math.PI / 2, { //up + x: x + 200, + y: -900 + }, -Math.PI / 2) //up + const portal2 = level.portal({ + x: x + 1275, + y: -800 + }, Math.PI, { //right + x: x + -1275, + y: -1875 + }, 2 * Math.PI) //right + + stationCustom = () => { + portal1[2].query(true) + portal1[3].query(true) + portal2[2].query() + portal2[3].query() + } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + portal1[0].draw(); + portal1[1].draw(); + portal1[2].draw(); + portal1[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + } + }, + () => { //tower levels and squares + const buttonsCoords = [{ x: x - 300, y: -3120 }, { x: x + 600, y: -3020 }, { x: x - 575, y: -1770 }, { x: x - 450, y: -2370 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -1625, -3950, 3225, 350);//roof + spawn.mapRect(x + 1300, -3850, 300, 2150); //roof wall + spawn.mapRect(x + -1625, -3950, 325, 2250); //roof wall + spawn.mapRect(x + -1050, -575, 1000, 75); + spawn.mapRect(x + 175, -575, 975, 75); + spawn.mapRect(x + -1050, -825, 150, 275); + spawn.mapRect(x + -900, -1200, 2275, 75); + spawn.mapRect(x + 125, -1425, 1250, 300); + spawn.mapRect(x + -925, -1775, 2100, 75); + spawn.mapRect(x + -100, -2050, 950, 350); + spawn.mapRect(x + -925, -2100, 100, 400); + spawn.mapRect(x + -700, -2375, 1225, 75); + spawn.mapRect(x + 650, -2375, 575, 75); + spawn.mapRect(x + -25, -2750, 350, 269); + spawn.mapRect(x + -950, -3125, 975, 75); + spawn.mapRect(x + 325, -3025, 900, 75); + spawn.bodyRect(x + -125, -1325, 225, 125, 0.3); + spawn.bodyRect(x + -225, -2100, 300, 50, 0.3); + spawn.bodyRect(x + -225, -2575, 100, 200, 0.3); + spawn.bodyRect(x + 850, -2575, 150, 200, 0.3); + spawn.bodyRect(x + 850, -1875, 75, 100, 0.3); + spawn.bodyRect(x + 500, -725, 175, 150, 0.3); + spawn.bodyRect(x + -925, -2250, 100, 150, 0.3); + spawn.bodyRect(x + -1050, -950, 150, 125, 0.3); + + const mobPlacement = [ + () => { //1st floor + spawn.randomMob(x + -775, -725, 0); + spawn.randomMob(x + -575, -700, 0); + spawn.randomMob(x + -275, -700, 0); + spawn.randomMob(x + -125, -650, 0); + spawn.randomMob(x + 250, -675, 0); + spawn.randomMob(x + 425, -650, 0); + spawn.randomMob(x + 775, -650, 0); + spawn.randomMob(x + 1050, -675, 0); + spawn.randomMob(x + 675, -950, 0); + spawn.randomMob(x + -625, -900, 0); + spawn.randomMob(x + -750, -1400, 0); + spawn.randomMob(x + -500, -2025, 0); + spawn.randomMob(x + -125, -3225, 0); + }, + () => { //2nd floor + spawn.randomMob(x + -950, -925, 0); + spawn.randomMob(x + -775, -1325, 0); + spawn.randomMob(x + -450, -1500, 0); + spawn.randomMob(x + -325, -1250, 0); + spawn.randomMob(x + 0, -1500, 0); + spawn.randomMob(x + 375, -1525, 0); + spawn.randomMob(x + 750, -1550, 0); + spawn.randomMob(x + 1175, -1550, 0); + spawn.randomMob(x + -875, -1350, 0); + spawn.randomMob(x + -875, -2375, 0); + spawn.randomMob(x + 175, -2850, 0); + spawn.randomMob(x + 750, -2475, 0); + }, + () => {//3rd floor + spawn.randomMob(x + 1075, -2000, 0); + spawn.randomMob(x + 725, -2125, 0); + spawn.randomMob(x + 350, -2125, 0); + spawn.randomMob(x + -325, -2000, 0); + spawn.randomMob(x + -675, -1875, 0); + spawn.randomMob(x + -725, -2200, 0); + spawn.randomMob(x + -675, -2575, 0); + spawn.randomMob(x + -425, -2675, 0); + spawn.randomMob(x + -50, -2875, 0); + spawn.randomMob(x + 425, -2725, 0); + spawn.randomMob(x + 1150, -2550, 0); + spawn.randomMob(x + 1150, -2175, 0); + spawn.randomMob(x + 1000, -1900, 0); + spawn.randomMob(x + 500, -2550, 0); + spawn.randomMob(x + 125, -2900, 0); + }, + () => {//all floors + spawn.randomMob(x + 1000, -850, 0); + spawn.randomMob(x + 300, -850, 0); + spawn.randomMob(x + -450, -825, 0); + spawn.randomMob(x + -1025, -1125, 0); + spawn.randomMob(x + -750, -1375, 0); + spawn.randomMob(x + -225, -1375, 0); + spawn.randomMob(x + 625, -1525, 0); + spawn.randomMob(x + 1025, -1925, 0); + spawn.randomMob(x + -425, -2100, 0); + spawn.randomMob(x + -400, -2650, 0); + spawn.randomMob(x + 150, -3000, 0); + spawn.randomMob(x + 675, -3200, 0); + spawn.randomMob(x + -550, -3300, 0); + + }, + ] + if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player + stationCustom = () => { } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + } + }, + () => { //jump pads and 6 sided platforms + const buttonsCoords = [{ x: x + 275, y: -1817 }, { x: x + 2025, y: -1995 }, { x: x - 2025, y: -2420 }, { x: x - 2100, y: -1995 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -3200, -3200, 300, 1400); //roof left wall + spawn.mapRect(x + 2600, -3200, 300, 1400);//roof right wall + spawn.mapRect(x + -3175, -3200, 6175, 225);//roof + if (Math.random() < 0.3) spawn.mapRect(x + -1350, -550, 150, 50); //wall ledge + if (Math.random() < 0.3) spawn.mapRect(x + 1175, -550, 200, 50); //wall ledge + spawn.mapVertex(x + 600, -900, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide + spawn.mapVertex(x - 600, -750, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide + spawn.mapVertex(x - 100, -1850, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall + spawn.mapVertex(x + -600, -2000, "-150 -450 150 -450 150 450 0 525 -150 450"); //hexagon but big and tall and flat + spawn.mapVertex(x + 350, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top + spawn.mapVertex(x + 850, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top + spawn.mapVertex(x + -2050, -2350, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //left top corner hexagon but wide + spawn.mapVertex(x + 1700, -2450, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall + + const mobPlacement = [ + () => { //rightish + spawn.randomMob(x + 2250, -2375, 0); + spawn.randomMob(x + 1950, -2825, 0); + spawn.randomMob(x + 1275, -2775, 0); + spawn.randomMob(x + 1450, -2200, 0); + spawn.randomMob(x + 825, -1950, 0); + spawn.randomMob(x + 400, -1875, 0); + spawn.randomMob(x + -75, -2275, 0); + spawn.randomMob(x + -650, -2550, 0); + spawn.randomMob(x + 1500, -2075, 0); + spawn.randomMob(x + 2125, -2650, 0); + spawn.randomMob(x + 2075, -2250, 0); + spawn.randomMob(x + 1000, -2850, 0); + spawn.randomMob(x + 750, -950, 0); + spawn.randomMob(x + -750, -1125, 0); + spawn.randomMob(x + -1575, -2050, 0); + }, + () => { //leftish + spawn.randomMob(x + -2225, -2125, 0); + spawn.randomMob(x + -2675, -2125, 0); + spawn.randomMob(x + -2600, -2600, 0); + spawn.randomMob(x + -2100, -2725, 0); + spawn.randomMob(x + -1425, -2600, 0); + spawn.randomMob(x + -1375, -2050, 0); + spawn.randomMob(x + -575, -2575, 0); + spawn.randomMob(x + -125, -2300, 0); + spawn.randomMob(x + 350, -1925, 0); + spawn.randomMob(x + -350, -1050, 0); + spawn.randomMob(x + -1000, -1000, 0); + spawn.randomMob(x + -700, -1300, 0); + spawn.randomMob(x + 350, -1150, 0); + spawn.randomMob(x + -575, -2525, 0); + spawn.randomMob(x + -1075, -2525, 0); + }, + () => {//centerish + spawn.randomMob(x + 25, -2275, 0); + spawn.randomMob(x + 300, -1975, 0); + spawn.randomMob(x + 700, -1950, 0); + spawn.randomMob(x + 325, -1200, 0); + spawn.randomMob(x + -225, -950, 0); + spawn.randomMob(x + -925, -975, 0); + spawn.randomMob(x + -675, -2575, 0); + spawn.randomMob(x + -1425, -2175, 0); + spawn.randomMob(x + 1575, -2075, 0); + spawn.randomMob(x + 2300, -2075, 0); + spawn.randomMob(x + 425, -1925, 0); + spawn.randomMob(x + 125, -2175, 0); + spawn.randomMob(x + -325, -2150, 0); + spawn.randomMob(x + -350, -950, 0); + spawn.randomMob(x + 600, -325, 0); + spawn.randomMob(x + -600, -375, 0); + }, + ] + if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player + const boost1 = level.boost(x - 50, -225, 790) + const boost2 = level.boost(x + 550, -985, 900) + const boost3 = level.boost(x + -850, -835, 1900) + stationCustom = () => { } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + boost1.query(); + boost2.query(); + boost3.query(); + } + }, + () => { //crouch tunnels + const buttonsCoords = [{ x: x + 625, y: -1395 }, { x: x - 15, y: -1595 }, { x: x - 800, y: -1295 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + spawn.mapRect(x + -1575, -2200, 3025, 300); //roof + spawn.mapRect(x + -1100, -925, 100, 425); + spawn.mapRect(x + -1100, -575, 375, 75); + spawn.mapRect(x + -925, -1300, 375, 125); + spawn.mapRect(x + -300, -1300, 620, 125); + spawn.mapRect(x + 450, -1400, 500, 225); + spawn.mapRect(x + 900, -550, 500, 50); + spawn.mapRect(x + 950, -925, 400, 270); + spawn.mapRect(x + 1250, -1250, 150, 75); + spawn.mapRect(x + -225, -525, 800, 210); + spawn.mapRect(x + -100, -1600, 300, 193); + spawn.mapRect(x + 925, -1250, 75, 75); + spawn.bodyRect(x + 200, -1475, 75, 175, 0.3); + spawn.bodyRect(x + -25, -625, 225, 100, 0.3); + spawn.bodyRect(x + -1000, -750, 125, 175, 0.3); + spawn.bodyRect(x + -625, -1450, 75, 150, 0.3); + spawn.bodyRect(x + -650, -300, 300, 75, 0.3); + if (!isExitOpen) { + spawn.randomMob(x + -750, -1425, 0); + spawn.randomMob(x + -1050, -1100, 0); + spawn.randomMob(x + -825, -750, 0); + spawn.randomMob(x + -500, -400, 0); + spawn.randomMob(x + 450, -650, 0); + spawn.randomMob(x + 0, -725, 0); + spawn.randomMob(x + 300, -1350, 0); + spawn.randomMob(x + 550, -1500, 0); + spawn.randomMob(x + 725, -1650, 0); + spawn.randomMob(x + 900, -1550, 0); + spawn.randomMob(x + 1100, -1300, 0); + spawn.randomMob(x + -1050, -1050, 0); + spawn.randomMob(x + -925, -350, 0); + spawn.randomMob(x + 75, -1750, 0); + spawn.randomMob(x + 1000, -375, 0); + } + stationCustom = () => { } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + ctx.fillStyle = "rgba(0,0,0,0.08)" + ctx.fillRect(x + -225, -325, 800, 125); + ctx.fillRect(x + -100, -1425, 300, 125); + ctx.fillRect(x + 950, -675, 400, 125); + } + }, + ] + // stations[1]() //for testing a specific station + stations[stationList[Math.abs(stationNumber % stationList.length)]]() //*************** run this one when uploading + //add in standard station map infrastructure + spawn.mapRect(x + -8000, 0, 16000, 800);//tunnel floor + spawn.mapRect(x + 1500 - 200, -2000, 6400, 1500); //tunnel roof + spawn.mapRect(x + -1500 - 6400 + 200, -2000, 6400, 1500); //tunnel roof + + // add debris so you can see how fast the train moves + const debrisCount = 4 + const size = 18 + Math.random() * 25; + const wide = 6400 + if (isInProgress) { + //adds new map elements to the level while the level is already running + for (let i = 0; i < map.length; ++i) { + map[i].collisionFilter.category = cat.map; + map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[i], true); //make static + Composite.add(engine.world, map[i]); //add to world + } + simulation.draw.setPaths() + simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight + + + //shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station + let repositionTrain + let playerOnTrain + if (Math.abs(train[0].position.x - m.pos.x) > Math.abs(train[1].position.x - m.pos.x)) { //figure out which train the player is farthest from and move it + repositionTrain = train[0] + playerOnTrain = train[1] + } else { + repositionTrain = train[1] + playerOnTrain = train[0] + } + repositionTrain.isMoving = false + if (repositionTrain.position.x > playerOnTrain.position.x) { //decide if the train is moving left or right + Matter.Body.setPosition(repositionTrain, { x: -1725 + x, y: repositionTrain.position.y }); + repositionTrain.changeDirection(false) //go left + repositionTrain.stops = { right: -1725 + x, left: -7225 + x } + for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); + } else { + Matter.Body.setPosition(repositionTrain, { x: 1725 + x, y: repositionTrain.position.y }); + repositionTrain.changeDirection(true) //go right + repositionTrain.stops = { left: 1725 + x, right: 7225 + x } + for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); + } + } else { + for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); + for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); + } + } + infrastructure(0, false) //if this is run before the level starts, it needs a false + + level.custom = () => { + for (let i = 0; i < train.length; i++) train[i].trainStop() + ctx.fillStyle = "rgba(0,0,0,0.1)"//"#ddd" + ctx.fillRect(m.pos.x - 4000, m.pos.y - 4000, 8000, 8000) + level.exit.drawAndCheck(); + // level.enter.draw(); + + //track what station the player is in + if (m.pos.x > 0.55 * stationWidth + stationNumber * stationWidth) { + stationNumber++ + // if ((stationNumber % stationList.length) == 0) stationNumber++ //skip the stationNumber that is the modulus of the length of the stationList + infrastructure(stationNumber * stationWidth) + } else if (m.pos.x < -0.55 * stationWidth + stationNumber * stationWidth) { + stationNumber-- + // if ((stationNumber % stationList.length) == 0) stationNumber--//skip the stationNumber that is the modulus of the length of the stationList + infrastructure(stationNumber * stationWidth) + } + stationCustom() + }; + level.customTopLayer = () => { + for (let i = 0; i < train.length; i++) train[i].draw() + stationCustomTopLayer() + }; + level.isProcedural = true //only used in generating text for the level builder + simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight + simulation.draw.drawMapPath = simulation.draw.drawMapSight + }, + reservoir() { + level.exit.x = 1700; + level.exit.y = -4510; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); + level.setPosToSpawn(-500, 850); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 2300 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + color.map = "#3d4240" + powerUps.spawnStartingPowerUps(-575, -2925) + //walls + spawn.mapRect(-3500, -5000, 1500, 6500); + spawn.mapRect(2000, -5000, 1500, 6500); + spawn.mapRect(-2500, 1100, 5000, 400); //slime floor + spawn.mapRect(-3500, -5475, 7000, 600); //top + spawn.mapRect(-1925, -4900, 175, 375); //pipe + spawn.mapRect(-1950, -4550, 225, 25); //pipe + //top floor exit + spawn.mapRect(1475, -4900, 50, 250); + spawn.mapRect(1400, -4475, 650, 50); + // ground + spawn.mapVertex(-687, 1060, "700 0 -700 0 -450 -300 450 -300"); //left base + spawn.mapVertex(863, 1060, "700 0 -700 0 -450 -300 450 -300"); //right base + //entrance + spawn.mapRect(-730, 525, 475, 50); + spawn.mapRect(-730, 550, 50, 150); + spawn.mapRect(-305, 550, 50, 500); + spawn.bodyRect(-717, 700, 25, 100); //door + spawn.bodyRect(-717, 800, 25, 100); //door + //1st floor //left + spawn.mapVertex(-1125 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); + spawn.mapRect(-1225, -100, 1070, 100); + if (Math.random() < 0.33) { + spawn.mapVertex(-687, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); + } else if (Math.random() < 0.5) { + spawn.mapVertex(-687, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450"); + } else { + spawn.mapVertex(-687, -700, "-150 0 150 0 150 450 0 525 -150 450"); + } + //right + spawn.mapVertex(425 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); + spawn.mapRect(325, -100, 1070, 100); + spawn.mapRect(175, 675, 425, 25); + spawn.mapRect(1125, 225, 425, 25); + spawn.mapRect(650, 450, 425, 25); + if (Math.random() < 0.33) { + spawn.mapVertex(855, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); + } else if (Math.random() < 0.5) { + spawn.mapVertex(855, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450"); + } else { + spawn.mapVertex(855, -700, "-150 0 150 0 150 450 0 525 -150 450"); + } + //2nd floor + spawn.mapVertex(-687, -1936, "-625 50 0 100 625 50 625 -50 -625 -50"); + spawn.mapVertex(855, -1936, "-625 50 0 100 625 50 625 -50 -625 -50"); + //2nd floor right building + spawn.mapRect(550, -3050, 600, 75); + spawn.bodyRect(-125, -2025, 475, 25); + spawn.mapRect(-925, -2350, 675, 50); + spawn.mapRect(-825, -2825, 425, 50); + spawn.mapRect(-450, -3125, 50, 350); + spawn.mapRect(-750, -3150, 350, 50); + spawn.mapRect(-650, -3400, 250, 300); + spawn.mapRect(-650, -3675, 200, 50); + spawn.bodyRect(-375, -2150, 100, 150, 0.2); + //2nd floor left pillar + spawn.mapRect(-1400, -2625, 325, 25); + spawn.mapRect(-1450, -3225, 425, 25); + spawn.mapRect(-1512.5, -3825, 550, 25); + + spawn.randomMob(1000, -275, 0.2); + spawn.randomMob(950, -1725, 0.1); + spawn.randomMob(-725, -1775, 0.1); + spawn.randomMob(-200, -2075, 0); + spawn.randomMob(-550, -3500, -0.2); + spawn.randomMob(375, -2125, 0); + spawn.randomMob(-700, -2450, -0.1); + spawn.randomMob(-1175, -2775, -0.1); + spawn.randomMob(1025, -3200, -0.2); + spawn.randomMob(-525, -3750, -0.2); + spawn.randomMob(1350, -2075, -0.3); + spawn.randomMob(1775, 1000, -0.4); + spawn.randomSmallMob(-575, -2925); + spawn.randomGroup(-400, -4400, 0); + if (simulation.difficulty > 1) { + spawn.randomLevelBoss(825, -3500); + spawn.secondaryBossChance(75, -1350) + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + const slime = level.hazard(-2000, -5000, 4000, 6060); // hazard(x, y, width, height, damage = 0.003) + slime.height -= slime.maxHeight - 60 //start slime at zero + slime.min.y += slime.maxHeight + slime.max.y = slime.min.y + slime.height + const elevator1 = level.elevator(-1625, -90, 310, 800, -2000, 0.0025, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + const elevator2 = level.elevator(1175, -3050, 200, 250, -4475, 0.0025, { up: 0.12, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + let waterFallWidth = 0 + let waterFallX = 0 + let waterFallSmoothX = 0 + let isWaterfallFilling = false + const riseRate = 0.30 + Math.min(1, simulation.difficulty * 0.005) + const spinnerArray = [] + if (simulation.isHorizontalFlipped) { //flip the map horizontally + spawn.mapVertex(584, -2500, "0 0 300 0 150 600 0 600"); + spawn.mapVertex(1116, -2500, "0 0 300 0 300 600 150 600"); + spawn.bodyRect(-200, -125, 625, 25); + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + elevator1.holdX = -elevator1.holdX // flip the elevator horizontally + elevator2.holdX = -elevator2.holdX // flip the elevator horizontally + spinnerArray.push(level.spinner(-110, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { + const boost1 = level.boost(-900, -2000, 790) + level.setPosToSpawn(500, 850); //normal spawn + level.custom = () => { + ctx.fillStyle = "#c0c3c9" ///!!!!!!!!!! for flipped x: newX = -oldX - width + ctx.fillRect(1468, -1975, 2, 1915) //elevator track + ctx.fillRect(-1274, -4460, 2, 1425) //elevator track + ctx.fillRect(1225, -3825, 25, 1850); //small pillar background + ctx.fillStyle = "#d0d4d6" + ctx.fillRect(275, -1925, 825, 2925) //large pillar background + ctx.fillRect(-1275, -1925, 825, 2925) //large pillar background + ctx.fillStyle = "#cff" //exit + ctx.fillRect(-2000, -4900, 525, 425) + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + boost1.query(); + elevator1.move(); + elevator2.move(); + ctx.fillStyle = "#233" + ctx.beginPath(); //central dot on spinners + ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI); + for (let i = 0, len = spinnerArray.length; i < len; i++) { + ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y) + ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI); + } + ctx.fill(); + //shadow + ctx.fillStyle = "rgba(0,10,30,0.1)" + ctx.fillRect(-1150, -3000, 600, 1025); + ctx.fillRect(450, -3100, 300, 275); + ctx.fillRect(450, -3625, 200, 225); + ctx.fillRect(400, -2775, 425, 450); + ctx.fillRect(250, -2300, 675, 300); + slime.query(); + if (isWaterfallFilling) { + if (slime.height < 5500) { + //draw slime fill + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` + ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) + if (!m.isBodiesAsleep) { + waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() + waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random() + waterFallX = 1857 - waterFallSmoothX + ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height) + //push player down if they go under waterfall + if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 2 + }); + } + } + slime.levelRise(riseRate) + } + } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) { + isWaterfallFilling = true + } + }; + } else { //not flipped + spawn.mapVertex(1116, -2500, "0 0 300 0 150 600 0 600"); + spawn.mapVertex(584, -2500, "0 0 300 0 300 600 150 600"); + if (Math.random() < 0.1) { + spinnerArray.push(level.spinner(65, -300, 40, 450, 0.003, Math.PI / 2)) + } else if (Math.random() < 0.25) { + spinnerArray.push(level.spinner(65, -500, 40, 500, 0.003, 0, 0, -0.015)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { + const r = 250 + const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` + Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon)) + } else { + const W = 410; + const H = 30; + spawn.bodyRect(-120, -75, W, H, 1, spawn.propsIsNotHoldable) + let b = body[body.length - 1]; + cons[cons.length] = Constraint.create({ + pointA: { + x: b.position.x - (W / 2) + 50 - 211, + y: b.position.y - 1825 + }, + bodyB: b, + pointB: { + x: -(W / 2) + 50, + y: 0 + }, + damping: 0.01, + stiffness: 0.002, + length: 1800 + }); + cons[cons.length] = Constraint.create({ + pointA: { + x: b.position.x + (W / 2) - 50 + 211, + y: b.position.y - 1825 + }, + bodyB: b, + pointB: { + x: (W / 2) - 50, + y: 0 + }, + damping: 0.01, + stiffness: 0.002, + length: 1800 + }); + Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]]) + } + + spinnerArray.push(level.spinner(50, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { + if (Math.random() < 0.5) { + const r = 200 + const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` + Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon)) + } + + const boost1 = level.boost(800, -2000, 790) + + level.custom = () => { + ctx.fillStyle = "#c0c3c9" + ctx.fillRect(-1470, -1975, 2, 1915) //elevator track + ctx.fillRect(1276, -4460, 2, 1425) //elevator track + ctx.fillRect(-1250, -3825, 25, 1850); //small pillar background + ctx.fillStyle = "#d0d4d6" + ctx.fillRect(-1100, -1925, 825, 2925) //large pillar background + ctx.fillRect(450, -1925, 825, 2925) //large pillar background + ctx.fillStyle = "#cff" //exit + ctx.fillRect(1475, -4900, 525, 425) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + + level.customTopLayer = () => { + boost1.query(); + elevator1.move(); + elevator2.move(); + + ctx.fillStyle = "#233" + ctx.beginPath(); //central dot on spinners + ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI); + for (let i = 0, len = spinnerArray.length; i < len; i++) { + ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y) + ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI); + } + ctx.fill(); + //shadow + ctx.fillStyle = "rgba(0,10,30,0.1)" + ctx.fillRect(550, -3000, 600, 1025); + ctx.fillRect(-750, -3100, 300, 275); + ctx.fillRect(-650, -3625, 200, 225); + ctx.fillRect(-825, -2775, 425, 450); + ctx.fillRect(-925, -2300, 675, 300); + + slime.query(); + if (isWaterfallFilling) { + if (slime.height < 5500) { + //draw slime fill + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` + ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) + if (!m.isBodiesAsleep) { + waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() + waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random() + waterFallX = waterFallSmoothX - 1985 + ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height) + //push player down if they go under waterfall + if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 2 + }); + } + } + slime.levelRise(riseRate) + } + } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) { + isWaterfallFilling = true + } + }; + } + }, + reactor() { + level.exit.x = 3500; + level.exit.y = -42; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); + level.defaultZoom = 2000 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#c3d6df" //"#d8dadf"; + color.map = "#303639"; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + + spawn.bodyRect(250, -70, 100, 70, 1); + spawn.mapRect(-425, 0, 4500, 2100); + spawn.mapRect(-475, -2825, 4500, 1025); + // spawn.mapRect(1200, -1300, 600, 800); + const a = 400 //side length + const c = 100 //corner offset + spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + //entrance + spawn.mapRect(-2025, -2825, 1250, 4925); + spawn.mapRect(-900, -2825, 1125, 1725); + spawn.mapRect(-900, -750, 1125, 2850); + spawn.mapRect(-325, -1250, 550, 300); + //exit + spawn.mapRect(3800, -2825, 1225, 4925); + spawn.mapRect(2750, -2150, 1325, 1775); + spawn.mapRect(2750, -475, 550, 300); + spawn.mapRect(2750, -7, 1050, 150); //exit room floor + + const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 + const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 + doorIn.collisionFilter.category = cat.map; + doorOut.collisionFilter.category = cat.map; // to prevent boson composite from letting the player skip the level + // doorOut.isClosing = true + let isDoorsLocked = false + let isFightOver = false + let isSpawnedBoss = false + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + level.setPosToSpawn(550, -800); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + const button = level.button(-1500, 0) + button.isUp = true + + level.custom = () => { + if (isDoorsLocked) { + if (player.position.x > 300) { //if player gets trapped inside starting room open up again + isDoorsLocked = false + doorIn.isClosing = false + } + } + doorIn.openClose(); + doorOut.openClose(); + ctx.fillStyle = "#d5ebef" + ctx.fillRect(-3800, -375, 1050, 375) + level.enter.draw(); + level.exit.drawAndCheck(); + button.draw(); + if (button.isUp) { + button.query(); + } else if (!isSpawnedBoss) { + if (player.position.x < 0) { + if (!doorOut.isClosed() || !doorIn.isClosed()) { + doorIn.isClosing = true + doorOut.isClosing = true + //block caught in a door + if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) { + button.isUp = true + doorIn.isClosing = false + doorOut.isClosing = false + } + } else { + isSpawnedBoss = true + isDoorsLocked = true + for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo") + for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal"); + const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54 + if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { + for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random()) + } else { + if (Math.random() < 0.07 && simulation.difficulty > 35) { + for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15 + for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); + for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) + for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); + } else { + if (Math.random() < 0.25) { + for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.33) { + for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.5) { + for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 + } else { + for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 + } + } + } + // spawn.secondaryBossChance(-2300, -800) + } + } else { + doorIn.isClosing = false + } + } else if (!isFightOver && !(simulation.cycle % 180)) { + let isFoundBoss = false + for (let i = 0; i < mob.length; i++) { + if (mob[i].isReactorBoss) { + isFoundBoss = true + break + } + } + if (!isFoundBoss) { + isFightOver = true + doorIn.isClosing = false + doorOut.isClosing = false + // powerUps.spawnBossPowerUp(-3600, -100) + powerUps.spawn(-3650, -50, "tech") + powerUps.spawn(-3650, -150, "tech") + powerUps.spawn(-3650, -300, "tech") + // if (player.position.x < 2760 && player.position.x > 210) {} + } + } + }; + + level.customTopLayer = () => { + doorIn.draw(); + doorOut.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-225, -1100, 1000, 350); + }; + } else { + level.setPosToSpawn(-550, -800); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + const button = level.button(1400, 0) + button.isUp = true + + level.custom = () => { + if (isDoorsLocked) { + if (player.position.x < -300) { //if player gets trapped inside starting room open up again + isDoorsLocked = false + doorIn.isClosing = false + } + } + doorIn.openClose(); + doorOut.openClose(); + ctx.fillStyle = "#d5ebef" + ctx.fillRect(2750, -375, 1050, 375) + level.enter.draw(); + level.exit.drawAndCheck(); + button.draw(); + if (button.isUp) { + button.query(); + } else if (!isSpawnedBoss) { + if (player.position.x > 0) { + if (!doorOut.isClosed() || !doorIn.isClosed()) { + doorIn.isClosing = true + doorOut.isClosing = true + //block caught in a door + if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) { + button.isUp = true + doorIn.isClosing = false + doorOut.isClosing = false + } + } else { + isSpawnedBoss = true + isDoorsLocked = true + for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo") + for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal"); + const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54 + if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { + for (let i = 0; i < 250; i++) spawn.starter(300 + 2400 * Math.random(), -1300 - 500 * Math.random()) + } else { + if (Math.random() < 0.07 && simulation.difficulty > 35) { + for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15 + for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); + for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) + for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); + } else { + if (Math.random() < 0.25) { + for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.33) { + for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.5) { + for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 + } else { + for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 + } + } + } + // spawn.secondaryBossChance(2200, -800) + } + } else { + doorIn.isClosing = false + } + } else if (!isFightOver && !(simulation.cycle % 180)) { + let isFoundBoss = false + for (let i = 0; i < mob.length; i++) { + if (mob[i].isBoss) { + isFoundBoss = true + break + } + } + if (!isFoundBoss) { + isFightOver = true + doorIn.isClosing = false + doorOut.isClosing = false + // powerUps.spawnBossPowerUp(3600, -100) + powerUps.spawn(3650, -50, "tech") + powerUps.spawn(3650, -150, "tech") + powerUps.spawn(3650, -300, "tech") + // if (player.position.x < 2760 && player.position.x > 210) {} + } + } + }; + + level.customTopLayer = () => { + doorIn.draw(); + doorOut.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-775, -1100, 1000, 350); + }; + } + + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + factory() { + // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why + + level.setPosToSpawn(2235, -1375); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + level.exit.x = 7875; + level.exit.y = -2480; + + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d0d2d4s"; + // color.map = "#262a2f" + let isPowerLeft = true + const movers = [] + //left side + movers.push(level.mover(125, -140, 925, 35, -5)) + movers.push(level.mover(1100, -437, 1100, 35, -5)) + movers.push(level.mover(2000, -600, 850, 35, -5)) + //right side + const moveSpeedStopGo = 8 + movers.push(level.mover(2700, -200, 3600, 35, 0)) + movers.push(level.mover(7175, -215, 2275, 50, 3)) + movers.push(level.mover(6475, -215, 275, 100, -3)) + movers.push(level.mover(6725, -500, 500, 375, 3)) + movers.push(level.mover(7675, -725, 500, 410, 0)) + movers.push(level.mover(6775, -1075, 375, 50, 0)) + movers.push(level.mover(5525, -1075, 450, 50, 0)) + movers.push(level.mover(6775, -2100, 375, 50, 0)) + movers.push(level.mover(5450, -1900, 525, 50, 0)) + + function setMoverDirection(VxGoal) { + for (let i = 7; i < movers.length; i++) movers[i].VxGoal = VxGoal + } + setMoverDirection(0) + + const buttonRight = level.button(7735, -1825) + buttonRight.isUp = true + const buttonLeft = level.button(5275, -1900) + + const lasers = [] + const laserX = 3390 //3882 - 1130 / 2 + const laserGap = 1295 //1130 + lasers.push(level.hazard(laserX, -500, 6, 300, 0.4)) + lasers.push(level.hazard(laserX + laserGap, -500, 6, 300, 0.4)) + lasers.push(level.hazard(laserX + laserGap * 2, -500, 6, 300, 0.4)) + for (let i = 0; i < lasers.length; i++) { + lasers[i].isOn = false; + spawn.mapRect(lasers[i].min.x - 55, -550, 110, 50); + spawn.mapRect(lasers[i].min.x - 10, -500, 25, 20); + } + const button1 = level.button(2235, -200) + button1.isUp = true + let bonusAmmoCount = 0 + level.custom = () => { + if (isPowerLeft) { + if (!(simulation.cycle % 90)) spawn.bodyRect(2730, -1600, 50, 50); + } else { + // for (let i = 0; i < trains.length; i++) { + // //oscillate back and forth + // if (trains[i].position.x < 5275) { + // trains[i].changeDirection(true) //go right + // } else if (trains[i].position.x > 7875) { + // trains[i].changeDirection(false) //go left + // } + // trains[i].move(); + // } + + const rate = 160 //multiples of 32! + if ((simulation.cycle % rate) === 80) { + for (let i = 0; i < lasers.length; i++) lasers[i].isOn = false; + movers[3].VxGoal = moveSpeedStopGo; + movers[3].force = 0.0005 + movers[2].VxGoal = moveSpeedStopGo; + movers[2].force = 0.0005 + } else if ((simulation.cycle % rate) === 0) { + movers[3].VxGoal = 0; + movers[3].force = 0 + movers[2].VxGoal = 0; + movers[2].force = 0 + spawn.bodyRect(2730, -1600, 50, 50); + if ((simulation.cycle % (rate * 3)) === 0) { + if (bonusAmmoCount < 3 && Math.random() < 0.5) { //some extra ammo because of all the extra mobs that don't drop ammo + bonusAmmoCount++ + powerUps.spawn(2760, -1550, Math.random() < 0.5 ? "heal" : "ammo", false); + } + + for (let i = 0; i < lasers.length; i++) lasers[i].isOn = true; + const block2Mob = (laserIndex) => { //convert block into mob + const laserHit = Matter.Query.ray(body, lasers[laserIndex].min, lasers[laserIndex].max) //check for collisions with 3rd laser + if (laserHit.length) { + for (let i = 0; i < body.length; i++) { + if (laserHit[0].body.id === body[i].id) { //need to find the block id so it can be removed + const list = ["flutter", "flutter", "flutter", "hopper", "slasher", "slasher", "slasher", "stabber", "springer", "striker", "sneaker", "launcher", "launcherOne", "exploder", "sucker", "spinner", "grower", "beamer", "spawner", "ghoster"] + const pick = list[Math.floor(Math.random() * list.length)] + spawn[pick](lasers[laserIndex].max.x, lasers[laserIndex].max.y - 20); + const who = mob[mob.length - 1] + Matter.Body.setVelocity(who, { x: (8 + 5 * Math.random()), y: -(14 + 10 * Math.random()) }); + who.locatePlayer() + who.leaveBody = false; + who.isDropPowerUp = false + //remove block + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + break + } + } + } + } + if (mob.length < 100 && !m.isBodiesAsleep) { + block2Mob(0) + block2Mob(1) + block2Mob(2) + } + } + } + } + if (buttonLeft.isUp) { + buttonLeft.query(); + if (!buttonLeft.isUp) { + setMoverDirection(7) + buttonRight.isUp = true //flip the other button up + } + } else if (buttonRight.isUp) { + buttonRight.query(); + if (!buttonRight.isUp) { + setMoverDirection(-7) + //check for blocks and remove them + const list = Matter.Query.region(body, buttonLeft) //are any blocks colliding with this + buttonLeft.isUp = true //flip the other button up + if (list.length > 0) { + list[0].isRemoveMeNow = true + for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array + if (body[i].isRemoveMeNow) { + Matter.Composite.remove(engine.world, list[0]); + body.splice(i, 1); + break + } + } + } + } + } + + if (button1.isUp) { + button1.query(); + if (!button1.isUp) { + isPowerLeft = false + for (let i = 0; i < 3; i++) { + movers[i].VxGoal = 0; + movers[i].force = movers[i].VxGoal > 0 ? 0.0005 : -0.0005 + } + powerUps.spawnStartingPowerUps(2760, -1550); + spawn.randomMob(2700, -350, 0.2); + spawn.randomMob(6975, -650, 0.2); + spawn.randomMob(6550, -325, 0.3); + spawn.randomMob(7350, -350, 0.3); + spawn.randomMob(7925, -975, 0.5); + spawn.randomMob(7950, -1725, 0.5); + spawn.randomMob(7000, -1375, 0.3); + spawn.randomMob(5700, -1350, 0.5); + spawn.randomMob(5250, -1575, 0.5); + spawn.randomMob(6325, -75, 0.3); + spawn.randomMob(7900, -1925, 0.1); + spawn.randomMob(5300, -1975, 0.3); + spawn.randomMob(7875, -1900, 0.3); + spawn.randomMob(5325, -1975, 0.4); + + spawn.randomGroup(3900, -725, 0.4); + if (simulation.difficulty > 1) spawn.randomLevelBoss(6501, -1771); + spawn.secondaryBossChance(6063, -661) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + } + } + buttonRight.draw(); + buttonLeft.draw(); + button1.draw(); + for (let i = 0; i < movers.length; i++) movers[i].push(); + level.exit.drawAndCheck(); + level.enter.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(6937, -2075, 50, 1775); //6937, -1050, 50, 675); + ctx.fillStyle = "rgba(0,255,255,0.15)" // ctx.fillStyle = "#f2f2f2" + ctx.fillRect(7675, -2875, 500, 425); //exit room + }; + level.customTopLayer = () => { + if (isPowerLeft) { + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(2400, -1650, 7050, 2750) //right side + ctx.fillRect(4950, -3075, 3225, 1425); + ctx.beginPath() + ctx.moveTo(2407, -576); + ctx.lineTo(2000, -573) + ctx.lineTo(1950, -439) + ctx.lineTo(1100, -432) + ctx.lineTo(1020, -143) + ctx.lineTo(125, -137) + ctx.lineTo(-109, 300) + ctx.lineTo(-125, 1089) + ctx.lineTo(2372, 1081) + ctx.lineTo(2452, 65) + ctx.fill(); + } else { + // for (let i = 0; i < trains.length; i++) trains[i].draw() + ctx.beginPath() + ctx.moveTo(2526, -589); + ctx.lineTo(2531, -597) + ctx.lineTo(2506, -594) + ctx.lineTo(2850, -600) + ctx.lineTo(2890, -193) + ctx.lineTo(6300, -200) + ctx.lineTo(6618, 857) + ctx.lineTo(6622, 1100) + ctx.lineTo(2521, 1100) + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fill(); + ctx.fillRect(-100, -1650, 2625, 2750) //left side + for (let i = 0; i < lasers.length; i++) lasers[i].opticalQuery() + } + ctx.fillStyle = "rgba(0,0,0,0.07)" + ctx.fillRect(7675, -2200, 1775, 2025); + ctx.fillRect(4950, -2075, 500, 1000); + ctx.fillRect(2050, -1650, 350, 325) //entrance room + for (let i = 0; i < movers.length; i++) movers[i].draw(); + }; + spawn.mapRect(-1550, -3050, 1450, 4150); //left wall + spawn.mapRect(-1550, -3050, 6525, 1400); //ceiling + spawn.mapRect(-1550, -3050, 6525, 1400); + spawn.mapRect(3000, -1700, 1975, 675); //ceiling center + + spawn.mapRect(3800, -4000, 5650, 950); + spawn.mapRect(3800, -4000, 1175, 2975); + spawn.mapRect(8175, -4000, 1275, 3685); //right wall + spawn.mapRect(8175, -200, 1275, 1300); //right wall + + spawn.mapRect(75, 0, 6275, 1100); //ground + spawn.mapRect(6475, -200, 2750, 1300); + spawn.mapRect(4975, -1087, 550, 62); + spawn.mapRect(4975, -1100, 500, 75); + + spawn.mapRect(7875, -1100, 175, 25); //right 3 hop stairs + spawn.mapRect(8075, -1450, 200, 25); + spawn.mapRect(7675, -1825, 375, 25); + spawn.mapRect(7675, -1800, 250, 725); + + spawn.mapRect(5125, -1275, 200, 25); //left 3 hop stairs + spawn.mapRect(4900, -1575, 175, 25); + spawn.mapRect(5125, -1900, 325, 25); + spawn.mapRect(5225, -1875, 225, 625); + spawn.mapRect(4950, -3075, 500, 1000); + + //exit + spawn.mapRect(7675, -2450, 525, 250); + spawn.mapRect(7675, -3050, 550, 175); + spawn.mapRect(7675, -2925, 50, 175); + + spawn.mapRect(1925, -1325, 550, 50); //entrance + spawn.mapRect(2050, -1675, 50, 175); //entrance + spawn.mapRect(1700, -200, 750, 275); //button shelf + if (Math.random() < 0.5) { //left side + spawn.mapRect(625, -1100, 425, 300); + spawn.mapRect(1375, -1100, 425, 300); + spawn.mapRect(1750, -835, 100, 35); + spawn.mapRect(-200, -525, 150, 35); + } else { + spawn.mapRect(800, -1125, 925, 400); + spawn.mapRect(75, -775, 400, 50); + spawn.mapRect(1700, -760, 75, 35); + spawn.mapRect(-200, -425, 150, 35); + } + spawn.mapRect(2400, -600, 125, 675); + spawn.mapRect(2400, -1750, 125, 1050); + spawn.mapRect(2700, -1700, 125, 85); + + spawn.randomMob(350, -325, 0.5); + spawn.randomMob(875, -375, 0.5); + spawn.randomMob(1250, -575, 0.5); + spawn.randomMob(1550, -600, 0.5); + spawn.randomSmallMob(1250, -175); + spawn.randomSmallMob(1500, -229); + spawn.randomSmallMob(1850, -300); + powerUps.spawn(5200, -1300, "ammo"); + }, + labs() { + level.isProcedural = true //used in generating text for the level builder + level.defaultZoom = 1700 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d9d9de" //"#d3d3db" //"#dcdcdf"; + let isDoorLeft, isDoorRight, x, y + doCustom = [] + doCustomTopLayer = [] + offset = { x: 0, y: 0 } + const mobSpawnChance = 0 // Math.random() < chance + 0.07 * simulation.difficulty + enterOptions = [ + (x = offset.x, y = offset.y) => { //lasers + level.setPosToSpawn(x + 1750, y - 800); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(x + 1450, y - 1350, 50, 450); //entrance left wall + spawn.bodyRect(x + 1460, y - 900, 30, 150); //entrance door + spawn.mapRect(x + 1600, y - 350, 500, 100); //toggle shelf + const toggle = level.toggle(x + 1650, y - 350, true) //(x,y,isOn,isLockOn = true/false) + let hazard1 + if (Math.random() > 0.5) { + spawn.mapRect(x + 550, y - 750, 1500, 50); //entrance shelf + hazard1 = level.hazard(x + 850, y - 920, 600, 10, 0.4) //laser + spawn.mapRect(x + 860, y - 925, 10, 20); //laser nose + spawn.mapRect(x + 660, y - 975, 200, 120); //laser body + } else { + spawn.mapRect(x + 1350, y - 750, 700, 50); //entrance shelf + hazard1 = level.hazard(x + 1040, y - 660, 1000, 10, 0.4) //laser + spawn.mapRect(x + 1050, y - 665, 10, 20); //laser nose + spawn.mapRect(x + 650, y - 705, 400, 100); //laser body + } + const hazard2 = level.hazard(x, y - 330, 450, 10, 0.4) //laser + spawn.mapRect(x + 440, y - 335, 10, 20); //laser nose + spawn.mapRect(x + 450, y - 375, 400, 100); //laser body + //exit hazards + const Xoffset = Math.floor(400 * Math.random()) + const hazard3 = level.hazard(x + Xoffset, y - 1300, 10, 1300, 0.4) //laser + spawn.mapRect(x + Xoffset - 5, y - 1310, 20, 20); //laser nose + const Xoffset2 = 1650 + Math.floor(300 * Math.random()) + const hazard4 = level.hazard(x + Xoffset2, y - 240, 10, 250, 0.4) //laser + spawn.mapRect(x + Xoffset2 - 5, y - 250, 20, 20); //laser nose + spawn.randomMob(x + 150, y + -1100, mobSpawnChance); + spawn.randomMob(x + 175, y + -775, mobSpawnChance); + spawn.randomMob(x + 150, y + -350, mobSpawnChance); + spawn.randomMob(x + 150, y + -75, mobSpawnChance); + spawn.randomMob(x + 650, y + -125, mobSpawnChance); + spawn.randomMob(x + 1200, y + -75, mobSpawnChance); + // let isSpawnedMobs = false + doCustomTopLayer.push( + () => { + toggle.query(); + hazard1.isOn = toggle.isOn + hazard2.isOn = toggle.isOn + hazard3.isOn = toggle.isOn + hazard4.isOn = toggle.isOn + if ((simulation.cycle % 120) > 60) { + hazard1.opticalQuery(); + hazard2.opticalQuery(); + } else { + hazard3.opticalQuery(); + hazard4.opticalQuery(); + } + // if (!isSpawnedMobs && !toggle.isOn) { + // isSpawnedMobs = true + // spawn.randomMob(x + 150, y + -1100, mobSpawnChance); + // spawn.randomMob(x + 175, y + -775, mobSpawnChance); + // spawn.randomMob(x + 150, y + -350, mobSpawnChance); + // spawn.randomMob(x + 150, y + -75, mobSpawnChance); + // spawn.randomMob(x + 650, y + -125, mobSpawnChance); + // spawn.randomMob(x + 1200, y + -75, mobSpawnChance); + // } + } + ) + }, + ] + exitOptions = [ + (x = offset.x, y = offset.y) => { + level.exit.x = x + 1725; + level.exit.y = y - 980; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(x + 1500, y - 950, 500, 25); //exit platform + spawn.mapRect(x + 1550, y - 1300, 25, 175); //exit side wall + spawn.mapVertex(x + 1300, y - 125, "-400 0 -250 -400 250 -400 400 0"); + + spawn.bodyRect(x + 1075, y - 475, 125, 125, 0.25); + spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25); + spawn.bodyRect(x + 200, y - 150, 100, 150, 0.25); + spawn.bodyRect(x + 1075, y - 1075, 100, 125, 0.25); + const density = 0.0015 //+ (simulation.difficultyMode < 5 ? 0.0035 : 0) + const angle = Math.PI / 2 + const variance = 0 //Math.PI + const frictionAir = 0.03 + const angularVelocity = 0 //0.01 + const spinVariance = 0 //0.02 + balance1 = level.spinner(x + 200, y - 500, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { + balance2 = level.spinner(x + 200, y - 950, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) + balance3 = level.spinner(x + 650, y - 750, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) + // balance4 = level.spinner(x + 750, y - 1050, 25, 350, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) + balance4 = level.spinner(x + 1250, y - 1000, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) + + let isInRoom = false + doCustom.push( + () => { + if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once + isInRoom = true + spawn.randomMob(x + 1175, y - 725, mobSpawnChance); + spawn.randomMob(x + 1450, y - 725, mobSpawnChance); + spawn.randomMob(x + 425, y - 100, mobSpawnChance); + spawn.randomMob(x + 1700, y - 300, mobSpawnChance); + spawn.randomMob(x + 1300, y - 375, mobSpawnChance); + } + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(x + 1550, y - 1300, 450, 350) + } + ) + doCustomTopLayer.push( + () => { + ctx.fillStyle = "#233" + ctx.beginPath(); + ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance2.pointA.x, balance2.pointA.y) + ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance3.pointA.x, balance3.pointA.y) + ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance4.pointA.x, balance4.pointA.y) + ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI); + ctx.fill(); + } + ) + }, + (x = offset.x, y = offset.y) => { + level.exit.x = x + 1750; + level.exit.y = y - 980; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(x + 1550, y - 950, 500, 25); //exit platform + spawn.mapRect(x + 1600, y - 1300, 25, 175); //exit side wall + spawn.bodyRect(x + 1275, y - 475, 125, 125, 0.25); + spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25); + spawn.bodyRect(x + 800, y - 150, 100, 150, 0.25); + spawn.bodyRect(x + 875, y + -50, 50, 50); + spawn.bodyRect(x + 1025, y + -50, 50, 50); + + if (Math.random() > 0.5) { + const density = 0.0012 //+ (simulation.difficultyMode < 5 ? 0.003 : 0) + const angle = Math.PI / 2 + const variance = 0.2 //Math.PI + const frictionAir = 0.015 + const height = 35 + balance1 = level.spinner(x + 1300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { + balance3 = level.spinner(x + 750, y - 650, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) + balance2 = level.spinner(x + 300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) + balance4 = level.spinner(x + 1250, y - 950, 50, 550, density, angle, 0.1) + const rotatingBlock = body[body.length - 1] + doCustom.push( + () => { + if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once + isInRoom = true + spawn.randomMob(x + 1175, y - 725, mobSpawnChance); + spawn.randomMob(x + 1450, y - 725, mobSpawnChance); + spawn.randomMob(x + 425, y - 100, mobSpawnChance); + spawn.randomMob(x + 1200, y - 125, mobSpawnChance); + spawn.randomMob(x + 1300, y - 375, mobSpawnChance); + } + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(x + 1600, y - 1300, 400, 350) + rotatingBlock.torque += rotatingBlock.inertia * 0.000005 + } + ) + } else { + const density = 0.001 //+ (simulation.difficultyMode < 5 ? 0.003 : 0) + const angle = Math.PI / 2 + const variance = Math.PI + const frictionAir = 0.015 + const width = 200 + const height = 200 + const spinVariance = 0.05 + balance1 = level.spinner(x + 175, y - 300, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { + balance2 = level.spinner(x + 500, y - 525, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) + balance3 = level.spinner(x + 850, y - 700, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) + balance4 = level.spinner(x + 1250, y - 850, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) + doCustom.push( + () => { + if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once + isInRoom = true + spawn.randomMob(x + 1175, y - 725, mobSpawnChance); + spawn.randomMob(x + 1450, y - 725, mobSpawnChance); + spawn.randomMob(x + 425, y - 100, mobSpawnChance); + spawn.randomMob(x + 1200, y - 125, mobSpawnChance); + spawn.randomMob(x + 1300, y - 375, mobSpawnChance); + } + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(x + 1600, y - 1300, 400, 350) + } + ) + } + let isInRoom = false + doCustomTopLayer.push( + () => { + ctx.fillStyle = "#233" + ctx.beginPath(); + ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance2.pointA.x, balance2.pointA.y) + ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance3.pointA.x, balance3.pointA.y) + ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance4.pointA.x, balance4.pointA.y) + ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI); + ctx.fill(); + } + ) + } + ] + emptyOptions = [ //nothing good here except the starting power up, and duplicated bosses + (x = offset.x, y = offset.y) => { //pulse + if (!isDoorLeft && isDoorRight) { //flipped, entering from the right + powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400); + spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall + spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun + spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); //gun nose + const button = level.button(x + 2000 - 290 - 140, y - 200) + button.isReadyToFire = true + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + b.pulse(90, Math.PI, { + x: x + 2000 - 560, + y: y - 150 + }) + } + } + ) + spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } else { + powerUps.spawnStartingPowerUps(x + 1650, y + -400); + spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 1525, y + -350, 250, 450); //wall + spawn.mapRect(x + 245, y + -200, 300, 100); //gun + spawn.mapRect(x + 530, y + -190, 25, 80); //gun nose + const button = level.button(x + 290, y - 200) + button.isReadyToFire = true + + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 255, y + -100, 280, 100); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + b.pulse(90, 0, { x: x + 560, y: y - 150 }) + } + } + ) + spawn.randomMob(x + 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } + }, + (x = offset.x, y = offset.y) => { //spawn block and fire it + if (!isDoorLeft && isDoorRight) { + powerUps.spawnStartingPowerUps(x + 1650, y + -400); + spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall + spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun + spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); + const button = level.button(x + 2000 - 290 - 140, y - 200) + button.isReadyToFire = true + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + fireBlock = function (xPos, yPos) { + const index = body.length + spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); + const bodyBullet = body[body.length - 1] + Matter.Body.setVelocity(body[index], { x: -120, y: -5 }); + body[index].isAboutToBeRemoved = true; + setTimeout(() => { //remove block + for (let i = 0; i < body.length; i++) { + if (body[i] === bodyBullet) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + }, 1000); + } + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 140); + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 160); + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 180); + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 200); + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 220); + fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 240); + } + } + ) + spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } else { + powerUps.spawnStartingPowerUps(x + 1650, y + -400); + spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 1525, y + -350, 250, 450); //wall + spawn.mapRect(x + 245, y + -200, 300, 100); //gun + spawn.mapRect(x + 530, y + -190, 25, 80); + const button = level.button(x + 290, y - 200) + button.isReadyToFire = true + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 255, y + -100, 280, 100); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + fireBlock = function (xPos, yPos) { + const index = body.length + spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); + const bodyBullet = body[body.length - 1] + Matter.Body.setVelocity(body[index], { x: 120, y: -5 }); + body[index].isAboutToBeRemoved = true; + + setTimeout(() => { //remove block + for (let i = 0; i < body.length; i++) { + if (body[i] === bodyBullet) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + }, 1000); + } + fireBlock(x + 560 + 30 * Math.random(), y - 140); + fireBlock(x + 560 + 30 * Math.random(), y - 160); + fireBlock(x + 560 + 30 * Math.random(), y - 180); + fireBlock(x + 560 + 30 * Math.random(), y - 200); + fireBlock(x + 560 + 30 * Math.random(), y - 220); + fireBlock(x + 560 + 30 * Math.random(), y - 240); + } + } + ) + spawn.randomMob(x + 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } + }, + (x = offset.x, y = offset.y) => { //fire an "ammo clip" of blocks + if (!isDoorLeft && isDoorRight) { //flipped, entering from the right + powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400); + spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall + spawn.mapRect(x + 2000 - 175 - 370, y + -200, 370, 100); //gun + spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); + spawn.mapRect(x + 2000 - 545 - 10, y + -770, 10, 325); //block loader for gun //walls + spawn.mapRect(x + 2000 - 620 - 10, y + -770, 10, 325); //walls + spawn.mapRect(x + 2000 + 50 - 150, y + -425, 150, 50); + spawn.mapRect(x + 2000 - 175 - 370, y + -650, 370, 50); + spawn.mapRect(x + 2000 - 540 - 95, y + -460, 95, 15); //bottom that opens and closes + const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later + for (let i = 0; i < 6; i++) spawn.bodyRect(x + 2000 - 60 - 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun + spawn.bodyRect(x + 2000 - 250 - 40, y + -700, 40, 50); //extra bullets + spawn.bodyRect(x + 2000 - 350 - 30, y + -700, 30, 35); + spawn.bodyRect(x + 2000 - 425 - 40, y + -700, 40, 70); + const button = level.button(x + 2000 - 280 - 140, y - 200) //trigger for gun + button.isReadyToFire = true + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 2000 - 200 - 325, y + -625, 325, 650); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + } else if (!button.isUp) { + const bounds = { + min: { + x: x + 2000 - 580, + y: y - 125 + }, + max: { + x: x + 2000 - 530, + y: y - 110 + } + } + const list = Matter.Query.region(body, bounds) + for (let i = 0, len = list.length; i < len; i++) { + Matter.Body.setVelocity(list[i], { + x: -120, + y: -5 + }); + } + if (Matter.Query.region([player], bounds).length) { + Matter.Body.setVelocity(player, { + x: -100, + y: -5 + }); + } + ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` + ctx.fillRect(bounds.min.x, y - 185, 38, 70); + } + } + ) + spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } else { + powerUps.spawnStartingPowerUps(x + 1650, y + -400); + spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall + spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall + spawn.mapRect(x + 1525, y + -350, 250, 450); //wall + spawn.mapRect(x + 175, y + -200, 370, 100); //gun + spawn.mapRect(x + 530, y + -190, 25, 80); + spawn.mapRect(x + 545, y + -770, 10, 325); //block loader for gun //walls + spawn.mapRect(x + 620, y + -770, 10, 325); //walls + spawn.mapRect(x - 50, y + -425, 150, 50); + spawn.mapRect(x + 175, y + -650, 370, 50); + spawn.mapRect(x + 540, y + -460, 95, 15); //bottom that opens and closes + const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later + for (let i = 0; i < 6; i++) spawn.bodyRect(x + 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun + spawn.bodyRect(x + 250, y + -700, 40, 50); //extra bullets + spawn.bodyRect(x + 350, y + -700, 30, 35); + spawn.bodyRect(x + 425, y + -700, 40, 70); + const button = level.button(x + 280, y - 200) //trigger for gun + button.isReadyToFire = true + doCustom.push( + () => { + ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; + ctx.fillRect(x + 200, y + -625, 325, 650); + button.query(); + button.draw(); + if (!button.isReadyToFire && button.isUp) { + button.isReadyToFire = true + bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + } else if (button.isReadyToFire && !button.isUp) { + button.isReadyToFire = false + bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + } else if (!button.isUp) { + const bounds = { + min: { + x: x + 530, + y: y - 125 + }, + max: { + x: x + 580, + y: y - 110 + } + } + const list = Matter.Query.region(body, bounds) + for (let i = 0, len = list.length; i < len; i++) { + Matter.Body.setVelocity(list[i], { + x: 120, + y: -5 + }); + } + if (Matter.Query.region([player], bounds).length) { + Matter.Body.setVelocity(player, { + x: 100, + y: -5 + }); + } + ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` + ctx.fillRect(bounds.min.x, y - 185, 38, 70); + } + } + ) + spawn.randomMob(x + 1600, y + -425, mobSpawnChance); + spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); + spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); + spawn.randomMob(x + 300, y + -1200, mobSpawnChance); + spawn.randomMob(x + 800, y + -125, mobSpawnChance); + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob + } + } + ] + lootOptions = [ //has some power up reward //field, ammo, research, gun + (x = offset.x, y = offset.y) => { + spawn.mapRect(x + 1925, y + -325, 125, 150); //4 wall ledges + spawn.mapRect(x + 1925, y + -865, 125, 150); //4 wall ledges + spawn.mapRect(x + -50, y + -325, 125, 150); //4 wall ledges + spawn.mapRect(x + -50, y + -865, 125, 150); //4 wall ledges + spawn.mapRect(x + 1700, y + -500, 200, 25); + spawn.mapRect(x + 75, y + -500, 200, 25); + + let chamberY = -650 + if (Math.random() > 0.5) { //upper chamber + chamberY = -650 - 640 + spawn.mapRect(x + 550, y + -10 - 640, 900, 25); //raised floor + spawn.mapRect(x + 450, y + -20 - 640, 1100, 25); + spawn.mapRect(x + 450, y + -675 - 640, 1100, 25); //chamber ceiling + powerUps.directSpawn(x + 998, y - 333 - 640, "tech", false); + spawn.mapVertex(x + 1000, y + -0, "575 0 -575 0 -450 -100 450 -100"); //base + } else { //lower chamber + spawn.mapRect(x + 400, y + -10, 1200, 50); //raised floor + spawn.mapRect(x + 450, y + -20, 1100, 50); + spawn.mapRect(x + 450, y + -675, 1100, 25); //chamber ceiling + spawn.mapRect(x + 550, y + -685, 900, 25); + powerUps.directSpawn(x + 998, y - 333, "tech", false); + } + const powerUp1 = powerUp[powerUp.length - 1] + powerUp1.holdPosition = { + x: powerUp1.position.x, + y: powerUp1.position.y + } + let isSpawnedMobs = false + doCustom.push( + () => { + ctx.fillStyle = "#e4e4e9" //"rgba(255,255,255,1)"; + ctx.fillRect(x + 450, y + chamberY, 1100, 650); //chamber background + // if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2000 && m.pos.y > y - 1300 && m.pos.y < y) { //is player inside this room? + // isInRoom = true + // } else + if (powerUp1.velocity.y !== 0) { //don't run this code if power up is gone //hack: powerUp1.velocity.y !== 0 seems to only be true if the power up up doesn't exist and is no longer being affected by gravity + ctx.strokeStyle = "#f0f" + ctx.lineWidth = 2; + if (Vector.magnitudeSquared(Vector.sub(m.pos, powerUp1.position)) < 90000) { //zone radius is 300 + //damage player and drain energy + if (m.immuneCycle < m.cycle) { + m.damage(0.01); + if (m.energy > 0.1) m.energy -= 0.02 + } + //draw electricity going towards player + const unit = Vector.normalise(Vector.sub(m.pos, powerUp1.position)) + let xElec = powerUp1.position.x + 40 * unit.x; + let yElec = powerUp1.position.y + 40 * unit.y; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (unit.x + 1.5 * (Math.random() - 0.5)) + yElec += step * (unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + } else { + //draw electricity going in random directions + const angle = Math.random() * 2 * Math.PI + const Dx = Math.cos(angle); + const Dy = Math.sin(angle); + let xElec = powerUp1.position.x + 40 * Dx; + let yElec = powerUp1.position.y + 40 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + } + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); //draw electricity + + ctx.beginPath(); //outline damage zone + ctx.arc(powerUp1.position.x, powerUp1.position.y, 300, 0, 2 * Math.PI); + ctx.stroke(); + //float power up in the air + Matter.Body.setPosition(powerUp1, { + x: powerUp1.holdPosition.x + 4 * Math.random(), //1300 -2 + y: powerUp1.holdPosition.y + 4 * Math.random() //335 -2 + }); + Matter.Body.setVelocity(powerUp1, { + x: 0, + y: 0 + }); + } else if (!isSpawnedMobs) { + isSpawnedMobs = true + if (chamberY === -650) { //lower chamber + spawn.randomMob(x + 250, y + -650, mobSpawnChance); + spawn.randomMob(x + 1825, y + -600, mobSpawnChance); + spawn.randomGroup(x + 275, y + -1050, mobSpawnChance); + spawn.randomGroup(x + 675, y + -975, mobSpawnChance); + spawn.randomGroup(x + 1225, y + -975, Infinity); + } else { //upper chamber + spawn.randomMob(x + 250, y + -650, mobSpawnChance); + spawn.randomMob(x + 1800, y + -625, mobSpawnChance); + spawn.randomGroup(x + 300, y + -300, mobSpawnChance); + spawn.randomGroup(x + 650, y + -275, mobSpawnChance); + spawn.randomGroup(x + 1125, y + -300, Infinity); + } + } + } + ) + } + ] + upDownOptions = [ //extra tall vertical section 3000x3000 //this is where the level boss is + (x = offset.x, y = offset.y) => { //mover + const button = level.button(x + 935, y + 0) + button.isUp = true + doCustomTopLayer.push( + () => { + button.draw(); + if (button.isUp) { + button.query(); + if (!button.isUp) { + const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + //map elements go here + //box around portals + spawn.mapRect(x + -50, y + -2700, 150, 110); + spawn.mapRect(x + -50, y + -2440, 150, 25); + spawn.mapRect(x + 1900, y + -2715, 150, 550); + spawn.mapRect(x + 1900, y + -2015, 150, 50); + spawn.mapRect(x + 1900, y + -1115, 150, 150); + spawn.mapRect(x + 1900, y + -815, 150, 50); + spawn.mapRect(x + -50, y + -340, 150, 50); + // spawn.mapRect(x + -50, y + -640, 150, 150); + spawn.mapRect(x + 1975, y - 1015, 50, 225); + spawn.mapRect(x + 1975, y - 2190, 50, 200); + spawn.mapRect(x + -25, y - 2615, 50, 200); + spawn.mapRect(x + -25, y - 515, 75, 200); + + //ledge to get to upper left door + // spawn.mapRect(x + -50, y - 1400, 100, 25); + spawn.mapRect(x + -25, y - 1075, 250, 25); + spawn.mapRect(x + -50, y - 1075, 150, 590); + + + const rampSpeed = 8 //+ Math.floor(4 * Math.random()) + const mover4 = level.mover(x, y + -2425, 1000, 50, rampSpeed) + const mover3 = level.mover(x + 1000, y + -2000, 1000, 50, rampSpeed) + const mover2 = level.mover(x + 1000, y + -800, 1000, 50, -rampSpeed) + const mover1 = level.mover(x, y + -325, 1000, 50, -rampSpeed) + const portal1 = level.portal({ + x: x + 125, + y: y - 415 + }, 2 * Math.PI, { //right + x: x + 125, + y: y - 2515 + }, 2 * Math.PI) //right + + const portal2 = level.portal({ + x: x + 1875, + y: y - 890 + }, Math.PI, { //left + x: x + 1875, + y: y - 2090 + }, Math.PI) //left + + doCustom.push(() => { + portal1[2].query() + portal1[3].query() + portal2[2].query() + portal2[3].query() + mover1.push(); + mover2.push(); + mover3.push(); + mover4.push(); + }) + doCustomTopLayer.push(() => { + portal1[0].draw(); + portal1[1].draw(); + portal1[2].draw(); + portal1[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + mover1.draw(); + mover2.draw(); + mover3.draw(); + mover4.draw(); + }) + for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + simulation.draw.setPaths() //update map graphics + + //blocks that ride the movers and portals + spawn.bodyRect(x + 175, y + -2525, 50, 75); + spawn.bodyRect(x + 300, y + -2525, 50, 50); + spawn.bodyRect(x + 500, y + -2525, 80, 75); + + //mobs go here + spawn.randomMob(x + 175, y + -125, 0); + spawn.randomMob(x + 1775, y + -125, 0); + // spawn.randomMob(x + 1750, y + -525, 0); + spawn.randomMob(x + 225, y + -1000, 0); + spawn.randomMob(x + 1675, y + -1075, 0); + // spawn.randomMob(x + 1575, y + -2450, 0); + spawn.randomMob(x + 425, y + -1850, 0); + spawn.randomMob(x + 1425, y + -1200, 0); + spawn.randomMob(x + 350, y + -1000, 0); + spawn.randomLevelBoss(x + 475, y + -1475); + spawn.secondaryBossChance(x + 1425, y + -1425); + } + } + } + ) + }, + (x = offset.x, y = offset.y) => { //hopBoss2 + const button = level.button(x + 935, y + 0) + button.isUp = true + // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges + // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges + doCustomTopLayer.push( + () => { + button.draw(); + if (button.isUp) { + button.query(); + if (!button.isUp) { + const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + //map elements go here + spawn.mapRect(x + 150, y + -1400, 750, 50); + spawn.mapRect(x + 1100, y + -1400, 750, 50); + spawn.mapRect(x + 1825, y + -1050, 200, 50); + spawn.mapRect(x + -25, y + -1050, 200, 50); + spawn.mapRect(x + 1825, y + -325, 200, 50); + spawn.mapRect(x + -25, y + -325, 200, 50); + spawn.mapRect(x + 275, y + -700, 525, 50); + spawn.mapRect(x + 1200, y + -700, 525, 50); + spawn.mapRect(x + -25, y + -1400, 125, 1125); //side walls + spawn.mapRect(x + 1900, y + -1400, 150, 1125); + spawn.mapRect(x + 1900, y + -2700, 125, 1000); + spawn.mapRect(x + -50, y + -2725, 150, 1025); + spawn.mapRect(x + -25, y + -1750, 450, 50); + spawn.mapRect(x + 1575, y + -1750, 450, 50); + spawn.mapRect(x + 525, y + -1750, 950, 50); + for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + simulation.draw.setPaths() //update map graphics + //mobs go here + powerUps.directSpawn(x + 50, y - 1525, "ammo"); + powerUps.directSpawn(x + 1950, y - 1525, "ammo"); + powerUps.directSpawn(x + 1900, y - 1525, "ammo"); + spawn.hopMomBoss(x + 800, y + -2200) + for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600) + for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600) + spawn.hopper(x + 1550, y + -775); + spawn.hopper(x + 500, y + -775); + spawn.hopper(x + 1400, y + -775); + spawn.hopper(x + 550, y + -775); + spawn.hopper(x + 525, y + -1475); + spawn.hopper(x + 1550, y + -1500); + } + } + } + ) + }, + (x = offset.x, y = offset.y) => { + // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { + // toggle.isAddedElements = false + + const button = level.button(x + 935, y + 0) + button.isUp = true + + + spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges + spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges + doCustomTopLayer.push( + () => { + button.draw(); + if (button.isUp) { + button.query(); + if (!button.isUp) { + // toggle.isAddedElements = true //only do this once + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + let r = 150 + let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` + //450 horizontal spread // -130-130-130 = 390 vertical + if (Math.random() < 0.5) { + spawn.mapVertex(x + 775, y + -260, hexagon); + spawn.mapVertex(x + 1225, y + -260, hexagon); + + spawn.mapVertex(x + 550, y + -650, hexagon); + spawn.mapVertex(x + 1000, y + -650, hexagon); + spawn.mapVertex(x + 1450, y + -650, hexagon); + + spawn.mapVertex(x + 325, y + -1040, hexagon); + spawn.mapVertex(x + 775, y + -1040, hexagon); + spawn.mapVertex(x + 1225, y + -1040, hexagon); + spawn.mapVertex(x + 1675, y + -1040, hexagon); + + spawn.mapVertex(x + 550, y + -1430, hexagon); + spawn.mapVertex(x + 1000, y + -1430, hexagon); + spawn.mapVertex(x + 1450, y + -1430, hexagon); + + const numberOfMapElementsAdded = 12 + for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + spawn.randomMob(x + 225, y + -1775, mobSpawnChance); + spawn.randomMob(x + 700, y + -1750, mobSpawnChance); + spawn.randomMob(x + 1175, y + -1725, mobSpawnChance); + spawn.randomMob(x + 1700, y + -1700, mobSpawnChance); + spawn.randomMob(x + 1750, y + -250, mobSpawnChance); + spawn.randomMob(x + 125, y + -250, mobSpawnChance); + } else { + spawn.mapVertex(x + 775, y + -260, hexagon); + spawn.mapVertex(x + 1225, y + -260, hexagon); + + spawn.mapVertex(x + 550, y + -650, hexagon); + spawn.mapVertex(x + 1000, y + -650, hexagon); + spawn.mapVertex(x + 1450, y + -650, hexagon); + + spawn.mapVertex(x + 775, y + -1040, hexagon); + spawn.mapVertex(x + 1225, y + -1040, hexagon); + + spawn.mapVertex(x + 550, y + -1430, hexagon); + spawn.mapVertex(x + 1000, y + -1430, hexagon); + spawn.mapVertex(x + 1450, y + -1430, hexagon); + + spawn.mapVertex(x + 775, y + -1820, hexagon); + spawn.mapVertex(x + 1225, y + -1820, hexagon); + const numberOfMapElementsAdded = 12 + for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + + spawn.randomMob(x + 225, y + -1025, mobSpawnChance); + spawn.randomMob(x + 250, y + -1025, mobSpawnChance); + spawn.randomMob(x + 200, y + -675, mobSpawnChance); + spawn.randomMob(x + 225, y + -200, mobSpawnChance); + spawn.randomMob(x + 1750, y + -1075, mobSpawnChance); + spawn.randomMob(x + 1700, y + -650, mobSpawnChance); + spawn.randomMob(x + 1725, y + -650, mobSpawnChance); + spawn.randomMob(x + 1675, y + -175, mobSpawnChance); + } + simulation.draw.setPaths() //update map graphics + spawn.randomGroup(x + 300, y + -2200); + spawn.randomGroup(x + 1625, y + -2200); + spawn.randomLevelBoss(x + 700, y + -2300); + spawn.secondaryBossChance(x + 1250, y + -2300) + } + } + // toggle.query(); + // if (toggle.isOn && !toggle.isAddedElements) { //this code runs once after the toggle is triggered + + // } + } + ) + }, + (x = offset.x, y = offset.y) => { + // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { + // toggle.isAddedElements = false + const button = level.button(x + 935, y + 0) + button.isUp = true + //left ledges + spawn.mapVertex(x + 5, y + -1868, "0 0 0 -250 125 -250"); + spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //door + spawn.mapVertex(x + 5, y + -768, "0 0 0 -250 125 -250"); + // right ledges + spawn.mapVertex(x + 2000, y + -1868, "0 0 0 -250 -125 -250"); + spawn.mapVertex(x + 2000, y + -1318, "0 0 0 -250 -125 -250"); //door + spawn.mapVertex(x + 2000, y + -768, "0 0 0 -250 -125 -250"); + + doCustomTopLayer.push( + () => { + button.draw(); + if (button.isUp) { + button.query(); + if (!button.isUp) { + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + //right side hexagons + let r = 300 + let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` + spawn.mapVertex(x + 1640, y + -365, hexagon); + // r = 275 + // let hexagonHalf = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ` + // spawn.mapVertex(x + 2300, y + -75, hexagonHalf); + r = 150 + const hexagon150 = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` + // spawn.mapVertex(x + 1750, y + -550, hexagon150); + spawn.mapVertex(x + 1750, y + -1100, hexagon150); + spawn.mapVertex(x + 1750, y + -1650, hexagon150); + spawn.mapVertex(x + 1750, y + -2200, hexagon150); + + //left side + r = 350 + let hexagonHalf = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ` + spawn.mapVertex(x + 425, y + -90, hexagonHalf); + + spawn.mapVertex(x + 850, y + -500, hexagon150); + spawn.mapVertex(x + 550, y + -850, hexagon150); + spawn.mapVertex(x + 250, y + -1200, hexagon150); + spawn.mapVertex(x + 250, y + -1700, hexagon150); + spawn.mapVertex(x + 725, y + -1950, hexagon150); + spawn.mapVertex(x + 1200, y + -2200, hexagon150); + const numberOfMapElementsAdded = 11 + for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + + spawn.randomMob(x + 1050, y + -1500, mobSpawnChance); + spawn.randomMob(x + 1075, y + -1500, mobSpawnChance); + spawn.randomMob(x + 325, y + -550, mobSpawnChance); + spawn.randomMob(x + 800, y + -925, mobSpawnChance); + spawn.randomMob(x + 1400, y + -1250, mobSpawnChance); + spawn.randomMob(x + 1325, y + -1725, mobSpawnChance); + spawn.randomMob(x + 1350, y + -1725, mobSpawnChance); + spawn.randomMob(x + 575, y + -1375, mobSpawnChance); + spawn.randomMob(x + 225, y + -2275, mobSpawnChance); + spawn.randomMob(x + 875, y + -2450, mobSpawnChance); + spawn.randomMob(x + 1550, y + -2525, mobSpawnChance); + spawn.randomMob(x + 1525, y + -2525, mobSpawnChance); + spawn.randomLevelBoss(x + 1075, y + -1500); + spawn.secondaryBossChance(x + 1200, y + -1000) + simulation.draw.setPaths() //update map graphics + } + } + } + ) + }, + // (x = offset.x, y = offset.y) => { + // const elevator1 = level.elevator(x + 1100, y - 200, 250, 30, -2100, 0.0015) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }, isTeleport = false) { + // // const elevator1 = level.elevator(x + 175, y - 200, 250, 30, -1400, 0.001) + // // const elevator2 = level.elevator(x + 2175, y - 200, 250, 30, -1400, 0.001) + + // spawn.mapRect(-200, -1400, 350, 50); //up left door ledge + // spawn.mapRect(2450, -1400, 350, 50); //up right door ledge + + // spawn.mapRect(225, -450, 350, 350); //left crawl zone + // // spawn.mapRect(725, -175, 275, 75); + // spawn.mapRect(725, -225, 350, 100); + // spawn.mapRect(275, -750, 200, 200); + // spawn.mapRect(1375, -700, 500, 750); //right side big elevator wall + // spawn.mapRect(2375, -325, 350, 50); + // spawn.mapRect(1800, -500, 250, 50); + + // //up high elevator + // spawn.mapRect(1375, -2100, 500, 175); + // spawn.mapRect(600, -2100, 475, 175); + + // if (simulation.difficulty > 3) spawn.randomLevelBoss(x + 1250, y - 1400); + // doCustomTopLayer.push( + // () => { + // elevator1.move() + // } + // ) + // } + ] + //pick which type of room spawns + enter = enterOptions[Math.floor(Math.random() * enterOptions.length)]; + exit = exitOptions[Math.floor(Math.random() * exitOptions.length)]; + empty = emptyOptions[Math.floor(Math.random() * emptyOptions.length)]; + loot = lootOptions[Math.floor(Math.random() * lootOptions.length)]; + upDown = upDownOptions[Math.floor(Math.random() * upDownOptions.length)]; + // upDown = upDownOptions[0] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** + //3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000) + //rooms + let rooms = ["exit", "loot", "enter", "empty"] + rooms = shuffle(rooms); //shuffles array order + //look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things + while ( //makes sure that the exit and entrance aren't both on the same floor + (rooms[0] === "enter" && rooms[2] === "exit") || + (rooms[2] === "enter" && rooms[0] === "exit") || + (rooms[1] === "enter" && rooms[3] === "exit") || + (rooms[3] === "enter" && rooms[1] === "exit") + ) rooms = shuffle(rooms); //shuffles array order + for (let i = 0; i < rooms.length; i++) { + if (rooms[i] === "enter") rooms[i] = enter + if (rooms[i] === "exit") rooms[i] = exit + if (rooms[i] === "empty") rooms[i] = empty + if (rooms[i] === "loot") rooms[i] = loot + } + // rooms = [enter, exit, loot, empty, ] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** + + outline = (isLower = true) => { + spawn.mapRect(offset.x - 100, offset.y - 1400, 2100, 100); //ceiling + if (isLower) spawn.mapRect(offset.x - 100, offset.y, 2200, 100); //only draw floor if on the lower level + if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 1400, 100, 1500); //left wall + if (isDoorRight) { //if door only add wall on right side + spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall + spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep + const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random()) + spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 175, doorWidth, 165); //block door + } else { + spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1500); //right wall + } + } + outlineUpDown = () => { + spawn.mapRect(offset.x - 100, offset.y + 0, 2100, 100); //floor + spawn.mapRect(offset.x - 100, offset.y - 2800, 2100, 100); //ceiling + if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 2800, 100, 2900); //left wall + if (isDoorRight) { //if door only add wall on right side + //upper door + spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 1225); //right wall + spawn.mapRect(offset.x + 2000, offset.y - 1410, 100, 20); //right doorstep + const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random()) + spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 1575, doorWidth, 165); //block door + //lower door + spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall + spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep + const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random()) + spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door + } else { + spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2900); //right wall + } + } + + let columns = [ + () => { + offset.y = 0 + outlineUpDown() + upDown() + }, + () => { + offset.y = 0 + outline() + rooms[0]() + + offset.y = -1400 + outline(false) + rooms[1]() + }, + () => { + offset.y = 0 + outline() + rooms[2]() + + offset.y = -1400 + outline(false) + rooms[3]() + }, + ] + columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION *************************************** + for (let i = 0; i < 3; i++) { + if (i === 0) { + isDoorLeft = false + isDoorRight = true + } else if (i === 1) { + isDoorLeft = true + isDoorRight = true + } else { + isDoorLeft = true + isDoorRight = false + } + offset.x = i * 2100 + columns[i]() + } + level.custom = () => { + for (let i = 0, len = doCustom.length; i < len; i++) doCustom[i]() //runs all the active code from each room + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + for (let i = 0, len = doCustomTopLayer.length; i < len; i++) doCustomTopLayer[i]() //runs all the active code from each room + }; + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + pavilion() { + const vanish = [] + level.exit.x = -850; + level.exit.y = -1485; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); + level.setPosToSpawn(-900, 225); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + spawn.debris(-150, -775, 1425, 3); //16 debris per level + spawn.debris(1525, -25, 950, 3); //16 debris per level + spawn.debris(-650, -2100, 575, 2); //16 debris per level + + //bottom floor + //entrance + spawn.mapRect(-200, -750, 1500, 100); + spawn.mapRect(-575, 0, 2150, 500); + // spawn.mapRect(-1275, 275, 875, 225); + spawn.mapRect(-1275, 275, 3975, 225); + spawn.mapRect(-1050, 0, 325, 50); + spawn.mapRect(-775, 0, 50, 140); + vanish.push(level.vanish(-725, 13, 150, 25)) + spawn.mapRect(-200, -750, 100, 600); + // spawn.mapRect(1200, -750, 100, 600); + vanish.push(level.vanish(-350, -225, 150, 225)) + vanish.push(level.vanish(-350, -450, 150, 223)) + spawn.mapRect(2475, -1800, 250, 2300); + + spawn.mapRect(1200, -750, 100, 450); + spawn.mapRect(1200, -375, 250, 75); + powerUps.spawnStartingPowerUps(550, -100); + spawn.mapRect(125, -12, 850, 50); + spawn.mapRect(175, -25, 750, 50); + spawn.bodyRect(1350, -175, 150, 175, 0.5); + spawn.bodyRect(1350, -600, 125, 225, 0.2); + + //middle floor + spawn.bodyRect(215, -1175, 100, 100, 0.3); + spawn.mapRect(-1300, -1800, 250, 2300); + // spawn.mapRect(-1300, -2075, 250, 2575); + if (Math.random() < 0.5) { + spawn.mapRect(500, -1350, 525, 425); + spawn.mapRect(25, -1050, 300, 198); + } else { + spawn.mapRect(500, -1350, 525, 497); + spawn.mapRect(25, -1050, 300, 150); + } + if (Math.random() < 0.5) { + vanish.push(level.vanish(400, -1600, 175, 25)) + vanish.push(level.vanish(950, -1600, 175, 25)) + } else { + vanish.push(level.vanish(550, -1575, 50, 225)) + vanish.push(level.vanish(925, -1575, 50, 225)) + } + + // vanish.push(level.vanish(575, -1575, 375, 225)) + + spawn.bodyRect(225, -850, 50, 100, 0.4); + spawn.mapRect(600, -1800, 325, 225); + spawn.mapRect(1900, -1500, 325, 25); + spawn.bodyRect(1050, -1825, 250, 20, 0.2); + if (Math.random() < 0.5) { + vanish.push(level.vanish(1400, -1000, 200, 25)) + vanish.push(level.vanish(1625, -1250, 200, 25)) + } else { + vanish.push(level.vanish(1400, -1075, 175, 175)) + vanish.push(level.vanish(1575, -1250, 175, 175)) + } + vanish.push(level.vanish(1125, -1800, 625, 25)) + + // vanish.push(level.vanish(1500, -1800, 225, 25)) + vanish.push(level.vanish(-50, -1800, 450, 25)) + + //exit + spawn.mapRect(-1050, -1450, 700, 25); + spawn.mapRect(-1050, -1800, 525, 25); + spawn.mapRect(-550, -1800, 25, 200); + + spawn.randomMob(-1175, -1975, -0.4); + spawn.randomMob(275, -1500, -0.3); + spawn.randomMob(700, -1875, -0.2); + spawn.randomMob(2000, -800, -0.2); + spawn.randomMob(2600, -1850, 0); + spawn.randomMob(1425, -525, 0.1); + spawn.randomMob(2025, -1600, 0.3); + spawn.randomMob(1625, -1875, 0.3); + spawn.randomMob(-150, -1975, 0.4); + spawn.randomSmallMob(900, -825); + spawn.randomSmallMob(1050, -50); + + if (simulation.difficulty > 1) { + spawn.randomGroup(750, -2150, -0.8) + spawn.randomLevelBoss(2050, -2025) + spawn.secondaryBossChance(100, -1500) + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + level.setPosToSpawn(900, 225); //normal spawn + level.custom = () => { + ctx.fillStyle = "#d0d3d9" + ctx.fillRect(-2500, -1800, 3575, 2100); + ctx.fillStyle = "#c0c3c9" + ctx.fillRect(-2075, -1475, 25, 1800); + ctx.fillStyle = "#cff" //exit + ctx.fillRect(550, -1800, 525, 350) + + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + //shadow + ctx.fillStyle = "rgba(0,10,30,0.1)" + ctx.fillRect(-1450, -300, 150, 325); + ctx.fillRect(-1300, -650, 1500, 650) + ctx.fillRect(725, 50, 325, 225) + ctx.fillRect(-325, -950, 300, 225) + ctx.fillRect(-1025, -1000, 525, 275); + ctx.fillRect(-925, -1600, 325, 275); + for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() + }; + + } else { + level.custom = () => { + ctx.fillStyle = "#d0d3d9" + ctx.fillRect(-1075, -1800, 3575, 2100); + ctx.fillStyle = "#c0c3c9" + ctx.fillRect(2050, -1475, 25, 1800); + ctx.fillStyle = "#cff" //exit + ctx.fillRect(-1050, -1800, 525, 350) + + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + //shadow + ctx.fillStyle = "rgba(0,10,30,0.1)" + ctx.fillRect(1300, -300, 150, 325); + ctx.fillRect(-200, -675, 1500, 700) + ctx.fillRect(500, -950, 525, 225); + ctx.fillRect(600, -1600, 325, 275); + ctx.fillRect(-1050, 50, 325, 225) + ctx.fillRect(25, -950, 300, 225) + for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() + }; + } + }, + testChamber() { + level.setPosToSpawn(0, -50); //lower start + level.exit.y = level.enter.y - 550; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = level.enter.x; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + level.defaultZoom = 2200 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d0d5d5"; + color.map = "#444" + spawn.mapRect(0, -1955, 175, 30); + const removeIndex1 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals + spawn.mapRect(1225, -1955, 175, 30); + const removeIndex2 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals + let portal, portal2, portal3 + const hazard = level.hazard((simulation.isHorizontalFlipped ? -350 - 700 : 350), -2025, 700, 10, 0.4) //laser + spawn.mapRect(340, -2032.5, 20, 25); //laser nose + const hazard2 = level.hazard((simulation.isHorizontalFlipped ? -1775 - 150 : 1775), -2550, 150, 10, 0.4) //laser + spawn.mapRect(1920, -2557.5, 20, 25); //laser nose + const button = level.button(2100, -2600) + const buttonDoor = level.button(600, -550) + const door = level.door(312, -750, 25, 190, 185) + + level.custom = () => { + if (!(m.cycle % 60)) { //so much work to catch blocks caught at the bottom of the vertical portals + let touching = Matter.Query.collides(map[removeIndex1], body) + if (touching.length) { + Matter.Composite.remove(engine.world, touching[0].bodyB); + for (let i = 0, len = body.length; i < len; i++) { + if (body[i].id === touching[0].bodyB.id) { + body.splice(i, 1); + break + } + } + } + touching = Matter.Query.collides(map[removeIndex2], body) + if (touching.length) { + Matter.Composite.remove(engine.world, touching[0].bodyB); + for (let i = 0, len = body.length; i < len; i++) { + if (body[i].id === touching[0].bodyB.id) { + body.splice(i, 1); + break + } + } + } + } + + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + + portal[2].query() + portal[3].query() + portal2[2].query() + portal2[3].query() + portal3[2].query() + portal3[3].query() + + if (button.isUp) { + hazard.isOn = false; + hazard2.isOn = false; + } else { + hazard.isOn = true; + hazard2.isOn = true; + } + button.query(); + button.draw(); + + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(-300, -1000, 650, 500) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + door.draw(); + hazard.opticalQuery(); + hazard2.opticalQuery(); + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + portal3[0].draw(); + portal3[1].draw(); + portal3[2].draw(); + portal3[3].draw(); + }; + powerUps.spawnStartingPowerUps(1875, -3075); + + const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead + x: -150, + y: -1775 + }, { + x: 2400, + y: -2650 + }, { + x: -175, + y: -1375 + }, { + x: 1325, + y: -150 + }]); + powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y); + powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y); + //outer wall + spawn.mapRect(2500, -3700, 1200, 3800); //right map wall + spawn.mapRect(-1400, -3800, 1100, 3900); //left map wall + spawn.mapRect(-1400, -4800, 5100, 1200); //map ceiling + spawn.mapRect(-1400, 0, 5100, 1200); //floor + //lower entrance /exit + spawn.mapRect(300, -375, 50, 225); + spawn.bodyRect(312, -150, 25, 140); + spawn.mapRect(300, -10, 50, 50); + spawn.mapVertex(1555, 0, "625 0 75 0 200 -100 500 -100"); //entrance ramp + //upper entrance / exit + spawn.mapRect(-400, -1050, 750, 50); + spawn.mapRect(300, -1050, 50, 300); + // spawn.bodyRect(312, -750, 25, 190); + spawn.mapRect(300, -560, 50, 50); + spawn.bodyRect(750, -725, 125, 125); + spawn.mapRect(1150, -1050, 250, 575); + spawn.mapRect(1725, -550, 50, 200); //walls around portal 3 + spawn.mapRect(1925, -550, 500, 200); + spawn.mapRect(1750, -390, 200, 40); + spawn.mapRect(-400, -550, 1800, 200); + spawn.mapRect(-200, -1700, 150, 25); //platform above exit room + spawn.mapRect(-200, -1325, 350, 25); + //portal 3 angled + spawn.mapRect(2425, -450, 100, 100); + //portal 1 bottom + spawn.mapRect(2290, -12, 375, 100); + spawn.mapRect(2350, -24, 375, 100); + spawn.mapRect(2410, -36, 375, 100); + //portal 1 top + spawn.mapRect(2290, -3012, 375, 50); + spawn.mapRect(2350, -3024, 375, 50); + spawn.mapRect(2410, -3036, 375, 50); + spawn.mapRect(1400, -3000, 1300, 50); //floor + spawn.mapRect(1750, -3050, 250, 75); + spawn.mapRect(1400, -3625, 50, 200); + spawn.mapRect(350, -3625, 50, 225); + spawn.mapRect(350, -3260, 50, 60); + spawn.mapRect(200, -3250, 1240, 50); + spawn.mapRect(1400, -3260, 50, 310); + spawn.bodyRect(1412, -3425, 25, 165); + spawn.mapRect(-150, -2925, 150, 25); + //portal 2 + spawn.mapRect(-300, -2600, 300, 675); //left platform + spawn.mapRect(1400, -2600, 375, 675); //right platform + spawn.mapRect(1925, -2600, 775, 675); //far right platform + spawn.bodyRect(2130, -2660, 50, 50); //button's block + spawn.mapRect(150, -2100, 200, 175); + spawn.mapRect(1050, -2100, 200, 175); + //mobs + spawn.randomMob(1075, -3500, -0.3); + spawn.randomMob(2175, -700, -0.2); + spawn.randomMob(-75, -850, -0.1); + spawn.randomMob(550, -3400, 0); + spawn.randomMob(0, -1175, 0.5); + spawn.randomMob(-75, -1150, 0.5); + spawn.randomMob(1075, -625, 0.5); + spawn.randomMob(800, -3400, -0.3); + spawn.randomMob(1225, -3375, -0.2); + spawn.randomMob(1200, -1125, -0.1); + spawn.randomMob(2050, -950, 0.5); + if (simulation.difficulty > 40) { + spawn.randomMob(2300, -2775, -0.5); + spawn.randomMob(600, -925, -0.5); + spawn.randomMob(1550, -2750, -0.5); + spawn.randomMob(1350, -1150, -0.5); + spawn.randomMob(-75, -1475, 0); + spawn.randomGroup(600, -2600, 0); + } + if (simulation.difficulty > 1) { + if (Math.random() < 0.5) { + spawn.randomLevelBoss(700, -1550); + } else { + spawn.randomLevelBoss(675, -2775); //["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "grenadierBoss"] + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(1925, -1250) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + // level.setPosToSpawn(0, -50); //-x // no need since 0 + button.min.x = -button.min.x - 126 // flip the button horizontally + button.max.x = -button.max.x + 126 // flip the button horizontally + buttonDoor.min.x = -buttonDoor.min.x - 126 // flip the button horizontally + buttonDoor.max.x = -buttonDoor.max.x + 126 // flip the button horizontally + + //this makes the hazard draw, but not collide for reasons I don't understand + //so don't use it, instead just call the hazard differently based on this flip flag + // hazard.min.x = -hazard.min.x - hazard.width //-x-width + // hazard.max.x = -hazard.max.x - hazard.width //-x-width + // hazard2.min.x = -hazard2.min.x - hazard2.width //-x-width + // hazard2.max.x = -hazard2.max.x - hazard2.width //-x-width + portal = level.portal({ + x: -2475, + y: -140 + }, 2 * Math.PI, { //right + x: -2475, + y: -3140 + }, 2 * Math.PI) //right + + portal2 = level.portal({ + x: -75, + y: -2150 + }, -Math.PI / 2, { //up + x: -1325, + y: -2150 + }, -Math.PI / 2) //up + + portal3 = level.portal({ + x: -1850, + y: -585 + }, -Math.PI / 2, { //up + x: -2425, + y: -600 + }, -1 * Math.PI / 3) //up left + + // level.custom = () => { }; + // level.customTopLayer = () => {}; + + } else { + portal = level.portal({ + x: 2475, + y: -140 + }, Math.PI, { //left + x: 2475, + y: -3140 + }, Math.PI) //left + portal2 = level.portal({ + x: 75, + y: -2150 + }, -Math.PI / 2, { //up + x: 1325, + y: -2150 + }, -Math.PI / 2) //up + portal3 = level.portal({ + x: 1850, + y: -585 + }, -Math.PI / 2, { //up + x: 2425, + y: -600 + }, -2 * Math.PI / 3) //up left + } + + }, + lock() { + level.setPosToSpawn(0, -65); //lower start + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.y = 2010; + level.exit.x = 2625; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + level.defaultZoom = 2200 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "hsl(138, 5%, 82%)"; + color.map = "#444" + powerUps.spawnStartingPowerUps(1768, 870); //on left side + const portal = level.portal({ x: 1070, y: -1485 }, -0.9, { x: 475, y: 50 }, -Math.PI / 2) + const doorCenterRight = level.door(2787, 775, 25, 225, 195, 5) //x, y, width, height, distance, speed = 1 + const doorCenterLeft = level.door(2537, 775, 25, 225, 195, 5) + const doorButtonRight = level.door(4462, 1010, 25, 225, 195, 5) + const doorLeft = level.door(2538, 1825, 25, 225, 195, 5) + const buttonLeft = level.button(4565, 1235) + const buttonRight = level.button(4142, -355) + // spawn.mapRect(4000, -350, 700, 125); //button platform + spawn.mapRect(4000, -350, 600, 75); + buttonLeft.isUp = true + buttonRight.isUp = true + const hazardSlimeLeft = level.hazard(900, -300, 1638, 2450) //hazard(x, y, width, height, damage = 0.002) { + const hazardSlimeRight = level.hazard(2812, -300, 1650, 2450) //hazard(x, y, width, height, damage = 0.002) { + //set slime to empty + // hazardSlimeLeft.height -= hazardSlimeLeft.maxHeight //start slime at zero + // hazardSlimeLeft.min.y += hazardSlimeLeft.maxHeight + // hazardSlimeLeft.max.y = hazardSlimeLeft.min.y + hazardSlimeLeft.height + // hazardSlimeRight.height -= hazardSlimeRight.maxHeight //start slime at zero + // hazardSlimeRight.min.y += hazardSlimeRight.maxHeight + // hazardSlimeRight.max.y = hazardSlimeRight.min.y + hazardSlimeRight.height + const balance = [] + level.custom = () => { + ctx.fillStyle = "hsl(175, 35%, 76%)" //exit + ctx.fillRect(2537, 1775, 275, 275) + level.exit.drawAndCheck(); + level.enter.draw(); + + doorButtonRight.isClosing = hazardSlimeRight.min.y < 1275 + doorCenterRight.isClosing = hazardSlimeRight.min.y < 1000 + doorCenterLeft.isClosing = hazardSlimeLeft.min.y < 1000 + doorLeft.isClosing = hazardSlimeLeft.min.y < 2050 + doorButtonRight.openClose(); + doorCenterRight.openClose(); + doorCenterLeft.openClose(); + doorLeft.openClose(); + if (buttonRight.isUp) { + buttonRight.query(); + if (!buttonRight.isUp) spawnRightMobs() + } + if (buttonLeft.isUp) { + buttonLeft.query(); + if (!buttonLeft.isUp) spawnLeftMobs() + } + buttonRight.draw(); + buttonLeft.draw(); + if (hazardSlimeLeft.min.y < 2050) { + const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeLeft.min.y / 500), 4) + hazardSlimeLeft.level(buttonLeft.isUp, drainRate) + } + if (hazardSlimeRight.min.y < 2050) { + const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeRight.min.y / 500), 4) + hazardSlimeRight.level(buttonRight.isUp, drainRate) + } + portal[2].query() + portal[3].query() + }; + level.customTopLayer = () => { + doorButtonRight.draw(); + doorCenterRight.draw(); + doorCenterLeft.draw(); + doorLeft.draw(); + hazardSlimeLeft.query(); + hazardSlimeRight.query(); + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + ctx.fillStyle = color.map //below portal + ctx.fillRect(375, 150, 200, 2525); + ctx.fillStyle = "rgba(0,0,0,0.1)" //shadows + ctx.fillRect(-250, -1550, 1250, 1575); + ctx.fillRect(2537, -350, 275, 2425); + ctx.fillStyle = "rgba(0,0,0,0.05)" //exit + ctx.fillRect(-175, -300, 375, 300) + ctx.fillRect(4460, 950, 350, 325); + ctx.fillStyle = "#233" //balances center dot + ctx.beginPath(); + for (let i = 0; i < balance.length; i++) { + ctx.arc(balance[i].center.x, balance[i].center.y, 9, 0, 2 * Math.PI); + } + ctx.fill(); + // for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() + }; + //entrance and outer walls + spawn.mapRect(-1400, 0, 1800, 2675); + spawn.mapRect(-1400, -1025, 1225, 1500); + spawn.mapRect(-325, -15, 525, 225); + spawn.mapRect(150, -350, 50, 175); + + spawn.mapRect(-1400, -3525, 1600, 3225); + spawn.mapRect(550, 0, 450, 2675); + + spawn.mapRect(550, -1550, 450, 125); + spawn.mapRect(150, -1550, 250, 125); + spawn.mapRect(750, -1425, 1100, 175); + spawn.mapRect(750, -1400, 250, 825); + spawn.mapRect(750, -350, 250, 575); + spawn.mapRect(625, 2100, 4300, 575); //floor + spawn.mapRect(-1400, -4425, 7250, 1000); //ceiling + // const vanish = [] + // vanish.push(level.vanish(400, -1512, 150, 50)) + // vanish.push(level.vanish(825, -625, 100, 325)) + + //left button room (on the far right in the + spawn.mapRect(4450, -3525, 1400, 4500); + spawn.mapRect(4450, 1235, 1400, 1440); + spawn.mapRect(4775, 750, 1075, 825); + spawn.mapRect(4450, 950, 50, 75); + + + //other ideas for left and right alternate setups + //just a floor covered with boosts + //something focused on funnel shapes + //several rooms with tunnels connecting them + //spinners + + //right side + if (Math.random() < 1) { + spawn.mapVertex(3350, 350, "-100 0 100 0 100 700 0 750 -100 700"); + balance.push(level.rotor(3463, 150, 300, 25, 0.001, 0)) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + balance.push(level.rotor(3463, 500, 300, 25, 0.001, 0)) + spawn.mapVertex(3875, 350, "-100 0 100 0 100 700 0 750 -100 700"); + + spawn.mapVertex(2900, 1743, "-100 0 70 0 100 30 100 1000 -100 1000"); + spawn.mapVertex(3025, 1811, "-150 0 120 0 150 30 150 600 -150 600"); + spawn.mapVertex(3200, 2079, "-150 0 120 0 150 30 150 600 -150 600"); + spawn.mapVertex(4425, 1743, "-150 30 -120 0 150 0 150 1000 -150 1000"); + spawn.mapVertex(4250, 1811, "-150 30 -120 0 150 0 150 600 -150 600"); + spawn.mapVertex(4075, 2079, "-150 30 -120 0 150 0 150 600 -150 600"); + + //escape ledge when drowning + spawn.mapRect(2750, 525, 100, 25); + spawn.mapRect(2750, 125, 100, 25); + spawn.mapRect(4425, 800, 75, 25); + spawn.mapRect(4425, 325, 75, 25); + // spawn.mapRect(4425, -100, 75, 25); + // spawn.mapRect(4425, -550, 75, 25); + // spawn.mapRect(4425, -1000, 75, 25); + + + // if (Math.random() < 0.5) { + // spawn.mapRect(2775, 525, 100, 25); + // spawn.mapRect(3200, 75, 125, 25); + // } else { + // spawn.mapRect(4400, 800, 100, 25); + // spawn.mapRect(3925, 400, 100, 25); + // } + } + //left side + if (Math.random() < 1) { + // spawn.mapVertex(2325, 1325, "-150 0 150 0 150 150 0 225 -150 150"); + spawn.mapVertex(1285, 1275, "-150 0 150 0 150 150 0 225 -150 150"); + spawn.mapVertex(1033, 1750, "0 200 200 200 300 50 300 -50 200 -200 0 -200"); + spawn.mapVertex(1575, 1750, "0 200 -200 200 -300 50 -300 -50 -200 -200 0 -200 100 -50 100 50"); //larger "0 400 -250 400 -400 100 -400 -100 -250 -400 0 -400" + + spawn.mapVertex(1287, 2185, "-100 30 -80 0 80 0 100 30 100 300 -100 300"); + spawn.mapVertex(2050, 2050, "-150 30 -120 0 120 0 150 30 150 300 -150 300"); + + // spawn.mapRect(1700, 1550, 275, 25); + // spawn.mapRect(2175, 1275, 325, 25); + spawn.mapRect(1600, 950, 375, 25); + + spawn.mapRect(1025, -50, 50, 25); + spawn.mapRect(1025, 275, 175, 25); + spawn.mapRect(1025, 600, 325, 25); + spawn.mapRect(2450, -50, 50, 25); + spawn.mapRect(2325, 275, 175, 25); + spawn.mapRect(2175, 600, 325, 25); + // spawn.mapVertex(3400, 1250, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); + } + + //left button room in center divider + spawn.mapRect(2525, -350, 300, 1100); + spawn.mapRect(2525, 975, 300, 800); + spawn.mapRect(2775, 650, 50, 125); + spawn.mapRect(2525, 650, 50, 125); + + //exit room + spawn.mapRect(2475, 2040, 350, 200); + spawn.mapRect(2800, 1750, 25, 325); + spawn.mapRect(2525, 1750, 50, 75); + + //safety edge blocks //maybe remove? + // spawn.mapRect(2525, -375, 25, 50); + // spawn.mapRect(2800, -375, 25, 50); + // spawn.mapRect(1825, -1450, 25, 50); + // spawn.mapRect(4000, -375, 25, 50); + + //blocks + spawn.bodyRect(150, -175, 50, 165, 0.2); //block at entrance + spawn.bodyRect(1275, 825, 100, 100, 0.2); + spawn.bodyRect(2600, -425, 150, 50, 0.2); + spawn.bodyRect(3900, -150, 50, 100, 0.2); + spawn.bodyRect(3350, 1950, 75, 100, 0.2); + spawn.bodyRect(3850, 1975, 75, 75, 0.2); + spawn.bodyRect(1600, 1950, 75, 100, 0.2); + spawn.bodyRect(725, -1650, 150, 100, 0.2); + spawn.bodyRect(800, -1700, 75, 50, 0.2); + + const spawnRightMobs = () => { + spawn.randomMob(4200, 100, 1); + spawn.randomMob(4200, 600, 1); + spawn.randomMob(2975, 625, 0.5); + spawn.randomMob(3050, 100, 0.5); + spawn.randomMob(3400, -100, 0.4); + spawn.randomMob(3825, -100, 0.4); + spawn.randomMob(3625, 1950, 0.4); + spawn.randomMob(3275, 1650, 0.4); + spawn.randomMob(3075, 1375, 0.3); + spawn.randomMob(4000, 1650, 0.1); + spawn.randomMob(4100, 1425, 0); + spawn.randomGroup(3025, 325, 1); + if (simulation.difficulty > 1) spawn.secondaryBossChance(3520, 1169) + } + + const spawnLeftMobs = () => { + spawn.randomMob(2375, 1900, 1); + spawn.randomMob(1825, 1325, 0.5); + spawn.randomMob(2250, 1050, 0.5); + spawn.randomMob(1675, 825, 0.4); + spawn.randomMob(1250, 575, 0.4); + spawn.randomMob(2400, 575, 0.4); + spawn.randomMob(1250, 1575, 0.3); + spawn.randomMob(1075, -100, 0.3); + spawn.randomMob(2450, -100, 0.2); + spawn.randomGroup(1350, -775, 1); + if (simulation.difficulty > 1) spawn.randomLevelBoss(1491, 495); + } + spawn.randomMob(2650, -750, 0.4); + spawn.randomMob(300, -1725, 0.4); + spawn.randomMob(750, -1775, 0.4); + spawn.randomMob(550, -2225, 0.4); + spawn.randomMob(2700, -475, 0.4); + spawn.randomMob(2375, -200, 0.2); + spawn.randomMob(3350, -225, 0.3); + + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + sewers() { + const button1 = level.button(6600, 2675) + // const hazard = level.hazard(4550, 2750, 4550, 150) + const hazard = level.hazard(simulation.isHorizontalFlipped ? -4550 - 4550 : 4550, 2750, 4550, 150) + let balance1, balance2, balance3, balance4, rotor + + const drip1 = level.drip(6100, 1900, 2900, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip2 = level.drip(7300, 1900, 2900, 150) + const drip3 = level.drip(8750, 1900, 2900, 70) + level.custom = () => { + drip1.draw(); + drip2.draw(); + drip3.draw(); + button1.query(); + button1.draw(); + ctx.fillStyle = "hsl(175, 15%, 76%)" + ctx.fillRect(9100, 2200, 800, 400) + ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows + ctx.fillRect(6250, 2025, 700, 650) + ctx.fillRect(8000, 2025, 600, 575) + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + rotor.rotate(); + + ctx.fillStyle = "#233" + ctx.beginPath(); + ctx.arc(balance1.center.x, balance1.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance2.center.x, balance2.center.y) + ctx.arc(balance2.center.x, balance2.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance3.center.x, balance3.center.y) + ctx.arc(balance3.center.x, balance3.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance4.center.x, balance4.center.y) + ctx.arc(balance4.center.x, balance4.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance5.center.x, balance5.center.y) + ctx.arc(balance5.center.x, balance5.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(rotor.center.x, rotor.center.y) + ctx.arc(rotor.center.x, rotor.center.y, 9, 0, 2 * Math.PI); + ctx.fill(); + hazard.query(); + hazard.level(button1.isUp) + }; + + level.setPosToSpawn(0, -50); //normal spawn + + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 9700; + level.exit.y = 2560; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; + color.map = "#3d4240" + powerUps.spawnStartingPowerUps(3475, 1775); + spawn.debris(4575, 2550, 1600, 9); //16 debris per level + spawn.debris(7000, 2550, 2000, 7); //16 debris per level + + spawn.mapRect(-500, -600, 200, 800); //left entrance wall + spawn.mapRect(-400, -600, 3550, 200); //ceiling + spawn.mapRect(-400, 0, 3000, 200); //floor + // spawn.mapRect(300, -500, 50, 400); //right entrance wall + // spawn.bodyRect(312, -100, 25, 100); + spawn.bodyRect(1450, -300, 150, 50); + + const xPos = shuffle([600, 1250, 2000]); + spawn.mapRect(xPos[0], -200, 300, 100); + spawn.mapRect(xPos[1], -250, 300, 300); + spawn.mapRect(xPos[2], -150, 300, 200); + + spawn.bodyRect(3100, 410, 75, 100); + spawn.bodyRect(2450, -25, 250, 25); + + spawn.mapRect(3050, -600, 200, 800); //right down tube wall + spawn.mapRect(3100, 0, 1200, 200); //tube right exit ceiling + spawn.mapRect(4200, 0, 200, 1900); + spawn.mapVertex(3500, 1000, "-500 -500 -400 -600 400 -600 500 -500 500 500 400 600 -400 600 -500 500"); + spawn.mapVertex(3600, 1940, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); + spawn.mapRect(3925, 2288, 310, 50); + spawn.mapRect(3980, 2276, 200, 50); + + spawn.mapRect(2625, 2288, 650, 50); + spawn.mapRect(2700, 2276, 500, 50); + + spawn.mapRect(2400, 0, 200, 1925); //left down tube wall + spawn.mapRect(600, 2300, 3750, 200); + spawn.bodyRect(3800, 275, 125, 125); + + spawn.mapRect(4200, 1700, 5000, 200); + spawn.mapRect(4150, 2300, 200, 400); + + spawn.mapRect(600, 1700, 2000, 200); //bottom left room ceiling + spawn.mapRect(500, 1700, 200, 800); //left wall + spawn.mapRect(675, 1875, 325, 150, 0.5); + + spawn.mapRect(4450, 2900, 4900, 200); //boss room floor + spawn.mapRect(4150, 2600, 400, 500); + spawn.mapRect(6250, 2675, 700, 325); + spawn.mapRect(8000, 2600, 600, 400); + spawn.bodyRect(5875, 2725, 200, 200); + spawn.bodyRect(6800, 2490, 50, 50); + spawn.bodyRect(6800, 2540, 50, 50); + spawn.bodyRect(6800, 2590, 50, 50); + spawn.bodyRect(8225, 2225, 100, 100); + spawn.mapRect(6250, 1875, 700, 150); + spawn.mapRect(8000, 1875, 600, 150); + + spawn.mapRect(9100, 1700, 900, 500); //exit + spawn.mapRect(9100, 2600, 900, 500); + spawn.mapRect(9900, 1700, 200, 1400); //back wall + // spawn.mapRect(9300, 2150, 50, 250); + spawn.mapRect(9300, 2590, 650, 25); + spawn.mapRect(9700, 2580, 100, 50); + + + spawn.randomGroup(1300, 2100, 0.1); + spawn.randomMob(8300, 2100, 0.1); + spawn.randomSmallMob(2575, -75, 0.1); //entrance + spawn.randomMob(8125, 2450, 0.1); + spawn.randomSmallMob(3200, 250, 0.1); + spawn.randomMob(2425, 2150, 0.1); + spawn.randomSmallMob(3500, 250, 0.2); + spawn.randomMob(3800, 2175, 0.2); + spawn.randomSmallMob(2500, -275, 0.2); //entrance + spawn.randomMob(4450, 2500, 0.2); + spawn.randomMob(6350, 2525, 0.2); + spawn.randomGroup(9200, 2400, 0.3); + spawn.randomSmallMob(1900, -250, 0.3); //entrance + spawn.randomMob(1500, 2100, 0.4); + spawn.randomSmallMob(1700, -150, 0.4); //entrance + spawn.randomMob(8800, 2725, 0.5); + spawn.randomMob(7300, 2200, 0.5); + spawn.randomMob(2075, 2025, 0.5); + spawn.randomMob(3475, 2175, 0.5); + spawn.randomMob(8900, 2825, 0.5); + spawn.randomMob(9600, 2425, 0.9); + spawn.randomMob(3600, 1725, 0.9); + spawn.randomMob(4100, 1225, 0.9); + spawn.randomMob(2825, 400, 0.9); + if (simulation.difficulty > 1) spawn.randomLevelBoss(6000, 2300, ["dragonFlyBoss", "beetleBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "orbitalBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(7725, 2275) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + // rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + // rotor = level.rotor(-5100, 2475, 0.001) //rotates other direction because flipped + rotor = level.rotor(-5600, 2390, 850, 50, 0.001, 0, 0.01, 0, 0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + balance1 = level.rotor(-300 - 25, -395, 25, 390, 0.001) //entrance + balance2 = level.rotor(-2605 - 390, 500, 390, 25, 0.001) //falling + balance3 = level.rotor(-2608 - 584, 1900, 584, 25, 0.001) //falling + balance4 = level.rotor(-9300 - 25, 2205, 25, 380, 0.001) //exit + balance5 = level.rotor(-2605 - 390, 1100, 390, 25, 0.001) //falling + // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + // level.setPosToSpawn(300, -700); //-x // no need since 0 + button1.min.x = -button1.min.x - 126 // flip the button horizontally + button1.max.x = -button1.max.x + 126 // flip the button horizontally + drip1.x *= -1 + drip2.x *= -1 + drip3.x *= -1 + level.custom = () => { + drip1.draw(); + drip2.draw(); + drip3.draw(); + + button1.query(); + button1.draw(); + rotor.rotate(); + + ctx.fillStyle = "hsl(175, 15%, 76%)" + ctx.fillRect(-9900, 2200, 800, 400) + ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows + ctx.fillRect(-6950, 2025, 700, 650) + ctx.fillRect(-8600, 2025, 600, 575) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + // level.customTopLayer = () => {}; + } else { + // rotor = level.rotor(5100, 2475, -0.001) + rotor = level.rotor(4700, 2390, 850, 50, 0.001, 0, 0.01, 0, -0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + balance1 = level.rotor(300, -395, 25, 390, 0.001) //entrance + balance2 = level.rotor(2605, 500, 390, 25, 0.001) //falling + balance3 = level.rotor(2608, 1900, 584, 25, 0.001) //falling + balance4 = level.rotor(9300, 2205, 25, 380, 0.001) //exit + balance5 = level.rotor(2605, 1100, 390, 25, 0.001) //falling + } + + }, + satellite() { + const boost1 = level.boost(5825, 235, 1400) + const elevator = level.elevator(4210, -1265, 380, 50, -3450) //, 0.003, { up: 0.01, down: 0.2 } + level.custom = () => { + boost1.query(); + + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(-250, -750, 420, 450) + ctx.fillStyle = "#d0d4d6" + ctx.fillRect(-300, -1900, 500, 1100) + ctx.fillRect(900, -2450, 450, 2050) + ctx.fillRect(2000, -2800, 450, 2500) + ctx.fillRect(3125, -3100, 450, 3300) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,20,40,0.25)" + ctx.fillRect(-250, -400, 1800, 775) + ctx.fillRect(1800, -275, 850, 775) + ctx.fillRect(5200, 125, 450, 200) + ctx.fillStyle = "rgba(0,20,40,0.1)" + ctx.fillRect(4000, -1200, 1050, 1500) + ctx.fillRect(4100, -3450, 600, 2250) + elevator.move() + }; + + level.setPosToSpawn(-100, 210); //normal spawn + spawn.mapRect(-150, 240, 100, 30); + level.exit.x = -100; + level.exit.y = -425; + spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 50); //exit bump + + level.defaultZoom = 1700 // 4500 // 1400 + simulation.zoomTransition(level.defaultZoom) + + powerUps.spawnStartingPowerUps(4900, -500); //1 per level + spawn.debris(1000, 20, 1800, 3); //16 debris per level //but less here because a few mobs die from laser + spawn.debris(4830, -1330, 850, 3); //16 debris per level + spawn.debris(3035, -3900, 1500, 3); //16 debris per level + + document.body.style.backgroundColor = "#dbdcde"; + + //spawn start building + spawn.mapRect(-350, -800, 100, 1100); + // spawn.mapRect(-300, -10, 500, 50); + spawn.mapRect(150, -510, 50, 365); + spawn.bodyRect(170, -140, 20, 163, 1, spawn.propsFriction); //door to starting room + spawn.mapVertex(175, 200, "625 0 300 0 425 -300 500 -300"); //entrance ramp + // spawn.mapRect(-300, 0, 1000, 300); //ground + spawn.mapRect(-350, 250, 6350, 300); //deeper ground + spawn.bodyRect(2100, 50, 80, 80); + spawn.bodyRect(2000, 50, 60, 60); + // spawn.bodyRect(1650, 50, 300, 200); + // spawn.mapRect(1800, Math.floor(Math.random() * 200), 850, 300); //stops above body from moving to right + spawn.mapVertex(2225, 250, "575 0 -575 0 -450 -100 450 -100"); //base + + //exit building + // spawn.mapRect(-100, -410, 100, 30); + spawn.mapRect(-350, -850, 550, 100); + spawn.mapRect(150, -800, 50, 110); + spawn.bodyRect(170, -690, 14, 180, 1, spawn.propsFriction); //door to exit room + spawn.mapRect(-300, -400, 500, 150); //far left starting ceiling + + //tall platform above exit + spawn.mapRect(-500, -1900, 400, 50); //super high shade + spawn.mapRect(0, -1900, 400, 50); //super high shade + spawn.mapRect(-150, -1350, 200, 25); //super high shade + spawn.bodyRect(140, -2100, 150, 200); //shield from laser + + //tall platform + spawn.mapVertex(1125, -450, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base + spawn.mapRect(150, -500, 1410, 100); //far left starting ceiling + spawn.mapRect(625, -2450, 1000, 50); //super high shade + spawn.bodyRect(1300, -3600, 150, 150); //shield from laser + //tall platform + spawn.mapVertex(2225, -250, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base + spawn.mapRect(1725, -2800, 1000, 50); //super high shade + spawn.mapRect(1790, -300, 870, 100); //far left starting ceiling + spawn.bodyRect(2400, -2950, 150, 150); //shield from laser + + //tall platform + spawn.mapVertex(3350, 175, "425 0 -425 0 -275 -300 275 -300"); //base + spawn.bodyRect(3350, -150, 200, 120); + spawn.mapRect(2850, -3150, 1000, 50); //super high shade + spawn.bodyRect(3675, -3470, 525, 20); //plank + spawn.bodyRect(3600, -3450, 200, 300); //plank support block + + //far right structure + spawn.mapRect(5200, -725, 100, 870); + spawn.mapRect(5300, -1075, 350, 1220); + + //structure bellow tall stairs + spawn.mapRect(3900, -300, 450, 50); + spawn.mapRect(4675, -375, 450, 50); + + // spawn.mapRect(4000, -1300, 1050, 100); + spawn.mapRect(4000, -1300, 200, 100); + spawn.mapRect(4600, -1300, 450, 100); + + //steep stairs + spawn.mapRect(4100, -2250, 100, 650); + spawn.mapRect(4100, -3450, 100, 850); //left top shelf + spawn.mapRect(4600, -3450, 100, 1850); + + spawn.randomSmallMob(4400, -3500); + spawn.randomSmallMob(4800, -800); + spawn.randomMob(800, -2600); + spawn.randomMob(700, -600, 0.3); + spawn.randomMob(3100, -3600, 0.3); + spawn.randomMob(3300, -1000, 0.3); + spawn.randomMob(4200, -250, 0.3); + spawn.randomMob(4900, -1500, 0.3); + spawn.randomMob(3800, 175, 0.4); + spawn.randomMob(5750, 125, 0.4); + spawn.randomMob(5900, -1500, 0.4); + spawn.randomMob(4700, -800, 0.4); + spawn.randomMob(1400, 200, 0.3); + spawn.randomMob(2850, 175, 0.4); + spawn.randomMob(2000, -2800, 0.4); + spawn.randomMob(2400, -400, 0.4); + spawn.randomMob(4475, -3550, 0.3); + spawn.randomGroup(5000, -2150, 1); + spawn.randomGroup(3700, -4100, 0.3); + spawn.randomGroup(2700, -1600, 0.1); + spawn.randomGroup(1600, -100, 0); + spawn.randomGroup(5000, -3900, -0.3); + if (simulation.difficulty > 1) { + if (Math.random() < 0.25) { + spawn.randomLevelBoss(2800, -1400); + } else if (Math.random() < 0.25) { + spawn.laserBoss(2900 + 300 * Math.random(), -2950 + 150 * Math.random()); + } else if (Math.random() < 0.33) { + spawn.laserBoss(1800 + 250 * Math.random(), -2600 + 150 * Math.random()); + } else if (Math.random() < 0.5) { + spawn.laserBoss(3500 + 250 * Math.random(), -2600 + 1000 * Math.random()); + } else { + spawn.laserBoss(600 + 200 * Math.random(), -2150 + 250 * Math.random()); + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(3950, -850) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + level.setPosToSpawn(100, 210); //-x + elevator.holdX = -elevator.holdX // flip the elevator horizontally + level.custom = () => { + boost1.query(); + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(250 - 420, -750, 420, 450) + ctx.fillStyle = "#d0d4d6" + ctx.fillRect(300 - 500, -1900, 500, 1100) + ctx.fillRect(-900 - 450, -2450, 450, 2050) + ctx.fillRect(-2000 - 450, -2800, 450, 2500) + ctx.fillRect(-3125 - 450, -3100, 450, 3300) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + elevator.move() + ctx.fillStyle = "rgba(0,20,40,0.25)" + ctx.fillRect(250 - 1800, -400, 1800, 775) + ctx.fillRect(-1800 - 850, -275, 850, 775) + ctx.fillRect(-5200 - 450, 125, 450, 200) + ctx.fillStyle = "rgba(0,20,40,0.1)" + ctx.fillRect(-4000 - 1050, -1200, 1050, 1500) + ctx.fillRect(-4100 - 600, -3450, 600, 2250) + }; + } + }, + rooftops() { + const elevator = level.elevator(1450, -990, 235, 45, -2000) + const boost1 = level.boost(4950, 0, 1100) + + level.custom = () => { + boost1.query(); + elevator.move(); + elevator.drawTrack(); + + ctx.fillStyle = "#d4f4f4" + if (isBackwards) { + ctx.fillRect(-650, -2300, 440, 300) + } else { + ctx.fillRect(3460, -700, 1090, 800) + } + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(710, -2225, 580, 225) + ctx.fillRect(3510, -1550, 330, 300) + ctx.fillRect(1735, -900, 1515, 1900) + ctx.fillRect(1735, -1550, 1405, 550) + ctx.fillRect(1860, -1950, 630, 350) + ctx.fillRect(-700, -1950, 2100, 2950) + ctx.fillRect(3400, 100, 2150, 900) + ctx.fillRect(4550, -725, 900, 725) + ctx.fillRect(3460, -1250, 1080, 550) + if (isBackwards) { + ctx.fillRect(3460, -700, 1090, 800) + } else { + ctx.fillRect(-650, -2300, 440, 300) + } + }; + + level.defaultZoom = 1700 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + + let isBackwards = false + if (Math.random() < 0.75) { + //normal direction start in top left + level.setPosToSpawn(-450, -2060); + level.exit.x = 3600; + level.exit.y = -300; + spawn.mapRect(3600, -285, 100, 50); //ground bump wall + //mobs that spawn in exit room + spawn.bodyRect(4850, -750, 300, 25, 0.6); // + spawn.randomSmallMob(4100, -100); + spawn.randomSmallMob(4600, -100); + spawn.randomMob(3765, -450, 0.3); + } else { + isBackwards = true + //reverse direction, start in bottom right + level.setPosToSpawn(3650, -325); + level.exit.x = -550; + level.exit.y = -2030; + spawn.mapRect(-550, -2015, 100, 50); //ground bump wall + } + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + spawn.debris(1650, -1800, 3800, 16); //16 debris per level + powerUps.spawnStartingPowerUps(2450, -1675); + + //spawn.mapRect(-700, 0, 6250, 100); //ground + spawn.mapRect(3400, 0, 2150, 100); //ground + spawn.mapRect(-700, -2000, 2125, 50); //Top left ledge + spawn.bodyRect(1300, -2125, 50, 125, 0.8); + spawn.bodyRect(1307, -2225, 50, 100, 0.8); + spawn.mapRect(-700, -2350, 50, 400); //far left starting left wall + spawn.mapRect(-700, -2010, 500, 50); //far left starting ground + spawn.mapRect(-700, -2350, 500, 50); //far left starting ceiling + spawn.mapRect(-250, -2350, 50, 200); //far left starting right part of wall + spawn.bodyRect(-240, -2150, 30, 36); //door to starting room + spawn.bodyRect(-240, -2115, 30, 36); //door to starting room + spawn.bodyRect(-240, -2080, 30, 35); //door to starting room + spawn.bodyRect(-240, -2045, 30, 35); //door to starting room + spawn.mapRect(1850, -2000, 650, 50); + spawn.bodyRect(200, -2150, 80, 220, 0.8); + spawn.mapRect(700, -2275, 600, 50); + spawn.mapRect(1000, -1350, 410, 50); + spawn.bodyRect(1050, -2350, 30, 30, 0.8); + // spawn.bodyRect(1625, -1100, 100, 75); + // spawn.bodyRect(1350, -1025, 400, 25); // ground plank + spawn.mapRect(-725, -1000, 2150, 100); //lower left ledge + spawn.bodyRect(350, -1100, 200, 100, 0.8); + spawn.bodyRect(370, -1200, 100, 100, 0.8); + spawn.bodyRect(360, -1300, 100, 100, 0.8); + spawn.bodyRect(950, -1050, 300, 50, 0.8); + spawn.bodyRect(-575, -1150, 125, 150, 0.8); + spawn.mapRect(1710, -1000, 1565, 100); //middle ledge + spawn.mapRect(3400, -1000, 75, 25); + spawn.bodyRect(2600, -1950, 100, 250, 0.8); + spawn.bodyRect(2700, -1125, 125, 125, 0.8); + spawn.bodyRect(2710, -1250, 125, 125, 0.8); + spawn.bodyRect(2705, -1350, 75, 100, 0.8); + spawn.mapRect(3500, -1600, 350, 50); + spawn.mapRect(1725, -1600, 1435, 50); + spawn.bodyRect(3100, -1015, 375, 15); + spawn.bodyRect(3500, -850, 75, 125, 0.8); + spawn.mapRect(3450, -1000, 50, 580); //left building wall + spawn.bodyRect(3460, -420, 30, 144); + spawn.mapRect(5450, -775, 100, 875); //right building wall + spawn.bodyRect(3925, -1400, 100, 150, 0.8); + spawn.mapRect(3450, -1250, 1090, 50); + // spawn.mapRect(3450, -1225, 50, 75); + spawn.mapRect(4500, -1250, 50, 415); + spawn.mapRect(3450, -725, 1500, 50); + spawn.mapRect(5100, -725, 400, 50); + spawn.mapRect(4500, -735, 50, 635); + spawn.bodyRect(4500, -100, 50, 100); + spawn.mapRect(4500, -885, 100, 50); + spawn.spawnStairs(3800, 0, 3, 150, 206); //stairs top exit + spawn.mapRect(3400, -275, 450, 275); //exit platform + + spawn.randomSmallMob(2200, -1775); + spawn.randomSmallMob(4000, -825); + spawn.randomSmallMob(-350, -3400); + spawn.randomMob(4250, -1350, 0.8); + spawn.randomMob(2550, -1350, 0.8); + spawn.randomMob(1875, -1075, 0.3); + spawn.randomMob(1120, -1200, 0.3); + spawn.randomMob(3000, -1150, 0.2); + spawn.randomMob(3200, -1150, 0.3); + spawn.randomMob(3300, -1750, 0.3); + spawn.randomMob(3650, -1350, 0.3); + spawn.randomMob(3600, -1800, 0.1); + spawn.randomMob(5200, -100, 0.3); + spawn.randomMob(5275, -900, 0.2); + spawn.randomMob(0, -1075, 0.3); + spawn.randomGroup(600, -1575, 0); + spawn.randomGroup(2225, -1325, 0.4); + spawn.randomGroup(4900, -1200, 0); + if (simulation.difficulty > 1) spawn.randomLevelBoss(3200, -1900); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(2175, -2425) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + + boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + elevator.holdX = -elevator.holdX // flip the elevator horizontally + + if (isBackwards) { + level.setPosToSpawn(-3650, -325); //-x + } else { + level.setPosToSpawn(450, -2060); //-x + } + level.custom = () => { + boost1.query(); + elevator.move(); + elevator.drawTrack(); + + ctx.fillStyle = "#d4f4f4" + if (isBackwards) { + ctx.fillRect(650 - 440, -2300, 440, 300) + } else { + ctx.fillRect(-3460 - 1090, -700, 1090, 800) + } + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-710 - 580, -2225, 580, 225) + ctx.fillRect(-3510 - 330, -1550, 330, 300) + ctx.fillRect(-1735 - 1515, -900, 1515, 1900) + ctx.fillRect(-1735 - 1405, -1550, 1405, 550) + ctx.fillRect(-1860 - 630, -1950, 630, 350) + ctx.fillRect(700 - 2100, -1950, 2100, 2950) + ctx.fillRect(-3400 - 2150, 100, 2150, 900) + ctx.fillRect(-4550 - 900, -725, 900, 725) + ctx.fillRect(-3460 - 1080, -1250, 1080, 550) + if (isBackwards) { + ctx.fillRect(-3460 - 1090, -700, 1090, 800) + } else { + ctx.fillRect(650 - 440, -2300, 440, 300) + } + }; + } + }, + aerie() { + const boost1 = level.boost(-425, 100, 1400) + const boost2 = level.boost(5350, 275, 2850); + + level.custom = () => { + boost1.query(); + boost2.query(); + if (backwards) { + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(-275, -1275, 425, 300) + } else { + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(3750, -3650, 550, 400) + } + ctx.fillStyle = "#c7c7ca" + ctx.fillRect(4200, -2200, 100, 2600) + // ctx.fillStyle = "#c7c7ca" + ctx.fillRect(-100, -1000, 1450, 1400) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + if (backwards) { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(3750, -3650, 550, 400) + } else { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-275, -1275, 425, 300) + } + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(3700, -3150, 1100, 950) + ctx.fillRect(2000, -1110, 450, 1550) + + ctx.fillStyle = "rgba(0,0,0,0.04)" + ctx.beginPath() + ctx.moveTo(-100, -900) + ctx.lineTo(300, -900) + ctx.lineTo(150, 100) + ctx.lineTo(-100, 100) + + ctx.moveTo(600, -900) + ctx.lineTo(1350, -900) + ctx.lineTo(1350, 100) + ctx.lineTo(750, 100) + ctx.fill() + }; + + // simulation.difficulty = 4; //for testing to simulate possible mobs spawns + level.defaultZoom = 2100 + simulation.zoomTransition(level.defaultZoom) + + const backwards = (Math.random() < 0.25 && simulation.difficulty > 8) ? true : false; + if (backwards) { + level.setPosToSpawn(4000, -3300); //normal spawn + level.exit.x = -100; + level.exit.y = -1025; + } else { + level.setPosToSpawn(-50, -1050); //normal spawn + level.exit.x = 3950; + level.exit.y = -3275; + } + + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 20); + + powerUps.spawnStartingPowerUps(1075, -550); + document.body.style.backgroundColor = "#dcdcde"; + + // starting room + spawn.mapRect(-300, -1000, 600, 100); + spawn.mapRect(-300, -1300, 450, 50); + spawn.mapRect(-300, -1300, 50, 350); + if (!backwards) spawn.bodyRect(100, -1250, 200, 240); //remove on backwards + //left building + spawn.mapRect(-100, -975, 100, 975); + spawn.mapRect(-500, 100, 1950, 400); + spawn.mapRect(600, -1000, 750, 100); + spawn.mapRect(900, -500, 550, 100); + spawn.mapRect(1250, -975, 100, 375); + spawn.bodyRect(1250, -600, 100, 100, 0.7); + spawn.mapRect(1250, -450, 100, 450); + spawn.bodyRect(1250, -1225, 100, 200, 0.7); //remove on backwards + spawn.bodyRect(1200, -1025, 350, 35); //remove on backwards + //middle super tower + if (backwards) { + spawn.bodyRect(2000, -800, 700, 35); + } else { + spawn.bodyRect(1750, -800, 700, 35); + } + spawn.mapVertex(2225, -2100, "0 0 450 0 300 -2500 150 -2500") + spawn.mapRect(2000, -700, 450, 300); + spawn.bodyRect(2360, -450, 100, 300, 0.6); + spawn.mapRect(2000, -75, 450, 275); + spawn.bodyRect(2450, 150, 150, 150, 0.4); + spawn.mapRect(1550, 300, 4600, 200); //ground + // spawn.mapRect(6050, -700, 450, 1200); + spawn.mapRect(6050, -1060, 450, 1560); + spawn.mapVertex(6275, -2100, "0 0 450 0 300 -2500 150 -2500") + + //right tall tower + spawn.mapRect(3700, -3200, 100, 800); + spawn.mapRect(4700, -2910, 100, 510); + spawn.mapRect(3700, -2600, 300, 50); + spawn.mapRect(4100, -2900, 900, 50); + spawn.mapRect(3450, -2300, 750, 100); + spawn.mapRect(4300, -2300, 750, 100); + spawn.mapRect(4150, -1600, 200, 25); + spawn.mapRect(4150, -700, 200, 25); + //exit room on top of tower + spawn.mapRect(3700, -3700, 600, 50); + spawn.mapRect(3700, -3700, 50, 500); + spawn.mapRect(4250, -3700, 50, 300); + spawn.mapRect(3700, -3250, 1100, 100); + + spawn.randomGroup(350, -500, 1) + spawn.randomSmallMob(-225, 25); + spawn.randomSmallMob(2100, -900); + + spawn.randomSmallMob(4000, -250); + spawn.randomSmallMob(4450, -3000); + spawn.randomSmallMob(5600, 100); + spawn.randomMob(4275, -2600, 0.8); + spawn.randomMob(1050, -700, 0.8) + spawn.randomMob(6050, -850, 0.7); + spawn.randomMob(2150, -300, 0.6) + spawn.randomMob(3900, -2700, 0.8); + spawn.randomMob(3600, -500, 0.8); + spawn.randomMob(3400, -200, 0.8); + // spawn.randomMob(1650, -1300, 0.7) + spawn.randomMob(425, 0, 0.7); + spawn.randomMob(4100, -50, 0.7); + spawn.randomMob(4100, -50, 0.5); + spawn.randomMob(1700, -50, 0.3) + spawn.randomMob(2350, -900, 0.3) + spawn.randomMob(4700, -150, 0.2); + spawn.randomGroup(4000, -350, 0.6); + spawn.randomGroup(2750, -550, 0.1); + spawn.randomMob(2175, -925, 0.5); + spawn.randomMob(2750, 100, 0.5); + spawn.randomMob(4250, -1725, 0.5); + spawn.randomMob(3575, -2425, 0.5); + spawn.randomMob(3975, -3900, 0.5); + spawn.randomMob(1725, 125, 0.5); + if (simulation.difficulty > 1) { + if (Math.random() < 0.5) { + spawn.randomLevelBoss(4250, -250); + spawn.debris(-250, 50, 1650, 2); //16 debris per level + spawn.debris(2475, 0, 750, 2); //16 debris per level + spawn.debris(3450, 0, 2000, 16); //16 debris per level + spawn.debris(3500, -2350, 1500, 2); //16 debris per level + } else { + powerUps.chooseRandomPowerUp(4000, 200); + powerUps.chooseRandomPowerUp(4000, 200); + //floor below right tall tower + spawn.bodyRect(3000, 50, 150, 250, 0.9); + spawn.bodyRect(4500, -500, 300, 250, 0.7); + spawn.bodyRect(3500, -100, 100, 150, 0.7); + spawn.bodyRect(4200, -500, 110, 30, 0.7); + spawn.bodyRect(3800, -500, 150, 130, 0.7); + spawn.bodyRect(4000, 50, 200, 150, 0.9); + spawn.bodyRect(4500, 50, 300, 200, 0.9); + spawn.bodyRect(4200, -350, 200, 50, 0.9); + spawn.bodyRect(4700, -350, 50, 200, 0.9); + spawn.bodyRect(4900, -100, 300, 300, 0.7); + spawn.suckerBoss(4500, -400); + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(5350, -325) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + + boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100 + boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100 + + + if (backwards) { + level.setPosToSpawn(-4000, -3300); //-x + } else { + level.setPosToSpawn(50, -1050); //-x + } + level.custom = () => { + boost1.query(); + boost2.query(); + if (backwards) { + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(275 - 425, -1275, 425, 300) + } else { + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(-3750 - 550, -3650, 550, 400) + } + ctx.fillStyle = "#c7c7ca" + ctx.fillRect(-4200 - 100, -2200, 100, 2600) + // ctx.fillStyle = "#c7c7ca" + ctx.fillRect(100 - 1450, -1000, 1450, 1400) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + if (backwards) { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-3750 - 550, -3650, 550, 400) + } else { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(275 - 425, -1275, 425, 300) + } + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-3700 - 1100, -3150, 1100, 950) + ctx.fillRect(-2000 - 450, -1110, 450, 1550) + ctx.fillStyle = "rgba(0,0,0,0.04)" + ctx.beginPath() + ctx.moveTo(100, -900) + ctx.lineTo(-300, -900) + ctx.lineTo(-150, 100) + ctx.lineTo(100, 100) + ctx.moveTo(-600, -900) + ctx.lineTo(-1350, -900) + ctx.lineTo(-1350, 100) + ctx.lineTo(-750, 100) + ctx.fill() + }; + } + }, + skyscrapers() { + const boost1 = level.boost(475, 0, 1300) + const boost2 = level.boost(4450, 0, 1300); + level.custom = () => { + boost1.query(); + boost2.query(); + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(1350, -2100, 400, 250) + ctx.fillStyle = "#d4d4d7" + ctx.fillRect(3350, -1300, 50, 1325) + ctx.fillRect(1300, -1800, 750, 1800) + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(2500, -1100, 450, 250) + ctx.fillRect(2400, -550, 600, 150) + ctx.fillRect(2550, -1650, 250, 200) + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(700, -110, 400, 110) + ctx.fillRect(3800, -110, 400, 110) + ctx.fillStyle = "rgba(0,0,0,0.15)" + ctx.fillRect(-250, -300, 450, 300) + }; + level.setPosToSpawn(-50, -60); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 1500; + level.exit.y = -1875; + level.defaultZoom = 2000 + simulation.zoomTransition(level.defaultZoom) + powerUps.spawnStartingPowerUps(1475, -1175); + spawn.debris(750, -2200, 3700, 16); //16 debris per level + document.body.style.backgroundColor = "#dcdcde"; + + spawn.mapRect(-300, 0, 5100, 300); //***********ground + spawn.mapRect(-300, -350, 50, 400); //far left starting left wall + spawn.mapRect(-300, -10, 500, 50); //far left starting ground + spawn.mapRect(-300, -350, 500, 50); //far left starting ceiling + spawn.mapRect(150, -350, 50, 200); //far left starting right part of wall + spawn.bodyRect(170, -130, 14, 140, 1, spawn.propsFriction); //door to starting room + spawn.mapRect(700, -1100, 400, 990); //far left building + spawn.mapRect(1600, -400, 1500, 500); //long center building + spawn.mapRect(1345, -1100, 250, 25); //left platform + spawn.mapRect(1755, -1100, 250, 25); //right platform + spawn.mapRect(1300, -1850, 800, 50); //left higher platform + spawn.mapRect(1300, -2150, 50, 350); //left higher platform left edge wall + spawn.mapRect(1300, -2150, 450, 50); //left higher platform roof + spawn.mapRect(1500, -1860, 100, 50); //ground bump wall + spawn.mapRect(2400, -850, 600, 300); //center floating large square + //spawn.bodyRect(2500, -1100, 25, 250); //wall before chasers + spawn.mapRect(2500, -1450, 450, 350); //higher center floating large square + spawn.mapRect(2500, -1675, 50, 300); //left wall on higher center floating large square + spawn.mapRect(2500, -1700, 300, 50); //roof on higher center floating large square + spawn.mapRect(3275, -750, 200, 25); //ledge by far right building + spawn.mapRect(3275, -1300, 200, 25); //higher ledge by far right building + spawn.mapRect(3800, -1100, 400, 990); //far right building + + spawn.bodyRect(3200, -1375, 300, 25, 0.9); + spawn.bodyRect(1825, -1875, 400, 25, 0.9); + // spawn.bodyRect(1800, -575, 250, 150, 0.8); + spawn.bodyRect(1800, -600, 110, 150, 0.8); + spawn.bodyRect(2557, -450, 35, 55, 0.7); + spawn.bodyRect(2957, -450, 30, 15, 0.7); + spawn.bodyRect(2900, -450, 60, 45, 0.7); + spawn.bodyRect(915, -1200, 60, 100, 0.95); + spawn.bodyRect(925, -1300, 50, 100, 0.95); + if (Math.random() < 0.9) { + spawn.bodyRect(2300, -1720, 400, 20); + spawn.bodyRect(2590, -1780, 80, 80); + } + spawn.bodyRect(2925, -1100, 25, 250, 0.8); + spawn.bodyRect(3325, -1550, 50, 200, 0.3); + if (Math.random() < 0.8) { + spawn.bodyRect(1400, -75, 200, 75); //block to get up ledge from ground + spawn.bodyRect(1525, -125, 50, 50); //block to get up ledge from ground + } + spawn.bodyRect(1025, -1110, 400, 25, 0.9); //block on far left building + spawn.bodyRect(1425, -1110, 115, 25, 0.9); //block on far left building + spawn.bodyRect(1540, -1110, 300, 25, 0.9); //block on far left building + + spawn.randomMob(-100, -1300, 0.5); + spawn.randomSmallMob(1850, -600); + spawn.randomSmallMob(3200, -100); + spawn.randomSmallMob(4450, -100); + spawn.randomSmallMob(2700, -475); + spawn.randomMob(2650, -975, 0.8); + spawn.randomMob(2650, -1550, 0.8); + spawn.randomMob(4150, -200, 0.15); + spawn.randomMob(1700, -1300, 0.2); + spawn.randomMob(1850, -1950, 0.25); + spawn.randomMob(2610, -1880, 0.25); + spawn.randomMob(3350, -950, 0.25); + spawn.randomMob(1690, -2250, 0.25); + spawn.randomMob(2200, -600, 0.2); + spawn.randomMob(850, -1300, 0.25); + spawn.randomMob(-100, -1700, -0.2); + spawn.randomGroup(3700, -1500, 0.4); + spawn.randomGroup(1700, -900, 0.4); + if (simulation.difficulty > 1) spawn.randomLevelBoss(2800 + 200 * Math.random(), -2200 + 200 * Math.random()); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(4000, -1825) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100 + boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100 + + level.setPosToSpawn(50, -60); //-x + level.custom = () => { + boost1.query(); + boost2.query(); + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(-1350 - 400, -2100, 400, 250) + ctx.fillStyle = "#d4d4d7" + ctx.fillRect(-3350 - 50, -1300, 50, 1325) + ctx.fillRect(-1300 - 750, -1800, 750, 1800) + + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-2500 - 450, -1100, 450, 250) + ctx.fillRect(-2400 - 600, -550, 600, 150) + ctx.fillRect(-2550 - 250, -1650, 250, 200) + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(-700 - 400, -110, 400, 110) + ctx.fillRect(-3800 - 400, -110, 400, 110) + ctx.fillStyle = "rgba(0,0,0,0.15)" + ctx.fillRect(250 - 450, -300, 450, 300) + }; + } + }, + highrise() { + const elevator1 = level.elevator(-790, -190, 180, 25, -1150, 0.0025, { + up: 0.01, + down: 0.2 + }, true) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + elevator1.addConstraint(); + // const button1 = level.button(-500, -200) + const toggle1 = level.toggle(-300, -200) //(x,y,isOn,isLockOn = true/false) + + const elevator2 = level.elevator(-3630, -970, 180, 25, -1740, 0.004) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + elevator2.addConstraint(); + // const button2 = level.button(-3100, -1330) + const toggle2 = level.toggle(-3100, -1330) //(x,y,isOn, isLockOn = true/false) + + + level.custom = () => { + // ctx.fillStyle = "#d0d0d2" + // ctx.fillRect(-2475, -2450, 25, 750) + // ctx.fillRect(-2975, -2750, 25, 600) + // ctx.fillRect(-3375, -2875, 25, 725) + ctx.fillStyle = "#cff" //exit + ctx.fillRect(-4425, -3050, 425, 275) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + // button1.draw(); + toggle1.query(); + if (!toggle1.isOn) { + if (elevator1.isOn) { + elevator1.isOn = false + elevator1.frictionAir = 0.2 + elevator1.addConstraint(); + } + } else if (!elevator1.isOn) { + elevator1.isOn = true + elevator1.isUp = false + elevator1.removeConstraint(); + elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + if (elevator1.isOn) { + elevator1.move(); + ctx.fillStyle = "#444" + } else { + ctx.fillStyle = "#aaa" + } + ctx.fillRect(-700, -1140, 1, 975) + + toggle2.query(); + // button2.draw(); + if (!toggle2.isOn) { + if (elevator2.isOn) { + elevator2.isOn = false + elevator2.frictionAir = 0.2 + elevator2.addConstraint(); + } + } else if (!elevator2.isOn) { + elevator2.isOn = true + elevator2.isUp = false + elevator2.removeConstraint(); + elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + + if (elevator2.isOn) { + elevator2.move(); + ctx.fillStyle = "#444" + } else { + ctx.fillStyle = "#aaa" + } + ctx.fillRect(-3540, -1720, 1, 770) + + ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section + ctx.fillRect(-4450, -750, 800, 200) + ctx.fillStyle = "rgba(0,0,0,0.12)" + ctx.fillRect(-2500, -1975, 150, 300); + ctx.fillRect(-1830, -1150, 2030, 1150) + ctx.fillRect(-3410, -2150, 495, 1550) + ctx.fillRect(-2585, -1675, 420, 1125) + ctx.fillRect(-1650, -1575, 750, 450) + }; + + level.setPosToSpawn(-300, -700); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = -4275; + level.exit.y = -2805; + + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + + powerUps.spawnStartingPowerUps(-2550, -700); + document.body.style.backgroundColor = "#dcdcde" //"#fafcff"; + + spawn.debris(-2325, -1825, 2400); //16 debris per level + spawn.debris(-2625, -600, 600, 5); //16 debris per level + spawn.debris(-2000, -60, 1200, 5); //16 debris per level + + //3 platforms that lead to exit + // spawn.mapRect(-3440, -2875, 155, 25); + // spawn.mapRect(-3025, -2775, 125, 25); + // spawn.mapRect(-2525, -2475, 125, 25); + // spawn.bodyRect(-2600, -2500, 225, 20, 0.7); + // spawn.bodyRect(-3350, -2900, 25, 25, 0.5); + // spawn.bodyRect(-3400, -2950, 50, 75, 0.5); + + powerUps.spawn(-4300, -700, "heal"); + powerUps.spawn(-4200, -700, "ammo"); + powerUps.spawn(-4000, -700, "ammo"); + spawn.mapRect(-4450, -1000, 100, 500); + spawn.bodyRect(-3300, -750, 150, 150); + + //building 1 + spawn.bodyRect(-1000, -675, 25, 25); + spawn.mapRect(-2225, 0, 2475, 150); + spawn.mapRect(175, -1000, 75, 1100); + spawn.mapRect(-600, -1075, 50, 475); + spawn.mapRect(-600, -650, 625, 50); + spawn.mapRect(-1300, -650, 500, 50); + spawn.bodyRect(-75, -300, 50, 50); + + spawn.mapRect(-600, -200, 500, 250); //ledge for boarding elevator + spawn.bodyRect(-500, -300, 100, 100, 0.6); //a nice block near the elevator + + spawn.bodyRect(-425, -1375, 400, 225); + spawn.mapRect(-925, -1575, 50, 475); + spawn.bodyRect(-1475, -1275, 250, 125); + + // spawn.mapRect(-1650, -1575, 600, 50); + // spawn.mapRect(-1875, -1575, 850, 50); + spawn.mapRect(-1675, -1575, 650, 50); + spawn.mapRect(-600, -1150, 850, 175); + spawn.mapRect(-1850, -1150, 1050, 175); + spawn.bodyRect(-1907, -1600, 550, 25); + if (simulation.difficulty < 4) { + spawn.bodyRect(-1600, -125, 125, 125); + spawn.bodyRect(-1560, -200, 75, 75); + } else { + spawn.bodyRect(-1200, -125, 125, 125); + spawn.bodyRect(-1160, -200, 75, 75); + } + //building 2 + spawn.mapRect(-4450, -600, 2300, 750); + spawn.mapRect(-2225, -450, 175, 550); + // spawn.mapRect(-2600, -975, 450, 50); + spawn.mapRect(-3425, -1325, 525, 75); + spawn.mapRect(-3425, -2200, 525, 50); + spawn.mapRect(-2600, -1700, 450, 50); + // spawn.mapRect(-2600, -2450, 450, 50); + spawn.bodyRect(-2275, -2700, 50, 60); + + // spawn.bodyRect(-2560, -1925, 250, 225); + // spawn.mapRect(-2525, -2025, 125, 25); + // spawn.mapRect(-2525, -1900, 125, 225); + // spawn.mapRect(-2600, -1975, 250, 25); + spawn.mapRect(-2515, -2000, 180, 50); + + spawn.bodyRect(-3410, -1425, 50, 50); + spawn.bodyRect(-3390, -1525, 40, 60); + // spawn.bodyRect(-3245, -1425, 100, 100); + //building 3 + spawn.mapRect(-4450, -1750, 800, 1050); + // spawn.mapRect(-3850, -2000, 125, 400); + spawn.mapRect(-4000, -2390, 200, 800); + // spawn.mapRect(-4450, -2650, 475, 1000); + spawn.mapRect(-4450, -2775, 475, 1125); + spawn.bodyRect(-3715, -2050, 50, 50); + // spawn.bodyRect(-3570, -1800, 50, 50); + spawn.bodyRect(-2970, -2250, 50, 50); + spawn.bodyRect(-3080, -2250, 40, 40); + spawn.bodyRect(-3420, -650, 50, 50); + + //exit + spawn.mapRect(-4450, -3075, 25, 300); + spawn.mapRect(-4450, -3075, 450, 25); + spawn.mapRect(-4025, -3075, 25, 100); + spawn.mapRect(-4275, -2785, 100, 25); + spawn.bodyRect(-3900, -2400, 50, 50); + + //mobs + spawn.randomMob(-2500, -2700, 1); + spawn.randomMob(-3200, -750, 1); + spawn.randomMob(-1875, -775, 0.2); + spawn.randomMob(-950, -1675, 0.2); + spawn.randomMob(-1525, -1750, 0.2); + spawn.randomMob(-1375, -1400, 0.2); + spawn.randomMob(-1625, -1275, 0.2); + spawn.randomMob(-1900, -1250, 0.2); + spawn.randomMob(-2250, -1850, 0.2); + spawn.randomMob(-2475, -2200, 0.2); + spawn.randomMob(-3000, -1475, 0.2); + spawn.randomMob(-3850, -2500, 0.2); + spawn.randomMob(-3650, -2125, 0.2); + spawn.randomMob(-4010, -3200, 0.2); + spawn.randomMob(-3500, -1825, 0.2); + spawn.randomMob(-975, -100, 0); + spawn.randomMob(-1050, -725, 0.2); + spawn.randomMob(-1525, -100, 0); + spawn.randomMob(-525, -1700, -0.1); + spawn.randomMob(-125, -1500, -0.1); + spawn.randomMob(-325, -1900, -0.1); + spawn.randomMob(-550, -100, -0.1); + spawn.randomGroup(-3250, -2700, 0.2); + spawn.randomGroup(-2450, -1100, 0); + + if (simulation.difficulty > 1) spawn.randomLevelBoss(-2400, -2650); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(-1825, -1975) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + level.setPosToSpawn(300, -700); //-x + elevator1.holdX = -elevator1.holdX // flip the elevator horizontally + elevator1.removeConstraint(); + elevator1.addConstraint(); + elevator2.holdX = -elevator2.holdX // flip the elevator horizontally + elevator2.removeConstraint(); + elevator2.addConstraint(); + + level.custom = () => { + ctx.fillStyle = "#cff" //exit + ctx.fillRect(4425 - 425, -3050, 425, 275) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + toggle1.query(); + if (!toggle1.isOn) { + if (elevator1.isOn) { + elevator1.isOn = false + elevator1.frictionAir = 0.2 + elevator1.addConstraint(); + } + } else if (!elevator1.isOn) { + elevator1.isOn = true + elevator1.isUp = false + elevator1.removeConstraint(); + elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + if (elevator1.isOn) { + elevator1.move(); + ctx.fillStyle = "#444" + ctx.fillRect(700 - 1, -1140, 1, 975) + } else { + ctx.fillStyle = "#aaa" + ctx.fillRect(700 - 1, -1140, 1, 975) + } + + toggle2.query(); + if (!toggle2.isOn) { + if (elevator2.isOn) { + elevator2.isOn = false + elevator2.frictionAir = 0.2 + elevator2.addConstraint(); + } + } else if (!elevator2.isOn) { + elevator2.isOn = true + elevator2.isUp = false + elevator2.removeConstraint(); + elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + + if (elevator2.isOn) { + elevator2.move(); + ctx.fillStyle = "#444" + ctx.fillRect(3540 - 1, -1720, 1, 740) + } else { + ctx.fillStyle = "#aaa" + ctx.fillRect(3540 - 1, -1720, 1, 740) + } + + ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section + ctx.fillRect(4450 - 800, -750, 800, 200) + ctx.fillStyle = "rgba(0,0,0,0.12)" + ctx.fillRect(2500 - 150, -1975, 150, 300); + ctx.fillRect(1830 - 2030, -1150, 2030, 1150) + ctx.fillRect(3410 - 495, -2150, 495, 1550) + ctx.fillRect(2585 - 420, -1675, 420, 1125) + ctx.fillRect(1650 - 750, -1575, 750, 450) + }; + } + }, + warehouse() { + level.custom = () => { + ctx.fillStyle = "#444" //light fixtures + ctx.fillRect(-920, -505, 40, 10) + ctx.fillRect(-920, 95, 40, 10) + ctx.fillRect(180, 95, 40, 10) + ctx.fillRect(-20, 695, 40, 10) + ctx.fillRect(-2320, 945, 40, 10) + + ctx.fillStyle = "#cff" //exit + ctx.fillRect(300, -250, 350, 250) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + + const lightingPath = new Path2D() //pre-draw the complex lighting path to save processing + lightingPath.moveTo(-1800, -500) + lightingPath.lineTo(-910, -500) //3rd floor light + lightingPath.lineTo(-1300, 0) + lightingPath.lineTo(-500, 0) + lightingPath.lineTo(-890, -500) + lightingPath.lineTo(-175, -500) + lightingPath.lineTo(-175, -250) + lightingPath.lineTo(175, -250) + lightingPath.lineTo(175, 0) + lightingPath.lineTo(-910, 100) //2nd floor light left + lightingPath.lineTo(-1300, 600) + lightingPath.lineTo(-500, 600) + lightingPath.lineTo(-890, 100) + lightingPath.lineTo(190, 100) //2nd floor light right + lightingPath.lineTo(-200, 600) + lightingPath.lineTo(600, 600) + lightingPath.lineTo(210, 100) + lightingPath.lineTo(1100, 100) + lightingPath.lineTo(1100, 1400) + lightingPath.lineTo(600, 1400) //1st floor light right + lightingPath.lineTo(10, 700) + lightingPath.lineTo(-10, 700) + lightingPath.lineTo(-600, 1400) + lightingPath.lineTo(-1950, 1400) //1st floor light left + lightingPath.lineTo(-2290, 950) + lightingPath.lineTo(-2310, 950) + lightingPath.lineTo(-2650, 1400) + lightingPath.lineTo(-3025, 1400) + lightingPath.lineTo(-3025, 150) + lightingPath.lineTo(-2590, 150) + lightingPath.lineTo(-2600, -150) + lightingPath.lineTo(-1800, -150) + lightingPath.lineTo(-1800, -500) //top left end/start of path + + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights + ctx.fill(lightingPath); + }; + + level.setPosToSpawn(25, -55); //normal spawn + level.exit.x = 425; + level.exit.y = -30; + + level.defaultZoom = 1300 + simulation.zoomTransition(level.defaultZoom) + + spawn.debris(-2250, 1330, 3000, 6); //16 debris per level + spawn.debris(-3000, -800, 3280, 6); //16 debris per level + spawn.debris(-1400, 410, 2300, 5); //16 debris per level + powerUps.spawnStartingPowerUps(25, 500); + document.body.style.backgroundColor = "#dcdcde" //"#f2f5f3"; + + spawn.mapRect(-1500, 0, 2750, 100); + spawn.mapRect(175, -270, 125, 300); + spawn.mapRect(-1900, -600, 1775, 100); + spawn.mapRect(-1900, -550, 100, 1250); + //house + spawn.mapRect(-225, -550, 100, 400); + spawn.mapRect(-225, -10, 400, 50); + spawn.mapRect(-25, -20, 100, 50); + + //exit house + spawn.mapRect(300, -10, 350, 50); + spawn.mapRect(-150, -350, 800, 100); + spawn.mapRect(600, -275, 50, 75); + spawn.mapRect(425, -20, 100, 25); + // spawn.mapRect(-1900, 600, 2700, 100); + spawn.mapRect(1100, 0, 150, 1500); + spawn.mapRect(-3150, 1400, 4400, 100); + spawn.mapRect(-2375, 875, 1775, 75); + spawn.mapRect(-1450, 865, 75, 435); + spawn.mapRect(-1450, 662, 75, 100); + spawn.bodyRect(-1418, 773, 11, 102, 1, spawn.propsFriction); //blocking path + spawn.mapRect(-3150, 50, 125, 1450); + spawn.mapRect(-2350, 600, 3150, 100); + spawn.mapRect(-2125, 400, 250, 275); + // spawn.mapRect(-1950, -400, 100, 25); + spawn.mapRect(-3150, 50, 775, 100); + spawn.mapRect(-2600, -250, 775, 100); + + let isElevators = false + let elevator1, elevator2, elevator3 + if (Math.random() < 0.5) { + isElevators = true + elevator1 = level.elevator(-1780, 500, 260, 40, 7, 0.0003) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + elevator2 = level.elevator(820, 1300, 260, 40, 607, 0.0003) + elevator3 = level.elevator(-2850, 1250, 160, 40, 600, 0.007) + if (simulation.isHorizontalFlipped) { + spawn.mapVertex(-2900, 225, "0 0 0 -500 -500 -500") + } else { + spawn.mapVertex(-2900, 225, "0 0 0 -500 500 -500") + } + spawn.mapRect(-3050, 1175, 175, 300); + spawn.bodyRect(-2375, 1300, 100, 100); + spawn.bodyRect(-2325, 1250, 50, 50); + spawn.bodyRect(-2275, 1350, 125, 50); + + + level.custom = () => { + elevator1.move(); + elevator1.drawTrack(); + elevator2.move(); + elevator2.drawTrack(); + elevator3.move(); + elevator3.drawTrack(); + + ctx.fillStyle = "#444" //light fixtures + ctx.fillRect(-920, -505, 40, 10) + ctx.fillRect(-920, 95, 40, 10) + ctx.fillRect(180, 95, 40, 10) + ctx.fillRect(-20, 695, 40, 10) + ctx.fillRect(-2320, 945, 40, 10) + + ctx.fillStyle = "#cff" //exit + ctx.fillRect(300, -250, 350, 250) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + } else { + spawn.mapRect(-2950, 1250, 175, 250); + spawn.mapRect(-3050, 1100, 150, 400); + + spawn.bodyRect(-1450, -125, 125, 125, 1, spawn.propsSlide); //weight + spawn.bodyRect(-1800, 0, 300, 100, 1, spawn.propsHoist); //hoist + cons[cons.length] = Constraint.create({ + pointA: { + x: -1650, + y: -500 + }, + bodyB: body[body.length - 1], + stiffness: 0.0001815, + length: 1 + }); + Composite.add(engine.world, cons[cons.length - 1]); + + spawn.bodyRect(600, 525, 125, 125, 1, spawn.propsSlide); //weight + spawn.bodyRect(800, 600, 300, 100, 1, spawn.propsHoist); //hoist + cons[cons.length] = Constraint.create({ + pointA: { + x: 950, + y: 100 + }, + bodyB: body[body.length - 1], + stiffness: 0.0001815, + length: 1 + }); + Composite.add(engine.world, cons[cons.length - 1]); + + spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight + spawn.bodyRect(-2550, 1200, 150, 150, 1, spawn.propsSlide); //weight + spawn.bodyRect(-2763, 1300, 350, 100, 1, spawn.propsHoist); //hoist + cons[cons.length] = Constraint.create({ + pointA: { + x: -2575, + y: 150 + }, + bodyB: body[body.length - 1], + stiffness: 0.0004, + length: 566 + }); + Composite.add(engine.world, cons[cons.length - 1]); + } + //blocks + spawn.bodyRect(-212, -150, 30, 35, 1); + spawn.bodyRect(-212, -115, 30, 35, 1); + spawn.bodyRect(-212, -80, 30, 35, 1); + spawn.bodyRect(-212, -45, 30, 35, 1); + + spawn.bodyRect(-750, 400, 150, 150, 0.5); + spawn.bodyRect(-400, 1175, 100, 250, 1); //block to get to top path on bottom level + + spawn.bodyRect(-2525, -50, 145, 100, 0.5); + spawn.bodyRect(-2325, -300, 150, 100, 0.5); + spawn.bodyRect(-1275, -750, 200, 150, 0.5); //roof block + spawn.bodyRect(-525, -700, 125, 100, 0.5); //roof block + + //mobs + spawn.randomSmallMob(-1125, 550); + spawn.randomSmallMob(-2950, -50); + spawn.randomMob(-2025, 175, 0.3); + spawn.randomMob(-2325, 450, 0.3); + spawn.randomMob(-2925, 675, 0.2); + spawn.randomMob(-2700, 300, 0.1); + spawn.randomMob(-2500, 300, 0.1); + spawn.randomMob(-2075, -425, 0.1); + spawn.randomMob(-1550, -725, 0.1); + spawn.randomMob(375, 1100, 0); + spawn.randomMob(-1575, 1100, 0); + spawn.randomSmallMob(825, 300); + spawn.randomMob(-800, -1750, 0); + spawn.randomMob(400, -750, -0.1); + spawn.randomMob(650, 1300, -0.1); + spawn.randomMob(-2450, 1050, -0.1); + spawn.randomMob(500, 400, -0.1); + spawn.randomMob(-75, -1700, -0.1); + spawn.randomMob(900, -800, -0.2); + spawn.randomGroup(-75, 1050, -0.1); + spawn.randomGroup(-900, 1000, 0.2); + spawn.randomGroup(-1300, -1100, -0.3); + spawn.randomSmallMob(-2325, 800); + spawn.randomSmallMob(-900, 825); + + if (simulation.difficulty > 1) { + if (Math.random() < 0.80) { + spawn.randomLevelBoss(-800, -1300) + } else { + spawn.dragonFlyBoss(-1000 + Math.random() * 2500, -1300); //boss snake with head + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(300, -800) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + + // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 + // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 + level.setPosToSpawn(-25, -55); //-x + + if (isElevators) { + elevator1.holdX = -elevator1.holdX // flip the elevator horizontally + elevator2.holdX = -elevator2.holdX // flip the elevator horizontally + elevator3.holdX = -elevator3.holdX // flip the elevator horizontally + level.custom = () => { + elevator1.move(); + elevator1.drawTrack(); + elevator2.move(); + elevator2.drawTrack(); + elevator3.move(); + elevator3.drawTrack(); + + ctx.fillStyle = "#444" //light fixtures + ctx.fillRect(920 - 40, -505, 40, 10) + ctx.fillRect(920 - 40, 95, 40, 10) + ctx.fillRect(-180 - 40, 95, 40, 10) + ctx.fillRect(20 - 40, 695, 40, 10) + ctx.fillRect(2320 - 40, 945, 40, 10) + + ctx.fillStyle = "#cff" //exit + ctx.fillRect(-300 - 350, -250, 350, 250) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + } else { + level.custom = () => { + ctx.fillStyle = "#444" //light fixtures + ctx.fillRect(920 - 40, -505, 40, 10) + ctx.fillRect(920 - 40, 95, 40, 10) + ctx.fillRect(-180 - 40, 95, 40, 10) + ctx.fillRect(20 - 40, 695, 40, 10) + ctx.fillRect(2320 - 40, 945, 40, 10) + + ctx.fillStyle = "#cff" //exit + ctx.fillRect(-300 - 350, -250, 350, 250) + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + } + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights + ctx.beginPath() + ctx.moveTo(1800, -500) + ctx.lineTo(910, -500) //3rd floor light + ctx.lineTo(1300, 0) + ctx.lineTo(500, 0) + ctx.lineTo(890, -500) + ctx.lineTo(175, -500) + ctx.lineTo(175, -250) + ctx.lineTo(-175, -250) + ctx.lineTo(-175, 0) + ctx.lineTo(910, 100) //2nd floor light left + ctx.lineTo(1300, 600) + ctx.lineTo(500, 600) + ctx.lineTo(890, 100) + ctx.lineTo(-190, 100) //2nd floor light right + ctx.lineTo(200, 600) + ctx.lineTo(-600, 600) + ctx.lineTo(-210, 100) + ctx.lineTo(-1100, 100) + ctx.lineTo(-1100, 1400) + ctx.lineTo(-600, 1400) //1st floor light right + ctx.lineTo(-10, 700) + ctx.lineTo(10, 700) + ctx.lineTo(600, 1400) + ctx.lineTo(1950, 1400) //1st floor light left + ctx.lineTo(2290, 950) + ctx.lineTo(2310, 950) + ctx.lineTo(2650, 1400) + ctx.lineTo(3025, 1400) + ctx.lineTo(3025, 150) + ctx.lineTo(2590, 150) + ctx.lineTo(2600, -150) + ctx.lineTo(1800, -150) + ctx.lineTo(1800, -500) //top left end/start of path + ctx.fill() + }; + } + }, + office() { + let button, door + let isReverse = false + if (Math.random() < 0.75) { //normal direction start in top left + button = level.button(525, 0) + door = level.door(1362, -400, 25, 400, 355, 1.5) //door(x, y, width, height, distance, speed = 1) { + level.setPosToSpawn(1375, -1550); //normal spawn + level.exit.x = 3088; + level.exit.y = -630; + } else { //reverse direction, start in bottom right + isReverse = true + button = level.button(3800, 0) + door = level.door(3012, -400, 25, 400, 355, 1.5) + level.setPosToSpawn(3137, -650); //normal spawn + level.exit.x = 1375; + level.exit.y = -1530; + } + level.custom = () => { + button.query(); + button.draw(); + if (button.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + ctx.fillStyle = "#ccc" + ctx.fillRect(2495, -500, 10, 525) + ctx.fillStyle = "#dff" + if (isReverse) { + ctx.fillRect(725, -1950, 825, 450) + } else { + ctx.fillRect(3050, -950, 625, 500) + } + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(3650, -1300, 1300, 1300) + ctx.fillRect(3000, -1000, 650, 1000) + ctx.fillRect(750, -1950, 800, 450) + ctx.fillRect(750, -1450, 650, 1450) + ctx.fillRect(-550, -1700, 1300, 1700) + // ctx.fillRect(0, 0, 0, 0) + door.draw(); + }; + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom) + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 50); //ground bump wall + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + document.body.style.backgroundColor = "#e0e5e0"; + + spawn.debris(-300, -200, 1000, 6); //ground debris //16 debris per level + spawn.debris(3500, -200, 800, 5); //ground debris //16 debris per level + spawn.debris(-300, -650, 1200, 5); //1st floor debris //16 debris per level + powerUps.spawnStartingPowerUps(-525, -700); + + spawn.mapRect(-600, 0, 2000, 325); //ground + spawn.mapRect(1400, 25, 1600, 300); //ground + spawn.mapRect(3000, 0, 2000, 325); //ground + spawn.mapRect(-600, -1700, 50, 2000 - 100); //left wall + spawn.bodyRect(-295, -1540, 40, 40); //center block under wall + spawn.bodyRect(-298, -1580, 40, 40); //center block under wall + spawn.bodyRect(1500, -1540, 30, 30); //left of entrance + spawn.mapRect(1550, -2000, 50, 550); //right wall + // spawn.mapRect(1350, -2000 + 505, 50, 1295); + spawn.mapRect(1350, -1500, 50, 1125); //right wall + spawn.mapRect(-600, -2000 + 250, 2000 - 700, 50); //roof left + spawn.mapRect(-600 + 1300, -2000, 50, 300); //right roof wall + spawn.mapRect(-600 + 1300, -2000, 900, 50); //center wall + + map[map.length] = Bodies.polygon(725, -1700, 0, 15); //circle above door + spawn.bodyRect(720, -1675, 15, 170, 1, spawn.propsDoor); // door + body[body.length - 1].isNotHoldable = true; + //makes door swing + consBB[consBB.length] = Constraint.create({ + bodyA: body[body.length - 1], + pointA: { + x: 0, + y: -90 + }, + bodyB: map[map.length - 1], + stiffness: 1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + spawn.mapRect(-600 + 300, -2000 * 0.75, 1900, 50); //3rd floor + spawn.mapRect(-600 + 2000 * 0.7, -2000 * 0.74, 50, 375); //center wall + spawn.bodyRect(-600 + 2000 * 0.7, -2000 * 0.5 - 106, 50, 106); //center block under wall + spawn.mapRect(-600, -1000, 1100, 50); //2nd floor + spawn.mapRect(600, -1000, 500, 50); //2nd floor + spawn.spawnStairs(-600, -1000, 4, 250, 350); //stairs 2nd + spawn.mapRect(375, -600, 350, 150); //center table + spawn.mapRect(-300, -2000 * 0.25, 1690, 50); //1st floor + spawn.spawnStairs(-610 + 2000 - 50, -500, 4, 250, 350, true); //stairs + spawn.spawnStairs(-600, 0, 4, 250, 350); //stairs ground + spawn.bodyRect(700, -200, 100, 100); //center block under wall + spawn.bodyRect(700, -300, 100, 100); //center block under wall + spawn.bodyRect(700, -400, 100, 100); //center block under wall + spawn.mapRect(1390, 13, 30, 20); //step left + spawn.mapRect(2980, 13, 30, 20); //step right + spawn.bodyRect(4250, -700, 50, 100); + spawn.mapRect(3000, -1000, 50, 625); //left wall + spawn.mapRect(3000 + 2000 - 50, -1300, 50, 1100); //right wall + spawn.mapRect(4150, -600, 350, 150); //table + spawn.mapRect(3650, -1300, 50, 700); //exit wall + spawn.mapRect(3650, -1300, 1350, 50); //exit wall + spawn.bodyRect(3665, -600, 20, 100); //door + + spawn.mapRect(3025, -600, 250, 125); + spawn.mapRect(3175, -550, 175, 75); + // spawn.mapVertex(3160, -525, "625 0 300 0 300 -140 500 -140"); //entrance/exit ramp + + spawn.mapRect(3000, -2000 * 0.5, 700, 50); //exit roof + spawn.mapRect(3010, -2000 * 0.25, 1690, 50); //1st floor + spawn.spawnStairs(3000 + 2000 - 50, 0, 4, 250, 350, true); //stairs ground + spawn.randomSmallMob(4575, -560, 1); + spawn.randomSmallMob(1315, -880, 1); + spawn.randomSmallMob(800, -600); + spawn.randomMob(4100, -225, 0.8); + spawn.randomMob(-250, -700, 0.8); + spawn.randomMob(4500, -225, 0.15); + spawn.randomMob(3250, -225, 0.15); + spawn.randomMob(-100, -225, 0.1); + spawn.randomMob(1150, -225, 0.15); + spawn.randomMob(2000, -225, 0.15); + spawn.randomMob(450, -225, 0.15); + spawn.randomMob(100, -1200, 1); + spawn.randomMob(950, -1150, -0.1); + spawn.randomGroup(1800, -800, -0.2); + spawn.randomGroup(4150, -1000, 0.6); + if (simulation.difficulty > 1) { + if (Math.random() < 0.5) { + spawn.tetherBoss(2850, -80, { x: 2500, y: -500 }) + } else { + spawn.randomLevelBoss(2200, -450) + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(1875, -675) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + level.setPosToSpawn(50, -60); + + if (!isReverse) { //normal direction start in top left + level.setPosToSpawn(-1375, -1550); //normal spawn //-x + } else { //reverse direction, start in bottom right + level.setPosToSpawn(-3137, -650); //normal spawn + } + button.min.x = -button.min.x - 126 // flip the button horizontally + button.max.x = -button.max.x + 126 // flip the button horizontally + level.custom = () => { + button.query(); + button.draw(); + if (button.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + ctx.fillStyle = "#ccc" + ctx.fillRect(-2495 - 10, -500, 10, 525) + ctx.fillStyle = "#dff" + if (isReverse) { + ctx.fillRect(-725 - 825, -1950, 825, 450) + } else { + ctx.fillRect(-3050 - 625, -950, 625, 500) + } + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-3650 - 1300, -1300, 1300, 1300) + ctx.fillRect(-3000 - 650, -1000, 650, 1000) + ctx.fillRect(-750 - 800, -1950, 800, 450) + ctx.fillRect(-750 - 650, -1450, 650, 1450) + ctx.fillRect(550 - 1300, -1700, 1300, 1700) + // ctx.fillRect(0, 0, 0, 0) + door.draw(); + }; + } + + }, + stronghold() { // player made level by Francois 👑 from discord + simulation.makeTextLog(`stronghold by Francois`); + + const boost1 = level.boost(1470, -250, 1080) + const boost2 = level.boost(-370, 0, 800) + const boost3 = level.boost(4865, 0, 1800) + level.custom = () => { + boost1.query(); + boost2.query(); + boost3.query(); + ctx.fillStyle = "#edf9f9"; + ctx.fillRect(-500, -1220, 550, -480); + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fillRect(0, -700, 1050, 700); + ctx.fillRect(-550, -1170, 550, 1170); + ctx.fillRect(1150, -1700, 250, 1700); + ctx.fillRect(1100, -1700, 50, 450); + ctx.fillRect(1050, -1200, 100, 1200); + ctx.fillRect(1400, -250, 200, -1500); + ctx.fillRect(1600, -550, 600, -1150); + ctx.fillRect(2530, -550, 430, -1450); + ctx.fillRect(3270, -1700, 80, 600); + ctx.fillRect(3350, -1350, 700, 230); + ctx.fillRect(4050, -1700, 600, 1290); + ctx.fillRect(3650, -110, 1000, 170); + ctx.fillRect(4865, -55, 100, 55); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + + }; + + level.setPosToSpawn(1900, -40); //normal spawn + level.exit.x = -350; + level.exit.y = -1250; + + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom) + + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); //exit bump + spawn.debris(3800, -1480, 300, 12); + spawn.debris(3600, -1130, 200, 2); + document.body.style.backgroundColor = "#dbdcde"; + // simulation.draw.mapFill = "#444" + // simulation.draw.bodyFill = "rgba(140,140,140,0.85)" + // simulation.draw.bodyStroke = "#222" + + // __________________________________________________________________________________________________ + // Spawn Box + spawn.mapRect(1600, -500, 50, 500); //Left Wall + spawn.mapRect(1600, -550, 1500, 50); //Roof + spawn.mapRect(2300, -500, 50, 300); //Right Wall + + spawn.mapRect(-550, 0, 4300, 200); //ground + spawn.mapRect(3700, 55, 1300, 145); //2nd ground + spawn.mapRect(5000, 0, 50, 200); //Last small part of the ground + spawn.mapRect(3100, -1070, 50, 570); // vertical 2nd roof + spawn.mapRect(3100, -1120, 950, 50); // Horizontal 2nd Roof + spawn.mapRect(4050, -1750, 600, 50); // Roof after lift + spawn.mapRect(4600, -1700, 50, 100); // Petit retour de toit, après ascenseur + + //Spawn "Upstairs" + spawn.mapRect(3650, -160, 400, 50); //Thin Walk + spawn.mapRect(4050, -410, 600, 300); //Large staircase block + spawn.mapRect(4600, -1120, 50, 710); //Left Wall Wall upstairs + spawn.mapRect(4550, -1170, 100, 50); //Bloque ascenseur + spawn.mapVertex(3700, 35, "0 0 450 0 300 -60 150 -60"); //first slope + spawn.mapVertex(4850, 35, "0 0 370 0 370 -65 150 -65"); //second slope + + spawn.bodyRect(3950, -280, 170, 120); //Bloc Marche Pour Monter À Ascenseur + // spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight + // spawn.bodyRect(-2550, 1150, 200, 100, 1, spawn.propsSlide); //weight + spawn.bodyRect(4050, -500, 275, 100, 1, spawn.propsSlide); //weight + spawn.bodyRect(4235, -500, 275, 100, 1, spawn.propsSlide); //weight + // spawn.bodyRect(-2775, 1300, 400, 100, 1, spawn.propsHoist); //hoist + spawn.bodyRect(4025, -450, 550, 100, 1, spawn.propsHoist); //hoist + cons[cons.length] = Constraint.create({ + pointA: { + x: 4325, + y: -1700, + }, + bodyB: body[body.length - 1], + stiffness: 0.0002, //1217, + length: 200 + }); + Composite.add(engine.world, cons[cons.length - 1]); + + spawn.bodyRect(2799, -870, 310, 290); //Gros bloc angle toit + spawn.mapRect(4000, -1750, 50, 400); //Right Wall Cuve + spawn.mapRect(3400, -1400, 600, 50); // Bottom Cuve + spawn.mapRect(3350, -1750, 50, 400); // Left Wall Cuve + spawn.bodyRect(3400, -1470, 110, 70); //Moyen bloc dans la cuve + spawn.mapRect(3270, -1750, 80, 50); // Rebord gauche cuve + + spawn.mapRect(2530, -2000, 430, 50); //First Plateforme + spawn.mapRect(1600, -1750, 600, 50); // Middle plateforme + spawn.mapRect(1100, -1750, 300, 50); //Derniere plateforme // Toit petite boite en [ + spawn.bodyRect(1830, -1980, 190, 230); // Fat bloc plateforme middle + spawn.bodyRect(1380, -1770, 250, 20) // Pont last plateforme + + spawn.mapRect(1000, -1250, 400, 50); //Sol de la petite boite en [ + spawn.mapRect(1100, -1550, 50, 190); //Mur gauche petite boite en [ + spawn.bodyRect(1100, -1380, 48, 109); //Bloc-porte petite boite en [ + + spawn.mapRect(-100, -750, 1100, 50); //Sol last salle + spawn.mapRect(1000, -1200, 50, 500) // Mur droit last salle + spawn.mapRect(50, -1550, 1050, 50); // Toit last salle + spawn.bodyRect(1, -900, 48, 150); //Bloc porte last salle + spawn.mapRect(0, -1170, 50, 270); //Mur gauche en bas last salle + spawn.bodyRect(920, -900, 120, 120); //Gros bloc last salle + + spawn.mapRect(0, -1700, 50, 320); // Mur droit salle exit / Mur gauche last salle + spawn.mapRect(-550, -1220, 600, 50); // Sol exit room + spawn.mapRect(-500, -1750, 550, 50); // Toit exit room + spawn.mapRect(-550, -1750, 50, 530); // Mur gauche exit room + spawn.bodyRect(-503, -1250, 30, 30); // Petit bloc exit room + + spawn.mapRect(500, -700, 100, 590); //Bloc noir un dessous last salle + spawn.mapRect(1350, -250, 250, 250); //Black Block left from the spawn + + + map[map.length] = Bodies.polygon(2325, -205, 0, 15); //circle above door + spawn.bodyRect(2325, -180, 15, 170, 1, spawn.propsDoor); // door + body[body.length - 1].isNotHoldable = true; + //makes door swing + consBB[consBB.length] = Constraint.create({ + bodyA: body[body.length - 1], + pointA: { + x: 0, + y: -90 + }, + bodyB: map[map.length - 1], + stiffness: 1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + spawn.bodyRect(650, 50, 70, 50); + spawn.bodyRect(300, 0, 100, 60); + spawn.bodyRect(400, 0, 100, 150); + spawn.bodyRect(2545, -50, 70, 50); + spawn.bodyRect(2550, 0, 100, 30); + + spawn.randomSmallMob(200, -1300, 0.5); + spawn.randomSmallMob(300, -1300, 0.9); + spawn.randomSmallMob(470, -650, 1); + spawn.randomSmallMob(1000, -400, 1); + spawn.randomSmallMob(2550, -560, 1); + spawn.randomSmallMob(3350, -900, 1); + spawn.randomSmallMob(3600, -1210, 1); + spawn.randomSmallMob(700, -1950, 0.2); + spawn.randomSmallMob(5050, -550); + spawn.randomMob(-250, -250, 0.8); + spawn.randomMob(-300, -600, 0.6); + spawn.randomMob(350, -900, 0.5); + spawn.randomMob(770, -950, 0.8) + spawn.randomMob(900, -160, 1); + spawn.randomMob(2360, -820, 0.8); + spawn.randomMob(2700, -2020, 0.8); + spawn.randomMob(3050, -1650, 0.8); + spawn.randomMob(3350, -600, 0.8); + spawn.randomMob(4400, -50, 1); + spawn.randomGroup(1500, -1900, 0.5); + spawn.randomGroup(2350, -850, 1); + spawn.randomGroup(100, -450, 0.9); + + if (simulation.difficulty > 1) spawn.randomLevelBoss(1850, -1400); + spawn.secondaryBossChance(1850, -1400) + + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + basement() { // player made level by Francois 👑 from discord + simulation.makeTextLog(`basement by Francois`); + let button, door, buttonDoor, buttonPlateformEnd, doorPlateform + let isLevelReversed = Math.random(); + if (isLevelReversed < 0.7) { + isLevelReversed = false; + } else { + isLevelReversed = true; + } + const elevator = level.elevator(4545, -220, 110, 30, -3000) + const hazard = level.hazard(1675, -1050, 800, 150); + const portal = level.portal({ + x: -620, + y: -257 + }, Math.PI / 2, { //down + x: 500, + y: 2025 + }, -Math.PI / 2) //up + spawn.mapRect(350, 2025, 300, 300); //Bloc portail n°2 + + if (isLevelReversed === false) { /// Normal Spawn + button = level.button(2700, -1150); + level.setPosToSpawn(2600, -2050); //normal spawn + level.exit.x = level.enter.x + 4510; + level.exit.y = level.enter.y + 600; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + } else { /// Reversed spawn + button = level.button(1450, -1150); + buttonPlateformEnd = level.button(3530, -1150); + buttonDoor = level.button(8033, -3625); + door = level.door(7700, -3905, 25, 184, 184); + doorPlateform = level.door(3200, -1225, 299, 80, 525); + level.setPosToSpawn(7110, -1450); //normal spawn + level.exit.x = level.enter.x - 4510; + level.exit.y = level.enter.y - 600; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(7675, -3935, 75, 25); + spawn.mapRect(7675, -3715, 75, 25); + spawn.bodyRect(8075, -3675, 50, 25); + } + const boost1 = level.boost(8290, -2100, 1800) + level.custom = () => { + boost1.query(); + + level.exit.drawAndCheck(); + portal[2].query() + portal[3].query() + button.query(); + button.draw(); + if (isLevelReversed === true) { ///Reversed spawn + buttonDoor.draw(); + buttonDoor.query(); + buttonPlateformEnd.draw(); + buttonPlateformEnd.query(); + // hazard.query(); //bug reported from discord? + if (buttonDoor.isUp) { + door.isClosing = false + } else { + door.isClosing = true + } + door.openClose(); + if (buttonPlateformEnd.isUp) { + doorPlateform.isClosing = true; + } else { + doorPlateform.isClosing = false; + } + door.openClose(); + doorPlateform.openClose(); + } + hazard.level(button.isUp) + + + level.enter.draw(); + elevator.move(); + elevator.drawTrack(); + }; + + level.customTopLayer = () => { + ctx.fillStyle = "rgba(61,62,62,0.95)"; + ctx.fillRect(-750, -900, 750, 450); + + if (isLevelReversed === true) { + door.draw(); + doorPlateform.draw(); + } + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + hazard.query(); + }; + + level.defaultZoom = 1300 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#c7c7c7"; + + // GROUND // + spawn.mapRect(-400, -2000, 400, 1430); //Gros left wall + spawn.mapRect(3700, -3000, 700, 2650); //Gros right wall //Puit + spawn.mapRect(-400, -2000, 3700, 250); //Ground + spawn.mapRect(2475, -1150, 1225, 250); + spawn.mapRect(500, -1150, 1175, 250); //Ground level 3 + spawn.mapRect(350, -180, 4600, 1255); // Last ground + spawn.mapRect(-400, -458, 750, 3337); //mur left sous-sol + spawn.mapRect(-2850, -3375, 5300, 1375); + spawn.mapRect(-2850, -4200, 8000, 825); + spawn.mapRect(3700, -3375, 550, 375); + spawn.mapRect(-2850, -5200, 10200, 1000); + spawn.mapRect(5600, -1250, 3550, 2000); + spawn.mapRect(9150, -5200, 1725, 5800); + // SPAWN BOX // + spawn.mapRect(2300, -3375, 950, 1000); + spawn.mapRect(3550, -3375, 150, 1625); + spawn.mapVertex(2020, -791, " 250 250 -860 250 -2200 0 250 0"); //map vertex en haut + spawn.mapVertex(690, -295, "1700 0 -200 0 -200 -284 500 -284"); //map vertex en bas + spawn.mapRect(2950, -900, 750, 250); //Extension ground apres map vertex + if (isLevelReversed === false) { + spawn.mapRect(3250, -1800, 50, 150); //Petit picot en haut, à gauche + spawn.mapRect(3400, -1800, 50, 150); //Petit picot en haut, à droite + spawn.mapRect(3150, -1300, 50, 200) //Petit picot en bas, à gauche + spawn.mapRect(3500, -1300, 50, 200) //Petit picot en bas, à droite + spawn.mapRect(3050, -3375, 500, 1260); + spawn.mapRect(3400, -2265, 150, 515); //Mur fond tunnel + spawn.bodyRect(3625, -1225, 75, 75); //Pitit bloc à droite en bas spawn + } else { + spawn.mapRect(3050, -3375, 500, 1000); + spawn.mapRect(3400, -2400, 150, 650); //Mur fond tunnel + spawn.bodyRect(3425, -1515, 75, 75); //Petit en bas spawn + spawn.mapRect(3200, -1275, 300, 175); + } + + // TRAMPOLING // + if (isLevelReversed === false) { /// Normal spawn + spawn.bodyRect(0, -1000, 500, 120, 1, spawn.propsHoist); //hoist + cons[cons.length] = Constraint.create({ + pointA: { + x: 250, + y: -1750, + }, + bodyB: body[body.length - 1], + stiffness: 0.00014, + length: 120 + }); + Composite.add(engine.world, cons[cons.length - 1]); + spawn.bodyRect(0, -1250, 240, 190) //Fat cube ascenseur + } else { /// Reversed spawn + spawn.bodyRect(0, -650, 225, 175); + spawn.mapRect(425, -950, 175, 50); + spawn.mapRect(-25, -1150, 100, 50); + } + // PUIT // + spawn.mapVertex(4200, -1810, "0 0 450 0 600 -2500 0 -2500") + spawn.mapVertex(5000, -1809, "0 0 450 0 450 -2500 -150 -2500") + spawn.mapRect(4800, -3000, 800, 5875); //big right Puit + // BOSS AREA // + spawn.mapRect(4800, -3150, 50, 200); //Premiere barriere + spawn.mapRect(5100, -3530, 50, 380); //2nd barriere + spawn.mapRect(5100, -3200, 150, 50); //Marche en dessous mapVertex 1 + spawn.mapVertex(5450, -3650, "220 0 200 30 -200 30 -220 0 -200 -30 200 -30"); + spawn.mapVertex(6225, -3350, "275 0 250 50 -250 50 -275 0 -250 -50 250 -50"); + spawn.mapRect(5600, -3000, 1600, 725); //ground Boss Area + //Ouverture right boss area + spawn.mapRect(7300, -3325, 50, 50); //petite marche pour accéder à l'ouverture + spawn.mapRect(7350, -4075, 850, 50); //Bouche + spawn.mapRect(7400, -4050, 800, 50); //Bouche + spawn.mapRect(7450, -4025, 750, 50); //Bouche + spawn.mapRect(7500, -4000, 700, 50); //Bouche + spawn.mapRect(7550, -3975, 650, 50); //Bouche + spawn.mapRect(7350, -3600, 850, 50); //Bouche + spawn.mapRect(7400, -3625, 800, 50); //Bouche + spawn.mapRect(7450, -3650, 575, 50); //Bouche + spawn.mapRect(7500, -3675, 525, 50); //Bouche + spawn.mapRect(7550, -3700, 475, 50); //Bouche + //Murs + spawn.mapRect(7350, -5200, 1800, 1125); + spawn.mapRect(8475, -4075, 675, 2825); + spawn.mapRect(7300, -2100, 1175, 850); + spawn.mapRect(7350, -3550, 850, 1275); + //Escaliers + spawn.mapRect(6600, -2100, 200, 75); //escaliers + spawn.mapRect(6750, -2100, 750, 250); //escaliers + spawn.mapRect(6950, -1850, 550, 200); //escaliers + spawn.mapRect(6750, -1400, 750, 150); //escaliers + spawn.mapRect(6550, -1625, 250, 375); //escaliers + spawn.mapRect(6350, -1800, 250, 550); //escaliers + spawn.mapRect(5600, -2275, 800, 1025); //escaliers + // BLOCS + if (isLevelReversed === false) { /// Normal spawn + spawn.bodyRect(1350, -1175, 225, 25); + spawn.bodyRect(1450, -1200, 25, 25); + } else { /// Reversed spawn + spawn.bodyRect(700, -1175, 225, 25); + spawn.bodyRect(800, -1200, 25, 25); + } + spawn.bodyRect(1100, -1375, 225, 225); + spawn.bodyRect(1775, -925, 75, 25); + spawn.bodyRect(2225, -950, 75, 50); + spawn.bodyRect(2000, -1000, 50, 100); + spawn.bodyRect(3100, -1175, 50, 25); + spawn.bodyRect(2200, -375, 50, 50); + spawn.bodyRect(2200, -425, 50, 50); + spawn.bodyRect(2200, -475, 50, 50); + spawn.bodyRect(2200, -525, 50, 50); + spawn.bodyRect(1050, -400, 50, 25); + spawn.mapRect(2200, -650, 50, 125); + spawn.mapRect(2200, -325, 50, 150); + spawn.mapRect(2875, -225, 250, 50); + spawn.mapRect(2050, -1225, 75, 100); //Plateforme over acid + // MOBS + if (isLevelReversed === false) { ///Normal spawn + if (simulation.difficulty > 1) { + if (Math.random() < 0.2) { + spawn.tetherBoss(7000, -3300, { x: 7300, y: -3300 }) // tether ball + } else { + spawn.randomLevelBoss(6100, -3600, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "pulsarBoss"]); + } + } + } else { /// Reversed spawn + if (simulation.difficulty > 1) { + if (Math.random() < 0.2) { + spawn.tetherBoss(2300, -1300, { x: 2300, y: -1750 }) // tether ball + } else { + spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "dragonFlyBoss", "pulsarBoss"]); + } + } + } + spawn.randomSmallMob(100, -1000, 1); + spawn.randomSmallMob(1340, -675, 1); + spawn.randomSmallMob(7000, -3750, 1); + spawn.randomSmallMob(6050, -3200, 1); + spawn.randomMob(1970 + 10 * Math.random(), -1150 + 20 * Math.random(), 1); + spawn.randomMob(3500, -525, 0.8); + spawn.randomMob(6700, -3700, 0.8); + spawn.randomMob(2600, -1300, 0.7); + spawn.randomMob(600, -1250, 0.7); + spawn.randomMob(2450, -250, 0.6); + spawn.randomMob(6200, -3200, 0.6); + spawn.randomMob(900, -700, 0.5); + spawn.randomMob(1960, -400, 0.5); + spawn.randomMob(5430, -3520, 0.5); + spawn.randomMob(400, -700, 0.5); + spawn.randomMob(6500, -4000, 0.4); + spawn.randomMob(3333, -400, 0.4); + spawn.randomMob(3050, -1220, 0.4); + spawn.randomMob(800, 1200, 0.3); + spawn.randomMob(7200, -4000, 0.3); + spawn.randomMob(250, -1550, 0.3); + spawn.randomGroup(900, -1450, 0.3); + spawn.randomGroup(2980, -400, 0.3); + spawn.randomGroup(5750, -3860, 0.4); + spawn.randomGroup(1130, 1300, 0.1); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + powerUps.spawn(1900, -940, "heal"); + powerUps.spawn(3000, -230, "heal"); + powerUps.spawn(5450, -3675, "ammo"); + + // SECRET BOSS AREA // + //hidden house + spawn.mapRect(-850, -2000, 600, 1150); //Toit hidden house + spawn.mapRect(-2850, -2000, 2150, 4880); //Mur gauche hidden house + spawn.mapRect(-850, -458, 500, 3340); //Bloc sol hidden house + // + spawn.mapRect(-400, 2025, 3450, 850); //Sol secret boss area + spawn.mapRect(625, 1300, 225, 50); //Plateforme horizontale n°1 + spawn.mapRect(850, 1775, 470, 50); //Plateforme horizontale n°2 + spawn.mapRect(1000, 1625, 100, 150); //Plateforme vertiale n°1 + spawn.mapRect(1400, 1275, 100, 100); //Plateforme carrée + spawn.mapRect(1700, 1675, 75, 450); //Plateforme verticale n°2 + spawn.mapRect(2100, 1375, 450, 50); //Plateforme accroche boss + spawn.mapRect(2900, 900, 175, 325); //Débord de toit droite haut + spawn.mapRect(2900, 1675, 150, 350); //Muret en bas à droite + spawn.mapRect(2900, 1225, 75, 100); //Picot haut entrée salle trésor + spawn.mapRect(2900, 1575, 75, 100); //Picot bas entrée salle trésor + spawn.mapRect(2800, 1575, 100, 25); //Plongeoir sortie salle trésor + spawn.mapRect(3050, 1675, 400, 1200); //Sol sallle trésor + spawn.mapRect(3075, 1075, 375, 150); //Plafond salle trésor + spawn.mapRect(3300, 1075, 1500, 1800); //Mur droite salle trésor + // tether ball + spawn.tetherBoss(2330, 1850, { x: 2330, y: 1425 }) + spawn.secondaryBossChance(2330, 1850) + powerUps.chooseRandomPowerUp(3100, 1630); + }, + // detours() { //by Francois from discord + // simulation.makeTextLog(`detours by Francois`); + // level.setPosToSpawn(0, 0); //lower start + // level.exit.y = 150; + // spawn.mapRect(level.enter.x, 45, 100, 20); + // level.exit.x = 10625; + // spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + // level.defaultZoom = 1400; + // simulation.zoomTransition(level.defaultZoom) + // document.body.style.backgroundColor = "#d5d5d5"; + // const BGColor = "rgba(0,0,0,0.1)"; + // // level.fill.push({ + // // x: -150, + // // y: -250, + // // width: 625, + // // height: 325, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 475, + // // y: -520, + // // width: 5375, + // // height: 875, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 5850, + // // y: -1275, + // // width: 2800, + // // height: 2475, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 8650, + // // y: -500, + // // width: 1600, + // // height: 750, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 10250, + // // y: -700, + // // width: 900, + // // height: 950, + // // color: BGColor + // // }); + // const balance = level.spinner(5500, -412.5, 25, 660) //entrance + // const rotor = level.rotor(7000, 580, -0.001); + // const doorSortieSalle = level.door(8590, -520, 20, 800, 750) + // // let buttonSortieSalle + // // let portalEnBas + // let portalEnHaut + // // let door3isClosing = false; + + // function drawOnTheMapMapRect(x, y, dx, dy) { + // spawn.mapRect(x, y, dx, dy); + // len = map.length - 1 + // map[len].collisionFilter.category = cat.map; + // map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + // Matter.Body.setStatic(map[len], true); //make static + // Composite.add(engine.world, map[len]); //add to world + // simulation.draw.setPaths() //update map graphics + // } + + // function drawOnTheMapBodyRect(x, y, dx, dy) { + // spawn.bodyRect(x, y, dx, dy); + // len = body.length - 1 + // body[len].collisionFilter.category = cat.body; + // body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + // Composite.add(engine.world, body[len]); //add to world + // body[len].classType = "body" + // } + + // function spawnCouloirEnHaut() { + // // level.fill.push({ + // // x: 2575, + // // y: -1150, + // // width: 2550, + // // height: 630, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 1900, + // // y: -2300, + // // width: 1650, + // // height: 1150, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 3550, + // // y: -1625, + // // width: 1650, + // // height: 475, + // // color: BGColor + // // }); + // // level.fill.push({ + // // x: 1800, + // // y: -1120, + // // width: 775, + // // height: 600, + // // color: BGColor + // // }); + // drawOnTheMapMapRect(3800, -270, 75, 75); + // drawOnTheMapMapRect(3900, -895, 500, 75); + // drawOnTheMapMapRect(3900, -1195, 75, 375); + // drawOnTheMapMapRect(3525, -1195, 450, 75); + // drawOnTheMapMapRect(3525, -1995, 50, 1575); + // drawOnTheMapMapRect(3325, -1995, 50, 1575); + // drawOnTheMapMapRect(3525, -1670, 1675, 75); + // drawOnTheMapMapRect(5100, -1670, 100, 1250); + // drawOnTheMapMapRect(1800, -1195, 1575, 75); + // drawOnTheMapMapRect(1800, -1520, 375, 400); + // drawOnTheMapMapRect(1800, -2370, 100, 1250); + // drawOnTheMapMapRect(2375, -1845, 375, 250); + // drawOnTheMapMapRect(2700, -1745, 650, 75); + // drawOnTheMapMapRect(1800, -2370, 1775, 100); + // drawOnTheMapMapRect(3525, -2370, 50, 775); + // drawOnTheMapMapRect(4650, -1220, 550, 75); + // drawOnTheMapBodyRect(3225, -1845, 100, 100); + // drawOnTheMapBodyRect(3575, 1255, 125, 25); + // drawOnTheMapBodyRect(2450, 2255, 25, 25); + // drawOnTheMapBodyRect(3975, -945, 175, 50); + // drawOnTheMapBodyRect(4825, -1295, 50, 75); + // drawOnTheMapBodyRect(4850, -720, 250, 200); + // drawOnTheMapBodyRect(4050, -970, 25, 25); + // drawOnTheMapBodyRect(3075, -1245, 50, 50); + // portalEnHaut = level.portal({ + // x: 3650, + // y: -1470 + // }, Math.PI / 2, { + // x: 3250, + // y: -1473 + // }, Math.PI / 2) + + // spawn.randomSmallMob(2500, -2070 + Math.random(), 1); + // spawn.randomSmallMob(5000, -1370, 1); + // spawn.randomMob(5000, -645, 0.9); + // spawn.randomMob(4050, -970, 0.9); + // spawn.randomSmallMob(2800, -1620, 0.7); + // spawn.randomMob(2400, -1370, 0.5); + // spawn.randomMob(3725, -1320, 0.3); + // spawn.randomGroup(2115, -2020, 0.1) + + // powerUps.spawn(5000, -1275, "heal"); + + // levelCustom2(); + // } + // ////////////////////////////////////////// + // level.custom = () => { + // level.exit.drawAndCheck(); + // rotor.rotate(); + // // rotor2.rotate() + + // level.enter.draw(); + // }; + // level.customTopLayer = () => { + // doorSortieSalle.draw(); + // ctx.fillStyle = "#233" + // ctx.beginPath(); + // ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI); + // ctx.fill(); + // }; + // //////////////////////////////////////// + // function levelCustom2() { + // level.custom = () => { + // portalEnHaut[2].query(); + // portalEnHaut[3].query(); + // rotor.rotate(); + // doorSortieSalle.openClose(); + // level.exit.drawAndCheck(); + + // level.enter.draw(); + // }; + // // ////////////////////////////////////// + // level.customTopLayer = () => { + // doorSortieSalle.draw(); + // portalEnHaut[0].draw(); + // portalEnHaut[1].draw(); + // portalEnHaut[2].draw(); + // portalEnHaut[3].draw(); + // ctx.fillStyle = "#233" + // ctx.beginPath(); + // ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI); + // ctx.fill(); + + // }; + // } + // //spawn box + // spawn.mapRect(-200, -295, 75, 425); + // spawn.mapRect(-200, 55, 700, 75); + // spawn.mapRect(-200, -295, 700, 75); + // spawn.bodyRect(470, -220, 25, 275); //porte spawn box + // //couloir + // spawn.mapRect(450, -520, 50, 300); //muret gauche haut + // spawn.mapRect(450, 55, 50, 300); //muret gauche bas + // spawn.mapRect(1700, -520, 50, 325); //muret 2 haut + // spawn.mapRect(1700, 55, 50, 300); //muret 2 bas + // spawn.mapRect(4375, 55, 50, 300); + // spawn.mapRect(4575, 55, 50, 300); + // spawn.bodyRect(4625, 155, 75, 100); + // spawn.bodyRect(4725, 230, 50, 25); + // if (Math.random() > 0.5) { + // powerUps.chooseRandomPowerUp(4500, 200); + // } else { + // powerUps.chooseRandomPowerUp(8350, -630); + // } + // //blocs + // spawn.bodyRect(7475, 1055, 50, 75); + // spawn.bodyRect(7775, 1105, 25, 25); + // spawn.bodyRect(6925, 1105, 125, 25); + // spawn.bodyRect(6375, 380, 50, 50); + // spawn.bodyRect(6425, -220, 125, 150); + // spawn.bodyRect(6475, -245, 125, 25); + // spawn.bodyRect(7675, -245, 100, 50); + // spawn.bodyRect(7075, -520, 50, 100); + // spawn.bodyRect(8400, -595, 100, 75); + // spawn.bodyRect(1700, 5, 50, 50); + // spawn.bodyRect(1700, -45, 50, 50); + // spawn.bodyRect(1700, -95, 50, 50); + // spawn.bodyRect(1700, -145, 50, 50); + // spawn.bodyRect(1700, -195, 50, 50); + // spawn.mapRect(450, -520, 1600, 100); //plafond 1 + // spawn.mapRect(450, 255, 1600, 100); //sol 1 + // spawn.mapRect(2250, -45, 1450, 75); //entresol + // spawn.mapRect(3900, -520, 2000, 100); //plafond 2 + // spawn.mapRect(3900, 255, 2000, 100); //sol 2 + // //grande salle + // spawn.bodyRect(5900, 830, 325, 300); //bloc en bas à gauche + // spawn.mapRect(5775, -1295, 2900, 100); + // spawn.mapRect(5775, 1130, 2900, 100); //plancher + sol grande salle + // spawn.mapRect(5925, -70, 650, 50); //plateforme middle entrée + // spawn.mapRect(7575, -520, 1100, 100); //sol salle en haut à droite + // spawn.mapRect(6800, -420, 450, 50); //petite plateforme transition vers salle en haut + // spawn.mapRect(7750, -1295, 75, 575); //mur gauche salle en haut à droite + // spawn.mapRect(6100, 430, 375, 50); //plateforme en bas, gauche rotor + // spawn.mapRect(7450, -195, 1225, 75); //longue plateforme + // //murs grande salle + // spawn.mapRect(5775, -1295, 125, 875); + // spawn.mapRect(5775, 255, 125, 975); + // spawn.mapRect(8550, -1295, 125, 875); + // spawn.mapRect(8550, 180, 125, 1050); + // //couloir 2 + // spawn.mapRect(8875, -520, 1425, 325); + // spawn.mapRect(8550, -520, 1750, 100); + // spawn.mapRect(8550, 180, 2625, 100); + // spawn.mapRect(10175, -745, 125, 325); + // spawn.mapRect(10175, -745, 1000, 125); + // spawn.mapRect(11050, -745, 125, 1025); + // spawn.mapRect(8875, 80, 1425, 200); + // //MOBS + // spawn.randomSmallMob(900, -70, 1); + // spawn.randomMob(4300, 95, 1); + // spawn.randomSmallMob(6250, 630, 1); + // spawn.randomMob(6255, -835, 0.9); + // spawn.randomMob(8200, -900, 0.7); + // spawn.randomMob(5700, -270, 0.7); + // spawn.randomMob(8275, -320, 0.7); + // spawn.randomMob(2700, -270, 0.7); + // spawn.randomMob(7575, 950, 0.5); + // spawn.randomMob(7000, -695, 0.4); + // spawn.randomMob(1850, -345, 0.3); + // spawn.randomMob(3600, -270, 0.3); + // spawn.randomMob(1500, -270, 0.2); + // spawn.randomMob(1250, 55, 0.2); + // spawn.randomMob(8800, -45, 0.2); + // spawn.randomGroup(8025, -845, 0.2); + + // if (simulation.difficulty > 2) { + // // if (Math.random() < 0.2) { + // // // tether ball + // // spawn.tetherBoss(8000, 630, { x: 8550, y: 680 }) + // // let me = mob[mob.length - 1]; + // // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs + // // this.removeCons(); //remove constraint + // // spawnCouloirEnHaut() + // // doorSortieSalle.isClosing = false; + // // }; + // // if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105); + // // } else { + // spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss", "pulsarBoss"]); + // spawn.secondaryBossChance(8000, 630) + // //find level boss index + // let me + // for (let i = 0, len = mob.length; i < len; i++) { + // if (mob[i].isBoss) me = mob[i] + // } + // if (me) { + // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs + // spawnCouloirEnHaut() + // doorSortieSalle.isClosing = false; + // }; + // } else { + // spawnCouloirEnHaut() + // doorSortieSalle.isClosing = false; + // } + // // } + // } else { + // spawn.randomLevelBoss(8000, 630, ["shooterBoss"]); + // let me + // for (let i = 0, len = mob.length; i < len; i++) { + // if (mob[i].isBoss) me = mob[i] + // } + // if (me) { + // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs + // spawnCouloirEnHaut() + // doorSortieSalle.isClosing = false; + // }; + // } else { + // spawnCouloirEnHaut() + // doorSortieSalle.isClosing = false; + // } + // } + // }, + house() { //by Francois from discord + simulation.makeTextLog(`house by Francois`); + const rotor = level.rotor(4251, -325, 120, 20, 200, 0, 0.01, 0, -0.0001); + const hazard = level.hazard(4350, -1000, 300, 110); + const doorBedroom = level.door(1152, -1150, 25, 250, 250); + const doorGrenier = level.door(1152, -1625, 25, 150, 160); + const buttonBedroom = level.button(1250, -850); + const voletLucarne1 = level.door(1401, -2150, 20, 26, 28); + const voletLucarne2 = level.door(1401, -2125, 20, 26, 53); + const voletLucarne3 = level.door(1401, -2100, 20, 26, 78); + const voletLucarne4 = level.door(1401, -2075, 20, 26, 103); + const voletLucarne5 = level.door(1401, -2050, 20, 26, 128); + const voletLucarne6 = level.door(1401, -2025, 20, 26, 153); + let hasAlreadyBeenActivated = false; + let grd + + level.setPosToSpawn(0, -50); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 3100; + level.exit.y = -2480; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "rgb(170 170 170)" + + level.custom = () => { + ctx.fillStyle = "rgb(221, 221, 221)"; + ctx.fillRect(1175, -1425, 4000, 1200); + ctx.fillStyle = "rgb(170 170 170)"; + ctx.fillRect(1650, -1300, 175, 150); + ctx.fillStyle = "rgb(77, 76, 76)"; + ctx.fillRect(624, -1150, 28, 1075); + ctx.fillStyle = "#ababab"; + ctx.fillRect(3420, -380, 285, 40); + ctx.fillStyle = "#474747"; + ctx.fillRect(3555, -367.5, 15, 15); + ctx.fillRect(3418, -344, 288, 8); + ctx.fillRect(3555, -327.5, 15, 15); + ctx.fillRect(3418, -304, 288, 8); + ctx.fillRect(3555, -285, 15, 15); + ctx.fillStyle = "#ababab"; + ctx.fillRect(3420, -340, 285, 40); + ctx.fillRect(3420, -300, 285, 45); + ctx.fillStyle = "rgba(141, 141, 141,1)"; + ctx.fillRect(3800, -1275, 250, 425); + ctx.fillStyle = "#000"; + ctx.fillRect(3800, -1275, 250, 3); + ctx.fillRect(4048, -1275, 3, 425); + ctx.fillRect(3800, -1275, 3, 425); + ctx.fillRect(3830, -1050, 35, 10); + ctx.fillStyle = "rgba(225, 242, 245,0.6)"; + ctx.fillRect(4050, -1425, 1125, 600); + ctx.fillStyle = "#444"; + ctx.fillRect(1736, -1300, 3, 150); + ctx.fillRect(1650, -1224, 175, 3); + ctx.fillStyle = "#5806ac"; + ctx.fillRect(3375, -625, 375, 175); + ctx.fillStyle = "rgba(166, 166, 166,0.8)"; + ctx.fillRect(4050, -1425, 1, 600); + ctx.fillRect(4090, -1425, 1, 600); + ctx.fillRect(4130, -1425, 1, 600); + ctx.fillRect(4170, -1425, 1, 600); + ctx.fillRect(4210, -1425, 1, 600); + ctx.fillRect(4250, -1425, 1, 600); + ctx.fillRect(4290, -1425, 1, 600); + ctx.fillRect(4330, -1425, 1, 600); + ctx.fillRect(4370, -1425, 1, 600); + ctx.fillRect(4410, -1425, 1, 600); + ctx.fillRect(4450, -1425, 1, 600); + ctx.fillRect(4490, -1425, 1, 600); + ctx.fillRect(4530, -1425, 1, 600); + ctx.fillRect(4570, -1425, 1, 600); + ctx.fillRect(4610, -1425, 1, 600); + ctx.fillRect(4650, -1425, 1, 600); + ctx.fillRect(4690, -1425, 1, 600); + ctx.fillRect(4730, -1425, 1, 600); + ctx.fillRect(4770, -1425, 1, 600); + ctx.fillRect(4810, -1425, 1, 600); + ctx.fillRect(4850, -1425, 1, 600); + ctx.fillRect(4890, -1425, 1, 600); + ctx.fillRect(4930, -1425, 1, 600); + ctx.fillRect(4970, -1425, 1, 600); + ctx.fillRect(5010, -1425, 1, 600); + ctx.fillRect(5050, -1425, 1, 600); + ctx.fillRect(5090, -1425, 1, 600); + ctx.fillRect(5130, -1425, 1, 600); + ctx.fillRect(4050, -1425, 1125, 2); + ctx.fillRect(4050, -1385, 1125, 2); + ctx.fillRect(4050, -1345, 1125, 2); + ctx.fillRect(4050, -1305, 1125, 2); + ctx.fillRect(4050, -1265, 1125, 2); + ctx.fillRect(4050, -1225, 1125, 2); + ctx.fillRect(4050, -1185, 1125, 2); + ctx.fillRect(4050, -1145, 1125, 2); + ctx.fillRect(4050, -1105, 1125, 2); + ctx.fillRect(4050, -1065, 1125, 2); + ctx.fillRect(4050, -1025, 1125, 2); + ctx.fillRect(4050, -985, 1125, 2); + ctx.fillRect(4050, -945, 1125, 2); + ctx.fillRect(4050, -905, 1125, 2); + ctx.fillRect(4050, -865, 1125, 2); + + buttonBedroom.query(); + buttonBedroom.draw(); + if (buttonBedroom.isUp) { + if (hasAlreadyBeenActivated == false) { + doorBedroom.isClosing = true; + doorGrenier.isClosing = true; + voletLucarne1.isClosing = true; + voletLucarne2.isClosing = true; + voletLucarne3.isClosing = true; + voletLucarne4.isClosing = true; + voletLucarne5.isClosing = true; + voletLucarne6.isClosing = true; + } + } else { + doorBedroom.isClosing = false; + doorGrenier.isClosing = false; + voletLucarne1.isClosing = false; + voletLucarne2.isClosing = false; + voletLucarne3.isClosing = false; + voletLucarne4.isClosing = false; + voletLucarne5.isClosing = false; + voletLucarne6.isClosing = false; + if (hasAlreadyBeenActivated == false) { + hasAlreadyBeenActivated = true; + } + } + doorBedroom.openClose(); + doorGrenier.openClose(); + voletLucarne1.openClose(); + voletLucarne2.openClose(); + voletLucarne3.openClose(); + voletLucarne4.openClose(); + voletLucarne5.openClose(); + voletLucarne6.openClose(); + rotor.rotate(); + /// + grd = ctx.createRadialGradient(512.5, -1025, 5, 512.5, -1025, 100); + grd.addColorStop(0, "rgb(255, 199, 43)"); + grd.addColorStop(1, "rgb(170 170 170)"); + ctx.fillStyle = grd; + ctx.fillRect(450, -1025, 125, 100); + /// + grd = ctx.createRadialGradient(762.5, -1025, 5, 762.5, -1025, 100); + grd.addColorStop(0, "rgb(255, 199, 43, 1)"); + grd.addColorStop(1, "rgb(170 170 170)"); + ctx.fillStyle = grd; + ctx.fillRect(700, -1025, 125, 100); + /// + ctx.lineWidth = 7; + ctx.strokeStyle = "#444444" + ctx.strokeRect(1650, -1300, 175, 150); + + chair.force.y += chair.mass * simulation.g; + chair2.force.y += chair2.mass * simulation.g; + person.force.y += person.mass * simulation.g; + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(64,64,64,0.97)"; + ctx.fillRect(2800, -400, 275, 175); + + hazard.query(); + doorBedroom.draw(); + doorGrenier.draw(); + voletLucarne1.draw(); + voletLucarne2.draw(); + voletLucarne3.draw(); + voletLucarne4.draw(); + voletLucarne5.draw(); + voletLucarne6.draw(); + }; + //chairs + const part1 = Matter.Bodies.rectangle(4525, -255, 25, 200, { + density: 0.0005, + isNotHoldable: true, + }); + const part2 = Matter.Bodies.rectangle(4562, -235, 100, 25, { + density: 0.0005, + isNotHoldable: true, + }); + const part3 = Matter.Bodies.rectangle(4600, -202, 25, 91.5, { + density: 0.0005, + isNotHoldable: true, + }); + const part4 = Matter.Bodies.rectangle(5100, -255, 25, 200, { + density: 0.0005, + isNotHoldable: true, + }); + const part5 = Matter.Bodies.rectangle(5063, -235, 100, 25, { + density: 0.0005, + isNotHoldable: true, + }); + const part6 = Matter.Bodies.rectangle(5025, -202, 25, 91.5, { + density: 0.0005, + isNotHoldable: true, + }); + chair = Body.create({ + parts: [part1, part2, part3], + }); + chair2 = Body.create({ + parts: [part4, part5, part6], + }); + Composite.add(engine.world, [chair]); + Composite.add(engine.world, [chair2]); + composite[composite.length] = chair; + composite[composite.length] = chair2; + body[body.length] = part1; + body[body.length] = part2; + body[body.length] = part3; + body[body.length] = part4; + body[body.length] = part5; + body[body.length] = part6; + setTimeout(function () { + chair.collisionFilter.category = cat.body; + chair.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + }, 1000); + setTimeout(function () { + chair2.collisionFilter.category = cat.body; + chair2.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + }, 1000); + var head = Matter.Bodies.rectangle(300, -200 - 60, 34, 40, { + isNotHoldable: true, + }); + var chest = Matter.Bodies.rectangle(300, -200, 55, 80, { + isNotHoldable: true, + }); + var rightUpperArm = Matter.Bodies.rectangle(300 + 39, -200 - 15, 20, 40, { + isNotHoldable: true, + }); + var rightLowerArm = Matter.Bodies.rectangle(300 + 39, -200 + 25, 20, 60, { + isNotHoldable: true, + }); + var leftUpperArm = Matter.Bodies.rectangle(300 - 39, -200 - 15, 20, 40, { + isNotHoldable: true, + }); + var leftLowerArm = Matter.Bodies.rectangle(300 - 39, -200 + 25, 20, 60, { + isNotHoldable: true, + }); + var leftUpperLeg = Matter.Bodies.rectangle(300 - 20, -200 + 57, 20, 40, { + isNotHoldable: true, + }); + var leftLowerLeg = Matter.Bodies.rectangle(300 - 20, -200 + 97, 20, 60, { + isNotHoldable: true, + }); + var rightUpperLeg = Matter.Bodies.rectangle(300 + 20, -200 + 57, 20, 40, { + isNotHoldable: true, + }); + var rightLowerLeg = Matter.Bodies.rectangle(300 + 20, -200 + 97, 20, 60, { + isNotHoldable: true, + }); + + //man + var person = Body.create({ + parts: [chest, head, leftLowerArm, leftUpperArm, + rightLowerArm, rightUpperArm, leftLowerLeg, + rightLowerLeg, leftUpperLeg, rightUpperLeg + ], + }); + Composite.add(engine.world, [person]); + composite[composite.length] = person + body[body.length] = chest + body[body.length] = head + body[body.length] = part3 + body[body.length] = leftLowerLeg + body[body.length] = leftUpperLeg + body[body.length] = leftUpperArm + body[body.length] = leftLowerArm + body[body.length] = rightLowerLeg + body[body.length] = rightUpperLeg + body[body.length] = rightLowerArm + body[body.length] = rightUpperArm + setTimeout(function () { + person.collisionFilter.category = cat.body; + person.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + }, 1000); + + //rez de chaussée + spawn.mapRect(-200, 0, 5400, 100); //ground + spawn.mapRect(1150, -255, 4050, 355); //additionnal ground + spawn.mapRect(800, -255, 400, 90); //1st step + spawn.mapRect(650, -170, 550, 90); //2nd step + spawn.mapRect(500, -85, 700, 90); //3rd step + spawn.mapRect(1150, -850, 50, 175); //porte entrée + spawn.bodyRect(1162.5, -675, 25, 420) //porte entrée + spawn.mapRect(1150, -850, 1500, 50); //plafond 1 + spawn.mapRect(3025, -850, 2175, 50); //plafond 2 + spawn.mapRect(5150, -850, 50, 650); //mur cuisine + //lave-vaisselle + spawn.mapRect(4225, -400, 25, 150); + spawn.mapRect(4225, -400, 175, 25); + spawn.mapRect(4375, -400, 25, 150); + spawn.bodyRect(4350, -350, 20, 40); + spawn.bodyRect(4325, -325, 20, 20); + spawn.bodyRect(4325, -275, 20, 20); + //escalier + spawn.mapRect(3025, -850, 50, 225); + spawn.mapRect(2925, -775, 150, 150); + spawn.mapRect(2800, -700, 275, 75); + spawn.mapRect(2575, -400, 175, 175); + spawn.mapRect(2475, -325, 175, 100); + spawn.mapRect(2675, -475, 400, 100); + spawn.mapRect(2675, -475, 150, 250); + //cuisine + spawn.mapRect(4025, -850, 50, 175); //porte cuisine + spawn.mapRect(4025, -375, 50, 125); //porte cuisine + + map[map.length] = Bodies.polygon(4050, -675, 0, 15); //circle above door + spawn.bodyRect(4040, -650, 20, 260, 1, spawn.propsDoor); // door + body[body.length - 1].isNotHoldable = true; + //makes door swing + consBB[consBB.length] = Constraint.create({ + bodyA: body[body.length - 1], + pointA: { + x: 0, + y: -130 + }, + bodyB: map[map.length - 1], + stiffness: 1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + //table + chaises + spawn.mapRect(4025, -850, 50, 175); + spawn.mapRect(4650, -375, 325, 25); + spawn.mapRect(4700, -350, 25, 100); + spawn.mapRect(4900, -350, 25, 100); + spawn.bodyRect(4875, -400, 75, 25); + spawn.bodyRect(4700, -400, 75, 25); + + //murs télé + spawn.mapRect(3400, -400, 20, 150); + spawn.mapRect(3705, -400, 20, 150); + spawn.mapRect(3400, -400, 325, 20); + //socle écran + spawn.mapRect(3500, -415, 125, 17); + spawn.mapRect(3550, -450, 25, 50); + // ??? + spawn.bodyRect(3075, -375, 125, 125); + spawn.bodyRect(3075, -400, 50, 25); + spawn.bodyRect(3725, -325, 100, 75); + spawn.bodyRect(3375, -275, 25, 25); + // premier étage + spawn.mapRect(1150, -1450, 4050, 50); + spawn.mapRect(5150, -1450, 50, 650); + spawn.mapRect(1150, -1450, 50, 300); + spawn.mapRect(1150, -900, 50, 100); + spawn.mapVertex(1066, -730, "-200 60 0 -60 100 -60 100 60") + //chambre + spawn.mapRect(2350, -1450, 50, 175); //porte chambre + //lit + spawn.mapRect(1475, -1025, 25, 225); //pied de lit 1 + spawn.mapRect(1850, -925, 25, 125); //pied de lit 2 + spawn.mapRect(1475, -925, 400, 50); //sommier + spawn.bodyRect(1500, -950, 375, 25); //matelat + spawn.bodyRect(1500, -1000, 75, 50); //oreiller + //table + spawn.bodyRect(1950, -1000, 30, 150); //pied table + spawn.bodyRect(2250, -1000, 30, 150); //pied table + spawn.bodyRect(1920, -1025, 390, 25); //table + //salle de bain + spawn.mapRect(4025, -1450, 50, 175); //porte salle de bain + map[map.length] = Bodies.polygon(5050, -925, 0, 35.4); + spawn.mapRect(5015, -960, 125, 40); + spawn.mapRect(5050, -925, 90, 35.4); + spawn.mapVertex(5086.5, -875, "100 60 -30 60 20 0 100 0") + spawn.mapRect(5125, -1070, 15, 120) + spawn.bodyRect(5016, -965, 108, 15) + //baignoire + spawn.mapVertex(4316, -965, "30 100 0 100 -80 -50 30 -50") //bord 1 + spawn.mapVertex(4675, -961.5, "30 100 0 100 0 -50 80 -50") //bord 2 + spawn.mapVertex(4400, -860, "0 -20 -20 20 20 20 0 -20") //pied 1 + spawn.mapVertex(4600, -860, "0 -20 -20 20 20 20 0 -20") //pied 2 + spawn.mapRect(4325, -900, 350, 25); //fond baignoire + spawn.mapRect(4300, -1175, 25, 175); + spawn.mapRect(4300, -1175, 125, 25); + spawn.mapRect(4400, -1175, 25, 50); //pied pommeau de douche + spawn.mapVertex(4412.5, -1105, "-20 -20 -30 40 30 40 20 -20") //pommeau de douche + + //grenier + spawn.mapRect(1150, -1475, 50, 50); + spawn.mapRect(1150, -1800, 50, 175); + spawn.mapRect(5150, -1800, 50, 400); //murs + spawn.mapVertex(1300, -1900, "-150 200 -200 200 50 0 100 0"); + spawn.mapVertex(1800, -2300, "-150 200 -200 200 175 -100 225 -100"); + spawn.mapRect(1390, -2180, 250, 30); //lucarne + spawn.mapVertex(5050, -1900, "150 200 200 200 -50 0 -100 0"); + spawn.mapVertex(4550, -2300, "150 200 200 200 -175 -100 -225 -100"); + spawn.mapRect(4710, -2175, 250, 25); //lucarne 2 + spawn.mapRect(5150, -1450, 200, 50); + //obstacles + spawn.mapRect(3775, -1800, 99, 50); + spawn.mapRect(2425, -2150, 50, 425); + spawn.mapRect(2150, -1775, 325, 50); + spawn.mapRect(3825, -2150, 50, 750); + spawn.mapRect(3826, -2150, 149, 50); + spawn.mapRect(4125, -2150, 149, 50); + spawn.mapRect(4225, -2150, 50, 450); + spawn.mapRect(4225, -1750, 250, 50); + level.chain(2495, -2130, 0, true, 10); + + spawn.bodyRect(2950, -375, 120, 120) //bloc hidden zone + spawn.bodyRect(2350, -1850, 75, 75); + spawn.bodyRect(4275, -1900, 75, 100); + spawn.bodyRect(4825, -1650, 325, 200); + spawn.bodyRect(5025, -1725, 25, 25); + spawn.bodyRect(4900, -1700, 200, 75); + spawn.mapVertex(2950, -2096, "-75 -50 75 -50 75 0 0 100 -75 0") + + /*cheminée + roof*/ + spawn.mapRect(1963, -2450, 2425, 35); + spawn.mapRect(2925, -2900, 125, 480); + spawn.mapRect(2900, -2900, 175, 75); + spawn.mapRect(2900, -2975, 25, 100); + spawn.mapRect(3050, -2975, 25, 100); + spawn.mapRect(2875, -3000, 225, 25); + // lampadaire + jump + spawn.mapRect(1000, -1450, 200, 25); + spawn.mapRect(500, -1150, 275, 25); + spawn.mapRect(750, -1150, 25, 75); + spawn.mapRect(500, -1150, 25, 75); + spawn.mapRect(450, -1075, 125, 50); + spawn.mapRect(700, -1075, 125, 50); + spawn.mapRect(2985, -4600, 0.1, 1700) + + //bodyRects ~= debris + spawn.bodyRect(1740, -475, 80, 220) + spawn.bodyRect(1840, -290, 38, 23) + spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random()); + spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random()); + spawn.bodyRect(3070 + 600 * Math.random(), -1100, 20 + 50 * Math.random(), 150 + 100 * Math.random()) + spawn.bodyRect(3050 + 1000 * Math.random(), -920, 30 + 100 * Math.random(), 15 + 65 * Math.random()); + spawn.bodyRect(1600 + 250 * Math.random(), -1540, 80, 220) //boss room + spawn.debris(3070, -900, 1000, 3); //16 debris per level + spawn.debris(1200, -350, 1475, 4); //16 debris per level + spawn.debris(1250, -1550, 3565, 9); //16 debris per level + + powerUps.chooseRandomPowerUp(2860, -270); + // Mobs + + spawn.randomSmallMob(1385, -600, 1); + spawn.randomSmallMob(5000, -680, 1); + spawn.randomSmallMob(4750, -925, 1); + spawn.randomSmallMob(2300, -1830, 1); + spawn.randomMob(3170, -720, 0.8); + spawn.randomMob(3700, -975, 0.8); + spawn.randomMob(2625, -1150, 0.7); + spawn.randomMob(4175, -750, 0.7); + spawn.randomMob(2100, -370, 0.7); + spawn.randomMob(2000, -1230, 0.7); + spawn.randomMob(4175, -1075, 0.6); + spawn.randomMob(3965, -1650, 0.6) + spawn.randomMob(4650, -1750, 0.6); + spawn.randomMob(830, -1170, 0.5); + spawn.randomGroup(3730, -1100, 0.5); + spawn.randomMob(2650, -2250, 0.3); + spawn.randomMob(1615, -2270, 0.3); + spawn.randomMob(1380, -1280, 0.25); + spawn.randomMob(2280, -650, 0.2); + spawn.randomGroup(2450, -2650, 0.2); + spawn.randomMob(3800, -580, 0.2); + spawn.randomMob(4630, -425, 0.1); + spawn.randomGroup(630, -1300, -0.1); + spawn.randomGroup(3450, -2880, -0.2) + if (simulation.difficulty > 3) { + spawn.secondaryBossChance(3380, -1775) + if (Math.random() < 0.16) { + spawn.tetherBoss(3380, -1775, { x: 3775, y: -1775 }) + } else { + spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "dragonFlyBoss", "laserBoss"]); + } + } + }, + perplex() { //by Oranger from discord + simulation.makeTextLog(`perplex by Oranger`); + document.body.style.backgroundColor = "#dcdcde"; + level.setPosToSpawn(-600, 400); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 550; + level.exit.y = -2730; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + + const portal = level.portal({ //main portals + x: -1000, + y: 50 + }, -Math.PI / 2, { //up + x: 1000, + y: 50 + }, -Math.PI / 2) //up + const portal2 = level.portal({ //portals in upper right corner + x: 1400, + y: -2200 + }, -Math.PI / 2, { //up + x: 1700, + y: -1700 + }, -Math.PI / 2) //up + // rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + const rotor = level.rotor(-600, -1950, 800, 50, 0.001, 0, 0.01, 0, -0.001) + + level.custom = () => { + portal[2].query(true) + portal[3].query(true) + portal2[2].query(true) + portal2[3].query(true) + rotor.rotate(); + + ctx.fillStyle = "#d4f4f4"; + ctx.fillRect(375, -3000, 450, 300); + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + + level.customTopLayer = () => { + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + ctx.fillStyle = "rgba(0,0,0,0.03)"; + ctx.fillRect(-875, -250, 1500, 700); + ctx.fillRect(-925, -505, 930, 255); + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fillRect(725, -1400, 200, 200); + ctx.fillRect(925, -2150, 150, 2175); + ctx.fillRect(925, -3400, 150, 850); + ctx.fillStyle = "rgba(0,0,0,0.03)"; + ctx.fillRect(1800, -2600, 400, 400); + ctx.fillRect(2200, -2600, 400, 1250); + + }; + + level.defaultZoom = 1700 // 4500 // 1400 + simulation.zoomTransition(level.defaultZoom) + + //section 1: before portals + spawn.mapRect(-925, 450, 1850, 250); //1-1 base + spawn.mapRect(-925, -300, 55, 755); //1 left wall + spawn.mapRect(-875, 50, 1100, 50); //1-1 ceiling + spawn.mapRect(620, -300, 305, 755); //1-1 and 1-2 right wall + spawn.bodyRect(200, 350, 230, 100); + spawn.bodyRect(300, 250, 150, 100); + spawn.mapRect(-875, -300, 580, 50); //1-2 ceiling on left + spawn.mapRect(0, -300, 625, 50); //1-2 ceiling on right + spawn.mapRect(0, -650, 150, 350); //1-3 right wall + spawn.mapRect(-925, -650, 975, 150); //1-3 ceiling + spawn.mapRect(-1280, 100, 205, 150); //1-4 floor + spawn.mapRect(-1280, 245, 360, 455); //bottom left corner + spawn.mapRect(-1600, -200, 200, 50); //1-4 platform 1 + + //section 2: lower central room (gone through main portals 1 time) + spawn.mapRect(920, 245, 160, 455); //below right portal + spawn.mapRect(1075, -300, 500, 1000); //2-1 right floor + spawn.bodyRect(100, -1000, 50, 350); + spawn.bodyRect(100, -1015, 250, 15); + spawn.mapRect(-925, -1600, 100, 1000); //2-2 left wall + spawn.mapRect(725, -2150, 200, 750); //2-2 right wall + spawn.mapRect(725, -1200, 200, 200); //2-2 right wall 2 + spawn.mapRect(300, -1000, 625, 50); //2 central ledge + //shute + spawn.mapRect(1075, -2005, 550, 1055); //shute right wall + spawn.mapRect(875, -1000, 50, 300); //shute left 1 + spawn.mapRect(860, -1030, 50, 300); //shute left 2 + spawn.mapRect(850, -1100, 50, 300); //shute left 3 + spawn.mapRect(830, -980, 50, 50); //shute left 4 + spawn.mapRect(1075, -1000, 50, 300); //shute right 1 + spawn.mapRect(1090, -1030, 50, 300); //shute right 2 + spawn.mapRect(1100, -1100, 50, 300); //shute right 3 + spawn.mapRect(1120, -980, 50, 50); //shute right 4 + spawn.mapRect(1850, -650, 400, 50); //drop from 4-1 + //section 3: upper left room and upper central room (gone through main portals 2 times) + //3-2 is just the upper part of 2-2 + spawn.mapRect(-1775, -1000, 700, 300); //3-1 floor + spawn.mapRect(-1900, -2300, 175, 1600); //3-1 left wall + spawn.mapRect(-1375, -1300, 300, 50); //3-1 platform 1 + spawn.mapRect(-1600, -1650, 300, 50); //3-1 platform 2 + spawn.mapRect(-1775, -2300, 700, 300); //3-1 ceiling + spawn.mapRect(-830, -1600, 300, 50); //3-2 left ledge + spawn.mapRect(250, -2150, 675, 50); //3-2 right ledge + spawn.mapRect(-925, -2300, 100, 300); //3-2 left wall + spawn.mapRect(-600, -2700, 1525, 150); //3-2 ceiling + spawn.mapRect(1075, -2150, 250, 150); //next to upper portal + // level.fill.push({ + // x: -1730, + // y: -2300, + // width: 870, + // height: 1600, + // color: "rgba(0,0,0,0.03)" + // }); + + //section 4: upper right portals + spawn.mapRect(1475, -2700, 150, 700); //4-1 left wall + spawn.mapRect(1775, -1650, 250, 150); //4-1 floor-ish + spawn.mapRect(1575, -1505, 450, 555); //below upper right portal + spawn.mapRect(1800, -2250, 400, 50); //4-1 platform 2 + spawn.bodyRect(2200, -2250, 15, 300); + spawn.mapRect(2200, -1950, 400, 50); //4-1 platform 1 + //spawn.bodyRect(2575, -2600, 25, 650); + spawn.mapRect(2600, -1650, 400, 50); //4-1 platform 0 + spawn.mapRect(2200, -1350, 400, 50); //4-1 platform -1 + spawn.bodyRect(2200, -1900, 15, 550); + spawn.bodyRect(2585, -1650, 15, 300); + + spawn.mapRect(1800, -4200, 800, 1600); //4-2 right wall + spawn.mapRect(800, -4200, 1800, -500); //4-2 ceiling + spawn.mapRect(1075, -3400, 225, 850); //upper shute right wall + spawn.mapRect(800, -3400, 125, 850); //upper shute left wall + + //section 5: after portals (gone through main portals 3 times) + spawn.mapRect(-700, -2700, 100, 450); //5-1 right wall + spawn.mapRect(-1450, -2700, 900, 50); //5-1 ceiling + spawn.mapRect(-925, -2300, 325, 50); //5-1 right floor + spawn.mapRect(-1900, -3000, 450, 50); //stair cover + spawn.bodyRect(-1150, -2950, 150, 250); //5-2 block + + //top left corner stuff + spawn.mapRect(-1900, -2450, 250, 450); // + //exit room + spawn.mapRect(350, -3000, 50, 100); //exit room left wall + spawn.mapRect(350, -3000, 450, -1700); //exit room ceiling + spawn.bodyRect(350, -2900, 50, 50.5); //door + spawn.bodyRect(350, -2850, 50, 50.5); //door + spawn.bodyRect(350, -2800, 50, 50.5); //door + spawn.bodyRect(350, -2750, 50, 50.5); //door + + spawn.debris(-400, 450, 400, 5); //16 debris per level + spawn.debris(-1650, -2300, 250, 4); //16 debris per level + spawn.debris(-750, -650, 750, 3); //16 debris per level + + //mobs + spawn.randomMob(-650, -100, 0.7); //1-2 left + spawn.randomMob(100, -150, 0.3); //1-2 right + spawn.randomMob(-100, -400, 0); //1-3 right + //spawn.randomMob(-1500, -300, 0.3); //1-4 platform + spawn.randomMob(1450, -450, 0); //2-1 right + spawn.randomMob(1700, -800, 1); //2-1 off the edge. chance is 1 because some enemies just fall + spawn.randomGroup(-550, -900, -0.3); //2-2 + spawn.randomMob(-1550, -1800, 0.7); //3-1 upper platform + //spawn.randomMob(-1225, -1400, 0.3); //3-1 lower platform + spawn.randomMob(450, -2350, 0.3); //3-2 right ledge + //spawn.randomMob(1150, -2250, 0); //3-2 far right + spawn.randomGroup(2400, -2300, -0.3); //4-1 floating + spawn.randomMob(2400, -1450, 0); //4-1 platform -1 + spawn.randomMob(2800, -1800, 0.5); //4-1 platform 0 + spawn.randomMob(-1700, -3200, 0.7); //5-2 left platform + spawn.randomMob(-550, -2800, 0.3); //5-2 middle + if (simulation.difficulty > 3) { + if (Math.random() < 0.5) { + spawn.randomLevelBoss(450, -1350, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]); + } else { + spawn.randomLevelBoss(-300, -3200, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]); + } + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(7725, 2275) + }, + coliseum() { + simulation.makeTextLog(`coliseum by iNoobBoi`); + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { }; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + //Level + level.setPosToSpawn(200, 50); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + level.exit.x = 8950; + level.exit.y = 170; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + + //Map + spawn.mapRect(-100, -400, 100, 600); + spawn.mapRect(-100, 100, 700, 100); + spawn.mapRect(500, 100, 100, 1700); + spawn.mapRect(500, 1700, 4000, 100); + spawn.mapRect(4100, 600, 400, 100); + spawn.mapRect(4400, 600, 100, 1600); + spawn.mapRect(4400, 2100, 4300, 100); + spawn.mapRect(8600, 200, 100, 2000); + spawn.mapRect(8600, 200, 700, 100); + spawn.mapRect(9200, -300, 100, 600); + spawn.mapRect(8600, -300, 700, 100); + spawn.mapRect(8600, -700, 100, 500); + spawn.mapRect(4400, -700, 4300, 100); + spawn.mapRect(4400, -700, 100, 900); + spawn.mapRect(-100, -400, 4600, 100); + + //Platforms + spawn.mapRect(1100, 400, 300, 100); + spawn.mapRect(500, 500, 300, 100); + spawn.mapRect(1050, 800, 300, 100); + spawn.mapRect(1770, 1050, 300, 100); + spawn.mapRect(1800, 500, 300, 100); + spawn.mapRect(2550, 900, 300, 100); + spawn.mapRect(2800, 1400, 300, 100); + spawn.mapRect(1250, 1350, 300, 100); + spawn.mapRect(4750, 850, 300, 100); + spawn.mapRect(3200, 1050, 300, 100); + spawn.mapRect(4700, 100, 300, 100); + spawn.mapRect(5350, 0, 300, 100); + spawn.mapRect(3800, 900, 300, 100); + spawn.mapRect(5100, 500, 300, 100); + spawn.mapRect(5900, -300, 300, 100); + spawn.mapRect(6500, -700, 300, 1300); + spawn.mapRect(7900, 0, 300, 100); + spawn.mapRect(8050, 800, 300, 100); + spawn.mapRect(7800, 1900, 300, 100); + spawn.mapRect(8300, 450, 300, 100); + spawn.mapRect(8400, 1200, 300, 100); + spawn.mapRect(7570, 1100, 300, 100); + spawn.mapRect(6700, 1850, 300, 100); + spawn.mapRect(8000, 1500, 300, 100); + spawn.mapRect(7120, -100, 300, 100); + spawn.mapRect(7000, 1500, 300, 100); + spawn.mapRect(6500, 1000, 300, 1200); + spawn.mapRect(5800, 1100, 300, 100); + spawn.mapRect(5900, 1700, 300, 100); + spawn.mapRect(5300, 1400, 300, 100); + spawn.mapRect(5200, 1100, 300, 100); + spawn.mapRect(6700, 1100, 300, 100); + spawn.mapRect(4800, 1650, 300, 100); + + //Room 1 Spawning + spawn.randomMob(1000, 700, 0.7); + spawn.randomGroup(1100, 700, 0.5); + spawn.randomMob(1900, 400, 0.7); + spawn.randomGroup(2000, 400, 0.4); + spawn.randomGroup(1800, 1100, 0.4); + spawn.randomGroup(2700, 700, 0.5); + spawn.randomMob(2900, 1200, 0.7); + spawn.randomSmallMob(3200, 300, 0.9); + spawn.randomSmallMob(3700, 800, 0.9); + spawn.randomMob(1100, 700, 0.6); + spawn.randomGroup(1200, 700, 0.5); + spawn.randomMob(2000, 400, 0.8); + spawn.randomGroup(2100, 400, 0.5); + spawn.randomGroup(1900, 1100, 0.5); + spawn.randomGroup(2800, 700, 0.5); + spawn.randomMob(3000, 1200, 0.7); + spawn.randomSmallMob(3200, 300, 0.9); + spawn.randomSmallMob(3700, 800, 0.9); + spawn.randomMob(800, 1500, 0.9); + spawn.randomMob(1500, 1500, 0.7); + spawn.randomMob(2200, 1500, 0.6); + spawn.randomMob(2500, 1500, 0.7); + spawn.randomMob(2800, 1500, 0.7); + spawn.randomMob(3300, 1500, 0.6); + + //Room 2 Spawning + spawn.randomGroup(4700, 2000, 0.9); + spawn.randomMob(5000, 2000, 0.5); + spawn.randomSmallMob(5700, 1500, 0.9); + spawn.randomMob(8500, 2000, 0.6); + spawn.randomGroup(8000, 1300, 0.9); + spawn.randomMob(8300, -300, 0.4); + spawn.randomSmallMob(7600, -200, 0.9); + spawn.randomMob(5200, -300, 0.5); + spawn.randomSmallMob(4700, -200, 0.5); + spawn.randomGroup(4700, 2000, 0.8); + spawn.randomMob(5000, 2000, 0.5); + spawn.randomSmallMob(5700, 1500, 0.9); + spawn.randomGroup(8500, 2000, 0.3); + spawn.randomSmallMob(8000, 1300, 0.4); + spawn.randomMob(8300, -300, 0.3); + spawn.randomGroup(7600, -200, 0.5); + spawn.randomMob(5200, -300, 0.3); + spawn.randomGroup(4700, -200, 0.4); + spawn.randomGroup(8650, -200, 0.9); //end guards + spawn.randomMob(8650, -200, 0.9); //end guards + + + //Boss Spawning + if (simulation.difficulty > 3) { + spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]); + // if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500); + // if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]); + } + + //Blocks + spawn.bodyRect(550, -300, 50, 400); //spawn door + spawn.bodyRect(4400, 200, 100, 400); //boss door + spawn.bodyRect(6600, 600, 50, 400); //boss 2 door + spawn.debris(400, 800, 400, 2); + spawn.debris(3800, 1600, 1200, 6); + spawn.debris(7500, 2000, 800, 4); + spawn.debris(5500, 2000, 800, 4); + + //Powerups + powerUps.spawnStartingPowerUps(1250, 1500); + // powerUps.spawnStartingPowerUps(1500, 1500); + powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); + powerUps.spawn(200, 50, "heal"); + // powerUps.spawn(200, 50, "ammo"); + // powerUps.spawn(200, 50, "ammo"); + // powerUps.spawn(200, 50, "ammo"); + + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(6600, 600) + }, + crossfire() { + simulation.makeTextLog(`crossfire by iNoobBoi`); + + //*1.5 + //Level Setup + const slimePitOne = level.hazard(0, 850, 3800, 120); + const slimePitTwo = level.hazard(4600, 430, 2000, 120); + const slimePitThree = level.hazard(6500, 200, 1000, 170); + + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + level.customTopLayer = () => { + slimePitOne.query(); + slimePitTwo.query(); + slimePitThree.query(); + }; + + level.setPosToSpawn(-500, 550); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + level.exit.x = 10300; + level.exit.y = -830; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + + level.defaultZoom = 3000 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + + //Map Elements + spawn.mapRect(-800, -600, 800, 200); + spawn.mapRect(-200, -600, 200, 800); + spawn.mapRect(-800, -600, 200, 800); + spawn.mapRect(-1000, 0, 1000, 200); + spawn.mapRect(-1000, 0, 200, 800); + spawn.mapRect(-1000, 600, 1400, 200); + spawn.mapRect(0, 600, 200, 400); + spawn.mapRect(0, 950, 4000, 100); + spawn.mapRect(800, 800, 600, 200); + spawn.mapRect(1700, 700, 500, 300); + spawn.mapRect(2500, 600, 400, 400); + spawn.mapRect(3200, 600, 1200, 200); + spawn.mapRect(3800, 600, 200, 800); // + spawn.mapRect(3800, 1200, 800, 200); + spawn.mapRect(4400, 400, 300, 1000); + spawn.mapRect(4400, 500, 2000, 100); + spawn.mapRect(6500, 300, 1000, 100); + spawn.mapRect(5000, 200, 700, 400); + spawn.mapRect(6000, 0, 650, 600); + spawn.mapRect(6900, -300, 700, 100); + spawn.mapRect(7400, -600, 200, 1100); + spawn.mapRect(7400, 300, 2600, 200); + spawn.mapRect(9800, -800, 200, 1300); + spawn.mapRect(9800, -800, 1000, 200); + spawn.mapRect(10600, -1400, 200, 800); + spawn.mapRect(9800, -1400, 200, 400); + spawn.mapRect(7400, -1400, 3400, 200); + spawn.mapRect(7400, -1600, 200, 800); + spawn.mapRect(5400, -1600, 2200, 200); + spawn.mapRect(6000, -1600, 200, 800); + spawn.mapRect(5400, -1600, 200, 800); + spawn.mapRect(4800, -1000, 1400, 200); + spawn.mapRect(4800, -1000, 200, 600); + spawn.mapRect(3800, -600, 1200, 200); + spawn.mapRect(3200, -800, 800, 200); + spawn.mapRect(3200, -800, 200, 800); + spawn.mapRect(3800, -800, 200, 800); + spawn.mapRect(-200, -200, 4200, 200); + + //Boss Room Platforms + spawn.mapRect(7700, 100, 300, 40); + spawn.mapRect(8600, 0, 300, 40); + spawn.mapRect(9200, 100, 300, 40); + spawn.mapRect(9400, -200, 300, 40); + spawn.mapRect(8000, -200, 300, 40); + spawn.mapRect(8500, -400, 300, 40); + spawn.mapRect(9000, -600, 300, 40); + spawn.mapRect(9400, -800, 300, 40); + spawn.mapRect(8600, -1000, 300, 40); + spawn.mapRect(7900, -800, 300, 40); + + //Mob Spawning + spawn.randomMob(200, 400, 0.7); + // spawn.randomMob(1200, 400, 0.7); + spawn.randomMob(2000, 400, 0.7); + // spawn.randomMob(3000, 400, 0.7); + spawn.randomMob(5000, 0, 0.7); + spawn.randomMob(5600, 0, 0.7); + spawn.randomMob(6200, -200, 0.7); + // spawn.randomMob(6600, -200, 0.7); + spawn.randomMob(7200, -800, 0.7); + spawn.randomSmallMob(800, 400, 0.9); + spawn.randomSmallMob(1800, 400, 0.9); + // spawn.randomSmallMob(2600, 400, 0.9); + spawn.randomSmallMob(5200, 0, 0.9); + // spawn.randomSmallMob(5400, 0, 0.9); + spawn.randomSmallMob(6400, -200, 0.9); + spawn.randomGroup(3800, 400, 0.5); + spawn.randomGroup(4200, 400, 0.5); + // spawn.randomGroup(4400, 200, 0.5); + spawn.randomGroup(7000, -800, 0.5); + // spawn.randomGroup(7700, 300, 0.5); + spawn.randomGroup(9800, 300, 0.5); + // spawn.randomGroup(7700, -1100, 0.5); + spawn.randomGroup(9800, -1100, 0.5); + + if (simulation.difficulty > 3) spawn.randomLevelBoss(8600, -600, ["powerUpBoss", "bomberBoss", "dragonFlyBoss", "spiderBoss", "historyBoss"]) + spawn.secondaryBossChance(7900, -400) + + //Boss Spawning + if (simulation.difficulty > 10) { + spawn.pulsarBoss(3600, -400); + powerUps.chooseRandomPowerUp(4006, 400); + powerUps.chooseRandomPowerUp(4407, 400); + powerUps.spawnStartingPowerUps(4400, 400); + if (simulation.difficulty > 30) { + powerUps.chooseRandomPowerUp(4002, 400); + powerUps.chooseRandomPowerUp(4004, 400); + spawn.pulsarBoss(4200, 1000); + if (simulation.difficulty > 60) { + powerUps.chooseRandomPowerUp(4409, 400); + spawn.pulsarBoss(5800, -1200); + if (simulation.difficulty > 80) { + spawn.pulsarBoss(-400, -200); + if (simulation.difficulty > 100) { + spawn.pulsarBoss(3600, -400); + if (simulation.difficulty > 120) { + spawn.pulsarBoss(-400, -200); + } + } + } + } + } + } + + //Powerup Spawning + powerUps.spawnStartingPowerUps(4000, 400); + powerUps.addResearchToLevel(); //needs to run after mobs are spawned + + //Block Spawning + // spawn.bodyRect(-100, 200, 100, 400); //spawn door + spawn.bodyRect(7450, -800, 25, 200); //boss room door + spawn.bodyRect(9850, -1000, 25, 200); //end door + spawn.mapRect(-200, 350, 200, 450); + + // spawn.mapRect(3875, -75, 50, 575); + spawn.mapRect(3800, -75, 200, 525); + spawn.mapRect(3875, 590, 50, 150); + spawn.mapRect(3875, 350, 50, 140); + + const debrisCount = 3 + spawn.debris(1050, 700, 400, debrisCount); + spawn.debris(1900, 600, 400, debrisCount); + spawn.debris(2700, 500, 400, debrisCount); + // spawn.debris(3500, 450, 400, debrisCount); + spawn.debris(4150, 500, 400, debrisCount); + spawn.debris(5300, 0, 400, debrisCount); + spawn.debris(6300, -100, 400, debrisCount); + spawn.debris(7200, -500, 400, debrisCount); + spawn.debris(8000, -600, 400, debrisCount); + spawn.debris(8700, -700, 400, debrisCount); + spawn.debris(9300, -900, 400, debrisCount); + }, + vats() { // Made by Dablux#6610 on Discord + simulation.makeTextLog(`vats by Dablux`); + + simulation.zoomScale = 1500; + level.setPosToSpawn(4400, -1060) + spawn.mapRect(level.enter.x, level.enter.y + 30, 100, 20) + level.exit.x = 3900; + level.exit.y = 1060; + spawn.mapRect(level.exit.x, level.exit.y + 30, 100, 20) + document.body.style.backgroundColor = "#dcdcde"; + + var nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30) + const door = level.door(475, 900, 50, 200, 201) + const exitDoor = level.door(3375, 900, 50, 200, 201) + const deliveryButton = level.button(3500, -410) + const buttonGreen = level.button(-1600, 1090) + const buttonYellow = level.button(-1600, -1160) + const buttonRed = level.button(5874, -2410) + let g = false; + let y = false; + let r = false; + const deliverySlime = level.hazard(3700, -940, 100, 480) + const deliverySlime2 = level.hazard(3700, -461, 100, 1141) + const slimePit = level.hazard(700, 1200, 2500, 1300, 0.004) + const topSlime = level.hazard(800, -460, 2900, 90, 0.004) + // const rotor = level.rotor(0, -725, 0.001) + const rotor = level.rotor(-400, -725, 800, 50, 0.001, 0, 0.01, 0, 0.001) + + + const portal = level.portal({ + x: -135, + y: 800 + }, Math.PI / 2, { + x: 570, + y: -395 + }, -Math.PI / 2) + const portal2 = level.portal({ + x: -1800, + y: 1900 + }, Math.PI, { + x: 200, + y: 1105 + }, -Math.PI / 2) + const drip1 = level.drip(1875, -660, -400, 70) + const drip2 = level.drip(3525, -940, -400, 150) + const drip3 = level.drip(1975, 100, 1200, 100) + door.isClosing = true; + exitDoor.isClosing = true; + + // UPPER AREA // + spawn.mapRect(4500, -2400, 1700, 2050) + spawn.mapRect(3800, -1000, 700, 650) + spawn.mapRect(4000, -1310, 50, 60) + spawn.mapRect(4450, -1310, 50, 60) + spawn.mapRect(4000, -1320, 500, 20) + level.chain(4025, -1225, 0.5 * Math.PI, false, 5, 25) + spawn.mapRect(3650, -460, 50, 90) + spawn.mapRect(3525, -1000, 325, 20) + spawn.mapRect(3650, -1000, 50, 440) + spawn.mapRect(3300, -1000, 50, 450) + spawn.mapRect(3325, -725, 150, 25) + spawn.mapRect(3500, -980, 175, 35) + spawn.mapRect(3325, -980, 50, 35) + spawn.mapRect(-1800, -1250, 50, 120) + spawn.mapRect(6150, -2500, 50, 120) + spawn.bodyRect(3350, -1000, 175, 20, 1, spawn.propsIsNotHoldable) // Cover + Matter.Body.setMass(body[body.length - 1], 0.7) // Make cover easier to remove + spawn.mapRect(750, -475, 50, 75); + for (let i = 1; i < 5; i++) { + spawn.mapRect(800 + (i * 100) + (500 * (i - 1)), -460 + (i * -120) + (20 * (i - 1)), 500, 20) + } + + // ARENA // + spawn.mapRect(400, -400, 2950, 500) + spawn.mapRect(-1800, -1150, 1800, 1950) + spawn.mapRect(-1800, 1100, 780, 1800) + spawn.mapRect(-300, 1100, 1000, 1800) + //spawn.mapRect(-1800, -1450, 100, 2000) + spawn.blockDoor(-1800, 1070) + level.chain(-1000, 1120, 0, true, 18, 20) + spawn.mapRect(700, 2500, 2500, 900) + spawn.mapRect(400, 100, 200, 599) + spawn.mapRect(400, 650, 75, 250) + spawn.mapRect(525, 650, 75, 250) + spawn.mapRect(3300, 650, 75, 250) + spawn.mapRect(3425, 650, 75, 250) + spawn.mapRect(3200, 1100, 1800, 2200) + spawn.mapRect(3300, -400, 200, 1099) // STOP CHANGING THIS ONE!!!! + spawn.mapRect(3450, -400, 250, 1100) + spawn.mapRect(3650, 680, 200, 20) + spawn.mapRect(3800, -400, 1400, 1100) + spawn.mapRect(4100, 700, 100, 300) + spawn.mapRect(4900, -400, 1300, 2500) + spawn.bodyRect(4100, 1000, 100, 100) + + spawn.bodyRect(-2100, 2050, 290, 30) //Portal platform + let b = body[body.length - 1]; + b.isNotHoldable = true + cons[cons.length] = Constraint.create({ + pointA: { + x: -1820, + y: 2065 + }, + bodyB: b, + pointB: { + x: -135, + y: 0 + }, + stiffness: 1, + length: 1 + }); + cons[cons.length] = Constraint.create({ + pointA: { + x: -1800, + y: 1400 + }, + bodyB: b, + pointB: { + x: 135, + y: 0 + }, + stiffness: 0.005, + length: 700 + }); + Composite.add(engine.world, [cons[cons.length - 2], cons[cons.length - 1]]); + + spawn.bodyRect(5225, -2525, 300, 75); + spawn.bodyRect(4700, -2525, 100, 75, 0.5); + spawn.bodyRect(4900, -2600, 50, 50, 0.4); + spawn.bodyRect(5050, -2475, 500, 100, 0.4); + spawn.bodyRect(2950, -950, 175, 75, 0.5); + spawn.bodyRect(3050, -1000, 75, 50, 0.3); + spawn.bodyRect(2300, -850, 75, 50, 0.7); + spawn.bodyRect(2150, -575, 100, 175, 0.6); + spawn.bodyRect(2500, -550, 400, 150, 0.2); + spawn.bodyRect(1525, -500, 225, 100, 0.2); + spawn.bodyRect(1625, -575, 100, 75); + spawn.bodyRect(1000, -475, 100, 100, 0.8); + spawn.bodyRect(1225, -450, 125, 50, 0.9); + spawn.bodyRect(525, -500, 175, 125, 0.75); + spawn.bodyRect(575, -600, 100, 75, 0.5); + spawn.bodyRect(-925, -1225, 275, 75, 0.4); + spawn.bodyRect(-1125, -1300, 200, 150, 0.7); + spawn.bodyRect(-475, -1250, 200, 100, 0.8); + spawn.bodyRect(-425, -1300, 100, 50, 0.75); + spawn.bodyRect(-1225, -1200, 100, 25, 0.45); + spawn.bodyRect(-1025, -1350, 75, 50, 0.5); + spawn.bodyRect(-450, 1025, 75, 50, 0.5); + spawn.bodyRect(-775, 1050, 50, 50, 0.6); + spawn.bodyRect(-650, 975, 75, 75, 0.2); + spawn.bodyRect(-475, 1025, 100, 50, 0.7); + spawn.bodyRect(-450, 1025, 75, 50, 0.6); + spawn.bodyRect(-800, 1050, 100, 50, 0.5); + spawn.bodyRect(-600, 950, 75, 75, 0.3); + spawn.bodyRect(-500, 1000, 75, 25, 0.2); + spawn.bodyRect(-900, 1025, 150, 50); + spawn.bodyRect(-1350, 1000, 100, 100, 0.4); + spawn.bodyRect(-1225, 1075, 100, 25); + spawn.debris(900, -1000, 2000, 16); + + // MOBS // + spawn.randomSmallMob(2900, -1000) + spawn.randomSmallMob(1750, -700) + spawn.randomMob(4250, -1400) + spawn.randomMob(4800, -2400, 0.3) + spawn.randomMob(1000, 600, 0.3) + spawn.randomMob(1650, 950, 0.2) + spawn.randomMob(1300, -1250, 0) + spawn.randomMob(-600, -1250, 0.1) + spawn.randomMob(1000, -600, 0.4) + spawn.randomMob(1800, -700, 0.4) + spawn.randomMob(2200, 950, 0.2) + spawn.randomMob(-1900, 1400, 0.3) + spawn.randomMob(-750, -1000, 0.3) + spawn.randomMob(3250, 1000, 0.1) + spawn.randomMob(2000, -2800, 0.4) + spawn.randomMob(2200, -500, 0) + spawn.randomMob(1800, -450, 0.3) + spawn.randomGroup(2300, -450, 1) + spawn.randomGroup(3000, -450, 0.3) + spawn.randomGroup(6000, -2700, 0) + spawn.randomGroup(-1200, -1300, -0.3) + powerUps.addResearchToLevel() + + if (simulation.difficulty > 3) { + spawn.randomLevelBoss(1900, 400, ["shieldingBoss", "shooterBoss", "launcherBoss", "streamBoss"]) + } else { + exitDoor.isClosing = false; + } + spawn.secondaryBossChance(800, -800) + + powerUps.spawn(4450, 1050, "heal"); + if (Math.random() > (0.2 + (simulation.difficulty / 60))) { + powerUps.spawn(4500, 1050, "ammo"); + powerUps.spawn(4550, 1050, "ammo"); + } else { + powerUps.spawn(4500, 1050, "tech"); + spawn.randomMob(4550, 1050, Infinity); + } + powerUps.spawnStartingPowerUps(3750, -940) + + const W = 500; + const H = 20; + for (let i = 1; i < 5; i++) { + spawn.bodyRect(700 + (i * 100) + (W * (i - 1)), 1110, W, H, 1, spawn.propsIsNotHoldable) + let b = body[body.length - 1]; + cons[cons.length] = Constraint.create({ + pointA: { + x: b.position.x - (W / 2) + 50, + y: b.position.y - 1025 + }, + bodyB: b, + pointB: { + x: -(W / 2) + 50, + y: 0 + }, + stiffness: 0.002, + length: 1000 + }); + cons[cons.length] = Constraint.create({ + pointA: { + x: b.position.x + (W / 2) - 50, + y: b.position.y - 1025 + }, + bodyB: b, + pointB: { + x: (W / 2) - 50, + y: 0 + }, + stiffness: 0.002, + length: 1000 + }); + Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]]) + } + const boost1 = level.boost(4400, -1385, 1200) + + level.custom = () => { + boost1.query(); + buttonGreen.query() + buttonYellow.query() + buttonRed.query() + + if (!buttonGreen.isUp) { + if (!g) { + Matter.Composite.remove(engine.world, cons[1]) + cons.splice(1, 2) + } + g = true; + } + if (!buttonYellow.isUp) { + y = true; + } + if (!buttonRed.isUp) { + r = true; + } + + if (g && y && r) { + door.isClosing = false; + } else { + door.isClosing = true; + } + + door.openClose() + exitDoor.openClose() + + if (m.pos.y > 1600 && 700 < m.pos.x && m.pos.x < 3200) { // Saving player from slime pit + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 200, + y: 1000 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + + if (simulation.cycle >= nextBlockSpawn && body.length < 100) { + var len = body.length; + body[len] = Matter.Bodies.polygon(Math.floor(Math.random() * 1700) + 1050, 100, Math.floor(Math.random() * 11) + 10, Math.floor(Math.random() * 20) + 15) + body[len].collisionFilter.category = cat.body; + body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + Composite.add(engine.world, body[len]) + nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30) + } + + if (exitDoor.isClosing) { + exitDoor.isClosing = false; + for (i = 0; i < mob.length; i++) { + if (mob[i].isBoss && 525 < mob[i].position.x < 3200 && -2500 < mob[i].position.y < 100) { + exitDoor.isClosing = true; + } + } + } + + for (let i = 0, len = body.length; i < len; i++) { + if (body[i].position.x > 700 && body[i].position.x < 3200 && body[i].position.y > 1200 && !body[i].isNotHoldable) { + Matter.Body.scale(body[i], 0.99, 0.99); + if (body[i].velocity.y > 3) body[i].force.y -= 0.96 * body[i].mass * simulation.g + const slowY = (body[i].velocity.y > 0) ? Math.max(0.3, 1 - 0.0015 * body[i].velocity.y * body[i].velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(body[i].velocity.y)) //down : up + Matter.Body.setVelocity(body[i], { + x: Math.max(0.6, 1 - 0.07 * Math.abs(body[i].velocity.x)) * body[i].velocity.x, + y: slowY * body[i].velocity.y + }); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]) + body.splice(i, 1) + break + } + } + } + + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].position.x > 700 && mob[i].position.x < 3200 && mob[i].alive && !mob[i].isShielded && mob[i].position.y > 1200) { + mobs.statusDoT(mob[i], 0.005, 30) + } + } + + ctx.beginPath() + ctx.fillStyle = "#666"; + ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 20, 0, 2 * Math.PI) + ctx.fillRect(buttonGreen.min.x - 55, buttonGreen.max.y + 25, 10, -95) + ctx.fill() + ctx.beginPath() + ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 20, 0, 2 * Math.PI) + ctx.fillRect(buttonYellow.min.x - 55, buttonYellow.max.y + 25, 10, -95) + ctx.fill() + ctx.beginPath() + ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 20, 0, 2 * Math.PI) + ctx.fillRect(buttonRed.min.x - 55, buttonRed.max.y + 25, 10, -95) + ctx.fill() + + ctx.beginPath() + ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 10, 0, 2 * Math.PI) + ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + ctx.beginPath() + ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 10, 0, 2 * Math.PI) + ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + ctx.beginPath() + ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 10, 0, 2 * Math.PI) + ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + + slimePit.query(); + ctx.shadowColor = 'hsla(160, 100%, 50%, 1)' + ctx.shadowBlur = 100; + // slimePit.draw() + ctx.shadowBlur = 0; + ctx.shadowColor = 'rgba(0, 0, 0, 0)' + + deliveryButton.query() + portal[2].query() + //portal[3].query() + portal2[2].query() + //portal2[3].query() + + deliverySlime.level(deliveryButton.isUp) + topSlime.level(!r) + rotor.rotate() + + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(3500, 675, 600, 450) + level.enter.draw() + level.exit.drawAndCheck() + } + + level.customTopLayer = () => { + topSlime.query(); + deliverySlime.query() + deliverySlime2.query() + drip1.draw() + drip2.draw() + drip3.draw() + + ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((4200 - m.pos.x) / 100, 0.99))})` + ctx.fillRect(4100, 650, 850, 500) + + ctx.fillStyle = "rgba(0,20,40,0.1)" + ctx.fillRect(4025, -1300, 475, 300) + ctx.fillRect(3325, -1000, 375, 600) + ctx.fillRect(425, 100, 3050, 2400) + ctx.fillRect(-1775, 800, 1750, 2100) + ctx.fillStyle = "rgba(0,20,40,0.2)" + ctx.fillRect(2725, -860, 450, 460) + ctx.fillRect(2125, -760, 450, 360) + ctx.fillRect(1525, -660, 450, 260) + ctx.fillRect(925, -560, 450, 160) + ctx.fillRect(3700, -980, 100, 1200) + + ctx.fillStyle = `#444`; + ctx.fillRect(465, 690, 70, 209) + ctx.fillRect(3365, 690, 70, 209) + + ctx.beginPath() + ctx.arc(500, 870, 20, 0, 2 * Math.PI) + ctx.arc(500, 820, 20, 0, 2 * Math.PI) + ctx.arc(500, 770, 20, 0, 2 * Math.PI) + ctx.fillStyle = "rgba(0, 0, 0, 0.3"; + ctx.fill() + + ctx.beginPath() + ctx.arc(500, 870, 10, 0, 2 * Math.PI) + ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + ctx.beginPath() + ctx.arc(500, 820, 10, 0, 2 * Math.PI) + ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + ctx.beginPath() + ctx.arc(500, 770, 10, 0, 2 * Math.PI) + ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); + ctx.fill() + + deliveryButton.draw() + // deliverySlime.draw() + // deliverySlime2.draw() + // topSlime.draw() + buttonGreen.draw() + buttonYellow.draw() + buttonRed.draw() + portal[0].draw() + portal[2].draw() + portal2[0].draw() + portal2[2].draw() + } + }, + ngon() { //make by Oranger + simulation.makeTextLog(`"ngon" by Oranger`); + + document.body.style.backgroundColor = "#dcdcde"; + let needGravity = []; + let s = { //mech statue + x: -200, + y: -2350, + angle: 0, + scale: 15, + h: { //hip + x: 12, + y: 24 + }, + k: { //knee + x: -30.96, //-17.38 + y: 58.34, //70.49 + //x2: -33.96, //x - 3 + //y2: 58.34 //same as y + }, + f: { //foot + x: 0, + y: 91 //112 + }, + fillColor: "#ccc", //white + fillColorDark: "#bbb", //25% from white + lineColor: "#999", //#333 + lineColorLight: "#aaa" //#4a4a4a + } + const boost1 = level.boost(2550, 1500, 1700) + const boost2 = level.boost(-3400, -2050, 2100) + + level.custom = () => { + boost1.query(); + boost2.query(); + + level.exit.drawAndCheck(); + + level.enter.draw(); + for (let i = 0; i < needGravity.length; i++) { + needGravity[i].force.y += needGravity[i].mass * simulation.g; + } + ctx.fillStyle = "#444" //light fixtures + ctx.fillRect(2350, 995, 40, 10) + //ctx.fillRect(2280, -6005, 40, 10) + + //statue + ctx.save(); + ctx.translate(s.x, s.y); + //statueLeg is at the bottom, below the enemies but above the NGON function + statueLeg(-3, s.lineColorLight); + statueLeg(0, s.lineColor); + //head + ctx.rotate(s.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30 * s.scale, 0, 2 * Math.PI); + let grd = ctx.createLinearGradient(-30 * s.scale, 0, 30 * s.scale, 0); + grd.addColorStop(0, s.fillColorDark); + grd.addColorStop(1, s.fillColor); + ctx.fillStyle = grd; + ctx.fill(); + ctx.arc(15 * s.scale, 0, 4 * s.scale, 0, 2 * Math.PI); + ctx.strokeStyle = s.lineColor; + ctx.lineWidth = 2 * s.scale; + ctx.stroke(); + ctx.restore(); + }; + + level.customTopLayer = () => { + //boost chute for lack of a better name + ctx.fillStyle = "rgba(60,60,60,0.9)"; + ctx.fillRect(-3451, -4000, 202, 1500); + ctx.fillRect(2499, -170, 202, 1170); + + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.beginPath(); //basement + ctx.moveTo(2360, 1000); + ctx.lineTo(2120, 900); + ctx.lineTo(1500, 900); + ctx.lineTo(1500, 1500); + ctx.lineTo(3000, 1500); + ctx.lineTo(3000, 1000); + ctx.lineTo(2380, 1000); + ctx.lineTo(2870, 1500); + ctx.lineTo(1870, 1500); + ctx.lineTo(2360, 1000); + ctx.fill(); + // ctx.beginPath(); //exit + // ctx.moveTo(1600, -6000); + // ctx.lineTo(1600, -5000); + // ctx.lineTo(3000, -5000); + // ctx.lineTo(3000, -6000); + // ctx.lineTo(2310, -6000); + // ctx.lineTo(2600, -5000); + // ctx.lineTo(2000, -5000); + // ctx.lineTo(2290, -6000); + // ctx.lineTo(1600, -6000); + // ctx.fill(); + + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fillRect(1600, -1000, 1400, 830); + ctx.fillRect(1600, -170, 520, 170); + ctx.fillRect(-1300, -200, 2200, 200); //statue base + ctx.fillRect(-800, -400, 1200, 200); + ctx.fillRect(-500, -700, 600, 300); + //ctx.fillRect(-4000, -6000, 2000, 1000); //left side + ctx.fillRect(-4000, -2500, 2000, 2500); + }; + + level.setPosToSpawn(1810, 1450); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 2700; + level.exit.y = -4030; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + level.defaultZoom = 3500 + simulation.zoomTransition(level.defaultZoom) + + // powerUps.spawnStartingPowerUps(1475, -1175); + spawn.debris(2750, 1500, 200, 4); //16 debris per level + spawn.debris(1770, -350, 120, 4); //16 debris per level + spawn.debris(-3200, 0, 1000, 6); //16 debris per level + + //boundaries + spawn.mapRect(-4100, 1500, 7200, 100); //base floor + spawn.mapRect(3000, -4000, 100, 5600); //right barrier + spawn.mapRect(-4100, -4000, 100, 5600); //left barrier + //spawn.mapRect(1600, -10000, 1500, 4000); //upper right wall + //spawn.mapRect(-4100, -10000, 2100, 4000); //upper left wall + spawn.mapRect(1600, -4000, 1500, 3000); //right wall + spawn.mapRect(-4100, 0, 5600, 1550); //floor + + //starting room + spawn.mapRect(1500, 0, 700, 900); + spawn.mapRect(2120, -170, 380, 1170); + spawn.mapRect(2700, -170, 400, 1170); + //spawn.mapVertex(2296, 400, "0 0 0 1200 300 1200 400 0"); + //spawn.mapVertex(2904, 400, "0 0 0 1200 -300 1200 -400 0"); + + //left area + spawn.mapRect(-3500, -300, 300, 400); //floor 1 + spawn.mapRect(-3900, -600, 300, 100); + spawn.mapRect(-3500, -900, 300, 100); + spawn.mapRect(-3100, -1150, 1000, 150); //floor 2 + spawn.mapRect(-2200, -2600, 200, 1600); + spawn.mapRect(-2700, -1450, 300, 100); + spawn.mapRect(-3100, -1750, 300, 100); + spawn.mapRect(-3500, -2050, 300, 100); + spawn.mapRect(-4100, -4000, 650, 1500); //floor 3 + spawn.mapRect(-3250, -4000, 1250, 1500); + + //statue base + spawn.mapRect(-700, -900, 1000, 200); //top + //left + spawn.mapRect(-700, -900, 200, 500); + spawn.mapRect(-1000, -600, 500, 200); + spawn.mapRect(-1000, -600, 200, 400); + spawn.mapRect(-1300, -300, 500, 100); + //right + spawn.mapRect(100, -900, 200, 500); + spawn.mapRect(100, -600, 500, 200); + spawn.mapRect(400, -600, 200, 400); + spawn.mapRect(400, -300, 500, 100); + + hangingNGON(-1900, -4000, 1, 1000, 1, false, { + density: 0.001, //default density is 0.001 + friction: 0.0001, + frictionAir: 0.001, + frictionStatic: 0, + restitution: 0, + isNotHoldable: true + }); + hangingNGON(1900, -4600, 0.2, 300, 0.0005, false, { + density: 0.00005, //default density is 0.001 + friction: 0.0001, + frictionAir: 0.003, + frictionStatic: 0, + restitution: 1, + isNotHoldable: true + }); + + // // Never gonna give you up + // spawn.bodyRect(-8000, -10100, 15, 100); + // // Never gonna let you down + // spawn.bodyRect(-7915, -10100, 15, 100); + // // Never gonna run around and desert you + // body[body.length] = Bodies.polygon(-7950, -10025, 0, 25, { //circle + // friction: 0.05, + // frictionAir: 0.001 + // }); + // // Never gonna make you cry + // spawn.bodyRect(6985, -10100, 15, 100); + // // Never gonna say goodbye + // spawn.bodyRect(6900, -10100, 15, 100); + // // Never gonna tell a lie and hurt you + // body[body.length] = Bodies.polygon(6950, -10025, 0, 25, { //circle + // friction: 0.05, + // frictionAir: 0.001 + // }); + + //pile of blocks + spawn.bodyRect(1920, -400, 200, 400) + spawn.bodyRect(1720, -250, 200, 250) + spawn.bodyRect(1770, -300, 150, 50) + spawn.bodyRect(2120, -280, 100, 100) + spawn.bodyRect(1990, -500, 100, 100) + + //doors under statue + spawn.bodyRect(850, -50, 50, 50) + spawn.bodyRect(850, -100, 50, 50) + spawn.bodyRect(850, -150, 50, 50) + spawn.bodyRect(850, -200, 50, 50) + spawn.bodyRect(-1300, -50, 50, 50) + spawn.bodyRect(-1300, -100, 50, 50) + spawn.bodyRect(-1300, -150, 50, 50) + spawn.bodyRect(-1300, -200, 50, 50) + + // on the statue base + spawn.randomMob(700 + Math.random() * 100, -500 + Math.random() * 100, 1); + spawn.randomMob(400 + Math.random() * 100, -800 + Math.random() * 100, 0.4); + spawn.randomMob(100 + Math.random() * 100, -1100 + Math.random() * 100, -0.2); + spawn.randomGroup(-200, -1400, -0.4); + spawn.randomMob(-600 + Math.random() * 100, -1100 + Math.random() * 100, -0.2); + spawn.randomMob(-900 + Math.random() * 100, -800 + Math.random() * 100, 0.4); + spawn.randomMob(-1200 + Math.random() * 100, -500 + Math.random() * 100, 1); + + //in the statue base + spawn.randomSmallMob(400 + Math.random() * 300, -150 + Math.random() * 100, 0.2); + spawn.randomSmallMob(-1100 + Math.random() * 300, -150 + Math.random() * 100, 0.2); + + //bottom left + spawn.randomMob(-2600 + Math.random() * 300, -700 + Math.random() * 300, 0.6); + spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0.2); + spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0); + spawn.randomMob(-3900 + Math.random() * 100, -200 + Math.random() * 100, 0.6); + spawn.randomMob(-3400 + Math.random() * 100, -400, 0.4); + spawn.randomSmallMob(-3800 + Math.random() * 100, -700, -0.4); + spawn.randomMob(-3400 + Math.random() * 100, -1000, 0.6); + spawn.randomMob(-3000 + Math.random() * 100, -1850, 0); + spawn.randomGroup(-2700, -2000, 0.4); + + //top left + spawn.randomSmallMob(-3800, -5800, -0.2); + spawn.randomSmallMob(-2400, -5200, 0.2); + + //top right + spawn.randomGroup(2000, -5700, 0.6); + + powerUps.addResearchToLevel() //needs to run after mobs are spawned + let bosses = ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "laserBoss", "growBossCulture"]; + let abc = Math.random(); + if (simulation.difficulty > 3) { + if (abc < 0.6) { + spawn.randomLevelBoss(-1500 + Math.random() * 250, -1100 + Math.random() * 200, bosses); + } else if (abc < 0.85) { + spawn.laserBoss(-350 + Math.random() * 300, -600 + Math.random() * 200); + } else { + spawn.randomLevelBoss(850 + Math.random() * 250, -1100 + Math.random() * 200, bosses); + } + } + spawn.secondaryBossChance(850 + Math.random() * 250, -1100 + Math.random() * 200) + + //draw leg for statue + function statueLeg(shift, color) { + ctx.save(); + ctx.translate(shift, shift); + //front leg + let stroke = color; + ctx.beginPath(); + ctx.moveTo((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale); + ctx.lineTo((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale); + ctx.lineTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7 * s.scale; + ctx.stroke(); + //toe lines + ctx.beginPath(); + ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); + ctx.lineTo((s.f.x - 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale); + ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); + ctx.lineTo((s.f.x + 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale); + ctx.lineWidth = 4 * s.scale; + ctx.stroke(); + //hip joint + ctx.beginPath(); + ctx.arc((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale, 11 * s.scale, 0, 2 * Math.PI); + //knee joint + ctx.moveTo((s.k.x + 7 + 2 * shift) * s.scale, (s.k.y + shift) * s.scale); + ctx.arc((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale, 7 * s.scale, 0, 2 * Math.PI); + //foot joint + ctx.moveTo((s.f.x + 6 + shift) * s.scale, (s.f.y + shift) * s.scale); + ctx.arc((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale, 6 * s.scale, 0, 2 * Math.PI); + ctx.fillStyle = s.fillColor; + ctx.fill(); + ctx.lineWidth = 2 * s.scale; + ctx.stroke(); + ctx.restore(); + } + + // | | | | | + // n - g o n + //when s = 1 (scale), it's 3408 long and 800 tall (height of g) + function hangingNGON(x, y, s, height, stiffness, pin, properties) { + //makes a compound part of 3 bodyVertex parts + function compound3Parts(x1, y1, v1, x2, y2, v2, x3, y3, v3, properties) { + const part1 = Matter.Bodies.fromVertices(x1, y1, Vertices.fromPath(v1), properties); + const part2 = Matter.Bodies.fromVertices(x2, y2, Vertices.fromPath(v2), properties); + const part3 = Matter.Bodies.fromVertices(x3, y3, Vertices.fromPath(v3), properties); + const compoundParts = Body.create({ + parts: [part1, part2, part3], + }); + Composite.add(engine.world, [compoundParts]); + needGravity[needGravity.length] = compoundParts; + composite[composite.length] = compoundParts; + body[body.length] = part1; + body[body.length] = part2; + body[body.length] = part3; + setTimeout(function () { + compoundParts.collisionFilter.category = cat.body; + compoundParts.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + }, 1000); + } + + //for attaching the block to a point + function addConstraint(x, y, px, py, stiff, body, pin = false) { + if (pin) { + map[map.length] = Bodies.polygon(x, y, 0, 15); //circle above + } + cons[cons.length] = Constraint.create({ + pointA: { + x: x, + y: y + }, + bodyB: body, + pointB: { + x: px, + y: py + }, + stiffness: stiff + }); + Composite.add(engine.world, cons[cons.length - 1]); + } + + //I SINCERELY APOLOGIZE FOR THE ILLEGIBLE BLOCKS OF STRING CONCATENATION + //s is scale + + //n + compound3Parts( + x + 100 * s, + y + 310 * s, + ("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s), + x + 300 * s, + y + 160 * s, + (200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s), + x + 499 * s, + y + 333.3 * s, + (400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s), + properties + ); + addConstraint(x + 300 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin); + + //- + spawn.bodyRect(x + 800 * s, y + 250 * s, 200 * s, 100 * s, 1, properties); + body[body.length - 1].frictionAir = 0.05 //friction to make jump easier + addConstraint(x + 900 * s, y - height, 0, -30 * s, stiffness, body[body.length - 1], pin); + + //g + compound3Parts( + x + 1400 * s, + y + 300 * s, + ("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 450 * s + " " + 275 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s), + x + 1636 * s, + y + 633 * s, + (425 * s + " " + 450 * s + " " + 425 * s + " " + 750 * s + " " + 375 * s + " " + 800 * s + " " + 275 * s + " " + 675 * s + " " + 275 * s + " " + 600 * s), + x + 1398 * s, + y + 737 * s, + (375 * s + " " + 800 * s + " " + -75 * s + " " + 800 * s + " " + -75 * s + " " + 675 * s + " " + 275 * s + " " + 675 * s), + properties + ); + addConstraint(x + 1500 * s, y - height, 0, -15 * s, stiffness, composite[composite.length - 1], pin); + + //o + spawn.bodyVertex( + x + 2300 * s, + y + 300 * s, + ("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 425 * s + " " + 250 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s), + properties + ); + addConstraint(x + 2300 * s, y - height, 0, -10 * s, stiffness, body[body.length - 1], pin); + + //n + compound3Parts( + x + 2900 * s, + y + 310 * s, + ("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s), + x + 3100 * s, + y + 160 * s, + (200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s), + x + 3300 * s, + y + 333.3 * s, + (400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s), + properties + ); + addConstraint(x + 3100 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin); + } + }, + tunnel() { // by Scarlettt + simulation.makeTextLog(`tunnel by Scarlettt`); + + level.custom = () => { + level.exit.drawAndCheck(); + + //enter + ctx.beginPath(); + ctx.moveTo(level.enter.x, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y - 80); + ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80); + ctx.lineTo(level.enter.x + 100, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y + 30); + ctx.fillStyle = "#013"; + ctx.fill(); + + //exit + ctx.beginPath(); + ctx.moveTo(level.exit.x, level.exit.y + 30); + ctx.lineTo(level.exit.x, level.exit.y - 80); + ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80); + ctx.lineTo(level.exit.x + 100, level.exit.y + 30); + ctx.lineTo(level.exit.x, level.exit.y + 30); + ctx.fillStyle = "#9ff"; + ctx.fill(); + + // hiding rooms in path to second floor + ctx.fillStyle = "#322"; + ctx.fillRect(3750, -1650, 3500, 350); + + // prevent the user from getting into the secreter room without defeating all mobs + if (m.pos.x > 1500 && m.pos.x < 2500 && m.pos.y > -4000 && m.pos.y < -3500 && mob.reduce((a, i) => { + return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); + }, false) && !emergencyActivated) { + Matter.Body.setPosition(player, { + x: 2800, + y: m.pos.y + }); + } + + button.query(); + isButtonTapped = isButtonTapped || !button.isUp; + hazard.level(!isButtonTapped); + if (Matter.Query.region([player], hazard).length) m.energy -= 0.001; + + buttonSec.query(); + buttonSec.draw(); + if (!buttonSec.isUp && !hasSecretButton) { + for (var i = 0; i < 5; i++) { + powerUps.spawn(3614, -3700, "ammo"); + } + hasSecretButton = true; + } + buttonThird.query(); + buttonThird.draw(); + if (!buttonThird.isUp && !hasSecretButton2) { + for (var i = 0; i < 1; i++) powerUps.spawn(1614, -3700, "research"); + hasSecretButton2 = true; + } + if (!buttonSec.isUp) { + secretAnimTrans += 2; + secretAnimTime = 1; + secretAnimTrans = Math.max(0, Math.min(secretAnimTrans, 60)); + } else { + secretAnimTrans--; + if (secretAnimTime) secretAnimTrans += 3; + secretAnimTrans = Math.min(60, Math.max(secretAnimTrans, 0)); + } + if (secretAnimTime > 0) { + secretAnimTime++; + if (secretAnimTime > 150) secretAnimTime = 0; + } + + if (emergencyActivated || !buttonThird.isUp) { + secretAnimTrans2 += 2; + secretAnimTime2 = 1; + secretAnimTrans2 = Math.max(0, Math.min(secretAnimTrans2, 60)); + } else { + secretAnimTrans2--; + if (secretAnimTime2) secretAnimTrans2 += 3; + secretAnimTrans2 = Math.min(60, Math.max(secretAnimTrans2, 0)); + } + if (secretAnimTime2 > 0) { + secretAnimTime2++; + if (secretAnimTime2 > 150) secretAnimTime2 = 0; + } + + + + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 200, 0, 2 * Math.PI); + ctx.fillStyle = "#ff25"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 400, 0, 2 * Math.PI); + ctx.fillStyle = "#ff22"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 700, 0, 2 * Math.PI); + ctx.fillStyle = "#ff21"; + ctx.fill(); + elevator.move(); + elevator.drawTrack(); + }; + level.customTopLayer = () => { + hazard.query(); + secretHazard.level(emergencyActivated); + secretHazard.query(); + button.draw(); + + // Fire damage + let isInRange = flames.reduce((a, i) => a || Math.sqrt((m.pos.x - i[0]) * (m.pos.x - i[0]) + (m.pos.y + 90 - i[1]) * (m.pos.y + 90 - i[1])) < 50, false); + + if (isInRange) { + fireDmgLevel++; + fireDmgLevel = Math.min(fireDmgLevel, 100); + } else { + fireDmgLevel--; + fireDmgLevel = Math.max(fireDmgLevel, -8); + } + + if (fireDmgLevel > -8) { + ctx.fillStyle = "#fa0b"; + ctx.fillRect(m.pos.x - 50, m.pos.y - 100, Math.min(fireDmgLevel * 12.5 + 100, 100), 15); + } + + if (fireDmgLevel > 0) { + ctx.fillStyle = "#f00c"; + ctx.fillRect(m.pos.x - 50, m.pos.y - 100, fireDmgLevel, 15); + + m.damage(0.001 * (1.5 * isInRange + 1)); + + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); + drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); + drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); + drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); + drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); + } + + for (let j = 0; j < 5; j++) { + drawFlame(1130 + j * 10, -1700) + for (let i = 0; i < 7; i++) drawFlame(2550 + i * 200, -2800); + for (let i = 0; i < 10; i++) drawFlame(2800 + i * 500, -1650); + for (let i = 0; i < 9; i++) drawFlame(1595 + i * 95, -3860); + drawFlame(4850, -1300); + drawFlame(6350, -1300); + } + ctx.fillStyle = "#541"; + for (let i = 0; i < 9; i++) { + ctx.fillRect(1592 + i * 95, -3860, 6, 70); + } + + if (m.pos.x > 1500 && m.pos.x < 3750 && m.pos.y > -5000 && m.pos.y < -3300) { + secretRoomTrans -= 5; + secretRoomTrans = Math.max(secretRoomTrans, 85); + } else { + secretRoomTrans += 5; + secretRoomTrans = Math.min(secretRoomTrans, 250); + } + + + let hasMob = mob.reduce((a, i) => { + return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); + }, false) && !emergencyActivated; + + door.isClosing = hasMob; + + door.openClose(); + ctx.fillStyle = "#444444" + secretRoomTrans.toString(16); + ctx.fillRect(1480, -5000, 2270, 1710); + if (hasMob) { + ctx.fillStyle = "#444"; + ctx.fillRect(1480, -5000, 1070, 1710); + } + + if (secretAnimTrans > 0) { + drawProject([3614, -3530], [2900, -3900], [3400, -3600], secretAnimTrans, 60); + if (secretAnimTrans >= 42) { + ctx.font = "27px monospace"; + ctx.textAlign = "start" + ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); + ctx.fillText("Waste Discharge Interruption:", 2910, -3870); + ctx.fillText("Owner 'Scarlet' not found", 2910, -3830); + ctx.fillText("Detected user: 'm'", 2910, -3790); + ctx.fillStyle = (hasMob ? "#ff6644" : "#ffff44") + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); + ctx.fillText(hasMob ? "AREA HAS MOBS." : "Area clear.", 2910, -3710); + ctx.fillText(hasMob ? "'openDoor' failed." : "'openDoor' complete.", 2910, -3670); + ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans - 40) * 6).toString(16); + ctx.beginPath(); + ctx.arc(3300, -3730, 60, 0, 2 * Math.PI); + ctx.stroke(); + ctx.arc(3330, -3730, 8, 0, 2 * Math.PI); + ctx.lineWidth = 4; + ctx.stroke(); + ctx.textAlign = "center"; + ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); + ctx.font = "30px monospace"; + ctx.fillText("n-gon inc", 3300, -3630); + + ctx.font = "25px Arial"; + } + } + if (secretAnimTrans2 > 0) { + drawProject([1614, -3530], [2050, -3900], [1550, -3600], secretAnimTrans2, 60); + if (secretAnimTrans2 >= 42) { + ctx.font = "27px monospace"; + ctx.textAlign = "start"; + ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); + ctx.fillText("SECURITY BREACH DETECTED", 1560, -3870); + ctx.fillText("Entity name: m", 1560, -3830); + ctx.fillStyle = (tech.totalCount < 25 ? (tech.totalCount < 10 ? "#ffff44" : "#22ff22") : "#ff6644") + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); + ctx.fillText("Threat level: " + (tech.totalCount < 25 ? (tech.totalCount < 10 ? "Low" : "Medium") : "HIGH"), 1560, -3790); + if (tech.totalCount >= 15) ctx.fillText("PROCEDURE ACTIVATED", 1560, -3750); + ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans2 - 40) * 6).toString(16); + ctx.beginPath(); + ctx.arc(1950, -3730, 60, 0, 2 * Math.PI); + ctx.stroke(); + ctx.arc(1980, -3730, 8, 0, 2 * Math.PI); + ctx.lineWidth = 4; + ctx.stroke(); + ctx.textAlign = "center"; + ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); + ctx.font = "30px monospace"; + ctx.fillText("n-gon inc", 1950, -3630); + + ctx.font = "25px Arial"; + if (secretAnimTrans2 >= 60) { + if (!emergencyActivated && tech.totalCount >= 10) { + for (let i = 0; i < 5; i++) { + spawn.exploder(1614, -3900); + if (tech.totalCount >= 25) spawn.randomMob(1614, -3900, Infinity); + } + emergencyActivated = true; + } + } + } + } + }; + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 8500; + level.exit.y = 680; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#123"; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + + // spawn blocks + spawn.mapRect(-100, 0, 1050, 100); + spawn.mapRect(900, -300, 50, 300); + spawn.mapRect(700, -300, 50, 200); + + // first room + spawn.mapRect(-100, -350, 850, 50); + spawn.mapRect(900, -350, 850, 50); + spawn.mapRect(-100, -1550, 50, 1200); + spawn.mapRect(1700, -1550, 50, 1200); + spawn.mapRect(-100, -1550, 850, 50); + spawn.mapRect(900, -1550, 850, 50); + spawn.bodyRect(700, -400, 50, 50); + spawn.bodyRect(900, -400, 50, 50); + + spawn.mapRect(500, -650, 650, 25); + spawn.mapRect(200, -1000, 200, 25); + spawn.mapRect(1250, -1000, 200, 25); + spawn.mapRect(600, -1300, 450, 25); + + spawn.mapRect(700, -1650, 50, 100); + spawn.mapRect(900, -1650, 50, 100); + + + // pathway to second room + spawn.mapRect(950, -1650, 3050, 50); + spawn.mapRect(1100, -1700, 100, 50); + + // second room + spawn.mapRect(0, -5000, 1500, 3000); + spawn.mapRect(1500, -2050, 300, 50); + spawn.mapRect(2000, -3100, 300, 1100); + spawn.mapRect(1500, -5000, 2250, 1000); + spawn.mapRect(1500, -3500, 1050, 225); + spawn.mapRect(4000, -5000, 500, 3000); + spawn.mapRect(3748, -5000, 252, 1550); + + spawn.mapRect(1700, -2400, 300, 50); + spawn.mapRect(1500, -2750, 300, 50); + + spawn.mapRect(2300, -3000, 1700, 50); + spawn.mapRect(2300, -2800, 1700, 800); + spawn.mapRect(2450, -3300, 1300, 100); + + // secret room in second room + spawn.mapRect(2700, -3500, 1050, 50); + spawn.mapRect(2549, -5000, 1201, 1000); + + const buttonSec = level.button(3550, -3500); + const buttonThird = level.button(1550, -3500); + let hasSecretButton = false, + hasSecretButton2 = false, + secretAnimTrans = 0, + secretAnimTime = 0, + secretAnimTrans2 = 0, + secretAnimTime2 = 0; + let emergencyActivated = false; + + const door = level.door(2450, -4000, 100, 500, 490); + const secretHazard = level.hazard(1500, -4000, 1000, 510, 0.01); + + // hazards + const button = level.button(3800, -3000); + const hazard = level.hazard(2300, -3090, 1700, 110, 0.005); + let isButtonTapped = false; + + // if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun"); + powerUps.spawn(3900, -3100, "heal", true, null, 30); + powerUps.spawn(3900, -3100, "heal", true, null, 30); + + // path to the third room + spawn.mapRect(2000, -1850, 50, 200); + spawn.mapRect(2200, -2000, 50, 200); + spawn.mapRect(2400, -1850, 50, 200); + + spawn.mapRect(4200, -1650, 1300, 50); + spawn.mapRect(5700, -1650, 1300, 50); + spawn.mapRect(7200, -1650, 750, 50); + + spawn.mapRect(3700, -1600, 50, 350); + spawn.mapRect(7250, -1600, 50, 350); + spawn.mapRect(3750, -1300, 3500, 50); + spawn.mapRect(4500, -2150, 3550, 50) + + // third room + spawn.mapRect(7900, -1600, 50, 1000); + spawn.mapRect(8050, -3000, 50, 2400); + spawn.mapRect(7000, -600, 950, 50); + spawn.mapRect(8050, -600, 950, 50); + spawn.mapRect(7000, -600, 50, 1000); + spawn.mapRect(8950, -600, 50, 1000); + spawn.mapRect(7000, 400, 950, 50); + spawn.mapRect(8050, 400, 950, 50); + spawn.mapRect(7900, 400, 50, 300); + spawn.mapRect(7900, 700, 1000, 50); + + const elevator = level.elevator(7962.5, 500, 75, 50, -1800) + + + // fire damage + const flames = []; + flames.push([1150, -1700], [1150, -1770]); + for (let i = 0; i < 10; i++) flames.push([2800 + i * 500, -1650], [2800 + i * 500, -1720]); + flames.push([4850, -1300], [6350, -1300], [4850, -1370], [6350, -1370]); + + let fireDmgLevel = -8; + + let secretRoomTrans = 250; + + // mobs + let mobList1 = [ + [500, -750], + [1150, -750], + [825, -1100], + [300, -1100], + [1350, -1100] + ]; + while (mobList1.length > 5 - Math.sqrt(simulation.difficulty * 2.5) && mobList1.length) { + let rand = Math.floor(Math.random() * mobList1.length); + spawn[["hopper", "sneaker", "striker"][Math.floor(Math.random() * 3)]](mobList1[rand][0], mobList1[rand][1], 60 + Math.random() * 10); + mobList1.splice(rand, 1); + } + + let hasLaser = spawn.pickList.includes("laser"); + if (hasLaser) spawn.pickList.splice(spawn.pickList.indexOf("laser"), 1); + let mobList2 = [ + [50, -1400], + [1600, -450], + [50, -450], + [1600, -1400] + ]; + for (let i = 0; i < 10; i++) mobList2.push([2800 + i * 500, -1800]); + while (mobList2.length && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)) { + let rand = Math.floor(Math.random() * mobList2.length); + spawn.randomMob(...mobList2[rand]); + mobList2.splice(rand, 1); + } + + let groupList = ["spawn.randomGroup(8250, 575);", + `spawn.randomGroup(3200, -3700); + if (simulation.difficulty > 15) + spawn.randomGroup(3500, -3700, 0.3);`, + "spawn.randomGroup(7800, -1800, 0.5);" + ]; + while (groupList.length > 0) { + let ind = Math.floor(Math.random() * groupList.length); + Function(groupList[ind])(); + groupList.splice(ind, 1); + } + if (hasLaser) spawn.pickList.push("laser"); + + spawn.shieldingBoss(3900, -3200, 70); + + let randomBoss = Math.floor(Math.random() * 2); + if (simulation.difficulty > 5) spawn[["shooterBoss", "launcherBoss"][randomBoss]](7500, -150, 100, false); + else spawn[["shooter", "launcher"][randomBoss]](7500, -150, 150); + spawn[["shooter", "launcher"][randomBoss]](8500, -150, 150); + + // canvas stuff + function drawFlame(x, y, color = "#f81", angle = Math.PI / 2) { + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.strokeStyle = color; + ctx.lineWidth = 3; + for (let i = 0; i < 3; i++) { + let randAng = (Math.random() - 0.5) * 2 + angle; + randLen = 30 + Math.random() * 10; + + x = x + Math.cos(randAng) * randLen; + y = y - Math.sin(randAng) * randLen; + ctx.lineTo(x, y); + } + ctx.stroke(); + } + + function drawProject(startPos, endPos1, endPos2, tValue, tValueM) { + ctx.strokeStyle = "#003"; + ctx.fillStyle = "#0055aa" + ('0' + (tValue * 60 / tValueM).toString(16)).slice(-2); + + let inter = (tValueM - tValue) / tValueM; + let endpos1i = endPos1.map((i, j) => (startPos[j] - i) * inter), + endpos2i = endPos2.map((i, j) => (startPos[j] - i) * inter); + + ctx.beginPath(); + ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]); + ctx.lineTo(...startPos); + ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]); + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]); + ctx.lineTo(...startPos); + ctx.lineTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]); + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]); + ctx.lineTo(...startPos); + ctx.lineTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]); + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]); + ctx.lineTo(...startPos); + ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]); + ctx.fill(); + ctx.stroke(); + + if (tValue >= tValueM * 2 / 3) { + ctx.fillStyle = "#0055aa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 6.25 * 60 / tValueM).toString(16)).slice(-2); + ctx.strokeStyle = "#000033" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2); + ctx.fillRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]); + ctx.shadowColor = "#00aaaa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2); + ctx.shadowBlur = 10; + ctx.strokeRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]); + ctx.shadowBlur = 0; + ctx.shadowColor = "#0000"; + } + } + }, + run() { + simulation.makeTextLog(`run by iNoobBoi`); + + addPartToMap = (len) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + map[len].collisionFilter.category = cat.map; + map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[len], true); //make static + Composite.add(engine.world, map[len]); + } + + anotherBoss = (x, y) => { + if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { + tech.isScaleMobsWithDuplication = true + spawn.randomLevelBoss(x, y, ["historyBoss"]); + tech.isScaleMobsWithDuplication = false + } else if (tech.isResearchBoss) { + if (powerUps.research.count > 2) { + powerUps.research.changeRerolls(-3) + simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) + } else { + tech.addJunkTechToPool(0.49) + } + spawn.randomLevelBoss(x, y, ["historyBoss"]); + } + } + + const climbPad = level.boost(8200, -200, 500); + var climbTime = false; + var climbGroup = 0; + var initialSpawn = false; + var endTime = false; + + let runMobList = [ + "hopper", + "slasher", + "striker", + "stabber", + "springer", + "pulsar", + "sneaker", + "spinner", + "grower", + "focuser", + "spawner", + ]; + + let removeList = []; + + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + + climbPad.query(); + + if (m.pos.x > 8000 && climbTime === false) { + spawn.mapRect(7800, -900, 200, 900); + addPartToMap(map.length - 1); + simulation.draw.setPaths(); + + simulation.makeTextLog(`UNKNOWN: "Well done. Now climb."`, 600); + simulation.makeTextLog(`UNKNOWN: "I left a gift at the top."`, 600); + + climbTime = true; + } //toggles on a mapRect when player passes a certain area + + if (m.pos.x > 9000 && endTime === false) { + simulation.makeTextLog("UNKNOWN: \"Good luck. I hope you get out of here.\"", 600); + endTime = true; + } + + for (i in mob) { + mob[i].damageReduction = 0; + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * 0.97, + y: mob[i].velocity.y * 0.97 + }); + } //makes everything slow and immune + }; + + level.customTopLayer = () => { + ctx.fillStyle = "#888"; + + if (climbGroup === 0) { + //toggle on fillRect: 1 + ctx.fillRect(8000, -900, 300, 100); + ctx.fillRect(8500, -1800, 300, 100); + ctx.fillRect(8300, -2700, 300, 100); + ctx.fillRect(8000, -3600, 300, 100); + ctx.fillRect(8200, -4500, 300, 100); + } else if (climbGroup === 1) { + //toggle on fillRect: 2 + ctx.fillRect(8300, -1200, 300, 100); + ctx.fillRect(8500, -2100, 300, 100); + ctx.fillRect(8100, -3000, 300, 100); + ctx.fillRect(8000, -3900, 300, 100); + ctx.fillRect(8200, -4800, 300, 100); + } else if (climbGroup === 2) { + //toggle on fillRect: 0 + ctx.fillRect(8500, -600, 300, 100); + ctx.fillRect(8100, -1500, 300, 100); + ctx.fillRect(8000, -2400, 300, 100); + ctx.fillRect(8500, -3300, 300, 100); + ctx.fillRect(8500, -4200, 300, 100); + } + + if ((simulation.cycle % 120) === 0) { + for (var i = 0; i < map.length; i++) { + if (map[i].isRemove) { + Matter.Composite.remove(engine.world, map[i]); + map.splice(i, 1); + } + } + + if (climbGroup === 0) { + //toggle on platforms: 0 + spawn.mapRect(8000, -900, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8500, -1800, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8300, -2700, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8000, -3600, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8200, -4500, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + + climbGroup = 1; + } else if (climbGroup === 1) { + //toggle on platforms: 1 + spawn.mapRect(8300, -1200, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8500, -2100, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8100, -3000, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8000, -3900, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8200, -4800, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + + climbGroup = 2; + } else if (climbGroup === 2) { + //toggle on platforms: 2 + spawn.mapRect(8500, -600, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8100, -1500, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8000, -2400, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8500, -3300, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + spawn.mapRect(8500, -4200, 300, 100); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + + climbGroup = 0; + } + + simulation.draw.setPaths(); //update map graphics + } //every 120 cycles, first deletes previous group, then cycles through one of 3 toggle groups + }; + + if (!initialSpawn) { + level.defaultZoom = 1300 //was 800 I changed this + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + //Level + level.setPosToSpawn(-100, -1450); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + + level.exit.x = 9300; + level.exit.y = -5130; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + + //Map + spawn.mapRect(-1400, -2200, 1700, 200); + spawn.mapRect(100, -2200, 200, 1000); + spawn.mapRect(-600, -1400, 8600, 200); + spawn.mapRect(-1400, -2200, 200, 1000); + spawn.mapRect(-2800, -1400, 1600, 200); + spawn.mapRect(-2800, -1400, 200, 1400); + spawn.mapRect(-2800, -200, 11800, 200); + spawn.mapRect(-900, -600, 600, 600); + spawn.mapRect(200, -800, 500, 100); + spawn.mapRect(1300, -1400, 200, 900); + spawn.mapRect(1300, -600, 500, 100); + spawn.mapRect(2300, -800, 200, 200); + spawn.mapRect(2900, -400, 100, 400); + spawn.mapRect(3200, -600, 100, 600); + spawn.mapRect(3500, -800, 100, 800); + spawn.mapRect(4400, -900, 500, 100); + spawn.mapRect(4400, -600, 500, 100); + spawn.mapRect(4800, -900, 100, 400); + spawn.mapRect(5300, -550, 600, 550); + spawn.mapRect(5600, -900, 300, 800); + spawn.mapRect(6300, -300, 1100, 300); + spawn.mapRect(6600, -400, 500, 200); + spawn.mapRect(6600, -800, 500, 100); + spawn.mapRect(7000, -1400, 100, 700); + spawn.mapRect(7800, -5900, 200, 5100); + spawn.mapRect(7800, -5900, 1900, 200); + spawn.mapRect(9500, -5900, 200, 1000); + spawn.mapRect(8800, -5100, 900, 200); + spawn.mapRect(8800, -5100, 200, 5100); + + //Text + spawn.mapRect(400, -1600, 100, 10); + spawn.mapRect(400, -1600, 10, 100); + spawn.mapRect(490, -1600, 10, 40); + spawn.mapRect(400, -1570, 100, 10); + spawn.mapRect(400, -1540, 100, 10); + spawn.mapRect(490, -1540, 10, 40); + + spawn.mapRect(600, -1600, 10, 100); + spawn.mapRect(600, -1510, 100, 10); + spawn.mapRect(690, -1600, 10, 100); + + spawn.mapRect(800, -1600, 100, 10); + spawn.mapRect(800, -1600, 10, 100); + spawn.mapRect(890, -1600, 10, 100); + + spawn.mapRect(0, 0, 1, 1); //dont ask why i have these + spawn.mapRect(1, 0, 1, 1); //dont ask why i have these + spawn.mapRect(0, 1, 1, 1); //dont ask why i have these + spawn.mapRect(1, 1, 1, 1); //dont ask why i have these + spawn.mapRect(-1, 0, 1, 1); //dont ask why i have these + spawn.mapRect(0, -1, 1, 1); //dont ask why i have these + spawn.mapRect(-1, -1, 1, 1); //dont ask why i have these + spawn.mapRect(1, -1, 1, 1); //dont ask why i have these + spawn.mapRect(-1, 1, 1, 1); //dont ask why i have these + + //Mob Spawning + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"You cannot kill them.\"", 600); + }, 2000); + + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"But I have slowed them down for you.\"", 600); + }, 6000); + + + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -400); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1800, -1000); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3200, -1000); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6200, -400); + + if (simulation.difficulty > 10) { + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -400); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](2400, -400); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4000, -400); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6600, -1000); + + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"Run.\"", 600); + }, 10000); + } //some of the mobs + if (simulation.difficulty > 20) { + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -1000); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3100, -300); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4200, -1000); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7400, -800); + + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"RUN!\"", 600); + }, 11000); + } //most of the mobs + if (simulation.difficulty > 30) { + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -1000); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3400, -300); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](5200, -800); + spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7500, -300); + + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"GET OUT OF HERE.\"", 600); + }, 12000); + } //all the mobs + + //Boss Spawning + if (simulation.difficulty > 5) { + spawn.randomLevelBoss(-2200, -700, ["powerUpBossBaby", "blockBoss", "revolutionBoss"]); + + setTimeout(() => { + simulation.makeTextLog("UNKNOWN: \"They are coming for you.\"", 600); + }, 14000); + } + anotherBoss(-1800, -700); //custom second boss spawn + + //Blocks + spawn.bodyRect(1300, -500, 200, 100); + spawn.bodyRect(1400, -500, 200, 100); + spawn.bodyRect(1500, -500, 200, 100); + + spawn.bodyRect(5700, -1200, 100, 100); + spawn.bodyRect(5700, -1100, 100, 100); + spawn.bodyRect(5700, -1000, 100, 100); + + spawn.bodyRect(6800, -700, 100, 100); + spawn.bodyRect(6800, -600, 100, 100); + spawn.bodyRect(6800, -500, 100, 100); + + spawn.debris(4400, -300, 500, 16); + spawn.debris(3300, -600, 200, 6); + spawn.debris(3000, -500, 20, 6); + spawn.debris(2300, -300, 200, 6); + spawn.debris(200, -300, 500, 16); + + //Powerups + if (simulation.difficulty > 10) { + powerUps.spawn(1600, -700, "tech"); + } + powerUps.spawnRandomPowerUp(1700, -700); + + // if (simulation.difficulty > 20) { + // powerUps.spawn(4600, -700, "tech"); + // } + powerUps.spawnRandomPowerUp(4700, -700); + + // if (simulation.difficulty > 30) { + // powerUps.spawn(6800, -1000, "tech"); + // } + powerUps.spawnRandomPowerUp(6900, -1000); + + powerUps.spawn(9200, -5400, "tech"); + + if (simulation.difficulty > 10) { + powerUps.spawn(9200, -5500, "tech"); + } + // if (simulation.difficulty > 20) { + // powerUps.spawn(9200, -5600, "tech"); + // } + // if (simulation.difficulty > 30) { + // powerUps.spawn(9200, -5700, "tech"); + // } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + initialSpawn == true; + } + }, + islands() { + simulation.makeTextLog(`islands by Richard0820`); + + const boost1 = level.boost(58500, -18264, 1300); + let portal2, portal3; + // const removeIndex1 = map.length - 1; + const drip1 = level.drip(59300, -18975, -18250, 100); // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip2 = level.drip(60000, -18953, -18250, 150); + const drip3 = level.drip(60905, -18652, -18250, 70); + const slimePit1 = level.hazard(58850, -18300, 2275, 100, 0.01); //hazard(x, y, width, height, damage = 0.003) spawn.mapRect(58850, -18300, 2275, 100); + const slimePit2 = level.hazard(74400, -18075, 350, 100, 0.01); + let isSpawnedBoss = false; + level.custom = () => { + level.exit.drawAndCheck(); + boost1.query(); + level.enter.draw(); + drip1.draw(); + drip2.draw(); + drip3.draw(); + // portal[2].query(); + // portal[3].query(); + // portal[0].draw(); + // portal[1].draw(); + // portal[2].draw(); + // portal[3].draw(); + portal2[2].query(); + portal2[3].query(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + portal3[2].query(); + portal3[3].query(); + portal3[0].draw(); + portal3[1].draw(); + portal3[2].draw(); + portal3[3].draw(); + }; + level.customTopLayer = () => { + slimePit1.query(); + slimePit2.query(); + ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-17650 - m.pos.y) / 100, 0.99))})`; + ctx.fillRect(58390, -17655, 1490, 740); + }; + document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; + level.setPosToSpawn(57680, -18330); + level.exit.x = 76343; + level.exit.y = -18020; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 30); + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 30); + level.defaultZoom = 2000; + simulation.zoomTransition(level.defaultZoom); + // spawn.setSpawnList = [ + // "hopper", + // "slasher", + // "striker", + // "stabber", + // "springer", + // "pulsar", + // "sneaker", + // "spinner", + // "grower", + // "focuser", + // "spawner", + // ]; + spawn.mapRect(57800, -18550, 50, 100); + spawn.mapRect(57500, -18550, 50, 275); + spawn.mapRect(66900, -18675, 300, 200); + spawn.mapRect(66925, -19050, 125, 225); + spawn.mapRect(67825, -16975, 125, 100); + spawn.mapRect(74900, -18075, 225, 100); + spawn.mapRect(73925, -18225, 150, 275); + spawn.mapRect(76200, -18325, 50, 125); + spawn.mapRect(76525, -18325, 75, 400); + spawn.mapRect(61325, -18350, 50, 25); + spawn.mapRect(61450, -18425, 50, 25); + spawn.mapRect(61475, -18450, 25, 25); + spawn.mapRect(58725, -18350, 125, 50); + spawn.mapRect(58675, -18275, 50, 75); + spawn.mapRect(58600, -18275, 75, 75); + spawn.mapRect(58675, -18325, 50, 50); + spawn.mapRect(58250, -16925, 1825, 1050); + spawn.mapRect(57500, -18200, 4475, 550); + spawn.mapRect(61500, -18475, 475, 275); + spawn.mapRect(62175, -18575, 325, 400); + spawn.mapRect(62900, -18850, 525, 375); + spawn.mapRect(63900, -18925, 450, 400); + spawn.mapRect(64725, -19000, 625, 500); + spawn.mapRect(65825, -19050, 675, 400); + spawn.mapRect(66800, -18950, 400, 400); + spawn.mapRect(68775, -18850, 525, 400); + spawn.mapRect(67375, -16900, 1800, 1450); + spawn.mapRect(67375, -17475, 325, 575); + spawn.mapRect(68900, -17500, 250, 500); + spawn.mapRect(69425, -17050, 500, 475); + spawn.mapRect(70400, -17150, 425, 175); + spawn.mapRect(71175, -17325, 450, 325); + spawn.mapRect(72000, -17425, 325, 300); + spawn.mapRect(72725, -17450, 350, 275); + spawn.mapRect(70050, -18800, 550, 350); + spawn.mapRect(67750, -19400, 375, 1200); + spawn.mapRect(67750, -18200, 1425, 700); + spawn.mapRect(66800, -18550, 575, 1650); + spawn.mapRect(66800, -16900, 575, 1450); + spawn.mapRect(67350, -18175, 250, 750); + spawn.mapRect(71050, -18450, 725, 275); + spawn.mapRect(72100, -18150, 475, 200); + spawn.mapRect(73325, -17975, 3275, 475); + spawn.mapRect(73175, -17775, 150, 300); + spawn.mapRect(72975, -17675, 225, 250); + spawn.mapRect(76200, -18325, 400, 75); + spawn.mapRect(76525, -18250, 75, 275); + spawn.mapRect(76200, -18250, 50, 50); + spawn.mapRect(57500, -17675, 900, 1800); + spawn.mapRect(59875, -17675, 1975, 1800); + spawn.mapRect(57550, -18275, 225, 75); + spawn.mapRect(61375, -18375, 50, 50); + spawn.mapRect(61100, -18350, 75, 50); + spawn.mapRect(61175, -18325, 50, 25); + spawn.mapRect(61850, -16525, 250, 175); + spawn.mapRect(57500, -18500, 50, 325); + spawn.mapRect(57500, -18550, 350, 50); + spawn.mapRect(57800, -18500, 50, 50); + spawn.mapRect(61275, -18325, 375, 125); + spawn.mapRect(61425, -18400, 200, 75); + spawn.mapRect(62125, -18575, 125, 75); + spawn.mapRect(62250, -18200, 175, 125); + spawn.mapRect(62850, -18725, 100, 75); + spawn.mapRect(63075, -18550, 225, 225); + spawn.mapRect(62800, -18275, 75, 75); + spawn.mapRect(62500, -18475, 75, 50); + spawn.mapRect(63825, -18900, 150, 50); + spawn.mapRect(63950, -18575, 150, 125); + spawn.mapRect(64200, -18550, 100, 250); + spawn.mapRect(64925, -18525, 200, 275); + spawn.mapRect(64625, -18425, 75, 125); + spawn.mapRect(65225, -18675, 150, 175); + spawn.mapRect(65350, -18950, 75, 100); + spawn.mapRect(65950, -18575, 75, 150); + spawn.mapRect(66000, -18725, 225, 175); + spawn.mapRect(66275, -18675, 75, 125); + spawn.mapRect(66275, -18550, 75, 75); + spawn.mapRect(66150, -18550, 100, 50); + spawn.mapRect(66225, -18875, 25, 150); + spawn.mapRect(66200, -18750, 75, 25); + spawn.mapRect(66925, -19100, 125, 150); + spawn.mapRect(66000, -19100, 75, 50); + spawn.mapRect(65000, -19075, 100, 75); + spawn.mapRect(66750, -18625, 100, 100); + spawn.mapRect(68050, -18500, 350, 350); + spawn.mapRect(68125, -18975, 150, 475); + spawn.mapRect(69850, -18675, 150, 200); + spawn.mapRect(70000, -18625, 150, 50); + spawn.mapRect(68850, -18575, 325, 225); + spawn.mapRect(69100, -18400, 75, 100); + spawn.mapRect(70150, -18525, 125, 200); + spawn.mapRect(70425, -18525, 125, 200); + spawn.mapRect(70250, -18350, 175, 225); + spawn.mapRect(70325, -18475, 50, 150); + spawn.mapRect(70275, -18450, 150, 150); + spawn.mapRect(71175, -18250, 525, 250); + spawn.mapRect(71050, -18200, 150, 375); + spawn.mapRect(70925, -18300, 200, 250); + spawn.mapRect(71425, -18525, 175, 150); + spawn.mapRect(70225, -18950, 275, 250); + spawn.mapRect(70475, -17050, 225, 175); + spawn.mapRect(70625, -17250, 100, 150); + spawn.mapRect(71300, -17150, 200, 350); + spawn.mapRect(71100, -17250, 125, 100); + spawn.mapRect(71550, -17400, 150, 150); + spawn.mapRect(67675, -17150, 225, 300); + spawn.mapRect(68225, -17000, 100, 125); + spawn.mapRect(67900, -16975, 375, 100); + spawn.mapRect(68275, -16950, 150, 50); + spawn.bodyRect(76200, -18200, 50, 200); + spawn.mapRect(76200, -18000, 50, 25); + spawn.bodyRect(57800, -18450, 50, 175); + spawn.mapRect(68725, -17600, 300, 250); + spawn.mapRect(68625, -17550, 175, 100); + spawn.mapRect(68850, -17400, 150, 125); + spawn.mapRect(69325, -16900, 200, 225); + spawn.mapRect(69575, -16625, 175, 275); + spawn.mapRect(69850, -16875, 250, 200); + spawn.mapRect(69875, -16650, 150, 300); + spawn.mapRect(69825, -16800, 375, 325); + spawn.mapRect(69650, -16775, 325, 475); + spawn.mapRect(71975, -17325, 100, 125); + spawn.mapRect(72075, -17200, 150, 150); + spawn.mapRect(72275, -17350, 150, 150); + spawn.mapRect(72325, -17275, 150, 225); + spawn.mapRect(72225, -18050, 200, 225); + spawn.mapRect(71925, -18150, 250, 175); + spawn.mapRect(72075, -18275, 125, 175); + spawn.mapRect(72500, -18025, 125, 175); + spawn.mapRect(72400, -17975, 150, 175); + spawn.mapRect(73925, -18225, 350, 275); + spawn.mapRect(74750, -18125, 275, 175); + spawn.mapRect(74250, -18100, 150, 75); + spawn.mapRect(74275, -18050, 200, 75); + spawn.mapRect(73750, -18100, 275, 125); + spawn.mapRect(73075, -17475, 3525, 300); + spawn.mapRect(73275, -17600, 3325, 225); + spawn.mapRect(57775, -18250, 150, 50); + spawn.mapRect(57775, -18275, 75, 25); + spawn.mapRect(57925, -18225, 50, 25); + spawn.debris(68300, -17000, 3700, 16); + spawn.mapRect(62000, -16525, 100, 200); + spawn.mapRect(59125, -19125, 325, 200); + spawn.mapRect(59925, -19175, 350, 225); + spawn.mapRect(60800, -18850, 275, 200); + spawn.mapRect(75025, -18075, 200, 100); + spawn.mapRect(75225, -18025, 100, 50); + spawn.bodyRect(74300, -18150, 50, 25); + spawn.bodyRect(73850, -18150, 75, 75); + spawn.bodyRect(74700, -18000, 75, 50); + spawn.bodyRect(74250, -18325, 25, 25); + spawn.bodyRect(74275, -18325, 25, 25); + spawn.bodyRect(74275, -18325, 25, 25); + spawn.bodyRect(74300, -18325, 100, 25); + // portal = level.portal( + // { + // x: 58625, + // y: -16925, + // }, + // 1.5 * Math.PI, + // { + // //right + // x: 58625, + // y: -17650, + // }, + // 2.5 * Math.PI + // ); //right + portal2 = level.portal({ + x: 61920, + y: -16525, + }, + 1.5 * Math.PI, { + //right + x: 58400, + y: -17325, + }, + 2 * Math.PI + ); + portal3 = level.portal({ + x: 59865, + y: -17300, + }, + 3 * Math.PI, { + //right + x: 60820, + y: -31130, + }, + 2.5 * Math.PI + ); + + spawn.mapRect(60275, -32250, 975, 400); + spawn.mapRect(60375, -31925, 275, 225); + spawn.mapRect(61025, -31950, 175, 300); + spawn.mapRect(60825, -31725, 100, 350); + spawn.mapRect(60675, -31875, 200, 225); + spawn.mapRect(60225, -31950, 100, 725); + spawn.mapRect(60250, -31525, 250, 375); + spawn.mapRect(60675, -31475, 425, 350); + spawn.mapRect(60625, -32500, 225, 300); + spawn.mapRect(61025, -32325, 125, 175); + spawn.mapRect(60375, -32325, 175, 150); + spawn.mapRect(60250, -19075, 100, 100); + spawn.randomMob(59850, -18825, Infinity); + spawn.randomMob(62325, -18800, Infinity); + spawn.randomMob(61725, -18800, Infinity); + spawn.randomMob(63050, -19025, Infinity); + spawn.randomMob(64100, -19200, Infinity); + spawn.randomMob(64225, -19100, Infinity); + spawn.randomMob(64875, -19300, Infinity); + spawn.randomMob(65125, -19325, Infinity); + spawn.randomMob(65850, -19275, Infinity); + spawn.randomMob(66200, -19300, Infinity); + spawn.randomMob(65975, -19425, Infinity); + spawn.randomMob(67925, -19600, Infinity); + spawn.randomMob(66975, -19275, Infinity); + spawn.randomMob(67550, -18750, Infinity); + spawn.randomMob(69625, -17275, Infinity); + spawn.randomMob(70550, -17350, Infinity); + spawn.randomMob(71375, -17475, Infinity); + spawn.randomMob(72200, -17600, Infinity); + spawn.randomMob(73000, -18025, Infinity); + spawn.randomMob(73850, -18350, Infinity); + spawn.randomMob(75725, -18300, Infinity); + spawn.randomMob(75875, -18275, Infinity); + spawn.randomMob(75700, -18200, Infinity); + spawn.randomMob(75550, -18275, Infinity); + spawn.randomMob(75825, -18150, Infinity); + spawn.randomMob(75575, -18150, Infinity); + spawn.randomGroup(75575, -18150, 0); + level.chain(67250, -19325, 0, true, 14, 20); + spawn.mapRect(58725, -18300, 125, 100); + spawn.mapRect(61100, -18300, 175, 100); + spawn.mapRect(67175, -19375, 100, 100); + spawn.mapRect(59125, -19125, 325, 200); + spawn.mapRect(59925, -19175, 350, 225); + spawn.mapRect(60800, -18850, 275, 200); + spawn.mapRect(60850, -18725, 50, 200); + spawn.mapRect(60950, -18675, 50, 200); + spawn.mapRect(59975, -19025, 50, 250); + spawn.mapRect(60125, -19025, 50, 400); + spawn.mapRect(60075, -19025, 50, 450); + spawn.mapRect(59425, -19075, 100, 100); + spawn.mapRect(59175, -19000, 100, 225); + spawn.mapRect(59325, -19000, 75, 450); + spawn.mapRect(59050, -19000, 100, 100); + spawn.mapRect(61050, -18775, 100, 75); + spawn.mapRect(60725, -18850, 125, 125); + spawn.bodyRect(61850, -16525, 250, 175); + if (simulation.difficulty > 1) { + spawn.randomGroup(75575, -18150, 0); + spawn.randomLevelBoss(68450, -17300); + } + if (!isSpawnedBoss) { + isSpawnedBoss = true; + if (Math.random() < 0.33) { + for ( + let i = 0, len = Math.min(simulation.difficulty / 20, 6); i < len; + ++i + ) + spawn.bounceBoss(59025, -17325, 50, false); + } else if (Math.random() < 0.5) { + for ( + let i = 0, len = Math.min(simulation.difficulty / 9, 8); i < len; + ++i + ) + spawn.sprayBoss(59025, -17325, 50, false); + } else { + for ( + let i = 0, len = Math.min(simulation.difficulty / 6, 10); i < len; + ++i + ) + spawn.mineBoss(59025, -17325, 50, false); + } + powerUps.spawn(59352, -17115, "tech"); + // for (let i = 0, len = 3 + simulation.difficulty / 20; i < len; ++i) spawn.mantisBoss(1487 + 300 * i, -1525, 35, false) + } + simulation.fallHeight = -15000; + powerUps.addResearchToLevel(); + powerUps.spawn(3000, -230, "heal"); + // level.difficultyIncrease(60) + }, + temple() { + simulation.makeTextLog(`temple by Scar1337`); + + const V = Vector; + const Equation = (function () { + function Equation(a, b, c) { + this.a = a; + this.b = b; + this.c = c; + } + Equation.prototype.getXfromY = function (y) { + return (-this.b * y - this.c) / this.a; + } + Equation.prototype.getYfromX = function (x) { + return (-this.a * x - this.c) / this.b; + } + Equation.fromPoints = function (v1, v2) { + if (v1.x === v2.x) return new Equation(1, 0, -v1.x); + if (v1.y === v2.y) return new Equation(0, 1, -v1.y); + const d = (v2.y - v1.y) / (v2.x - v1.x); + return new Equation(-d, 1, d * v1.x - v1.y); + }; + return Equation; + })(); + const Rect = (function () { + function Rect(x, y, w, h) { + this.pos = { + x, + y + }; + this.width = w; + this.height = h; + } + Rect.prototype.has = function ({ + x, + y + }) { + return x >= this.pos.x && x <= this.pos.x + this.width && + y >= this.pos.y && y <= this.pos.y + this.height; + } + Rect.prototype.hasLine = function (eq) { + const leftInter = eq.getYfromX(this.pos.x); + const rightInter = eq.getYfromX(this.pos.x + this.width); + const topInter = eq.getXfromY(this.pos.y); + return (leftInter >= this.pos.y && leftInter <= this.pos.y + this.height) || + (rightInter >= this.pos.y && rightInter <= this.pos.y + this.height) || + (topInter >= this.pos.x && topInter <= this.pos.x + this.width); + } + Rect.prototype.addToMap = function () { + spawn.mapRect(this.pos.x, this.pos.y, this.width, this.height); + } + Object.defineProperty(Rect.prototype, "midPos", { + get() { + return V.add(this.pos, { + x: this.width / 2, + y: this.height / 2 + }); + } + }); + Rect.fromBounds = function (min, max) { + return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); + } + Rect.prototype.isCollidingWith = function (other) { + const tc = { + p1: [this.pos.x, this.pos.y], + p2: [this.pos.x + this.width, this.pos.y + this.height] + }; + const oc = { + p1: [other.pos.x, other.pos.y], + p2: [other.pos.x + other.width, other.pos.y + other.height] + }; + + // If one rectangle is on left side of other + if (tc.p1[0] >= oc.p2[0] || oc.p1[0] >= tc.p2[0]) + return false; + + // If one rectangle is above other + if (tc.p1[1] >= oc.p2[1] || oc.p1[1] >= tc.p2[1]) + return false; + + return true; + } + return Rect; + })(); + + function isInBound(bound) { + return bound.has(player.bounds.min) || bound.has(player.bounds.max); + } + + function addWIMP(x, y) { + spawn.WIMP(x, y); + const me = mob[mob.length - 1]; + me.isWIMP = true; + } + + function relocateWIMPs(x, y) { + for (const i of mob) { + if (i.isWIMP) { + setPos(i, { + x: x + 300 * (Math.random() - 0.5), + y: y + 300 * (Math.random() - 0.5) + }); + } + } + } + + function secondRoomSuckerBoss(x, y, isDark = false, radius = 25) { + mobs.spawn(x, y, 12, radius, isDark ? "#000" : "#fff"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.isDark = isDark; + + me.stroke = "transparent"; + me.eventHorizon = 500; // How family friendly content much do I have to reduce this + me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given + me.accelMag = 0.00003 * simulation.accelScale; + me.collisionFilter.mask = cat.player | cat.bullet; + me.memory = 1600; + me.randomPRNGMult = Math.random() * 500; + + me.attackCycle = 0; + me.lastAttackCycle = 0; + Matter.Body.setDensity(me, 0.012); // extra dense, normal is 0.001 // makes effective life much larger + me.onDeath = function () { + // applying forces to player doesn't seem to work inside this method, not sure why + powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); + if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); + if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.do = function () { + // keep it slow, to stop issues from explosion knock backs + if (this.speed > 1) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } + if (!(simulation.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak for black holes + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + this.checkStatus(); + if (this.seePlayer.recall) { + // accelerate towards the player + const forceMag = this.accelMag * this.mass; + const dx = this.seePlayer.position.x - this.position.x + const dy = this.seePlayer.position.y - this.position.y + const mag = Math.sqrt(dx * dx + dy * dy) + this.force.x += forceMag * dx / mag; + this.force.y += forceMag * dy / mag; + + // eventHorizon waves in and out + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); + + // draw darkness + ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.6)" : "rgba(225,215,255,0.6)"; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); + ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.4)" : "rgba(225,215,255,0.4)"; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI); + ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.3)" : "rgba(225,215,255,0.3)"; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI); + ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.2)" : "rgba(225,215,255,0.2)"; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI); + ctx.fillStyle = this.isDark ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)"; + DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + // when player is inside event horizon + if (distance(this.position, player.position) < eventHorizon) { + if (this.isDark) { + // Standard black hole stuff + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.003; + if (m.energy < 0.1) m.damage(0.00015 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x -= 0.0005 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + player.force.y -= 0.0005 * Math.sin(angle) * player.mass; + // draw line to player + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + DrawTools.line([this.position, m.pos]); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + } else { + // Lightning attacks + this.attackCycle++; + if (this.attackCycle >= 30) { + this.attackCycle = 0; + this.lastAttackCycle = simulation.cycle; + Matter.Body.setVelocity(player, V.add(player.velocity, { + x: 0, + y: -10 + })); + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.03; + m.damage(0.005 * simulation.dmgScale); + } + } + DrawTools.lightning(this.position, m.pos, this.lastAttackCycle, this.randomPRNGMult); + ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`; + DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + } + } + } + } + }; + + function secondRoomPlacerBoss(x, y, isDark = false, size = 70) { + mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0008" : "#fff8"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.isDark = isDark; + + me.stroke = isDark ? "#000" : "#fff"; + me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given + me.accelMag = 0.0001 * simulation.accelScale; + me.collisionFilter.mask = cat.player | cat.bullet; + me.memory = 1600; + me.randomPRNGMult = Math.random() * 500; + + me.attackCycle = 0; + me.maxAttackCycle = isDark ? 90 : 240; + Matter.Body.setDensity(me, 0.006); // extra dense, normal is 0.001 // makes effective life much larger + me.onDeath = function () { + powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); + if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); + if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.do = function () { + // keep it slow, to stop issues from explosion knock backs + if (this.speed > 2) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } + if (!(simulation.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + this.checkStatus(); + if (this.seePlayer.recall) { + // accelerate towards the player + const forceMag = this.accelMag * this.mass; + const dx = this.seePlayer.position.x - this.position.x + const dy = this.seePlayer.position.y - this.position.y + const mag = Math.sqrt(dx * dx + dy * dy) + this.force.x += forceMag * dx / mag; + this.force.y += forceMag * dy / mag; + this.attackCycle++; + if (this.attackCycle > this.maxAttackCycle) { + this.attackCycle = 0; + secondRoomObstacle(this.position.x, this.position.y, this.isDark, size); + } + } + } + }; + + function secondRoomObstacle(x, y, isDark = false, size = 70) { + mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0004" : "#fff4"); + let me = mob[mob.length - 1]; + + me.stroke = isDark ? "#000b" : "#fffb"; + me.collisionFilter.mask = isDark ? cat.player | cat.bullet : 0; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.leaveBody = false; + me.timeLeft = 1200; + me.isObstacle = true; + me.damageReduction = isDark ? 0.5 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) : 0; + if (!isDark) { + me.isBadTarget = true; + me.attackCycle = 0; + me.maxAttackCycle = 10; + me.inertia = Infinity; + } + me.do = isDark ? function () { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } : function () { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + if (Rect.fromBounds(this.bounds.min, this.bounds.max).isCollidingWith(Rect.fromBounds(player.bounds.min, player.bounds.max))) { + this.attackCycle++; + this.attackCycle = Math.min(this.attackCycle, 10); + } else { + this.attackCycle--; + this.attackCycle = Math.max(this.attackCycle, 0); + } + if (this.attackCycle > 0) { + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + if (this.attackCycle >= 10) { + ctx.shadowBlur = 10; + ctx.shadowColor = "rgb(255, 210, 64)"; + } + ctx.fillStyle = `rgba(255, 210, 64, ${this.attackCycle / 15})`; + ctx.fill(); + ctx.shadowBlur = 0; + if (this.attackCycle >= 10) { + DrawTools.lightning(this.position, m.pos, simulation.cycle); + m.damage(0.003 * simulation.dmgScale); + } + } + this.timeLimit(); + } + } + + function mobGrenade(...args) { + spawn.grenade(...args); + const pulseRadius = args[3] || Math.min(550, 250 + simulation.difficulty * 3) + let me = mob[mob.length - 1]; + me.fill = "#ace"; + me.damageReduction = 0; + me.onDeath = function () { + //damage player if in range + if (distance(player.position, this.position) < pulseRadius && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + m.damage(0.02 * simulation.dmgScale); + } + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: pulseRadius, + color: "rgba(170,204,238,0.3)", + time: simulation.drawTime + }); + }; + me.do = function () { + this.timeLimit(); + ctx.beginPath(); //draw explosion outline + ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = "rgba(170,204,238,0.1)"; + ctx.fill(); + }; + } + // Todo: nerf ThirdRoomBoss a bit? + function thirdRoomBoss(x, y) { + mobs.spawn(x, y, 6, 60, "#000"); + let me = mob[mob.length - 1]; + // Fix in place + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + me.isBoss = true; + + me.stroke = "transparent"; + me.eventHorizon = 900; + me.collisionFilter.mask = cat.player | cat.bullet | cat.body; + + me.memory = Infinity; + me.attackCycle = 0; + me.lastAttackCycle = 0; + me.spawnCycle = 0; + Matter.Body.setDensity(me, 0.08); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i] !== this) { + if (mob[i].isInvulnerable) { //disable invulnerability + mob[i].isInvulnerable = false + mob[i].damageReduction = 1 + } + mob[i].damage(Infinity, true); + } + } + } + // You earned it: One more tech + powerUps.spawn(this.position.x, this.position.y, "tech"); + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + templePlayer.room3ToEndAnim = 1; + }; + me.nextHealthThreshold = 0.75; + me.trapCycle = 0; + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.trapCycle = 1; + this.isInvulnerable = true; + this.damageReduction = 0; + } + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.rings = [{ + colour: "#65f", + radius: 300, + id: 0 + }, { + colour: "#0f0", + radius: 400, + id: 1 + }, { + colour: "#f00", + radius: 500, + id: 2 + }]; + me.ring = function () { + if (this.isInvulnerable) return; + ctx.lineWidth = 10; + for (const ring of this.rings) { + const radius = ring.radius * (1 + 0.3 * Math.sin(simulation.cycle / 60 * (ring.id + 2))); + if (Math.abs(distance(player.position, this.position) - radius) < 60 && m.immuneCycle < simulation.cycle) { + m.damage(0.4 / radius); + } + ctx.strokeStyle = ring.colour; + DrawTools.arcOut(this.position.x, this.position.y, radius, 0, Math.PI * 2); + } + } + me.horizon = function () { + if (this.isInvulnerable) return this.fill = "#f00"; + // eventHorizon waves in and out + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); + + const charge = this.attackCycle / 90; + this.fill = `rgb(${charge * 255},${charge * 255},${charge * 255})`; + // draw darkness + ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.6)`; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.4)`; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.3)`; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.2)`; + DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(${charge * 255},${charge * 255},${charge * 255},0.05)`; + DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + + // when player is inside event horizon + if (V.magnitude(V.sub(this.position, player.position)) < eventHorizon) { + // Standard black hole stuff + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.004; + if (m.energy < 0.1) m.damage(0.0002 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x -= 0.001 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + player.force.y -= 0.001 * Math.sin(angle) * player.mass; + // draw line to player + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + DrawTools.line([this.position, m.pos]); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + // Lightning attacks + this.attackCycle++; + if (this.attackCycle >= 90) { + this.attackCycle = 0; + this.lastAttackCycle = simulation.cycle; + Matter.Body.setVelocity(player, V.add(player.velocity, { + x: 0, + y: -20 + })); + if (m.immuneCycle < m.cycle) { + m.damage(0.012 * simulation.dmgScale); + } + } + const lightningCycle = simulation.cycle * 2 / 3 + this.lastAttackCycle / 3; + DrawTools.lightning(this.position, m.pos, lightningCycle, 1, 12); + DrawTools.lightning(this.position, m.pos, lightningCycle, 2, 12); + ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`; + DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + } + } + me.periodicSpawns = function () { + if (this.isInvulnerable) return; + this.spawnCycle++; + // Spawn annoying purple thing(s) that chases the player + if (!(this.spawnCycle % 180)) { + spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7); + spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7); + spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7); + } + // Spawn the annoying pink (now blue) exploder that doesn't chase the player + if (!(this.spawnCycle % 300)) { + for (let i = 0; i < 3; i++) { + mobGrenade(1100 + 700 * i, -13030, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10); + setVel(mob[mob.length - 1], { + x: 0, + y: -10 + }); + mobGrenade(1100 + 700 * i, -14370, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10); + setVel(mob[mob.length - 1], { + x: 0, + y: 10 + }); + } + } + // Spawn a bunch of mobs + if (simulation.difficulty > 10 && !(this.spawnCycle % 600)) { + // This is just ripped off of spawn.nodeGroup because I don't want the shield + const nodes = 3; + const radius = Math.ceil(Math.random() * 10) + 18; + const sideLength = Math.ceil(Math.random() * 100) + 70; + const stiffness = Math.random() * 0.03 + 0.005; + spawn.allowShields = false; //don't want shields on individual group mobs + const angle = 2 * Math.PI / nodes + for (let i = 0; i < nodes; ++i) { + spawn.focuser(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius); + } + spawn.constrainAllMobCombos(nodes, stiffness); + spawn.allowShields = true; + } + } + me.invulnerableTrap = function () { + if (this.trapCycle < 1) return; + this.trapCycle++; + // 24 is just an arbitrarily large number + const spawnCycles = Math.min(24, Math.max(6, 4 + Math.floor(simulation.difficulty / 3))); + // I have no idea how to balance at all, please help me + const spawnDelay = Math.floor(5 + 10 / (1 + Math.sqrt(simulation.difficulty) / 5)); + if (this.trapCycle >= 90) { + const cycle = this.trapCycle - 90; + if (!(cycle % spawnDelay)) { + const radius = Math.min(500, 200 + simulation.difficulty * 3); + mobGrenade(600, -13050, 30, radius); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: 35, + y: 0 + }); + mobGrenade(3000, -13050, 30, radius); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: -35, + y: 0 + }); + mobGrenade(600, -14350, 30, radius); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: 35, + y: 0 + }); + mobGrenade(3000, -14350, 30, radius); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: -35, + y: 0 + }); + if (Math.floor(cycle / spawnDelay) >= spawnCycles - 1) { + this.trapCycle = 0; + this.isInvulnerable = false; + this.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + } + } + } + ctx.font = "100px Arial"; + ctx.fillStyle = "#f00"; + ctx.shadowBlur = 10; + ctx.shadowColor = "#f00"; + ctx.textAlign = "center"; + ctx.textBaseLine = "middle"; + ctx.fillText("!", 900, -13050); + ctx.fillText("!", 900, -14350); + ctx.fillText("!", 1800, -13050); + ctx.fillText("!", 1800, -14350); + ctx.fillText("!", 2700, -13050); + ctx.fillText("!", 2700, -14350); + ctx.shadowBlur = 0; + } + me.do = function () { + this.checkStatus(); + this.horizon(); + this.ring(); + this.periodicSpawns(); + this.invulnerableTrap(); + } + }; + let oldNextLevel = level.nextLevel; + const oldFallHeight = simulation.fallHeight; + level.nextLevel = () => { + color.map = "#444"; + m.death = m.oldDeath; + canvas.style.filter = ""; + level.nextLevel = oldNextLevel; + simulation.fallHeight = oldFallHeight; + oldNextLevel(); + } + let bounds = []; + let mobPositionsQueue = Array.from(Array(10), () => []); + m.oldDeath = m.death; + m.death = function () { + if (!tech.isImmortal) { + requestAnimationFrame(() => color.map = "#444"); + m.death = m.oldDeath; + canvas.style.filter = ""; + level.nextLevel = oldNextLevel; + simulation.fallHeight = oldFallHeight; + m.oldDeath(); + } else { + m.switchWorlds(); + Matter.Body.setPosition(player, { + x: level.enter.x + 50, + y: level.enter.y - 20 + }); + makeLore("How did you not die?"); + setTimeout(() => makeLore("I'll let you off this one time."), 2000); + tech.isImmortal = false; + } + } + let name = "⥟ᘊ5⪊Ыᳪៗⱕ␥ዘᑧ⍗"; + addPartToMap = (len = map.length - 1) => { + map[len].collisionFilter.category = cat.map; + map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[len], true); // make static + Composite.add(engine.world, map[len]); + } + level.setPosToSpawn(50, -50); // normal spawn + // Make the level exit really far away so WIMP powerups don't show up at all + level.exit.x = 1e6; + level.exit.y = -1e6; + Promise.resolve().then(() => { + // Clear all WIMPS and their research + for (let i = 0; i < mob.length; i++) { + while (mob[i] && !mob[i].isMACHO) { + mob[i].replace(i); + } + } + for (let i = 0; i < powerUp.length; i++) { + while (powerUp[i] && powerUp[i].name === "research") { + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + } + } + level.exit.x = 1500; + level.exit.y = -30; + }); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(1500, -10, 100, 20); + level.defaultZoom = 1800 + simulation.setZoom(1200); + document.body.style.backgroundColor = "#daa69f"; + color.map = "#600"; + + function box(x, y, w, h, s) { + spawn.mapRect(x, y, w, s); + spawn.mapRect(x, y, s, h); + spawn.mapRect(x + w - s, y, s, h); + spawn.mapRect(x, y + h - s, w, s); + } + + function diamond(x, y, s) { + spawn.mapVertex(x, y, `0 -${s} -${s} 0 0 ${s} ${s} 0`); + } + + // Fake level + bounds.push(new Rect(-200, -500, 2000, 600)); + box(-200, -500, 2000, 600, 100); + + // Actual level, Room 1 + const firstRoomBounds = new Rect(-200, -4000, 5000, 2100); + bounds.push(firstRoomBounds); + + box(-200, -4000, 5000, 2100, 100); + spawn.mapRect(-200, -2500, 1300, 100); + spawn.mapRect(3500, -2500, 1300, 100); + spawn.mapRect(-200, -4000, 1000, 1600); + spawn.mapRect(3800, -4000, 1000, 1600); + // Enter and Exit platforms + spawn.mapRect(0, -2010, 100, 20); + spawn.mapRect(4500, -2010, 100, 20); + + // Altar of Room 1 + spawn.mapRect(2100, -2200, 100, 300); + spawn.mapRect(2400, -2200, 100, 300); + spawn.mapRect(2070, -2200, 460, 20); + + spawn.debris(1700, -2100, 300, 10); + spawn.debris(2500, -2100, 300, 10); + + // Actual level, Room 2 + const secondRoomBounds = new Rect(-1500, -10500, 3000, 3600); + bounds.push(secondRoomBounds); + + box(-1500, -10500, 3000, 3600, 100); + spawn.mapRect(-2000, -8500, 1600, 1600); + spawn.mapRect(400, -8500, 1600, 1600); + // Enter and Exit platforms + spawn.mapRect(-50, -7010, 100, 20); + spawn.mapRect(-50, -10010, 100, 20); + + // Hazard traversing + spawn.mapRect(-300, -7320, 800, 20); + spawn.mapRect(175, -7600, 325, 20); + spawn.mapRect(200, -7775, 300, 20); + spawn.mapRect(-500, -7600, 325, 20); + spawn.mapRect(-500, -7775, 300, 20); + spawn.mapRect(-500, -7950, 800, 20); + spawn.mapRect(-300, -8100, 800, 20); + spawn.mapRect(-500, -8250, 800, 20); + for (let i = 0; i < 2; i++) spawn.mapRect(-250, -8400 + 150 * i, 500, 60); + const room2SlimePit = level.hazard(-400, -8410, 800, 1090); + room2SlimePit.logic = function () { + if (this.height > 0 && Matter.Query.region([player], this).length) { + if (m.immuneCycle < m.cycle) { + // Trolled + const hasCPT = tech.isRewindAvoidDeath; + tech.isRewindAvoidDeath = false; + const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + 0.001; + if (m.energy > DRAIN && !tech.isEnergyHealth) { + m.energy -= DRAIN; + } + m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.25 : 1)); + if (tech.isEnergyHealth) { + const previousEnergy = m.energy; + m.regenEnergy(); + const energyRegenerated = m.energy - previousEnergy; + if (energyRegenerated > 0) { + m.energy = previousEnergy; + m.damage(energyRegenerated); + } + } + tech.isRewindAvoidDeath = hasCPT; + } + player.force.y -= 0.3 * player.mass * simulation.g; + setVel(player, Vector.sub(player.velocity, { + x: 0, + y: player.velocity.y * 0.02 + })); + } + // Float power ups + powerUpCollide = Matter.Query.region(powerUp, this) + for (let i = 0, len = powerUpCollide.length; i < len; i++) { + const diameter = 2 * powerUpCollide[i].size + const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter + powerUpCollide[i].force.y -= buoyancy * 1.14 * powerUpCollide[i].mass * simulation.g; + setVel(powerUpCollide[i], { + x: powerUpCollide[i].velocity.x, + y: 0.96 * powerUpCollide[i].velocity.y + }); + } + } + room2SlimePit.draw = function () { + if (this.isOn) { + ctx.fillStyle = "hsla(160, 100%, 35%, 0.75)"; + ctx.fillRect(this.min.x, this.min.y, this.width, this.height); + } + } + // Room 2 spawning bounds + const secondRoomSpawnBounds = new Rect(-1500, -10500, 3000, 2000); + spawn.mapRect(-700, -8700, 150, 20); + spawn.mapRect(550, -8700, 150, 20); + spawn.mapRect(-400, -8900, 800, 20); + diamond(-600, -9800, 30); + diamond(0, -9800, 30); + diamond(600, -9800, 30); + + spawn.mapRect(-1000, -10000, 2000, 30); + + // Actual level, Room 3 (Final Boss?) + const thirdRoomBounds = new Rect(-200, -14500, 4000, 1600); + bounds.push(thirdRoomBounds); + box(-200, -14500, 4000, 1600, 100); + spawn.mapRect(-200, -14500, 800, 1100); + spawn.mapRect(3000, -14500, 800, 1100); + // Enter and Exit platforms + spawn.mapRect(0, -13110, 100, 20); + spawn.mapRect(-200, -13100, 800, 200); + spawn.mapRect(3500, -13110, 100, 20); + spawn.mapRect(3000, -13100, 800, 200); + for (let i = 0; i < 4; i++) spawn.bodyRect(570, -13400 + i * 75, 30, 75); + + for (let i = 0; i < 3; i++) { + diamond(1100 + 700 * i, -13000, 30, 30); + diamond(1100 + 700 * i, -14400, 30, 30); + } + + const Objects = { + altar: { + get isHeal() { + return simulation.cycle % 600 >= 300; + }, + pos: { + x: 2300, + y: -2200 + }, + get isActive() { + const roughPlayerCentre = V.add(m.pos, { + x: 0, + y: 40 + }); + return distance(roughPlayerCentre, this.pos) < 240 && + (Math.abs(angle(roughPlayerCentre, this.pos) - Math.PI / 2) < 1); + }, + logic() { + if (!this.isActive) return; + if (this.isHeal) { + m.energy += 0.005; + } else { + m.energy = Math.max(m.energy - 0.006, 0); + if (m.energy <= 0.01 && m.immuneCycle < m.cycle) m.damage(0.002); + } + }, + drawTop() { + if (!isInBound(firstRoomBounds)) return; + const colour = this.isHeal ? m.fieldMeterColor : "#f00"; + DrawTools.flame([2300, -2200, 26, 4, colour], 7); + ctx.fillStyle = colour; + ctx.fillRect(2200, -2200, 200, 200); + }, + drawBottom() { + ctx.fillStyle = this.isHeal ? "#fff5" : "#0005"; + for (const radius of [230, 180, 130, 80, 30]) { + DrawTools.arc(2300, -2200, radius, 0, Math.PI, true); + } + } + }, + room2Initiator: { + pos: { + x: 0, + y: -9050 + }, + get distance() { + return distance(player.position, this.pos); + }, + range: 120, + rings: [{ + colour: [102, 85, 255], + radius: 200 + }, { + colour: [0, 255, 0], + radius: 300 + }, { + colour: [255, 0, 0], + radius: 400 + }], + get ringNumber() { + return this.rings.length; + }, + get cap() { + return (this.ringNumber + 1) * 90 + 240; + }, + get capped() { + return templePlayer.room2.spawnInitiatorCycles > this.cap; + }, + logic() { + if (this.distance < this.range) { + templePlayer.room2.spawnInitiatorCycles++; + } + }, + draw() { + Promise.resolve().then(() => { + const cycle = templePlayer.room2.spawnInitiatorCycles; + if (!this.capped && this.distance < 400) { + ctx.fillStyle = `rgba(0, 0, 0, ${Math.min(1, (400 - this.distance) / (400 - this.range)) * 0.9})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + ctx.save(); + simulation.camera(); + if (this.distance < this.range && !this.capped) { + DrawTools.lightning(V.sub(this.pos, { + x: 300, + y: 300 + }), V.add(this.pos, { + x: 300, + y: 300 + }), simulation.cycle - 5); + DrawTools.lightning(V.add(this.pos, { + x: -300, + y: 300 + }), V.add(this.pos, { + x: 300, + y: -300 + }), simulation.cycle - 5); + } + if (!this.capped && cycle >= this.cap - 200) { + const multCoeff = (cycle - this.cap + 200) * 0.4 + ctx.translate((Math.random() - 0.5) * multCoeff, (Math.random() - 0.5) * multCoeff); + } + ctx.shadowBlur = 20; + ctx.lineWidth = 12; + ctx.strokeStyle = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000"; + ctx.shadowColor = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000"; + DrawTools.arcOut(this.pos.x, this.pos.y, 100, 0, Math.PI * 2); + if (templePlayer.room2.cycles <= 100) { + for (let i = 0; i < this.ringNumber; i++) { + if (cycle < i * 90 + 90) break; + const ring = this.rings[i]; + ctx.shadowColor = `rgb(${ring.colour.join(",")})`; + const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2 - 0.5); + ctx.strokeStyle = `rgba(${ring.colour.join(",")}, ${Math.min(opacity, 1)})`; + const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90 - 90, 45) / 90 * Math.PI)) * ring.radius; + DrawTools.arcOut(this.pos.x, this.pos.y, radius, 0, Math.PI * 2); + } + } + ctx.restore(); + }); + } + }, + room2Lightning: { + one: [{ + x: -1400, + y: -10400 + }, { + x: 1400, + y: -8500 + }], + two: [{ + x: -1400, + y: -8500 + }, { + x: 1400, + y: -10400 + }], + get isHeal() { + return simulation.cycle % 360 < 180; + }, + get oneEq() { + return Equation.fromPoints(this.one[0], this.one[1]); + }, + get twoEq() { + return Equation.fromPoints(this.two[0], this.two[1]); + }, + logic() { + if (!isInBound(secondRoomSpawnBounds) || !templePlayer.room2.cycles) return; + + const playerbounds = Rect.fromBounds(player.bounds.min, player.bounds.max); + if (playerbounds.hasLine(this.oneEq) || playerbounds.hasLine(this.twoEq)) { + if (this.isHeal) { + m.energy += 0.003; + } else if (m.immuneCycle < m.cycle) { + m.energy -= 0.003; + } + } + }, + draw() { + if (!isInBound(secondRoomBounds) || !templePlayer.room2.cycles) return; + + const colour = this.isHeal ? undefined : [0, 0, 0]; + DrawTools.lightning(...this.one, Math.floor(simulation.cycle / 15) * 15, 1, 9, colour); + DrawTools.lightning(...this.two, Math.floor(simulation.cycle / 15) * 15, 2, 9, colour); + } + }, + room2GeneratedPath: { + rects: (function () { + const rects = []; + for (let i = 0; i < 4; i++) { + rects.push(new Rect(-1405 + (i & 1) * 200, -9700 + i * 300, 205, 30)); + rects.push(new Rect(1200 - (i & 1) * 200, -9700 + i * 300, 205, 30)); + } + return rects; + })(), + logic() { + if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle === 180) { + for (const r of this.rects) { + r.addToMap(); + addPartToMap(); + simulation.draw.setPaths(); + } + } + }, + draw() { + if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle < 180) { + ctx.fillStyle = "#fe79"; + for (const r of this.rects) { + ctx.fillRect(r.pos.x, r.pos.y, r.width, r.height); + } + } else if (simulation.cycle - templePlayer.room2.readyPathCycle < 195) { + for (const r of this.rects) { + DrawTools.lightning(Objects.room2Initiator.pos, r.midPos, templePlayer.room2.readyPathCycle + 180); + } + } + } + }, + room3Rotors: { + rotor1: (function () { + const rotor = level.spinner(900, -13700, 200, 30); + rotor.rotate = function () { + Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity + 0.01) * 0.9) + } + return rotor; + })(), + rotor2: (function () { + const rotor = level.spinner(2700, -13700, 200, 30); + rotor.rotate = function () { + Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity - 0.01) * 0.9) + } + return rotor; + })(), + logic() { + this.rotor1.rotate(); + this.rotor2.rotate(); + } + }, + room3SlimePits: { + pit1: level.hazard(-100, -13400, 0, 0, 0.004), + pit2: level.hazard(3700, -13400, 0, 0, 0.004), + logic() { + if (templePlayer.room2ToRoom3Anim >= 1320 && templePlayer.room2ToRoom3Anim <= 1570) { + this.pit1.height = this.pit2.height = 300; + this.pit1.max.y = this.pit2.max.y = -13100; + this.pit1.width = this.pit2.width = templePlayer.room2ToRoom3Anim * 2 - 2640; + this.pit1.max.x = this.pit1.min.x + this.pit1.width; + this.pit2.min.x = this.pit2.max.x - this.pit2.width; + } + if (templePlayer.room3ToEndAnim) { + this.pit1.height = this.pit1.width = 0; + this.pit2.height = this.pit2.width = 0; + } + }, + draw() { + this.pit1.query(); + this.pit2.query(); + } + } + }; + let templePlayer = { + room1: { + cycles: 300 + }, + room2: { + spawnInitiatorCycles: 0, + cycles: 0, + readyPathCycle: 0 + }, + stage: 1, + startAnim: 0, + room1ToRoom2Anim: 0, + room2ToRoom3Anim: 0, + room3ToEndAnim: 0, + initialTrapY: 0, + clearedCycle: 0, + drawExit: true + }; + + const RoomTransitionHandler = { + room0() { + if (templePlayer.startAnim <= 0) return; + templePlayer.startAnim++; + if (templePlayer.startAnim == 120) { + makeLore("Not so fast."); + } + if (templePlayer.startAnim < 360) { + trapPlayer(1000, templePlayer.initialTrapY); + } else { + level.exit.x = 4500; + level.exit.y = -2030; + relocateTo(50, -2050); + simulation.fallHeight = -1000; + simulation.setZoom(1800); + templePlayer.startAnim = -1; + templePlayer.drawExit = false; + } + }, + room1() { + if (templePlayer.room1ToRoom2Anim <= 0) return; + if (templePlayer.room1ToRoom2Anim === 1) { + level.exit.x = -50; + level.exit.y = -10030; + makeLore("Pathetic."); + } + if (templePlayer.room1ToRoom2Anim === 121) { + makeLore("You will never succeed."); + } + if (templePlayer.room1ToRoom2Anim >= 360 && templePlayer.room1ToRoom2Anim <= 720) { + const factor = 200 - 200 * Math.cos((templePlayer.room1ToRoom2Anim / 120 - 3) * Math.PI); + ctx.translate(factor, factor); + Promise.resolve().then(() => { + ctx.save(); + ctx.globalCompositeOperation = "color-burn"; + ctx.fillStyle = DrawTools.randomColours; + DrawTools.updateRandomColours(5); + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + }); + } + if (templePlayer.room1ToRoom2Anim === 720) { + makeLore("You are trying too hard."); + relocateTo(0, -7050); + simulation.fallHeight = -6000; + templePlayer.stage = 2; + } + if (templePlayer.room1ToRoom2Anim === 960) { + makeLore("I have mastered the understandings of the universe."); + } + if (templePlayer.room1ToRoom2Anim === 1200) { + // Congrats, you discovered the actual words by looking at the source code. Are you happy now? + const x = ( + ["a speck of dust", "an insignificant hindrance", "a tiny obstacle"] + )[Math.floor(Math.random() * 3)].split(""); + for (let i = 0; i < x.length / 1.6; i++) { + const randomIndex = Math.floor(Math.random() * x.length); + if (x[randomIndex] !== " ") { + x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192); + } + }; + makeLore(`You are no more than ${x.join("")} to me.`); + relocateWIMPs(0, -10030); + } + templePlayer.room1ToRoom2Anim++; + }, + room2() { + if (templePlayer.room2ToRoom3Anim <= 0) return; + if (templePlayer.room2ToRoom3Anim === 1) { + level.exit.x = 3500; + level.exit.y = -13130; + makeLore("Do not try me."); + } + if (templePlayer.room2ToRoom3Anim === 180) { + makeLore("I have absolute power over you."); + canvas.style.filter = "hue-rotate(90deg)"; + } + if (templePlayer.room2ToRoom3Anim === 360) { + makeLore("You will not succeed..."); + canvas.style.filter = "invert(0.2)"; + } + if (templePlayer.room2ToRoom3Anim === 420) { + makeLore("
...
"); + canvas.style.filter = "invert(0.4)"; + } + if (templePlayer.room2ToRoom3Anim > 480 && templePlayer.room2ToRoom3Anim <= 660) { + canvas.style.filter = `sepia(${(templePlayer.room2ToRoom3Anim - 480) / 180}) invert(${0.5 + (templePlayer.room2ToRoom3Anim - 480) / 180})`; + } + if (templePlayer.room2ToRoom3Anim === 780) { + makeLore("Do not interfere with me."); + templePlayer.stage = 3; + relocateTo(50, -13150); + simulation.fallHeight = -10000; + simulation.zoomTransition(1800); + templePlayer.startAnim = -1; + // Might be a bit harsh to the player if the WIMPs are involved in the third level + for (let i = 0; i < mob.length; i++) { + while (mob[i] && !mob[i].isWIMP) { + mob[i].replace(i); + } + } + } + if (templePlayer.room2ToRoom3Anim > 780 && templePlayer.room2ToRoom3Anim <= 960) { + canvas.style.filter = `sepia(${(960 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(960 - templePlayer.room2ToRoom3Anim) / 180})`; + } + templePlayer.room2ToRoom3Anim++; + }, + room3() { + if (templePlayer.room3ToEndAnim <= 0) return; + if (templePlayer.room3ToEndAnim === 1) { + makeLore("No."); + } + if (templePlayer.room3ToEndAnim === 120) { + makeLore("This cannot be."); + } + if (templePlayer.room3ToEndAnim === 240) { + makeLore("Has my power failed me?"); + } + if (templePlayer.room3ToEndAnim === 360) { + makeLore("Was it worth it, destroying this place?"); + } + if (templePlayer.room3ToEndAnim === 600) { + makeLore("No one is greater than me."); + } + const text = "noone-"; + for (let i = 0; i < 12; i++) { + if (templePlayer.room3ToEndAnim === 720 + i * 20) { + name = name.slice(0, -1); + simulation.makeTextLog(`${name}:   ${text[i % 6]}`); + canvas.style.filter = `brightness(${1 - i / 22})`; + } + } + if (templePlayer.room3ToEndAnim === 1060) { + templePlayer.drawExit = true; + for (let i = 0; i < 5 * tech.wimpCount; i++) { + powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false); + } + canvas.style.filter = ""; + } + templePlayer.room3ToEndAnim++; + }, + end() { + if (!templePlayer.clearedCycle) return; + Promise.resolve().then(() => { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 300})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + }); + if (simulation.cycle - templePlayer.clearedCycle > 420) level.nextLevel(); + } + }; + const LogicHandler = { + bounds() { + let isInBounds = false; + for (const b of bounds) { + if (isInBound(b)) { + isInBounds = true; + break; + } + } + if (!isInBounds) { + m.damage(0.1 * simulation.difficultyMode); + trapPlayer(level.enter.x, level.enter.y); + simulation.makeTextLog("" + name + ":   You thought I could let you get away with that?"); + } + }, + room0() { + // Block the player from entering the first seemingly innocuous exit + if ((m.pos.x > 1000) && templePlayer.startAnim === 0) { + spawn.mapRect(1200, -500, 100, 600); + templePlayer.initialTrapY = Math.min(player.position.y, -75); + trapPlayer(1000, templePlayer.initialTrapY); + + addPartToMap(); + simulation.draw.setPaths(); + templePlayer.startAnim = 1; + } + }, + room1() { + WaveHandler.room1(); + Objects.altar.logic(); + }, + room2() { + room2SlimePit.logic(); + Objects.room2Initiator.logic(); + Objects.room2Lightning.logic(); + Objects.room2GeneratedPath.logic(); + WaveHandler.room2(); + }, + room3() { + Objects.room3Rotors.logic(); + Objects.room3SlimePits.logic(); + WaveHandler.room3(); + }, + exit() { + if (!templePlayer.drawExit) return; + if (player.position.x > level.exit.x && + player.position.x < level.exit.x + 100 && + player.position.y > level.exit.y - 150 && + player.position.y < level.exit.y - 40 && + player.velocity.y < 0.1 && + level.exitCount + (input.down ? 8 : 2) > 100) { + if (templePlayer.stage === 1) { + templePlayer.drawExit = false; + level.exitCount = 0; + templePlayer.room1ToRoom2Anim = 1; + } else if (templePlayer.stage === 2) { + templePlayer.drawExit = false; + templePlayer.room2ToRoom3Anim = 1; + level.exitCount = 0; + } else { + level.exitCount = 99 - (input.down ? 8 : 2); + if (!templePlayer.clearedCycle) templePlayer.clearedCycle = simulation.cycle; + } + } + } + }; + const DrawHandler = { + // Bottom layer + base() { + // Draw base red background + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.fillStyle = color.map; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + // Draw the normal bg on the bounds + ctx.fillStyle = "#eab6af"; + for (const b of bounds) { + if (isInBound(b)) ctx.fillRect(b.pos.x + 2, b.pos.y + 2, b.width - 4, b.height - 4); + } + }, + entrance() { + ctx.beginPath(); + ctx.moveTo(level.enter.x, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y - 80); + ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80); + ctx.lineTo(level.enter.x + 100, level.enter.y + 30); + ctx.lineTo(level.enter.x, level.enter.y + 30); + ctx.fillStyle = "#fca"; + ctx.fill(); + }, + room1() { + if (!isInBound(firstRoomBounds)) return; + + // Draw Cross + ctx.fillStyle = "#fed"; + ctx.fillRect(2200, -3300, 200, 800); + ctx.fillRect(2000, -3100, 600, 200); + + // Final boss-like spawn fire thing. Was it necessary? No! + const spawnFlameAngle = Math.min(Math.min(templePlayer.room1.cycles, 2520) % 600, 120) * Math.PI / 30 + Math.PI / 2; + DrawTools.flame([2300, -3000, 26, 4, "#f60", spawnFlameAngle], 7); + + Objects.altar.drawBottom(); + }, + room2() { + if (!isInBound(secondRoomBounds)) return; + + if (templePlayer.room2.cycles) { + ctx.fillStyle = "#0006"; + ctx.fillRect(secondRoomBounds.pos.x + 2, secondRoomBounds.pos.y + 2, secondRoomBounds.width - 4, secondRoomBounds.height - 4); + } + room2SlimePit.draw(); + }, + room3() { + if (!isInBound(thirdRoomBounds)) return; + ctx.fillStyle = "#0006"; + ctx.fillRect(thirdRoomBounds.pos.x + 2, thirdRoomBounds.pos.y + 2, thirdRoomBounds.width - 4, thirdRoomBounds.height - 4); + Objects.room3SlimePits.draw(); + }, + // Top layer + mobTrails() { + if (simulation.cycle % 4 === 0) { + let newMobPositions = []; + for (const i of mob) { + if (!(i.isMACHO || i.isWIMP || i.isObstacle)) newMobPositions.push({ + x: i.position.x, + y: i.position.y + }); + } + mobPositionsQueue.shift(); + mobPositionsQueue.push(newMobPositions); + } + // Draw "holy" trails for mobs for no particular reason at all + ctx.strokeStyle = "#bae"; + ctx.lineWidth = 3; + for (let i = 0; i < 10; i++) { + const p = mobPositionsQueue[i]; + for (const m of p) { + DrawTools.holy(m.x, m.y, i / 2 + 10); + } + } + ctx.shadowBlur = 0; + }, + waveTimer() { + const roomConditions = [ + isInBound(firstRoomBounds) && templePlayer.room1.cycles < 2400, + isInBound(secondRoomBounds) && templePlayer.room2.cycles > 0 && templePlayer.room2.cycles < 2160, + isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 1320 + ]; + Promise.resolve(roomConditions).then(roomConditions => { + // First Room + if (roomConditions[0]) { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.fillStyle = "#0004"; + ctx.fillRect(canvas.width2 - 288, 50, 576, 20); + ctx.fillStyle = "#0cf"; + ctx.fillRect(canvas.width2 - 288, 50, 0.96 * (600 - templePlayer.room1.cycles % 600), 20); + ctx.restore(); + } + // Second Room + if (roomConditions[1]) { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.fillStyle = "#0004"; + ctx.fillRect(canvas.width2 - 288, 50, 576, 20); + ctx.fillStyle = (Math.ceil(templePlayer.room2.cycles / 720) & 1) ? "#000" : "#e1d7ff"; + ctx.fillRect(canvas.width2 - 288, 50, 0.8 * (720 - templePlayer.room2.cycles % 720), 20); + ctx.restore(); + } + // Third Room + if (roomConditions[2]) { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.fillStyle = "#0004"; + ctx.fillRect(canvas.width2 - 288, 50, 576, 20); + ctx.fillStyle = "#000"; + ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (1320 - templePlayer.room2ToRoom3Anim), 20); + ctx.restore(); + } + }); + }, + room2Top() { + if (!isInBound(secondRoomBounds)) return; + Objects.room2Lightning.draw(); + Objects.room2GeneratedPath.draw(); + Objects.room2Initiator.draw(); + } + }; + const WaveHandler = { + room1() { + if (!isInBound(firstRoomBounds)) return; + if (templePlayer.room1.cycles === 0) powerUps.spawnStartingPowerUps(0, -2050); + templePlayer.room1.cycles++; + if (templePlayer.room1.cycles === 2400) { + spawn.secondaryBossChance(2300, -2800); + powerUps.addResearchToLevel(); + } + if (templePlayer.room1.cycles % 600 === 0 && templePlayer.room1.cycles <= 2400) { + const spawnAmt = Math.min(3 + Math.pow(simulation.difficulty / 1.7, 0.6), 10) + Math.floor(templePlayer.room1.cycles / 720); + for (let i = 0; i < spawnAmt; i++) { + if (Math.random() < 0.5 + 0.07 * simulation.difficulty) { + spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity); + } + } + spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity); + } + if (templePlayer.room1.cycles === 2520) { + templePlayer.drawExit = true; + } + }, + room2() { + if (!isInBound(secondRoomBounds)) return; + if (templePlayer.room2.spawnInitiatorCycles > Objects.room2Initiator.cap) { + const randomSecondRoomBoss = [secondRoomSuckerBoss, secondRoomPlacerBoss]; + if (templePlayer.room2.cycles % 720 === 0 && templePlayer.room2.cycles <= 2160) { + const isOdd = Math.floor(templePlayer.room2.cycles / 720) & 1; + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](-600, -9800, isOdd); + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](600, -9800, isOdd); + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](0, -9800, !isOdd); + } + templePlayer.room2.cycles++; + if (templePlayer.room2.cycles === 2400) { + templePlayer.drawExit = true; + templePlayer.room2.readyPathCycle = simulation.cycle; + } + } + }, + room3() { + if (templePlayer.room2ToRoom3Anim === 1320) { + thirdRoomBoss(1800, -13700); + for (let i = 0; i < 3; i++) { + powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal"); + } + } + } + }; + const DrawTools = { + get randomColours() { + return `rgb(${this._randomColours.join(",")})` + }, + _randomColours: [Math.random() * 255, Math.random() * 255, Math.random() * 255], + updateRandomColours(x = 0.8) { + for (let i = 0; i < this._randomColours.length; i++) { + this._randomColours[i] = Math.max(Math.min(this._randomColours[i] + (this.randFact() * x * 2) - x, 255), 0); + } + }, + randFact() { + return Math.random() * 0.8 + Math.sin(Date.now() / 300) * 0.2; + }, + + line(vecs) { + ctx.beginPath(); + ctx.moveTo(vecs[0].x, vecs[0].y); + for (const v of vecs.slice(1)) ctx.lineTo(v.x, v.y); + ctx.stroke(); + }, + arc(...x) { + ctx.beginPath(); + ctx.arc(...x); + ctx.fill(); + }, + arcOut(...x) { + ctx.beginPath(); + ctx.arc(...x); + ctx.stroke(); + }, + flame(props, repeat) { + for (let i = 0; i < repeat; i++) this.singleFlame(...props); + }, + singleFlame(x, y, size = 10, repeat = 3, color = "#f00", angle = Math.PI / 2) { + ctx.strokeStyle = color; + ctx.lineWidth = 3; + const path = [{ + x, + y + }]; + for (let i = 0; i < repeat; i++) { + const randAng = (Math.random() - 0.5) * 2 + angle; + const randLen = 2 * size + Math.random() * size; + + x += Math.cos(randAng) * randLen; + y -= Math.sin(randAng) * randLen; + path.push({ + x, + y + }) + } + DrawTools.line(path); + }, + lightning(from, to, cycle, randomPRNGMult = 1, width = 8, color = [255, 240, 127]) { + const diff = simulation.cycle - cycle; + if (diff >= 15) return; + ctx.strokeStyle = `rgba(${color.join(",")},${(1 - diff / 15) * 255})`; + ctx.lineWidth = width * (1 - diff / 15); + ctx.shadowColor = `rgb(${color.join(",")})`; + ctx.shadowBlur = 20; + const path = [{ + x: from.x, + y: from.y + }]; + let vector = { + x: from.x, + y: from.y + }; + let distanceLeft = V.magnitude(V.sub(from, to)); + const d = distanceLeft > 800 ? distanceLeft / 40 : 20; + const normalised = V.normalise(V.sub(to, from)); + while (1) { + const randOffset = rotateVector({ + y: RNG(Math.floor(cycle * randomPRNGMult + distanceLeft)) * 2 * d - d, + x: 0 + }, normalised); + const randLen = RNG(Math.floor(cycle * (randomPRNGMult + 1) + distanceLeft)) * d + d; + distanceLeft -= randLen; + if (distanceLeft <= 0) { + path.push({ + x: to.x, + y: to.y + }); + break; + } + vector = V.add(vector, V.mult(normalised, randLen)); + path.push({ + x: vector.x + randOffset.x, + y: vector.y + randOffset.y + }); + } + DrawTools.line(path); + ctx.shadowBlur = 0; + }, + holy(x, y, size = 12) { + this.line([{ + x, + y: y - size + }, { + x: x - size, + y + }, + { + x, + y: y + size + }, { + x: x + size, + y + }, { + x, + y: y - size + } + ]); + } + }; + + function RNG(x) { + x += Math.seed; + let start = Math.pow(x % 97, 4.3) * 232344573; + const a = 15485863; + const b = 521791; + start = (start * a) % b; + for (let i = 0; i < (x * x) % 90 + 90; i++) { + start = (start * a) % b; + } + return start / b; + } + + function rotateVector(v, ang) { + const c = typeof ang === "number" ? { + x: Math.cos(ang), + y: Math.sin(ang) + } : V.normalise(ang); + return { + x: v.x * c.x - v.y * c.y, + y: v.y * c.x + v.x * c.y + }; + } + + function trapPlayer(x, y) { + setPosAndFreeze(player, { + x, + y + }); + const bLen = bullet.length; + for (let i = 0; i < bLen; i++) { + if (bullet[i].botType) setPosAndFreeze(bullet[i], V.add(player.position, { + x: 100 * (RNG(i) - 0.5), + y: 100 * (RNG(i + bLen) - 0.5) + })); + } + } + + function relocateTo(x, y) { + level.setPosToSpawn(x, y); + trapPlayer(x, y); + for (let i = 0; i < mob.length; i++) { + if (mob[i].isMACHO) { + setPos(mob[i], { + x, + y + }); + break; + } + } + m.resetHistory(); + } + const distance = (a, b) => V.magnitude(V.sub(a, b)); + const angle = (a, b) => Math.atan2(b.y - a.y, a.x - b.x); + const setPos = (a, b) => Matter.Body.setPosition(a, b); + const setVel = (a, b) => Matter.Body.setVelocity(a, b); + const freeze = a => setVel(a, { + x: 0, + y: 0 + }); + const setPosAndFreeze = (a, b) => { + setPos(a, b); + freeze(a); + }; + const makeLore = (x, t) => simulation.makeTextLog(`

${name}:

 

${x}

`, t); + level.custom = () => { + // All the logic gets handled here. How nice! + for (const i in LogicHandler) { + LogicHandler[i](); + } + + // Animations and lore for things that seem like exits + for (const i in RoomTransitionHandler) { + RoomTransitionHandler[i](); + } + + // Bottom layer graphics + DrawHandler.base(); + DrawHandler.room1(); + DrawHandler.room2(); + DrawHandler.room3(); + DrawHandler.entrance(); + if (templePlayer.drawExit) level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + // Top layer graphics + DrawHandler.mobTrails(); + Objects.altar.drawTop(); + DrawHandler.waveTimer(); + DrawHandler.room2Top(); + }; + }, + dripp() { + simulation.makeTextLog(`dripp by M. B.`); + + const door = level.door(780, -350, 15, 400, 265); + const buttonDoor = level.button(420, -10); + const boost = level.boost(130, -445); + const hazard = level.hazard(690, -1050, 10, 700, 0.4) + const hazard2 = level.hazard(2470, -1515, 162, 14, 0.4) + const hazard3 = level.hazard(740, -1050, 10, 700, 0.4) + const hazard4 = level.hazard(3400, -380, 350, 6, 0.2) + const hazard5 = level.hazard(3425, -1420, 400, 8, 0.2) + const slimePit = level.hazard(2250, -100, 2700, 200, 0.004) + const door2 = level.door(3131, -898, 40, 520, 522) + const buttonDoor2 = level.button(2495, -270) + const toggle = level.toggle(1463, -708, true) + const elevator = level.elevator(4310, -150, 200, 50, -1443, 0.0025, { + up: 0.1, + down: 0.2 + }) + const portal = level.portal({ //main portals + x: 2117, + y: -1560 + }, -2 * Math.PI, { //up + x: -80, + y: -475 + }, -Math.PI / 100) //up + + const drip1 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip2 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 207) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip3 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 133) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip4 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 157) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + + level.custom = () => { + level.exit.drawAndCheck(); + + drip1.draw() + drip2.draw() + drip3.draw() + drip4.draw() + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + + buttonDoor2.query(); + buttonDoor2.draw(); + if (buttonDoor2.isUp) { + door2.isClosing = true + } else { + door2.isClosing = false + } + door2.openClose(); + + // shadow/shades builds + ctx.fillStyle = "rgba(0, 0, 0, 0.05)" + ctx.fillRect(3169, -900, 891, 580) + ctx.fillRect(417, -1057, 380, 730) + ctx.fillRect(930, -515, 207, 520) + ctx.fillRect(930, -1280, 207, 760) + ctx.fillRect(1220, -1280, 54, 800) + ctx.fillRect(1221, -1394, 451, 1398) + ctx.fillRect(1924, -800, 219, 674) + ctx.fillRect(2264, -1488, 214, 1550) + ctx.fillRect(2631, -1488, 201, 1550) + ctx.fillRect(2889, -930, 237, 1090) + ctx.fillRect(3124, -311, 957, 360) + ctx.fillRect(1919, -1480, 220, 700) + // ctx.fillRect(1768, -1200, 71, 500) + level.enter.draw(); + elevator.move(); + toggle.query(); + }; + level.customTopLayer = () => { + boost.query(); + hazard.opticalQuery(); + hazard2.opticalQuery(); + hazard3.opticalQuery(); + hazard4.opticalQuery(); + hazard5.opticalQuery(); + slimePit.query(); + // slimePit.draw(); + hazard.isOn = toggle.isOn + hazard3.isOn = toggle.isOn + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[2].query() + portal[3].query() + }; + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 1400; + level.exit.y = -1500; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + + //builds + spawn.mapRect(-100, 0, 1485, 100); + spawn.mapRect(-279, -750, 200, 850); + spawn.mapRect(1781, -125, 375, 75); + spawn.mapRect(1670, -100, 590, 200); + spawn.mapRect(2261, 50, 3100, 50); + spawn.mapRect(2420, -260, 300, 50); + spawn.bodyRect(235, -240, 50, 50, 1, spawn.propSLide) + spawn.mapRect(410, -1100, 400, 50); + spawn.mapRect(1220, -1470, 420, 80) + spawn.mapRect(927, -1325, 220, 50); + spawn.mapRect(4950, -200, 425, 375); + spawn.bodyRect(5200, -300, 100, 100); + + //random builds + spawn.mapRect(2150, 50, 225, 50); + + //large border walls + spawn.mapRect(-300, -2375, 6075, 475); + spawn.mapRect(-951, -2374, 675, 2476); + spawn.mapRect(-950, 100, 6950, 500); + spawn.mapRect(5300, -2374, 700, 2700); + + // create enemies + spawn.randomMob(3000, -300, 0.5); + spawn.randomMob(1900, -1000, 0.5); + spawn.randomMob(2960, -800, 0.6) + spawn.randomMob(3500, -1700, 0.4) + spawn.randomMob(800, -1700, 0.6) + spawn.randomMob(100, -1150, 0.6) + spawn.randomMob(1095, -700, 0.6) + + //powerUps + powerUps.spawn(590, -200, "ammo") + powerUps.spawn(600, -200, "heal") + // powerUps.spawn(590, -200, "gun") + powerUps.spawnStartingPowerUps(590, -200); + + // more builds + spawn.blockDoor(1230, -1490) + // spawn.blockDoor(728, -1130); + spawn.mapRect(-100, -380, 900, 50); + spawn.mapRect(-279, -1400, 200, 650); + spawn.mapRect(-279, -1900, 200, 650); + // spawn.mapRect(-100, -1900, 2300, 75); + // spawn.mapRect(2200, -1900, 1025, 75); + // spawn.mapRect(2700, -1900, 2000, 75); + spawn.mapRect(2270, -1530, 199, 50); + spawn.mapRect(2633, -1530, 199, 50) + // spawn.mapRect(4570, -1825, 125, 1925); + spawn.mapRect(3106, -400, 300, 50) + spawn.mapRect(3750, -400, 330, 50) + spawn.mapRect(3130, -1030, 930, 130); + spawn.mapRect(4015, -900, 46, 275); + spawn.blockDoor(4016, -400) + spawn.mapRect(3168, -1440, 290, 50); + spawn.mapRect(3771, -1440, 294, 50); + spawn.mapRect(3106, -355, 974, 42); + spawn.mapRect(3228, -1395, 834, 380); + spawn.mapRect(3129, -1350, 100, 325); + spawn.mapRect(3129, -1400, 175, 100); + spawn.mapRect(3129, -1437, 125, 75); + spawn.mapRect(1382, 0, 295, 100); + spawn.mapRect(1600, -50, 560, 85); + spawn.mapRect(2264, -945, 220, 50); + spawn.mapRect(1925, -800, 220, 50); + spawn.mapRect(1390, -700, 260, 50); + spawn.mapRect(927, -520, 220, 50); + spawn.mapRect(2894, -948, 300, 50) + spawn.mapRect(1230, -1825, 440, 81); + spawn.mapRect(1616, -1750, 54, 360); + spawn.mapRect(3128, -1440, 50, 50); + spawn.mapRect(1705, -120, 125, 75); + spawn.mapRect(1550, -25, 150, 50); + spawn.mapRect(1628, -75, 100, 50); + spawn.mapRect(1729, -130, 650, 75); + //ground for blue portal + spawn.mapRect(1917, -1484, 300, 50); + spawn.mapRect(1917, -1950, 200, 325); + spawn.mapRect(1917, -1825, 50, 375); + + //split + spawn.mapRect(1221, -1420, 57, 465); + spawn.mapRect(1221, -634, 57, 450); + spawn.bodyRect(1227, -105, 42, 189, 1, spawn.propSlide) + // spawn.mapRect(1770, -1900, 70, 750); + spawn.mapRect(1770, -780, 70, 400) + spawn.bodyRect(1783, -289, 38, 250, 1, spawn.propSlide) + if (simulation.difficulty > 1) spawn.randomLevelBoss(4800, -750); + spawn.secondaryBossChance(4700, -1500) + + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + biohazard() { + // MAP BY INOOBBOI AND THESHWARMA + simulation.makeTextLog(`biohazard by INOOBBOI and THESHWARMA`); + + // set here for the cutscene later + level.setPosToSpawn(-2800, -150) + + // set up cutscenes + simulation.cutscene = (locations, speed, stay, xPos = m.pos.x, yPos = m.pos.y) => { + // locations: an array of location vectors, reversed for the pop ahead + locations.reverse() + // speed: the speed of the cutscene transition (0 to 1) + // stay: how much to stay in the destination (ticks) + // xPos & yPos: the initial location, also used as the current location + + // start by disabling the default camera draw. Don't worry, it's backed up + const camera = simulation.camera + // create a new camera function + simulation.camera = () => { + ctx.save() + ctx.translate(canvas.width2, canvas.height2) //center + ctx.scale(simulation.zoom, simulation.zoom) + const xScaled = canvas.width2 - xPos + const yScaled = canvas.height2 - yPos + ctx.translate(-canvas.width2 + xScaled, -canvas.height2 + yScaled) //translate + } + + // and set a restoring function + const restore = () => (simulation.camera = camera) + + // then choose the next destination. There should be always at least one destination, + // if there isn't there's no point checking, the game should and will crash + let dest = locations.pop() + // animate the camera + const lerp = (first, second, percent) => first * (1 - percent) + second * percent + const speedDelta = speed / 5 + // wait timer + let wait = 0 + // polls the animation, should be called every tick + const poll = () => { + // update position + xPos = lerp(xPos, dest.x, speedDelta) + yPos = lerp(yPos, dest.y, speedDelta) + // if position is close enough, wait and go to the next position + const TOO_CLOSE = 100 + if (Math.abs(dest.x - xPos) < TOO_CLOSE && Math.abs(dest.y - yPos) < TOO_CLOSE) { + // wait for a bit + if (++wait > stay) { + // if there is another target, reset the wait timer and go there + // otherwise end the cutscene + wait = 0 + if (!(dest = locations.pop())) { + // no more locations! End + restore() + return true + } + } + } + // early return if the player skips by fielding + if (input.field) { + restore() + return true + } + return false + } + return poll + } + + const boost1 = level.boost(-1400, -100, 900) + const boost2 = level.boost(500, -900, 2500) + const boost3 = level.boost(4200, -100, 900) + const boost4 = level.boost(2200, -900, 2500) + + const toggle = level.toggle(1340, -600, false, true) + + let bossInit = false + + const cutscenePoll = simulation.cutscene([{ + x: 230, + y: -2700 + }, { + x: 3500, + y: -1400 + }, { + x: 1450, + y: -1150 + }, m.pos], 0.1, 10) + let hasEnded = false + + // ** PROPS ** + // create some drips + const rndInRange = (min, max) => Math.random() * (max - min) + min + + const amount = Math.round(5 + 20 * Math.random()) + const drips = [] + for (let i = 0; i < amount; i++) { + const locX = rndInRange(-2000, 4800) + drips.push(level.drip(locX, -3100, 1500, 200 + Math.random() * 500)) + } + + // a barrel of radioactive waste, which can drop ammo and heals + const barrelMob = (x, y, dirVector) => { + const MAX_WIDTH = 150 + const personalWidth = MAX_WIDTH / 2 + mobs.spawn(x, y, 4, personalWidth, 'rgb(232, 191, 40)') + const me = mob[mob.length - 1] + // steal some vertices + const betterVertices = Matter.Bodies.rectangle(x, y, personalWidth, personalWidth * 1.7).vertices + me.vertices = betterVertices + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob | cat.bullet + me.g = simulation.g + me.leaveBody = me.isDropPowerUp = false + me.do = function () { + this.gravity() + // apply shock damage when touching the map, if it's fast + if (this.speed > 5) { + const collision = Matter.Query.collides(this, map) + if (collision.length > 0) { + // on collision reduce health + this.health = this.health - this.speed / 250 + // die when it's too low, doesn't register for some reason + } + } + // becomes more radioactive as it gets damaged! + this.fill = `rgb(${232 * this.health}, 191, 40)` + } + + me.onDeath = function () { + const END = Math.floor(input.down ? 10 : 7) + const totalBullets = 10 + const angleStep = (input.down ? 0.4 : 1.3) / totalBullets + let dir = m.angle - (angleStep * totalBullets) / 2 + for (let i = 0; i < totalBullets; i++) { + //5 -> 7 + dir += angleStep + const me = bullet.length + bullet[me] = Bodies.rectangle( + this.position.x + 50 * Math.cos(this.angle), + this.position.y + 50 * Math.sin(this.angle), + 17, + 4, + b.fireAttributes(dir) + ) + const end = END + Math.random() * 4 + bullet[me].endCycle = 2 * end + simulation.cycle + const speed = (25 * end) / END + const dirOff = dir + (Math.random() - 0.5) * 3 + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dirOff), + y: speed * Math.sin(dirOff) + }) + bullet[me].onEnd = function () { + b.explosion( + this.position, + 150 + (Math.random() - 0.5) * 40 + ) //makes bullet do explosive damage at end + } + bullet[me].beforeDmg = function () { + this.endCycle = 0 //bullet ends cycle after hitting a mob and triggers explosion + } + bullet[me].do = function () { } + Composite.add(engine.world, bullet[me]) //add bullet to world + } + // barrels drop a ton of ammo and some heals, scales up with difficulty because I have mercy + const amount = ~~(5 * Math.random() * simulation.difficulty / 10) + for (let i = 0; i < amount; i++) { + powerUps.spawn(this.position.x, this.position.y, 'ammo', true) + if (Math.random() > 0.7) { + powerUps.spawn(this.position.x, this.position.y, 'heal', true) + } + } + } + Matter.Body.rotate(me, Math.random() * Math.PI) + Matter.Body.setVelocity(me, dirVector) + } + + // creates a platform with shadow + const platformShadow = (x, y, width, height, shadowList) => { + // faster than making manual shadows... Why not just calculate them semi-realsitically? + // the shadows are calculated on the object creation, so if you add map blocks it won't update. + // shadowList is an array of shadows that'll be rendered. When the platform shadow is ready, + // it is added to the list. + // some helper functions first + const perpCollision = point => { + // takes a point, and finds a collision with the map downwards + // the end of the ray, 3000 units down + const lowerPoint = Vector.add(point, { + x: 0, + y: 3000 + }) + // the destination point. If a collision was not found, then it defaults to some + // arbiterary point 3000 units down. + let dest = lowerPoint + for (const mapBody of map) { + const check = simulation.checkLineIntersection(point, lowerPoint, mapBody.vertices[0], mapBody.vertices[1]) + // a collision was found + if (check.onLine1 && check.onLine2) { + dest = { + x: check.x, + y: check.y + } + break + } + } + return dest + } + const boundsToRectangle = (firstBound, secondBound) => { + // takes two bounds and returns an (x, y, width, height) rectangle. The first one + // must be the top left, and the second one must be the bottom right + // sub to find the width and height + const width = Math.abs(firstBound.x - secondBound.x) + const height = Math.abs(firstBound.y - secondBound.y) + // compile to an object + return { + x: firstBound.x, + y: firstBound.y, + width, + height + } + } + // create the actual platform + spawn.mapRect(x, y, width, height) + const me = map[map.length - 1] + // the bottom vertices are the third and fourth ones + const first = me.vertices[3] + const second = me.vertices[2] + // cast shadows to find the last shadow location. + // iterate over all map objects, and check for collisions between a perpendicular ray + // cast from the vertex down to the map object's top panel + // const firstDown = perpCollision(first) // not needed in a rectangle settings + const secondDown = perpCollision(second) + // possible TODO: make it multirect for efficiency + // create a single rectangle and return + shadowList.push(boundsToRectangle(first, secondDown)) + } + + // cages with mobs, One of them holds the boss pre mutation + const cage = (x, y, maxChainLength, drawList, mobType = null, isTheBoss = false) => { + // the drawList is an array that the drawing function is added to + // if the cage containing the boss it has a 50% chance to just not spawn. Spices things a bit + if (!isTheBoss && Math.random() > 0.5) { + return + } + if (!mobType) { + // if mob type is null, then it picks a random mob + mobType = spawn.fullPickList[~~(Math.random() * spawn.fullPickList.length)] + } + // create the chain length, must take into account the radius of the mob. + // therefore, it'll create a pseudo mob of that type, take it radius and instantly kill it + const chainLength = maxChainLength / 5 + maxChainLength * Math.random() + + // spawn and insantly kill a mob of the same type to get the radius. + // this is used to prevent spawning the mob too short, it's a horrible + // solution but it works + spawn[mobType](0, 0) + mob[mob.length - 1].leaveBody = mob[mob.length - 1].isDropPowerUp = false + const radius = mob[mob.length - 1].radius + mob[mob.length - 1].alive = false + // spawn the mob. Disable shields first + spawn.allowShields = false + spawn[mobType](x, y + chainLength + radius * 2) + const trappedMob = mob[mob.length - 1] + // destroy its mind so it won't attack + trappedMob.do = () => { } + // spawn the cage + mobs.spawn(x, y + chainLength + radius * 2, 4, trappedMob.radius + 50, 'rgba(150, 255, 150, 0.3)') + const cage = mob[mob.length - 1] + cage.g = simulation.g + cage.do = function () { + this.gravity() + } + // label it + cage.label = 'Cage' + // a special orb when hit + let damageTick = 0 + cage.onDamage = (dmg) => { + // add some damage ticks, if the trapped mob is still alive. + // activating the boss by this method is almost impossible, since you need 10x damage + if (trappedMob.alive) damageTick += ~~(isTheBoss ? 5 * dmg : 50 * dmg) + } + // collision filter + trappedMob.collisionFilter.mask = cage.collisionFilter.mask = cat.player | cat.map | cat.bullet + // constrain together + spawn.constrain2AdjacentMobs(2, 0.05, false) + // move them to be together + trappedMob.position = Vector.clone(cage.position) // make sure you clone... Otherwise........ + // invincibility, make homing bullets not hit these, remove health bar + trappedMob.health = cage.health = Infinity + trappedMob.isBadTarget = cage.isBadTarget = true + trappedMob.showHealthBar = cage.showHealthBar = false + trappedMob.leaveBody = trappedMob.isDropPowerUp = cage.leaveBody = trappedMob.isDropPowerUp = false + // cross all edges of the cage with the rope, and see where it collides. Attach the rope there + const verts = cage.vertices + // the crossing location, doesn't stay null + let cross = null + for (let i = 0; i < verts.length; i++) { + // iterate over all vertices to form lines + const v1 = verts[i] + const v2 = verts[(i + 1) % verts.length] + const result = simulation.checkLineIntersection(cage.position, { + x, + y + }, v1, v2) + if (result.onLine1 && result.onLine2) { + // both lines cross! + cross = result + break + } + } + + if (!cross) { + // for some odd reason, sometimes it never finds a collision. I have no idea why + // just default to the center then + console.error("Couldn't find a cross... Origin: ", { + x, + y + }, " center: ", cage.position, ' vertices: ', cage.vertices) + cross = cage.position + } + // create the rope + const rope = Constraint.create({ + pointA: { + x, + y + }, + // offset the point be in the attachment point + pointB: Vector.sub(cross, cage.position), + bodyB: cage, + // the longer the rope, the looser it is + stiffness: Math.max(0.0005 - chainLength / 10000000, 0.00000001), + length: chainLength + }) + Matter.Composite.add(engine.world, rope) + // create and return a function for drawing the rope + const draw = () => { + // draw a little recantagle at the base + ctx.fillStyle = color.map + ctx.fillRect(x - 20, y - 5, 40, 25) + // if the cage was destroyed... Do nothing beyond + if (!cage.alive) { + return + } + // draw the rope + ctx.beginPath() + ctx.moveTo(x, y) + // line to the crossing point + // ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y); + ctx.lineTo(cage.position.x + rope.pointB.x, cage.position.y + rope.pointB.y); + ctx.lineWidth = 7 + ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)' + ctx.stroke() + // now draw a mystic hit orb if touched + if (damageTick) damageTick-- // reduce the ticks + ctx.beginPath() + ctx.arc(cage.position.x, cage.position.y, cage.radius + 30, 0, Math.PI * 2) + ctx.lineWidth = 10 + ctx.fillStyle = `rgba(255, 0, 0, ${Math.min(1, damageTick / 2000)})` + ctx.strokeStyle = `rgba(255, 100, 0, ${Math.min(1, damageTick / 1000)})` + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) + ctx.stroke() + ctx.setLineDash([]) + ctx.fill() + // if it's the boss, draw sucking arcs + if (isTheBoss && bossInit) { + for (const entity of mob) { + // suck the health of all mobs + // I hate string manipulation in control flow but heh + if (entity.alive) { + ctx.beginPath() + ctx.moveTo(entity.position.x, entity.position.y) + ctx.lineTo(trappedMob.position.x, trappedMob.position.y) + ctx.lineWidth = 10 + ctx.strokeStyle = 'rgba(38, 0, 255, 0.67)' + ctx.stroke() + // damage the mob + entity.damage(1) + // damage itself bonus + cage.damage(1) + } + } + cage.damage(5) + } + + // ok if it's too much, explode + if (damageTick > 2000) { + b.explosion(cage.position, cage.radius * 10) + // die a silent death + trappedMob.alive = cage.alive = false + damageTick = 0 + if (isTheBoss) { + // become the real boss + geneticBoss(trappedMob.position.x, trappedMob.position.y) + } + } + } + // restore the shields + spawn.allowShields = true + // add the drawing function + drawList.push(draw) + } + + // platform shadows + const shadows = [] + // cages + const cages = [] + + level.custom = () => { + level.exit.drawAndCheck() //draws the exit + level.enter.draw() //draws the entrance + + player.force.y -= player.mass * simulation.g * 0.25 //this gets rid of some gravity on player + + // if the cutscene is yet to end, continue polling + if (!hasEnded) { + hasEnded = cutscenePoll() + } + + for (const drip of drips) drip.draw() + // throw some barrels after the boss spawns + if (Math.random() > 0.999 && bossInit && !hasEnded) { + const spawnLocs = [-1415, -30, 1345, 2815, 4285] + // const randVec = Vector.mult({ x: Math.cos(randAngle), y: Math.sin(randAngle) }, Math.random() * 15) + barrelMob(spawnLocs[~~(spawnLocs.length * Math.random())], -4200, { + x: 0, + y: 0 + }) + } + + // platform shadow + ctx.beginPath() + for (const shadow of shadows) { + ctx.rect(shadow.x, shadow.y, shadow.width, shadow.height) + } + ctx.fillStyle = 'rgba(0,10,30,0.1)' + ctx.fill() + + // player pressed lever + if (toggle.isOn && !bossInit) { + bossInit = true + } + // draw the cage + } //for dynamic stuff that updates while playing that is one Z layer below the player + + level.customTopLayer = () => { + boost1.query() + boost2.query() + boost3.query() + boost4.query() + toggle.query() + + // shadow holes + ctx.fillStyle = 'rgba(68, 68, 68,0.95)' + ctx.fillRect(-1450 - 10, -4350, 150 + 20, 1250) + ctx.fillRect(-50 - 10, -4350, 150 + 20, 1250) + ctx.fillRect(1325 - 10, -4350, 150 + 20, 1250) + ctx.fillRect(2800 - 10, -4350, 150 + 20, 1250) + ctx.fillRect(4275 - 10, -4350, 150 + 20, 1250) + + for (const drawCage of cages) { + drawCage() + } + } //for dynamic stuff that updates while playing that is one Z layer above the player + + const anotherBoss = (x, y) => { + if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { + tech.isScaleMobsWithDuplication = true + spawn.historyBoss(x, y) + tech.isScaleMobsWithDuplication = false + } else if (tech.isResearchBoss) { + if (powerUps.research.count > 2) { + powerUps.research.changeRerolls(-3) + simulation.makeTextLog( + `m.research -= 3
${powerUps.research.count}` + ) + } else { + tech.addJunkTechToPool(0.49) + } + spawn.historyBoss(x, y) + } + } + + //GENETICBOSS + function drawEnergyBar(mob) { + if (mob.seePlayer.recall && mob.energy > 0) { + const h = mob.radius * 0.3 + const w = mob.radius * 2 + const x = mob.position.x - w / 2 + const y = mob.position.y - w * 0.9 + ctx.fillStyle = 'rgba(100, 100, 100, 0.3)' + ctx.fillRect(x, y, w, h) + ctx.fillStyle = '#0cf' + ctx.fillRect(x, y, w * mob.energy, h) + } + } + + function generateGenome() { + // creates a random genome and returns it + const genome = { + density: Math.random() * 0.001, + size: 15 + Math.random() * 15, + speed: Math.random() * 0.1, + // color and vertex properties are "trash" genes as they don't really contribute to the orb + color: [Math.random() * 255, Math.random() * 255, Math.random() * 255, 50 + Math.random() * 205], + vertexCount: Math.floor(Math.random() * 5) + 3, + // TODO fix possible concaving + vertexOffset: null // placeholder + } + // initialized here as it depends on vertexCount. I could use `new function()` but nah. + genome.vertexOffset = Array(genome.vertexCount) + .fill() + .map(() => ({ + x: Math.random() - 0.5, + y: Math.random() - 0.5 + })) + return genome + } + + function mutateGenome(genome) { + // takes an existing genome and applies tiny changes + const randomInRange = (min, max) => Math.random() * (max - min) + min + const tinyChange = x => randomInRange(-x, x) + + const vertexMutator = x => ({ + x: x.x + tinyChange(0.5), + y: x.y + tinyChange(0.5) + }) + // mutates a genome and returns the mutated version. + const newGenome = { + density: genome.density + tinyChange(0.0005), + size: genome.size + tinyChange(5), + speed: genome.speed + tinyChange(0.05), + color: genome.color.map(x => (x + tinyChange(10)) % 255), // wrap around + vertexCount: Math.max(genome.vertexCount + Math.round(tinyChange(1)), 3), + vertexOffset: genome.vertexOffset.map(vertexMutator) + } + if (genome.vertexOffset.length < newGenome.vertexCount) { + const vo = newGenome.vertexOffset + vo.push(vertexMutator(vo[~~(vo.length * Math.random())])) + } else if (genome.vertexOffset.length > newGenome.vertexCount) { + newGenome.vertexOffset.pop() + } + + return newGenome + } + + function calculateGenomeCost(genome) { + // calculates the cost of a genome and returns it. The cost is used to + // determine how "costly" the genome is to make, and after the orb's life ends it + // is used together with the orb success score to determine the fittest orb. + const score = (1 / (genome.density * genome.size * genome.speed)) * 0.000001 + return score + } + // distance functions + const dist2 = (a, b) => (a.x - b.x) ** 2 + (a.y - b.y) ** 2 + const dist = (a, b) => Math.sqrt(dist2(a, b)) + + // ** MAP SPECIFIC MOBS ** + function energyTransferBall(origin, target, boss, charge) { + // transports energy to the boss + // when the boss is hit by it, how much of the energy stored the boss actually recives + const ENERGY_TRANSFER_RATE = 80 /*%*/ + // add 1 to the active ball list + boss.activeBalls++ + const color = `rgba(${150 + 105 * charge}, 81, 50, 0.6)` + mobs.spawn(origin.x, origin.y, 12, 20 + 20 * charge, color) + const me = mob[mob.length - 1] + me.end = function () { + simulation.drawList.push({ + // some nice graphics + x: this.position.x, + y: this.position.y, + radius: this.radius, + color: '#f3571d', + time: ~~(Math.random() * 20 + 10) + }) + // on death spawn and explode a bomb + if (Math.random() > 0.95) { + spawn.bomb(this.position.x, this.position.y, this.radius, this.vertices.length) + mob[mob.length - 1].death() + } + // remove 1 from the active ball list + boss.activeBalls-- + this.death() + } + me.collisionFilter.mask = cat.player | cat.map + // me.onHit = this.end + me.life = 0 + me.isDropPowerUp = false + me.leaveBody = false + me.do = function () { + // die on collision with the map + if (Matter.Query.collides(this, map).length > 0) { + this.end() + } + // die if too much time passes. Stronger bullets explode earlier + if (++this.life > 200 - charge * 100) { + this.end() + } + // if the orb collides with the boss, die but give energy to the boss + if (Matter.Query.collides(this, [boss]).length > 0) { + boss.energy = Math.min(charge * (ENERGY_TRANSFER_RATE / 100) + boss.energy, 1) + // also make the boss fire once regardless of energy + boss.spawnOrbs() + this.end() + } + const movement = Vector.normalise(Vector.sub(target, origin)) + Matter.Body.setVelocity(this, { + x: this.velocity.x + movement.x, + y: this.velocity.y + movement.y + }) + // nice graphics + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: this.radius, + color: '#e81e1e', + time: 3 + }) + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: this.radius, + color: '#e87f1e', + time: 6 + }) + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: this.radius, + color: '#e8e41e', + time: 9 + }) + } + me.onDamage = me.end + } + + function energyBeacon(x, y, parentBoss) { + // an unmoving beacon that charges the genetic boss with energy either stolen + // from the player or generated. That energy is used to create stronger mobs. + mobs.spawn(x, y, 3, 50, '') // default color, changed an instant later + const me = mob[mob.length - 1] + me.laserRange = 500 + me.leaveBody = false + me.isDropPowerUp = false + // custom variables + // max energy is 1 + me.energy = 0 + me.seed = simulation.cycle // seed to make sure this mob is unique render wise + me.chargeTicks = 0 // used to time charging the boss + me.bossPos = null // the position that the mob remembers when charging + me.density = me.density * 2 + Matter.Body.setDensity(me, 0.0022 * 3 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense + me.do = function () { + // if the boss is dead, die + if (!parentBoss.alive) { + this.death() + } + // slowly rotate + Matter.Body.setAngularVelocity(this, 0.01) + this.fill = `rgba(${this.energy * 255}, 29, 136, 0.80)` + this.seePlayerCheck() + // steal energy from player + // this.harmZone() // regular harmZone + // custom effects on top of that + if (this.distanceToPlayer() < this.laserRange) { + if (m.immuneCycle < m.cycle) { + // suck extra energy from the player if it's in range + if (m.energy > 0.1 && this.energy < 1 - 0.012) { + m.energy -= 0.012 + this.energy += 0.012 + } + // special "sucking" graphics + ctx.beginPath() + ctx.moveTo(this.position.x, this.position.y) + ctx.lineTo(m.pos.x, m.pos.y) + ctx.lineWidth = 3 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 2 + ctx.strokeStyle = `rgb(${( + Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 + ).toFixed(3)}, 204, 255)` + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) + ctx.stroke() + ctx.setLineDash([]) + } + } + // if the mob's energy is at least 50% full, try to send that energy to the boss. + // don't send that energy yet if more than 5 other transfer balls are active + if (this.energy > 0.5 && parentBoss.energy < 1 && parentBoss.activeBalls <= 5 && this.chargeTicks === 0) { + const seesBoss = Matter.Query.ray(map, this.position, parentBoss.position).length === 0 + if (seesBoss) { + this.chargeTicks = 100 + this.bossPos = Vector.clone(parentBoss.position) + } + } + if (this.chargeTicks > 0) { + if (--this.chargeTicks === 0) { + // spawn the orb + const location = Vector.add( + Vector.mult(Vector.normalise(Vector.sub(this.bossPos, this.position)), this.radius * 3), + this.position + ) + energyTransferBall(location, this.bossPos, parentBoss, this.energy) + this.energy = 0 + } + // create a beam and aim it at the bossPos + ctx.beginPath() + ctx.moveTo(this.position.x, this.position.y) + ctx.lineTo(this.bossPos.x, this.bossPos.y) + ctx.lineWidth = 10 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 5 + ctx.strokeStyle = `rgb(${( + Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 + ).toFixed(3)}, 204, 255)` + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) + ctx.stroke() + ctx.setLineDash([]) + } + // generate (0.15 * difficulty / 4)% energy per tick + if (this.energy < 1) this.energy += 0.0015 * (simulation.difficulty / 4) + // draw energy bar + drawEnergyBar(this) + } + me.onDeath = function () { + // remove itself from the list + const beacons = parentBoss.energyBeacons + beacons.splice(beacons.indexOf(this), 1) + // explode with the strength of its energy! + this.alive = false // to prevent retriggering infinitly + b.explosion(this.position, this.energy * this.radius * 15) + // when it dies, it reduces some of the boss' energy + parentBoss.energy -= 0.025 + // and stuns it + mobs.statusStun(parentBoss, 70 + ~~(100 / simulation.difficulty)) + } + } + + function geneticSeeker(x, y, genome, parentBoss) { + // special bullets that get score based on their performance. + mobs.spawn(x, y, genome.vertexCount, genome.size, '#' + genome.color.map(it => (~~it).toString(16)).join('')) + const me = mob[mob.length - 1] + // apply genome + Matter.Body.setDensity(me, genome.density) + me.accelMag = genome.speed + // apply vertex offset + for (let i = 0; i < me.vertices.length; i++) { + const vertex = me.vertices[i] + const offset = genome.vertexOffset[i] + if (!offset) console.log(genome, me) + vertex.x += offset.x + vertex.y += offset.y + } + + me.stroke = 'transparent' + Matter.Body.setDensity(me, 0.00001) //normal is 0.001 + // increased if the orb done things that are deemed successful + me.score = 30 + me.timeLeft = 9001 / 9 + me.accelMag = 0.00017 * simulation.accelScale //* (0.8 + 0.4 * Math.random()) + me.frictionAir = 0.01 + me.restitution = 0.5 + me.leaveBody = false + me.isDropPowerUp = false + me.isBadTarget = true + me.isMobBullet = true + me.showHealthBar = false + me.collisionFilter.category = cat.mobBullet + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet + me.do = function () { + this.alwaysSeePlayer() + this.attraction() + this.timeLimit() + + if (Matter.Query.collides(this, map).length > 0) { + // colliding with the map gives a score reduction, 0.5 per tick + this.score -= 0.5 + } + // default score is slowly reduced every tick to give mobs that reached the player faster a benefit + this.score -= 0.05 + if (this.score < 0) { + this.alive = false // no point continuing if this orb is that bad! Silent death + } + // give a bonus if some projectile is nearby or the mouse position is close (like laser pointing) + // if a mob survives this for long, then it gets a score benefit. + const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */) + // player shoots and aims close + const mouseCloseToOrb = dist2(this.position, simulation.mouseInGame) < 10000 && input.fire + if (bulletCloseToOrb || mouseCloseToOrb) { + this.score += 1 + } + // die if too far from the boss... It would be incredibly difficult to dodge otherwise + if (dist2(this.position, parentBoss.position) > 2000 * 2000) { + this.alive = false + } + // DEBUG score printer + // ctx.font = '48px sans-serif' + // ctx.fillStyle = 'rgba(252, 0, 143, 1)' + // ctx.fillText(~~this.score, this.position.x - this.radius, this.position.y - this.radius) + } + me.onHit = function () { + // hitting the player gives a 50 points score bonus + this.score += 50 + this.score += this.mass * 2 // bigger mass = bigger damage, add that too + // a marker for later + this.hitPlayer = true + this.explode(this.mass) + } + me.onDeath = function () { + if (!this.hitPlayer) { + // if it didn't hit the player, give it a score based on its distance + this.score += 10000 / this.distanceToPlayer() + } + // 3% chance to drop ammo + if (Math.random() > 0.97) { + powerUps.spawn(this.position.x, this.position.y, 'ammo', true) + } + parentBoss.deadOrbs.push({ + genome: genome, + score: this.score + }) + } + } + + function geneticBoss(x, y, radius = 130, spawnBossPowerUp = true) { + // a modified orb shooting boss that evolves its orbs. + // the way this boss works is different from the regular orb shooting boss, + // because the orbs have evolving properties via a "machine learning" scoring algorithm. + const MAX_BEACONS = Math.round(3 + Math.random() * simulation.difficulty / 3) + mobs.spawn(x, y, 8, radius, 'rgb(83, 32, 58)') + let me = mob[mob.length - 1] + me.isBoss = true + + me.accelMag = 0.0001 * simulation.accelScale + me.fireFreq = Math.floor((330 * simulation.CDScale) / simulation.difficulty) + me.frictionStatic = 0 + me.friction = 0 + me.frictionAir = 0.02 + me.memory = (420 / 69) * 42 // 🧌 + me.repulsionRange = 1000000 + me.energyBeacons = [] + me.activeBalls = 0 + // starts by random, or by the stored genomes if they exist + const init = () => ({ + genome: generateGenome(), + score: 0 + }) + me.fittestOrbs = (localStorage && localStorage.genome) ? JSON.parse(localStorage.genome) : [init(), init(), init()] // best genomes so far. Size of three + // when an orb died it's moved here. When a new spawn cycle starts, their scores get calculated + // and they get put in the fittest orbs array, if they are better than the old ones. + me.deadOrbs = [] + me.energy = 1 + // this boss has no orbitals, because it's not meant to ever attack on its own + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // has a shield and sustains that shield + spawn.shield(me, x, y, Infinity) + me.fireFreq = 30 + me.ionizeFreq = 20 + me.ionized = [] + me.laserRange = radius * 4 + + Matter.Body.setDensity(me, 0.0022 * 4 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + if (spawnBossPowerUp) { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + const amount = ~~(5 * Math.random() * simulation.difficulty / 10) * 2 + for (let i = 0; i < amount; i++) { + powerUps.spawn(this.position.x, this.position.y, 'ammo', true) + if (Math.random() > 0.7) { + powerUps.spawn(this.position.x, this.position.y, 'heal', true) + } + } + } + // keep the best genome and use it next fight... + if (localStorage) { + localStorage.setItem("genome", JSON.stringify(this.fittestOrbs)) + } + + // stop spawning barrels + bossInit = false + } + me.onDamage = function () { } + me.spawnBeacon = function () { + // the vertex to spawn the beacon from + const vert = this.vertices[~~(Math.random() * this.vertices.length)] + // the position should be a little to the side to prevent crashing into the boss + // TODO check for collisions with the wall + const spawnPos = Vector.add(vert, Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -60)) + // some velocity + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -5) + energyBeacon(spawnPos.x, spawnPos.y, this) // spawn the beacon, a bit ahead + const beacon = mob[mob.length - 1] + this.energyBeacons.push(beacon) + Matter.Body.setVelocity(beacon, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }) + } + me.spawnOrbs = function () { + Matter.Body.setAngularVelocity(this, 0.11) + // sort the vertices by the distance to the player + const sorted = [...this.vertices].sort(dist2) + // spawn the bullets based on how close they are to the player. + // the way it works is it picks the fittest three orbs and clones them. + // but start by taking old mobs and checking if they are better than the new ones + let next + while ((next = this.deadOrbs.pop())) { + // material costs are calculated as a contra to the score + const cost = calculateGenomeCost(next.genome) * 500 // normalize via multiplication + const totalScore = next.score - cost + // try to insert itself into the best orbs, if it can + for (let i = 0; i < this.fittestOrbs.length; i++) { + const fitEntry = this.fittestOrbs[i] + if (fitEntry.score < totalScore) { + this.fittestOrbs[i] = next + break + } + } + } + // finally sort them using their score + this.fittestOrbs.sort((a, b) => a.score - b.score) + // only take the genome, the score doesn't matter here + const bestOrbs = this.fittestOrbs.map(it => it.genome) + for (let vertex of sorted) { + // pick a random fit orb and try to spawn it. If the cost is too high, it'll attempt + // to generate a new random orb instead. If that orb is too expensive too, just ignore this vertex. + // the evolution part comes here, as the genome is mutated first. + let randGenome = mutateGenome(bestOrbs[~~(Math.random() * bestOrbs.length)]) + const cost = calculateGenomeCost(randGenome) * 2 + if (this.energy - cost < 0) { + // okay this orb is too expensive for the boss to spawn, + // make a new orb from scratch + randGenome = generateGenome() + const secondCost = calculateGenomeCost(randGenome) + if (this.energy - secondCost < 0) { + // that was too expensive too, heh + continue + } + } else { + // alright the boss can afford that + this.energy -= Math.abs(cost) // TODO: Fix this, why the heck can it even be negative?? + } + + geneticSeeker(vertex.x, vertex.y, randGenome, this) + // give the bullet a rotational velocity as if they were attached to a vertex + const velocity = Vector.mult( + Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))), + -10 + ) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }) + } + } + me.do = function () { + this.seePlayerCheck() + this.checkStatus() + this.attraction() + this.repulsion() + // draw laser arcs if it sees the player + this.harmZone() + // + const regularChance = Math.random() > 0.99 + const biggerChance = Math.random() > 0.95 && this.energy > 0.25 + // start by making sure there is always at least one beacon + if (this.energyBeacons.length === 0) { + this.spawnBeacon() + } + // then, spawn some energy beacons if there are less than the maximum. + // small chance if there's no energy, bigger chance if there is at least 10% (which is drained) + if ((this.energyBeacons.length < MAX_BEACONS && biggerChance) || regularChance) { + if (biggerChance) { + // if the spawn was a selection of bigger chance, reduce 10% energy + this.energy -= 0.10 + } + this.spawnBeacon() + } + // then, spawn genetic seekers + if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + // fire a bullet from each vertex if there's enough energy + if (this.energy > 0.15) { + this.spawnOrbs() + } + } + + if (this.energy > 1) { + // clean excess energy + this.energy -= 0.003 + } else { + // or slowly generate energy + this.energy += 0.001 + } + // the boss will ionize every bullet in its radius, but that will cause its energy to deplete + if (!(simulation.cycle % this.ionizeFreq)) { + for (let i = 0; i < bullet.length; i++) { + const it = bullet[i] + // if it's not a bot and it's close + if (!it.botType && dist(this.position, it.position) < this.laserRange) { + // add it to the ionized list + this.ionized.push({ + target: it, + ticks: 0 + }) + } + } + } + + for (let i = 0; i < this.ionized.length; i++) { + const entry = this.ionized[i] + + // skip if there's not enough energy + if (this.energy <= 0) break + + // terminate if it's no longer in the radius + if (dist(this.position, entry.target.position) > this.laserRange) { + this.ionized.splice(i, 1) + continue + } + // terminate after some ticks + if (++entry.ticks === 10) { + entry.target.endCycle = 0 + // draw nice popping graphics + simulation.drawList.push({ + x: entry.target.position.x, + y: entry.target.position.y, + radius: 5, + color: '#f24', + time: ~~(Math.random() * 20 + 10) + }) + // and remove + this.ionized.splice(i, 1) + continue + } + // draw line + ctx.beginPath() + ctx.moveTo(this.position.x, this.position.y) + ctx.lineTo(entry.target.position.x, entry.target.position.y) + ctx.lineWidth = 7 + ctx.strokeStyle = `rgb(${60 - entry.ticks * 2}, 50, 50)` + ctx.stroke() + // reduce energy, as it's hard to ionize + this.energy -= entry.target.mass / 25 + } + + // if it has energy, shield itself and drain energy + if (!this.isShielded && this.energy > 0.5) { + spawn.shield(this, this.position.x, this.position.y, Infinity) + this.energy -= 0.25 + } + drawEnergyBar(this) + // change fill color + this.fill = `rgb(${((Math.sin(simulation.cycle / 100) + 1) / 2) * 100}, 32, 58)` + } + // start by spawning several beacons to gain initial energy + const amount = Math.ceil(2 + Math.random() * simulation.difficulty / 5) + for (let i = 0; i < amount; i++) + me.spawnBeacon() + } + + // LEVEL SETUP + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20) //don't change this + + level.exit.x = 5700 //you exit at x + level.exit.y = -130 //you exit at y + spawn.mapRect(5800, -110, -100, 10) + + level.defaultZoom = 2000 //how far out you want the image to be zoomed at (lower = zoom in, higher = zoom out) + simulation.zoomTransition(level.defaultZoom) //makes the level transition to have the zoom at the start of a level + document.body.style.backgroundColor = 'hsl(138, 3%, 74%)' //sets background color + + //LEVEL STRUCTURE + spawn.mapRect(-3100, -100, 9200, 100) + spawn.mapRect(-3100, -600, 100, 500) + spawn.mapRect(-3100, -600, 1100, 100) + spawn.mapRect(-2100, -3100, 100, 2700) + spawn.mapRect(4800, -3100, 100, 2600) + spawn.mapRect(4800, -600, 1300, 100) + spawn.mapRect(6000, -600, 100, 600) + + spawn.mapRect(400, -200, 2000, 100) + spawn.mapRect(600, -300, 1600, 100) + spawn.mapRect(800, -400, 1200, 100) + spawn.mapRect(1000, -500, 800, 100) + spawn.mapRect(1200, -600, 400, 100) + // roof + spawn.mapRect(-2100, -4350, 650, 1250) + spawn.mapRect(-1300, -4350, 1250, 1250) + spawn.mapRect(100, -4350, 1225, 1250) + spawn.mapRect(1475, -4350, 1325, 1250) + spawn.mapRect(2950, -4350, 1325, 1250) + spawn.mapRect(4425, -4350, 475, 1250) + + // arc + // spawn.mapVertex(1400, -892, '700, -800, 700, -900, 1000, -1000, 1800, -1000, 2100, -900, 2100, -800') + + + //PLATFORMS + platformShadow(-1200, -500, 300, 100, shadows) + platformShadow(-400, -700, 300, 100, shadows) + platformShadow(400, -900, 300, 100, shadows) + platformShadow(-2000, -800, 300, 100, shadows) + platformShadow(-1000, -1000, 300, 100, shadows) + platformShadow(-400, -1300, 300, 100, shadows) + platformShadow(-1600, -1300, 300, 100, shadows) + platformShadow(-1300, -1600, 300, 100, shadows) + platformShadow(-2000, -1700, 300, 100, shadows) + platformShadow(-700, -1800, 300, 100, shadows) + platformShadow(-1500, -2100, 300, 100, shadows) + platformShadow(-600, -2200, 300, 100, shadows) + platformShadow(-2000, -2500, 300, 100, shadows) + platformShadow(-1100, -2400, 300, 100, shadows) + platformShadow(-500, -2700, 300, 100, shadows) + platformShadow(100, -2400, 300, 100, shadows) + platformShadow(700, -2700, 300, 100, shadows) + + platformShadow(3700, -500, 300, 100, shadows) + platformShadow(2900, -700, 300, 100, shadows) + platformShadow(2100, -900, 300, 100, shadows) + platformShadow(4500, -800, 300, 100, shadows) + platformShadow(3500, -1000, 300, 100, shadows) + platformShadow(4100, -1300, 300, 100, shadows) + platformShadow(2900, -1300, 300, 100, shadows) + platformShadow(3800, -1600, 300, 100, shadows) + platformShadow(4500, -1700, 300, 100, shadows) + platformShadow(3200, -1800, 300, 100, shadows) + platformShadow(4000, -2100, 300, 100, shadows) + platformShadow(3100, -2200, 300, 100, shadows) + platformShadow(4500, -2500, 300, 100, shadows) + platformShadow(3600, -2400, 300, 100, shadows) + platformShadow(3000, -2700, 300, 100, shadows) + platformShadow(2400, -2400, 300, 100, shadows) + platformShadow(1800, -2700, 300, 100, shadows) + + // cages + cage(-1492, -1200, 100, cages) + cage(-875, -2300, 300, cages) + cage(-1600, -3100, 1000, cages) + cage(225, -2300, 1000, cages) + cage(-750, -3100, 700, cages) + cage(-625, -1700, 1200, cages) + cage(2200, -3100, 500, cages) + cage(3275, -1700, 500, cages) + cage(3650, -900, 300, cages) + cage(2500, -2300, 300, cages) + cage(3625, -2300, 300, cages) + cage(3875, -1500, 300, cages) + cage(4025, -3100, 300, cages) + + // boss cage + platformShadow(1275, -2150, 250, 100, shadows) + cage(1400, -2050, 500, cages, 'starter', true) + map[map.length] = Bodies.trapezoid(1400, -2193, 250, 100, 0.5) + //DEBRIS + //idk just put the debris wherever you want + spawn.debris(-550, -225, 100) + spawn.debris(-1150, -1725, 75) + spawn.debris(-275, -1400, 50) + spawn.debris(2850, -2075, 150) + spawn.debris(4250, -2250, 150) + //BOSS + // geneticBoss(1400, -3800) + anotherBoss(0, 0) //will only spawn historyBoss if there is an additional boss + }, + stereoMadness() { + simulation.makeTextLog(`stereoMadness by Richard0820`); + let totalCoin = 0; + const hunter = function (x, y, radius = 30) { //doesn't stop chasing until past 105000 + mobs.spawn(x, y, 6, radius, "black"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.collisionFilter.mask = cat.player | cat.bullet; + me.accelMag = 0.0006 * Math.min(simulation.difficulty + 1, 4); + me.showHealthBar = false; + me.isUnblockable = true; + me.isShielded = true; + me.memory = Infinity; + me.seeAtDistance2 = Infinity; + Matter.Body.setDensity(me, 1) + simulation.makeTextLog(`Ω: Intruder Detected`); + me.boost = 10; + me.do = function () { + if (me.boost == 1 && m.fieldMode == 3 || m.fieldMode == 9 && me.boost == 1) { + me.accelMag *= 1.5; + me.boost--; + } + this.attraction(); + this.checkStatus(); + this.repelBullets(); + this.locatePlayer(); + this.alwaysSeePlayer() + if (player.position.x > 105000) { + this.death() + } + }; + me.onHit = function () { + for (let i = 0; i < 10; i++) { + spawn.spawns(this.position.x + Math.random() * 1000 - Math.random() * 1000, this.position.y - Math.random() * 1000) + } + } + } + const coin = function (x, y, radius = 50) { + mobs.spawn(x, y, 40, radius, "yellow"); + let me = mob[mob.length - 1]; + me.stroke = "#00000055" + me.isShielded = true; + me.leaveBody = false; + me.isBadTarget = true; + me.isUnblockable = true; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.collisionFilter.category = 0; + Matter.Body.setDensity(me, 0.0045); + me.onDeath = function () { + totalCoin++; + }; + me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.PI / 2 + 0.5) + ctx.strokeStyle = "#000000"; + ctx.beginPath() + ctx.arc(0, 0, 30, -1, Math.PI, false) + ctx.moveTo(20, -5) + ctx.arc(0, 0, 20, -1, Math.PI, false) + ctx.lineWidth = 5; + ctx.stroke() + ctx.restore() + if (!simulation.isTimeSkipping) { + const sine = Math.sin(simulation.cycle * 0.015) + this.radius = 50 * (1 + 0.1 * sine) + const sub = Vector.sub(player.position, this.position) + const mag = Vector.magnitude(sub) + const force = Vector.mult(Vector.normalise(sub), 0.000000003) + if (mag < this.radius) { //heal player when inside radius + if (m.health < 0.7) { + m.damage(-0.001); + } else if (m.health == 0.7 || m.health > 0.7) { + this.death() + } + ctx.strokeStyle = "#00FFDD55"; + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 34, 0, 2 * Math.PI); + ctx.lineWidth = 6; + ctx.stroke(); + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius + 15, 0, 2 * Math.PI); + ctx.strokeStyle = "#000" + ctx.lineWidth = 1; + ctx.stroke(); + }; + } + } + const spike = function (x, y, angle = Math.PI * 0.5, radius = 50) { + mobs.spawn(x, y, 3, radius, "#454545"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.isDropPowerUp = false; + me.showHealthBar = false; + Matter.Body.setDensity(me, 50) + me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 0, + damping: 0 + }); + me.do = function () { + if (this.health < 1) { + this.health += 0.001; //regen + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: this.radius / 1.5, + color: `rgba(0, 255, 20, ${Math.random() * 0.1})`, + time: simulation.drawTime + }); + } + this.checkStatus(); + Matter.Body.setAngle(me, angle); + }; + me.onHit = function () { + m.damage(0.01) //extra damage + me.collisionFilter.mask = 0; + setTimeout(() => { + me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; + }, 1000); + } + me.onDeath = function () { + tech.addJunkTechToPool(0.1) + } + Composite.add(engine.world, me.constraint); + } + + function drawStar(cx, cy, spikes, outerRadius, innerRadius) { + outerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15)); + innerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15)); + var rot = Math.PI / 2 * 3; + var xs = cx; + var y = cy; + var step = Math.PI / spikes; + + ctx.strokeSyle = "#000"; + ctx.beginPath(); + ctx.moveTo(cx, cy - outerRadius) + for (i = 0; i < spikes; i++) { + xs = cx + Math.cos(rot) * outerRadius; + y = cy + Math.sin(rot) * outerRadius; + ctx.lineTo(xs, y) + rot += step + + xs = cx + Math.cos(rot) * innerRadius; + y = cy + Math.sin(rot) * innerRadius; + ctx.lineTo(xs, y) + rot += step + } + ctx.lineTo(cx, cy - outerRadius) + ctx.closePath(); + ctx.lineWidth = 5; + ctx.strokeStyle = 'red'; + ctx.stroke(); + ctx.fillStyle = 'darkred'; + ctx.fill(); + + } + const slimePit1 = level.hazard(7475, -75, 475, 100, 0.01) + const slimePit2 = level.hazard(11275, -75, 1450, 100, 0.01) + const slimePit3 = level.hazard(13400, -150, 3500, 200, 0.1) + const slimePit4 = level.hazard(20275, -400, 3475, 450, 0.01) + const slimePit5 = level.hazard(25300, -800, 20000, 650, 0.003) + const slimePit6 = level.hazard(47725, -425, 2500, 475, 0.01) + const slimePit7 = level.hazard(50975, -575, 4050, 650, 0.01) + const slimePit8 = level.hazard(54950, -950, 6650, 1050, 0.01) + const slimePit9 = level.hazard(63550, -75, 2150, 100, 0.01) + const slimePit10 = level.hazard(70875, -75, 1200, 100, 0.01) + const slimePit11 = level.hazard(72075, -50, 900, 75, 1) + const slimePit12 = level.hazard(75900, -75, 2575, 100, 0.01) + const slimePit13 = level.hazard(78475, -35, 2300, 70, 0.01) + const slimePit14 = level.hazard(82875, -75, 5100, 100, 0.1) + + const drip1 = level.drip(32474, -2165, -800, 100); + const drip2 = level.drip(37750, -2165, -800, 100); + const drip3 = level.drip(43525, -2165, -800, 100); + const drip4 = level.drip(20475, -830, -375, 100); + const drip5 = level.drip(49960, -2315, -423, 200) + let textlogOne = 0; + let textlogTwo = 0; + let barThere = true; + let bar = document.createElement("div"); + bar.style.cssText = `position: absolute; top: 80px; background-color: black; width: 80vw; z-index: 1; border-radius: 10px; height: 20px; left: 5em; right: 5em;`; + bar.innerHTML += `
` + document.body.appendChild(bar); + let innerBar = document.getElementById("innerBar"); + level.custom = () => { + level.exit.drawAndCheck(); + if (barThere == true) { + innerBar.style.width = "calc(" + `${Math.max(0, Math.min(player.position.x / 1310, 80))}` + "vw - 10px)"; + innerBar.style.backgroundColor = m.eyeFillColor; + } + if (m.pos.x > 25360 && textlogOne == 0) { + simulation.makeTextLog(`
A stong force pushes you forward...
`) + textlogOne++; + } + if (m.pos.x < -3000) { + Matter.Body.setVelocity(player, { + x: 99, + y: 19 + }); + + if (textlogTwo == 0) + simulation.makeTextLog(`
A strong force pushes you away...
`); + textlogTwo++; + } + if (m.pos.y > 1055) { + Matter.Body.setPosition(player, { x: 0, y: -150 }); + simulation.makeTextLog(`
There is nowhere to run...
`); + m.damage(0.1 * simulation.difficultyMode); + } + if (m.alive == false && barThere == true) { + document.body.removeChild(bar); + barThere = false; + } + ctx.beginPath() + ctx.lineWidth = 5; + ctx.strokeStyle = "#000000"; + ctx.moveTo(40, -1000) + ctx.arc(0, -1000, 40, 0, 2 * Math.PI) + ctx.stroke() + ctx.fillStyle = "#FF00FF55" + ctx.fillRect(89750, -1300, 800, 200) + ctx.fillRect(89750, -200, 800, 200) + ctx.fillRect(92050, -200, 800, 200) + ctx.fillRect(92050, -1675, 800, 575) + ctx.fillRect(93950, -350, 200, 350); + ctx.fillRect(95100, -1350, 150, 375); + ctx.fillRect(100900, -1325, 1175, 250); + ctx.fillRect(100900, -225, 1200, 250); + ctx.fillRect(98725, -1325, 450, 150); + ctx.fillRect(98725, -125, 450, 150); + ctx.beginPath() + //lines! + ctx.lineWidth = 10; + ctx.strokeStyle = "#000000"; + ctx.moveTo(7462.5, -250) + ctx.lineTo(7462.5, -170) + ctx.moveTo(7710, -330) + ctx.lineTo(7710, -250) + ctx.moveTo(7960, -420) + ctx.lineTo(7960, -320) + ctx.moveTo(13725, -250) + ctx.lineTo(13725, -180) + ctx.moveTo(14025, -350) + ctx.lineTo(14025, -280) + ctx.moveTo(14325, -450) + ctx.lineTo(14325, -380) + ctx.moveTo(14625, -550) + ctx.lineTo(14625, -480) + ctx.moveTo(14925, -650) + ctx.lineTo(14925, -580) + ctx.moveTo(15225, -750) + ctx.lineTo(15225, -680) + ctx.moveTo(15525, -850) + ctx.lineTo(15525, -780) + ctx.moveTo(15825, -950) + ctx.lineTo(15825, -880) + ctx.moveTo(16125, -1050) + ctx.lineTo(16125, -980) + ctx.moveTo(16425, -1150) + ctx.lineTo(16425, -1080) + ctx.moveTo(22600, -650) + ctx.lineTo(22600, -580) + ctx.moveTo(22800, -750) + ctx.lineTo(22800, -680) + ctx.moveTo(23000, -850) + ctx.lineTo(23000, -780) + ctx.moveTo(23200, -950) + ctx.lineTo(23200, -880) + ctx.moveTo(23400, -1050) + ctx.lineTo(23400, -980) + ctx.moveTo(23600, -1150) + ctx.lineTo(23600, -1080) + ctx.moveTo(29550, -1625) + ctx.lineTo(29550, -1425) + ctx.moveTo(32275, -2125) + ctx.lineTo(32275, -1925) + ctx.moveTo(33775, -2125) + ctx.lineTo(33775, -1925) + ctx.moveTo(32275, -350) + ctx.lineTo(32275, -550) + ctx.moveTo(33775, -350) + ctx.lineTo(33775, -550) + ctx.moveTo(35475, -650) + ctx.lineTo(35475, -450) + ctx.moveTo(37650, -2000) + ctx.lineTo(37650, -1800) + ctx.moveTo(39675, -400) + ctx.lineTo(39675, -600) + ctx.moveTo(40375, -500) + ctx.lineTo(40375, -700) + ctx.moveTo(41075, -600) + ctx.lineTo(41075, -800) + ctx.moveTo(43625, -1925) + ctx.lineTo(43625, -1725) + ctx.moveTo(50975, -1125) + ctx.lineTo(50975, -925) + ctx.moveTo(51387.5, -1325) + ctx.lineTo(51387.5, -1125) + ctx.moveTo(51787.5, -1525) + ctx.lineTo(51787.5, -1325) + ctx.moveTo(52187.5, -1725) + ctx.lineTo(52187.5, -1525) + ctx.moveTo(52587.5, -1725) + ctx.lineTo(52587.5, -1925) + ctx.moveTo(52987.5, -2125) + ctx.lineTo(52987.5, -1925) + ctx.moveTo(53387.5, -2325) + ctx.lineTo(53387.5, -2125) + ctx.moveTo(53787.5, -2525) + ctx.lineTo(53787.5, -2325) + ctx.moveTo(54187.5, -2725) + ctx.lineTo(54187.5, -2525) + ctx.moveTo(54587.5, -2925) + ctx.lineTo(54587.5, -2725) + ctx.moveTo(54987.5, -3125) + ctx.lineTo(54987.5, -2925) + ctx.moveTo(57500, -1925) + ctx.lineTo(57650, -1925) + ctx.moveTo(57520, -1845) + ctx.lineTo(57650, -1845) + ctx.moveTo(57500, -1925) + ctx.lineTo(57895 + 300, -1925) + ctx.moveTo(57520, -1845) + ctx.lineTo(57895 + 300, -1845) + ctx.moveTo(58525, -1725) + ctx.lineTo(58525 + 125, -1725) + ctx.moveTo(58525, -1625) + ctx.lineTo(58525 + 125, -1625) + ctx.moveTo(59000, -1725) + ctx.lineTo(59150, -1725) + ctx.moveTo(59150, -1625) + ctx.lineTo(59000, -1625) + ctx.moveTo(70875, -200) + ctx.lineTo(70875, -100) + ctx.moveTo(63700, -200) + ctx.lineTo(63800, -200) + ctx.moveTo(64000, -200) + ctx.lineTo(64100, -200) + ctx.moveTo(64675, -200) + ctx.lineTo(64575, -200) + ctx.moveTo(64875, -200) + ctx.lineTo(64975, -200) + ctx.moveTo(65025, -300) + ctx.lineTo(64925, -300) + ctx.moveTo(65225, -300) + ctx.lineTo(65325, -300) + ctx.moveTo(71275, -200) + ctx.lineTo(71275, -300) + ctx.moveTo(71675, -300) + ctx.lineTo(71675, -400) + ctx.moveTo(72075, -400) + ctx.lineTo(72075, -500) + ctx.moveTo(72425, -325) + ctx.lineTo(72425, -425) + ctx.moveTo(72675, -200) + ctx.lineTo(72675, -300) + ctx.moveTo(72925, -75) + ctx.lineTo(72925, -175) + ctx.moveTo(75225, -100) + ctx.lineTo(75225, -200) + ctx.moveTo(76600, -125) + ctx.lineTo(76600, -225) + ctx.moveTo(76900, -200) + ctx.lineTo(76900, -300) + ctx.moveTo(77175, -275) + ctx.lineTo(77175, -375) + ctx.moveTo(77475, -350) + ctx.lineTo(77475, -450) + ctx.moveTo(85575, -275) + ctx.lineTo(85575, -375) + ctx.moveTo(86000, -275) + ctx.lineTo(86000, -375) + ctx.moveTo(86275, -275) + ctx.lineTo(86275, -375) + ctx.moveTo(86950, -425) + ctx.lineTo(86950, -525) + ctx.moveTo(89700, -175) + ctx.lineTo(89700, -75) + ctx.moveTo(89700, -1125) + ctx.lineTo(89700, -1225) + ctx.moveTo(90600, -1225) + ctx.lineTo(90600, -1125) + ctx.moveTo(90600, -100) + ctx.lineTo(90600, -175) + ctx.moveTo(92000, -100) + ctx.lineTo(92000, -175) + ctx.moveTo(92900, -100) + ctx.lineTo(92900, -175) + ctx.moveTo(92900, -1225) + ctx.lineTo(92900, -1125) + ctx.moveTo(94500, -1475) + ctx.lineTo(94500, -1575) + ctx.moveTo(94700, -1475) + ctx.lineTo(94700, -1575) + ctx.moveTo(94900, -1475) + ctx.lineTo(94900, -1575) + ctx.moveTo(96125, -1500) + ctx.lineTo(96125, -1575) + ctx.moveTo(96550, -1575) + ctx.lineTo(96550, -1500) + ctx.moveTo(97000, -1475) + ctx.lineTo(97000, -1575) + + ctx.stroke() + ctx.beginPath() + ctx.strokeStyle = "#FFFFFF"; + ctx.fillStyle = document.body.style.backgroundColor; + let cycles = Math.sin(simulation.cycle * 0.15) + ctx.moveTo(7482.5, -270) + ctx.arc(7462.5, -270, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI); + ctx.moveTo(7730, -350) + ctx.arc(7710, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI); + ctx.moveTo(7980, -420) + ctx.arc(7960, -420, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(13745, -180) + ctx.arc(13725, -180, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(14045, -280) + ctx.arc(14025, -280, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(14345, -380) + ctx.arc(14325, -380, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(14645, -480) + ctx.arc(14625, -480, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(14945, -580) + ctx.arc(14925, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(15245, -680) + ctx.arc(15225, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(15545, -780) + ctx.arc(15525, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(15845, -880) + ctx.arc(15825, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(16145, -980) + ctx.arc(16125, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(16445, -1080) + ctx.arc(16425, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(22620, -580); + ctx.arc(22600, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(22820, -680); + ctx.arc(22800, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(23020, -780); + ctx.arc(23000, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(23220, -880); + ctx.arc(23200, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(23420, -980); + ctx.arc(23400, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(23620, -1080); + ctx.arc(23600, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(29570, -1425) + ctx.arc(29550, -1425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(32295, -1925) + ctx.arc(32275, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(33795, -1925) + ctx.arc(33775, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(32295, -550) + ctx.arc(32275, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(33795, -550) + ctx.arc(33775, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(35495, -650) + ctx.arc(35475, -650, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(37670, -1800) + ctx.arc(37650, -1800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(39695, -600) + ctx.arc(39675, -600, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(40395, -700) + ctx.arc(40375, -700, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(41095, -800) + ctx.arc(41075, -800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(43645, -1725) + ctx.arc(43625, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(50995, -1125) + ctx.arc(50975, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(51407.5, -1325) + ctx.arc(51387.5, -1325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(51807.5, -1525) + ctx.arc(51787.5, -1525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(52207.5, -1725) + ctx.arc(52187.5, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(52607.5, -1925) + ctx.arc(52587.5, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(53007.5, -2125) + ctx.arc(52987.5, -2125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(53407.5, -2325) + ctx.arc(53387.5, -2325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(53807.5, -2525) + ctx.arc(53787.5, -2525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(54207.5, -2725) + ctx.arc(54187.5, -2725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(54607.5, -2925) + ctx.arc(54587.5, -2925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(55007.5, -3125) + ctx.arc(54987.5, -3125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(57520, -1925) + ctx.arc(57500, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(57520, -1845) + ctx.arc(57500, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(58525, -1725) + ctx.arc(58505, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(57895 + 300, -1925) + ctx.arc(57875 + 300, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(57895 + 300, -1845) + ctx.arc(57875 + 300, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(58525, -1625) + ctx.arc(58505, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(58690 + 375 + 125, -1625) + ctx.arc(58670 + 375 + 125, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(59190, -1725) + ctx.arc(59170, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(70895, -200) + ctx.arc(70875, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(63720, -200) + ctx.arc(63700, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(64120, -200) + ctx.arc(64100, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(64595, -200) + ctx.arc(64575, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(64995, -200) + ctx.arc(64975, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(64945, -300) + ctx.arc(64925, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(65345, -300) + ctx.arc(65325, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(71295, -300) + ctx.arc(71275, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(71695, -400) + ctx.arc(71675, -400, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(72095, -500) + ctx.arc(72075, -500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(72445, -425) + ctx.arc(72425, -425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(72695, -300) + ctx.arc(72675, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(72945, -175) + ctx.arc(72925, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(75245, -200) + ctx.arc(75225, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(76620, -125) + ctx.arc(76600, -125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(76920, -200) + ctx.arc(76900, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(77195, -275) + ctx.arc(77175, -275, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(77495, -350) + ctx.arc(77475, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(85595, -375) + ctx.arc(85575, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(86020, -375) + ctx.arc(86000, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(86295, -375) + ctx.arc(86275, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(86970, -525) + ctx.arc(86950, -525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(89720, -175) + ctx.arc(89700, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(89720, -1125) + ctx.arc(89700, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(90620, -1125) + ctx.arc(90600, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(90620, -175) + ctx.arc(90600, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(92020, -175) + ctx.arc(92000, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(92920, -175) + ctx.arc(92900, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(92920, -1125) + ctx.arc(92900, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(94520, -1575) + ctx.arc(94500, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(94720, -1575) + ctx.arc(94700, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(94920, -1575) + ctx.arc(94900, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(96145, -1575) + ctx.arc(96125, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(96570, -1500) + ctx.arc(96550, -1500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.moveTo(97020, -1575) + ctx.arc(97000, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) + ctx.fill() + ctx.stroke() + slimePit1.query() + slimePit2.query() + slimePit3.query() + slimePit4.query() + slimePit5.query() + slimePit5.query() + slimePit6.query() + slimePit7.query() + slimePit8.query() + slimePit9.query() + slimePit10.query() + slimePit11.query() + slimePit12.query() + slimePit13.query() + slimePit14.query() + drip1.draw() + drip2.draw() + drip3.draw() + drip4.draw() + drip5.draw() + + ctx.fillStyle = "rgba(0,255,255,0.9)" + ctx.fillRect(25325, -1375, 75, 400) + ctx.fillRect(46425, -1350, 100, 500) + ctx.fillRect(87925, -725, 75, 450) + + /* + if (player.position.x < 46470) { + document.body.style.backgroundColor = "#DD00DD"; + } + */ + if (player.position.x > 25360 && player.position.x < 46470) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, //+ 0.2, + y: player.velocity.y, + }); + if (input.up) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y, //- 1, + }); + } + document.body.style.backgroundColor = "#fb3310" + } else if (player.position.x > 46470 && player.position.x < 61675) { + document.body.style.backgroundColor = "#7704FF" + } else if (player.position.x > 9700 && player.position.x < 46470) { + document.body.style.backgroundColor = "#7704FF" + } else if (player.position.x > 61675 && player.position.x < 87950) { + document.body.style.backgroundColor = "#DD1111" + } else if (player.position.x > 87950) { + document.body.style.backgroundColor = "#7704FF" + } + + if (m.pos.y > -200 && 20350 < m.pos.x && m.pos.x < 23635) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 20250, + y: -1000 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -150 && m.pos.x > 47770 && m.pos.x < 50130) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 47640, + y: -900 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -150 && 50975 < m.pos.x && m.pos.x < 54925) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 50965, + y: -1100 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -150 && 55025 < m.pos.x && m.pos.x < 57675) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 55148, + y: -3072 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -150 && 57875 < m.pos.x && m.pos.x < 58700) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 57800, + y: -2200 + + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -150 && 58875 < m.pos.x && m.pos.x < 61650) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 58850, + y: -2025 + + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode) + m.energy -= 0.1 * simulation.difficultyMode + } + if (m.pos.y > -1677 && 104650 < m.pos.x && m.pos.x < 105000 && barThere == true) { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 132577, + y: -300 + + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + document.body.style.transitionDuration = "0ms"; + document.body.style.backgroundColor = "#696969"; + simulation.makeTextLog(`
You have earned: ` + Math.min(3, totalCoin) + ` tech
`) + if (barThere == true) { //only runs once + document.body.removeChild(bar); + for (let i = 0, len = Math.min(3, totalCoin); i < len; i++) powerUps.directSpawn(player.position.x, player.position.y, "tech"); + barThere = false; + + + } + } + ctx.fillStyle = "#FFFFFF53" + ctx.fillRect(57645, -2055, 385, 85) + ctx.fillRect(58645, -1880, 385, 85) + //chains + ctx.strokeStyle = "#FF0000" + ctx.strokeRect(66975, -725, 25, 50) + ctx.strokeRect(67050, -725, 25, 50) + ctx.strokeRect(66975 + 1150, -725, 25, 50) + ctx.strokeRect(67050 + 1250, -725, 25, 50) + ctx.strokeRect(69162, -725, 25, 50) + ctx.strokeRect(69862, -725, 25, 50) + ctx.strokeRect(74412.5, -412.5, 25, 50) + ctx.strokeRect(74612.5, -412.5, 25, 50) + ctx.strokeRect(77962.5, -900, 25, 50) + ctx.strokeRect(78212.5, -775, 25, 50) + ctx.strokeRect(78462.5, -650, 25, 50) + ctx.strokeRect(81587.5, -725, 25, 50) + ctx.strokeRect(81687.5, -725, 25, 50) + ctx.strokeRect(81787.5, -725, 25, 50) + ctx.strokeRect(83037.5, -215, 25, 50) + ctx.strokeRect(83362.5, -215, 25, 50) + ctx.strokeRect(83687.5, -215, 25, 50) + ctx.strokeRect(84187.5, -315, 25, 50) + ctx.strokeStyle = "#FF000088" + ctx.strokeRect(66975, -850, 25, 50) + ctx.strokeRect(67050, -850, 25, 50) + ctx.strokeRect(66975 + 1150, -850, 25, 50) + ctx.strokeRect(67050 + 1250, -850, 25, 50) + ctx.strokeRect(69162, -850, 25, 50) + ctx.strokeRect(69862, -850, 25, 50) + ctx.strokeRect(74412.5, -525, 25, 50) + ctx.strokeRect(74612.5, -525, 25, 50) + ctx.strokeRect(77962.5, -985, 25, 50) + ctx.strokeRect(78212.5, -860, 25, 50) + ctx.strokeRect(78462.5, -735, 25, 50) + ctx.strokeRect(81587.5, -850, 25, 50) + ctx.strokeRect(81687.5, -850, 25, 50) + ctx.strokeRect(81787.5, -850, 25, 50) + ctx.strokeRect(83037.5, -315, 25, 50) + ctx.strokeRect(83362.5, -315, 25, 50) + ctx.strokeRect(83687.5, -315, 25, 50) + ctx.strokeRect(84187.5, -415, 25, 50) + ctx.strokeStyle = "#FF000044" + ctx.strokeRect(66975, -975, 25, 50) + ctx.strokeRect(67050, -975, 25, 50) + ctx.strokeRect(66975 + 1150, -975, 25, 50) + ctx.strokeRect(67050 + 1250, -975, 25, 50) + ctx.strokeRect(69162, -975, 25, 50) + ctx.strokeRect(69862, -975, 25, 50) + ctx.strokeRect(74412.5, -633, 25, 50) + ctx.strokeRect(74612.5, -633, 25, 50) + ctx.strokeRect(77962.5, -1075, 25, 50) + ctx.strokeRect(78212.5, -950, 25, 50) + ctx.strokeRect(78462.5, -825, 25, 50) + ctx.strokeRect(81587.5, -975, 25, 50) + ctx.strokeRect(81687.5, -975, 25, 50) + ctx.strokeRect(81787.5, -975, 25, 50) + ctx.strokeRect(83037.5, -415, 25, 50) + ctx.strokeRect(83362.5, -415, 25, 50) + ctx.strokeRect(83687.5, -415, 25, 50) + ctx.strokeRect(84187.5, -515, 25, 50) + ctx.strokeStyle = "#FF000011" + ctx.strokeRect(66975, -1100, 25, 50) + ctx.strokeRect(67050, -1100, 25, 50) + ctx.strokeRect(66975 + 1150, -1100, 25, 50) + ctx.strokeRect(67050 + 1250, -1100, 25, 50) + ctx.strokeRect(69162, -1100, 25, 50) + ctx.strokeRect(69862, -1100, 25, 50) + ctx.strokeRect(74412.5, -741, 25, 50) + ctx.strokeRect(74612.5, -741, 25, 50) + ctx.strokeRect(77962.5, -1165, 25, 50) + ctx.strokeRect(78212.5, -1040, 25, 50) + ctx.strokeRect(78462.5, -915, 25, 50) + ctx.strokeRect(81587.5, -1100, 25, 50) + ctx.strokeRect(81687.5, -1100, 25, 50) + ctx.strokeRect(81787.5, -1100, 25, 50) + ctx.strokeRect(83037.5, -515, 25, 50) + ctx.strokeRect(83362.5, -515, 25, 50) + ctx.strokeRect(83687.5, -515, 25, 50) + ctx.strokeRect(84187.5, -615, 25, 50) + ctx.stroke() + ctx.beginPath() + ctx.strokeStyle = "#FF0000" + ctx.moveTo(66987.5, -680) + ctx.lineTo(66987.5, -625) + ctx.moveTo(67062.5, -680) + ctx.lineTo(67062.5, -625) + ctx.moveTo(66987.5 + 1150, -680) + ctx.lineTo(66987.5 + 1150, -625) + ctx.moveTo(67062.5 + 1250, -680) + ctx.lineTo(67062.5 + 1250, -625) + ctx.moveTo(69175, -680) + ctx.lineTo(69175, -625) + ctx.moveTo(69875, -680) + ctx.lineTo(69875, -625) + ctx.moveTo(74425, -290) + ctx.lineTo(74425, -370) + ctx.moveTo(74625, -290) + ctx.lineTo(74625, -370) + ctx.moveTo(77975, -790) + ctx.lineTo(77975, -855) + ctx.moveTo(78225, -665) + ctx.lineTo(78225, -730) + ctx.moveTo(78475, -540) + ctx.lineTo(78475, -605) + ctx.moveTo(81600, -680) + ctx.lineTo(81600, -625) + ctx.moveTo(81700, -680) + ctx.lineTo(81700, -625) + ctx.moveTo(81800, -680) + ctx.lineTo(81800, -625) + ctx.moveTo(83050, -100) + ctx.lineTo(83050, -170) + ctx.moveTo(83375, -100) + ctx.lineTo(83375, -170) + ctx.moveTo(83700, -100) + ctx.lineTo(83700, -170) + ctx.moveTo(84200, -200) + ctx.lineTo(84200, -270) + ctx.stroke() + ctx.strokeStyle = "#FF000099" + ctx.moveTo(66987.5, -810) + ctx.lineTo(66987.5, -715) + ctx.moveTo(67062.5, -810) + ctx.lineTo(67062.5, -715) + ctx.moveTo(66987.5 + 1150, -810) + ctx.lineTo(66987.5 + 1150, -715) + ctx.moveTo(67062.5 + 1250, -810) + ctx.lineTo(67062.5 + 1250, -715) + ctx.moveTo(69175, -810) + ctx.lineTo(69175, -715) + ctx.moveTo(69875, -810) + ctx.lineTo(69875, -715) + ctx.moveTo(74425, -405) + ctx.lineTo(74425, -480) + ctx.moveTo(74625, -405) + ctx.lineTo(74625, -480) + ctx.moveTo(77975, -890) + ctx.lineTo(77975, -940) + ctx.moveTo(78225, -765) + ctx.lineTo(78225, -815) + ctx.moveTo(78475, -640) + ctx.lineTo(78475, -690) + ctx.moveTo(81600, -810) + ctx.lineTo(81600, -715) + ctx.moveTo(81700, -810) + ctx.lineTo(81700, -715) + ctx.moveTo(81800, -810) + ctx.lineTo(81800, -715) + ctx.moveTo(83050, -210) + ctx.lineTo(83050, -270) + ctx.moveTo(83375, -210) + ctx.lineTo(83375, -270) + ctx.moveTo(83700, -210) + ctx.lineTo(83700, -270) + ctx.moveTo(84200, -310) + ctx.lineTo(84200, -370) + ctx.stroke() + ctx.strokeStyle = "#FF000044" + ctx.moveTo(66987.5, -930) + ctx.lineTo(66987.5, -845) + ctx.moveTo(67062.5, -930) + ctx.lineTo(67062.5, -845) + ctx.moveTo(66987.5 + 1150, -930) + ctx.lineTo(66987.5 + 1150, -845) + ctx.moveTo(67062.5 + 1250, -930) + ctx.lineTo(67062.5 + 1250, -845) + ctx.moveTo(69175, -930) + ctx.lineTo(69175, -845) + ctx.moveTo(69875, -930) + ctx.lineTo(69875, -845) + ctx.moveTo(74425, -515) + ctx.lineTo(74425, -590) + ctx.moveTo(74625, -515) + ctx.lineTo(74625, -590) + ctx.moveTo(77975, -975) + ctx.lineTo(77975, -1040) + ctx.moveTo(78225, -850) + ctx.lineTo(78225, -915) + ctx.moveTo(78475, -725) + ctx.lineTo(78475, -790) + ctx.moveTo(81600, -930) + ctx.lineTo(81600, -845) + ctx.moveTo(81700, -930) + ctx.lineTo(81700, -845) + ctx.moveTo(81800, -930) + ctx.lineTo(81800, -845) + ctx.moveTo(83050, -305) + ctx.lineTo(83050, -370) + ctx.moveTo(83375, -305) + ctx.lineTo(83375, -370) + ctx.moveTo(83700, -305) + ctx.lineTo(83700, -370) + ctx.moveTo(84200, -405) + ctx.lineTo(84200, -470) + ctx.stroke() + ctx.strokeStyle = "#FF000022" + ctx.moveTo(66987.5, -1060) + ctx.lineTo(66987.5, -965) + ctx.moveTo(67062.5, -1060) + ctx.lineTo(67062.5, -965) + ctx.moveTo(66987.5 + 1150, -1060) + ctx.lineTo(66987.5 + 1150, -965) + ctx.moveTo(67062.5 + 1250, -1060) + ctx.lineTo(67062.5 + 1250, -965) + ctx.moveTo(69175, -1060) + ctx.lineTo(69175, -965) + ctx.moveTo(69875, -1060) + ctx.lineTo(69875, -965) + ctx.moveTo(74425, -620) + ctx.lineTo(74425, -712.5) + ctx.moveTo(74625, -620) + ctx.lineTo(74625, -712.5) + ctx.moveTo(77975, -1075) + ctx.lineTo(77975, -1120) + ctx.moveTo(78225, -950) + ctx.lineTo(78225, -995) + ctx.moveTo(78475, -825) + ctx.lineTo(78475, -870) + ctx.moveTo(81600, -1060) + ctx.lineTo(81600, -965) + ctx.moveTo(81700, -1060) + ctx.lineTo(81700, -965) + ctx.moveTo(81800, -1060) + ctx.lineTo(81800, -965) + ctx.moveTo(83050, -405) + ctx.lineTo(83050, -470) + ctx.moveTo(83375, -405) + ctx.lineTo(83375, -470) + ctx.moveTo(83700, -405) + ctx.lineTo(83700, -470) + ctx.moveTo(84200, -505) + ctx.lineTo(84200, -570) + ctx.stroke() + let star = 95525; + for (let i = 0; i < 3; i++) { + drawStar(star, -1540, 5, 40, 15); + star += 200; + } + drawStar(97375, -1540, 5, 25, 10) + drawStar(97675, -1540, 5, 25, 10) + drawStar(97975, -1540, 5, 25, 10) + drawStar(98275, -1540, 5, 25, 10) + drawStar(98575, -1540, 5, 25, 10) + + }; + var gradient = ctx.createLinearGradient(0, 0, 175, 0); + gradient.addColorStop(0, "#7704FF00"); + gradient.addColorStop(1, "#00FFFF"); + level.customTopLayer = () => { + if (player.position.x > 25360 && player.position.x < 46470 && player.position.y > -2348 || player.position.x > 87995 && player.position.x < 103950 && player.position.y > -1350) { + player.force.x += 0.0045 + m.airControl = () => { + if (input.up) { + player.force.y -= 0.02 + } + } + m.draw = () => { + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(player.position.x, player.position.y); + ctx.rotate(player.angle); + if (input.up) { //forward thrust drawing + ctx.rotate(`${Math.max(-0.5, Math.min(m.angle, 0.5))}`) + } else { + ctx.rotate(`${Math.max(0.5, Math.min(m.angle, -0.5))}`) + } + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath() + ctx.moveTo(30, 0) + ctx.lineTo(60, 10) + ctx.lineTo(60, 30) + ctx.lineTo(20, 40) + ctx.lineTo(0, 40) + ctx.lineTo(-50, 60) + ctx.lineTo(-50, 0) + ctx.lineTo(-40, -40) + ctx.lineTo(-40, -40) + ctx.lineTo(-30, 10) + ctx.lineTo(30, 10) + ctx.lineTo(30, 0) + ctx.fill(); + ctx.moveTo(-50, 30) + ctx.lineTo(-60, 30) + ctx.lineTo(-60, 0) + ctx.lineTo(-50, 0) + ctx.fill() + ctx.stroke() + ctx.restore(); + } + } else { + m.resetSkin() + m.airControl = () => { + if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + 5 > m.cycle) m.jump() + if (m.buttonCD_jump + 60 > m.cycle && !(input.up) && m.Vy < 0) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y * 0.94 + }); + } + if (input.left) { + if (player.velocity.x > -m.airSpeedLimit / player.mass / player.mass) player.force.x -= m.FxAir; // move player left / a + } else if (input.right) { + if (player.velocity.x < m.airSpeedLimit / player.mass / player.mass) player.force.x += m.FxAir; //move player right / d + } + } + } + ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.1, Math.min((-1400 - m.pos.y) / -100, 0.99))})`; + ctx.fillRect(91900, -1675, 12050, 375) + ctx.save() + ctx.translate(104700, -1675); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 175, 1675) + ctx.restore() + }; + level.setPosToSpawn(0, -150); //spawn + level.defaultZoom = 1800 //default I think + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#0150FF"; + document.body.style.transitionDuration = "1500ms"; //smoother transitions, so that people don't complain + level.exit.x = 133150; + level.exit.y = -260; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); + //ground? I forgot + spawn.mapRect(-5000, 0, 110000, 1000); + //*mountains to prevent player from running away* + spawn.mapVertex(-5775, -1330, "0 0 1000 7000 -1000 7000"); + spawn.mapVertex(-7000, -330, "0 0 1000 4000 -1000 4000"); + spawn.mapVertex(-10000, -330, "0 0 1000 4000 -1000 4000"); + spawn.mapVertex(-9000, -330, "0 0 1000 4000 -1000 4000"); + spawn.mapVertex(-7500, -330, "0 0 1000 4000 -1000 4000"); + spawn.mapRect(-10000, 0, 3500, 1000); + //spawn.mapRect(-10000, -10000, 1000, 11000); + //hunter(-600, -150) + //stage one --> 0% + //spikes + spike(3000, -30); + spike(5000, -30); + spike(4925, 0); + spike(7275, -30); + spike(7375, -30); + spike(9806, -30); + spike(9900, -30); + spike(12000, -130); + spike(13050, -255); + spike(17300, -1105); + spike(17400, -1105); + spike(17200, -1105); + spike(17500, -1105); + spike(18200, -1105); + spike(18100, -1105); + spike(18300, -1105); + spike(18400, -1105); + spike(19875, -1108); + spike(19975, -930); + spike(21000, -930); + spike(21725, -780); + spike(23600, -1230); + spawn.mapVertex(62000, -300, "0 1000 1000 1000 0 0"); + //upside down spikes + let u = Math.PI * 1.5; + spike(18825, -1170, u); + spike(18925, -1170, u); + spike(19025, -1170, u); + spike(19125, -1170, u); + spike(19225, -1170, u); + spike(19325, -1170, u); + spike(49962.5, -2370, u); + spike(50062.5, -2370, u); + spike(50162.5, -2370, u); + spike(50262.5, -2370, u); + spike(50362.5, -2370, u); + //bottom of the flying part + spike(32375, -280); + spike(32475, -280); + spike(32575, -280); + spike(32675, -280); + spike(32775, -280); + spike(32875, -280); + spike(32975, -280); + spike(33075, -280); + spike(33175, -280); + spike(33275, -280); + spike(33375, -280); + spike(33475, -280); + spike(33575, -280); + spike(33675, -280); + spike(35575, -280); + spike(35675, -280); + spike(35775, -280); + spike(35875, -280); + spike(39775, -280); + spike(39875, -280); + spike(39975, -280); + spike(40075, -280); + spike(40175, -280); + spike(40275, -280); + spike(40475, -280); + spike(40575, -280); + spike(40675, -280); + spike(40775, -280); + spike(40875, -280); + spike(40975, -280); + //top of the flying part + spike(32375, -2193, u); + spike(32475, -2193, u); + spike(32575, -2193, u); + spike(32675, -2193, u); + spike(32775, -2193, u); + spike(32875, -2193, u); + spike(32975, -2193, u); + spike(33075, -2193, u); + spike(33175, -2193, u); + spike(33275, -2193, u); + spike(33375, -2193, u); + spike(33475, -2193, u); + spike(33575, -2193, u); + spike(33675, -2193, u); + spike(37750, -2193, u); + spike(37850, -2193, u); + spike(37950, -2193, u); + spike(38050, -2193, u); + spike(43025, -2193, u); + spike(43125, -2193, u); + spike(43225, -2193, u); + spike(43325, -2193, u); + spike(43425, -2193, u); + spike(43525, -2193, u); + spike(43725, -2193, u); + spike(43825, -2193, u); + spike(43925, -2193, u); + spike(44025, -2193, u); + spike(44125, -2193, u); + spike(44225, -2193, u); + spike(44325, -2193, u); + spike(44425, -2193, u); + spike(44525, -2193, u); + spike(44625, -2193, u); + spike(44725, -2193, u); + spike(44825, -2193, u); + //about 55% of the map + spike(63375, -30); + spike(63475, -30); + spike(65775, -30); + spike(65875, -30); + spike(66975, -30); + spike(67075, -30); + spike(66975, -500, u); + spike(67075, -500, u); + spike(68125, -30); + spike(68225, -30); + spike(68325, -30); + spike(68125, -500, u); + spike(68225, -500, u); + spike(68325, -500, u); + spike(69175, -500, u); + spike(69175, -30); + spike(69875, -500, u); + spike(69875, -30); + spike(70675, -30); + spike(70775, -30); + spike(73725, -30); + spike(73825, -30); + spike(74425, -195, u); + spike(74525, -195, u); + spike(74625, -195, u); + spike(75125, -30); + spawn.mapVertex(78725, -466, "0 50 100 50 50 0"); //ocd still triggers from -467 + spike(79300, -180); + spike(80800, -30); + spike(80900, -30); + spike(81600, -30); + spike(81700, -30); + spike(81800, -30); + spike(81600, -500, u); + spike(81700, -500, u); + spike(81800, -500, u); + spike(93425, -1430); + spike(93525, -1430); + spike(85800, -305); + spike(86475, -305); + spike(87150, -455); + spike(94025, -1570, u); + spike(94125, -1570, u); + spike(94500, -1430); + spike(94700, -1430); + spike(94900, -1430); + spike(94600, -1400); + spike(94800, -1400); + spike(94212.5, -1675, u); + spike(94287.5, -1675, u); + //even more 90% + spike(95525, -1400) + spike(95525, -1675, u) + spike(95625, -1400) + spike(95625, -1675, u) + spike(95725, -1400) + spike(95725, -1675, u) + spike(95825, -1400) + spike(95825, -1675, u) + spike(95925, -1400) + spike(95925, -1675, u) + spike(96025, -1400) + spike(96225, -1400) + spike(96650, -1675, u) + spike(96900, -1400) + spike(97150, -1675, u) + spike(98900, -1400) + spike(96975, -155) + spike(97075, -155) + spike(97175, -155) + spike(97275, -155) + spike(97375, -155) + spike(97475, -155) + spike(96975, -1170, u) + spike(97075, -1170, u) + spike(97175, -1170, u) + spike(97275, -1170, u) + spike(97375, -1170, u) + spike(97475, -1170, u) + spike(98700, -1070, u) + spike(98800, -1070, u) + spike(98900, -1070, u) + spike(99000, -1070, u) + spike(99100, -1070, u) + spike(99200, -1070, u) + spike(98700, -230) + spike(98800, -230) + spike(98900, -230) + spike(99000, -230) + spike(99100, -230) + spike(99200, -230) + spike(98975, -1400) + spike(99375, -1675, u) + spike(99300, -1675, u) + spike(99575, -1675, u) + spike(100000, -1400) + //actual stuff + spawn.mapRect(7425, -175, 75, 175); + spawn.mapRect(7675, -250, 75, 250); + spawn.mapRect(7925, -325, 75, 325); + spawn.mapRect(10625, -100, 725, 100); + spawn.mapRect(11625, -100, 725, 100); + spawn.mapRect(12650, -225, 800, 225); + spawn.mapRect(13675, -300, 100, 50); + spawn.mapRect(13975, -400, 100, 50); + spawn.mapRect(14275, -500, 100, 50); + spawn.mapRect(14575, -600, 100, 50); + spawn.mapRect(14875, -700, 100, 50); + spawn.mapRect(15175, -800, 100, 50); + spawn.mapRect(15475, -900, 100, 50); + spawn.mapRect(15775, -1000, 100, 50); + spawn.mapRect(16075, -1100, 100, 50); + spawn.mapRect(16375, -1200, 100, 50); + spawn.mapRect(16625, -1075, 350, 100); + spawn.mapRect(16800, -1075, 1825, 1125); + spawn.mapRect(17250, -1225, 200, 50); + spawn.mapRect(18150, -1225, 200, 50); + spawn.mapRect(18550, -975, 1050, 1025); + spawn.mapRect(19525, -1075, 400, 1125); + spawn.mapRect(18775, -1275, 600, 75); + spawn.mapRect(19825, -900, 500, 950); + spawn.mapRect(20150, -900, 375, 125); + spawn.mapRect(20750, -900, 300, 50); + spawn.mapRect(21225, -750, 550, 50); + spawn.mapRect(21950, -625, 450, 50); + spawn.mapRect(22550, -700, 100, 50); + spawn.mapRect(22750, -800, 100, 50); + spawn.mapRect(22950, -900, 100, 50); + spawn.mapRect(23150, -1000, 100, 50); + spawn.mapRect(23350, -1100, 100, 50); + spawn.mapRect(23550, -1200, 100, 50); + spawn.mapRect(23525, -975, 400, 100); + spawn.mapRect(23650, -975, 1825, 1025); + spawn.mapRect(23750, -2500, 625, 1125); + spawn.mapRect(24000, -2500, 1200, 1300); + spawn.mapRect(24900, -2500, 575, 1125); + spawn.mapRect(25425, -2500, 20000, 275); + spawn.mapRect(25425, -250, 20000, 300); + spawn.mapRect(29450, -2300, 200, 675); + spawn.mapRect(32225, -2225, 100, 100); + spawn.mapRect(33725, -2225, 100, 100); + spawn.mapRect(32225, -350, 100, 100); + spawn.mapRect(33725, -350, 100, 100); + spawn.mapRect(37600, -2225, 100, 225); + spawn.mapRect(35425, -475, 100, 225); + spawn.mapRect(39625, -400, 100, 150); + spawn.mapRect(40325, -500, 100, 250); + spawn.mapRect(41025, -600, 100, 350); + spawn.mapRect(43575, -2225, 100, 300); + spawn.mapRect(44875, -2500, 1675, 1225); + spawn.mapRect(44875, -950, 1675, 1000); + spawn.mapRect(46425, -825, 1400, 875); + spawn.mapRect(48075, -1100, 175, 1150); + spawn.mapRect(48575, -1300, 175, 1375); + spawn.mapRect(49075, -1500, 175, 1550); + spawn.mapRect(49575, -1700, 175, 975); + spawn.mapRect(49575, -500, 175, 550); + spawn.mapRect(50075, -1900, 175, 700); + spawn.mapRect(50075, -1000, 1000, 1000); + spawn.mapRect(49912.5, -2525, 500, 125); + spawn.mapRect(51300, -1200, 175, 1225); + spawn.mapRect(51700, -1400, 175, 1425); + spawn.mapRect(52100, -1600, 175, 1625); + spawn.mapRect(52500, -1800, 175, 1825); + spawn.mapRect(52900, -2000, 175, 2025); + spawn.mapRect(53300, -2200, 175, 2225); + spawn.mapRect(53700, -2400, 175, 2425); + spawn.mapRect(54100, -2600, 175, 2625); + spawn.mapRect(54500, -2800, 175, 2825); + spawn.mapRect(54900, -3000, 175, 3025); + spawn.mapRect(55050, -3000, 175, 75); + spawn.mapRect(55475, -2875, 250, 75); + spawn.mapRect(55900, -2625, 250, 75); + spawn.mapRect(56500, -2400, 375, 75); + spawn.mapRect(57200, -2200, 250, 75); + spawn.mapRect(57650, -2050, 375, 75); + spawn.mapRect(57650, -1970, 375, 1975); + spawn.mapRect(58650, -1875, 375, 75); + spawn.mapRect(58650, -1795, 375, 1975); + spawn.mapRect(59525, -1750, 175, 75); + spawn.mapRect(60125, -1600, 175, 75); + spawn.mapRect(60750, -1425, 175, 75); + spawn.mapRect(61250, -1225, 175, 75); + spawn.mapRect(61550, -1025, 225, 1125); + spawn.mapRect(63525, -100, 100, 100); + spawn.mapRect(63800, -225, 200, 50); + spawn.mapRect(64175, -100, 100, 100); + spawn.mapRect(64275, -100, 100, 100); + spawn.mapRect(64375, -100, 100, 100); + spawn.mapRect(64675, -225, 200, 50); + spawn.mapRect(65025, -325, 200, 50); + spawn.mapRect(65425, -100, 300, 100); + spawn.mapRect(66925, -650, 200, 120); + spawn.mapRect(68075, -650, 300, 120); + spawn.mapRect(69125, -650, 100, 120); + spawn.mapRect(69825, -650, 100, 120); + spawn.mapRect(70825, -100, 100, 100); + spawn.mapRect(71225, -200, 100, 200); + spawn.mapRect(71625, -300, 100, 300); + spawn.mapRect(72025, -400, 100, 400); + spawn.mapRect(72125, -400, 100, 50); + spawn.mapRect(72375, -325, 100, 50); + spawn.mapRect(72625, -200, 100, 50); + spawn.mapRect(72875, -75, 100, 50); + spawn.mapRect(74375, -300, 300, 75); + spawn.mapRect(75175, -100, 100, 100); + spawn.mapRect(75900, -150, 400, 50); + spawn.mapRect(76550, -250, 100, 50); + spawn.mapRect(76850, -325, 100, 50); + spawn.mapRect(77125, -400, 100, 50); + spawn.mapRect(77425, -475, 300, 50); + spawn.mapRect(77925, -400, 100, 100); + spawn.mapRect(78175, -275, 100, 100); + spawn.mapRect(78425, -150, 100, 100); + spawn.mapRect(78675, -50, 100, 100); + spawn.mapRect(77925, -800, 100, 100); + spawn.mapRect(78175, -675, 100, 100); + spawn.mapRect(78425, -550, 100, 100); + spawn.mapRect(78675, -450, 100, 100); + spawn.mapRect(78450, -100, 50, 125); + spawn.mapRect(79025, -150, 100, 50); + spawn.mapRect(79250, -150, 100, 50); + spawn.mapRect(79475, -150, 100, 50); + spawn.mapRect(79800, -225, 300, 50); + spawn.mapRect(80250, -150, 100, 50); + spawn.mapRect(80450, -100, 300, 50); + spawn.mapRect(81550, -650, 300, 120); + spawn.mapRect(82800, -100, 100, 100); + spawn.mapRect(82900, -100, 200, 50); + spawn.mapRect(83325, -100, 100, 50); + spawn.mapRect(83650, -100, 200, 50); + spawn.mapRect(83850, -100, 100, 100); + spawn.mapRect(83950, -200, 100, 200); + spawn.mapRect(84050, -200, 200, 50); + spawn.mapRect(84500, -350, 100, 50); + spawn.mapRect(84725, -250, 100, 50); + spawn.mapRect(84950, -150, 300, 50); + spawn.mapRect(85525, -275, 100, 50); + spawn.mapRect(85750, -275, 100, 50); + spawn.mapRect(85950, -275, 375, 50); + spawn.mapRect(86425, -275, 100, 50); + spawn.mapRect(86625, -275, 100, 50); + spawn.mapRect(86900, -425, 300, 50); + spawn.mapRect(87375, -275, 300, 50); + spawn.mapRect(87900, -300, 125, 300); + spawn.mapRect(87900, -1850, 125, 1150); + spawn.mapRect(87900, -1850, 17000, 175); + spawn.mapRect(104875, -1850, 125, 2850); //Last part + spawn.mapRect(87900, -1850, 4000, 550); + spawn.mapRect(89650, -100, 100, 100); + spawn.mapRect(89750, -200, 100, 100); + spawn.mapRect(89850, -300, 600, 100); + spawn.mapRect(90450, -200, 100, 100); + spawn.mapRect(90550, -100, 100, 100); + spawn.mapRect(89650, -1300, 100, 100); + spawn.mapRect(89750, -1200, 100, 100); + spawn.mapRect(89850, -1100, 600, 100); + spawn.mapRect(90450, -1200, 100, 100); + spawn.mapRect(90550, -1300, 100, 100); + spawn.mapRect(91950, -100, 100, 100); + spawn.mapRect(92050, -200, 100, 100); + spawn.mapRect(92150, -300, 600, 100); + spawn.mapRect(92750, -200, 100, 100); + spawn.mapRect(92850, -100, 100, 100); + spawn.mapRect(92050, -1200, 100, 100); + spawn.mapRect(92150, -1100, 600, 100); + spawn.mapRect(92750, -1200, 100, 100); + spawn.mapRect(92850, -1300, 100, 100); + spawn.mapRect(92950, -1400, 11000, 100); + spawn.mapRect(93975, -1700, 200, 100); + spawn.mapRect(96075, -1500, 100, 100); + spawn.mapRect(96500, -1675, 100, 100); + spawn.mapRect(96950, -1490, 1900, 100); + spawn.mapRect(97200, -1685, 1650, 100); + spawn.mapRect(93900, -300, 100, 300); + spawn.mapRect(93900, -400, 300, 100); + spawn.mapRect(94100, -300, 100, 300); + spawn.mapRect(95025, -1300, 100, 300); + spawn.mapRect(95025, -1000, 300, 100); + spawn.mapRect(95225, -1300, 100, 300); + spawn.mapRect(96925, -1300, 600, 100); + spawn.mapRect(96925, -125, 600, 125); + spawn.mapRect(98650, -1300, 100, 200); + spawn.mapRect(98650, -1200, 600, 100); + spawn.mapRect(99150, -1300, 100, 200); + spawn.mapRect(98650, -200, 100, 200); + spawn.mapRect(99150, -200, 100, 200); + spawn.mapRect(98650, -200, 600, 100); + spawn.mapRect(100825, -1300, 100, 300); + spawn.mapRect(100825, -1100, 1325, 100); + spawn.mapRect(102050, -1300, 100, 300); + spawn.mapRect(100825, -300, 100, 300); + spawn.mapRect(100825, -300, 1350, 100); + spawn.mapRect(102075, -300, 100, 300); + spawn.mapRect(99425, -1675, 100, 125); + spawn.mapRect(100050, -1525, 100, 125); + + spawn.mapRect(132025, -225, 2325, 525); + spawn.mapRect(132025, -1450, 500, 1750); + spawn.mapRect(133875, -1475, 475, 1775); + spawn.mapRect(132025, -1925, 2325, 475); + + // simulation.enableConstructMode() //also remove when done + coin(50165.9, -1090) + coin(78725.4, -600) + coin(103830.0, -1473) + hunter(0, -1000) + }, + yingYang() { + simulation.makeTextLog(`yingYang by Richard0820`); + + let destroyed = false; + const lock = level.door(425, -1400, 50, 300, 300); + const core = function (x, y, radius = 100 + Math.ceil(Math.random() * 25)) { + radius = 9 + radius / 8; //extra small + mobs.spawn(x, y, 6, radius, "transparent"); + let me = mob[mob.length - 1]; + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + me.stroke = "transparent"; //used for drawSneaker + me.eventHorizon = radius * 40; + me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon + me.accelMag = 0.00012 * simulation.accelScale; + me.frictionAir = 0.025; + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + me.showHealthBar = false; + me.memory = Infinity; + me.isBoss = true; + Matter.Body.setDensity(me, 1); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + destroyed = true; + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + } + me.do = function () { + if (this.health < 1) { + this.health += 0.001; //regen + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: this.radius / 1.5, + color: `rgba(0, 255, 20, ${Math.random() * 0.1})`, + time: simulation.drawTime + }); + } + this.curl() + this.repelBullets() + this.seePlayerCheckByDistance() + this.checkStatus(); + const eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(simulation.cycle * 0.011)) + //draw darkness + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.25, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(250,250,250,0.9)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.55, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(250,250,250,0.5)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(250,250,250,0.1)"; + ctx.fill(); + //when player is inside event horizon + if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.005 + if (m.energy < 0.1) m.damage(0.0001 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x += 0.00125 * player.mass * Math.cos(angle) * (m.onGround ? 1.8 : 1); + player.force.y += 0.0001 * player.mass * Math.sin(angle); + //draw line to player + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(250,250,250,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(250,250,250,0.3)"; + ctx.fill(); + } + } + } + const sniper = function (x, y, radius = 35 + Math.ceil(Math.random() * 30)) { //same, just white so that we can seen + mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)") + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + me.isVerticesChange = true + // Matter.Body.rotate(me, Math.PI) + me.stroke = "transparent"; //used for drawSneaker + me.alpha = 1; //used in drawSneaker + me.showHealthBar = false; + me.frictionStatic = 0; + me.friction = 0; + me.canTouchPlayer = false; //used in drawSneaker + me.isBadTarget = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + + me.memory = 30 //140; + me.fireFreq = 0.005 + Math.random() * 0.002 + 0.0005 * simulation.difficulty; //larger = fire more often + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionAir = 0.05; + me.torque = 0.0001 * me.inertia; + me.fireDir = { + x: 0, + y: 0 + }; + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + // spawn.shield(me, x, y); + me.do = function () { + // this.seePlayerByLookingAt(); + this.seePlayerCheck(); + this.checkStatus(); + + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + // this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + //rotate towards fireAngle + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, this.fireDir) + const threshold = 0.03; + if (dot > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.000004 * this.inertia; + } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { + //fire + spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 5); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v + Math.random(), + y: this.velocity.y + this.fireDir.y * v + Math.random() + }); + this.noseLength = 0; + // recoil + this.force.x -= 0.005 * this.fireDir.x * this.mass; + this.force.y -= 0.005 * this.fireDir.y * this.mass; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + // else if (this.noseLength < -0.1) { + // this.noseLength += this.fireFreq / 4; + // setNoseShape(); + // } + + if (this.seePlayer.recall) { + if (this.alpha < 1) this.alpha += 0.01; + } else { + if (this.alpha > 0) this.alpha -= 0.03; + } + //draw + if (this.alpha > 0) { + if (this.alpha > 0.95) { + this.healthBar(); + if (!this.canTouchPlayer) { + this.canTouchPlayer = true; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player + } + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = `rgba(250,250,250,${this.alpha * this.alpha})`; + ctx.fill(); + } else if (this.canTouchPlayer) { + this.canTouchPlayer = false; + this.isBadTarget = true + this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + } + }; + } + const portal = level.portal({ + x: 650, + y: -1000 + }, Math.PI * 1.5, { + x: 525, + y: 2625 + }, -Math.PI) + document.body.style.transition = '0ms' + document.body.style.backgroundColor = "#061026" //"#061026"; + + var yy = new Image(); + yy.src = 'https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot6.png'; + color.map = "#FFFFFF11"; + color.bullet = "#FFFFFF"; + level.custom = () => { + level.enter.draw(); + level.exit.drawAndCheck(); + ctx.drawImage(yy, 0 - 500, 0 - 500, 1000, 1000) + portal[0].draw(); + portal[1].draw(); + portal[2].query(); + portal[3].query(); + if (destroyed == false) { + lock.isClosing = true; + } else { + lock.isClosing = false; + } + lock.openClose(); + + }; + level.customTopLayer = () => { + lock.draw() + /* + ctx.beginPath() + ctx.strokeStyle = "transparent"; + ctx.fillStyle = "#FFFFFF22" + ctx.arc(m.pos.x, m.pos.y, 500, 0, Math.PI * 2) + ctx.fill() + ctx.fillStyle = "#FFFFFF55" + ctx.arc(m.pos.x, m.pos.y, 1000, 0, Math.PI * 2) + ctx.fill(); + ctx.stroke(); */ + ctx.beginPath(); + ctx.moveTo(m.pos.x, m.pos.y) + const arc = Math.PI / 4 + ctx.arc(m.pos.x, m.pos.y, 100, m.angle + arc, m.angle - arc) + ctx.arc(m.pos.x, m.pos.y, 4000, m.angle - arc, m.angle + arc) + ctx.fillStyle = "rgba(255,255,255,0.7)"; + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + }; + level.setPosToSpawn(0, -50); + level.exit.x = -275; + level.exit.y = 2900; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + //map + spawn.mapRect(-125, -325, 225, 25); + spawn.mapRect(300, -150, 25, 325); + spawn.mapRect(-325, -125, 25, 300); + spawn.mapRect(-150, 300, 275, 25); + spawn.mapRect(125, -300, 25, 25); + spawn.mapRect(175, -275, 25, 25); + spawn.mapRect(225, -250, 25, 25); + spawn.mapRect(250, -200, 25, 25); + spawn.mapRect(-175, -300, 25, 25); + spawn.mapRect(-225, -275, 25, 25); + spawn.mapRect(-300, -200, 25, 25); + spawn.mapRect(150, 275, 25, 25); + spawn.mapRect(200, 225, 25, 25); + spawn.mapRect(250, 200, 25, 25); + spawn.mapRect(250, -225, 25, 25); + spawn.mapRect(275, -175, 25, 25); + spawn.mapRect(200, -275, 25, 25); + spawn.mapRect(150, -300, 25, 25); + spawn.mapRect(100, -325, 25, 25); + spawn.mapRect(-150, -300, 25, 25); + spawn.mapRect(-200, -300, 25, 25); + spawn.mapRect(-250, -250, 25, 25); + spawn.mapRect(-275, -225, 25, 25); + spawn.mapRect(-300, -175, 25, 50); + spawn.mapRect(275, 175, 25, 25); + spawn.mapRect(250, 200, 25, 25); + spawn.mapRect(225, 225, 25, 25); + spawn.mapRect(175, 250, 25, 25); + spawn.mapRect(125, 300, 25, 25); + spawn.mapRect(-300, 325, 200, 150); + spawn.mapRect(-400, 425, 225, 150); + spawn.mapRect(-4450, 2900, 1550, 150); + spawn.mapRect(-4500, 2525, 150, 525); + spawn.mapRect(-4800, 2150, 150, 400); + spawn.mapRect(-4400, 2025, 650, 150); + spawn.mapRect(-2425, 50, 2125, 150); + spawn.mapRect(-2425, 50, 150, 1300); + spawn.mapRect(-4600, 1175, 2325, 175); + spawn.mapRect(-5075, 1650, 450, 150); + spawn.mapRect(-4650, 1225, 75, 125); + spawn.mapRect(-4700, 1275, 75, 75); + spawn.mapRect(-425, 2925, 425, 125); + spawn.mapRect(-450, 2375, 450, 100); + spawn.mapRect(-3050, 550, 150, 450); + spawn.mapRect(-2925, 825, 100, 175); + spawn.mapRect(-2650, 375, 275, 125); + spawn.mapRect(-75, 2950, 300, 100); + spawn.mapRect(-625, -500, 125, 575); + spawn.mapRect(-1050, -325, 275, 100); + spawn.mapRect(-1075, -775, 100, 550); + spawn.mapRect(-1075, -775, 300, 100); + spawn.mapRect(-525, -1100, 1025, 625); + spawn.mapRect(450, -1000, 450, 1500); + spawn.mapRect(-300, 500, 1200, 75); + spawn.mapRect(-200, 425, 725, 100); + spawn.mapRect(525, 2450, 275, 600); + spawn.mapRect(-25, 2375, 825, 125); + spawn.mapRect(400, -1500, 500, 100); + spawn.mapRect(800, -1500, 100, 525); + spawn.mapRect(-400, 500, 1250, 1900); + spawn.mapRect(-300, 2910, 150, 25); + spawn.mapRect(-625, -1000, 125, 175); + spawn.spawnStairs(-400, 3000, 25, 2500, 2500, 250); + spawn.spawnStairs(500, 3000, 5, 250, 250, 250); + spawn.debris(-1550, -250, 100); + spawn.debris(-1100, 850, 100); + spawn.debris(-3700, 1025, 100); + spawn.debris(-3525, 2725, 100); + spawn.debris(-4750, 2050, 100); + spawn.debris(-4000, 1900, 100); + spawn.debris(225, -1225, 100); + + //mobs + spawn.sneaker(-1350, 1350); + spawn.sneaker(-2275, 2275); + sniper(-3050 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1475 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); + sniper(-2925 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1775 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); + sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1600 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); + sniper(-3100 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1975 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); + sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1750 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); + sniper(-3350, 425); + sniper(-3550, 600); + sniper(-3325, 775); + sniper(-5525, 1975); + sniper(-50, -1300); + for (let i = 0; i < 10 + simulation.difficulty; i++) { + spawn.ghoster(0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000), 0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000)) + } + core(-2000, -1000); + powerUps.spawnStartingPowerUps(0, 0) + powerUps.addResearchToLevel() + }, + staircase() { + simulation.makeTextLog(`staircase by ryanbear`); + + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + aaa.query(); + bbb.query(); + ccc.query(); + ddd.move(); + eee.query(); + fff.query(); + ggg.query(); + hhh.query(); + iii.query(); + jjj.query(); + kk.query(); + lll.query(); + mmm.query(); + nnn.query(); + ooo.query(); + ppp.query(); + }; + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 7300; + level.exit.y = -5154; + // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + + spawn.mapRect(-100, 0, 2100, 100); + spawn.mapRect(1984, 17, 100, 500); + spawn.mapRect(2013, 522, 1618, 100); + spawn.bodyRect(2090, 328, 100, 100); + + spawn.mapRect(3619, 14, 100, 500) + var aaa = level.hazard(1999, 10, 1618, 500); + var bbb = level.vanish(2320, -345, 234, 20); + var ccc = level.vanish(2862, -324, 234, 20); + var eee = level.vanish(3002, -1100, 234, 20); + var ddd = level.elevator(3399, -420, 200, 200, -950, 0.003, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + var fff = level.vanish(3359, -1300, 234, 20); + var ggg = level.boost(3020, -1600, 700); + var hhh = level.vanish(2700, -1940, 1147, 20); + var iii = level.boost(5038, -2000, 700); + var jjj = level.vanish(5092, -3498, 100, 100); + var kk = level.boost(5092, -3772, 700); + var lll = level.boost(5372, -2824, 700); + var mmm = level.vanish(5112, -3000, 100, 100); + var nnn = level.vanish(5367, -3000, 100, 100); + var ooo = level.boost(4810, -3161, 700); + var ppp = level.vanish(5383, -3485, 100, 100); + spawn.mapRect(5377, -4198, 1000, 100); + spawn.mapRect(6390, -4359, 200, 200); + spawn.mapRect(6605, -4563, 200, 200); + spawn.mapRect(6809, -4758, 200, 200); + spawn.mapRect(7014, -4962, 200, 200); + spawn.mapRect(7212, -5158, 200, 200); + + + + spawn.mapRect(4156, -1898, 1000, 100); + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + spawn.randomMob(590, -315); + spawn.randomMob(1343, -757); + spawn.randomMob(4037, -926); + spawn.randomMob(3621, -2376); + spawn.randomMob(5026, -2441); + spawn.randomMob(4253, -2863); + spawn.randomMob(4355, -2430); + spawn.randomMob(5316, -3265); + spawn.randomMob(5885, -4427); + spawn.randomMob(6666, -4979); + + + + spawn.laserBoss(6128, -4905); + + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + fortress() { + simulation.makeTextLog(`fortress by Desboot`); + const boost1 = level.boost(3600, -250, 1000) + const boost2 = level.boost(60, -604, 1000) + const boost3 = level.boost(2160, -1260, 1000) + powerUps.spawnStartingPowerUps(1033.3, -121.4) + level.custom = () => { + boost1.query(); + boost2.query(); + boost3.query(); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 3586; //3586.5, -1464.0 + level.exit.y = -1494; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.3)" + ctx.fillRect(-272, -580, 1700, 600) + ctx.fillRect(1427.5, -487, 1280, 600) + ctx.fillRect(2707.3, -580, 1200, 600) + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(2752, -1744, 1075, 1164) + ctx.fillRect(937, -1590, 339, 550) + ctx.fillRect(1158, -1040, 118, 550) + ctx.fillRect(3049, -1063, 339, 500) + ctx.fillRect(1439, -1281, 297, 800) + ctx.fillRect(2130, -1182, 167, 800) + ctx.fillRect(1892, -2073, 238, 1593) + ctx.fillRect(2297, -2073, 238, 1593) + ctx.fillStyle = "rgba(0,0,0,0.15)" + ctx.fillRect(483, -1277, 350, 700) + ctx.fillRect(833, -1000, 325, 450) + ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section + ctx.fillRect(2800, -1712, 730, 300) + }; + + spawn.debris(2700, -120, 180, 3); + spawn.debris(1350, -100, 280, 3); + spawn.debris(2300, -700, 380, 5); + spawn.debris(976, -775, 38, 5); + spawn.debris(840, -1424, 3080, 5); + spawn.debris(2300, -700, 3080, 5); + + spawn.mapRect(-272, 0, 4198, 123); + spawn.mapRect(-272, -581, 132, 581); + spawn.mapRect(-272, -581, 572, 326); + spawn.mapRect(1462, -229, 92, 229); + spawn.mapRect(1462, -229, 352, 57); + spawn.mapRect(2872, -220, 1056, 330); + spawn.mapRect(170, -260, 484, 80); + spawn.mapRect(476, -581, 1162, 75); + spawn.mapRect(951, -519, 1760, 132); + spawn.mapRect(1747, -492, 506, 66); + spawn.mapRect(2462, -581, 1074, 75); + spawn.mapRect(1136, -616, 510, 100); + spawn.mapRect(3815.6, -1461, 114, 1300); //far right wall + spawn.mapRect(480, -1456, 106, 651); //far left wall + spawn.mapRect(1426, -906, 106, 400); + spawn.mapRect(480, -1302, 374, 57); + spawn.mapRect(788, -1302, 75, 308); + spawn.mapRect(788, -1055, 370, 62); + spawn.mapRect(3049, -1170, 471, 106); + spawn.mapRect(3348, -1170, 188, 663); + spawn.mapRect(2751, -1461, 1088, 53); //roof under the exit + spawn.mapRect(2751, -1743, 92, 915); //wall on left or far right side + spawn.mapRect(937, -1667, 339, 84); //upper left platform + spawn.mapRect(1135, -3239, 119, 1450); + spawn.mapRect(1440, -1346, 295, 66); //center left platform + spawn.mapRect(2090, -1240, 242, 57); //center righ platform + spawn.mapRect(1892, -2214, 88, 220); //vertical part of left L + spawn.mapRect(1892, -2073, 238, 84); //flat part of left L + spawn.mapRect(2447, -2214, 88, 220); //vertical part of right L + spawn.mapRect(2297, -2073, 238, 84); //flat part of right L + spawn.mapRect(2751, -1743, 1078, 57); //exit roof //3587.2, -1470.0 + spawn.mapRect(3584, -1470, 103, 57); //wall below door3689 + spawn.mapRect(3428, -1735, 103, 173); //wall covering secret + + spawn.mapRect(-11000, -1000, 100, 10); //SAL + spawn.mapRect(-11000, -1000, 10, 100); //SAL + spawn.mapRect(-10900, -900, 10, 100); //SAL + spawn.mapRect(-11000, -900, 100, 10); //SAL + spawn.mapRect(-11000, -800, 100, 10); //SAL + spawn.mapRect(-10800, -1000, 10, 200); //SAL + spawn.mapRect(-10700, -1000, 10, 200); //SAL + spawn.mapRect(-10800, -1000, 100, 10); //SAL + spawn.mapRect(-10800, -900, 100, 10); //SAL + spawn.mapRect(-10600, -1000, 10, 200); //SAL + spawn.mapRect(-10600, -800, 100, 10); //SAL + + spawn.mapRect(-11000, -91000, 100, 10); //SAL + spawn.mapRect(-11000, -91000, 10, 100); //SAL + spawn.mapRect(-10900, -90900, 10, 100); //SAL + spawn.mapRect(-11000, -90900, 100, 10); //SAL + spawn.mapRect(-11000, -90800, 100, 10); //SAL + spawn.mapRect(-10800, -91000, 10, 200); //SAL + spawn.mapRect(-10700, -91000, 10, 200); //SAL + spawn.mapRect(-10800, -91000, 100, 10); //SAL + spawn.mapRect(-10800, -90900, 100, 10); //SAL + spawn.mapRect(-10600, -91000, 10, 200); //SAL + spawn.mapRect(-10600, -90800, 100, 10); //SAL + //mobs + spawn.randomMob(3104.9, -1284.9, 0.2); + spawn.randomMob(1784.7, -95.9, 0.2); + spawn.randomMob(3474.2, -406.7, 0.1); + spawn.randomMob(1603.2, -1493.5, 0.4); + spawn.randomMob(772.4, -1505.2, 0.2); + spawn.randomMob(824.6, -781.3, 0.2); + spawn.randomMob(818.8, -1468.9, 0.2); + spawn.randomMob(-124.7, -853, 0.2); + spawn.randomMob(3011.1, -1978.0, -0.2); + spawn.randomMob(2428.0, -236.8, 0.1); + spawn.randomSmallMob(694.3, -385.3); + spawn.randomSmallMob(1142.0, -808.4); + spawn.randomSmallMob(791.5, -803.7); + spawn.randomSmallMob(3175.8, -830.8); + spawn.randomSmallMob(1558.5, -1940.8); + spawn.randomSmallMob(2700, -475); + spawn.randomSmallMob(2700, -475); + spawn.pulsar(1762.9, -2768.3) + spawn.pulsar(3821.5, -2373.9) + let randomBoss = Math.floor(Math.random() * 5); + spawn[["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](2058.5, -711.4, 100, false); + + //spawn powerups + // powerUps.spawn(3167.6, -1300, "tech") + powerUps.spawn(3125.8, -1543.4, "tech") + powerUps.spawn(3125.8, -1543.4, "heal") + powerUps.spawn(3125.8, -1543.4, "ammo") + powerUps.spawn(3125.8, -1543.4, "ammo") + powerUps.spawn(3137.6, -1300, "ammo") + powerUps.spawn(1605.2, -1436.9, "heal") + powerUps.spawn(2912.9, -1940.9, "ammo") + powerUps.spawn(3167.6, -1300, "heal") + powerUps.spawn(1, 1, "ammo") + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + commandeer() { + simulation.makeTextLog(`commandeer by Desboot`); + + let waterFallWidth = 400 + let waterFallX = 15900 + let waterFallSmoothX = 0 + const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550) + 15900 && player.position.x < 16300 && player.position.y > -960.2 + //const slime = level.hazard(15900, -960, 400, 6000); + const slime2 = level.hazard(15147.2, -1782.4, 2000, 822); + const boost1 = level.boost(5950, -20, 700) + const boost2 = level.boost(21088, -1672, 700) + const boost3 = level.boost(19390, -31, 1700) + const boost4 = level.boost(19390, -31, 1700) + const boost5 = level.boost(17274, -1242, 1000) + const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI) + const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI) + const buttonDoor = level.button(21889, -10) + const door = level.door(19119, -2133, 110, 510, 480) + const buttonDoor2 = level.button(18711, -2210) + const door2 = level.door(17041, -412, 110, 510, 480) + const buttonDoor3 = level.button(20456.6, -1636.2) + const door3 = level.door(20238, -781.4, 88, 452, 412) + const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485 + + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 15316; + level.exit.y = -30; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#001738"; + color.map = "#444" //custom map color + + + + level.custom = () => { + //spawn.mapRect(22330, -2688.75, 400, 800); + //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5 + //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3 + + + ctx.fillStyle = "rgba(250,250,250,0.8)"//lights + ctx.beginPath() + ctx.moveTo(1124, -628) + ctx.lineTo(496, 0) + ctx.lineTo(1852, 0) + ctx.lineTo(1224, -628) + ctx.fill() + + ctx.beginPath() + ctx.moveTo(906, -1365) + ctx.lineTo(206, -690) + ctx.lineTo(1706, -690) + ctx.lineTo(1006, -1365) + ctx.fill() + + ctx.beginPath() + ctx.moveTo(3330, -1905)//-700 + ctx.lineTo(2815.6, -1405.8) + ctx.lineTo(2815.6, -1230) + ctx.lineTo(4022.9, -1283.9) + ctx.lineTo(4023.5, -1405.8) + ctx.lineTo(3430, -1905) + + ctx.fill() + + + + + ctx.fillStyle = "rgba(63,247,251,0.8)" + ctx.fillRect(22330, -2713.75, 550, 700)//15845.0, -1262.2 + ctx.fillRect(22330, -1743.5, 550, 700) + ctx.fillRect(22330, -754.25, 550, 700) + + ctx.fillRect(6237, -1830.7, 550, 700) + ctx.fillRect(6237, -840.4, 550, 700) + ctx.fillStyle = "rgba(200,200,200,0.8)" + ctx.fillRect(-192, -1973, 6484, 2071) + ctx.fillStyle = "rgba(240,240,240,0.8)" + + ctx.fillRect(15109.5, -2867.5, 7284, 2971) + ctx.fillStyle = "rgba(35,35,35,0.8)" + ctx.fillRect(15145.9, -960, 200, 25) + + + + + ctx.fillStyle = "rgba(255,255,255,0.9)" + + + + + + buttonDoor.query(); + buttonDoor.draw(); + buttonDoor2.query(); + buttonDoor2.draw(); + buttonDoor3.query(); + buttonDoor3.draw(); + + + //slime.query(); + slime2.query(); + + + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + if (buttonDoor2.isUp) { + door2.isClosing = true + } else { + door2.isClosing = false + } + if (buttonDoor3.isUp) { + door3.isClosing = true + } else { + door3.isClosing = false + } + door.openClose(); + door2.openClose(); + door3.openClose(); + portal[2].query() + portal[3].query() + portal2[2].query() + portal2[3].query() + + boost1.query(); + boost2.query(); + boost3.query(); + boost4.query(); + boost5.query(); + level.exit.drawAndCheck(); + level.enter.draw(); + ctx.fillStyle = "rgba(0,0,0,0.2)"//shadows + ctx.fillRect(2773, -682, 469, 500) + ctx.fillRect(3947, -851, 469, 700) + ctx.fillRect(4818, -1006, 400, 400) + ctx.fillRect(5313, -1309, 1000, 700) + + ctx.fillRect(16705, -2831, 40, 700) + ctx.fillRect(16140, -2812, 40, 400) + ctx.fillRect(15559, -2855, 40, 800) + ctx.fillRect(16530, -2855, 30, 200) + + + ctx.beginPath() + ctx.moveTo(18254.7, -2194.1) + ctx.lineTo(18554.6, -1952.7) + ctx.lineTo(18554.6, -1992.7) + ctx.lineTo(18294.7, -2194.1) + ctx.fill() + ctx.beginPath() + ctx.moveTo(18154.7, -1004.1) + ctx.lineTo(18554.6, -762.7) + ctx.lineTo(18554.6, -802.7) + ctx.lineTo(18214.7, -1004.1) + ctx.fill() + + ctx.beginPath() + ctx.moveTo(17585.2, -1123.8) + ctx.lineTo(17151.2, -781.7) + ctx.lineTo(17151.2, -741.7) + ctx.lineTo(17625.2, -1123.8) + ctx.fill() + + + ctx.fillRect(20540, -1103, 610, 300) + ctx.fillRect(20820, -243, 410, 300) + ctx.fillRect(5772, -609, 469, 700) + ctx.fillRect(5772, -609, 469, 700) + + ctx.fillStyle = "rgba(48,184,140,255)" + ctx.fillRect(waterFallX, -960, waterFallWidth, 6000) + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` + ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 8, 6000) + ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 5, 6000) + waterFallWidth = 0.995 * waterFallWidth + 4 * Math.random()//4.7 + waterFalSmoothlX = 0.96 * waterFallSmoothX + 20 * Math.random()//3.5 + waterFallX = waterFallSmoothX + 15900 + + + ctx.fillStyle = "rgba(0,0,0,0.4)"//wires + ctx.fillRect(20990, -2672, 20, 112) + ctx.fillRect(21090, -2506, 72, 20) + ctx.fillRect(21090, -1970, 72, 20) + + + + ctx.fillRect(16901.8, -2497.7, 25, 100) + ctx.fillRect(16901.8, -2397.7, 50, 25) + ctx.fillRect(16951.8, -2397.7, 25, 1640) + ctx.fillRect(16901.8, -782.7, 50, 25) + ctx.fillRect(16901.8, -757.7, 25, 100) + + + ctx.fillRect(20900, -2666, 500, 9) + ctx.fillRect(20900, -2651, 1315, 9) + ctx.fillRect(20900, -2636, 1300, 9) + ctx.fillRect(20900, -2621, 245, 9) + ctx.fillRect(20900, -2606, 230, 9) + ctx.fillRect(20900, -2591, 215, 9) + ctx.fillRect(20900, -2576, 200, 9) + + ctx.fillRect(21145, -2621, 9, 700) + ctx.fillRect(21130, -2606, 9, 1000) + ctx.fillRect(21115, -2591, 9, 1000) + ctx.fillRect(21100, -2576, 9, 850) + + ctx.fillRect(21400, -3066, 9, 409) + + + ctx.fillRect(20900, -1726, 209, 9) + ctx.fillRect(21145, -1921, 270, 9) + ctx.fillRect(21415, -1921, 9, 50) + + ctx.fillRect(22200, -2636, 9, 1300) + ctx.fillRect(22215, -2651, 9, 300) + + ctx.fillRect(22200, -1336, 300, 9) + ctx.fillRect(22215, -2351, 300, 9) + + + //943.9, -1698.0 + + ctx.fillRect(916.5, -1725, 80, 80)//+55 // 55/2=27.5 + ctx.fillRect(1204, -1706, 25, 40)//179 + ctx.fillRect(1354, -1706, 25, 40) + ctx.fillRect(1504, -1885, 25, 40) + ctx.fillRect(3504, -1885, 25, 40) + ctx.fillRect(5504, -1885, 25, 40) + + + ctx.fillRect(1019, -1718, 9, 20) + ctx.fillRect(1019, -1674, 9, 20) + + ctx.fillRect(996, -1718, 23, 9) + ctx.fillRect(996, -1663, 23, 9) + + + + + ctx.fillRect(1019, -1698, 425, 9) + ctx.fillRect(1444, -1868, 9, 179) + ctx.fillRect(1444, -1877, 4700, 9) + + ctx.fillRect(1019, -1683, 440, 9) + ctx.fillRect(1459, -1853, 9, 179) + ctx.fillRect(1459, -1862, 4670, 9) + + ctx.fillRect(6144, -1877, 9, 100) + ctx.fillRect(6144, -1777, 100, 9) + + ctx.fillRect(6129, -1862, 9, 1100) + ctx.fillRect(6129, -762, 150, 9) + + + + + + + + }; + + + level.customTopLayer = () => { + + + + door.draw(); + door2.draw(); + door3.draw(); + + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + elevator.move() + + + // if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y + 10 + // }); + // }else{ + // if (Math.abs(player.velocity.x) > 0.5){ + // if (m.onGround){ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)), + // y: player.velocity.y - 0.2 + + // }); + // }else{ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y - 0.2 + // }); + // } + // }else{ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y - 0.2 + // }); + // } + // } + + if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 2 + }); + } else { + if (Math.abs(player.velocity.x) > 0.5) { + if (m.onGround) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)), + y: player.velocity.y - 0.2 + + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y - 0.2 + }); + } + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y - 0.2 + }); + } + } + hazard2.opticalQuery(); + + + }; + + + + + + //1273.2, -1404.7 + + spawn.mapRect(1124, -653, 100, 25); + spawn.mapRect(906, -1390, 100, 25); + spawn.mapRect(3330, -1930, 100, 25); + + + //first ship base + spawn.mapRect(-300, 0, 6684, 100);//lower floor + spawn.mapRect(-300, -2071, 154, 2071);//left right wall + spawn.mapRect(2511, -300, 1309, 308);//left big block + spawn.mapRect(3820, -184, 1309, 184);//right big block + spawn.mapRect(-300, -739, 2549, 100);//upper right floor + spawn.mapRect(2056, -1309, 2764, 169);//upper center floor + spawn.mapRect(2056, -1309, 193, 650);//upper left floor wall + spawn.mapRect(4636, -1309, 193, 793);//upper right floor wall + spawn.mapRect(4821, -654, 955, 138);//upper right floor + spawn.mapRect(6237, -2071, 147, 2071);//far right wall + spawn.mapRect(-300, -2071, 6684, 154);//roof + + //first ship details + spawn.mapRect(245, -360, 70, 400);//start room wall + spawn.mapRect(500, -1929, 154, 462); + spawn.mapRect(185, -1517, 469, 77); + spawn.mapRect(2773, -682, 469, 77);//walls in 1st room + spawn.mapRect(3743, -566, 77, 469); + spawn.mapRect(3947, -851, 469, 77); + spawn.mapRect(5313, -1309, 1000, 70);//walls in second area + spawn.mapRect(4818, -1006, 400, 70); + spawn.mapRect(4768, -1626, 800, 70); + spawn.mapRect(4760, -1626, 70, 400); + spawn.mapRect(645.1, -1480.8, 700, 100);//room for shielding boss + spawn.mapVertex(515, -1447, "0 0 0 100 -400 0"); + spawn.mapRect(1245.1, -1980.8, 100, 500); + spawn.mapRect(2346.9, -1658.8, 469, 77); + spawn.mapRect(4023.6, -1723.7, 469, 77); + + //engines //y -2972 -> 0 + spawn.mapRect(6237, -1880.7, 400, 800); + spawn.mapRect(6237, -890.4, 400, 800); + + + //first ship blocks/debris + spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level + spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level + spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level + spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level + spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level + spawn.bodyRect(1540, -1110, 218, 125, 0.9); + + + + //first ship mobs + spawn.randomSmallMob(893.5, -120.8); + + // spawn.randomMob(2903.9, -754.5, 0.4); + // spawn.randomMob(5577.0, -217.0, 0.2); + // spawn.randomMob(765.8, -1029.7, 0.5); + // spawn.randomMob(2680.1, -1779.2, 0.6); + // spawn.randomMob(20079.4, -2219.7, 0.4); + // spawn.randomMob(3924.9, -1504.1, 0.5); + // spawn.randomMob(21284.2, -983.1, 0.3); + // spawn.randomMob(20381.0, -254.2, 0.5); + // spawn.randomMob(18375.6, -1574.4, 0.6); + // spawn.randomMob(19448.2, -1323.3, 0.3); + // spawn.randomMob(18397.7, -711.2, 0.3); + // spawn.randomMob(15547.2, -2249.6, 0.5); + // spawn.randomSmallMob(16114.6, -2524.2); + // spawn.randomSmallMob(15378.9, -2549.6); + + // spawn.randomSmallMob(3266.4, -1578.4); + // spawn.randomSmallMob(4386.2, -439.6); + // spawn.randomSmallMob(5667.0, -847.8); + // spawn.randomSmallMob(3158.5, -1581.8); + // spawn.randomSmallMob(3866.7, -1483.2); + // spawn.randomSmallMob(4652.3, -1729.4); + // spawn.randomSmallMob(1068.7, -106.1); + // spawn.randomSmallMob(3382.5, -1590.6);//3545.0, -413.0 + // spawn.randomSmallMob(5099.7, -1204.2); + // spawn.randomSmallMob(1456.4, -1014.8); + // spawn.randomSmallMob(20432.4, -1374.3); + // spawn.randomSmallMob(20381.0, -254.2); + // spawn.randomSmallMob(3505.1, -1531.1); + // spawn.randomSmallMob(20648.1, -136.8); + // spawn.randomSmallMob(17502.8, -1520.6); + // spawn.randomSmallMob(17438.7, -876.7); + + spawn.randomMob(18375.6, -1574.4, 0.2); + spawn.randomSmallMob(15378.9, -2549.6); + spawn.randomSmallMob(5820.2, -1545.2); + spawn.randomMob(765.8, -1029.7, 0.2); + spawn.randomMob(21284.2, -983.1, 0.3); + spawn.randomSmallMob(3382.5, -1590.6); + spawn.randomSmallMob(3545.0, -413.0); + spawn.randomMob(20381.0, -254.2, 0.6); + spawn.randomSmallMob(20432.4, -1374.3); + spawn.randomSmallMob(5667.0, -847.8); + spawn.randomMob(2903.9, -754.5, 0.2); + spawn.randomSmallMob(3266.4, -1578.4); + spawn.randomSmallMob(20648.1, -136.8); + spawn.randomSmallMob(16114.6, -2524.2); + spawn.randomSmallMob(20381.0, -254.2); + spawn.randomMob(5577.0, -217.0, 0.3); + spawn.randomSmallMob(1456.4, -1014.8); + spawn.randomSmallMob(1068.7, -106.1); + spawn.randomSmallMob(5099.7, -1204.2); + spawn.randomSmallMob(17502.8, -1520.6); + spawn.randomMob(15547.2, -2249.6, 0.2); + spawn.randomMob(19448.2, -1323.3, 0.7); + spawn.randomSmallMob(3158.5, -1581.8); + spawn.randomSmallMob(17438.7, -876.7); + spawn.randomMob(20079.4, -2219.7, 0.2); + spawn.randomMob(2680.1, -1779.2, 0.6); + spawn.randomMob(3924.9, -1504.1, 0.3); + spawn.randomSmallMob(4652.3, -1729.4); + spawn.randomMob(18397.7, -711.2, 0.3); + spawn.randomSmallMob(4386.2, -439.6); + + spawn.randomSmallMob(3505.1, -1531.1); + spawn.randomSmallMob(3866.7, -1483.2); + + + + + //second ship mobs + spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level + spawn.debris(17827.2, -2357.1, 700, 5); //16 debris per level + spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level + spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level + spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level + spawn.debris(20534.5, -1282.1, 700, 5); //16 debris per level + + + + + + + + spawn.randomSmallMob(1300, -70); + spawn.shieldingBoss(943.9, -1698.0) + + + //second ship base + spawn.mapRect(15000, 0, 515, 185);//lower floor 1 + spawn.mapRect(17015, 0, 5500, 185);//lower floor 2 + spawn.mapRect(15000, -2972, 185, 2972);//left wall + spawn.mapRect(15000, -2972, 7515, 185);//roof + spawn.mapRect(22330, -2972, 185, 2972);//right wall + spawn.mapRect(17002, -2972, 169, 2564);//left middle wall + spawn.mapRect(19089, -2972, 169, 855);//right middle wall upper + spawn.mapRect(19089, -1625, 169, 1800);//right middle wall lower + spawn.mapRect(20760, -2972, 169, 1350);//medium wall left of portal + spawn.mapRect(19720, -1625, 1725, 162);//right room upper floor + spawn.mapRect(21440, -2325, 169, 863);//medium wall right of portal + spawn.mapRect(19720, -855, 2725, 162);//right room lower floor + + //engines //y -2972 -> 0 + spawn.mapRect(22330, -2763.75, 400, 800); + spawn.mapRect(22330, -1793.5, 400, 800); + spawn.mapRect(22330, -804.25, 400, 800); + + + + //second ship details + spawn.mapRect(19904, -1465, 85, 362);//upper L + spawn.mapRect(19542, -1191, 412, 88);//lower L + spawn.mapRect(18546, -2199, 600, 82);//2nd room enternce wall + spawn.mapRect(18546, -2499, 82, 2300); + spawn.mapRect(18108, -326, 500, 82);//walls/floors in middle room + spawn.mapRect(17750, -682, 300, 82); + spawn.mapRect(17156, -468, 500, 60); + spawn.mapRect(18022, -1082, 600, 82); + spawn.mapRect(17151, -1196, 500, 82); + spawn.mapRect(17453, -2060, 500, 82); + spawn.mapRect(18197, -2269, 400, 82); + spawn.mapRect(18108, -326, 500, 82); + spawn.mapRect(20542, -1191, 612, 88); + spawn.mapRect(20238, -1191, 88, 412); + spawn.mapRect(21520, -1468, 88, 412); + spawn.mapRect(20238, -330.2, 88, 412); + spawn.mapRect(20819, -328.3, 412, 88); + spawn.mapRect(21532, -708, 88, 412); + spawn.mapRect(15483.8, 12.5, 388, 30);//broken floor + spawn.mapRect(15487.6, 76.6, 488, 24); + spawn.mapRect(15506.5, 134.2, 288, 45); + spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30"); + spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20"); + //tank + spawn.mapRect(15310, -960, 600, 135); + spawn.mapRect(16290, -960, 800, 135); + //in tank + spawn.mapRect(16524.8, -2726.8, 40, 400); + spawn.mapRect(16524.8, -2130.9, 400, 40); + spawn.mapRect(16010.2, -2412.2, 300, 40); + spawn.mapRect(15379.2, -2055.1, 400, 40); + + spawn.mapVertex(17626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); + spawn.mapRect(17226.3, -3035, 400, 40); + + spawn.mapVertex(17626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); + spawn.mapRect(17226.3, 225, 400, 40); + + spawn.mapVertex(19626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); + spawn.mapRect(19226.3, -3035, 400, 40); + + spawn.mapVertex(19626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); + spawn.mapRect(19226.3, 225, 400, 40); + + spawn.mapVertex(21626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); + spawn.mapRect(21226.3, -3035, 400, 40); + + spawn.mapVertex(21626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); + spawn.mapRect(21226.3, 225, 400, 40); + + + + //add fuel tanks in the last room + + + spawn.mapRect(21531.9, -707.8, 488, 8); + + //22185.5, -114.8 + spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); + spawn.mapRect(22056.6, -70, 225, 212); + + spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300"); + spawn.mapRect(20571.9, -1701.0, 225, 212); + + spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); + spawn.mapRect(22056.6, -70, 225, 212); + //spawn.mapVertex(x,y, "coordinates") + //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction + + //second ship blocks/debris + spawn.bodyRect(21525, -113, 50, 50, 9);//first button block + spawn.bodyRect(18993, -2283, 50, 50, 9);//second button block + spawn.bodyRect(20303, -1736, 50, 50, 9);//third button block + + + + // spawn.randomLevelBoss(17902, -1689, ["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "blockBoss", "bladeBoss", "revolutionBoss", "spawnerBossCulture", "spiderBoss", "sneakBoss", "snakeSpitBoss"]) + spawn.randomLevelBoss(17902, -1689, ["launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); + + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + clock() { + simulation.makeTextLog(`clock by Cornbread 2100`); + + function drawBackgroundGear(x, y, r1, r2, rot, color, speed, numTeeth = 5, toothWidth = 75, linew = 2) { + var vertices = getGearVertices(x, y, r1, r2, numTeeth, simulation.cycle * speed + rot, toothWidth / 100); + + // draw gear + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + for (var i = 1; i < vertices.length; i++) { + ctx.lineTo(vertices[i].x, vertices[i].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 2; + ctx.fillStyle = color; + ctx.fill(); + ctx.strokeStyle = "#3a3f20"; + ctx.lineWidth = linew; + ctx.stroke(); + } + + function drawFallingBackgroundGear(x, y, r1, r2, rot, color, speed, fallSpeed, startCycle, numTeeth = 5, linew = 2) { + rot *= speed; + numTeeth *= 2; + + const gearInc = (2 * Math.PI) / numTeeth; + ctx.beginPath() + for (var i = 0; i <= numTeeth; i++) { + var gear_r = r2; + if (i % 2 == 1) gear_r = r1; + ctx.arc(x, y + (simulation.cycle - startCycle) * fallSpeed, gear_r, (i * gearInc) + rot, ((i + 1) * gearInc) + rot); + } + ctx.fillStyle = color; + ctx.fill(); + ctx.strokeStyle = "#3a3f20"; + ctx.lineWidth = linew; + ctx.stroke(); + } + + function getGearVertices(x, y, r1, r2, numTeeth, rot = 0, teethWidth = 0) { + if (teethWidth == 0) { + teethWidth = (2 * Math.PI) / (2 * numTeeth); + } + const gearInc = (2 * Math.PI) / numTeeth; + var vertices = []; + + for (var i = 0; i < numTeeth; i++) { + //inner vertices of gear teeth + var distance = i * gearInc + rot; + var vX = Math.sin(distance + teethWidth / 2) * r1; + var vY = Math.cos(distance + teethWidth / 2) * r1; + + var point1 = { x: vX, y: vY, point: 1 }; + + vX = Math.sin(distance - teethWidth / 2) * r1; + vY = Math.cos(distance - teethWidth / 2) * r1; + + var point4 = { x: vX, y: vY, point: 4 }; + + vX = Math.sin(distance) * r1; + vY = Math.cos(distance) * r1; + + if (vX == 0) { + vX = 0.0001 + } + + var slope = vY / vX; + + var angle = Math.atan2(vY, vX); + + //outer vertices of gear teeth + var point2 = { x: point1.x, y: point1.y, point: 2 }; + point2.x += Math.cos(angle) * (r2 - r1); + point2.y += Math.sin(angle) * (r2 - r1); + + var point3 = { x: point4.x, y: point4.y, point: 3 }; + point3.x += Math.cos(angle) * (r2 - r1); + point3.y += Math.sin(angle) * (r2 - r1); + + vertices.push(point4); + vertices.push(point3); + vertices.push(point2); + vertices.push(point1); + } + + for (var i = 0; i < vertices.length; i++) { + vertices[i].x += x; + vertices[i].y += y; + } + + return vertices; + } + + function getGearTeethVertices(x, y, r1, r2, numTeeth, toothIndex, teethWidth = 0) { + if (teethWidth == 0) { + teethWidth = (2 * Math.PI) / (2 * numTeeth); + } + + const gearInc = (2 * Math.PI) / numTeeth; + var vertices = []; + + for (var i = 0; i < numTeeth; i++) { + //inner vertices of gear teeth + var distance = i * gearInc; + var vX = Math.sin(distance + teethWidth / 2) * r1; + var vY = Math.cos(distance + teethWidth / 2) * r1; + + var point1 = { x: vX, y: vY, point: 1 }; + + vX = Math.sin(distance - teethWidth / 2) * r1; + vY = Math.cos(distance - teethWidth / 2) * r1; + + var point4 = { x: vX, y: vY, point: 4 }; + + vX = Math.sin(distance) * r1; + vY = Math.cos(distance) * r1; + + if (vX == 0) { + vX = 0.0001 + } + + var slope = vY / vX; + + var angle = Math.atan2(vY, vX); + + //outer vertices of gear teeth + var point2 = { x: point1.x, y: point1.y, point: 2 }; + point2.x += Math.cos(angle) * (r2 - r1); + point2.y += Math.sin(angle) * (r2 - r1); + + var point3 = { x: point4.x, y: point4.y, point: 3 }; + point3.x += Math.cos(angle) * (r2 - r1); + point3.y += Math.sin(angle) * (r2 - r1); + + if (i == toothIndex) { + vertices.push(point4); + vertices.push(point3); + vertices.push(point2); + vertices.push(point1); + } + } + + for (var i = 0; i < vertices.length; i++) { + vertices[i].x += x; + vertices[i].y += y; + } + + return vertices; + } + + function mapGear(x, y, r1, r2, rot, speed, numTeeth = 5, toothWidth = 50, additionalCircleRadius = 10) { + const part1 = body[body.length] = Bodies.polygon(x, y, 0, r1 + additionalCircleRadius, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: 0, + friction: 1, + frictionStatic: 1, + restitution: 0 + }); + + var parts = [part1]; + + for (var i = 0; i < numTeeth; i++) { + var toothVertices = getGearTeethVertices(0, 0, r2 - r1, toothWidth + r2 - r1, numTeeth, i, 70); // for some reason the teeth are sideways + + var center = { // the center of the inner line of the gear + x: toothVertices[3].x - toothVertices[0].x, + y: toothVertices[3].y - toothVertices[0].y + }; + + distanceToCenter = Math.sqrt((center.x ** 2) + (center.y ** 2)); + + var radiusScale = (r1 + ((r2 - r1) / 2)) / distanceToCenter; + + gearToothSlope = center.y / center.x; + + var newPart = body[body.length] = Bodies.fromVertices(x + center.x * radiusScale, y + center.y * radiusScale, toothVertices, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: 0.01, + friction: 1, + frictionStatic: 1, + restitution: 0 + }); + + parts.push(newPart); + } + + const who = Body.create({ + parts: parts + }); + + Composite.add(engine.world, who); + composite[composite.length] = who; + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + + const constraint = Constraint.create({ + pointA: { + x: x, + y: y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + + + Matter.Body.setDensity(who, 0.0001) + Composite.add(engine.world, constraint); + Matter.Body.setAngle(who, 0) + Matter.Body.setAngularVelocity(who, 0); + who.center = { x: x, y: y } + + who.rotate = function () { + var rotation = simulation.cycle * speed + rot; + Matter.Body.setAngle(who, rotation); + } + + who.gearSettings = { + x: x, + y: y, + r1: r1, + r2: r2, + rot: rot, + speed: speed, + numTeeth: numTeeth, + toothWidth: toothWidth + } + + return who; + } + + function clockHand(x, y, width, height, speed = 15 * Math.PI / 180, angle = 0, density = 0.001) { + var who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0 + }); + + const who = Body.create({ + parts: [who1], + handRotation: 0 + }); + + Composite.add(engine.world, who); + composite[composite.length] = who; + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + + who.position.y = y; + + const constraint = Constraint.create({ + pointA: { + x: who.position.x, + y: who.position.y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + + Matter.Body.setDensity(who, density) + Composite.add(engine.world, constraint); + who.center = { x: who.position.x, y: who.position.y } + + who.rotate = function () { + if (simulation.cycle % 60 == 0) { + who.handRotation += speed; + if (Math.abs(who.handRotation % (Math.PI * 2) - Math.PI) < 0.2) { + // spawn random mob at exit door + const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; + spawn[pick](300, 600); + } + if (Matter.Query.collides(player, [this]).length != 0) { + var playerAngle = Math.atan((m.pos.y - y) / (m.pos.x - x)); + if (m.pos.x - x < 0) playerAngle += Math.PI; + const playerDistance = Math.sqrt((m.pos.x - x) ** 2 + (m.pos.y - y) ** 2); + Matter.Body.setPosition(player, { + x: x + Math.cos(playerAngle + speed) * playerDistance, + y: y + Math.sin(playerAngle + speed) * playerDistance + }) + } + } + Matter.Body.setAngle(who, who.handRotation + angle); + } + + return who + } + + function pendulum(x, y, width, height, swingTime = 50, swingDistanceMultiplier = 0.5, bobSides = 0, bobRadius = 200, density = 100, angle = 0, frictionAir = 0, angularVelocity = 0) { + const who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: frictionAir, + friction: 1, + frictionStatic: 1, + restitution: 0 + }); + + const who2 = body[body.length] = Bodies.polygon(x, y + height, bobSides, bobRadius, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + isNotHoldable: true, + frictionAir: 0.01, + friction: 1, + frictionStatic: 1, + restitution: 0 + }); + + const who = Body.create({ + parts: [who1, who2], + }); + + Composite.add(engine.world, who); + composite[composite.length] = who; + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map + + who.position.y = y; + + const constraint = Constraint.create({ + pointA: { + x: x, + y: y + }, + bodyB: who, + stiffness: 1, + damping: 1 + }); + + + Matter.Body.setDensity(who, density) + Composite.add(engine.world, constraint); + Matter.Body.setAngle(who, angle) + Matter.Body.setAngularVelocity(who, angularVelocity); + who.center = { x: x, y: y + height / 2 } + + who.rotate = function () { + var rotation = Math.sin(simulation.cycle / swingTime) * swingDistanceMultiplier; + + if (Matter.Query.collides(player, [this]).length != 0) { + var playerAngle = Math.atan((player.position.y - y) / (player.position.x - x)) + rotation - Math.sin((simulation.cycle - 1) / swingTime) * swingDistanceMultiplier; + if (player.position.x - x < 0) playerAngle += Math.PI; + const playerDistance = Math.sqrt((player.position.x - x) ** 2 + (player.position.y - y) ** 2); + Matter.Body.setPosition(player, { + x: x + Math.cos(playerAngle) * playerDistance, + y: y + Math.sin(playerAngle) * playerDistance + }) + } + + Matter.Body.setAngle(who, rotation); + } + + return who; + } + + function gearMob(x, y, leaveBody = true, autoFindPlayer = false, radius = Math.floor(25 + 40 * Math.random()), teethRadius = 0) { + if (teethRadius == 0) { + teethRadius = radius + 15 + Math.floor(Math.random() * 20); + } + + mobs.spawn(x, y, 0, teethRadius, "transparent"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + + me.delay = 100 + 40 * simulation.CDScale; + me.accelMag = Math.PI / 10000; + me.memory = 120; + me.seeAtDistance2 = 2000000; // 140 + Matter.Body.setDensity(me, 0.001); + + me.leaveBody = leaveBody; + + const numTeeth = Math.round(5 + Math.random() * 3); + + me.gearRotation = 0; + me.gearSpeed = Math.round(-0.1 + Math.random() * 0.2); + me.gearAccelerating = true; + + me.do = function () { + if (autoFindPlayer) { + me.locatePlayer(); + } + + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + + if (me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed >= 0.1)) { + me.gearAccelerating = false; + } else if (!me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed <= -0.1)) { + me.gearAccelerating = true; + } + + if (me.gearAccelerating) { + me.gearSpeed += 0.001; + } else { + me.gearSpeed -= 0.001; + } + + me.gearRotation += me.gearSpeed; + + var newVertices = getGearVertices(me.position.x, me.position.y, radius, teethRadius, numTeeth, me.gearRotation); + // draw body + ctx.beginPath(); + ctx.moveTo(newVertices[0].x, newVertices[0].y); + for (let i = 1; i < newVertices.length; i++) { + ctx.lineTo(newVertices[i].x, newVertices[i].y); + } + ctx.lineTo(newVertices[0].x, newVertices[0].y); + ctx.fillStyle = "#7b3f00"; + ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + } + } + + function customDoor(x, y, width, height) { + x = x + width / 2 + y = y + height / 2 + const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + isClosing: false, + setPos(x, y) { + if (y - this.position.y <= 0 || ( // only move down if clear of stuff + Matter.Query.ray([player], Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0 && + Matter.Query.ray(body, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length <= 1 && + Matter.Query.ray(mob, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0) + ) { + const position = { + x: x, + y: y + } + Matter.Body.setPosition(this, position); + } + }, + draw() { + ctx.fillStyle = "#555" + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) { + ctx.lineTo(v[i].x, v[i].y); + } + ctx.lineTo(v[0].x, v[0].y); + ctx.fill(); + } + }); + Matter.Body.setStatic(doorBlock, true); //make static + Composite.add(engine.world, doorBlock); //add to world + doorBlock.classType = "body" + return doorBlock + } + + function horizontalDoor(x, y, width, height, distance, speed = 1) { + x = x + width / 2 + y = y + height / 2 + const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { + collisionFilter: { + category: cat.map, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + isClosing: false, + openClose() { + if (!m.isBodiesAsleep) { + if (this.isClosing) { + if (this.position.x > x) { //try to close + if ( //if clear of stuff + Matter.Query.collides(this, [player]).length === 0 && + Matter.Query.collides(this, body).length < 2 && + Matter.Query.collides(this, mob).length === 0 + ) { + const position = { + x: this.position.x - speed, + y: this.position.y + } + Matter.Body.setPosition(this, position) + } + } + } else { + if (this.position.x < x + distance) { //try to open + const position = { + x: this.position.x + speed, + y: this.position.y + } + Matter.Body.setPosition(this, position) + } + } + } + }, + isClosed() { + return this.position.x < x + 1 + }, + draw() { + ctx.fillStyle = "#555" + ctx.beginPath(); + const v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) { + ctx.lineTo(v[i].x, v[i].y); + } + ctx.lineTo(v[0].x, v[0].y); + ctx.fill(); + } + }); + Matter.Body.setStatic(doorBlock, true); //make static + Composite.add(engine.world, doorBlock); //add to world + doorBlock.classType = "body" + return doorBlock + } + + function drawBelt(circle1, circle2) { + // circle 1 + const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); + const distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius); + const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); + const angleToTangent = Math.acos(-circle1.radius / distanceToIntersection); + const tangentIntersection = { + x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x, + y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y + } + const tangentPoint = { + x: Math.cos(angleToTangent + slopeAngle) * -circle1.radius + circle1.x, + y: Math.sin(angleToTangent + slopeAngle) * -circle1.radius + circle1.y + } + const invertedTangentPoint = { + x: Math.cos(-angleToTangent + slopeAngle) * -circle1.radius + circle1.x, + y: Math.sin(-angleToTangent + slopeAngle) * -circle1.radius + circle1.y + } + + // circle 2 + const tangentPoint2 = { + x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + const invertedTangentPoint2 = { + x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + + // draw + ctx.beginPath(); + ctx.moveTo(tangentPoint.x, tangentPoint.y); + ctx.lineTo(tangentPoint2.x, tangentPoint2.y); + const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x)); + const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x)); + ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI); + ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y); + const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x)); + const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x)); + ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3 + Math.PI, newAngle4); + ctx.strokeStyle = '#000'; + ctx.lineWidth = 5; + ctx.stroke(); + } + + function drawDiagonalBelt(circle1, circle2) { + // circle 1 + const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); + const distanceToIntersection = (circle1.radius * distance) / (circle1.radius + circle2.radius); + const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); + const angleToTangent = Math.acos(circle1.radius / distanceToIntersection); + const tangentIntersection = { + x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x, + y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y + } + const tangentPoint = { + x: Math.cos(angleToTangent + slopeAngle) * circle1.radius + circle1.x, + y: Math.sin(angleToTangent + slopeAngle) * circle1.radius + circle1.y + } + const invertedTangentPoint = { + x: Math.cos(-angleToTangent + slopeAngle) * circle1.radius + circle1.x, + y: Math.sin(-angleToTangent + slopeAngle) * circle1.radius + circle1.y + } + + // circle 2 + const tangentPoint2 = { + x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + const invertedTangentPoint2 = { + x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + + // draw + ctx.beginPath(); + ctx.moveTo(tangentPoint.x, tangentPoint.y); + ctx.lineTo(tangentPoint2.x, tangentPoint2.y); + const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x)); + const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x)); + ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI); + ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y); + const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x)); + const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x)); + ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3, newAngle4 + Math.PI, true); + ctx.strokeStyle = '#000'; + ctx.lineWidth = 5; + ctx.stroke(); + } + + function getIntersection(v1, v1End, domain) { + const intersections = getIntersections(v1, v1End, domain); + + var best = { + x: v1End.x, + y: v1End.y, + dist: Math.sqrt((v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2) + } + for (const intersection of intersections) { + const dist = Math.sqrt((intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2); + if (dist < best.dist) { + best = { + x: intersection.x, + y: intersection.y, + dist: dist + }; + } + } + + return best; + } + + function getIntersections(v1, v1End, domain) { + const intersections = []; + + for (const obj of domain) { + for (var i = 0; i < obj.vertices.length - 1; i++) { + results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]); + if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); + } + results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]); + if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); + } + + return intersections; + } + + function circleLoS(pos, radius, domain) { + + function allCircleLineCollisions(c, radius, domain) { + var lines = []; + for (const obj of domain) { + //const obj = domain[0] + for (var i = 0; i < obj.vertices.length - 1; i++) { + lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius)); + } + lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius)); + } + + const collisionLines = []; + for (const line of lines) { + if (line.length == 2) { + const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2) + const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x); + const queryPoint1 = { + x: Math.cos(angle1) * (distance1 - 1) + c.x, + y: Math.sin(angle1) * (distance1 - 1) + c.y + } + const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2) + const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x); + const queryPoint2 = { + x: Math.cos(angle2) * (distance2 - 1) + c.x, + y: Math.sin(angle2) * (distance2 - 1) + c.y + } + + collisionLines.push(line) + } + } + + return collisionLines; + } + + function circleLineCollisions(a, b, c, radius) { + // calculate distances + const angleOffset = Math.atan2(b.y - a.y, b.x - a.x); + const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2); + const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); + const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2); + + // calculate the closest point on line AB to point C + const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x) + const sideAD = Math.cos(angleA) * sideB; + const d = { // closest point + x: Math.cos(angleOffset) * sideAD + a.x, + y: Math.sin(angleOffset) * sideAD + a.y + } + const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2); + if (distance == radius) { + // tangent + return [d]; + } else if (distance < radius) { + // secant + const angleOffset = Math.atan2(d.y - c.y, d.x - c.x); + const innerAngle = Math.acos(distance / radius); + const intersection1 = { + x: Math.cos(angleOffset + innerAngle) * radius + c.x, + y: Math.sin(angleOffset + innerAngle) * radius + c.y + } + + const intersection2 = { + x: Math.cos(angleOffset - innerAngle) * radius + c.x, + y: Math.sin(angleOffset - innerAngle) * radius + c.y + } + + const distance1 = { + a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2), + b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2) + } + const distance2 = { + a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2), + b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2) + } + const result = []; + if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) { + result.push(intersection1); + } else { + if (distance1.a < distance1.b) { + if (sideB <= radius) result.push(a); + } else { + if (sideA <= radius) result.push(b) + } + } + if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) { + result.push(intersection2); + } else { + if (distance2.a <= distance2.b) { + if (sideB <= radius) result.push(a); + } else { + if (sideA <= radius) result.push(b) + } + } + + return result; + } else { + // no intersection + return []; + } + } + + var vertices = []; + for (const obj of losDomain) { + for (var i = 0; i < obj.vertices.length; i++) { + const vertex = obj.vertices[i]; + const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + const queryPoint = { + x: Math.cos(angleToVertex + Math.PI) + vertex.x, + y: Math.sin(angleToVertex + Math.PI) + vertex.y + } + + if (Matter.Query.ray(domain, pos, queryPoint).length == 0) { + var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2); + var endPoint = { + x: vertex.x, + y: vertex.y + } + + if (distance > radius) { + const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { + x: Math.cos(angle) * radius + pos.x, + y: Math.sin(angle) * radius + pos.y + } + + distance = radius + } + + var best = getIntersection(pos, endPoint, domain); + + if (best.dist >= distance) { + best = { + x: endPoint.x, + y: endPoint.y, + dist: distance + } + } + vertices.push(best) + + + var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { + x: Math.cos(angle + 0.001) * radius + pos.x, + y: Math.sin(angle + 0.001) * radius + pos.y + } + + best = getIntersection(pos, endPoint, domain); + + if (best.dist >= radius) { + best = { + x: endPoint.x, + y: endPoint.y, + dist: radius + } + } + vertices.push(best) + + + angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { + x: Math.cos(angle - 0.001) * radius + pos.x, + y: Math.sin(angle - 0.001) * radius + pos.y + } + + best = getIntersection(pos, endPoint, domain); + + if (best.dist >= radius) { + best = { + x: endPoint.x, + y: endPoint.y, + dist: radius + } + } + vertices.push(best) + } + } + } + + const outerCollisions = allCircleLineCollisions(pos, radius, domain); + const circleCollisions = []; + for (const line of outerCollisions) { + for (const vertex of line) { + const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2) + const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + const queryPoint = { + x: Math.cos(angle) * (distance - 1) + pos.x, + y: Math.sin(angle) * (distance - 1) + pos.y + } + if (Math.abs(distance - radius) < 1 && Matter.Query.ray(domain, pos, queryPoint).length == 0) circleCollisions.push(vertex) + } + } + + for (var i = 0; i < circleCollisions.length; i++) { + const vertex = circleCollisions[i]; + var nextIndex = i + 1; + if (nextIndex == circleCollisions.length) nextIndex = 0; + const nextVertex = circleCollisions[nextIndex]; + const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x); + var newAngle; + if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) { + // if the arc between the to points crosses over the left side (+/- pi radians) + const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1)); + const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2)); + newAngle = (newAngle1 + newAngle2) / 2; + var multiplier; + if (newAngle == 0) { + multiplier = 1; + } else { + multiplier = newAngle / Math.abs(newAngle); + } + newAngle = Math.PI * multiplier - newAngle * multiplier; + test = true; + } else { + newAngle = (angle1 + angle2) / 2; + } + + // shoot ray between them + var endPoint = { + x: Math.cos(newAngle) * radius + pos.x, + y: Math.sin(newAngle) * radius + pos.y + } + + var best = getIntersection(pos, endPoint, domain); + + vertices.push(vertex); + + if (best.dist <= radius) vertices.push({ x: best.x, y: best.y }) + } + + vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x)); + return vertices; + } + + function compareArrays(array1, array2) { + for (var i = 0; i < array1.length; i++) { + if (array1[i] != array2[i]) return false; + } + + return true; + } + + function generateIntersectMap() { + // include intersections in map elements to avoid LoS issues with overlapping + const intersectMap = []; + for (var i = 0; i < map.length; i++) { + const obj = map[i]; + const newVertices = []; + const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1)) + for (var j = 0; j < obj.vertices.length - 1; j++) { + var intersections = getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap); + newVertices.push(obj.vertices[j]); + for (const vertex of intersections) { + newVertices.push({ x: vertex.x, y: vertex.y }); + } + } + intersections = getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap); + newVertices.push(obj.vertices[obj.vertices.length - 1]); + for (const vertex of intersections) { + newVertices.push({ x: vertex.x, y: vertex.y }); + } + + intersectMap.push({ vertices: newVertices }); + } + + return intersectMap; + } + + function addPartToMap(len) { // from "run" map + map[len].collisionFilter.category = cat.map; + map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[len], true); + Composite.add(engine.world, map[len]); + } + + level.setPosToSpawn(-500, -50); + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 250; + level.exit.y = 720; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + + spawn.mapRect(-925, 0, 2650, 100); + + spawn.mapRect(-925, -1700, 325, 1800); + spawn.mapRect(-650, -325, 325, 50); + spawn.mapRect(-650, -1400, 325, 50); + spawn.mapRect(-1700, -1700, 1100, 200); + spawn.mapRect(-1700, -4600, 300, 3100); + spawn.mapRect(-1700, -4600, 1250, 200); + spawn.mapRect(200, -4600, 2750, 200); + spawn.mapRect(-400, -4225, 200, 50); + spawn.mapRect(2800, -4600, 150, 1400); + + spawn.mapRect(1350, -325, 100, 50); + spawn.mapRect(1400, -1500, 325, 1600); + spawn.mapRect(1400, -1500, 1550, 50); + spawn.mapRect(1400, -1900, 900, 50); + spawn.mapRect(1400, -2900, 100, 1050); + + spawn.mapRect(-600, -2900, 3550, 100); + spawn.mapRect(2850, -2900, 100, 700); + spawn.mapRect(2850, -2200, 100, 350); + map[map.length - 1].fallsOff2 = true; // this piece will fall off in the middle of cutscene + + spawn.mapRect(2300, -1900, 500, 50); + map[map.length - 1].fallsOff = true; // this piece wall fall off at the start of cutscene + + spawn.mapRect(2800, -1900, 200, 50); + spawn.mapRect(2900, -1900, 50, 450); + powerUps.directSpawn(2700, -1675, "tech"); + + spawn.mapRect(2800, -3300, 825, 100); + spawn.mapRect(3525, -3300, 100, 3000); + spawn.mapRect(3400, -2850, 225, 50); + spawn.mapRect(2875, -2525, 175, 25); + spawn.mapRect(3325, -2675, 150, 25); + spawn.mapRect(3400, -2850, 75, 200); + spawn.mapRect(3150, -2225, 100, 25); + + spawn.mapRect(-2300, 750, 5450, 100); + + pendulum1 = pendulum(400, -2500, 75, 1700, 50, 0.3, 0, 300); + const gear1 = mapGear(-1200, -2000, 100, 200, 0, -0.05, 5, 75); + const gear2 = mapGear(-700, -2500, 150, 270, -0.5, 0.05, 5, 50); + const gear3 = mapGear(-3500, -1000, 1100, 1500, -0.5, 0.005, 10, 150, 40); + const piston1 = customDoor(1650, -1850, 100, 350); // x, y, width, height, distance, speed = 1 + const piston2 = customDoor(1950, -1850, 100, 350); + const piston3 = horizontalDoor(-2000, -4200, 300, 100, 300, 20); + const piston4 = horizontalDoor(-2000, -3800, 300, 100, 300, 20); + const piston5 = horizontalDoor(-2000, -3400, 300, 100, 300, 20); + const piston6 = horizontalDoor(-2000, -3000, 300, 100, 300, 20); + const piston7 = horizontalDoor(-2000, -2600, 300, 100, 300, 20); + const hand1 = clockHand(400, -3700, 75, 600); + const elevator1 = level.elevator(3200, 0, 150, 50, -1750, 0.0025, { up: 0.05, down: 0.2 }); + + spawn.debris(-300, 0, 1300, 6); + spawn.debris(0, -2900, 2500, 8); + + spawn.randomSmallMob(-500, -500, 1); + spawn.randomMob(190, -1300, 1); + spawn.randomMob(200, -320, 0.3); + spawn.randomMob(1000, -1100, 1); + spawn.randomMob(-160, -2050, 1); + spawn.randomMob(-1100, -2900, 0.5); + spawn.randomLevelBoss(1900, -3800, spawn.randomBossList.splice(0, spawn.randomBossList.indexOf("shieldingBoss"), 1).concat(spawn.randomBossList.splice(spawn.randomBossList.indexOf("shieldingBoss") + 1))); // shieldingBoss lags out the lighting system for some reason + spawn.randomMob(2500, -3500, 0.3); + spawn.randomMob(1300, -4100, 0.5); + spawn.randomMob(3400, -2450, 1); + spawn.randomMob(2850, -2050, 0.4); + spawn.randomGroup(-150, -2400, 0.5); + spawn.randomMob(-1250, -5150, 1); + spawn.randomMob(-2900, -4000, 0.4); + spawn.randomMob(-1350, -950, 1); + spawn.randomMob(2700, -850, 0.4); + spawn.randomMob(2500, -50, 0.4); + + powerUps.addResearchToLevel() // needs to run after mobs are spawned + + var dealtPiston1Damage = false; + var dealtPiston2Damage = false; + var dealtPiston1MobDamage = false; + var dealtPiston2MobDamage = false; + var lastPistonDirection = false; + var pistonsLocked = false; + var finishedGearFight = false; + var roofReadyToFall = false; + var roofFallCycle = 0; + var drawGear = false; + var gearCycle = simulation.cycle; + var gearPositions = []; + var pistonUnlockCycle = 0; + + for (var i = 0; i < 15; i++) { + gearPositions.push({ + x: 2400 + Math.random() * 200, + y: -3300 - Math.random() * 3000 + }); + } + + var gearSizes = []; + + for (var i = 0; i < 15; i++) { + const r1 = 30 + Math.random() * 50; + gearSizes.push({ + r1: r1, + r2: r1 + 15 + Math.random() * 30 + }) + } + + var circleHead = Matter.Bodies.polygon(m.pos.x, m.pos.y, 0, 31); + var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]); + var oldMap = [...map]; + var oldMob = [...mob]; + var spawnGearMobCycle = 0; + var gearsSpawned = 0; + var lastSmallGearRot = 0; + var smallGearRot = 0; + var smallGearPosRot = 0; + var bigGearRot = 0; + var finalGearRot; + var lastFinalGearRot; + var startCycle = simulation.cycle; // used to offset simulation.cycle to avoid the swing starting halfway through at the start of the level and messing up syncronization + + level.custom = () => { + Matter.Body.setPosition(circleHead, m.pos) + if (!(compareArrays(oldMap, map) && compareArrays(oldMob, mob))) { + losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]); + } + oldMap = [...map]; + oldMob = [...mob]; + ctx.fillStyle = "#b0b0b2"; + ctx.fillRect(-600, -1700, 2000, 1700); + ctx.fillRect(1350, -1851, 1550, 350); + ctx.fillRect(-1400, -2950, 4250, 1450); + ctx.fillRect(-1400, -4400, 4350, 1500); + ctx.fillRect(-450, -4600, 650, 250); + ctx.fillRect(2750, -3200, 200, 1300); + ctx.fillRect(2750, -3200, 200, 1300); + ctx.fillStyle = "#000"; + ctx.fillRect(350, -2800, 100, 25); + // light + var lightPos = { x: 400, y: -2775 }; + var lightRadius = 2950; + const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead])); if (vertices.length > 0 && vertices[0].x) { + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + for (var i = 1; i < vertices.length; i++) { + var currentDistance = Math.sqrt((vertices[i - 1].x - lightPos.x) ** 2 + (vertices[i - 1].y - lightPos.y) ** 2); + var newDistance = Math.sqrt((vertices[i].x - lightPos.x) ** 2 + (vertices[i].y - lightPos.y) ** 2); + if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) { + const currentAngle = Math.atan2(vertices[i - 1].y - lightPos.y, vertices[i - 1].x - lightPos.x); + const newAngle = Math.atan2(vertices[i].y - lightPos.y, vertices[i].x - lightPos.x); + ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[i].x, vertices[i].y) + } + } + newDistance = Math.sqrt((vertices[0].x - lightPos.x) ** 2 + (vertices[0].y - lightPos.y) ** 2); + currentDistance = Math.sqrt((vertices[vertices.length - 1].x - lightPos.x) ** 2 + (vertices[vertices.length - 1].y - lightPos.y) ** 2); + if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) { + const currentAngle = Math.atan2(vertices[vertices.length - 1].y - lightPos.y, vertices[vertices.length - 1].x - lightPos.x); + const newAngle = Math.atan2(vertices[0].y - lightPos.y, vertices[0].x - lightPos.x); + ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[0].x, vertices[0].y) + } + ctx.fillStyle = "rgba(216, 218, 223, 0.5)"; + ctx.fill(); + } + + ctx.beginPath(); + ctx.moveTo(425, -2775); + ctx.arc(400, -2775, 25, 0, Math.PI); + ctx.fillStyle = "#c6aa12"; + ctx.fill(); + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 1; + ctx.stroke(); + pendulum1.rotate(); + gear1.rotate(); + gear2.rotate(); + gear3.rotate(); + hand1.rotate(); + drawBackgroundGear(-1200, -2300, 75, 150, 0.3, "#ccc", -0.05); + drawBackgroundGear(-1010, -2380, 30, 100, -0.1, "#ccc", 0.05); + + // pendulum gears + if (!m.isBodiesAsleep) smallGearPosRot += Math.sin((simulation.cycle - startCycle) / 50) * 0.3 - Math.sin((simulation.cycle - startCycle - 1) / 50) * 0.3; + if (smallGearPosRot > 0.1) smallGearPosRot = 0.1; + if (smallGearPosRot < -0.1) smallGearPosRot = -0.1; + var circ = 2 * Math.PI * 150; + var arcLength = ((smallGearPosRot - Math.sin((simulation.cycle - startCycle) / 50) * 0.2) / (Math.PI * 2)) * circ; + lastSmallGearRot = smallGearRot; + smallGearRot = arcLength / (2 * Math.PI * 50) * Math.PI * -2 + 0.6; + + if (Math.abs(smallGearPosRot) == 0.1) { + bigGearRot += Math.abs((smallGearRot - lastSmallGearRot) * (50 / 75)); + } + + drawBackgroundGear(740, -2625, 270, 330, bigGearRot, "#d2d3d4", 0, 15, 20); // the big one in the background + + drawBackgroundGear(400, -2500, 100, 150, Math.sin((simulation.cycle - startCycle) / 50) * -0.3, "#ccc", 0, 8, 20); // attached to pendulum + + drawBackgroundGear(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 50, 75, smallGearRot, "#ccc", 0, 7, 20); + ctx.beginPath(); + ctx.arc(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 10, 0, 2 * Math.PI); + ctx.strokeStyle = "#444"; + ctx.lineWidth = 10; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(400, -2500, 200, -0.1, 0.1); + ctx.strokeStyle = "#444"; + ctx.lineWidth = 10; + ctx.stroke(); + + drawBackgroundGear(740, -2625, 75, 110, bigGearRot, "#ccc", 0, 8, 20); + ctx.beginPath(); + ctx.arc(740, -2625, 40, 0, 2 * Math.PI); + ctx.fillStyle = "#bbb"; + ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + drawBackgroundGear(740, -2375, 75, 110, bigGearRot * -1, "#ccc", 0, 8, 20); + ctx.beginPath(); + ctx.arc(740, -2375, 40, 0, 2 * Math.PI); + ctx.fillStyle = "#bbb"; + ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + + drawDiagonalBelt({ x: 740, y: -2625, radius: 40 }, { x: 740, y: -2375, radius: 40 }) + + if (finalGearRot != null) lastFinalGearRot = finalGearRot; + finalGearRot = Math.round((-bigGearRot * 294.72 / 25) * 100) / 100 + Math.PI / 2; + + drawBackgroundGear(1080, -2650, 10, 20, finalGearRot, "#ccc", 0, 5, 50); + ctx.beginPath(); + ctx.arc(1080, -2650, 10, 0, 2 * Math.PI); + ctx.fillStyle = "#bbb"; + ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + drawBackgroundGear(1650, -2550, 300, 360, finalGearRot, "#ccc", 0, 6, 50); + ctx.beginPath(); + ctx.arc(1650, -2550, 100, 0, 2 * Math.PI); + ctx.fillStyle = "#bbb"; + ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 2; + ctx.stroke(); + drawBelt({ x: 1080, y: -2650, radius: 10 }, { x: 1650, y: -2550, radius: 100 }); + ctx.beginPath(); + ctx.arc(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + drawBackgroundGear(2300, -2550, 300, 360, -finalGearRot + 0.5, "#ccc", 0, 6, 50); + ctx.beginPath(); + ctx.arc(Math.cos(finalGearRot) * 294 + 2300, Math.sin(finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(1630, -2215, 15, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(1670, -2215, 15, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(1940, -2250, 15, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(2300, -2215, 15, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + + if (!finishedGearFight && !pistonsLocked && m.pos.x > 2100 && m.pos.x < 2900 && m.pos.y > -1850 && m.pos.y < -1500) { + pistonsLocked = true; + + roofFallCycle = simulation.cycle + 250; + roofReadyToFall = true; + } + + if (roofReadyToFall && simulation.cycle >= roofFallCycle) { + // section of roof is deleted + for (var i = 0; i < map.length; i++) { + if (map[i].fallsOff) { + Matter.Composite.remove(engine.world, map[i]); + map.splice(i, 1); + } + } + + // replace it with a block + spawn.bodyRect(2310, -1900, 480, 50); + roofReadyToFall = false; + drawGear = true; + gearCycle = simulation.cycle + 100; + } + + //draw some background gears falling when roof falls + if (drawGear && simulation.cycle >= gearCycle) { + for (var i = 0; i < 15; i++) { + drawFallingBackgroundGear(gearPositions[i].x, gearPositions[i].y, gearSizes[i].r1, gearSizes[i].r2, simulation.cycle, "#ccc", 0.1, 25, gearCycle); + } + + if (spawnGearMobCycle == 0) { + spawnGearMobCycle = simulation.cycle + 100; + } + } + + if (spawnGearMobCycle > 0 && simulation.cycle >= spawnGearMobCycle) { + if (gearsSpawned < 40) { + gearMob(1600 + Math.random() * 1000, -2300 - Math.random() * 300, false, true); + gearsSpawned++; + spawnGearMobCycle = simulation.cycle + 25 - (simulation.difficulty - simulation.difficultyMode) / 2; + } else if (pistonUnlockCycle == 0) { + pistonUnlockCycle = simulation.cycle + 50; + } + } + + if (!finishedGearFight && pistonUnlockCycle > 0 && simulation.cycle > pistonUnlockCycle) { + pistonsLocked = false; + finishedGearFight = true; + + for (var i = 0; i < map.length; i++) { + if (map[i].fallsOff2) { + Matter.Composite.remove(engine.world, map[i]); + map.splice(i, 1); + } + } + + spawn.bodyRect(2850, -2180, 100, 280); + Matter.Body.setAngularVelocity(body[body.length - 1], 0.025); + } + + if (Math.sin((simulation.cycle + 15) / 25) < 0 && !lastPistonDirection) { // 15 cycles early to line up better with pendulum + piston3.isClosing = true; + piston4.isClosing = false; + piston5.isClosing = true; + piston6.isClosing = false; + piston7.isClosing = true; + } else if (Math.sin((simulation.cycle + 15) / 25) > 0 && lastPistonDirection) { + piston3.isClosing = false; + piston4.isClosing = true; + piston5.isClosing = false; + piston6.isClosing = true; + piston7.isClosing = false; + } + + if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) { + dealtPiston1Damage = false; + dealtPiston1MobDamage = false; + } + if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { + dealtPiston2Damage = false; + dealtPiston2MobDamage = false; + } + + piston3.openClose(); + piston4.openClose(); + piston5.openClose(); + piston6.openClose(); + piston7.openClose(); + + if (!pistonsLocked) { + piston1.isLocked = false; + } + + if (!pistonsLocked || ((Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) != 0 || piston1.position.y < -1850) && !piston1.isLocked)) { + piston1.setPos(1650, -1850 + Math.sin(-finalGearRot) * 175); + } else { + piston1.isLocked = true; + } + + piston2.setPos(1950, -1850 + Math.sin(finalGearRot) * 175); + ctx.beginPath(); + ctx.moveTo(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550); + ctx.lineTo(1650, -2230); + ctx.lineTo(piston1.position.x, piston1.position.y - 175) + ctx.strokeStyle = "#777"; + ctx.lineWidth = 10; + ctx.stroke(); + + var circle1; + var circle2; + if (Math.cos(finalGearRot) * 294 > 0) { + circle1 = { + x: Math.cos(finalGearRot) * 294 + 2300, + y: Math.sin(finalGearRot) * 294 - 2550, + radius: -25 + } + + circle2 = { + x: 2300, + y: -2215, + radius: -15 + } + } else { + circle1 = { + x: Math.cos(finalGearRot) * 294 + 2300, + y: Math.sin(finalGearRot) * 294 - 2550, + radius: 25 + } + + circle2 = { + x: 2300, + y: -2215, + radius: 15 + } + } + + // same method used in drawBelt() + var distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); + var distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius); + var slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); + var angleToTangent = Math.acos(-circle1.radius / distanceToIntersection); + const tangentPoint = { + x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + + // same method used in drawDiagonalBelt() + const circle3 = { + x: 1940, + y: -2250, + radius: 15 + } + + distance = Math.sqrt((circle2.x - circle3.x) ** 2 + (circle2.y - circle3.y) ** 2); + distanceToIntersection = (circle3.radius * distance) / (circle3.radius + circle2.radius); + slopeAngle = Math.atan((circle2.y - circle3.y) / (circle2.x - circle3.x)); + angleToTangent = Math.acos(circle3.radius / distanceToIntersection); + const tangentPoint2 = { + x: Math.cos(angleToTangent + slopeAngle) * circle3.radius + circle3.x, + y: Math.sin(angleToTangent + slopeAngle) * circle3.radius + circle3.y + } + const invertedTangentPoint2 = { + x: Math.cos(-angleToTangent + slopeAngle) * circle3.radius + circle3.x, + y: Math.sin(-angleToTangent + slopeAngle) * circle3.radius + circle3.y + } + + const tangentPoint3 = { + x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + const invertedTangentPoint3 = { + x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, + y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y + } + + distance = Math.sqrt((piston2.position.y - 175 - circle3.y) ** 2 + (piston2.position.x - 50 - circle3.x) ** 2); + slopeAngle = Math.atan((piston2.position.y - 175 - circle3.y) / (piston2.position.x - 50 - circle3.x)); + angleToTangent = Math.acos(circle3.radius / distance); + const tangentPoint4 = { + x: Math.cos(angleToTangent) * distance + circle3.x, + y: Math.sin(angleToTangent) * distance + circle3.y + } + + // draw + ctx.beginPath(); + ctx.moveTo(circle1.x, circle1.y); + ctx.lineTo(tangentPoint.x, tangentPoint.y); + const newAngle = Math.atan((tangentPoint.y - circle2.y) / (tangentPoint.x - circle2.x)); + const newAngle2 = Math.atan((tangentPoint3.y - circle2.y) / (tangentPoint3.x - circle2.x)); + ctx.arc(circle2.x, circle2.y, Math.abs(circle2.radius), newAngle, -newAngle2); + ctx.lineTo(invertedTangentPoint2.x, invertedTangentPoint2.y); + const newAngle3 = Math.atan((invertedTangentPoint2.y - circle3.y) / (invertedTangentPoint2.x - circle3.x)); + ctx.arc(circle3.x, circle3.y, circle3.radius, newAngle3, Math.PI / 2 + angleToTangent, true); + ctx.lineTo(tangentPoint4.x, tangentPoint4.y); + ctx.strokeStyle = '#777'; + ctx.lineWidth = 10; + ctx.stroke(); + + lastPistonDirection = Math.sin((simulation.cycle + 15) / 25) < 0; + + if (Matter.Query.ray([player], Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5).length > 0 && !dealtPiston1Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { + m.damage(0.1); + dealtPiston1Damage = true; + } + + var piston1MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5); + if (piston1MobCollisions.length > 0 && !dealtPiston1MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { + for (var mobHit of piston1MobCollisions) { + mobHit.body.damage(1); + } + dealtPiston1MobDamage = true; + } + + if (Matter.Query.ray([player], Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5).length > 0 && !dealtPiston2Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) { + m.damage(0.1); + dealtPiston2Damage = true; + } + + var piston2MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5); + if (piston2MobCollisions.length > 0 && !dealtPiston2MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { + for (var mobHit of piston2MobCollisions) { + mobHit.body.damage(1); + } + dealtPiston2MobDamage = true; + } + + // clock + ctx.beginPath(); + ctx.arc(400, -3700, 600, 0, 2 * Math.PI); + ctx.fillStyle = "#e9e9e9"; + ctx.fill(); + ctx.strokeStyle = "#3a3f20"; + ctx.lineWidth = 2; + ctx.stroke(); + + ctx.lineCap = "butt"; + ctx.beginPath(); + ctx.moveTo(350, -4275); + ctx.lineTo(390, -4150); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 20; + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(390, -4275); + ctx.lineTo(350, -4150); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(420, -4275); + ctx.lineTo(420, -4150); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(450, -4275); + ctx.lineTo(450, -4150); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(-Math.PI / 3) * 510, + y: -3700 + Math.sin(-Math.PI / 3) * 510 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x, numberOffset.y - 62); + ctx.lineTo(numberOffset.x, numberOffset.y + 63); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(-Math.PI / 6) * 510, + y: -3700 + Math.sin(-Math.PI / 6) * 510 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 20, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 20, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 20, numberOffset.y + 63); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(870, -3762); + ctx.lineTo(870, -3637); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(910, -3762); + ctx.lineTo(910, -3637); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(950, -3762); + ctx.lineTo(950, -3637); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI / 6) * 535, + y: -3700 + Math.sin(Math.PI / 6) * 535 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 50, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 50, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); + ctx.lineTo(numberOffset.x, numberOffset.y + 53); + ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI / 3) * 515, + y: -3700 + Math.sin(Math.PI / 3) * 515 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); + ctx.lineTo(numberOffset.x, numberOffset.y + 53); + ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI / 2) * 515, + y: -3700 + Math.sin(Math.PI / 2) * 515 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 15, numberOffset.y + 53); + ctx.lineTo(numberOffset.x + 5, numberOffset.y - 62); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI * 2 / 3) * 500, + y: -3700 + Math.sin(Math.PI * 2 / 3) * 500 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53); + ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI * 5 / 6) * 500, + y: -3700 + Math.sin(Math.PI * 5 / 6) * 500 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53); + ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x + 65, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 65, numberOffset.y + 63); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(Math.PI) * 500, + y: -3700 + Math.sin(Math.PI) * 500 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 5, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 5, numberOffset.y + 63); + ctx.lineTo(numberOffset.x + 35, numberOffset.y - 62); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62); + ctx.lineTo(numberOffset.x - 35, numberOffset.y + 63); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(-Math.PI * 5 / 6) * 500, + y: -3700 + Math.sin(-Math.PI * 5 / 6) * 500 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 25, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 25, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 25, numberOffset.y + 63); + ctx.lineTo(numberOffset.x + 25, numberOffset.y - 62); + ctx.stroke(); + + var numberOffset = { + x: 400 + Math.cos(-Math.PI * 2 / 3) * 500, + y: -3700 + Math.sin(-Math.PI * 2 / 3) * 500 + } + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 10, numberOffset.y - 62); + ctx.lineTo(numberOffset.x + 40, numberOffset.y + 63); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 10, numberOffset.y + 63); + ctx.lineTo(numberOffset.x + 40, numberOffset.y - 62); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(numberOffset.x - 40, numberOffset.y + 63); + ctx.lineTo(numberOffset.x - 40, numberOffset.y - 62); + ctx.stroke(); + + ctx.lineCap = "round"; + + level.exit.drawAndCheck(); + level.enter.draw(); + } + + var lastBlock = Math.sin(simulation.cycle / 50) < 0; + + level.customTopLayer = () => { + elevator1.move(); + + ctx.beginPath(); + ctx.moveTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y); + for (var i = 0; i < pendulum1.parts[2].vertices.length; i++) { + ctx.lineTo(pendulum1.parts[2].vertices[i].x, pendulum1.parts[2].vertices[i].y); + } + ctx.lineTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y); + ctx.fillStyle = "#999"; + ctx.fill(); + ctx.lineWidth = 2 + ctx.strokeStyle = color.blockS; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y); + for (var i = 0; i < gear3.parts[1].vertices.length; i++) { + ctx.lineTo(gear3.parts[1].vertices[i].x, gear3.parts[1].vertices[i].y); + } + ctx.lineTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y); + ctx.fillStyle = "#999"; + ctx.fill(); + ctx.lineWidth = 2 + ctx.strokeStyle = color.blockS; + ctx.stroke(); + + ctx.fillStyle = "#444"; + ctx.fillRect(3275, -1750, 1, 1750); + + ctx.fillStyle = "#888"; + + if (Math.sin(simulation.cycle / 50) < 0 && !lastBlock) { + // remove old elements + for (var i = 0; i < map.length; i++) { + if (map[i].isRemove) { + Matter.Composite.remove(engine.world, map[i]); + map.splice(i, 1); + } + } + + // add new element + spawn.mapRect(-200, -600, 275, 50); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + } else if (Math.sin(simulation.cycle / 50) * 0.3 >= 0 && lastBlock) { + for (var i = 0; i < map.length; i++) { + if (map[i].isRemove) { + Matter.Composite.remove(engine.world, map[i]); + map.splice(i, 1); + } + } + + spawn.mapRect(825, -600, 275, 50); + addPartToMap(map.length - 1); + map[map.length - 1].isRemove = true; + } + + simulation.draw.setPaths(); + lastBlock = Math.sin(simulation.cycle / 50) * 0.3 < 0; + } + }, + buttonbutton() { + simulation.makeTextLog(`buttonbutton by ||Destabilized E||`); + const mover = level.mover(1425, -1949, 600, 25); //x,y,width.height,VxGoal,force + + let portal + portal = level.portal({ + x: -146, + y: 131 + }, 2 * Math.PI, { + x: 1805, + y: -2295 + }, 90) + + const button = level.button(-456, -1320) + spawn.bodyRect(-400, -1475, 75, 75); + const button2 = level.button(1781, -61) + spawn.bodyRect(1781, (-61) - 100, 75, 75); + const boost1 = level.boost(1366, -1942, 1300) + + button.isUp = true + button2.isUp = true + + const train = level.transport(-250, 1151, 400, 50, 8 + simulation.difficultyMode) + level.custom = () => { + if (train.position.x < -244) { + train.changeDirection(true) //go right + } else if (train.position.x > 1700) { + train.changeDirection(false) //go left + } + if (button.isUp && button2.isUp) train.move(); + mover.push(); + ctx.fillStyle = "rgba(0,255,255,0.1)"; + ctx.fillRect(6400, -550, 300, 350); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + button.query(); + button.draw(); + button2.query(); + button2.draw(); + boost1.query(); + train.draw() + portal[2].query() + portal[3].query() + portal[0].draw(); + portal[1].draw(); + mover.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-150, -650, 900, 250) + }; + level.setPosToSpawn(0, -450); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = -525; + level.exit.y = 1128; + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#ddd"; + spawn.mapRect(-725, -1325, 575, 1900); + spawn.mapRect(1425, -1925, 600, 1550); + spawn.mapRect(1450, -50, 500, 425); + spawn.mapRect(1950, 75, 325, 300); + spawn.mapRect(2275, 200, 200, 175); + spawn.mapRect(-150, -400, 900, 250); + spawn.mapRect(-150, 300, 900, 275); + spawn.mapRect(1700, 900, 450, 275); + spawn.mapRect(1800, 1600, 450, 250); + spawn.mapRect(1675, 1675, 275, 175); + spawn.mapRect(1575, 1675, 275, 175); + spawn.mapRect(-550, 1150, 150, 100); + spawn.bodyRect(-1475, -225, 50, 50); + spawn.bodyRect(2450, 1525, 925, 850); + spawn.mapRect(2275, 1400, 300, 150); + spawn.mapRect(2125, 1025, 125, 150); + spawn.mapRect(2250, 1175, 175, 75); + spawn.mapRect(2150, 1175, 175, 75); + spawn.mapRect(1725, 1150, 475, 100); + spawn.mapRect(2225, 675, 650, 50); + spawn.bodyRect(2400, 500, 150, 175); + spawn.nodeGroup(326, 85, "grenadier", 6) + spawn.mapRect(-225, -1325, 625, 225); + + spawn.randomMob(151, -1500) + spawn.randomMob(-88, -1829) + spawn.randomMob(2339, 896) + + + spawn.randomMob(1907, 1381) + spawn.randomMob(2398, 1301) + spawn.randomMob(1839, 811) + + + spawn.randomMob(2282, 1103) + spawn.randomMob(8, 124) + spawn.randomMob(629, 111) + + spawn.randomMob(43, 831) + spawn.randomMob(168, 1002) + spawn.randomMob(2956, 1006) + + spawn.randomMob(2713, 535) + spawn.randomMob(2396, 117) + spawn.randomMob(1498, -121) + + spawn.nodeGroup(2030, -16, "grower", 6) + spawn.randomLevelBoss(1840, 675) + }, + movers() { + simulation.makeTextLog(`movers by ryanbear`); + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + for (var i = 0; i < trains.length; i++) { + //oscillate back and forth + if (trains[i].position.x < 5075) { + trains[i].changeDirection(true) //go right + } else if (trains[i].position.x > 7875) { + trains[i].changeDirection(false) //go left + } + trains[i].draw(); + trains[i].move(); + } + for (var j = 0; j < zzz.length; j++) { + zzz[j][0].query(); + } + mvr.push(); + v3.query(); + ctx.fillStyle = "rgba(68,68,68,1)"; + ctx.fillRect(1725, -2400, 1000, 150); + ctx.fillRect(2175, -2775, 250, 450); + ctx.fillRect(2200, -2825, 225, 200); + ctx.fillRect(2075, -2575, 150, 200); + ctx.fillRect(2075, -2700, 150, 150); + ctx.fillRect(1875, -2525, 300, 125); + ctx.fillRect(1975, -2575, 150, 75); + ctx.fillRect(1800, -2475, 175, 100); + ctx.fillRect(2150, -2725, 350, 375); + ctx.fillRect(2475, -2575, 175, 200); + ctx.fillRect(2675, -2550, 25, 175); + ctx.fillRect(2625, -2550, 75, 200); + ctx.fillRect(2025, -2600, 200, 175); + ctx.fillRect(2025, -2675, 225, 225); + ctx.fillRect(2125, -2800, 250, 375); + ctx.fillRect(2400, -2625, 175, 175); + ctx.fillRect(2450, -2700, 100, 225); + ctx.fillRect(1950, -2600, 150, 200); + ctx.fillRect(1675, -2325, 250, 75); + ctx.fillRect(2700, -2525, 25, 150); + }; + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 23885; + level.exit.y = 800; + spawn.mapRect(-98, -8, 1000, 20); //bump for level entrance + spawn.mapRect(972, -287, 200, 20); //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + var trains = []; + var zzz = []; + spawn.mapRect(9850, 475, 200, 75); + for (var i = 0; i < 6; i++) { + trains.push(level.transport(6275, -2100 + 525 * i, 600, 50, (2 * i % 2 - 1) * 4 * Math.min(simulation.difficulty / 2, 2) * (1 + Math.random()))) + zzz.push([level.boost(6275, -2100 + 525 * i, 100), 6275]); + } + document.body.style.backgroundColor = "#d8dadf"; + + const portal1 = level.portal({ + x: 3984, + y: 1293 + }, -2 * Math.PI, { //right + x: 23863, + y: 82 + }, 2 * Math.PI) //right + + spawn.mapRect(1825, -2250, 3300, 300); spawn.mapRect(3250, -2875, 150, 625); + spawn.mapRect(3250, -2875, 425, 125); + spawn.mapRect(3425, -2725, 150, 300); + spawn.mapRect(3400, -2750, 175, 350); + spawn.mapRect(3575, -2625, 150, 375); + spawn.mapRect(3175, -2750, 75, 300); + spawn.mapRect(3100, -2750, 275, 300); + spawn.mapRect(3675, -2875, 75, 125); + spawn.mapRect(3675, -2625, 75, 400); + spawn.mapRect(3350, -2425, 100, 175); + spawn.mapRect(8350, 825, 1825, 250); + spawn.mapRect(3950, 800, 800, 375); + var hzd = level.hazard(3750, -2625, 1375, 375); + // spawn.mapRect(3750, -2625, 1375, 375); + var v1 = level.vanish(3975, -2600, 225, 25); + var v2 = level.vanish(4275, -2975, 225, 25); + var mvr = level.mover(2585, 1928, 2375, 100); + //spawn.mapRect(4925, 1725, 300, 25); + var v3 = level.vanish(4925, 1725, 100, 25); + for (var i = 0; i < 16; i++) { + if (i < 10) { + level.boost(1600 + 62 * i, -2307 - 62 * i, 100); + } + else { + level.boost(1600 + 62 * i, -2307 - 62 * 20 + 62 * i, 100); + } + } + + + for (var i = -1; i < 10; i++) { + level.boost(3847 - 62 * i, 879 + 62 * i, 100); + } + spawn.mapRect(3050, 1500, 1600, 200); + spawn.mapRect(1850, -1950, 3275, 1275); spawn.mapRect(1850, -675, 3275, 1300); + + spawn.mapRect(2700, -2525, 25, 175); + spawn.mapRect(3825, 925, 125, 575); spawn.mapRect(3600, 1100, 350, 400); spawn.mapRect(3375, 1350, 275, 150); spawn.mapRect(3550, 1300, 100, 50); spawn.mapRect(3800, 1000, 100, 150); spawn.mapRect(3725, 1075, 150, 125); spawn.mapRect(3725, 1025, 150, 125); spawn.mapRect(3550, 1225, 150, 125); spawn.mapRect(3500, 1275, 175, 125); + // color.map = "#444" //custom map color + bosses = ["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"]; + let randomBoss = Math.floor(Math.random() * bosses.length); + spawn[bosses[randomBoss]](2240, -2499, 100, false); + var btn = level.button(9889, 747); + btn.isUp = true; + spawn.randomMob(475, -725, 0.7); spawn.randomMob(825, -1825, 0.7); spawn.randomMob(3275, -3475, 0.7); spawn.randomMob(8550, 350, 0.7); spawn.randomMob(9350, -175, 0.7); spawn.randomMob(1575, 225, 0.7); spawn.randomMob(22825, 250, 0.7); + spawn.mapRect(-100, 0, 1000, 100); + var ddd = level.elevator(1326, -447, 200, 200, -2131, 0.003, { up: 0.1, down: 0.2 }); + /// transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { + spawn.mapRect(9500, 750, 675, 75); + spawn.mapRect(22350, 825, 3000, 150); + powerUps.spawn(4246, 1335, "tech") + powerUps.spawn(4246.8, 1335, "heal") + powerUps.spawn(4246.8, 1335.4, "ammo") + spawn.bodyRect(9200, 725, 50, 25); spawn.mapRect(12200, 675, 125, 50); spawn.mapRect(12925, 675, 100, 100); spawn.mapRect(13675, 650, 150, 150); spawn.mapRect(14200, 750, 25, 25); spawn.mapRect(14200, 675, 25, 75); spawn.mapRect(14550, 675, 125, 50); spawn.mapRect(15850, 675, 125, 100); spawn.mapRect(17175, 600, 25, 200); spawn.mapRect(17725, 700, 175, 50); spawn.mapRect(18775, 675, 175, 75); + spawn.bodyRect(8975, 700, 25, 25); spawn.bodyRect(8850, 575, 50, 50); spawn.bodyRect(9050, 650, 50, 50); spawn.bodyRect(8625, 575, 100, 75); spawn.bodyRect(8475, 675, 75, 25); + var train1 = level.transport(10250 - 700, 775, Math.max(1200 / simulation.difficulty, 200), 1350, 8); + level.customTopLayer = () => { + ddd.move(); + hzd.query(); + v1.query(); + v2.query(); + btn.draw(); + portal1[2].query(); + portal1[2].draw(); + portal1[3].query(); + portal1[3].draw(); + btn.query(); + if (!btn.isUp) { + spawn.mapRect(4050, 1175, 600, 325); + } + if (!btn.isUp && train1.position.x < 23785) { + train1.draw(); + train1.move(); + } + // if (trains[i].position.x < 5075) { + // trains[i].changeDirection(true) //go right + }; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + downpour() { + simulation.makeTextLog(`Downpour by DesBoot`); + let mobsspawned = 0 + const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro + + //5381, -3268, 10, 0.4 + spawn.mapRect(340, -2032.5, 20, 25); //laser nose //laserintro + const laserbutton = level.button(5485, -2510) + const doorbutton = level.button(7618, -3204) + const doortoggle = level.toggle(5088.4, 1226.7) + const door = level.door(6500, -1200, 100, 350, 100) + const bunkerdoor = level.door(10700, -2500, 100, 500, 200) + + const boost1 = level.boost(7300, 1209, 2200) + const boost2 = level.boost(6232.6, -832.8, 1400) + const portal = level.portal({ x: 4886.4, y: 1050.7 }, 2 * Math.PI, { x: 7686, y: -2121 }, 2 * Math.PI) + //let portal + const slime = level.hazard(-1800, 10, 4200, 400); + const slime2 = level.hazard(2400, -2100, 200, 2100); + const slime3 = level.hazard(2600, -2100, 3600, 200); + const slime4 = level.hazard(6400, -2100, 3600, 200); + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 13130.3; + let rainCount = 1 + level.exit.y = -370; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + let stopcycle = 0 + let flashcycle = Math.round(Math.random() * 25 + 260) + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#2e416e";//d8dadf + // color.map = "#444" //custom map color + + + //simulation.makeTextLog(stopcycle) + level.custom = () => { + do { + + ctx.beginPath() + ctx.fillStyle = "rgba(30,150,117,255)" + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1300 + 4500, -5000, Math.random() * 3 + 2.5, 2500) + ctx.rect(Math.random() * 1300 + 7500, -5000, Math.random() * 3 + 2.5, 1800) + ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) + ctx.fillStyle = "rgba(30,150,117,255)" + ctx.fill() + // } + // if (rainCount > 12) { + // rainCount = 1 + // simulation.makeTextLog(rainCount) + + // } else { + // rainCount = rainCount + 1 + // simulation.makeTextLog(rainCount) + // } + } while (Math.random() < 0.8); + //simulation.makeTextLog(stopcycle) + //simulation.makeTextLog(m.cycle) + // ctx.fillStyle = "rgba(228,255,0,0.8)" + // //simulation.makeTextLog(stopcycle) + // ctx.fillRect(50.4, -1210.0, 100, 100) + // stopcycle = m.cycle + Math.random * 600; + //stopcycle = m.cycles + Math.random * 600 + + if (stopcycle > 300) { + stopcycle = 0 + flashcycle = Math.round(Math.random() * 25 + 260) + document.body.style.backgroundColor = "#2e416e"; + } else { + if (stopcycle > flashcycle) { + document.body.style.backgroundColor = "#7391ff"; + for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], Math.random() * 20 + 30) + } + stopcycle = stopcycle + 1 + } + + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(12984, -704, 420, 450) + ctx.fillStyle = "rgba(0,0,0,0.5)" + ctx.fillRect(4703, -2362, 100, 100) + ctx.fillRect(5053, -2362, 100, 100) + ctx.fillRect(5403, -2362, 100, 100) + ctx.fillRect(4703, -2062, 100, 100) + ctx.fillRect(5053, -2062, 100, 100) + ctx.fillRect(5403, -2062, 100, 100) + ctx.fillRect(4523, -2512, 1150, 800) + ctx.fillRect(4735, -1233, 100, 500)//tree + ctx.beginPath() + ctx.moveTo(4487, -1195)//slope of -1/3 + ctx.lineTo(4736, -792) + ctx.lineTo(4736, -852) + ctx.lineTo(4527, -1195) + + + ctx.moveTo(5087, -1195)//slope of -1/3 + ctx.lineTo(4836, -792) + ctx.lineTo(4836, -852) + ctx.lineTo(5047, -1195) + ctx.fill() + ctx.moveTo(5252.4, -2483.5) + ctx.lineTo(5141.2, -2507.8) + ctx.lineTo(5209.2, -2625.2) + ctx.lineTo(5290.2, -2626.6) + + ctx.lineTo(5361.2, -2697.9) + ctx.lineTo(5410.6, -2717.0) + + ctx.lineTo(5680.2, -2648.7) + ctx.lineTo(5687.7, -2471.5) + + ctx.fill() + + + + //building 2 spawn.mapRect(8473, -2513, 50, 50); + ctx.fillRect(8673, -2137, 50, 175) + + ctx.fillRect(7630, -2540, 100, 100) + ctx.fillRect(7930, -2540, 100, 100) + ctx.fillRect(8230, -2540, 100, 100) + + ctx.fillRect(8530, -2765, 100, 100) + + ctx.fillRect(7630, -2990, 100, 100) + ctx.fillRect(7930, -2990, 100, 100) + + ctx.fillRect(8230, -2990, 100, 100) + + + ctx.beginPath() + ctx.moveTo(7475, -3213) + ctx.lineTo(8100, -3213) + ctx.lineTo(8191.2, -3334.7) + ctx.lineTo(8318.0, -3388.3) + ctx.lineTo(8348.5, -3496.9) + ctx.lineTo(8480.0, -3512.6) + ctx.lineTo(8670, -3482) + ctx.lineTo(8725, -3213) + ctx.lineTo(8725, -1463) + ctx.lineTo(7475, -1463) + ctx.fill() + + + + //stairs spawn.mapRect(7523, -2313, 800, 75); + ctx.fillRect(8523, -2563, 50, 50) + ctx.fillRect(8473, -2613, 50, 50) + ctx.fillRect(8423, -2663, 50, 50) + ctx.fillRect(8373, -2713, 50, 50) + ctx.fillRect(8323, -2763, 50, 50) + + ctx.fillRect(8323, -2813, 50, 50) + ctx.fillRect(8373, -2863, 50, 50) + ctx.fillRect(8423, -2913, 50, 50) + ctx.fillRect(8473, -2963, 50, 50) + + ctx.fillRect(8523, -3013, 50, 50)//make block + ctx.fillRect(8473, -3063, 50, 50)//make block + ctx.fillRect(8423, -3113, 50, 50)//make block + ctx.fillRect(8373, -3163, 50, 50) + ctx.fillRect(8323, -3213, 50, 50) + + //caves + + ctx.fillStyle = "rgba(30,150,117,255)"//fake slime + //87,189,146,255 + ctx.fillRect(6100, -1900, 100, 1050) + ctx.fillRect(6400, -1900, 100, 1050) + ctx.fillRect(2600, -850, 4700, 200) + ctx.fillRect(7200, -650, 100, 1900) + ctx.fillRect(2399, -1, 200, 400) + + //bunker + ctx.fillStyle = "rgba(0,0,0,0.5)" + + ctx.beginPath() + ctx.moveTo(10800, -2400)//slope of -1/3 + ctx.lineTo(10800, -340) + ctx.lineTo(12980, -340) + ctx.lineTo(12980, -700) + ctx.lineTo(13465, -700) + ctx.lineTo(13541, -1737) + ctx.lineTo(11864.6, -1967.0) + ctx.lineTo(11003, -2400) + ctx.fill() + ctx.fillRect(6100, -2000, 400, 50) + + // -2000 -> 2500 + // Math.random() * 5000 -2500 + ctx.fillStyle = "rgba(0,0,0,0.6)" + ctx.beginPath() + ctx.moveTo(6100, -1700) + ctx.lineTo(5799.5, -800) + ctx.lineTo(2600, -800) + ctx.lineTo(2600, -1700) + ctx.lineTo(5799.5, -1700) + + ctx.moveTo(6500, -1200) + ctx.lineTo(7600, -1200) + ctx.lineTo(8000, 1400) + ctx.lineTo(4600, 1500) + ctx.lineTo(4500.5, 0) + ctx.lineTo(6500, -200) + ctx.lineTo(6500, -1200) + ctx.fill() + + + + + portal[2].query() + portal[3].query() + if (laserbutton.isUp) { + laser.isOn = true; + } else { + laser.isOn = false; + } + + + ctx.fillStyle = "rgba(0,0,0,0.6)" + ctx.fillRect(2113, -791, 500, 75) + ctx.fillRect(1766, -1091, 250, 310) + ctx.fillRect(4473, -2912, 50, 1000) + ctx.fillRect(5673, -2712, 50, 800) + ctx.fillStyle = "rgba(0,0,0,0.2)" + + ctx.fillRect(4523, -2512, 350, 75) + ctx.fillRect(5273, -2212, 400, 75) + + + level.exit.drawAndCheck(); + slime.query(); + slime2.query(); + slime3.query(); + slime4.query(); + + // spawn.mapRect(4873, -2512, 800, 75); + // spawn.mapRect(4473, -2212, 800, 75); + //setTimeout(function(){/*YourCode*/},1000); + + //water falling/flowing effect + ctx.fillStyle = `hsla(160, 100%, 26%,${0.5 + 0.07 * Math.random()})`//lower river + ctx.fillRect(-1800 + Math.random() * 100, 10 + 400 * Math.random(), 3900, 5) + ctx.fillRect(-1800, 10 + 400 * Math.random(), 4400, 5) + + ctx.fillRect(2400 + 200 * Math.random(), Math.random() * - 100 - 2000, 5, 2000)//first waterfall + ctx.fillRect(6100 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050)//twin waterfalls + ctx.fillRect(6400 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050) + + ctx.fillRect(7200 + 100 * Math.random(), -800 - 50 * Math.random(), 5, 2032) + level.enter.draw(); + laserbutton.query(); + laserbutton.draw(); + doortoggle.query(); + if (!doortoggle.isOn) { + door.isClosing = true + bunkerdoor.isClosing = true + + } else { + door.isClosing = false + bunkerdoor.isClosing = false + if (mobsspawned == 0) { + spawn.randomSmallMob(6128.0, 822.6); + spawn.randomSmallMob(6854.8, 560.2); + spawn.randomSmallMob(8320.7, -3402.4); + spawn.randomMob(6629.0, 711.3, 0.8); + spawn.randomMob(8199.2, -2545.5, 0.8); + spawn.randomMob(8067.7, -2957.2, 0.8); + spawn.randomMob(5149.6, -1444.1, 0.8); + + mobsspawned = 1 + + } + + } + door.openClose(); + bunkerdoor.openClose(); + + }; + level.customTopLayer = () => { + door.draw(); + bunkerdoor.draw(); + + laser.opticalQuery(); + if (player.position.y > -70 && player.position.x < 2785) { + if (m.onGround) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - (2 + m.pos.y / 150), + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x - (1 + m.pos.y / 150), + y: player.velocity.y + }); + } + + } + if (player.position.x > 2400 && player.position.x < 2600) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 4 + }); + + + } + boost1.query(); + boost2.query(); + if (player.position.x > 2600 && player.position.x < 4500 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 2, + y: player.velocity.y + }); + } + if (player.position.x > 4500 && player.position.x < 6000 && player.position.y < -1900 && player.position.y > -2121.3) { + + if (input.left) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.1, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.5, + y: player.velocity.y + }); + } + } + if (player.position.x > 6500 && player.position.x < 7500 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 1, + y: player.velocity.y + }); + } + if (player.position.x > 7500 && player.position.x < 10000 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 1, + y: player.velocity.y + }); + } + if (player.position.x > 2600 && player.position.x < 6100 && player.position.y < -650 && player.position.y > -920) { + if (input.right) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 0.2, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 0.4, + y: player.velocity.y + }); + } + } + if (player.position.x > 6500 && player.position.x < 7300 && player.position.y < -650 && player.position.y > -920 && m.onGround) { + if (input.left) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.2, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.4, + y: player.velocity.y + }); + } + } + if (player.position.x > 7200 && player.position.x < 7350 && player.position.y > -950 && player.position.y < 1250) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.8 + }); + } + if (player.position.x > 6100 && player.position.x < 6200 && player.position.y < -800 && player.position.y > -2000) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.3 + }); + } + if (player.position.x > 6400 && player.position.x < 6500 && player.position.y < -800 && player.position.y > -2000) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.3 + }); + } + // ctx.fillRect(7200, -650, 100, 1900) + + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + + + }; + + spawn.mapRect(-100, 0, 1000, 100); + spawn.mapRect(-1800, 400, 4400, 1300); + spawn.mapRect(-1800, 0, 100, 400); + spawn.mapRect(2600, -2000, 3500, 300); + spawn.mapRect(2600, -2000, 500, 800); + spawn.mapRect(2955, -1779, 800, 300); + spawn.mapRect(2600, -800, 2300, 2500); + spawn.mapRect(-460, 100, 1570, 400); + spawn.mapVertex(965, 67, "0 -100 220 0 0 0"); + spawn.mapVertex(-185, 67, "0 -100 -420 0 0 0"); + spawn.mapVertex(1210, 365, "0 -400 300 0 0 0"); + spawn.mapRect(217.5, -358.5, 50, 360); + spawn.mapRect(-83, -358.5, 300, 50); + + //blocks in river/waterfall + + spawn.mapRect(1275, 0, 450, 75); + spawn.mapRect(2027, -388, 600, 75); + spawn.mapRect(1666, -791, 450, 75); + spawn.mapRect(1666, -1091, 450, 75); + //buildings + + spawn.mapRect(4873, -2512, 800, 75); + spawn.mapRect(4473, -2212, 800, 75); + spawn.mapRect(4473, -2912, 50, 800); + spawn.mapRect(5673, -2712, 50, 575); + + spawn.mapRect(6671.5, -2401.4, 500, 50); + spawn.mapRect(6105.1, -2354.1, 400, 50); + + spawn.mapRect(4473, -2952, 8, 75);//1,3,2 + spawn.mapRect(4493, -3032, 15, 150); + spawn.mapRect(4513, -2982, 7, 75); + + spawn.mapRect(5673, -2742, 12, 50); + spawn.mapRect(5703, -2772, 8, 100); + + + //building 2 + // ctx.fillRect(8323, -2363, 50, 50) + + spawn.mapRect(7473, -3412, 50, 800); + spawn.mapRect(7473, -2312, 50, 500); + spawn.mapRect(8673, -3212, 50, 1075); + spawn.mapRect(7523, -2313, 800, 75); + spawn.mapRect(7523, -2763, 800, 75); + spawn.mapRect(7523, -3213, 800, 75); + spawn.mapRect(8725, -2340, 400, 50); + spawn.mapRect(8925, -2640, 200, 50); + spawn.mapRect(8725, -2940, 200, 50); + + //stairs + spawn.mapRect(8323, -2363, 50, 50); + spawn.mapRect(8373, -2413, 50, 50); + spawn.mapRect(8423, -2463, 50, 50); + spawn.mapRect(8473, -2513, 250, 50); + //stairs 2 + spawn.mapRect(8523, -3013, 50, 50)//make block + spawn.mapRect(8473, -3063, 50, 50)//make block + spawn.mapRect(8423, -3113, 50, 50)//make block + //trees in tunnel + spawn.mapRect(4485, -1243, 600, 50) + spawn.mapRect(3967, -1056, 400, 50) + spawn.mapRect(5453, -1150, 50, 300) + spawn.mapRect(5453, -1700, 50, 300) + + + //tunnels and boss + spawn.mapRect(6500, -2000, 3100, 800); + spawn.mapRect(7500, -2000, 3300, 3700); + spawn.mapRect(4900, -800, 2300, 1000); + spawn.mapRect(4354, 1230, 4000, 470); + spawn.mapRect(5388, 863, 100, 500); + spawn.mapRect(5388, 63, 100, 500); + spawn.mapRect(5834, 549, 500, 80); + spawn.mapRect(6756, 897, 400, 80); + + + //extra boss + spawn.mapRect(9196, -11492, 500, 100); + spawn.mapRect(9196, -11492, 500, 100); + + //bunker + spawn.mapRect(11500, -2000, 1900, 500); + spawn.mapRect(10800, -900, 800, 2600); + spawn.mapRect(11600, -340, 1800, 2600); + spawn.mapRect(13400, -2000, 1800, 3600); + spawn.mapRect(10800, -2500, 200, 100); + spawn.mapVertex(11400, -2235, "0 10 900 510 800 510 750 510 0 110"); + + spawn.mapVertex(10100, -2000, "0 0 0 -250 400 0"); + spawn.mapRect(12945.0, -741.9, 600, 50); + spawn.mapRect(12945.0, -741.9, 50, 250); + //stairs + spawn.mapRect(11600, -850, 50, 550); + spawn.mapRect(11650, -800, 50, 500); + spawn.mapRect(11700, -750, 50, 450); + spawn.mapRect(11750, -700, 50, 400); + spawn.mapRect(11800, -650, 50, 350); + spawn.mapRect(11850, -600, 50, 300); + spawn.mapRect(11900, -550, 50, 250); + spawn.mapRect(11950, -500, 50, 200); + spawn.mapRect(12000, -450, 50, 150); + spawn.mapRect(12050, -400, 50, 100); + spawn.mapRect(12100, -350, 50, 50); + + + //mobs + //spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) + + if (Math.random() < 0.5) { + spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) + } else { + spawn.randomLevelBoss(5977, 992) + } + + + //mobs for waterfall and first cavern + //spawn.randomSmallMob(1999.2, -487.4); + spawn.randomMob(1999.2, -487.4, 0.8); + //spawn.randomSmallMob(2080.0, -1206.4); + spawn.randomMob(2080.0, -1206.4, 0.8); + spawn.randomSmallMob(3287.5, -1021.1); + //spawn.randomSmallMob(3992.2, -1223.9); + spawn.randomSmallMob(5018.1, -1483.5); + spawn.randomGroup(6776.2, -3054.5, 0.4); + spawn.randomGroup(4217.4, -1403.6, 0.4); + + + //surface area mobs + spawn.randomSmallMob(5089.0, -2284.1); + spawn.randomSmallMob(6988.3, -2580.2); + spawn.randomSmallMob(7975.0, -2920.3); + spawn.randomMob(5132.0, -2646.2, 0.8); + spawn.randomMob(6365.2, -2459.2, 0.8); + spawn.randomMob(8129.0, -2406.7, 0.8); + spawn.randomMob(8129.0, -2406.7, 0.8); + spawn.randomGroup(2225.3, -1543.2, 0.4); + + + spawn.debris(4426.9, -1433.8, 700, 1); //16 debris per level + spawn.debris(4651.2, -2597.3, 700, 1); //16 debris per level + spawn.debris(9920.9, -2378.3, 700, 2); //16 debris per level + spawn.debris(8298.5, -2883.8, 700, 1); //16 debris per level + spawn.debris(6779.2, -2662.9, 700, 1); //16 debris per level + spawn.debris(6371.5, 442.3, 700, 2); //16 debris per level + spawn.debris(1873.5, -1297.5, 700, 1); //16 debris per level + + spawn.bodyRect(6457.9, -2541.5, 300, 25, 0.9); + //spawn.bodyRect(5685, -2140, 25, 140, 0.9); + spawn.bodyRect(4473, -2110, 50, 110, 0.9); + //spawn.bodyRect(5292.1, -2617.2, 50, 50, 0.9); + spawn.bodyRect(6370.1, -2408.4, 50, 50, 0.9); + //spawn.bodyRect(5467, -1400, 25, 250, 0.9); + + spawn.bodyRect(4509.0, -1425.7, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(8082.9, -2488.1, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + spawn.bodyRect(7859.6, -2883.6, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(5609.5, 948.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + spawn.bodyRect(5803.7, 1125.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(5492.1, 1061.7, 90, 169, 0.9); + spawn.bodyRect(5582.1, 1061.7, 110, 70, 0.9); + //spawn.bodyRect(5582.1, 961.7, 50, 30, 0.9); + + + + + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + dungeon() { + let destroyed = false; + const door = level.door(2650, -825, 50, 250, 250, 10); + const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 }) + const slimePit = level.hazard(-4775, -350, 1975, 175); + const boost = level.boost(137.5, -600, 75, 25); + let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + dong = Matter.Body.create({ + parts: [base, left, right, left2, right2] + }); + body[body.length] = base; + body[body.length] = left; + body[body.length] = right; + body[body.length] = left2; + body[body.length] = right2; + Matter.Composite.add(engine.world, dong) + Matter.Composite.add(engine.world, Constraint.create({ + pointA: { x: -3825, y: -975 }, + bodyB: dong, + stiffness: 0.2, + damping: 0.1 + })); + composite[composite.length] = dong; + setTimeout(function () { + dong.collisionFilter.category = cat.body; + dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map + }, 1000); + level.custom = () => { + ctx.save() + ctx.beginPath() + ctx.fillStyle = "#80808077"; + ctx.strokeStyle = "#80808022"; + ctx.fillRect(225, -1025, 2400, 450); + ctx.fillRect(-2950, -1025, 3100, 450); + ctx.fillRect(-7050, -1025, 2400, 450); + ctx.fillRect(-10575, -3975, 4525, 1025); + ctx.fillRect(-4650, -1700, 1700, 1100); + ctx.fillRect(-11150, -3575, 575, 3050); + ctx.fillRect(-11900, -1000, 750, 475); + ctx.fill() + ctx.stroke() + ctx.restore() + ctx.save() + ctx.beginPath() + ctx.fillStyle = "#d8dadf"; + ctx.strokeStyle = "#d8dadf"; + ctx.moveTo(-2950, -600); + ctx.lineTo(-3730, -1725); + ctx.lineTo(-3730, -600); + + ctx.moveTo(-4650, -600); + ctx.lineTo(-3925, -1725); + ctx.lineTo(-3925, -575); + + ctx.moveTo(-10575, -3425); //NE section + ctx.lineTo(-10100, -2975); + ctx.lineTo(-10575, -2975); + + // ctx.moveTo(-7625, -3800); + // ctx.lineTo(-6750, -2975); + // ctx.lineTo(-7625, -2975); + + ctx.moveTo(-7975, -2975); + ctx.lineTo(-7625, -3800); + ctx.lineTo(-7350, -2950); + + ctx.moveTo(-6750, -2975); + ctx.lineTo(-7075, -3800); + ctx.lineTo(-7350, -2950); + + // ctx.moveTo(-7975, -2975); + // ctx.lineTo(-7075, -3800); + // ctx.lineTo(-7075, -2975); + + ctx.moveTo(-11900, -950); + ctx.lineTo(-11900, -550); + ctx.lineTo(-11500, -550); + + ctx.fillRect(-3925, -1675, 200, 1075); + ctx.fillRect(-7625, -3800, 550, 875); + ctx.clearRect(-10600, -4000, 525, 475); + ctx.clearRect(-10100, -4000, 500, 300); + ctx.clearRect(-9625, -4000, 500, 175); + ctx.fillRect(-11125, -3600, 550, 50); + ctx.fillRect(-10600, -3400, 50, 425); + ctx.fillRect(-11925, -925, 45, 375); + ctx.fillRect(-3950, -1675, 75, 1100); + ctx.fillRect(-3925, -625, 950, 50); + ctx.fillRect(-4650, -600, 1700, 375); + ctx.fillRect(-14550, -2400, 2650, 2050); + //ctx.clearRect(-11050, -3000, 475, 50); + + ctx.moveTo(-11150, -3575); + ctx.lineTo(-10575, -2150); + ctx.lineTo(-10575, -3575); + + ctx.stroke() + ctx.fill() + ctx.restore() + boost.query() + slimePit.query() + if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty); + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(dong, [mob[i]]).length > 0) { + const dmg = 1; + mob[i].damage(dmg, true); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + break + } + } + level.exit.drawAndCheck(); + ctx.beginPath() + ctx.fillStyle = '#68686822'; + ctx.fillRect(-25, -2175, 100, 200); + ctx.fill() + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + ctx.moveTo(-3825, -975) + ctx.lineTo(dong.position.x, dong.position.y) + ctx.stroke(); + ctx.setLineDash([]); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: 10, + color: color.block, + time: 20 + }); + ctx.beginPath() + ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-3000, -2175, 175, 25); + ctx.fillRect(-2850, -2300, 25, 150); + ctx.fillRect(-3000, -2300, 175, 25); + ctx.fillRect(-3000, -2425, 25, 150); + ctx.fillRect(-3000, -2425, 175, 25); + ctx.fill() + ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-2725, -2425, 25, 275); + ctx.fillRect(-2725, -2425, 175, 25); + ctx.fillRect(-2575, -2425, 25, 275); + ctx.fillRect(-2725, -2300, 175, 25); + ctx.fill() + ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-2450, -2425, 25, 275); + ctx.fillRect(-2450, -2175, 175, 25); + ctx.fill() + ctx.stroke(); + ctx.fillStyle = `#00FFFF22`; + ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100) + ctx.fill() + ctx.fillStyle = `#00FFFF66` + ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) + ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) + ctx.fillStyle = `rgba(68, 68, 68)` + ctx.fillRect(-7675, -3075, 50, 125); + ctx.fillRect(-7075, -3075, 50, 125); + ctx.fillRect(-7725, -3025, 75, 75); + ctx.fillRect(-7050, -3025, 75, 75); + ctx.fill() + for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically + if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) { + body[i].force.y -= simulation.g * body[i].mass + 0.012; + } + } + for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically + if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) { + bullet[i].force.y -= simulation.g * bullet[i].mass; + } + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically + if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) { + powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12; + } + } + for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically + if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) { + mob[i].force.y -= simulation.g * mob[i].mass + 0.0012; + } + } + if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) { + player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2); + + } + elevator.move() + }; + level.setPosToSpawn(30, -2000); //normal spawn + level.exit.x = 2775; + level.exit.y = -650; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom + 800) + document.body.style.backgroundColor = "#d8dadf"; + spawn.mapRect(-225, -1950, 350, 75); + spawn.mapRect(225, -1950, 50, 75); + spawn.mapRect(-250, -2025, 50, 150); + spawn.mapRect(250, -2025, 50, 150); + spawn.mapRect(-250, -2250, 50, 125); + spawn.mapRect(-225, -2325, 500, 100); + spawn.mapRect(250, -2250, 50, 125); + spawn.mapRect(-100, -2400, 250, 100); + spawn.mapRect(-25, -2475, 100, 100); + spawn.mapRect(125, -2350, 50, 50); + spawn.mapRect(-125, -2350, 50, 50); + spawn.mapRect(-50, -2425, 50, 50); + spawn.mapRect(50, -2425, 50, 50); + spawn.mapRect(-250, -2350, 50, 50); + spawn.mapRect(250, -2350, 50, 50); + spawn.mapRect(-75, -1975, 200, 50); + spawn.mapRect(-50, -2000, 150, 50); + spawn.mapRect(100, -1950, 50, 75); + spawn.mapRect(-75, -2250, 200, 50); + spawn.mapRect(-50, -2225, 150, 50); + spawn.mapRect(-2950, -1900, 3100, 900); + spawn.mapRect(225, -1900, 2875, 900); + spawn.mapRect(-2950, -600, 6050, 450); + spawn.mapRect(-3050, -500, 200, 350); + spawn.mapRect(-3150, -400, 200, 250); + spawn.mapRect(-3250, -300, 200, 150); + spawn.mapRect(2950, -1050, 150, 500); + spawn.mapRect(-4675, -1900, 1825, 200); + spawn.mapRect(-5325, -1900, 675, 900); + spawn.mapRect(-5325, -250, 2100, 100); + spawn.mapRect(-5325, -600, 675, 450); // - + spawn.mapRect(-4700, -500, 150, 350); + spawn.mapRect(-4650, -400, 200, 250); + spawn.mapRect(-4550, -300, 200, 150); + spawn.mapRect(-3875, -1025, 100, 100); + spawn.mapRect(-3800, -1050, 50, 50); + spawn.mapRect(-3900, -1050, 50, 50); + spawn.mapRect(-3800, -950, 50, 50); + spawn.mapRect(-3900, -950, 50, 50); + spawn.mapRect(-6925, -1175, 1700, 175); + spawn.mapRect(-6925, -600, 1725, 175); + spawn.mapRect(-7700, -600, 800, 425);// - + spawn.mapRect(-7800, -2950, 175, 2775); + spawn.mapRect(-7075, -2950, 175, 1950); + spawn.mapRect(-9150, -2975, 1525, 175); + spawn.mapRect(-7075, -2975, 1150, 175); + spawn.mapRect(-6100, -3900, 175, 1100); + spawn.mapRect(-9150, -3975, 3225, 175); + spawn.mapRect(-9175, -3850, 75, 75); + spawn.mapRect(-9625, -3825, 500, 150); + spawn.mapRect(-9650, -3725, 75, 75); + spawn.mapRect(-10100, -3700, 500, 150); + spawn.mapRect(-10100, -2975, 975, 175); + spawn.mapRect(-10125, -3600, 75, 75); + spawn.mapRect(-10575, -3575, 500, 150); + spawn.mapRect(-10575, -2975, 500, 175); + spawn.mapRect(-11325, -2975, 250, 175); + spawn.mapRect(-11325, -3575, 175, 775); + // spawn.mapRect(-11325, -3575, 800, 150); + spawn.mapRect(-11225, -2975, 150, 2000); + spawn.mapRect(-10575, -2975, 150, 2500); + spawn.mapRect(-11650, -550, 1225, 150); + spawn.mapRect(-11650, -1100, 575, 150); + spawn.mapRect(-14675, -2525, 2925, 150); + spawn.mapRect(-11900, -2525, 150, 1575); + spawn.mapRect(-11850, -1100, 250, 150); + spawn.mapRect(-11875, -550, 275, 150); + spawn.mapRect(-11900, -550, 150, 350); + spawn.mapRect(-14675, -2525, 150, 2300); + spawn.mapRect(-14675, -375, 2925, 175); + spawn.mapRect(2725, -625, 250, 50); + spawn.mapRect(2625, -1025, 100, 225); + spawn.mapRect(2700, -1025, 300, 125); + spawn.mapRect(2625, -612.5, 125, 50); + spawn.mapRect(-3950, -1725, 250, 50); + spawn.mapRect(-7650, -3825, 600, 50); + spawn.mapRect(-13900, -2400, 200, 50); + spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0'); + spawn.mapVertex(-14470, -430, '175 175 0 175 0 0'); + spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0'); + spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175'); + //spawn.mapRect(-13900, -2150, 1375, 125); + const sword = function () { //the ultimate blade of las destruction + mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent"); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 1); + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + me.collisionFilter.category = cat.bullet; + me.collisionFilter.mask = cat.mob | cat.mobBullet; + me.isDropPowerUp = false; + me.isShielded = true; + me.showHealthBar = false; + me.isUnblockable = true; + me.leaveBody = false; + me.isBadTarget = true; + me.stroke = "transparent"; + me.isSword = true; + let index = 0; + let radius = 50; + me.do = function () { + this.health = Infinity;//just in case + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) { + const dmg = 0.25;//do not nerf + mob[i].damage(dmg, true); + simulation.drawList.push({ //add dmg to draw queue + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + break + } + } + Matter.Body.setPosition(this, { + x: player.position.x + Math.cos(m.angle) * 100, + y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100 + }) + Matter.Body.setAngle(this, m.angle + Math.PI * 2); + const setNoseShape = () => { + const mag = radius + radius * 10; + this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; + this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; + this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; + }; + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100) + const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500) + const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500) + this.vertices[2].x = this.position.x + spike.x / 100 + this.vertices[2].y = this.position.y + spike.y / 100 + this.vertices[4].x = this.position.x + spike2.x / 75 + this.vertices[4].y = this.position.y + spike2.y / 75 + this.vertices[0].x = this.position.x + spike3.x / 75 + this.vertices[0].y = this.position.y + spike3.y / 75 + if (index == 0) { + setNoseShape(); + index++; + } + ctx.save() + ctx.beginPath(); + const vertices = this.vertices; + ctx.lineWidth = 100; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15); + // Add three color stops + gradient.addColorStop(0, m.eyeFillColor); + gradient.addColorStop(0.9, "white"); + gradient.addColorStop(1, "darkgray"); + ctx.fillStyle = gradient; + ctx.strokeStyle = "transparent"; + ctx.shadowBlur = 10; + ctx.shadowColor = m.eyeFillColor; + ctx.fill(); + ctx.stroke(); + ctx.restore() + + const Dx = Math.cos(m.angle); + const Dy = Math.sin(m.angle); + let xElec = this.position.x + 10 * Dx; + let yElec = this.position.y + 10 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + ctx.strokeStyle = m.eyeFillColor; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + if (this.alive && m.energy > 0) { + const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50))); + // Add three color stops + gradient.addColorStop(0, m.eyeFillColor); + gradient.addColorStop(0.9, "white"); + gradient.addColorStop(1, "gray"); + ctx.save() + ctx.beginPath() + ctx.moveTo(this.position.x, this.position.y) + ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI) + ctx.fillStyle = gradient; + ctx.strokeStyle = "transparent"; + ctx.shadowBlur = 10; + ctx.shadowColor = m.eyeFillColor; + ctx.fill() + ctx.stroke() + ctx.restore() + m.energy -= 0.002; + + ctx.save() + ctx.translate(this.vertices[2].x, this.vertices[2].y) + ctx.rotate(m.angle + Math.PI / 2) + ctx.beginPath() + ctx.font = "16px Arial"; + ctx.fillStyle = "black"; + ctx.strokeStyle = "black"; + // ctx.fillText("Θ", 0,0 - 110) + // ctx.fillText("ά", 0,15 - 110) + // ctx.fillText("ν", 0,30 - 110) + // ctx.fillText("α", 0,45 - 110) + // ctx.fillText("τ", 0,60 - 110) + // ctx.fillText("ο", 0,75 - 110) + // ctx.fillText("ς", 0,90 - 110) + ctx.fillText("Ω", 0, 55) + ctx.fill() + ctx.stroke() + ctx.restore() + + simulation.drawList.push({ + x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300), + y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300), + radius: 2, + color: m.eyeFillColor, + time: simulation.drawTime + }); + } else { + this.death() + powerUps.activated = false + } + } + } + //setTimeout(function() {sword();}, 100); + const wire = function () { + const breakingPoint = -1600; + const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100); + mobs.spawn(spawnx, -2375, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + let boss = mob[0]; + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + me.restitution = 0; + me.stroke = "transparent" + me.freeOfWires = false; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + const wireX = spawnx; + const wireY = -2375; + //const randomw = Math.floor(Math.random() * 100 - Math.random() * 100); + const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10)); + const randomx = Math.floor(30 * Math.random() - 30 * Math.random()); + const randomy = Math.floor(10 * Math.random() - 10 * Math.random()) + me.do = function () { + if (this.freeOfWires) { + this.gravity(); + } else { + if (boss.position.y > breakingPoint) { + this.freeOfWires = true; + this.force.y -= -0.0006; + this.force.x += Math.random() * boss.velocity.x / 10000; + this.fill = "#111"; + } + //move mob to mob + Matter.Body.setPosition(this, { + x: boss.position.x + randomx, + y: boss.position.y + randomy + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y); + ctx.lineWidth = width; + ctx.lineCap = "butt"; + ctx.strokeStyle = "#111"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + } + + const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs? + mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(me, 0.00001); //normal is 0.001 + me.timeLeft = 500; + me.friction = 0; + me.restitution = 1; + me.leaveBody = false; + me.isDropPowerUp = false; + //me.inertia = Infinity; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; + let index = 0; + me.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + this.force.y += this.mass * 0.0012; + } + } + const normalBullet = function (x, y, radius = 9, sides = 3) { + //bullets + mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + me.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + me.timeLeft = 220; + me.frictionAir = -0.01; + me.restitution = -1; + me.leaveBody = false; + me.isDropPowerUp = false; + //me.inertia = Infinity; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = null; + let index = 0; + me.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + const setNoseShape = () => { + const mag = this.radius + this.radius * 10; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + Matter.Body.setAngle(this, angle); + }; + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000) + this.vertices[1].x = this.position.x + spike.x / 100 + this.vertices[1].y = this.position.y + spike.y / 100 + if (index == 0) { + setNoseShape(); + index++; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { + const slow = 0.69 //im sorry it looks cool though + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 10, + color: '#000000', + time: simulation.drawTime + }); + if (this.velocity.x == 0 && this.velocity.y == 0) { + this.death(); + } + this.frictionAir += 0.0001; + Matter.Body.setAngularVelocity(this, 0) + } + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this + const dmg = 0.013 * simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: '#000000', + time: simulation.drawTime + }); + } + }; + } + const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets + mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + me.timeLeft = 120; + // me.g = 0.0005; //required if using this.gravity + me.accelMag = 0.00006; + me.isVerticesChange = true + me.delay = 360 * simulation.CDScale; + me.spikeVertex = 0; + me.spikeLength = 0; + me.isSpikeGrowing = false; + me.spikeGrowth = 0; + me.isSpikeReset = false; + me.frictionAir = 0; + me.restitution = 0; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.isUnblockable = true; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player; + me.do = function () { + if (this.distanceToPlayer2() < 40000) { + this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004) + const slow = 0.99999999999999999; + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + } + // this.gravity(); + this.timeLimit(); + // for (let i = 0, len = this.vertices.length; i < len; i++) { + // const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); + // const distMag = Vector.magnitude(dist); + // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag) + // this.vertices[i].x = this.position.x + spike.x / 100 + // this.vertices[i].y = this.position.y + spike.y / 100 + // } + if (this.radius < 50) { + const scale = 1.05; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { + const slow = 0.97 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.9 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + if (this.radius < 1) { + this.death() + } + } else { + this.attach(); + } + + }; + me.attach = function () { + if (Matter.Query.collides(this, [player]).length > 0) { + Matter.Body.setPosition(this, player.position) + if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) + Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9); + m.damage(0.00003); //balanced? not sure + } + } + }; + + const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot + mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.01); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.showHealthBar = false; + me.isOrbital = true; + me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2))) + //damage player + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.013 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + //this.death(); + } + }; + } + const sniper = function (x, y, radius = 30) { + mobs.spawn(x, y, 8, radius, '#00000000'); + let me = mob[mob.length - 1]; + me.accelMag = 0.0003 + me.stroke = 'transparent'; + //me.isBoss = true; + me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, 0.01) + me.fireDir = { x: 0, y: 0 } + me.seePlayerFreq = 0 + me.repulsionRange = 400000 + radius * radius; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.do = function () { + this.seePlayerCheck(); + this.attraction(); + this.repulsion(); + this.search() + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,255,255,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + if (this.health < 1) { + this.health += 0.0005; //regen + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + //set direction to turn to fire + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.seePlayer.recall -= 10; + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + }; + } + const laserEM = function (x, y, radius = 30) { + mobs.spawn(x, y, 8, radius, '#00000000'); + let me = mob[mob.length - 1]; + me.accelMag = 0.0003 + me.stroke = 'transparent'; + //me.isBoss = true; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, 0.01) + me.seePlayerFreq = 0 + me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; + me.swordDamage = 0.025 * simulation.dmgScale + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.repulsionRange = 50000; + me.do = function () { + this.repulsion(); + this.search() + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,255,255,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + if (this.health < 1) { + this.health += 0.0005; //regen + } + if (this.seePlayer.recall) { + this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + this.seePlayerCheck(); + this.attraction(); + } + me.laserSword = function (where, angle, length) { + const sub = Vector.sub(this.seePlayer.position, this.position) + const unit = Vector.normalise(sub) + const path = [{ + x: this.position.x + 20 * Math.cos(this.angle), + y: this.position.y + 20 * Math.sin(this.angle) + }, + { + x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), + y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) + } + ]; + this.seePlayer.recall -= 3; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; + // vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(80,0,255,0.07)"; + ctx.stroke(); // Draw it + + const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + let xElec = this.position.x + 40 * Dx; + let yElec = this.position.y + 40 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }; + if (powerUps.pass == undefined) { + let pass = { pass: true, activated: false }; + Object.assign(powerUps, pass) + } + const loadOut = { + loadOut: { + name: "loadOut", + color: "#000000", //"hsl(248,100%,65%)", + size() { return 40 }, + effect() { + if (m.alive) { + // tech.damage *= 2; + let text = ""; + if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?" : "✕"}
`; }; + text += `

Blessing Of Sal

`; + text += `
  Speed Boost
Increase speed by 5%
`; + text += `
  Defense Boost
Reduce damage by 5%
`; + text += `
  Damage Boost
Increase damage by 10%
`; + if (powerUps.pass == true) { + text += `
  Blade of Sal
Press Shift to summon the Mythical Las Slayer
Drains Energy
`; + } + document.getElementById("choose-grid").innerHTML = text; + powerUps.showDraft();//no known bugs ig idk, im keep this as it is + } + }, + choose(index) { + if (index == 1) { + tech.squirrelFx += 0.25; + tech.squirrelJump += 0.1; + m.setMovement(); + powerUps.endDraft("buff"); + } else if (index == 2) { + simulation.dmgScale *= 0.95; + powerUps.endDraft("buff"); + } else if (index == 3) { + m.dmgScale *= 1.1; + powerUps.endDraft("buff"); + } else if (index == 4) { //sword! + powerUps.pass = false; + addEventListener("keydown", function (event) { + if (event.key == "Shift" && powerUps.activated == false) { + sword() + powerUps.activated = true; + } else if (event.key == "Shift" && powerUps.activated == true) { + for (let i = 0; i < mob.length; i++) { + if (mob[i].isSword) { + mob[i].death() + } + powerUps.activated = false; + } + } + }) + powerUps.endDraft("buff"); + } + } + } + } + Object.assign(powerUps, loadOut) + const restoreBoss = function (x, y, radius = 30) { + mobs.spawn(x, y, 8, radius, 'transparent'); + let me = mob[mob.length - 1]; + me.stroke = 'transparent'; + let aim = '#FFFFFF'; + me.accelMag = 0.0006 + me.isBoss = true; + me.restoreBoss = true; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode)) + me.seePlayerFreq = 0 + me.swordDamage = 0.025 * simulation.dmgScale + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.repulsionRange = 500000; + me.isDropPowerUp = false; + //Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + let index = 0; + me.energy = 1; + me.maxEnergy = 1; + me.immuneCycle = 0; + me.cycle = 0; + me.onDeath = function () { + powerUps.spawn(this.position.x, this.position.y, "loadOut"); + } + for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so... + orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed + index++; + } + me.do = function () { + this.cycle++; + if (this.seePlayer.recall) { //fields + if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) { + if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) { + this.pushM(); + } + this.drawField(); + // this.repel(); + } + if (m.fieldMode == 2) { + if (this.distanceToPlayer2() < 200000) { + if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) { + this.pushM(); + } + this.drawField() + } + if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair... + const wave = Math.cos(m.cycle * 0.022); + const range = 200 + 140 * wave + 150 * m.energy + const distance = Vector.magnitude(Vector.sub(this.position, m.pos)) + const cap = this.immuneCycle > this.cycle ? 8 : 4 + if (distance < range) { + if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction + } + } + for (let i = 0; i < bullet.length; i++) { + const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position)) + if (distance2 < range) { + if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.08)"; + ctx.fill(); + } + } + if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) { + this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); + } + if (m.fieldMode == 6) { + this.timeAttack(); + } + if (m.fieldMode == 9) { + if (this.distanceToPlayer2() < 300000) { + this.teleportAway() //blink but reversed + } + } + } + if (m.immuneCycle > m.cycle) { + me.damageReduction = 0; + } else { + me.damageReduction = 1; + } + this.repulsion(); + this.seePlayerCheck(); + this.attraction(); + this.lostPlayer(); + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(10, 10, 10, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(x, y, w * this.energy, h); + } + if (this.health < 1) { + this.health += 0.0001; //regen + } else if (this.health < 1) { + this.health += 0.00005; //reduced regen + } + if (this.energy < 0) {//energy thingy + this.energy = 0; + } else if (this.energy > this.maxEnergy) { + this.energy = this.maxEnergy; + } else if (this.energy < this.maxEnergy) { + this.energy += 0.001; + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.health > 0.5) { + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + } else { + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -6, 7, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 10, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#555"; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(3, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(26, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + } + if (m.fieldMode == 1) { //render over I think + if (this.energy > 0.1) { + this.harmonic3Phase(); + } + } + if (m.fieldMode == 3) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + me.damageReduction = 0.5; + me.accelMag = 0.0012; + if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) { + this.diveAttack() + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.strokeStyle = "#f5f5ff55"; + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.stroke() + ctx.setLineDash([]); + } else { + me.accelMag = 0.0006; + } + if (this.immuneCycle > this.cycle) { + this.damageReduction = 0; + } else { + if (m.fieldMode == 3) { + this.damageReduction = 0.5; + } else { + this.damageReduction = 1; + } + } + if (this.seePlayer.recall) { //fields + this.gun() + } + } + me.laserSword = function (where, angle, length) { + const sub = Vector.sub(this.seePlayer.position, this.position) + const unit = Vector.normalise(sub) + const path = [{ + x: this.position.x + 20 * Math.cos(this.angle), + y: this.position.y + 20 * Math.sin(this.angle) + }, + { + x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), + y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) + } + ]; + this.seePlayer.recall -= 3; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; + // vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(0,0,0,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(0,0,0,0.07)"; + ctx.stroke(); // Draw it + + const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + let xElec = this.position.x + 40 * Dx; + let yElec = this.position.y + 40 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + me.drawField = function () { + if (m.fieldMode != 2) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + const range = m.fieldRange; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2; + ctx.stroke(); + let eye = 13; + if (m.fieldMode == 2) { + eye = 30 + } + let aMag = 0.75 * Math.PI * m.fieldArc + let a = angle + aMag + let cp1x = this.position.x + 0.6 * range * Math.cos(a) + let cp1y = this.position.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)) + a = angle - aMag + cp1x = this.position.x + 0.6 * range * Math.cos(a) + cp1y = this.position.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc)) + ctx.fill(); + // ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); + + //draw random lines in field for cool effect + let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5); + ctx.beginPath(); + eye = 15; + ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); + ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle)); + ctx.strokeStyle = "rgba(0,0,0,0.6)"; + ctx.lineWidth = 1; + ctx.stroke(); + } else { + ctx.beginPath(); + const wave = Math.cos(m.cycle * 0.022); + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = angle + aMag + let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) + let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) + a = angle - aMag + cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) + cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc)) + ctx.fill(); + } + } + me.pushM = function () { + const unit = Vector.normalise(Vector.sub(this.position, player.position)) + if (tech.blockDmg) { + Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y }); + //draw electricity + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { + let x = this.position.x - 20 * unit.x; + let y = this.position.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + m.damage(0.025 * simulation.dmgScale) + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#000"; + ctx.stroke(); + } + const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk + Matter.Body.setVelocity(player, { + x: this.velocity.x - (15 * unit.x) / massRoot, + y: this.velocity.y - (15 * unit.y) / massRoot + }); + } + me.diveAttack = function () { + const forceMag = this.accelMag * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += 150 * forceMag * Math.cos(angle); + this.force.y += 150 * forceMag * Math.sin(angle); + ctx.beginPath() + ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle)) + ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y) + aim = '#000000'; + ctx.stroke() + } + me.teleportAway = function () {//hehe + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (this.seePlayer.recall && !(simulation.cycle % 17)) { + const dist = Vector.sub(this.position, this.seePlayer.position); + const distMag = Vector.magnitude(dist); + const unitVector = Vector.normalise(dist); + const rando = (Math.random() - 0.5) * 50; + if (distMag < 20000) { + Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); + } else { + Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando)); + } + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = "rgba(0,0,0,0.08)"; + ctx.stroke(); + if (!this.seePlayer.yes) { + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (this.seePlayer.recall && !(simulation.cycle % 17)) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + const unitVector = Vector.normalise(dist); + const rando = (Math.random() - 0.5) * 50; + if (distMag < 200000) { + Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); + } else { + Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando)); + } + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = "rgba(0,0,0,0.08)"; + ctx.stroke(); + } + } + me.timeAttack = function () { + if (!simulation.isTimeSkipping || input.field) { + requestAnimationFrame(() => { + simulation.timePlayerSkip(5) + m.walk_cycle += m.flipLegs * m.Vx * 5 + }); //just doing what lilgreenland did + } + } + me.harmonic3Phase = function () { //normal standard 3 different 2-d circles + if (tech.harmonics === 2) { + const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) { + me.pushM(); + } + for (let i = 0; i < bullet.length; i++) { + if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < m.fieldRange) { + const angle = Math.atan2(dy, dx); + const mag = (1500 * bullet[i].mass * simulation.g) / dist; + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + this.energy -= 0.0012; + } + } + } else { + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(0,0,0,0.5)" + ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`; + // ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + //360 block + if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) { + me.pushM(); + } + for (let i = 0; i < bullet.length; i++) { + if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < m.fieldRange) { + const angle = Math.atan2(dy, dx); + const mag = (1500 * bullet[i].mass * simulation.g) / dist; + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + this.energy -= 0.0012; + } + } + } + } + me.railGun = function () { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const X = this.position.x + const Y = this.position.y + const unitVector = { x: Math.cos(angle), y: Math.sin(angle) } + const unitVectorPerp = Vector.perp(unitVector) + + function magField(mag, arc) { + ctx.moveTo(X, Y); + ctx.bezierCurveTo( + X + unitVector.x * mag, Y + unitVector.y * mag, + X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, + X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) + ctx.bezierCurveTo( + X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, + X - unitVector.x * mag, Y - unitVector.y * mag, + X, Y) + } + ctx.fillStyle = `rgba(50,0,100,0.05)`; + const magSize = 8 * c * tech.railChargeRate ** 3 + const arcSize = 6 * c * tech.railChargeRate ** 3 + for (let i = 3; i < 7; i++) { + const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) + const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) + ctx.beginPath(); + magField(MAG, ARC) + magField(MAG, -ARC) + ctx.fill(); + } + } + me.waves = []; + me.doLongitudinal = function () { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + // const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767 + const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767 + const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this + for (let i = this.waves.length - 1; i > -1; i--) { + const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) + const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) + //draw wave + ctx.moveTo(v1.x, v1.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); + //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector + let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + player.force.x += 0.01 * (Math.random() - 0.5) * player.mass + player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity + Matter.Body.setVelocity(player, { //friction + x: player.velocity.x * 0.95, + y: player.velocity.y * 0.95 + }); + m.damage(damage) + } + hits = Matter.Query.ray(body, v1, v2, 50) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + const who = hits[j].body + //make them shake around + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity + + let vertices = who.vertices; + const vibe = 25 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + // ctx.stroke(); //draw vibes + this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move + if (this.waves[i].radius > end - 30) { + this.waves[i].expanding = -1 + if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end + } else if (this.waves[i].radius < 25) { + this.waves[i].expanding = 1 + if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + } + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + const dmg = 0.0011 * simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.lineWidth = 10; + ctx.stroke(); + } + me.pulse = function (charge, angle, where = this.position) { + let best; + angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + let explosionRadius = 5.5 * charge + let range = 5000 + const path = [{ + x: where.x + 20 * Math.cos(angle), + y: where.y + 20 * Math.sin(angle) + }, + { + x: where.x + range * Math.cos(angle), + y: where.y + range * Math.sin(angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + if (!best.who) { + vertexCollision(path[0], path[1], body); + vertexCollision(path[0], path[1], [player]); + vertexCollision(path[0], path[1], map); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + } + if (best.who) { + b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)") + const off = explosionRadius * 1.2 + b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") + b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") + } + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + if (charge > 50) { + ctx.strokeStyle = "rgba(0,0,0,0.10)" + ctx.lineWidth = 70 + ctx.stroke(); + } + ctx.strokeStyle = "rgba(0,0,0,0.25)" + ctx.lineWidth = 20 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); + + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) { + const dist = Math.random() + simulation.drawList.push({ + x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5), + radius: 1.5 + 5 * Math.random(), + color: "rgba(0,0,0,0.5)", + time: Math.floor(9 + 25 * Math.random() * Math.random()) + }); + } + } + let c = 0 + me.gun = function () { + if (b.activeGun == 0) {// nailgun + if (this.seePlayer.recall && !(simulation.cycle % 20)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + normalBullet(this.position.x, this.position.y); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } + if (b.activeGun == 1) {// shotgun + if (this.seePlayer.recall && !(simulation.cycle % 90)) { + const side = 22 + for (let i = 0; i < 12; i++) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const dir = angle + (Math.random() - 0.5) * 1 + const SPEED = 52 + Math.random() * 8 + normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5)) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + } + } + } else if (b.activeGun == 2) { // super balls + if (this.seePlayer.recall && !(simulation.cycle % 20)) { + const num = 3; + const SPREAD = 0.13; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + let dir = angle - SPREAD * (num - 1) / 2; + const SPEED = 33 + for (let i = 0; i < num; i++) { + ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + dir += SPREAD + } + } + } else if (b.activeGun == 3) { // wave + this.doLongitudinal() + const halfArc = 0.275 + const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const angle = anglex + 0.3 * (Math.random() - 0.5) + this.waves.push({ + position: { + x: this.position.x + 25 * Math.cos(anglex), + y: this.position.y + 25 * Math.sin(anglex), + }, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + reflection: 0, + expanding: 1, + resonanceCount: 0 + }) + } else if (b.activeGun == 4) { // missiles + + } else if (b.activeGun == 5) { // grenades + + } else if (b.activeGun == 6) { // spores + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + me.drop(this.position.x, this.position.y) + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 7) { // drones + + } else if (b.activeGun == 8) { // foam + if (this.seePlayer.recall && !(simulation.cycle % 1)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69); + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 9) { // harpoon - railgun + if (c > 1) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + this.railBullet(this.position.x, this.position.y); + const v = 10 + 80 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + c = 0; + } else { + c += 0.02; + this.railGun(); + } + } else if (b.activeGun == 10) { // laserMines + if (this.seePlayer.recall && !(simulation.cycle % 100)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + me.laserMine(this.position.x, this.position.y) + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 11) { // laser - pulse + //this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + //if (this.seePlayer.recall && !(simulation.cycle % 20)) { + if (c > 1) { + this.pulse(c * 100) + c = 0; + } else { + if (this.energy < 1 || this.energy > 0.5) { + c += 0.01; + ctx.beginPath(); + const mag = Math.sqrt(c) + ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI) + ctx.fillStyle = '#000000' + ctx.strokeStyle = 'transparent' + ctx.fill(); + ctx.stroke(); + this.energy -= 0.01; + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 1.5 + // ctx.globalAlpha = 1; + } else { + c = 0; + this.energy += 0.1 + } + } + //} + } + } + me.railBullet = function (x, y) { + mobs.spawn(x, y, 5, 20, "black"); + let xy = mob[mob.length - 1]; + xy.stroke = "black"; + xy.vertices = Matter.Vertices.rotate(xy.vertices, Math.PI, xy.position); + xy.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(xy, 0.00004); //normal is 0.001 + xy.timeLeft = 220; + xy.frictionAir = -0.01; + xy.restitution = -1; + xy.leaveBody = false; + xy.isDropPowerUp = false; + //xy.inertia = Infinity; + xy.isBadTarget = true; + xy.isMobBullet = true; + xy.showHealthBar = false; + xy.collisionFilter.category = cat.mobBullet; + xy.collisionFilter.mask = cat.player; + let index = 0; + xy.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + const setNoseShape = () => { + const mag = this.radius + this.radius * 10; + const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x); + this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; + this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; + this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; + Matter.Body.setAngle(this, angle); + }; + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 1000) + const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 1000) + const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 1000) + this.vertices[2].x = this.position.x + spike.x / 100 + this.vertices[2].y = this.position.y + spike.y / 100 + this.vertices[4].x = this.position.x + spike2.x / 75 + this.vertices[4].y = this.position.y + spike2.y / 75 + this.vertices[0].x = this.position.x + spike3.x / 75 + this.vertices[0].y = this.position.y + spike3.y / 75 + if (index == 0) { + setNoseShape(); + index++; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { + const slow = 0.69 //im sorry it looks cool though + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: 10, + // color: '#000000', + // time: simulation.drawTime + // }); + if (this.velocity.x == 0 && this.velocity.y == 0) { + this.death(); + } + this.frictionAir += 0.0001; + Matter.Body.setAngularVelocity(this, 0) + } + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this + const dmg = 0.013 * simulation.dmgScale; + m.damage(dmg); + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: Math.sqrt(dmg) * 200, + // color: '#000000', + // time: simulation.drawTime + // }); + } + }; + } + me.laserMine = function (x, y) { + mobs.spawn(x, y, 3, 20, "#000000"); + let xx = mob[mob.length - 1]; + xx.stroke = "#00000000"; + Matter.Body.setDensity(xx, 0.000005) //one tap + xx.isUnstable = true; + xx.timeLeft = 40 + Math.floor(180 * Math.random()) + xx.leaveBody = false; + xx.isDropPowerUp = false; + xx.collisionFilter.mask = cat.bullet | cat.player + xx.showHealthBar = false; + //xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position); + me.onHit = function () { + this.death(); + }; + xx.do = function () { + this.timeLimit(); + Matter.Body.setAngularVelocity(this, 0.01) + ctx.beginPath(); + ctx.lineWidth = 1; + ctx.strokeStyle = "#00000000" + for (let i = 0; i < this.vertices.length; i++) { + const where = this.vertices[i] + const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500)) + me.lasers(this.vertices[0], this.angle + Math.PI / 3); + me.lasers(this.vertices[1], this.angle + Math.PI); + me.lasers(this.vertices[2], this.angle - Math.PI / 3); + } + ctx.strokeStyle = randomColor({ + hue: "#FF00FF" + }); + ctx.stroke(); + ctx.save() + ctx.beginPath(); + ctx.moveTo(this.vertices[0].x, this.vertices[0].y); + ctx.lineTo(this.vertices[1].x, this.vertices[1].y); + ctx.lineTo(this.vertices[2].x, this.vertices[2].y); + ctx.fillStyle = "#000000"; + ctx.strokeStyle = "transparent"; + ctx.fill(); + ctx.closePath(); + ctx.stroke(); + ctx.restore() + } + } + me.seeker = function (x, y) { + mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)"); + let yy = mob[mob.length - 1]; + yy.stroke = "transparent"; + yy.onHit = function () { + this.explode(this.mass * 20); + }; + Matter.Body.setDensity(yy, 0.000015); //normal is 0.001 + yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random()); + yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + yy.restitution = 0.5; + yy.leaveBody = false; + yy.isDropPowerUp = false; + yy.isBadTarget = true; + yy.isMobBullet = true; + yy.showHealthBar = false; + yy.collisionFilter.category = cat.mobBullet; + yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + let index = 0; + yy.do = function () { + this.alwaysSeePlayer() + this.timeLimit(); + this.attraction(); + }; + } + me.drop = function (x, y) { + mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)"); + let yyy = mob[mob.length - 1]; + yyy.stroke = "transparent"; + yyy.onDeath = function () { + for (let i = 0, len = 5; i < len; i++) { + me.seeker(this.position.x, this.position.y) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: Math.random() * 30 - Math.random() * 30, + y: Math.random() * 30 - Math.random() * 30 + }); + } + }; + Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001 + yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random()); + yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + yyy.restitution = 0.5; + yyy.leaveBody = false; + yyy.isDropPowerUp = false; + yyy.isBadTarget = true; + yyy.isMobBullet = true; + yyy.showHealthBar = false; + yyy.collisionFilter.category = cat.mobBullet; + yyy.collisionFilter.mask = null; + yyy.maxRadius = 30; + let index = 0; + yyy.do = function () { + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + Matter.Body.setPosition(this, player.position) + if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) + } + this.alwaysSeePlayer() + this.timeLimit(); + ctx.save() + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y) + ctx.fillStyle = "black"; + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI) + ctx.stroke() + ctx.fill() + ctx.restore() + if (this.maxRadius > 0) { + this.maxRadius -= 0.5; + } + }; + } + }; + restoreBoss(-13350, -1800); + laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + //restoreBoss(-350, -3225); + wire(); + wire(); + wire(); + wire(); + wire(); + color.map = '#00000000'; + level.customTopLayer = () => { + if (dong.position.x > -3825) { + dong.force.y -= dong.mass * simulation.g; + } else { + dong.force.y += dong.mass * simulation.g; + } + Matter.Body.setAngularVelocity(dong, -0.5) + if (destroyed == false) { + door.isClosing = false; + } else { + door.isClosing = true; + } + door.openClose(); + door.draw() + for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map + // ctx.beginPath() + // //ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + // ctx.fillRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y)) + // //ctx.strokeRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y)) + // ctx.fillStyle = "rgba(68,68,68)" + // //ctx.strokeStyle = "transparent" + // ctx.stroke() + // ctx.fill() + ctx.beginPath(); + ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y); + for (let j = 0, length = map[i].vertices.length; j < length; j++) { + ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y); + } + ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y); + ctx.fillStyle = "rgba(68,68,68)"; + ctx.strokeStyle = "transparent"; + ctx.fill(); + ctx.stroke(); + // ctx.setLineDash([]); + } + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].restoreBoss) { + ctx.save(); + ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); + ctx.restore(); + } + } + }; + simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget + for (let i = 0; i < spawn.bossTypeSpawnOrder.length * Math.random(); i++) { + spawn.bossTypeSpawnOrder.splice(i * Math.floor(Math.random() * spawn.bossTypeSpawnOrder.length), 1, "restoreBoss") //meh good enough + } + const obj = { restoreBoss }; + Object.assign(spawn, obj); //ez + }, + superNgonBros() { + simulation.makeTextLog(`Super N-gon Bros by DesBoot`); + + let bowserKilled = 0 + let flagY = -750 + let flagReached = 0 + const elevator1 = level.elevator(3975, -11650, 450, 50, -13100, 0.003) + const elevator2 = level.elevator(5575, -11650, 450, 50, -13100, 0.003) + let firstElevatorY = -11650 + + const portal = level.portal({ x: 3990, y: 100 }, 1.5 * Math.PI, { x: 100, y: -13500 }, -1.5 * Math.PI) + const portal2 = level.portal({ x: 7135, y: -12270 }, -1 * Math.PI, { x: 12325, y: -2000 }, -1.5 * Math.PI) + + const bowser = function (x, y, radius = 150) { //define the mob the same as spawn mob code + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.05; + me.g = 0.002; //required if using this.gravity + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.delay = 80 * simulation.CDScale; + me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); + me.randomHopCD = simulation.cycle + me.randomHopFrequency; + Matter.Body.rotate(me, Math.random() * Math.PI); + spawn.shield(me, x, y); + me.do = function () { + //spawn.grenade(me.position.x, me.position.y); + // //const v = 5 * simulation.accelScale; + // Matter.Body.setVelocity(mob[mob.length - 1], { + // x: this.velocity.x + this.fireDir.x * v + Math.random(), + // y: this.velocity.y + this.fireDir.y * v + Math.random() + // }); + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + if (this.seePlayer.recall) { + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += forceMag * Math.cos(angle) * 0.5; + this.force.y += (forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.1) * this.mass) * 0.7; //antigravity + if (Math.random() < 0.5) { + spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: -5, + y: 0 + }); + } else { + spawn.bullet(this.position.x, this.position.y, 25); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: -25, + y: -25 + }); + } + } + } else { + //randomly hob if not aware of player + if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.randomHopCD = simulation.cycle + this.randomHopFrequency; + //slowly change randomHopFrequency after each hop + this.randomHopFrequency = Math.max(100, this.randomHopFrequency + (0.5 - Math.random()) * 200); + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.1 + Math.random() * 0.3); + const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle) - 0.07 * this.mass; //antigravity + spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: -5, + y: 0 + }); + } + } + }; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + bowserKilled = 1 + } + + } + + bowser(20500, -400) //call the mob + //fire(15300, -200) + const brick = function (x, y, angle = Math.PI * 0.5, radius = 53) {//credit to Richard0820 for the code + mobs.spawn(x, y, 4, radius, "#ab6101"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.isDropPowerUp = false; + me.showHealthBar = false; + Matter.Body.setDensity(me, 999999) + me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 0, + damping: 0 + }); + me.do = function () { + this.isStunned = true; + if (this.health < 1) { + this.health += 0.001; //regen + } + this.checkStatus(); + Matter.Body.setAngle(me, angle); + if ((player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) && Math.abs(player.position.x - me.position.x) < 50 && Math.abs(player.position.y - me.position.y) < 150) { + me.death() + } + }; + + me.onHit = function () { + + if (player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) { + + me.death() + + } + } + me.onDeath = function () { + if (Math.random() < 0.1) { + spawn.randomSmallMob(me.position.x, me.position.y - 75); + simulation.makeTextLog('mob') + } else { + if (Math.random() < 0.07) { + powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "tech", true); + simulation.makeTextLog('tech') + } else { + if (Math.random() < 0.4) { + powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "heal", true); + simulation.makeTextLog('heal') + } else { + //if (Math.random() < 0.8){ + powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "ammo", true); + simulation.makeTextLog('ammo') + //} + } + } + } + } + Composite.add(engine.world, me.constraint); + } + simulation.enableConstructMode() + let firstMobsSpawned = 1 + let secondMobsSpawned = 0 + let thirdMobsSpawned = 0 + let fourthMobsSpawned = 0 + let firstMobsReached = 0 + let secondMobsReached = 0 + let thirdMobsReached = 0 + let fourthMobsReached = 0 + let finalRoomReached = 0 + let undergroundMobsSpawned = 0 + let undergroundMobsReached = 0 + + + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 22100; + level.exit.y = -40; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#a2a5ff"; + // color.map = "#444" //custom map color + + level.custom = () => { + if (player.position.x > 14950 && flagReached == 0) { + flagReached = 1 + } + if (flagReached == 1 && flagY < -150) { + flagY += 5 + } + ctx.fillStyle = "rgba(64,64,64,0.97)" + ctx.fillRect(4200, -13100, 2, 1450) + ctx.fillRect(5800, -13100, 2, 1450) + if (firstElevatorY < -12099) { + firstElevatorY = -11650 + } else { + firstElevatorY -= 5 + } + + //simulation.makeTextLog(firstElevatorY) + elevator1.move(); + elevator2.move(); + if (player.position.x > 0 && player.position.y < -9000 && player.position.y > -10000) { + //m.death() + m.damage(0.05 * simulation.difficultyMode) + Matter.Body.setPosition(player, { + x: 275, + y: -12175 + }); + } + + portal[2].query() + portal[3].query() + portal2[2].query() + portal2[3].query() + //simulation.makeTextLog(firstBlockBroken) + level.exit.drawAndCheck(); + if (player.position.x > 4100 && secondMobsReached == 0) { + secondMobsSpawned = 1 + } + if (player.position.x > 7000 && thirdMobsReached == 0) { + thirdMobsSpawned = 1 + } + if (player.position.x > 14300 && fourthMobsReached == 0) { + fourthMobsSpawned = 1 + } + if (player.position.y < -11000 && undergroundMobsReached == 0) { + undergroundMobsSpawned = 1 + }//player.position.x > 14300 && + if (m.onGround) { + if (Math.abs(player.velocity.x) > 0.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + (0.1 * (Math.abs(player.velocity.x) / player.velocity.x)), + y: player.velocity.y + 0.2 + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.2 + }); + } + } else { + if (input.down) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 2 + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.2 + }); + } + } + + level.enter.draw(); + if (finalRoomReached == 0 && player.position.x > 21150) { + finalRoomReached = 1 + simulation.makeTextLog('Thank you M, but our techs are in another castle!') + } + //mobs + if (firstMobsSpawned == 1 && firstMobsReached == 0) { + spawn.randomSmallMob(1260, -75); + spawn.randomMob(2100, -75, 0.4); + spawn.randomSmallMob(2400, -75); + spawn.randomSmallMob(2500, -75); + spawn.randomMob(2900, -75, 0.2); + spawn.randomMob(3400, -75, 0.4); + spawn.randomMob(3400, -75, 0.4); + firstMobsReached = 1 + } + if (secondMobsSpawned == 1 && secondMobsReached == 0) { + spawn.randomSmallMob(4400, -75); + spawn.randomSmallMob(5500, -75); + spawn.randomSmallMob(5835.6, -402.4); + spawn.randomSmallMob(5835.6, -402.4); + spawn.randomSmallMob(6543.2, -730.0); + spawn.randomMob(6795.4, -162.4, 0.1); + spawn.randomMob(6795.4, -162.4, 0.1); + secondMobsReached = 1 + } + if (thirdMobsSpawned == 1 && thirdMobsReached == 0) { + spawn.randomMob(8465.6, -469.9, 0.1); + spawn.randomMob(9839.6, -444.5, 0.4); + spawn.randomSmallMob(11033.2, -155.3); + spawn.randomMob(12161.3, -85.1, 0.3); + spawn.randomMob(12161.3, -85.1, 0.3); + spawn.randomMob(13399.8, -93.4, 0.4); + thirdMobsReached = 1 + + } + if (fourthMobsSpawned == 1 && fourthMobsReached == 0) { + spawn.randomSmallMob(16500, -400); + spawn.randomSmallMob(19278.9, -211.1); + spawn.randomMob(18839.0, -463.2, 0.3); + spawn.randomMob(18036.9, -205.9, 0.3); + spawn.randomMob(16950.4, -365.2, 0.4); + spawn.randomMob(16355.6, -390.8, 0.1); + + fourthMobsReached = 1 + + } + if (undergroundMobsSpawned == 1 && undergroundMobsReached == 0) { + spawn.randomSmallMob(1140.0, -12228.0); + spawn.randomSmallMob(2429.9, -12371.2); + spawn.randomSmallMob(4899.4, -12139.6); + spawn.randomMob(18839.0, -463.2, 0.3); + spawn.randomMob(2844.5, -12281.0, 0.2); + spawn.randomMob(4967.5, -12550.8, 0.4); + spawn.randomMob(6696.9, -12437.9, 0.1); + + undergroundMobsReached = 1 + + } + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + }; + level.customTopLayer = () => { + //spawn.mapRect(886, firstElevatorY + 10000, 75, 5); + + ctx.fillStyle = "rgba(64,64,64,0.97)" + ctx.fillRect(3928, -300, 120, 500) + //6940, -12360, 200, 5 + ctx.fillRect(6940, -12350, 170, 120) + ctx.fillRect(7090, -12380, 120, 200) + ctx.fillRect(14980, -750, 10, 750)//flagpole + ctx.beginPath() + ctx.moveTo(14980, flagY) + ctx.lineTo(14905, flagY) + ctx.lineTo(14980, flagY + 75) + ctx.fill() + + }; + + + + brick(923.5, -262); + spawn.mapRect(886, -304, 75, 5); + spawn.mapRect(886, -229, 75, 5); + spawn.mapRect(883, -304, 5, 80); + spawn.mapRect(958, -304, 5, 80); + brick(1250.5, -262); + spawn.mapRect(1138, -304, 375, 5); + spawn.mapRect(1138, -229, 375, 5); + brick(1400.5, -262); + brick(1325.5, -562); + spawn.mapRect(1288, -604, 75, 5); + spawn.mapRect(1288, -529, 75, 5); + spawn.mapRect(1285, -604, 5, 80); + spawn.mapRect(1360, -604, 5, 80); + brick(5787.5, -262); + spawn.mapRect(5675, -304, 225, 5); + spawn.mapRect(5675, -229, 225, 5); + brick(6987.5, -562); + spawn.mapRect(6725, -604, 300, 5); + spawn.mapRect(6725, -529, 300, 5); + spawn.mapRect(7025, -604, 5, 80); + brick(7887.5, -262);//4 separated blocks in the middle + spawn.mapRect(7850, -304, 75, 5); + spawn.mapRect(7850, -225, 75, 5); + spawn.mapRect(7850, -304, 5, 80); + spawn.mapRect(7925, -304, 5, 84); + brick(8112.5, -262); + spawn.mapRect(8075, -304, 75, 5); + spawn.mapRect(8075, -225, 75, 5); + spawn.mapRect(8075, -304, 5, 80); + spawn.mapRect(8150, -304, 5, 84); + brick(8337.5, -262); + spawn.mapRect(8300, -304, 75, 5); + spawn.mapRect(8300, -225, 75, 5); + spawn.mapRect(8300, -304, 5, 80); + spawn.mapRect(8375, -304, 5, 84); + brick(8112.5, -562); + spawn.mapRect(8075, -604, 75, 5); + spawn.mapRect(8075, -525, 75, 5); + spawn.mapRect(8075, -604, 5, 80); + spawn.mapRect(8150, -604, 5, 84); + brick(9612.5, -562); + spawn.mapRect(9500, -604, 300, 5); + spawn.mapRect(9500, -525, 300, 5); + spawn.mapRect(9647.5, -600, 5, 75); + brick(9687.5, -562); + brick(12887.5, -262); + spawn.mapRect(12700, -304, 300, 5); + spawn.mapRect(12700, -225, 300, 5); + brick(5212.5, -12337.5); + spawn.mapRect(4725, -12377, 525, 5); + spawn.mapRect(4725, -12303, 525, 5); + spawn.mapRect(5250, -12377, 5, 79); + + + + + + spawn.mapRect(-100, 0, 4033, 2000); + spawn.mapRect(4043, 0, 882, 2000); + spawn.mapRect(3909.5, 203.6, 150, 2000); + spawn.mapRect(1138, -300, 75, 75); + spawn.mapRect(1288, -300, 75, 75); + spawn.mapRect(1438, -300, 75, 75); + spawn.mapRect(1738, -150, 150, 75);//pipe 1 + spawn.mapRect(1753, -150, 120, 150); + spawn.mapRect(2488, -225, 150, 75);//pipe 2 + spawn.mapRect(2503, -225, 120, 225); + spawn.mapRect(3088, -300, 150, 75);//pipe 3 + spawn.mapRect(3103, -300, 120, 300); + spawn.mapRect(3913, -300, 20, 75);//pipe 4 + spawn.mapRect(3928, -300, 5, 300); + spawn.mapRect(4043, -300, 20, 75); + spawn.mapRect(4043, -300, 5, 300); + + spawn.mapRect(5225, 0, 1125, 2000); + spawn.mapRect(6575, 0, 4900, 2000); + spawn.mapRect(5675, -300, 75, 75); + spawn.mapRect(5825, -300, 75, 75); + spawn.mapRect(5900, -600, 600, 75); + spawn.mapRect(6725, -600, 225, 75); + spawn.mapRect(6950, -300, 75, 75); + spawn.mapRect(7400, -300, 150, 75); + + spawn.mapRect(8750, -300, 75, 75); + spawn.mapRect(8975, -600, 225, 75);//raised platform + spawn.mapRect(9575, -300, 150, 75); + spawn.mapRect(9500, -600, 75, 75);//upper block with double bricks + spawn.mapRect(9725, -600, 75, 75); + spawn.mapRect(9950, -75, 300, 75);//staircase + spawn.mapRect(10025, -150, 225, 75); + spawn.mapRect(10100, -225, 150, 75); + spawn.mapRect(10175, -300, 75, 75); + + spawn.mapRect(10475, -75, 300, 75); + spawn.mapRect(10475, -150, 225, 75); + spawn.mapRect(10475, -225, 150, 75); + spawn.mapRect(10475, -300, 75, 75); + + spawn.mapRect(11100, -75, 375, 75);//staircase 2 + spawn.mapRect(11175, -150, 300, 75); + spawn.mapRect(11250, -225, 225, 75); + spawn.mapRect(11325, -300, 150, 75); + + spawn.mapRect(11725, -75, 300, 75); + spawn.mapRect(11725, -150, 225, 75); + spawn.mapRect(11725, -225, 150, 75); + spawn.mapRect(11725, -300, 75, 75); + + spawn.mapRect(11725, 0, 5975, 2000);//platform after the staircase + spawn.mapRect(12325, -150, 150, 75);//exit pipe + spawn.mapRect(12340, -150, 120, 300); + spawn.mapRect(12700, -300, 150, 75); + spawn.mapRect(12925, -300, 75, 75); + spawn.mapRect(13525, -150, 150, 72);//final pipe + spawn.mapRect(13540, -150, 120, 150); + spawn.mapRect(13675, -75, 675, 75);//final staircase + spawn.mapRect(13750, -150, 600, 75); + spawn.mapRect(13825, -225, 525, 75); + spawn.mapRect(13900, -300, 450, 75); + spawn.mapRect(13975, -375, 375, 75); + spawn.mapRect(14050, -450, 300, 75); + spawn.mapRect(14125, -525, 225, 75); + spawn.mapRect(14200, -600, 150, 75); + + //flag + spawn.mapRect(14950, -75, 75, 75); + + + + spawn.mapRect(1750, -4600, 500, 25);//loss + spawn.mapRect(2000, -4850, 25, 500); + spawn.mapRect(1800, -4800, 25, 150); + spawn.mapRect(2075, -4800, 25, 150); + spawn.mapRect(2150, -4775, 25, 125); + spawn.mapRect(1875, -4550, 25, 150); + spawn.mapRect(1800, -4550, 25, 150); + spawn.mapRect(2075, -4550, 25, 150); + spawn.mapRect(2123, -4430, 100, 25); + // spawn.mapRect(-250, -600, 500, 25); + // spawn.mapRect(-250, -600, 500, 25); + + //underground area + spawn.mapRect(0, -12000, 2025, 2000); + spawn.mapRect(2325, -12225, 150, 2000); + spawn.mapRect(2775, -12000, 900, 2000); + spawn.mapRect(3525, -12300, 150, 300); + spawn.mapRect(3450, -12225, 75, 300); + spawn.mapRect(3375, -12150, 75, 300); + spawn.mapRect(3300, -12075, 75, 300); + spawn.mapRect(4725, -12375, 450, 75); + spawn.mapRect(4725, -12000, 600, 2000); + spawn.mapRect(-100, -13500, 100, 3500); + spawn.mapRect(-100, -13500, 100, 3500); + spawn.mapRect(6375, -12225, 1650, 2000); + spawn.mapRect(7225, -13225, 2850, 3000); + spawn.mapRect(-100, -13700, 100, 200);//roof + spawn.mapRect(-100, -13700, 3775, 100); + spawn.mapRect(6450, -13225, 1000, 100); + spawn.mapRect(7090, -13225, 120, 885);//pipe + //spawn.mapRect(6940, -12360, 200, 120); + spawn.mapRect(6940, -12350, 200, 5); + spawn.mapRect(6950, -12240, 140, 5); + spawn.mapRect(6940, -12365, 75, 15); + spawn.mapRect(6940, -12235, 75, 15); + + //castle + spawn.mapRect(17700, 0, 4975, 2000); + spawn.mapRect(18600, -225, 375, 2225); + spawn.mapRect(19500, -225, 450, 2225); + spawn.mapRect(19500, -825, 450, 225); + spawn.mapRect(15924, -1575, 6751, 750); + spawn.mapRect(19950, -225, 975, 75); + spawn.mapRect(20925, -300, 225, 300); + spawn.mapRect(21000, -825, 150, 300); + spawn.mapRect(15924, -225, 1776, 2225); + spawn.mapRect(17175, -825, 525, 225); + spawn.mapRect(22600, -825, 75, 825); + + + + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + underpass() { + simulation.makeTextLog(`underpass by Richard0820`); + + let key = false; + const door = level.door(2650, -825, 50, 250, 250, 10); + const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 }) + const slimePit = level.hazard(-4775, -350, 1975, 175); + const boost = level.boost(137.5, -600, 75, 25); + let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, { + density: 0.05, + isNotHoldable: true, + isStatic: false + }); + dong = Matter.Body.create({ + parts: [base, left, right, left2, right2] + }); + body[body.length] = base; + body[body.length] = left; + body[body.length] = right; + body[body.length] = left2; + body[body.length] = right2; + Matter.Composite.add(engine.world, dong) + Matter.Composite.add(engine.world, Constraint.create({ + pointA: { x: -3825, y: -975 }, + bodyB: dong, + stiffness: 0.2, + damping: 0.1 + })); + composite[composite.length] = dong; + setTimeout(function () { + dong.collisionFilter.category = cat.body; + dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map + }, 1000); + level.custom = () => { + ctx.save() + ctx.beginPath() + ctx.fillStyle = "#80808077"; + ctx.strokeStyle = "#80808022"; + ctx.fillRect(225, -1025, 2400, 450); + ctx.fillRect(-2950, -1025, 3100, 450); + ctx.fillRect(-7050, -1025, 2400, 450); + ctx.fillRect(-10575, -3975, 4525, 1025); + ctx.fillRect(-4650, -1700, 1700, 1100); + ctx.fillRect(-11150, -3575, 575, 3050); + ctx.fillRect(-11900, -1000, 750, 475); + ctx.fill() + ctx.stroke() + ctx.restore() + ctx.save() + ctx.beginPath() + ctx.fillStyle = "#d8dadf"; + ctx.strokeStyle = "#d8dadf"; + ctx.moveTo(-2950, -600); + ctx.lineTo(-3730, -1725); + ctx.lineTo(-3730, -600); + + ctx.moveTo(-4650, -600); + ctx.lineTo(-3925, -1725); + ctx.lineTo(-3925, -575); + + ctx.moveTo(-10575, -3425); //NE section + ctx.lineTo(-10100, -2975); + ctx.lineTo(-10575, -2975); + + // ctx.moveTo(-7625, -3800); + // ctx.lineTo(-6750, -2975); + // ctx.lineTo(-7625, -2975); + + ctx.moveTo(-7975, -2975); + ctx.lineTo(-7625, -3800); + ctx.lineTo(-7350, -2950); + + ctx.moveTo(-6750, -2975); + ctx.lineTo(-7075, -3800); + ctx.lineTo(-7350, -2950); + + // ctx.moveTo(-7975, -2975); + // ctx.lineTo(-7075, -3800); + // ctx.lineTo(-7075, -2975); + + ctx.moveTo(-11900, -950); + ctx.lineTo(-11900, -550); + ctx.lineTo(-11500, -550); + + ctx.fillRect(-3925, -1675, 200, 1075); + ctx.fillRect(-7625, -3800, 550, 875); + ctx.clearRect(-10600, -4000, 525, 475); + ctx.clearRect(-10100, -4000, 500, 300); + ctx.clearRect(-9625, -4000, 500, 175); + ctx.fillRect(-11125, -3600, 550, 50); + ctx.fillRect(-10600, -3400, 50, 425); + ctx.fillRect(-11925, -925, 45, 375); + ctx.fillRect(-3950, -1675, 75, 1100); + ctx.fillRect(-3925, -625, 950, 50); + ctx.fillRect(-4650, -600, 1700, 375); + ctx.fillRect(-14550, -2400, 2650, 2050); + //ctx.clearRect(-11050, -3000, 475, 50); + + ctx.moveTo(-11150, -3575); + ctx.lineTo(-10575, -2150); + ctx.lineTo(-10575, -3575); + + ctx.stroke() + ctx.fill() + ctx.restore() + boost.query() + slimePit.query() + if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty); + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(dong, [mob[i]]).length > 0) { + const dmg = 1; + mob[i].damage(dmg, true); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + break + } + } + level.exit.drawAndCheck(); + ctx.beginPath() + ctx.fillStyle = '#68686822'; + ctx.fillRect(-25, -2175, 100, 200); + ctx.fill() + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + ctx.moveTo(-3825, -975) + ctx.lineTo(dong.position.x, dong.position.y) + ctx.stroke(); + ctx.setLineDash([]); + simulation.drawList.push({ //add dmg to draw queue + x: dong.position.x, + y: dong.position.y, + radius: 10, + color: color.block, + time: 20 + }); + ctx.beginPath() + ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-3000, -2175, 175, 25); + ctx.fillRect(-2850, -2300, 25, 150); + ctx.fillRect(-3000, -2300, 175, 25); + ctx.fillRect(-3000, -2425, 25, 150); + ctx.fillRect(-3000, -2425, 175, 25); + ctx.fill() + ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-2725, -2425, 25, 275); + ctx.fillRect(-2725, -2425, 175, 25); + ctx.fillRect(-2575, -2425, 25, 275); + ctx.fillRect(-2725, -2300, 175, 25); + ctx.fill() + ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})` + ctx.fillRect(-2450, -2425, 25, 275); + ctx.fillRect(-2450, -2175, 175, 25); + ctx.fill() + ctx.stroke(); + ctx.fillStyle = `#00FFFF22`; + ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100) + ctx.fill() + ctx.fillStyle = `#00FFFF66` + ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) + ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) + ctx.fillStyle = `rgba(68, 68, 68)` + ctx.fillRect(-7675, -3075, 50, 125); + ctx.fillRect(-7075, -3075, 50, 125); + ctx.fillRect(-7725, -3025, 75, 75); + ctx.fillRect(-7050, -3025, 75, 75); + ctx.fill() + for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically + if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) { + body[i].force.y -= simulation.g * body[i].mass + 0.012; + } + } + for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically + if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) { + bullet[i].force.y -= simulation.g * bullet[i].mass; + } + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically + if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) { + powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12; + } + } + for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically + if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) { + mob[i].force.y -= simulation.g * mob[i].mass + 0.0012; + } + } + if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) { + player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2); + + } + ctx.save() + ctx.beginPath(); + ctx.fillStyle = `rgba(0,250,250, 0.05)`; + ctx.fillRect(2625, -1000, 350, 450); + ctx.fill() + ctx.restore() + elevator.move() + if (mob.length <= 5) { + key = true; + } + if (key) { + level.exit.x = 2775; + level.exit.y = -650; + } else { + + ctx.save() + ctx.beginPath(); + ctx.moveTo(2775, -650 + 30); + ctx.lineTo(2775, -650 - 80); + ctx.bezierCurveTo(2775, -650 - 170, 2775 + 100, -650 - 170, 2775 + 100, -650 - 80); + ctx.lineTo(2775 + 100, -650 + 30); + ctx.lineTo(2775, -650 + 30); + ctx.fillStyle = "#00ffff"; + ctx.fill(); + ctx.restore() + + const omega = new Image(); //the seal hehe + let width = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) * + let length = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) * + omega.src = "https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/MOSHED-2023-4-20-12-55-58-removebg-preview.png"; //"https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/omegafr.png"; + ctx.drawImage(omega, 2775 + 50 - width, -650 - 50 - length, width * 2, length * 2) + ctx.save() + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.beginPath() + // ctx.font = "80px monospace"; + // ctx.fillText("Ω", 2775 + 50, -650 - 50); + ctx.strokeStyle = "gray"; + ctx.arc(2775 + 50, -650 - 50, 100 * Math.abs(Math.sin(m.cycle * 0.07)) + 10, 0 + Math.sin(m.cycle * 0.1 * Math.PI), 0 + Math.cos(m.cycle * 0.1 + Math.PI)) + ctx.shadowBlur = 10; + ctx.shadowColor = "black"; + ctx.stroke() + //ctx.fill() + ctx.restore() + ctx.save() + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.beginPath() + // ctx.font = "80px monospace"; + // ctx.fillText("Ω", 2775 + 50, -650 - 50); + ctx.strokeStyle = "gray"; + ctx.arc(2775 + 50, -650 - 50, 90 * Math.abs(Math.sin(m.cycle * 0.08)) + 10, 0 + Math.cos(m.cycle * 0.1 * Math.PI), 0 + Math.sin(m.cycle * 0.1 + Math.PI)) + ctx.shadowBlur = 10; + ctx.shadowColor = "black"; + ctx.stroke() + //ctx.fill() + ctx.restore() + ctx.save() + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.beginPath() + // ctx.font = "80px monospace"; + // ctx.fillText("Ω", 2775 + 50, -650 - 50); + ctx.strokeStyle = "gray"; + ctx.arc(2775 + 50, -650 - 50, 80 * Math.abs(Math.sin(m.cycle * 0.09)) + 10, 0 + Math.tan(m.cycle * 0.1 * Math.PI), 0 + Math.tan(m.cycle * 0.1 + Math.PI)) + ctx.shadowBlur = 10; + ctx.shadowColor = "black"; + ctx.stroke() + //ctx.fill() + ctx.setLineDash([]); + ctx.restore() + } + }; + level.setPosToSpawn(30, -2000); //normal spawn + level.exit.x = 1375; + level.exit.y = -1500; + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom + 800) + document.body.style.backgroundColor = "#d8dadf"; + spawn.mapRect(-225, -1950, 350, 75); + spawn.mapRect(225, -1950, 50, 75); + spawn.mapRect(-250, -2025, 50, 150); + spawn.mapRect(250, -2025, 50, 150); + spawn.mapRect(-250, -2250, 50, 125); + spawn.mapRect(-225, -2325, 500, 100); + spawn.mapRect(250, -2250, 50, 125); + spawn.mapRect(-100, -2400, 250, 100); + spawn.mapRect(-25, -2475, 100, 100); + spawn.mapRect(125, -2350, 50, 50); + spawn.mapRect(-125, -2350, 50, 50); + spawn.mapRect(-50, -2425, 50, 50); + spawn.mapRect(50, -2425, 50, 50); + spawn.mapRect(-250, -2350, 50, 50); + spawn.mapRect(250, -2350, 50, 50); + spawn.mapRect(-75, -1975, 200, 50); + spawn.mapRect(-50, -2000, 150, 50); + spawn.mapRect(100, -1950, 50, 75); + spawn.mapRect(-75, -2250, 200, 50); + spawn.mapRect(-50, -2225, 150, 50); + spawn.mapRect(-2950, -1900, 3100, 900); + spawn.mapRect(225, -1900, 2875, 900); + spawn.mapRect(-2950, -600, 6050, 450); + spawn.mapRect(-3050, -500, 200, 350); + spawn.mapRect(-3150, -400, 200, 250); + spawn.mapRect(-3250, -300, 200, 150); + spawn.mapRect(2950, -1050, 150, 500); + spawn.mapRect(-4675, -1900, 1825, 200); + spawn.mapRect(-5325, -1900, 675, 900); + spawn.mapRect(-5325, -250, 2100, 100); + spawn.mapRect(-5325, -600, 675, 450); // - + spawn.mapRect(-4700, -500, 150, 350); + spawn.mapRect(-4650, -400, 200, 250); + spawn.mapRect(-4550, -300, 200, 150); + spawn.mapRect(-3875, -1025, 100, 100); + spawn.mapRect(-3800, -1050, 50, 50); + spawn.mapRect(-3900, -1050, 50, 50); + spawn.mapRect(-3800, -950, 50, 50); + spawn.mapRect(-3900, -950, 50, 50); + spawn.mapRect(-6925, -1175, 1700, 175); + spawn.mapRect(-6925, -600, 1725, 175); + spawn.mapRect(-7700, -600, 800, 425);// - + spawn.mapRect(-7800, -2950, 175, 2775); + spawn.mapRect(-7075, -2950, 175, 1950); + spawn.mapRect(-9150, -2975, 1525, 175); + spawn.mapRect(-7075, -2975, 1150, 175); + spawn.mapRect(-6100, -3900, 175, 1100); + spawn.mapRect(-9150, -3975, 3225, 175); + spawn.mapRect(-9175, -3850, 75, 75); + spawn.mapRect(-9625, -3825, 500, 150); + spawn.mapRect(-9650, -3725, 75, 75); + spawn.mapRect(-10100, -3700, 500, 150); + spawn.mapRect(-10100, -2975, 975, 175); + spawn.mapRect(-10125, -3600, 75, 75); + spawn.mapRect(-10575, -3575, 500, 150); + spawn.mapRect(-10575, -2975, 500, 175); + spawn.mapRect(-11325, -2975, 250, 175); + spawn.mapRect(-11325, -3575, 175, 775); + // spawn.mapRect(-11325, -3575, 800, 150); + spawn.mapRect(-11225, -2975, 150, 2000); + spawn.mapRect(-10575, -2975, 150, 2500); + spawn.mapRect(-11650, -550, 1225, 150); + spawn.mapRect(-11650, -1100, 575, 150); + spawn.mapRect(-14675, -2525, 2925, 150); + spawn.mapRect(-11900, -2525, 150, 1575); + spawn.mapRect(-11850, -1100, 250, 150); + spawn.mapRect(-11875, -550, 275, 150); + spawn.mapRect(-11900, -550, 150, 350); + spawn.mapRect(-14675, -2525, 150, 2300); + spawn.mapRect(-14675, -375, 2925, 175); + spawn.mapRect(2725, -625, 250, 50); + spawn.mapRect(2625, -1025, 100, 225); + spawn.mapRect(2700, -1025, 300, 125); + spawn.mapRect(2625, -612.5, 125, 50); + spawn.mapRect(-3950, -1725, 250, 50); + spawn.mapRect(-7650, -3825, 600, 50); + spawn.mapRect(-13900, -2400, 200, 50); + spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0'); + spawn.mapVertex(-14470, -430, '175 175 0 175 0 0'); + spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0'); + spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175'); + //spawn.mapRect(-13900, -2150, 1375, 125); + // const sword = function() { //the ultimate blade of las destruction // NO SWORD NO DROPS >:( + // mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent"); + // let me = mob[mob.length - 1]; + // Matter.Body.setDensity(me, 1); + // me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + // me.collisionFilter.category = cat.bullet; + // me.collisionFilter.mask = cat.mob | cat.mobBullet; + // me.isDropPowerUp = false; + // me.isShielded = true; + // me.showHealthBar = false; + // me.isUnblockable = true; + // me.leaveBody = false; + // me.isBadTarget = true; + // me.stroke = "transparent"; + // me.isSword = true; + // let index = 0; + // let radius = 50; + // // const len = cons.length; + // // cons[len] = Constraint.create({ + // // pointA: player.position, + // // bodyB: me, + // // stiffness: 0.14, + // // damping: 0.5 + // // }); + // // setTimeout(function(){ + // // Composite.add(engine.world, cons[cons.length - 1]); + // // }, 1) + // // cons[len].length = 100 + 1.5 * radius; + // me.do = function() { + // // this.health = Infinity;//just in case + // // for(let i = 0; i < mob.length; i++) { + // // if(Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) { + // // const dmg = 0.25;//do not nerf + // // mob[i].damage(dmg, true); + // // simulation.drawList.push({ //add dmg to draw queue + // // x: mob[i].position.x, + // // y: mob[i].position.y, + // // radius: Math.sqrt(dmg) * 50, + // // color: simulation.mobDmgColor, + // // time: simulation.drawTime + // // }); + // // break + // // } + // // } + // Matter.Body.setPosition(this, { + // x: player.position.x + Math.cos(m.angle) * 100, + // y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100 + // }) + // // this.force.x = Math.cos(m.angle) * 350; + // // this.force.y = Math.sin(m.angle) * 350; + // Matter.Body.setAngle(this, m.angle + Math.PI * 2); + // const setNoseShape = () => { + // const mag = radius + radius * 10; + // this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; + // this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; + // this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; + // this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; + // this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; + // this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; + // }; + // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100) + // const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500) + // const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500) + // this.vertices[2].x = this.position.x + spike.x / 100 + // this.vertices[2].y = this.position.y + spike.y / 100 + // this.vertices[4].x = this.position.x + spike2.x / 75 + // this.vertices[4].y = this.position.y + spike2.y / 75 + // this.vertices[0].x = this.position.x + spike3.x / 75 + // this.vertices[0].y = this.position.y + spike3.y / 75 + // if(index == 0) { + // setNoseShape(); + // index++; + // } + // ctx.save() + // ctx.beginPath(); + // const vertices = this.vertices; + // ctx.lineWidth = 100; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15); + // // Add three color stops + // gradient.addColorStop(0, m.eyeFillColor); + // gradient.addColorStop(0.9, "white"); + // gradient.addColorStop(1, "darkgray"); + // ctx.fillStyle = gradient; + // ctx.strokeStyle = "transparent"; + // ctx.shadowBlur = 10; + // ctx.shadowColor = m.eyeFillColor; + // ctx.fill(); + // ctx.stroke(); + // ctx.restore() + + // const Dx = Math.cos(m.angle); + // const Dy = Math.sin(m.angle); + // let xElec = this.position.x + 10 * Dx; + // let yElec = this.position.y + 10 * Dy; + // ctx.beginPath(); + // ctx.moveTo(xElec, yElec); + // const step = 40 + // for (let i = 0; i < 6; i++) { + // xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + // yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + // ctx.lineTo(xElec, yElec); + // } + // ctx.strokeStyle = m.eyeFillColor; + // ctx.lineWidth = 1.5; + // ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + // ctx.stroke(); // Draw it + // ctx.setLineDash([]); + // if(this.alive && m.energy > 0) { + // const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50))); + // // Add three color stops + // gradient.addColorStop(0, m.eyeFillColor); + // gradient.addColorStop(0.9, "white"); + // gradient.addColorStop(1, "gray"); + // ctx.save() + // ctx.beginPath() + // ctx.moveTo(this.position.x, this.position.y) + // ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI) + // ctx.fillStyle = gradient; + // ctx.strokeStyle = "transparent"; + // ctx.shadowBlur = 10; + // ctx.shadowColor = m.eyeFillColor; + // ctx.fill() + // ctx.stroke() + // ctx.restore() + // m.energy -= 0.002; + + // ctx.save() + // ctx.translate(this.vertices[2].x, this.vertices[2].y) + // ctx.rotate(m.angle + Math.PI / 2) + // ctx.beginPath() + // ctx.font = "16px Arial"; + // ctx.fillStyle = "black"; + // ctx.strokeStyle = "black"; + // // ctx.fillText("Θ", 0,0 - 110) + // // ctx.fillText("ά", 0,15 - 110) + // // ctx.fillText("ν", 0,30 - 110) + // // ctx.fillText("α", 0,45 - 110) + // // ctx.fillText("τ", 0,60 - 110) + // // ctx.fillText("ο", 0,75 - 110) + // // ctx.fillText("ς", 0,90 - 110) + // ctx.fillText("Ω", 0,55) + // ctx.fill() + // ctx.stroke() + // ctx.restore() + + // simulation.drawList.push({ + // x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300), + // y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300), + // radius: 2, + // color: m.eyeFillColor, + // time: simulation.drawTime + // }); + // } else { + // this.death() + // powerUps.activated = false + // } + // // me.onDeath = function() { + // // this.removeCons(); + // // }; + // } + // } + //setTimeout(function() {sword();}, 100); + const wire = function () { + const breakingPoint = -1600; + const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100); + mobs.spawn(spawnx, -2375, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + let boss = mob[0]; + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + me.restitution = 0; + me.stroke = "transparent" + me.freeOfWires = false; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + const wireX = spawnx; + const wireY = -2375; + //const randomw = Math.floor(Math.random() * 100 - Math.random() * 100); + const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10)); + const randomx = Math.floor(30 * Math.random() - 30 * Math.random()); + const randomy = Math.floor(10 * Math.random() - 10 * Math.random()) + me.do = function () { + if (this.freeOfWires) { + this.gravity(); + } else { + if (boss.position.y > breakingPoint) { + this.freeOfWires = true; + this.force.y -= -0.0006; + this.force.x += Math.random() * boss.velocity.x / 10000; + this.fill = "#111"; + } + //move mob to mob + Matter.Body.setPosition(this, { + x: boss.position.x + randomx, + y: boss.position.y + randomy + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y); + ctx.lineWidth = width; + ctx.lineCap = "butt"; + ctx.strokeStyle = "#111"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + } + + const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs? + mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(me, 0.00001); //normal is 0.001 + me.timeLeft = 500; + me.friction = 0; + me.restitution = 1; + me.leaveBody = false; + me.isDropPowerUp = false; + //me.inertia = Infinity; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; + let index = 0; + me.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + this.force.y += this.mass * 0.0012; + } + } + const normalBullet = function (x, y, radius = 9, sides = 3) { + //bullets + mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + me.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + me.timeLeft = 220; + me.frictionAir = -0.01; + me.restitution = -1; + me.leaveBody = false; + me.isDropPowerUp = false; + //me.inertia = Infinity; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = null; + me.boss = null; + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].restoreBoss) { + me.boss = mob[i]; + } + } + let index = 0; + me.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + const setNoseShape = () => { + const mag = this.radius + this.radius * 10; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + const angle = Math.atan2(this.position.y - me.boss.position.y, this.position.x - me.boss.position.x); + Matter.Body.setAngle(this, angle); + }; + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000) + this.vertices[1].x = this.position.x + spike.x / 100 + this.vertices[1].y = this.position.y + spike.y / 100 + if (index == 0) { + setNoseShape(); + index++; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { + const slow = 0.69 //im sorry it looks cool though + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 10, + color: '#000000', + time: simulation.drawTime + }); + if (this.velocity.x == 0 && this.velocity.y == 0) { + this.death(); + } + this.frictionAir += 0.0001; + Matter.Body.setAngularVelocity(this, 0) + } + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this + const dmg = 0.013 * simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: '#000000', + time: simulation.drawTime + }); + } + }; + } + const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets + mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + me.timeLeft = 120; + // me.g = 0.0005; //required if using this.gravity + me.accelMag = 0.00006; + me.isVerticesChange = true + me.delay = 360 * simulation.CDScale; + me.spikeVertex = 0; + me.spikeLength = 0; + me.isSpikeGrowing = false; + me.spikeGrowth = 0; + me.isSpikeReset = false; + me.frictionAir = 0; + me.restitution = 0; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.isUnblockable = true; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player; + me.do = function () { + if (this.distanceToPlayer2() < 40000) { + this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004) + const slow = 0.99999999999999999; + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + } + // this.gravity(); + this.timeLimit(); + // for (let i = 0, len = this.vertices.length; i < len; i++) { + // const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); + // const distMag = Vector.magnitude(dist); + // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag) + // this.vertices[i].x = this.position.x + spike.x / 100 + // this.vertices[i].y = this.position.y + spike.y / 100 + // } + if (this.radius < 50) { + const scale = 1.05; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { + const slow = 0.97 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.9 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + if (this.radius < 1) { + this.death() + } + } else { + this.attach(); + } + + }; + me.attach = function () { + if (Matter.Query.collides(this, [player]).length > 0) { + Matter.Body.setPosition(this, player.position) + if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) + Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9); + m.damage(0.00003); //balanced? not sure + } + } + }; + + const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot + mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.01); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.showHealthBar = false; + me.isOrbital = true; + me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2))) + //damage player + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.013 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + //this.death(); + } + }; + } + const missile = function (where, speed = 0.007, size = 1) { + mobs.spawn(where.x, where.y, 3, 20, '#000000'); + let me = mob[mob.length - 1]; + me.accelMag = speed; + Matter.Body.setDensity(me, 0.0000000000001); + me.stroke = 'transparent'; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; + me.leaveBody = false; + me.isDropPowerUp = false; + me.onHit = function () { + b.explosion(this.position, (tech.isMissileBig ? 230 : 180) + 60 * Math.random()) + this.death() + } + me.do = function () { + this.seePlayerByHistory(); + this.alwaysSeePlayer(); + this.attraction(); + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + Matter.Body.setAngle(this, angle); + // ctx.beginPath() + // ctx.strokeStyle = "rgba(255,155,0,0.5)"; + // ctx.lineWidth = 15; + // ctx.arc(this.vertices[0].x, this.vertices[0].x, 15, 0, 2 * Math.PI); + // ctx.stroke() + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 10, + color: `rgba(255,155,0,${Math.random()})`, + time: simulation.drawTime + }); + } + } + const railBullet = function (x, y) { + mobs.spawn(x, y, 5, 20, "black"); + let me = mob[mob.length - 1]; + me.stroke = "black"; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + me.onHit = function () { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: 20, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + me.timeLeft = 220; + me.frictionAir = -0.01; + me.restitution = -1; + me.leaveBody = false; + me.isDropPowerUp = false; + //me.inertia = Infinity; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player; + const radius = 30; + let index = 0; + me.do = function () { + this.timeLimit(); + this.alwaysSeePlayer() + const setNoseShape = () => { + const mag = me.radius + me.radius * 10; + const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x); + me.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; + me.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; + me.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; + me.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; + me.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; + me.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; + Matter.Body.setAngle(this, angle); + }; + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], me.position)), radius * 1000) + const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], me.position)), radius * 1000) + const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], me.position)), radius * 1000) + me.vertices[2].x = this.position.x + spike.x / 100 + me.vertices[2].y = this.position.y + spike.y / 100 + me.vertices[4].x = this.position.x + spike2.x / 75 + me.vertices[4].y = this.position.y + spike2.y / 75 + me.vertices[0].x = this.position.x + spike3.x / 75 + me.vertices[0].y = this.position.y + spike3.y / 75 + if (index == 0) { + setNoseShape(); + index++; + } + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { + const slow = 0.69 //im sorry it looks cool though + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: 10, + // color: '#000000', + // time: simulation.drawTime + // }); + if (this.velocity.x == 0 && this.velocity.y == 0) { + this.death(); + } + this.frictionAir += 0.0001; + Matter.Body.setAngularVelocity(this, 0) + } + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this + const dmg = 0.013 * simulation.dmgScale; + m.damage(dmg); + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: Math.sqrt(dmg) * 200, + // color: '#000000', + // time: simulation.drawTime + // }); + } + }; + } + const grenade = function (x, y, lifeSpan = 90 + Math.ceil(60 / simulation.accelScale), pulseRadius = Math.min(550, 250 + simulation.difficulty * 3), size = 3) { + mobs.spawn(x, y, 4, size, "rgb(0,0,0)"); //rgb(215,80,190) + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + + me.lifeSpan = lifeSpan; + me.timeLeft = me.lifeSpan; + // me.g = 0.0002; //required if using this.gravity + me.frictionAir = 0; + me.restitution = 0.8; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.onDeath = function () { + //damage player if in range + if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + m.damage(0.015 * simulation.dmgScale); + } + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: pulseRadius, + color: "rgba(0,0,0,0.3)", + time: simulation.drawTime + }); + }; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.map | cat.body | cat.player + // me.collisionFilter.mask = 0 + me.do = function () { + this.timeLimit(); + ctx.save() + ctx.beginPath(); //draw explosion outline + ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = "rgba(0,0,0,0.05)"; + ctx.fill(); + ctx.restore() + }; + } + const sniper = function (x, y, radius = 30) { + mobs.spawn(x, y, 8, radius, '#00000000'); + let me = mob[mob.length - 1]; + me.accelMag = 0.0003 + me.stroke = 'transparent'; + //me.isBoss = true; + me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, 0.01) + me.fireDir = { x: 0, y: 0 } + me.seePlayerFreq = 0 + me.repulsionRange = 400000 + radius * radius; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.do = function () { + this.seePlayerCheck(); + this.attraction(); + this.repulsion(); + this.search() + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,255,255,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + if (this.health < 1) { + this.health += 0.0005; //regen + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + //set direction to turn to fire + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.seePlayer.recall -= 10; + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + }; + } + const laserEM = function (x, y, radius = 30) { + mobs.spawn(x, y, 8, radius, '#00000000'); + let me = mob[mob.length - 1]; + me.accelMag = 0.0003 + me.stroke = 'transparent'; + //me.isBoss = true; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, 0.01) + me.seePlayerFreq = 0 + me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; + me.swordDamage = 0.025 * simulation.dmgScale + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.repulsionRange = 50000; + me.do = function () { + this.repulsion(); + this.search() + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,255,255,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + if (this.health < 1) { + this.health += 0.0005; //regen + } + if (this.seePlayer.recall) { + this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + this.seePlayerCheck(); + this.attraction(); + } + me.laserSword = function (where, angle, length) { + const sub = Vector.sub(this.seePlayer.position, this.position) + const unit = Vector.normalise(sub) + const path = [{ + x: this.position.x + 20 * Math.cos(this.angle), + y: this.position.y + 20 * Math.sin(this.angle) + }, + { + x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), + y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) + } + ]; + this.seePlayer.recall -= 3; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; + // vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(80,0,255,0.07)"; + ctx.stroke(); // Draw it + + const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + let xElec = this.position.x + 40 * Dx; + let yElec = this.position.y + 40 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }; + // if(powerUps.pass == undefined) { + // let pass = {pass:true, activated:false}; + // Object.assign(powerUps, pass) + // } + // const loadOut = { + // loadOut: { + // name: "loadOut", + // color: "#000000", //"hsl(248,100%,65%)", + // size() { return 40 }, + // effect() { + // if(m.alive) { + // // tech.damage *= 2; + // let text = ""; + // if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?":"✕"}
`; }; + // text += `

Blessing Of Sal

`; + // text += `
  Speed Boost
Increase speed by 5%
`; + // text += `
  Defense Boost
Reduce damage by 5%
`; + // text += `
  Damage Boost
Increase damage by 10%
`; + // if(powerUps.pass == true) { + // text += `
  Blade of Sal
Press Shift to summon the Mythical Las Slayer
Drains Energy
`; + // } + // document.getElementById("choose-grid").innerHTML = text; + // powerUps.showDraft();//no known bugs ig idk, im keep this as it is + // } + // }, + // choose(index) { + // if(index == 1) { + // tech.squirrelFx += 0.25; + // tech.squirrelJump += 0.1; + // m.setMovement(); + // powerUps.endDraft("buff"); + // } else if(index == 2) { + // simulation.dmgScale *= 0.95; + // powerUps.endDraft("buff"); + // } else if(index == 3) { + // m.dmgScale *= 1.1; + // powerUps.endDraft("buff"); + // } else if(index == 4) { //sword! + // powerUps.pass = false; + // addEventListener("keydown", function(event) { + // if(event.key == "Shift" && powerUps.activated == false) { + // sword() + // powerUps.activated = true; + // } else if(event.key == "Shift" && powerUps.activated == true) { + // for(let i = 0; i < mob.length; i++) { + // if(mob[i].isSword) { + // mob[i].death() + // } + // powerUps.activated = false; + // } + // } + // }) + // powerUps.endDraft("buff"); + // } + // } + // } + // } + // Object.assign(powerUps, loadOut) + const restoreBoss = function (x, y, radius = 30) { //ATTENTION LANDGREEN: RESTOREBOSS WILL NOT DROP ANY TECH, NOR WILL THERE BE ANY IN THE MAP. DO NOT ADD ANY TECH TO MY MAP + mobs.spawn(x, y, 8, radius, 'transparent'); + let me = mob[mob.length - 1]; + me.stroke = 'transparent'; + let aim = '#FFFFFF'; + me.accelMag = 0.0006 + me.isBoss = true; + me.restoreBoss = true; + me.frictionStatic = 0; + me.friction = 0; + me.seeAtDistance2 = 20000000 //14000 vision range + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.showHealthBar = false + Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode)) + me.seePlayerFreq = 0 + me.swordDamage = 0.025 * simulation.dmgScale + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet + me.repulsionRange = 500000; + me.isDropPowerUp = false; + //Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + let index = 0; + me.curlRange = 10000; //xd + me.fieldPhase = 0; + me.energy = 1; + me.maxEnergy = 1; + me.immuneCycle = 0; + me.cycle = 0; + // me.onDeath = function() { + // powerUps.spawn(this.position.x, this.position.y, "loadOut"); + // } + for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so... + orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed + index++; + } + me.do = function () { + if (this.position.x > -11500 && this.position.x < 10510) {// doesn't get one tapped by the elevator + me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.mob | cat.mobBullet; + } else { + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet; + } + this.cycle++; + if ((Matter.Query.ray(map, player.position, this.position).length === 0) == false) { + this.seePlayer.recall = null; + } + if (this.seePlayer.recall) { //fields + if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) { + if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) { + this.pushM(); + } + this.drawField(); + // this.repel(); + } + if (m.fieldMode == 2) { + if (this.distanceToPlayer2() < 200000) { + if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) { + this.pushM(); + } + this.drawField() + } + if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair... + const wave = Math.cos(m.cycle * 0.022); + const range = 200 + 140 * wave + 150 * m.energy + const distance = Vector.magnitude(Vector.sub(this.position, m.pos)) + const cap = this.immuneCycle > this.cycle ? 8 : 4 + if (distance < range) { + if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction + } + } + for (let i = 0; i < bullet.length; i++) { + const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position)) + if (distance2 < range) { + if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.08)"; + ctx.fill(); + } + } + if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) { + this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); + } + if (m.fieldMode == 9) { + if (this.distanceToPlayer2() < 300000) { + this.teleportAway() //blink but reversed + } + } + } + if (m.immuneCycle > m.cycle) { + me.damageReduction = 0; + } else { + me.damageReduction = 1; + } + this.repulsion(); + this.seePlayerCheck(); + this.attraction(); + this.lostPlayer(); + if (this.speed > 10) { // speed cap + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.99, + y: this.velocity.y * 0.99 + }); + } + if (this.seePlayer.recall) { + const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x); + let positionR = { x: 500 * Math.cos(angle) + mob[0].position.x, y: 500 * Math.sin(angle) + mob[0].position.y }; + let isWall = (Matter.Query.ray(map, mob[0].position, positionR).length === 0); + let isBlock = (Matter.Query.ray(body, mob[0].position, positionR).length === 0); + if (isWall == false || isBlock == false) { + this.force.x -= Math.cos(angle) * 10; + this.force.y -= Math.sin(angle) * 10; + } + } + if (this.seePlayer.recall) { + const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 1.5; + let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y }; + let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0); + if (isBullet == false) { + this.force.x -= Math.cos(angle) * 30; + this.force.y -= Math.sin(angle) * 30; + } + } + if (this.seePlayer.recall) { + const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 2.5; + let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y }; + let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0); + if (isBullet == false) { + this.force.x -= Math.cos(angle) * 30; + this.force.y -= Math.sin(angle) * 30; + } + } + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(10, 10, 10, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(x, y, w * this.energy, h); + } + if (this.health < 1) { + this.health += 0.0001; //regen + } else if (this.health < 1) { + this.health += 0.00005; //reduced regen + } + if (this.energy < 0) {//energy thingy + this.energy = 0; + } else if (this.energy > this.maxEnergy) { + this.energy = this.maxEnergy; + } else if (this.energy < this.maxEnergy) { + this.energy += 0.001; + } + ctx.save() + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (this.distanceToPlayer2() < this.repulsionRange) { + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + ctx.restore() + ctx.save() + ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 + ctx.translate(this.position.x, this.position.y) + ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + if (this.health > 0.5) { + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.beginPath(); //eye + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; + ctx.fill() + ctx.restore() + } else { + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -6, 7, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 10, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#555"; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(3, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(26, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + } + if (m.fieldMode == 1) { //render over I think + if (this.energy > 0.1) { + this.harmonic3Phase(); + } + } + if (m.fieldMode == 7) { + this.locatePlayer(); + } + if (m.fieldMode == 8) { + me.pilotWave() + } + if (m.fieldMode == 3) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + me.damageReduction = 0.5; + me.accelMag = 0.0012; + if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) { + this.diveAttack() + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.strokeStyle = "#f5f5ff55"; + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.stroke() + ctx.setLineDash([]); + } else { + me.accelMag = 0.0006; + } + if (this.immuneCycle > this.cycle) { + this.damageReduction = 0; + } else { + if (m.fieldMode == 3) { + this.damageReduction = 0.5; + } else { + this.damageReduction = 1; + } + } + if (m.fieldMode == 6) { + this.timeAttack(); + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-50000, -50000, 100000, 100000) + ctx.globalCompositeOperation = "source-over" + // stop time + // m.isBodiesAsleep = true; + // function sleep(who) { + // for (let i = 0, len = who.length; i < len; ++i) { + // if (!who[i].isSleeping) { + // who[i].storeVelocity = who[i].velocity + // who[i].storeAngularVelocity = who[i].angularVelocity + // } + // Matter.Sleeping.set(who[i], true) + // } + // } + // sleep(mob); + // sleep(body); + // sleep(bullet); + // sleep([player]); + // simulation.cycle--; + } + if (this.seePlayer.recall) { //fields + this.gun() + } + } + me.laserSword = function (where, angle, length) { + const sub = Vector.sub(this.seePlayer.position, this.position) + const unit = Vector.normalise(sub) + const path = [{ + x: this.position.x + 20 * Math.cos(this.angle), + y: this.position.y + 20 * Math.sin(this.angle) + }, + { + x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), + y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; + // vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(0,0,0,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(0,0,0,0.07)"; + ctx.stroke(); // Draw it + + const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); + let xElec = this.position.x + 40 * Dx; + let yElec = this.position.y + 40 * Dy; + ctx.beginPath(); + ctx.moveTo(xElec, yElec); + const step = 40 + for (let i = 0; i < 6; i++) { + xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) + yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(xElec, yElec); + } + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + me.drawField = function () { + if (m.fieldMode != 2) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + const range = m.fieldRange; + ctx.save() + ctx.beginPath(); + ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; + ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2; + ctx.stroke(); + let eye = 13; + if (m.fieldMode == 2) { + eye = 30 + } + let aMag = 0.75 * Math.PI * m.fieldArc + let a = angle + aMag + let cp1x = this.position.x + 0.6 * range * Math.cos(a) + let cp1y = this.position.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)) + a = angle - aMag + cp1x = this.position.x + 0.6 * range * Math.cos(a) + cp1y = this.position.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc)) + ctx.fill(); + // ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); + + //draw random lines in field for cool effect + let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5); + ctx.beginPath(); + eye = 15; + ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); + ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle)); + ctx.strokeStyle = "rgba(0,0,0,0.6)"; + ctx.lineWidth = 1; + ctx.stroke(); + ctx.restore() + } else { + ctx.save() + ctx.beginPath(); + const wave = Math.cos(m.cycle * 0.022); + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = angle + aMag + let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) + let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) + a = angle - aMag + cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) + cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc)) + ctx.fill(); + ctx.restore() + } + } + me.pushM = function () { + const unit = Vector.normalise(Vector.sub(this.position, player.position)) + if (tech.blockDmg) { + Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y }); + //draw electricity + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { + let x = this.position.x - 20 * unit.x; + let y = this.position.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + m.damage(0.025 * simulation.dmgScale) + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#000"; + ctx.stroke(); + } + const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk + Matter.Body.setVelocity(player, { + x: this.velocity.x - (15 * unit.x) / massRoot, + y: this.velocity.y - (15 * unit.y) / massRoot + }); + } + me.diveAttack = function () { + const forceMag = this.accelMag * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += 150 * forceMag * Math.cos(angle); + this.force.y += 150 * forceMag * Math.sin(angle); + ctx.beginPath() + ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle)) + ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y) + aim = '#000000'; + ctx.stroke() + } + me.phase = 2 * Math.PI * Math.random(); + me.index2 = 0; + me.pilotWave = function () { + const rotate = this.cycle * 0.008; + this.fieldPhase += 0.002; + const off1 = 1 + 0.01 * Math.sin(this.fieldPhase); + const off2 = 1 - 0.01 * Math.cos(this.fieldPhase); + ctx.save() + ctx.beginPath(); + ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#ffffff"; //"#eef"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 4; + ctx.stroke(); + ctx.restore() + const range = this.curlRange / 15, mag = -50; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const applyCurl = function (center, array, isAntiGravity = true) { + for (let i = 0; i < array.length; ++i) { + if (!array[i].isNotHoldable) { + const sub = Vector.sub(center, array[i].position) + const radius2 = Vector.magnitudeSquared(sub); + + //if too close, like center mob or shield, don't curl // if too far don't curl + if (radius2 < range * range && radius2 > 10000) { + const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) + //apply curl force + if (array[i].isMobBullet) { + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.97 + curlVector.x * 0.06 - (Math.cos(angle) * 5), + y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 - (Math.sin(angle) * 5) + }) + } else { + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.94 + curlVector.x * 0.06 - (Math.cos(angle) * 5), + y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 - (Math.sin(angle) * 5) + }) + } + if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass + } + } + } + } + applyCurl(this.position, [player]); + } + me.teleportAway = function () {//hehe + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (this.seePlayer.recall && !(simulation.cycle % 17)) { + const dist = Vector.sub(this.position, this.seePlayer.position); + const distMag = Vector.magnitude(dist); + const unitVector = Vector.normalise(dist); + const rando = (Math.random() - 0.5) * 50; + if (distMag < 20000) { + Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); + } else { + Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando)); + } + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = "rgba(0,0,0,0.08)"; + ctx.stroke(); + if (!this.seePlayer.yes) { + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (this.seePlayer.recall && !(simulation.cycle % 17)) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + const unitVector = Vector.normalise(dist); + const rando = (Math.random() - 0.5) * 50; + if (distMag < 200000) { + Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); + } else { + Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando)); + } + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = "rgba(0,0,0,0.08)"; + ctx.stroke(); + } + } + me.timeAttack = function () { + if (this.seePlayer.recall && !(simulation.cycle % 30) || this.distanceToPlayer2() < 300) { + requestAnimationFrame(() => { + simulation.timePlayerSkip(45) + simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations + }); //wrapping in animation frame prevents errors, probably + } + } + me.harmonic3Phase = function () { //normal standard 3 different 2-d circles + if (tech.harmonics === 2) { + const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) { + me.pushM(); + } + for (let i = 0; i < bullet.length; i++) { + if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < m.fieldRange) { + const angle = Math.atan2(dy, dx); + const mag = (1500 * bullet[i].mass * simulation.g) / dist; + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + this.energy -= 0.0012; + } + } + } else { + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(0,0,0,0.5)" + ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`; + // ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + //360 block + if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) { + me.pushM(); + } + for (let i = 0; i < bullet.length; i++) { + if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < m.fieldRange) { + const angle = Math.atan2(dy, dx); + const mag = (1500 * bullet[i].mass * simulation.g) / dist; + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + this.energy -= 0.0012; + } + } + } + } + me.railGun = function () { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const X = this.position.x + const Y = this.position.y + const unitVector = { x: Math.cos(angle), y: Math.sin(angle) } + const unitVectorPerp = Vector.perp(unitVector) + + function magField(mag, arc) { + ctx.moveTo(X, Y); + ctx.bezierCurveTo( + X + unitVector.x * mag, Y + unitVector.y * mag, + X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, + X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) + ctx.bezierCurveTo( + X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, + X - unitVector.x * mag, Y - unitVector.y * mag, + X, Y) + } + ctx.fillStyle = `rgba(50,20,100,0.05)`; + const magSize = 8 * c * tech.railChargeRate ** 3 + const arcSize = 6 * c * tech.railChargeRate ** 3 + for (let i = 3; i < 7; i++) { + const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) + const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) + ctx.beginPath(); + magField(MAG, ARC) + magField(MAG, -ARC) + ctx.fill(); + } + } + me.waves = []; + me.doLongitudinal = function () { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + // const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767 + const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767 + const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this + for (let i = this.waves.length - 1; i > -1; i--) { + const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) + const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) + //draw wave + ctx.moveTo(v1.x, v1.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); + //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector + let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + player.force.x += 0.01 * (Math.random() - 0.5) * player.mass + player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity + Matter.Body.setVelocity(player, { //friction + x: player.velocity.x * 0.95, + y: player.velocity.y * 0.95 + }); + m.damage(damage) + } + hits = Matter.Query.ray(body, v1, v2, 50) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + const who = hits[j].body + //make them shake around + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity + + let vertices = who.vertices; + const vibe = 25 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + // ctx.stroke(); //draw vibes + this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move + if (this.waves[i].radius > end - 30) { + this.waves[i].expanding = -1 + if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end + } else if (this.waves[i].radius < 25) { + this.waves[i].expanding = 1 + if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + } + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [player]); + if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { + const dmg = 0.0011 * simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.lineWidth = 10; + ctx.stroke(); + } + me.pulse = function (charge, angle, where = this.position) { + let best; + angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) + let explosionRadius = 5.5 * charge + let range = 5000 + const path = [{ + x: where.x + 20 * Math.cos(angle), + y: where.y + 20 * Math.sin(angle) + }, + { + x: where.x + range * Math.cos(angle), + y: where.y + range * Math.sin(angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + if (!best.who) { + vertexCollision(path[0], path[1], body); + vertexCollision(path[0], path[1], [player]); + vertexCollision(path[0], path[1], map); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + } + if (best.who) { + b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)") + const off = explosionRadius * 1.2 + b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") + b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") + } + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + if (charge > 50) { + ctx.strokeStyle = "rgba(0,0,0,0.10)" + ctx.lineWidth = 70 + ctx.stroke(); + } + ctx.strokeStyle = "rgba(0,0,0,0.25)" + ctx.lineWidth = 20 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); + + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) { + const dist = Math.random() + simulation.drawList.push({ + x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5), + radius: 1.5 + 5 * Math.random(), + color: "rgba(0,0,0,0.5)", + time: Math.floor(9 + 25 * Math.random() * Math.random()) + }); + } + } + let c = 0 + me.gun = function () { + if (b.activeGun == 0) {// nailgun + if (this.seePlayer.recall && !(simulation.cycle % 20)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + const dist = Vector.magnitudeSquared(Vector.sub(this.position, player.position)); + const unit = Vector.normalise(Vector.sub(Vector.add(player.position, Vector.mult(player.velocity, Math.sqrt(dist) / 60)), this.position)) + normalBullet(this.position.x, this.position.y); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], Vector.mult(unit, 0.009 * this.mass)) + // Matter.Body.setVelocity(mob[mob.length - 1], { + // x: this.velocity.x + this.fireDir.x * v, + // y: this.velocity.y + this.fireDir.y * v + // }); + } + } + if (b.activeGun == 1) {// shotgun + if (this.seePlayer.recall && !(simulation.cycle % 90)) { + const side = 22 + for (let i = 0; i < 12; i++) { + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const dir = angle + (Math.random() - 0.5) * 1 + const SPEED = 52 + Math.random() * 8 + normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5)) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + } + } + } else if (b.activeGun == 2) { // super balls + if (this.seePlayer.recall && !(simulation.cycle % 20)) { + const num = 3; + const SPREAD = 0.13; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + let dir = angle - SPREAD * (num - 1) / 2; + const SPEED = 33 + for (let i = 0; i < num; i++) { + ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + dir += SPREAD + } + } + } else if (b.activeGun == 3) { // wave + this.doLongitudinal() + const halfArc = 0.275 + const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + const angle = anglex + 0.3 * (Math.random() - 0.5) + this.waves.push({ + position: { + x: this.position.x + 25 * Math.cos(anglex), + y: this.position.y + 25 * Math.sin(anglex), + }, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + reflection: 0, + expanding: 1, + resonanceCount: 0 + }) + } else if (b.activeGun == 4) { // missiles + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + missile(this.position); + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 5) { // grenades + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + grenade(this.position.x, this.position.y) + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 6) { // spores + if (this.seePlayer.recall && !(simulation.cycle % 30)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + me.drop(this.position.x, this.position.y) + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 7) { // drones + ctx.save() + ctx.lineWidth = "8"; + ctx.strokeStyle = "rgba(100, 0, 150, 0.1)"; + ctx.beginPath(); + for (let i = 0, len = bullet.length; i < len; ++i) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 500) { + ctx.moveTo(this.position.x + dist, this.position.y); + ctx.arc(this.position.x, this.position.y, dist, 0, 2 * Math.PI) + //ctx.lineTo(bullet[i].position.x, bullet[i].position.y); + const angle = Math.atan2(dy, dx); + const mag = (1500 * bullet[i].mass * simulation.g) / (dist * 0.05); + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + } + ctx.stroke(); + ctx.restore() + } else if (b.activeGun == 8) { // foam + if (this.seePlayer.recall && !(simulation.cycle % 1)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69); + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 9) { // harpoon - railgun + if (c > 1) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + railBullet(this.position.x, this.position.y); + const v = 10 + 80 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + c = 0; + } else { + c += 0.02; + this.railGun(); + } + } else if (b.activeGun == 10) { // laserMines + if (this.seePlayer.recall && !(simulation.cycle % 100)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + me.laserMine(this.position.x, this.position.y) + const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v, + y: this.velocity.y + this.fireDir.y * v + }); + } + } else if (b.activeGun == 11) { // laser - pulse + //this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) + //if (this.seePlayer.recall && !(simulation.cycle % 20)) { + if (c > 1) { + this.pulse(c * 100) + c = 0; + } else { + if (this.energy < 1 || this.energy > 0.5) { + c += 0.01; + ctx.beginPath(); + const mag = Math.sqrt(c) + ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI) + ctx.fillStyle = '#000000' + ctx.strokeStyle = 'transparent' + ctx.fill(); + ctx.stroke(); + this.energy -= 0.01; + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 1.5 + // ctx.globalAlpha = 1; + } else { + c = 0; + this.energy += 0.1 + } + } + //} + } + } + me.laserMine = function (x, y) { + mobs.spawn(x, y, 3, 20, "#000000"); + let xx = mob[mob.length - 1]; + xx.stroke = "#00000000"; + Matter.Body.setDensity(xx, 0.000005) //one tap + xx.isUnstable = true; + xx.timeLeft = 40 + Math.floor(180 * Math.random()) + xx.leaveBody = false; + xx.isDropPowerUp = false; + xx.collisionFilter.mask = cat.bullet | cat.player | cat.map + xx.showHealthBar = false; + //xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position); + me.onHit = function () { + this.death(); + }; + xx.do = function () { + this.timeLimit(); + Matter.Body.setAngularVelocity(this, 0.01) + ctx.beginPath(); + ctx.lineWidth = 1; + ctx.strokeStyle = "#00000000" + for (let i = 0; i < this.vertices.length; i++) { + const where = this.vertices[i] + const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500)) + me.lasers(this.vertices[0], this.angle + Math.PI / 3); + me.lasers(this.vertices[1], this.angle + Math.PI); + me.lasers(this.vertices[2], this.angle - Math.PI / 3); + } + ctx.strokeStyle = "black"; + ctx.stroke(); + ctx.save() + ctx.beginPath(); + ctx.moveTo(this.vertices[0].x, this.vertices[0].y); + ctx.lineTo(this.vertices[1].x, this.vertices[1].y); + ctx.lineTo(this.vertices[2].x, this.vertices[2].y); + ctx.fillStyle = "#000000"; + ctx.strokeStyle = "transparent"; + ctx.fill(); + ctx.closePath(); + ctx.stroke(); + ctx.restore() + } + } + me.seeker = function (x, y) { + mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)"); + let yy = mob[mob.length - 1]; + yy.stroke = "transparent"; + yy.onHit = function () { + this.explode(this.mass * 20); + }; + Matter.Body.setDensity(yy, 0.000015); //normal is 0.001 + yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random()); + yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + yy.restitution = 0.5; + yy.leaveBody = false; + yy.isDropPowerUp = false; + yy.isBadTarget = true; + yy.isMobBullet = true; + yy.showHealthBar = false; + yy.collisionFilter.category = cat.mobBullet; + yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + let index = 0; + yy.do = function () { + this.alwaysSeePlayer() + this.timeLimit(); + this.attraction(); + }; + } + me.drop = function (x, y) { + mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)"); + let yyy = mob[mob.length - 1]; + yyy.stroke = "transparent"; + yyy.onDeath = function () { + for (let i = 0, len = 5; i < len; i++) { + me.seeker(this.position.x, this.position.y) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: Math.random() * 30 - Math.random() * 30, + y: Math.random() * 30 - Math.random() * 30 + }); + } + }; + Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001 + yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random()); + yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + yyy.restitution = 0.5; + yyy.leaveBody = false; + yyy.isDropPowerUp = false; + yyy.isBadTarget = true; + yyy.isMobBullet = true; + yyy.showHealthBar = false; + yyy.collisionFilter.category = cat.mobBullet; + yyy.collisionFilter.mask = null; + yyy.maxRadius = 30; + let index = 0; + yyy.do = function () { + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + Matter.Body.setPosition(this, player.position) + if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) + } + if (Matter.Query.collides(this, map).length > 0) { + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.1)) + } + this.alwaysSeePlayer() + this.timeLimit(); + ctx.save() + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y) + ctx.fillStyle = "black"; + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI) + ctx.stroke() + ctx.fill() + ctx.restore() + if (this.maxRadius > 0) { + this.maxRadius -= 0.5; + } + }; + } + }; + restoreBoss(-13350, -1800); + laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); + //restoreBoss(-350, -3225); + wire(); + wire(); + wire(); + wire(); + wire(); + color.map = '#00000000'; + level.customTopLayer = () => { + if (dong.position.x > -3825) { + dong.force.y -= dong.mass * simulation.g; + } else { + dong.force.y += dong.mass * simulation.g; + } + Matter.Body.setAngularVelocity(dong, -0.5) + if (key == true) { + door.isClosing = false; + } else { + door.isClosing = true; + } + door.openClose(); + door.draw() + for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map + ctx.beginPath(); + ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y); + for (let j = 0, length = map[i].vertices.length; j < length; j++) { + ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y); + } + ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y); + ctx.fillStyle = "rgba(68,68,68)"; + ctx.strokeStyle = "transparent"; + ctx.fill(); + ctx.stroke(); + // ctx.setLineDash([]); + } + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].restoreBoss) { + ctx.save(); + ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); + ctx.restore(); + } + } + }; + const obj = { restoreBoss }; + Object.assign(spawn, obj); //for next map, gonna be a rpg-like thingy I think + }, + map() { + + const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550) + 15900 && player.position.x < 16300 && player.position.y > -960.2 + const slime = level.hazard(15900, -960, 400, 6000); + const slime2 = level.hazard(15147.2, -1782.4, 2000, 822); + const boost1 = level.boost(5950, -20, 700) + const boost2 = level.boost(21088, -1672, 700) + const boost3 = level.boost(19390, -31, 1700) + const boost4 = level.boost(19390, -31, 1700) + const boost5 = level.boost(17274, -1242, 1000) + const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI) + const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI) + const buttonDoor = level.button(21889, -10) + const door = level.door(19119, -2133, 110, 510, 480) + const buttonDoor2 = level.button(18711, -2210) + const door2 = level.door(17041, -412, 110, 510, 480) + const buttonDoor3 = level.button(20456.6, -1636.2) + const door3 = level.door(20238, -781.4, 88, 452, 412) + //y=-1485 + + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 15316; + level.exit.y = -84; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#001738"; + color.map = "#444" //custom map color + + + + level.custom = () => { + //spawn.mapRect(22330, -2688.75, 400, 800); + //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5 + //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3 + + ctx.fillStyle = "rgba(63,247,251,0.8)" + ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2 + ctx.fillRect(22330, -1743.5, 550, 700) + ctx.fillRect(22330, -754.25, 550, 700) + ctx.fillRect(15845.0, -1262.2, 550, 300) + ctx.fillStyle = "rgba(235,235,235,0.9)" + ctx.fillRect(-192, -1973, 6484, 2071) + ctx.fillRect(15109.5, -2867.5, 7284, 2971) + ctx.fillStyle = "rgba(35,35,35,0.8)" + ctx.fillRect(15145.9, -960, 200, 25) + + ctx.fillStyle = "rgba(255,255,255,0.9)" + ctx.fillRect(-677.3, -610.9, 15, 15) + ctx.fillRect(-910.4, 458.3, 15, 15) + ctx.fillRect(-1029.0, 713.7, 15, 15) + ctx.fillRect(42.6, 1332.2, 15, 15) + ctx.fillRect(277.3, 751.8, 15, 15) + ctx.fillRect(797.1, 553.2, 15, 15) + ctx.fillRect(-1458.9, 340.9, 15, 15) + ctx.fillRect(-1780.0, -54.6, 15, 15) + ctx.fillRect(-1260.3, -686.4, 15, 15) + ctx.fillRect(-2064.3, -394.6, 15, 15) + ctx.fillRect(-1815.7, 1156.2, 15, 15) + ctx.fillRect(-1998.1, 1118.4, 15, 15) + + + + buttonDoor.query(); + buttonDoor.draw(); + buttonDoor2.query(); + buttonDoor2.draw(); + buttonDoor3.query(); + buttonDoor3.draw(); + slime.query(); + slime2.query(); + + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` + ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000) + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + if (buttonDoor2.isUp) { + door2.isClosing = true + } else { + door2.isClosing = false + } + if (buttonDoor3.isUp) { + door3.isClosing = true + } else { + door3.isClosing = false + } + door.openClose(); + door2.openClose(); + door3.openClose(); + portal[2].query() + portal[3].query() + portal2[2].query() + portal2[3].query() + + boost1.query(); + boost2.query(); + boost3.query(); + boost4.query(); + boost5.query(); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + + + level.customTopLayer = () => { + door.draw(); + door2.draw(); + door3.draw(); + + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + elevator.move() + + if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 10 + }); + } else { + + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y - 0.2 + }); + + } + + }; + + + //1273.2, -1404.7 + + //first ship base + spawn.mapRect(-300, 0, 6684, 100); //lower floor + spawn.mapRect(-300, -2071, 154, 2071); //far right wall + spawn.mapRect(2511, -300, 1309, 308); //left big block + spawn.mapRect(3820, -184, 1309, 184); //right big block + spawn.mapRect(-300, -739, 2549, 100); //upper right floor + spawn.mapRect(2056, -1309, 2764, 169); //upper center floor + spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall + spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall + spawn.mapRect(4821, -654, 955, 138); //upper right floor + spawn.mapRect(6237, -2071, 147, 2071); //far right wall + spawn.mapRect(-300, -2071, 6684, 154); //roof + + //first ship details + spawn.mapRect(245, -360, 70, 400); //start room wall + spawn.mapRect(500, -1929, 154, 462); + spawn.mapRect(185, -1517, 469, 77); + spawn.mapRect(2773, -682, 469, 77); //walls in 1st room + spawn.mapRect(3743, -566, 77, 469); + spawn.mapRect(3947, -851, 469, 77); + spawn.mapRect(5313, -1309, 1000, 70); //walls in second area + spawn.mapRect(4818, -1006, 400, 70); + spawn.mapRect(4768, -1626, 800, 70); + spawn.mapRect(4760, -1626, 70, 400); + + + //first ship blocks/debris + spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level + spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level + spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level + spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level + spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level + spawn.bodyRect(1540, -1110, 218, 125, 0.9); + + + + //first ship mobs + spawn.randomMob(2903.9, -754.5, 0.7); + spawn.randomMob(5577.0, -217.0, 0.6); + spawn.randomMob(765.8, -1029.7, 0.5); + spawn.randomMob(20079.4, -2219.7, 0.6); + spawn.randomMob(20079.4, -2219.7, 0.7); + spawn.randomMob(20890.9, -1306.0, 0.5); + spawn.randomMob(21284.2, -983.1, 0.5); + spawn.randomMob(20381.0, -254.2, 0.7); + spawn.randomMob(21027.8, -473.8, 0.6); + spawn.randomMob(19448.2, -1323.3, 0.6); + spawn.randomMob(18397.7, -711.2, 0.6); + spawn.randomMob(15547.2, -2249.6, 0.6); + spawn.randomSmallMob(16114.6, -2524.2); + spawn.randomSmallMob(15378.9, -2549.6); + + spawn.randomSmallMob(893.5, -120.8); + spawn.randomSmallMob(3521.8, -419.6); + spawn.randomSmallMob(4386.2, -439.6); + spawn.randomSmallMob(5667.0, -847.8); + spawn.randomSmallMob(3158.5, -1581.8); + spawn.randomSmallMob(3866.7, -1483.2); + spawn.randomSmallMob(4652.3, -1729.4); + spawn.randomSmallMob(1068.7, -106.1); + spawn.randomSmallMob(3545.0, -413.0); + spawn.randomSmallMob(4231.7, -446.3); + spawn.randomSmallMob(1456.4, -1014.8); + spawn.randomSmallMob(20432.4, -1374.3); + spawn.randomSmallMob(20381.0, -254.2); + spawn.randomSmallMob(20353.4, -1845.8); + spawn.randomSmallMob(20353.4, -1845.8); + spawn.randomSmallMob(20648.1, -136.8); + spawn.randomSmallMob(20024.4, -2213.1); + spawn.randomSmallMob(17438.7, -876.7); + + + + //second ship mobs + spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level + spawn.debris(18006.4, -2181.3, 700, 5); //16 debris per level + spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level + spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level + spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level + + + + + + + + spawn.randomSmallMob(1300, -70); + + + // const index = mob.length + spawn.shieldingBoss(769.8, -1119.0) + // console.log(mob[index].onDeath) + // requestAnimationFrame(() => mob[index].onDeath = function() {}); + // console.log(mob[index].onDeath) + + //second ship base + spawn.mapRect(15000, 0, 515, 185); //lower floor 1 + spawn.mapRect(17015, 0, 5500, 185); //lower floor 2 + spawn.mapRect(15000, -2972, 185, 2972); //left wall + spawn.mapRect(15000, -2972, 7515, 185); //roof + spawn.mapRect(22330, -2972, 185, 2972); //right wall + spawn.mapRect(17002, -2972, 169, 2564); //left middle wall + spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper + spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower + spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal + spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor + spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal + spawn.mapRect(19720, -855, 2725, 162); //right room lower floor + + //engines //y -2972 -> 0 + spawn.mapRect(22330, -2763.75, 400, 800); + spawn.mapRect(22330, -1793.5, 400, 800); + spawn.mapRect(22330, -804.25, 400, 800); + + + + //second ship details + spawn.mapRect(19904, -1465, 85, 362); //upper L + spawn.mapRect(19542, -1191, 412, 88); //lower L + spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall + spawn.mapRect(18546, -2499, 82, 2300); + spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room + spawn.mapRect(17750, -682, 300, 82); + spawn.mapRect(17156, -468, 500, 60); + spawn.mapRect(18022, -1082, 600, 82); + spawn.mapRect(17151, -1196, 500, 82); + spawn.mapRect(17453, -2060, 500, 82); + spawn.mapRect(18197, -2269, 400, 82); + spawn.mapRect(18108, -326, 500, 82); + spawn.mapRect(20542, -1191, 612, 88); + spawn.mapRect(20238, -1191, 88, 412); + spawn.mapRect(21520, -1468, 88, 412); + spawn.mapRect(20238, -330.2, 88, 412); + spawn.mapRect(20819, -328.3, 412, 88); + spawn.mapRect(21532, -708, 88, 412); + spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor + spawn.mapRect(15487.6, 76.6, 488, 24); + spawn.mapRect(15506.5, 134.2, 288, 45); + spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30"); + spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20"); + //tank + spawn.mapRect(15310, -960, 600, 135); + spawn.mapRect(16290, -960, 800, 135); + //in tank + spawn.mapRect(16524.8, -2726.8, 40, 400); + spawn.mapRect(16524.8, -2130.9, 400, 40); + spawn.mapRect(16010.2, -2412.2, 300, 40); + spawn.mapRect(15379.2, -2055.1, 400, 40); + + + + //add fuel tanks in the last room + + + spawn.mapRect(21531.9, -707.8, 488, 8); + + //22185.5, -114.8 + spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); + spawn.mapRect(22056.6, -70, 225, 212); + + spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300"); + spawn.mapRect(20571.9, -1701.0, 225, 212); + + spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); + spawn.mapRect(22056.6, -70, 225, 212); + //spawn.mapVertex(x,y, "coordinates") + //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction + + //second ship blocks/debris + spawn.bodyRect(21525, -113, 50, 50, 9); //first button block + spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block + spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block + + + + let randomBoss = Math.floor(Math.random() * 5); //change the bosses + spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false); + + + + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + cantilever() { // made by Eclipse#7932 on discord, (TheSpudguy)(@PurpleSunsetGames on github) + // simulation.enableConstructMode(); + simulation.makeTextLog(`underpass by Eclipse#7932`); + + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 5500; + level.exit.y = 950; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + spawn.mapRect(level.exit.x - 50, level.exit.y + 30, 200, 100); // exit platform + spawn.mapRect(level.exit.x - 50, level.exit.y - 300, 200, 100); // exit platform roof + const endElevator = level.elevator(level.exit.x - 150, level.exit.y - 300, 100, 425, level.exit.y - 300); // end access door + + spawn.randomMob(-200, 350, Infinity); // random mob at the beginning + + spawn.mapRect(-100, 0, 600, 100); // main platform at start + spawn.bodyRect(0, 300, 50, 50); // little squares at start (one of these should be taken while crossing the cantilever to complete the level more easily) + spawn.bodyRect(100, 200, 50, 50); + spawn.bodyRect(50, 250, 50, 50); + spawn.mapRect(450, -20, 50, 20); // main platform ledge + spawn.mapRect(-200, 500, 2200, 100); // lower platform + spawn.mapRect(1850, 380, 100, 50); // cantilever block + spawn.bodyRect(80, -1300, 70, 1300, 1, { friction: .03, frictionAir: .001 }); // cantilever + + spawn.mapRect(3400, 500, 300, 100); // lever platform + spawn.mapRect(3650, 500, 100, 800); // pit + spawn.mapRect(3650, 1300, 2600, 100); + spawn.mapRect(6150, 600, 100, 800); + spawn.mapRect(5650, 600, 100, 650); + spawn.randomMob(4700, 550, Infinity); + spawn.randomMob(4700, 450, Infinity); + spawn.randomMob(4600, 550, Infinity); + + const toggle = level.toggle(3500, 500, false); // first lever + const button = level.button(5900, 1300); + const slidingWall = level.elevator(3750, -1200, 100, 1800, -1200); // first sliding wall + + level.defaultZoom = 1500; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8badf"; + // color.map = "#444" //custom map color + + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + }; + + level.customTopLayer = () => { + toggle.query(); + button.query(); + button.draw(); + + if (toggle.isOn) { + slidingWall.force.y -= 400; + } + if (!button.isUp) { + endElevator.force.y -= 100; + } + slidingWall.move(); + endElevator.move() + }; + powerUps.addResearchToLevel(); //needs to run after mobs are spawned + }, + dojo() { // By + simulation.makeTextLog(`underpass by weird_pusheen`); + + const vanishes = []; + const smoofes = []; + const leftRotor = level.rotor(-550, 900, 950, 25); + leftRotor.frictionAir = 0.01; + var leftSchwoof = level.boost(-20, -60, -2000); + var rightSchwoof = level.button(2550, -50); + var rightSchwoofState = false; + var rightSchwoofLive = true; + spawn.mapRect(2513, -39, 200, 100); + var pathPoints = [ + [0, 0], // Index 0 is owned by M and is set to M's position during play + // this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it + [250, -750], /* Left bases */ + [250, -2500], + [350, -1500], // Left doorway + [1150, -1500], // Home base + [1150, -2750], // Upper base + [1950, -1500], // Right doorway + [2050, -750], /* Right bases */ + [2050, -2500], + [-150, -250], // Left porthole + ]; + function isntIn(point, array) { + for (var x = 0; x < array.length; x++) { + if (point[0] == array[x][0] && point[1] == array[x][1]) { + return false; + } + } + return true; + } + function isObstructed(v1, v2) { + var ret = Matter.Query.ray(map, + { + x: v1[0], + y: v1[1], + }, + { + x: v2[0], + y: v2[1] + }).length != 0; + return ret; // Kinda-ish stolen from mob.js + } + function pythag(p1, p2) { + var dx = p1[0] - p2[0]; + var dy = p1[1] - p2[1]; + return Math.sqrt(dx * dx + dy * dy); + } + var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up! + function pathFind(goalPoint, startPoint, curPath = []) { + var myPoint = startPoint; + if (curPath.length) { + myPoint = curPath[curPath.length - 1]; + } + if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time + return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy + } + if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived! + path = [...curPath]; + path.push(goalPoint); + return; + } + pathPoints.forEach(testPoint => { + if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong + if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object + var thing = [...curPath]; + thing.push(testPoint); + pathFind(goalPoint, startPoint, thing); // Branch to a valid test point + } + } + }); + } + level.setPosToSpawn(1200, 500); + level.exit.x = 51500; + level.exit.y = -1875; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.defaultZoom = 1500; + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + + spawn.mapRect(-500, 0, 3300, 300); // Floor + spawn.mapRect(-100, -3000, 2500, 100); // Ceiling + spawn.mapRect(-200, -3000, 100, 2600); // Left wall + spawn.mapRect(2400, -3000, 100, 3000); // Right wall + + spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */ + smoofes.push(map[map.length - 1]); + spawn.mapRect(500, -2500, 100, 500); + smoofes.push(map[map.length - 1]); + spawn.mapRect(1700, -1000, 100, 500); + smoofes.push(map[map.length - 1]); + spawn.mapRect(1700, -2500, 100, 500); + smoofes.push(map[map.length - 1]); + + spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low + spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high + spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky + spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low + spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high + spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky + const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500); + const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500); + + spawn.mapRect(-150, -1700, 200, 50); + spawn.mapRect(400, -2050, 200, 50); + spawn.mapRect(1600, -1000, 200, 50); + + spawn.randomMob(1200, 700); + spawn.randomMob(600, 1000); + spawn.randomMob(1800, 1000); + spawn.randomMob(3200, 400); + spawn.randomMob(3000, 200); + spawn.randomMob(-900, 400); + spawn.randomMob(-700, 200); + spawn.randomMob(1200, 1000); + for (var i = 0; i < 4; i++) { + spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400); + } + spawn.grenadier(-300, -1000); + spawn.grenadier(2600, -1000); + + spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor + + const slime = level.hazard(-1250, 1400, 4800, 50); + slime.maxHeight = 600; + simulation.draw.body = function () { + ctx.beginPath(); + for (let i = 0, len = body.length; i < len; ++i) { + if (!body[i].hidden) { + let vertices = body[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } + } + ctx.lineWidth = 2; + ctx.fillStyle = color.block; + ctx.fill(); + ctx.strokeStyle = color.blockS; + ctx.stroke(); + } // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent + + const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote + x += width / 2; + y += height / 2; + const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] }; + const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + const vertices = getVertices(x, y, width, height); + const block = body[body.length] = Bodies.fromVertices(x, y, vertices, { + collisionFilter: { + category: cat.map, + mask: cMask + }, + isNoSetCollision: true, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + isNonStick: true, //this keep sporangium from sticking + isTouched: false, + cWidth: width, + hiddenCycle: 0, + isStatic: true, + query() { + if (this.cWidth <= 0) { + if (this.cWidth > -100) { + this.cWidth = -100; + Matter.Body.setVertices(this, vertices); + } + this.isTouched = false; + this.collisionFilter.mask = undefined; + this.hidden = true; + this.hiddenCycle++; + if (this.hiddenCycle > 100) { + if (Matter.Query.collides(this, [player]).length) { + this.hiddenCycle = 50; + } + else { + this.hiddenCycle = 0; + this.cWidth = width; + this.collisionFilter.mask = cMask; + this.hidden = false; + } + } + } + else if (this.isTouched) { + Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width))); + this.cWidth -= 3; + } + else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection + this.isTouched = true; + } + } + }); + return block; + }; + + vanishes.push(vanish(800, 800, 800, 50)); + vanishes.push(vanish(400, 1100, 400, 50)); + vanishes.push(vanish(1600, 1100, 400, 50)); + spawn.bodyRect(1700, 812, 300, 25, 1, { + collisionFilter: { + category: cat.body, + mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map + }, + isNoSetCollision: true, + isNotHoldable: true, + isNonStick: true, //this keep sporangium from sticking + restitution: 1, + friction: 0, + frictionAir: 0, + frictionStatic: 0, + query() { + Matter.Body.setAngularVelocity(this, 0); + Matter.Body.applyForce(this, this.position, { + x: 0, + y: -(this.position.y - 812) * 0.002 + }); + } + }); + const zigzag = body[body.length - 1]; + Matter.Body.applyForce(zigzag, zigzag.position, { + x: 0.1, + y: 0 + }); + var buttonWasDown = false; + level.customTopLayer = () => { + + } + level.custom = () => { + rightSchwoof.isUp = false; + level.exit.drawAndCheck(); + leftSchwoof.query(); + level.enter.draw(); + pathPoints[0][0] = m.pos.x; + pathPoints[0][1] = m.pos.y; + leftElevator.move(); + rightElevator.move(); + slime.query(); + zigzag.query(); + slime.levelRise(0.2); + for (var i = 0; i < vanishes.length; i++) { + vanishes[i].query(); + } + if (!rightSchwoofState) { + var math = m.pos.y < leftRotor.position.y; + Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45); + } + if (rightSchwoofLive) { + rightSchwoof.query(); + rightSchwoof.draw(); + if (rightSchwoofState) { + ctx.fillStyle = "lightgreen"; + } + else { + ctx.fillStyle = "red"; + } + ctx.beginPath(); + ctx.arc(2615, -220, 40, 0, Math.PI * 2); + ctx.fill(); + } + if (rightSchwoof.isUp) { + buttonWasDown = true; + } + else if (buttonWasDown) { + buttonWasDown = false; + rightSchwoofState = !rightSchwoofState; + } + if (Matter.Query.collides(player, smoofes).length) { + Matter.Body.applyForce(player, player.position, { + x: 0, + y: -0.015 + }); + } + }; + + mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss + Modes: + Spawn: + Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course. + Occasionally strikes at M. + Hide: + Pathfinds to the point furthest from M + Strike: + Pathfind really, really fast to M + Recharge: + Stop moving for a bit to "recharge" (this is so the player has a chance to hit it) + + It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out. + Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these. + */ + var boss = mob[mob.length - 1]; + boss.isBoss = true; + boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + boss.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + level.exit.x = 2560; + level.exit.y = -90; + rightSchwoofLive = false; + }; + var spawnables = {}; + ["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */ + if (spawn[m]) { + spawnables[m] = { + fun: spawn[m], + name: m, + weight: 1 + } + } + }); + boss.stabCycle = 0; + boss.spawnCycle = 0; + function spawny() { + var totalWeight = 0; + Object.keys(spawnables).forEach(key => { + totalWeight += spawnables[key].weight; + }); + var cursorWeight = 0; + var choice = Math.random(); + var mC = undefined; + Object.values(spawnables).forEach((thing) => { + var lower = cursorWeight / totalWeight; + cursorWeight += thing.weight; + var upper = cursorWeight / totalWeight; + if ((choice > lower && choice <= upper) || !mC) { + mC = thing; + } + }); + mC.fun(boss.position.x, boss.position.y); + var sp = mob[mob.length - 1]; + sp.typeName = mC.name; + sp.onHit = () => { + spawnables[sp.typeName].weight += 1; + }; + var oldFun = sp.onDeath; + sp.onDeath = () => { /* Mobs that die are worth less */ + oldFun.call(sp); + spawnables[sp.typeName].weight -= 0.3; /* But not too much less */ + }; + } + boss.spawnDelay = 40; + boss.mode = "hide"; + boss.modeSwitch = -1; // Randomize mode immediately + boss.damageReduction = 0.1; + var oldOnHit = boss.onHit; + boss.onHit = () => { + boss.modeSwitch = -1; // After striking the player, always switch modes + oldOnHit.call(boss); + }; + boss.do = () => { + path = undefined; + var pfGoal = [0, 0]; + boss.modeSwitch--; + if (boss.modeSwitch < 0) { + if (!boss.isShielded) { + spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield + } + if (boss.mode == "hide" || boss.mode == "recharge") { + if (Math.random() > 0.5) { + boss.mode = "spawn"; + } + else { + boss.mode = "strike"; + } + boss.modeSwitch = 600; + } + else { + if (boss.mode == "strike") { + boss.mode = "hide"; // Always hides after striking + } + else { + if (Math.random() > 0.5) { + boss.mode = "hide"; + } + else { + boss.mode = "recharge"; // same when it goes into recharge mode + spawn.shield(boss, boss.position.x, boss.position.y, 1); + } + } + boss.modeSwitch = 200; + } + } + if (boss.mode == "hide") { /* Find the furthest point from M and get to it */ + var longest = 0; + pathPoints.forEach(item => { + if (item[0] == 1150) { + return; + } + var iL = pythag(item, [m.pos.x, m.pos.y]); + if (iL > longest) { + longest = iL; + pfGoal = item; + } + }); + } + else if (boss.mode == "strike") { + pfGoal = pathPoints[0]; // Target M + } + else if (boss.mode == "spawn") { + pfGoal = pathPoints[4]; // Go to Home Base to spawn + } + if (boss.mode != "recharge") { + if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) { + boss.mode = "hide"; + } + pathFind(pfGoal, [boss.position.x, boss.position.y]); + if (!path) { + return; // If it couldn't pathfind, just drift + } + var goalX = path[0][0]; + var goalY = path[0][1]; + + var dX = goalX - boss.position.x; + var dY = goalY - boss.position.y; + var hyp = Math.sqrt(dX * dX + dY * dY); + Matter.Body.applyForce(boss, { + x: goalX, + y: goalY + }, { + x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1), + y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005 + }); + } + if (boss.mode == "spawn") { + boss.stabCycle++; + if (boss.stabCycle > 25) { + if (Math.abs(dX) < 200 && dY > 0) { + Matter.Body.applyForce(boss, { + x: player.position.x, + y: player.position.y + }, { + x: 0, + y: 5 + }); + } + boss.stabCycle = 0; + } + boss.spawnCycle++; + if (boss.spawnCycle > boss.spawnDelay) { + spawny(); + boss.spawnDelay += 4; + boss.spawnCycle = 0; + } + } + }; + boss.showHealthBar = true; + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + tlinat() { // _Destined_ formerly Richard0820#2652 + simulation.makeTextLog(`tlinat by Richard0820`); + simulation.fallHeight = 1 / 0, level.setPosToSpawn(0, -1e3), level.exit.x = 5100, level.exit.y = 3770, spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20), spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20), level.defaultZoom = 3000, simulation.zoomTransition(level.defaultZoom), document.body.style.backgroundColor = "#d8dadf"; + let e = 0, + t = 0; + const boidsFlocking = function (mob, otherMobs) { + const cohesionFactor = 0.01; + const separationFactor = 0.0001; + const alignmentFactor = 0.04; + let averagePosition = { x: 0, y: 0 }; + let averageVelocity = { x: 0, y: 0 }; + let nearbyMobsCount = 0; + for (const otherMob of otherMobs) { + if (otherMob !== mob) { + const distanceSquared = Vector.magnitudeSquared(Vector.sub(mob.position, otherMob.position)); + const boidRangeSquared = 300 * 300; // Adjust boid range as needed + if (distanceSquared < boidRangeSquared) { + averagePosition = Vector.add(averagePosition, otherMob.position); + averageVelocity = Vector.add(averageVelocity, otherMob.velocity); + nearbyMobsCount++; + } + } + } + if (nearbyMobsCount > 0) { + averagePosition = Vector.div(averagePosition, nearbyMobsCount); + averageVelocity = Vector.div(averageVelocity, nearbyMobsCount); + const cohesionForce = Vector.mult(Vector.sub(averagePosition, mob.position), cohesionFactor); + mob.force = Vector.add(mob.force, cohesionForce); + const separationForce = Vector.mult(Vector.sub(mob.position, averagePosition), separationFactor); + mob.force = Vector.add(mob.force, separationForce); + const alignmentForce = Vector.mult(Vector.sub(averageVelocity, mob.velocity), alignmentFactor); + mob.force = Vector.add(mob.force, alignmentForce); + } + }; + function ghoster(x, y, radius = 50 + Math.ceil(Math.random() * 90)) { + mobs.spawn(x, y, 7, radius, "transparent"); + let me = mob[mob.length - 1]; + me.seeAtDistance2 = 300000; + me.accelMag = 0.00004 + 0.00015 * simulation.accelScale; + if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search + // Matter.Body.setDensity(me, 0.0015); //normal is 0.001 + me.damageReduction = 0.5 + me.stroke = "transparent"; //used for drawGhost + me.alpha = 1; //used in drawGhost + me.isNotCloaked = false; //used in drawGhost + me.isBadTarget = true; + // me.leaveBody = false; + me.collisionFilter.mask = cat.bullet //| cat.body + me.showHealthBar = false; + me.memory = 600; + me.do = function () { + boidsFlocking(me, mob);//Stack, increase power. + if (this.speed > 7) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.8, + y: this.velocity.y * 0.8 + }); + } + this.seePlayerCheckByDistance(); + this.checkStatus(); + this.attraction(); + this.search(); + //draw + if (this.distanceToPlayer2() < this.seeAtDistance2) { + if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid + } else { + if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide + } + if (this.alpha > 0) { + if (this.alpha > 0.7 && this.seePlayer.recall) { + this.healthBar(); + if (!this.isNotCloaked) { + this.isNotCloaked = true; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.bullet + } + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 1; + ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`; + ctx.fill(); + } else if (this.isNotCloaked) { + this.isNotCloaked = false; + this.isBadTarget = true; + this.collisionFilter.mask = cat.bullet; //can't touch player or walls + } + }; + } + function o(e, t, o) { + const l = { + J: [" #### ", " # ", " # ", " # ", " # # ", " # # ", " ## "], + I: [" # ", " # ", " # ", " # ", " # ", " # ", " # "], + N: [" # # ", " ## # ", " ## # ", " # ## ", " # ## ", " # # ", " # # "], + " ": [" ", " ", " ", " ", " ", " ", " "], + O: [" ## ", " # # ", " # # ", " # # ", " # # ", " # # ", " ## "], + U: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " ### "], + R: [" #### ", " # #", " #### ", " # # ", " # # ", " # #", " # #"], + D: [" ### ", " # ## ", " # # ", " # # ", " # # ", " # ## ", " ### "], + S: [" #### ", " # ", " # ", " ### ", " ## ", " # ", " ##### "], + C: [" ##### ", " # ", " # ", " # ", " # ", " # ", " ##### "], + V: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " # "], + E: [" ##### ", " # ", " # ", " ##### ", " # ", " # ", " ##### "], + ".": [" ", " ", " ", " ", " ", " ## ", " ## "], + "/": [" #", " # ", " # ", " # ", " # ", " # ", "# "], + G: [" ###### ", " # ", " # ", " # ### ", " # # ", " # # ", " ###### "], + Q: [" ###### ", " # # ", " # # ", " # # ", " # # # ", " # # ", " #### # ", " # "], + 8: [" ##### ", " # # ", " # # ", " ##### ", " # # ", " # # ", " ##### "], + g: [" ##### ", " # # ", " # # ", " ##### ", " # ", " # ", " ###### "], + Y: [" # # ", " # # ", " # # ", " # ", " # ", " # ", " # "], + 4: [" # ", " # # ", " # # ", " # # ", " ###### ", " # ", " # "], + W: [" # # ", " # # ", " # # ", " # # # ", " # # # # ", " ## ## ", " # # "], + e: [" ###### ", " # # ", " # # ", " ####### ", " # ", " # ", " ###### "], + c: [" ###### ", "# ", "# ", "# ", "# ", "# ", " ###### ", " "], + m: [" # ", " ### ### ", " # # # ", " # # # ", " # # # ", " # # # ", " # # # "] + }, + a = (e, t) => { + ctx.fillStyle = "black", ctx.fillRect(e, t, 50, 50) + }, + n = (e, t, o) => { + const n = l[e]; + if (n) + for (let e = 0; e < n.length; e++) { + const l = n[e]; + for (let n = 0; n < l.length; n++) { + if ("#" === l[n]) { + a(t + 20 * n, o + 20 * e) + } + } + } + }; + for (let l = 0; l < o.length; l++) { + n(o[l], e + 250 * l - Math.abs(1.5 * e), t) + } + } + simulation.makeTextLog(`
Look up
Walk right to tp to maze
Exit is at the bottom left`), Matter.Body.scale(player.parts[3], 2, 2), level.custom = () => { + if (level.exit.drawAndCheck(), level.enter.draw(), player.position.y > 1e5 && Matter.Body.setPosition(player, { + x: 5100, + y: -5925 + }), player.position.x > 2500 && 0 == e) { + Matter.Body.setPosition(player, { + x: 5100, + y: -5925 + }), e++; + for (let e = 0; e < map.length; e++) Math.random() < .75 && ghoster(map[e].position.x, map[e].position.y); + simulation.makeTextLog("Watch out for ghosters
Peace ✌️") + } + player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && player.position.y < level.exit.y - 0 && player.velocity.y < .15 && 0 == t && (t++, Matter.Body.scale(player.parts[3], .5, .5)) + }, level.customTopLayer = () => { + player.position.x > -1200 && player.position.x < 4500 && (o(2e3, -3e3, "JOIN OUR DISCORD SERVER"), o(1500, -2700, "DISCORD.GG/Q8gY4WeUcm")) + }, spawn.mapRect(-1e3, -950, 5950, 100), spawn.mapRect(-1325, -3450, 100, 2575), spawn.mapRect(-1325, -950, 350, 100), spawn.mapRect(4850, -3400, 100, 2550), spawn.mapRect(-1325, -3450, 6275, 100), + function (e, t, o, l, a) { + const n = o / a, + s = l / a, + i = e - o / 2, + p = t - l / 2, + r = []; + for (let e = 0; e < a; e++) { + r[e] = []; + for (let t = 0; t < a; t++) r[e][t] = 1 + } + const c = []; + (function e(t, o) { + r[t][o] = 0; + const l = [{ + dx: 0, + dy: -1 + }, { + dx: 1, + dy: 0 + }, { + dx: 0, + dy: 1 + }, { + dx: -1, + dy: 0 + }]; + l.sort((() => Math.random() - .5)); + for (const n of l) { + const l = t + 2 * n.dx, + s = o + 2 * n.dy; + l >= 0 && l < a && s >= 0 && s < a && 1 === r[l][s] && (r[t + n.dx][o + n.dy] = 0, r[l][s] = 0, c.push({ + x: t + n.dx, + y: o + n.dy + }), e(l, s)) + } + })(0, 0), r[a - 1][a - 1] = 1; + for (let e = -1; e < a + 1; e++) { + let t = -1, + o = -1; + for (let l = -1; l < a + 1; l++) + if (e >= 0 && e < a && l >= 0 && l < a && 1 === r[e][l]) - 1 === t && (t = l), o = l; + else if (-1 !== t) { + const l = i + e * n, + a = p + t * s, + r = n, + c = (o - t + 1) * s; + c !== s && spawn.mapRect(l, a, r, c), t = -1, o = -1 + } + } + for (let e = -1; e < a + 1; e++) { + let t = -1, + o = -1; + for (let l = -1; l < a + 1; l++) + if (l >= 0 && l < a && e >= 0 && e < a && 1 === r[l][e]) - 1 === t && (t = l), o = l; + else if (-1 !== t) { + const l = i + t * n, + a = p + e * s, + r = (o - t + 1) * n, + c = s; + r !== n && spawn.mapRect(l, a, r, c), t = -1, o = -1 + } + } + spawn.mapRect(i - n, p - s, n * a, s), spawn.mapRect(i - n, p - s, n, s * a), spawn.mapRect(i + (a - 1) * n, p - s, n, s * (a + 1)), spawn.mapRect(i - n, p + (a - 1) * s, n * (a + 1), s) + }(1e4, -1e3, 1e4, 1e4, 50); + }, + ruins() { // by SiddhUPe + // simulation.enableConstructMode() + simulation.makeTextLog(`ruins by SiddhUPe`); + + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 19531; + level.exit.y = 882 + 70; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + // color.map = "#444" //custom map color + + + level.customTopLayer = () => { }; + spawn.mapRect(875, 0, 1000, 100); + spawn.mapRect(1825, -400, 50, 225); + spawn.mapRect(1825, -400, 675, 50); + spawn.mapRect(1825, 0, 675, 100); + spawn.mapRect(2500, -575, 50, 225); + spawn.mapRect(2500, -575, 850, 50); + spawn.mapRect(2500, -100, 875, 175); + spawn.mapRect(2500, 75, 875, 25); + spawn.mapRect(3350, -575, 25, 50); + spawn.mapRect(2450, -50, 75, 75); + spawn.mapRect(2425, -25, 25, 25); + spawn.mapRect(2475, -75, 25, 25); + spawn.mapRect(3375, -575, 75, 325); + spawn.mapRect(3375, -100, 75, 300); + spawn.mapRect(3450, -50, 25, 250); + spawn.mapRect(3475, -25, 25, 225); + spawn.mapRect(3500, 0, 25, 200); + spawn.mapRect(3525, 25, 25, 175); + spawn.mapRect(3550, 75, 25, 125); + spawn.mapRect(3550, 50, 25, 150); + spawn.mapRect(3575, 75, 25, 125); + spawn.mapRect(3600, 100, 25, 100); + spawn.mapRect(3625, 150, 25, 50); + spawn.mapRect(2875, -1375, 350, 75); + spawn.mapRect(3150, -1375, 75, 350); + spawn.mapRect(3100, -1300, 50, 50); + spawn.mapRect(3075, -1300, 25, 25); + spawn.mapRect(3125, -1250, 25, 25); + spawn.mapRect(2825, -1375, 50, 125); + spawn.mapRect(3100, -1025, 125, 50); + spawn.mapRect(2800, -1350, 25, 75); + spawn.mapRect(3125, -975, 75, 25); + spawn.mapRect(3225, -1350, 25, 300); + spawn.mapRect(2875, -1400, 275, 25); + spawn.mapRect(2900, -1425, 225, 25); + spawn.mapRect(3250, -1325, 25, 250); + spawn.mapRect(2875, -1300, 25, 25); + spawn.mapRect(3125, -1050, 50, 25); + spawn.mapRect(5325, 800, 50, 225); + spawn.mapRect(5325, 975, 300, 50); + spawn.mapRect(5375, 925, 50, 50); + spawn.mapRect(5375, 900, 25, 25); + spawn.mapRect(5425, 950, 25, 25); + spawn.mapRect(5325, 775, 100, 25); + spawn.mapRect(5625, 925, 25, 100); + spawn.mapRect(5350, 800, 50, 25); + spawn.mapRect(5600, 950, 25, 50); + spawn.mapRect(5300, 800, 25, 175); + spawn.mapRect(5400, 1025, 225, 25); + spawn.mapRect(5450, 1050, 125, 25); + spawn.mapRect(5275, 850, 25, 100); + spawn.mapRect(5350, 750, 50, 25); + spawn.mapRect(5650, 950, 25, 50); + spawn.mapRect(16775, -975, 275, 50); + spawn.mapRect(17000, -975, 50, 200); + spawn.mapRect(16775, -975, 25, 100); + spawn.mapRect(17000, -775, 50, 50); + spawn.mapRect(16975, -725, 75, 25); + spawn.mapRect(16950, -925, 50, 50); + spawn.mapRect(16925, -925, 75, 25); + spawn.mapRect(17000, -925, 25, 75); + spawn.mapRect(16975, -925, 25, 50); + spawn.mapRect(16975, -925, 25, 75); + spawn.mapRect(16800, -1000, 200, 25); + spawn.mapRect(16850, -1025, 100, 25); + spawn.mapRect(17050, -925, 25, 200); + spawn.mapRect(17075, -925, 25, 150); + spawn.mapRect(16775, -925, 50, 25); + spawn.mapRect(17000, -750, 25, 25); + spawn.mapRect(16975, -750, 25, 50); + spawn.mapRect(16950, -725, 75, 25); + spawn.mapRect(9475, -1150, 50, 200); + spawn.mapRect(9475, -1150, 25, 25); + spawn.mapRect(9475, -1150, 300, 50); + spawn.mapRect(9725, -1150, 25, 25); + spawn.mapRect(9725, -1150, 50, 200); + spawn.mapRect(9500, -975, 25, 25); + spawn.mapRect(9500, -975, 75, 25); + spawn.mapRect(9700, -975, 25, 25); + spawn.mapRect(9675, -975, 75, 25); + spawn.mapRect(9525, -1175, 200, 25); + spawn.mapRect(9550, -1200, 150, 100); + spawn.mapRect(9450, -1125, 50, 150); + spawn.mapRect(9750, -1125, 50, 150); + spawn.mapRect(9525, -1100, 50, 50); + spawn.mapRect(9675, -1100, 50, 50); + spawn.mapRect(9575, -1100, 25, 25); + spawn.mapRect(9650, -1100, 25, 25); + spawn.mapRect(9500, -1050, 50, 25); + spawn.mapRect(9700, -1100, 25, 75); + spawn.mapRect(11925, -1175, 75, 275); + spawn.mapRect(11925, -1175, 475, 75); + spawn.mapRect(12325, -1175, 75, 275); + spawn.mapRect(11925, -925, 175, 25); + spawn.mapRect(12225, -925, 175, 25); + spawn.mapRect(11950, -925, 125, 50); + spawn.mapRect(12275, -925, 100, 50); + spawn.mapRect(11925, -1200, 475, 25); + spawn.mapRect(11975, -1225, 375, 25); + spawn.mapRect(12000, -1225, 50, 25); + spawn.mapRect(12000, -1275, 325, 75); + spawn.mapRect(11900, -1175, 50, 250); + spawn.mapRect(12375, -1175, 50, 250); + spawn.mapRect(11900, -1150, 50, 150); + spawn.mapRect(11875, -1150, 50, 200); + spawn.mapRect(12375, -1150, 75, 200); + spawn.mapRect(11975, -1100, 50, 25); + spawn.mapRect(12300, -1100, 75, 25); + spawn.mapRect(12300, -950, 25, 50); + spawn.mapRect(12000, -950, 25, 75); + + spawn.mapRect(3625, 125, 25, 50); + spawn.mapRect(3650, 150, 25, 50); + spawn.mapRect(3675, 175, 25, 25); + spawn.mapRect(3450, -75, 25, 50); + spawn.mapRect(3475, -50, 25, 125); + spawn.mapRect(3500, -25, 25, 125); + spawn.mapRect(3500, 0, 50, 125); + spawn.mapRect(3550, 25, 25, 125); + spawn.mapRect(3575, 50, 25, 125); + spawn.mapRect(3600, 75, 25, 75); + spawn.mapRect(3600, 100, 50, 75); + spawn.mapRect(3650, 125, 25, 75); + spawn.mapRect(3675, 150, 25, 50); + spawn.mapRect(3675, 150, 75, 450); + spawn.mapRect(3675, 525, 700, 75); + spawn.mapRect(4300, 150, 75, 450); + mover = level.mover(3375, -100, 75, 100); + pool = level.hazard(3750, 200, 550, 325); + spawn.mapRect(-150, -225, 75, 325); + spawn.mapRect(-150, -325, 425, 100); + spawn.mapRect(-100, -400, 300, 75); + spawn.mapRect(-25, -475, 150, 75); + spawn.mapRect(200, -350, 25, 50); + spawn.mapRect(-50, -425, 25, 100); + spawn.mapRect(-125, -350, 25, 100); + spawn.mapRect(100, -425, 50, 125); + spawn.mapRect(1875, -450, 625, 50); + spawn.mapRect(1950, -500, 550, 50); + spawn.mapRect(2025, -525, 475, 25); + spawn.mapRect(2025, -550, 525, 25); + spawn.mapRect(2125, -575, 400, 25); + spawn.mapRect(2125, -600, 1325, 50); + spawn.mapRect(2475, -550, 950, 200); + spawn.mapRect(1825, -350, 100, 175); + spawn.mapRect(3350, -375, 25, 125); + spawn.mapRect(1850, -425, 50, 25); + spawn.mapRect(1925, -475, 125, 25); + spawn.mapRect(2000, -525, 125, 25); + spawn.mapRect(2100, -575, 200, 25); + spawn.mapRect(2400, -650, 725, 75); + spawn.mapRect(2500, -675, 475, 75); + spawn.mapRect(2625, -725, 225, 100); + spawn.mapRect(2675, -750, 125, 25); + spawn.mapRect(2600, -700, 25, 50); + spawn.mapRect(2850, -700, 25, 75); + spawn.mapRect(3075, -625, 75, 50); + spawn.mapRect(2375, -625, 50, 75); + spawn.mapRect(1900, -350, 100, 75); + spawn.mapRect(1925, -300, 50, 50); + spawn.mapRect(1975, -350, 75, 50); + spawn.mapRect(3325, -350, 50, 25); + spawn.mapRect(150, -25, 1425, 50); + spawn.mapRect(175, 75, 1200, 50); + spawn.mapRect(400, -25, 575, 25); + spawn.mapRect(425, -50, 750, 75); + spawn.mapRect(1250, -50, 125, 100); + spawn.mapRect(1175, -50, 100, 25); + spawn.mapRect(725, 100, 500, 50); + spawn.mapRect(625, -75, 300, 50); + spawn.mapRect(250, -25, 225, 25); + spawn.mapRect(1400, 75, 225, 50); + spawn.mapRect(950, -75, 200, 25); + spawn.mapRect(1200, -75, 125, 25); + spawn.mapRect(425, -50, 100, 25); + spawn.mapRect(450, -75, 100, 50); + spawn.mapRect(250, -50, 125, 50); + spawn.mapRect(250, 125, 125, 25); + spawn.mapRect(475, 100, 100, 50); + spawn.mapRect(650, 125, 25, 25); + spawn.mapRect(675, 100, 75, 50); + spawn.mapRect(825, 125, 200, 50); + spawn.mapRect(-75, 75, 325, 50); + spawn.mapRect(0, 100, 175, 50); + spawn.mapRect(775, -100, 275, 50); + spawn.mapRect(2475, 0, 925, 125); + spawn.mapRect(2500, 50, 875, 100); + spawn.mapRect(2550, 100, 775, 75); + spawn.mapRect(2625, 150, 600, 50); + spawn.mapRect(3225, 125, 275, 75); + spawn.mapRect(1750, -225, 150, 50); + spawn.mapRect(1800, -275, 50, 75); + spawn.mapRect(1775, -250, 75, 75); + spawn.mapRect(3200, -625, 250, 50); + spawn.mapRect(3275, -650, 75, 25); + spawn.mapRect(3175, -625, 25, 25); + spawn.mapRect(3250, -700, 100, 100); + spawn.mapRect(3200, -650, 75, 50); + spawn.mapRect(3225, -675, 75, 100); + spawn.mapRect(3325, -675, 50, 100); + spawn.mapRect(3375, -650, 25, 75); + spawn.mapRect(1575, -25, 100, 25); + spawn.mapRect(1450, 100, 125, 50); + spawn.mapRect(250, -300, 50, 50); + spawn.mapRect(275, -275, 50, 25); + spawn.mapRect(200, -275, 125, 50); + spawn.mapRect(3725, 200, 50, 375); + spawn.mapRect(3750, 275, 50, 300); + spawn.mapRect(3800, 350, 25, 200); + spawn.mapRect(3825, 425, 25, 150); + spawn.mapRect(3850, 500, 25, 75); + spawn.mapRect(4275, 250, 50, 325); + spawn.mapRect(4250, 300, 50, 300); + spawn.mapRect(4225, 375, 75, 200); + spawn.mapRect(4200, 450, 75, 150); + spawn.mapRect(4175, 500, 75, 75); + spawn.mapRect(3950, 500, 150, 50); + spawn.mapRect(4000, 500, 50, 25); + spawn.mapRect(3875, 500, 425, 25); + spawn.mapRect(3625, 200, 50, 75); + spawn.mapRect(3575, 200, 50, 25); + spawn.mapRect(3675, 275, 25, 25); + spawn.mapRect(3650, 275, 25, 25); + spawn.mapRect(3600, 200, 25, 50); + spawn.mapRect(2600, 175, 25, 25); + spawn.mapRect(2700, 175, 425, 100); + spawn.mapRect(2650, 200, 75, 50); + spawn.mapRect(3100, 200, 75, 50); + spawn.mapRect(2675, 250, 25, 25); + spawn.mapRect(2625, 200, 100, 25); + spawn.mapRect(3150, 200, 75, 25); + spawn.mapRect(3175, 225, 25, 25); + spawn.mapRect(3775, 250, 25, 50); + spawn.mapRect(3800, 275, 25, 125); + spawn.mapRect(3800, 325, 50, 225); + spawn.mapRect(3875, 400, 25, 200); + spawn.mapRect(3825, 425, 50, 125); + spawn.mapRect(3850, 375, 25, 100); + spawn.mapRect(3900, 450, 25, 75); + spawn.mapRect(3925, 475, 25, 50); + spawn.mapRect(3450, -600, 25, 325); + spawn.mapRect(3475, -575, 25, 275); + spawn.mapRect(3500, -525, 25, 200); + spawn.mapRect(3525, -500, 25, 150); + spawn.mapRect(3550, -475, 25, 100); + spawn.mapRect(2725, 250, 350, 50); + spawn.mapRect(2750, 275, 300, 50); + spawn.mapRect(3150, 250, 25, 25); + spawn.mapRect(4325, 150, 975, 75); + spawn.mapRect(4375, 225, 900, 25); + spawn.mapRect(4375, 175, 875, 100); + spawn.mapRect(4375, 225, 850, 75); + spawn.mapRect(4375, 225, 825, 100); + spawn.mapRect(4375, 275, 800, 75); + spawn.mapRect(4350, 325, 800, 50); + spawn.mapRect(4375, 350, 750, 50); + spawn.mapRect(4375, 350, 725, 75); + spawn.mapRect(4350, 375, 725, 75); + spawn.mapRect(4350, 400, 700, 75); + spawn.mapRect(4350, 425, 675, 75); + spawn.mapRect(4350, 475, 650, 50); + spawn.mapRect(4375, 500, 600, 50); + spawn.mapRect(4375, 500, 575, 75); + spawn.mapRect(4375, 550, 550, 50); + spawn.mapRect(4425, 125, 775, 75); + spawn.mapRect(5300, 175, 75, 50); + spawn.mapRect(4475, 100, 150, 25); + spawn.mapRect(4825, 125, 300, 25); + spawn.mapRect(4800, 100, 250, 25); + spawn.mapRect(5100, 100, 50, 25); + spawn.mapRect(4650, 100, 75, 25); + spawn.mapRect(5475, 225, 125, 300); + spawn.mapRect(5450, 275, 25, 125); + spawn.mapRect(5450, 450, 25, 75); + spawn.mapRect(5425, 325, 25, 75); + spawn.mapRect(5425, 475, 25, 50); + spawn.mapRect(5575, 250, 50, 150); + spawn.mapRect(5575, 450, 50, 50); + spawn.mapRect(5475, 525, 125, 25); + spawn.mapRect(5500, 550, 75, 25); + spawn.mapRect(5525, 575, 25, 25); + spawn.mapRect(3675, 575, 1050, 50); + spawn.mapRect(4175, 600, 250, 50); + spawn.mapRect(3850, 625, 100, 25); + spawn.mapRect(3700, 625, 75, 25); + spawn.mapRect(4050, 625, 50, 25); + spawn.mapRect(4500, 625, 225, 25); + spawn.mapRect(5725, 150, 75, 225); + spawn.mapRect(5700, 175, 25, 150); + spawn.mapRect(5775, 250, 50, 100); + spawn.mapRect(5950, 325, 75, 75); + spawn.mapRect(5925, 375, 25, 25); + spawn.mapRect(6000, 350, 50, 50); + spawn.mapRect(6125, 425, 1050, 75); + spawn.mapRect(6425, 0, 750, 75); + spawn.mapRect(6400, 25, 50, 50); + spawn.mapRect(6500, -25, 675, 75); + spawn.mapRect(6550, -25, 275, 25); + spawn.mapRect(6475, -25, 125, 75); + spawn.mapRect(6450, -25, 100, 100); + spawn.mapRect(6475, -75, 700, 75); + spawn.mapRect(6500, -75, 75, 25); + spawn.mapRect(6500, -125, 675, 125); + spawn.mapRect(6525, -150, 650, 125); + spawn.mapRect(6550, -175, 625, 50); + spawn.mapRect(6900, -200, 275, 75); + spawn.mapRect(6925, -250, 250, 175); + spawn.mapRect(6950, -275, 225, 75); + spawn.mapRect(6975, -300, 200, 50); + spawn.mapRect(7025, -325, 125, 125); + spawn.mapRect(6400, 50, 75, 175); + spawn.mapRect(6450, 50, 100, 100); + spawn.mapRect(6475, 150, 25, 25); + spawn.mapRect(6550, 75, 25, 25); + spawn.mapRect(6375, 75, 25, 150); + spawn.mapRect(6350, 100, 25, 75); + spawn.mapRect(6650, 50, 225, 50); + spawn.mapRect(6975, 75, 75, 25); + spawn.mapRect(6625, -175, 225, 25); + spawn.mapRect(6675, -200, 275, 25); + spawn.mapRect(6750, -225, 250, 25); + spawn.mapRect(6200, 475, 575, 50); + spawn.mapRect(6925, 500, 125, 25); + spawn.mapRect(6325, 400, 475, 25); + spawn.mapRect(6950, 400, 200, 25); + spawn.mapRect(7100, 75, 75, 100); + spawn.mapRect(7075, 75, 25, 25); + spawn.mapRect(7175, -300, 1650, 350); + spawn.mapRect(7325, -700, 100, 450); + spawn.mapRect(7600, -700, 100, 450); + spawn.mapRect(7900, -700, 100, 450); + spawn.mapRect(8200, -700, 100, 425); + spawn.mapRect(8500, -700, 100, 425); + spawn.mapRect(7275, -825, 1375, 125); + spawn.mapRect(7400, -700, 50, 25); + spawn.mapRect(7575, -700, 50, 25); + spawn.mapRect(7875, -700, 150, 25); + spawn.mapRect(8175, -700, 150, 25); + spawn.mapRect(8475, -700, 150, 25); + spawn.mapRect(7300, -325, 150, 75); + spawn.mapRect(7575, -325, 150, 75); + spawn.mapRect(7875, -325, 150, 75); + spawn.mapRect(8175, -325, 150, 75); + spawn.mapRect(8475, -325, 150, 75); + spawn.mapRect(7700, -700, 25, 25); + spawn.mapRect(7300, -700, 75, 25); + spawn.mapRect(7150, 50, 75, 50); + spawn.mapRect(7175, 100, 25, 25); + spawn.mapRect(7225, 50, 25, 25); + spawn.mapRect(7300, -850, 1325, 75); + spawn.mapRect(7325, -875, 1275, 50); + spawn.mapRect(7375, -900, 1200, 25); + spawn.mapRect(7350, -900, 25, 25); + spawn.mapRect(7375, -900, 200, 25); + + spawn.mapRect(7375, -925, 1175, 25); + spawn.mapRect(7400, -950, 1125, 25); + spawn.mapRect(7425, -975, 1075, 25); + spawn.mapRect(7450, -1000, 1025, 25); + spawn.mapRect(7675, -1050, 525, 50); + spawn.mapRect(7700, -1050, 100, 25); + spawn.mapRect(7700, -1075, 450, 25); + spawn.mapRect(7725, -1100, 400, 25); + spawn.mapRect(7775, -1125, 300, 25); + spawn.mapRect(7650, -1025, 75, 25); + spawn.mapRect(8200, -1025, 25, 25); + spawn.mapRect(8825, -275, 25, 300); + spawn.mapRect(8825, -225, 50, 125); + spawn.mapRect(8850, -50, 25, 75); + spawn.mapRect(7150, 425, 100, 425); + spawn.mapRect(7150, 775, 1600, 100); + spawn.mapRect(8750, 400, 75, 475); + spawn.mapRect(8825, 400, 25, 475); + spawn.mapRect(7225, 450, 50, 325); + spawn.mapRect(7250, 500, 50, 275); + spawn.mapRect(7300, 550, 25, 225); + spawn.mapRect(7325, 600, 25, 175); + spawn.mapRect(7350, 650, 25, 125); + spawn.mapRect(7375, 675, 25, 100); + spawn.mapRect(7400, 700, 25, 75); + spawn.mapRect(7425, 725, 25, 50); + spawn.mapRect(7450, 750, 25, 25); + spawn.mapRect(8725, 425, 50, 375); + spawn.mapRect(8700, 450, 75, 375); + spawn.mapRect(8675, 475, 100, 375); + spawn.mapRect(8650, 500, 125, 375); + spawn.mapRect(8600, 525, 100, 350); + spawn.mapRect(8575, 550, 100, 275); + spawn.mapRect(8550, 575, 150, 250); + spawn.mapRect(8525, 625, 100, 225); + spawn.mapRect(8500, 675, 100, 125); + spawn.mapRect(8625, 825, 25, 25); + spawn.mapRect(8475, 700, 75, 75); + spawn.mapRect(8450, 725, 50, 50); + spawn.mapRect(8425, 750, 100, 25); + wastepool = level.hazard(7250, 575, 1450, 200); + spawn.mapRect(7375, 0, 250, 75); + spawn.mapRect(7700, 0, 725, 75); + spawn.mapRect(8575, 50, 150, 25); + spawn.mapRect(7750, 50, 475, 50); + spawn.mapRect(7425, 50, 175, 50); + spawn.mapRect(8600, 50, 50, 50); + spawn.mapRect(7200, 850, 875, 50); + spawn.mapRect(8225, 850, 125, 50); + spawn.mapRect(8475, 850, 275, 50); + spawn.mapRect(7300, 875, 375, 50); + spawn.mapRect(7925, 875, 100, 50); + spawn.mapRect(8525, 875, 125, 50); + spawn.mapRect(8250, 875, 75, 50); + spawn.mapRect(7800, 900, 50, 25); + spawn.mapRect(8125, 875, 50, 50); + spawn.mapRect(8075, 875, 50, 50); + spawn.mapRect(7125, 500, 25, 325); + spawn.mapRect(7100, 475, 25, 300); + spawn.mapRect(7075, 600, 50, 125); + spawn.mapRect(7075, 500, 50, 25); + spawn.mapRect(8850, 425, 75, 450); + spawn.mapRect(8925, 475, 75, 400); + spawn.mapRect(9000, 550, 75, 325); + spawn.mapRect(9075, 650, 75, 25); + spawn.mapRect(9075, 625, 75, 250); + spawn.mapRect(9150, 675, 75, 200); + spawn.mapRect(9225, 750, 75, 125); + spawn.mapRect(8925, 450, 25, 25); + spawn.mapRect(9000, 500, 25, 75); + spawn.mapRect(9000, 525, 25, 100); + spawn.mapRect(9000, 525, 50, 100); + spawn.mapRect(9050, 575, 50, 100); + spawn.mapRect(9075, 600, 50, 75); + spawn.mapRect(9150, 650, 25, 100); + spawn.mapRect(9225, 725, 50, 100); + spawn.mapRect(9225, 700, 25, 100); + spawn.mapRect(9300, 800, 1375, 75); + spawn.mapRect(9300, 775, 25, 50); + spawn.mapRect(9425, 775, 200, 25); + spawn.mapRect(9500, 875, 200, 25); + spawn.mapRect(9725, 825, 200, 75); + spawn.mapRect(10000, 850, 125, 50); + spawn.mapRect(10225, 850, 400, 50); + spawn.mapRect(9775, 775, 125, 25); + spawn.mapRect(10100, 775, 75, 75); + spawn.mapRect(10275, 750, 225, 75); + spawn.mapRect(9975, 775, 25, 50); + spawn.mapRect(10000, 775, 25, 50); + spawn.mapRect(10025, 775, 25, 75); + spawn.mapRect(10675, 775, 50, 100); + spawn.mapRect(10725, 725, 50, 150); + spawn.mapRect(10775, 650, 50, 225); + spawn.mapRect(10825, 575, 50, 300); + spawn.mapRect(10875, 500, 50, 375); + spawn.mapRect(10925, 425, 275, 450); + spawn.mapRect(11200, 500, 50, 375); + spawn.mapRect(11250, 575, 50, 300); + spawn.mapRect(11300, 650, 50, 225); + spawn.mapRect(11350, 725, 50, 150); + spawn.mapRect(11400, 775, 50, 100); + spawn.mapRect(10700, 750, 75, 75); + spawn.mapRect(10775, 700, 25, 25); + spawn.mapRect(10750, 700, 75, 75); + spawn.mapRect(10800, 625, 125, 75); + spawn.mapRect(10850, 550, 125, 50); + spawn.mapRect(10900, 475, 150, 75); + spawn.mapRect(11125, 475, 100, 50); + spawn.mapRect(11200, 550, 75, 50); + spawn.mapRect(11275, 625, 50, 75); + spawn.mapRect(11325, 700, 50, 75); + spawn.mapRect(11375, 750, 50, 75); + spawn.mapRect(11550, 225, 1525, 75); + spawn.mapRect(11450, 825, 1625, 50); + spawn.mapRect(11450, 800, 1625, 75); + spawn.mapRect(11525, -350, 50, 650); + spawn.mapRect(11850, -350, 50, 650); + spawn.mapRect(12225, -350, 50, 650); + spawn.mapRect(12600, -350, 50, 650); + spawn.mapRect(13000, -350, 75, 650); + spawn.mapRect(11525, -200, 1525, 50); + spawn.mapRect(11525, 50, 1550, 50); + spawn.mapRect(11525, -400, 1550, 50); + spawn.mapRect(11575, -425, 1450, 50); + spawn.mapRect(11625, -450, 1325, 75); + spawn.mapRect(11700, -475, 1175, 75); + spawn.mapRect(11725, -500, 1125, 75); + spawn.mapRect(11825, -400, 100, 75); + spawn.mapRect(11825, 200, 100, 75); + spawn.mapRect(12200, -375, 100, 50); + spawn.mapRect(12200, 200, 100, 75); + spawn.mapRect(12575, 200, 100, 75); + spawn.mapRect(12575, -375, 100, 50); + spawn.mapRect(11500, 825, 50, 25); + spawn.mapRect(11550, 775, 175, 25); + spawn.mapRect(11525, 875, 250, 25); + spawn.mapRect(11875, 750, 225, 50); + spawn.mapRect(11950, 850, 375, 50); + spawn.mapRect(12500, 775, 250, 75); + spawn.mapRect(12750, 850, 175, 50); + + + // books + spawn.bodyRect(11575, -300, 25, 100); + spawn.bodyRect(11600, -300, 25, 100); + spawn.bodyRect(11625, -300, 25, 100); + spawn.bodyRect(11650, -300, 25, 100); + spawn.bodyRect(11675, -300, 25, 100); + spawn.bodyRect(11700, -300, 25, 100); + spawn.bodyRect(11725, -300, 25, 100); + spawn.bodyRect(11750, -300, 25, 100); + spawn.bodyRect(11775, -300, 25, 100); + spawn.bodyRect(11800, -300, 25, 100); + spawn.bodyRect(11825, -300, 25, 100); + spawn.bodyRect(11900, -50, 25, 100); + spawn.bodyRect(11925, -50, 25, 100); + spawn.bodyRect(11950, -50, 25, 100); + spawn.bodyRect(11975, -50, 50, 100); + spawn.bodyRect(12025, -50, 50, 100); + spawn.bodyRect(12075, -50, 25, 100); + spawn.bodyRect(12100, -50, 50, 100); + spawn.bodyRect(12150, -50, 25, 100); + spawn.bodyRect(12175, -50, 25, 100); + spawn.bodyRect(12200, -50, 25, 100); + spawn.bodyRect(11900, -300, 25, 100); + spawn.bodyRect(11925, -300, 25, 100); + spawn.bodyRect(11950, -225, 75, 25); + spawn.bodyRect(12650, -50, 25, 100); + spawn.bodyRect(12675, -50, 25, 100); + spawn.bodyRect(12725, -50, 25, 100); + spawn.bodyRect(12750, -50, 50, 100); + spawn.bodyRect(12650, -275, 25, 75); + spawn.bodyRect(12675, -275, 25, 75); + spawn.bodyRect(12700, -275, 50, 75); + spawn.bodyRect(12750, -275, 25, 75); + spawn.bodyRect(12775, -275, 25, 75); + spawn.bodyRect(12800, -275, 25, 75); + spawn.bodyRect(12825, -275, 25, 75); + spawn.bodyRect(12850, -275, 50, 75); + spawn.bodyRect(12900, -275, 50, 75); + spawn.bodyRect(12950, -275, 50, 75); + spawn.mapRect(12200, 775, 175, 50); + spawn.mapRect(11550, 250, 1500, 75); + spawn.mapRect(11575, 275, 1450, 75); + spawn.mapRect(11600, 325, 1400, 50); + spawn.mapRect(11625, 350, 1350, 50); + spawn.mapRect(11725, 375, 1150, 50); + spawn.mapRect(11900, 400, 800, 50); + spawn.mapRect(12100, 425, 425, 50); + spawn.mapRect(12125, 475, 375, 25); + spawn.mapRect(12200, 475, 225, 50); + spawn.mapRect(11475, -400, 50, 700); + spawn.mapRect(11450, -375, 50, 625); + spawn.mapRect(11425, -350, 100, 600); + spawn.mapRect(11400, -300, 75, 525); + spawn.mapRect(11375, -250, 100, 400); + spawn.mapRect(11350, -150, 50, 200); + spawn.mapRect(13075, 825, 25, 50); + spawn.mapRect(13100, 850, 25, 25); + spawn.mapRect(13200, 700, 100, 225); + spawn.mapRect(13300, 775, 25, 100); + spawn.mapRect(13325, 825, 25, 50); + spawn.mapRect(13175, 775, 25, 125); + spawn.mapRect(13225, 675, 50, 25); + spawn.mapRect(13225, 925, 50, 25); + spawn.mapRect(9250, 75, 1400, 150); + spawn.mapRect(9250, -225, 150, 300); + spawn.mapRect(9250, -275, 575, 50); + spawn.mapRect(9675, -225, 150, 300); + spawn.mapRect(9325, -325, 400, 50); + spawn.mapRect(9400, -350, 250, 25); + spawn.mapRect(9475, -375, 125, 25); + spawn.mapRect(9825, -225, 150, 300); + spawn.mapRect(10225, -225, 150, 300); + spawn.mapRect(9825, -275, 550, 50); + spawn.mapRect(9900, -325, 375, 50); + spawn.mapRect(9950, -350, 275, 25); + spawn.mapRect(10000, -375, 175, 25); + spawn.mapRect(10350, -275, 50, 375); + spawn.mapRect(10400, -250, 25, 325); + spawn.mapRect(10425, -225, 25, 375); + spawn.mapRect(10450, -200, 25, 325); + spawn.mapRect(10475, -175, 25, 350); + spawn.mapRect(10500, -150, 25, 300); + spawn.mapRect(10525, -125, 25, 300); + spawn.mapRect(10550, -100, 25, 225); + spawn.mapRect(10575, -75, 25, 200); + spawn.mapRect(10600, -50, 25, 150); + spawn.mapRect(10625, -25, 25, 175); + spawn.mapRect(9225, -225, 25, 450); + spawn.mapRect(9200, -175, 25, 400); + spawn.mapRect(9175, -150, 50, 375); + spawn.mapRect(9150, -125, 50, 350); + spawn.mapRect(9400, -175, 25, 275); + spawn.mapRect(9425, -125, 25, 200); + spawn.mapRect(9650, -175, 25, 250); + spawn.mapRect(9625, -125, 25, 275); + spawn.mapRect(9975, -175, 25, 300); + spawn.mapRect(10000, -125, 25, 250); + spawn.mapRect(10200, -175, 25, 300); + spawn.mapRect(10175, -125, 25, 225); + spawn.mapRect(9325, 225, 225, 25); + spawn.mapRect(9675, 225, 250, 50); + spawn.mapRect(10075, 225, 200, 25); + spawn.mapRect(10400, 200, 175, 50); + spawn.mapRect(13425, 675, 1425, 100); + spawn.mapRect(13450, 725, 375, 75); + spawn.mapRect(13850, 775, 225, 50); + spawn.mapRect(14150, 750, 300, 50); + spawn.mapRect(14575, 750, 200, 75); + spawn.mapRect(13550, 800, 150, 25); + spawn.mapRect(14250, 800, 225, 25); + spawn.mapRect(13425, 275, 1425, 100); + spawn.mapRect(13475, 250, 1325, 75); + spawn.mapRect(13550, 225, 1125, 75); + spawn.mapRect(13600, 200, 1025, 25); + spawn.mapRect(13650, 150, 925, 50); + spawn.mapRect(13725, 100, 775, 100); + spawn.mapRect(13825, 50, 525, 100); + spawn.mapRect(13900, 0, 350, 75); + spawn.mapRect(13975, -25, 175, 75); + spawn.mapRect(13875, 25, 50, 50); + spawn.mapRect(13800, 75, 75, 50); + spawn.mapRect(13700, 125, 75, 50); + spawn.mapRect(13625, 200, 50, 25); + spawn.mapRect(13650, 175, 25, 25); + spawn.mapRect(13625, 175, 125, 75); + spawn.mapRect(14350, 75, 25, 50); + spawn.mapRect(14250, 0, 25, 75); + spawn.mapRect(14275, 50, 25, 50); + spawn.mapRect(14275, 25, 25, 75); + spawn.mapRect(14500, 125, 25, 75); + spawn.mapRect(14575, 175, 25, 75); + spawn.mapRect(13475, 650, 400, 50); + spawn.mapRect(13975, 675, 75, 25); + spawn.mapRect(14000, 650, 50, 50); + spawn.mapRect(14150, 625, 675, 100); + spawn.mapRect(14325, 625, 100, 25); + spawn.mapRect(14300, 600, 325, 25); + spawn.mapRect(13525, 325, 375, 100); + spawn.mapRect(13975, 375, 400, 25); + spawn.mapRect(14500, 325, 100, 75); + spawn.mapRect(14850, 675, 50, 200); + spawn.mapRect(14875, 700, 50, 175); + spawn.mapRect(14925, 725, 50, 150); + spawn.mapRect(14975, 750, 50, 125); + spawn.mapRect(15025, 775, 50, 100); + spawn.mapRect(15075, 750, 1150, 100); + spawn.mapRect(15100, 825, 225, 50); + spawn.mapRect(15500, 850, 225, 25); + spawn.mapRect(15925, 775, 275, 100); + spawn.mapRect(15775, 825, 50, 50); + spawn.mapRect(15225, 250, 1050, 125); + spawn.mapRect(15250, 200, 1000, 50); + spawn.mapRect(15275, 175, 950, 50); + spawn.mapRect(15300, 150, 900, 50); + spawn.mapRect(15325, 125, 850, 25); + spawn.mapRect(15350, 100, 800, 25); + spawn.mapRect(15375, 75, 750, 25); + spawn.mapRect(15400, 50, 700, 100); + spawn.mapRect(15425, 25, 650, 75); + spawn.mapRect(15450, 0, 600, 50); + spawn.mapRect(15475, -25, 550, 75); + spawn.mapRect(15500, -50, 500, 75); + spawn.mapRect(15525, -75, 450, 75); + spawn.mapRect(15550, -100, 400, 75); + spawn.mapRect(15575, -125, 350, 75); + spawn.mapRect(15600, -150, 300, 50); + spawn.mapRect(15625, -175, 250, 50); + spawn.mapRect(15650, -200, 200, 25); + spawn.mapRect(15675, -225, 150, 75); + spawn.mapRect(15700, -250, 100, 75); + spawn.mapRect(16275, 250, 25, 125); + spawn.mapRect(16250, 225, 25, 25); + spawn.mapRect(15200, 250, 25, 125); + spawn.mapRect(15225, 225, 25, 25); + spawn.mapRect(15275, 350, 175, 50); + spawn.mapRect(15550, 350, 425, 75); + spawn.mapRect(16100, 375, 175, 25); + spawn.mapRect(14700, -375, 50, 325); + spawn.mapRect(14700, -425, 375, 50); + spawn.mapRect(14750, -375, 125, 100); + spawn.mapRect(14750, -275, 75, 75); + spawn.mapRect(14850, -375, 100, 50); + spawn.mapRect(14825, -275, 25, 25); + spawn.mapRect(14950, -375, 25, 25); + spawn.mapRect(14875, -325, 25, 25); + spawn.mapRect(14725, -200, 50, 25); + spawn.mapRect(14700, -75, 100, 25); + spawn.mapRect(15050, -425, 25, 100); + spawn.mapRect(14725, -450, 325, 25); + spawn.mapRect(14775, -475, 225, 25); + spawn.mapRect(14825, -500, 125, 25); + spawn.mapRect(14675, -350, 25, 100); + spawn.mapRect(14675, -175, 25, 75); + spawn.mapRect(14850, 325, 25, 50); + spawn.mapRect(5700, -725, 375, 50); + spawn.mapRect(6025, -725, 50, 325); + spawn.mapRect(5775, -675, 250, 25); + spawn.mapRect(6000, -675, 25, 225); + spawn.mapRect(5950, -650, 50, 75); + spawn.mapRect(5900, -650, 75, 25); + spawn.mapRect(6000, -575, 25, 25); + spawn.mapRect(6050, -700, 50, 275); + spawn.mapRect(5925, -625, 75, 25); + spawn.mapRect(5775, -750, 100, 25); + spawn.mapRect(5950, -750, 100, 25); + spawn.mapRect(5675, -725, 25, 150); + spawn.mapRect(5975, -400, 100, 25); + spawn.mapRect(5650, -700, 25, 75); + spawn.mapRect(5700, -675, 25, 50); + spawn.mapRect(5700, -600, 25, 25); + spawn.mapRect(15275, 750, 225, 25); + spawn.mapRect(15225, 725, 250, 25); + spawn.mapRect(15675, 725, 275, 100); + spawn.mapRect(16075, 725, 125, 50); + spawn.pulsar(5775.349354333542, -594.9058498351887) + spawn.pulsar(5852.915433009502, -545.5679375496002) + spawn.pulsar(5921.99534574469, -480.69487503053097) + spawn.mapRect(3725, -975, 1525, 100); + spawn.mapRect(3750, -650, 300, 75); + spawn.mapRect(4300, -650, 300, 75); + spawn.mapRect(4950, -650, 300, 75); + spawn.mapRect(5250, -975, 75, 400); + spawn.mapRect(3725, -975, 75, 400); + spawn.mapRect(4325, -600, 250, 50); + spawn.mapRect(4350, -550, 200, 25); + spawn.mapRect(3800, -575, 225, 25); + spawn.mapRect(3825, -550, 175, 25); + spawn.mapRect(4975, -600, 275, 50); + spawn.mapRect(5025, -550, 175, 25); + spawn.mapRect(3800, -1025, 1450, 50); + spawn.mapRect(3875, -1075, 1275, 50); + spawn.mapRect(3975, -1125, 1000, 50); + spawn.mapRect(3950, -1100, 50, 25); + spawn.mapRect(3850, -1050, 150, 25); + spawn.mapRect(3775, -1000, 200, 25); + spawn.mapRect(5225, -975, 75, 25); + spawn.mapRect(4950, -1100, 75, 125); + spawn.mapRect(5100, -1050, 75, 75); + spawn.mapRect(5225, -1000, 50, 100); + spawn.mapRect(4350, -675, 150, 75); + spawn.mapRect(4525, -650, 50, 25); + spawn.mapRect(4550, -675, 50, 75); + spawn.mapRect(3825, -650, 50, 25); + spawn.mapRect(3825, -675, 150, 50); + spawn.mapRect(4025, -675, 25, 100); + spawn.mapRect(4950, -675, 75, 50); + spawn.mapRect(5075, -675, 75, 75); + spawn.mapRect(5200, -675, 75, 50); + spawn.pulsar(4068.196906578167, -653.550201356403) + spawn.pulsar(4147.672553167598, -651.0093457935446) + spawn.pulsar(4228.863663369247, -653.4768859607283) + spawn.pulsar(4619.092688319791, -657.3942377732394) + spawn.pulsar(4724.821759138369, -653.4213864043036) + spawn.pulsar(4873.583205330713, -657.4103118310311) + spawn.pulsar(3871.920598597728, -804.0595760512573) + spawn.pulsar(4053.019377134256, -778.0061810623848) + spawn.pulsar(4211.732836201937, -780.4633597161928) + spawn.pulsar(4380.7768131190005, -776.3400515412312) + spawn.pulsar(4533.031170401828, -791.1397513503708) + spawn.pulsar(4663.577749297493, -789.0488615794887) + spawn.pulsar(4965.48351658387, -809.0025104385204) + spawn.pulsar(5122.782442346123, -810.2526936643312) + spawn.mapRect(3700, -875, 25, 250); + spawn.mapRect(5325, -900, 25, 250); + spawn.mapRect(5325, -850, 50, 150); + spawn.mapRect(5375, -825, 25, 75); + spawn.pulsar(14786.058375868968, -140.5759223979466) + spawn.pulsar(14862.320083571307, -177.02507110101413) + spawn.pulsar(14888.982047411475, -216.4856450590454) + spawn.pulsar(14950.503812885598, -280.9333882582806) + spawn.pulsar(15003.202057456116, -316.6767970823471) + spawn.spinner(759.4093972764956, -356.0541595435453) + spawn.spinner(1467.1412487475097, -617.4326431210314) + spawn.mapRect(11850, 775, 50, 50); + spawn.mapRect(12075, 775, 50, 50); + spawn.mapRect(16225, 750, 75, 325); + spawn.mapRect(16300, 775, 50, 325); + spawn.mapRect(16350, 800, 50, 275); + spawn.mapRect(16375, 825, 50, 200); + spawn.mapRect(16450, 875, 25, 150); + spawn.mapRect(16450, 875, 25, 225); + spawn.mapRect(16400, 875, 50, 150); + spawn.mapRect(16225, 1025, 250, 75); + spawn.mapRect(16475, 925, 25, 175); + spawn.mapRect(16500, 975, 25, 125); + spawn.mapRect(16525, 1025, 25, 50); + spawn.mapRect(16425, 1075, 150, 25); + spawn.mapRect(16225, 1100, 1175, 75); + spawn.mapRect(17200, 1050, 25, 50); + spawn.mapRect(17225, 950, 25, 200); + spawn.mapRect(17250, 800, 25, 300); + spawn.mapRect(17275, 725, 25, 400); + spawn.mapRect(17300, 750, 75, 400); + spawn.mapRect(17300, 725, 100, 450); + spawn.mapRect(16300, 250, 1075, 125); + spawn.mapRect(16450, -75, 100, 400); + spawn.mapRect(17100, -75, 100, 400); + spawn.mapRect(16425, 200, 150, 50); + spawn.mapRect(17075, 200, 150, 50); + spawn.mapRect(16425, -75, 150, 25); + spawn.mapRect(17075, -75, 150, 50); + spawn.mapRect(16425, -50, 150, 50); + spawn.mapRect(16575, -75, 525, 50); + spawn.mapRect(17075, -50, 150, 50); + spawn.mapRect(16550, -100, 550, 25); + spawn.mapRect(16575, -125, 500, 75); + spawn.mapRect(16600, -150, 450, 75); + spawn.mapRect(16625, -175, 400, 75); + spawn.mapRect(16675, -200, 275, 50); + spawn.mapRect(16750, -225, 125, 100); + spawn.mapRect(19700, 675, 50, 325); + spawn.mapRect(19725, 700, 50, 250); + spawn.mapRect(19750, 750, 25, 175); + spawn.mapRect(16775, -25, 100, 275); + spawn.mapRect(16750, -25, 150, 25); + spawn.mapRect(16750, 225, 150, 50); + spawn.pulsar(3037.797768861211, -1242.9871362505041) + spawn.pulsar(3070.307596879197, -1219.5627538123044) + spawn.pulsar(3111.2633762820287, -1107.7297980154415) + spawn.pulsar(5417.516810698634, 842.824851834252) + spawn.pulsar(5484.672534515589, 883.9519420960905) + spawn.pulsar(5588.5457723826075, 907.389646857348) + spawn.pulsar(16845.139047921595, -885.6942536135854) + spawn.pulsar(16892.187197333486, -849.5235136465661) + spawn.pulsar(16912.323783455256, -764.5275187038021) + powerUps.spawn(2571.591711269197, -145.6717604789277, 'heal') + powerUps.spawn(4415.693974666946, -15.077304620299628, 'heal') + powerUps.spawn(7505.795753124317, -360.0330849392626, 'heal') + powerUps.spawn(7809.5145838152075, -388.5517653996709, 'heal') + powerUps.spawn(8049.726318297545, 534.4543327703304, 'heal') + powerUps.spawn(8514.444440033667, 551.0268033205841, 'heal') + powerUps.spawn(8927.146055851512, 407.25359241772685, 'heal') + powerUps.spawn(9730.170170158563, 463.5594890235955, 'ammo') + powerUps.spawn(9998.34942087522, 434.9511651200589, 'ammo') + powerUps.spawn(10119.083720019844, 437.4195779326937, 'ammo') + powerUps.spawn(10346.197135080345, 423.1868836972815, 'ammo') + powerUps.spawn(1853.3194789931017, -36.87254038474242, 'ammo') + powerUps.spawn(4491.396397908616, 40.2862012621236, 'ammo') + powerUps.spawn(4954.207518897743, 50.27790416201856, 'ammo') + spawn.mapRect(9125, -50, 75, 275); + spawn.mapRect(9100, 0, 50, 225); + spawn.mapRect(9075, 75, 75, 150); + spawn.mapRect(9050, 150, 125, 50); + spawn.mapRect(9050, 200, 225, 25); + mover1 = level.mover(4000, -1125, 975, 25); + mover2 = level.mover(15675, 725, 275, 25); + spawn.mapRect(15025, -375, 25, 25); + spawn.mapRect(12200, -150, 100, 25); + spawn.mapRect(11825, -150, 100, 25); + spawn.mapRect(11825, 75, 100, 50); + spawn.mapRect(12200, 75, 100, 50); + spawn.mapRect(12575, 100, 75, 25); + spawn.mapRect(12625, 50, 50, 75); + spawn.mapRect(12600, -175, 75, 50); + spawn.mapRect(12575, -175, 75, 50); + spawn.mapRect(14125, 650, 75, 25); + spawn.mapRect(13875, 375, 50, 25); + spawn.mapRect(13300, -525, 325, 50); + spawn.mapRect(13575, -525, 50, 250); + spawn.mapRect(13550, -300, 75, 25); + spawn.mapRect(13300, -525, 25, 75); + spawn.mapRect(13525, -475, 50, 50); + spawn.mapRect(13500, -475, 100, 25); + spawn.mapRect(13550, -475, 25, 100); + spawn.mapRect(13325, -550, 275, 25); + spawn.mapRect(13350, -575, 200, 25); + spawn.mapRect(13625, -500, 25, 175); + spawn.mapRect(13650, -450, 25, 75); + spawn.mapRect(13500, 375, 75, 25); + spawn.mapRect(15550, -950, 50, 225); + spawn.mapRect(15575, -750, 75, 25); + spawn.mapRect(15550, -950, 375, 50); + spawn.mapRect(15925, -950, 50, 225); + spawn.mapRect(15875, -750, 100, 25); + spawn.mapRect(15575, -1000, 375, 75); + spawn.mapRect(15625, -1050, 250, 100); + spawn.mapRect(15600, -1025, 75, 50); + spawn.mapRect(15875, -1000, 25, 25); + spawn.mapRect(15825, -1025, 75, 75); + spawn.mapRect(15700, -1100, 125, 50); + spawn.mapRect(15650, -1075, 75, 50); + spawn.mapRect(15800, -1075, 50, 75); + spawn.mapRect(15575, -725, 50, 25); + spawn.mapRect(15900, -725, 50, 25); + spawn.mapRect(15525, -925, 25, 175); + spawn.mapRect(15950, -925, 50, 200); + spawn.mapRect(15500, -875, 25, 75); + spawn.mapRect(16000, -875, 25, 75); + spawn.mapRect(15600, -900, 50, 75); + spawn.mapRect(15650, -900, 25, 25); + spawn.mapRect(15600, -825, 25, 25); + spawn.mapRect(15875, -900, 50, 50); + spawn.mapRect(15850, -925, 75, 50); + spawn.mapRect(15925, -875, 25, 25); + spawn.mapRect(15925, -850, 25, 25); + spawn.mapRect(15900, -875, 50, 50); + bigpool = level.hazard(9075, 575, 1950, 250); + spawn.mapRect(16000, -775, 25, 50); + spawn.mapRect(16000, -800, 25, 25); + spawn.mapRect(15500, -825, 25, 75); + spawn.mapRect(15475, -850, 75, 75); + spawn.mapRect(16000, -850, 50, 100); + spawn.mapRect(10775, -450, 50, 200); + spawn.mapRect(10775, -275, 100, 25); + spawn.mapRect(11100, -450, 50, 200); + spawn.mapRect(11050, -275, 75, 25); + spawn.mapRect(10775, -450, 375, 25); + spawn.mapRect(10825, -475, 275, 25); + spawn.mapRect(10875, -500, 150, 25); + spawn.mapRect(10900, -525, 100, 25); + spawn.mapRect(10750, -425, 25, 150); + spawn.mapRect(11150, -400, 25, 125); + spawn.mapRect(10725, -400, 50, 100); + spawn.mapRect(11150, -375, 50, 75); + spawn.mapRect(10800, -250, 50, 25); + spawn.mapRect(10825, -425, 50, 50); + spawn.mapRect(10875, -425, 25, 25); + spawn.mapRect(10825, -375, 25, 25); + spawn.mapRect(11050, -425, 50, 50); + spawn.mapRect(11025, -425, 25, 25); + spawn.mapRect(11075, -375, 25, 25); + spawn.mapRect(950, -1075, 50, 200); + spawn.mapRect(950, -1125, 300, 50); + spawn.mapRect(1000, -1075, 50, 50); + spawn.mapRect(1050, -1075, 25, 25); + spawn.mapRect(975, -1025, 50, 25); + spawn.mapRect(975, -1150, 250, 25); + spawn.mapRect(1000, -1175, 200, 25); + spawn.mapRect(900, -1075, 50, 175); + spawn.mapRect(875, -1050, 25, 125); + spawn.mapRect(950, -875, 125, 25); + spawn.mapRect(1250, -1125, 25, 125); + spawn.mapRect(975, -850, 75, 25); + spawn.mapRect(1250, -1100, 50, 75); + spawn.mapRect(925, -900, 50, 25); + spawn.mapRect(1050, -1200, 100, 25); + spawn.mapRect(1225, -1000, 50, 25); + spawn.mapRect(16375, 350, 900, 50); + spawn.mapRect(16400, 375, 850, 50); + spawn.mapRect(16425, 400, 800, 50); + spawn.mapRect(16475, 425, 675, 50); + spawn.mapRect(16625, 475, 375, 25); + spawn.mapRect(16650, 500, 325, 25); + spawn.mapRect(16675, 500, 275, 50); + spawn.mapRect(17400, 775, 25, 325); + spawn.mapRect(17425, 825, 25, 225); + spawn.mapRect(16200, 900, 25, 225); + spawn.mapRect(16175, 925, 25, 125); + spawn.mapRect(16150, 975, 25, 25); + spawn.mapRect(16400, 1150, 850, 50); + spawn.mapRect(16475, 1175, 650, 50); + spawn.mapRect(16575, 1225, 450, 25); + spawn.sneaker(7895.471733263175, 257.75477496554186) + spawn.sneaker(8109.4934675858085, 349.44686618726473) + spawn.sneaker(7525.886813944122, 391.9511482895349) + spawn.sneaker(8076.43795816953, 441.14947363958373) + spawn.pulsar(1064.583377612331, -976.2077284446908) + spawn.pulsar(1158.3436115513837, -1054.4975368803214) + spawn.pulsar(10966.055009228428, -373.8481911663377) + spawn.pulsar(10913.989668763379, -261.59108542627166) + spawn.pulsar(13454.158594286884, -402.8270664336466) + spawn.pulsar(13360.079608974078, -246.97797933698774) + spawn.pulsar(13497.913481830354, -251.68317759640576) + spawn.pulsar(15687.09056963911, -850.8426925141155) + spawn.pulsar(15829.058084589731, -785.4134546702737) + spawn.pulsar(15674.313958480483, -685.0594164868394) + spawn.pulsar(15819.881465281747, -686.4370174238113) + spawn.sneakBoss(18189.441342796745, 537.6633241821036) + thirdpool = level.hazard(16425, 925, 925, 200); + spawn.mapRect(17675, -525, 75, 725); + spawn.mapRect(17625, -475, 75, 650); + spawn.mapRect(17575, -425, 75, 575); + spawn.mapRect(17700, -525, 1125, 75); + spawn.mapRect(17675, 175, 1125, 75); + spawn.mapRect(18775, -525, 75, 775); + spawn.mapRect(18825, -475, 75, 675); + spawn.mapRect(18900, -450, 50, 625); + spawn.mapRect(18950, -400, 50, 500); + spawn.mapRect(17750, -575, 1000, 50); + spawn.mapRect(17775, -625, 950, 50); + spawn.mapRect(17800, -675, 900, 75); + spawn.mapRect(17825, -725, 850, 125); + spawn.mapRect(17850, -750, 800, 25); + spawn.mapRect(17750, 125, 50, 50); + spawn.mapRect(17750, 100, 25, 25); + spawn.mapRect(17800, 150, 25, 25); + spawn.mapRect(17750, -450, 75, 75); + spawn.mapRect(17750, -400, 25, 50); + spawn.mapRect(17800, -450, 50, 25); + spawn.mapRect(18750, -450, 25, 25); + spawn.mapRect(18725, -450, 50, 50); + spawn.mapRect(18700, -475, 25, 25); + spawn.mapRect(18725, -450, 25, 25); + spawn.mapRect(18700, -450, 75, 25); + spawn.mapRect(18750, -425, 25, 50); + spawn.mapRect(18725, 125, 75, 75); + spawn.mapRect(18700, 150, 50, 50); + spawn.mapRect(18750, 100, 75, 50); + spawn.mapRect(17850, 150, 850, 50); + spawn.mapRect(17825, 150, 25, 50); + spawn.mapRect(17550, -350, 25, 450); + spawn.mapRect(19000, -325, 25, 400); + spawn.mapRect(18000, -775, 475, 25); + spawn.mapRect(18025, -800, 425, 75); + spawn.mapRect(18050, -825, 375, 75); + spawn.mapRect(18075, -850, 325, 50); + spawn.mapRect(18100, -875, 275, 100); + spawn.mapRect(18125, -900, 225, 75); + spawn.mapRect(18150, -925, 175, 75); + spawn.mapRect(17275, 750, 1775, 125); + spawn.mapRect(17475, 725, 450, 50); + spawn.mapRect(18200, 725, 200, 50); + spawn.mapRect(18650, 725, 225, 75); + spawn.shieldingBoss(18253.51035871325, -131.1707821125636) + // spawn.blockBoss(12604.846253470663, 607.6074958800299) + spawn.mapRect(17725, 250, 1025, 25); + spawn.mapRect(17775, 275, 925, 25); + spawn.mapRect(17800, 300, 875, 25); + spawn.mapRect(17850, 325, 775, 25); + spawn.mapRect(17375, 275, 25, 75); + spawn.mapRect(19050, 750, 25, 275); + spawn.mapRect(19075, 775, 25, 250); + spawn.mapRect(19100, 800, 25, 225); + spawn.mapRect(19125, 850, 25, 175); + spawn.mapRect(19150, 875, 25, 150); + spawn.mapRect(19175, 925, 25, 100); + spawn.mapRect(19200, 950, 25, 75); + spawn.mapRect(19000, 850, 100, 175); + spawn.mapRect(19050, 975, 650, 50); + spawn.mapRect(19425, 650, 275, 50); + spawn.mapRect(19675, 650, 50, 375); + spawn.mapRect(19050, 1025, 625, 25); + spawn.mapRect(19075, 1050, 575, 25); + spawn.mapRect(19250, 1100, 200, 25); + spawn.mapRect(19175, 1075, 375, 25); + spawn.mapRect(19450, 625, 225, 25); + spawn.mapRect(19500, 600, 150, 50); + spawn.mapRect(19625, 700, 50, 50); + spawn.mapRect(19600, 700, 25, 25); + spawn.mapRect(19650, 750, 25, 25); + spawn.mapRect(19400, 650, 25, 100); + spawn.mapRect(19375, 675, 25, 50); + spawn.mapRect(17600, 875, 250, 25); + spawn.mapRect(18100, 850, 375, 50); + spawn.mapRect(18650, 875, 325, 25); + + pooldunker = level.mover(7175, 425, 50, 25); + level.custom = () => { + level.exit.drawAndCheck(); + pooldunker.VxGoal = 90; + pooldunker.push(); + mover.VxGoal = 45; + mover.push(); + level.enter.draw(); + pool.query(); + wastepool.query(); + thirdpool.query(); + mover1.VxGoal = 12; + mover1.push(); + mover2.VxGoal = 24; + mover2.push(); + bigpool.query(); + + for (i = 0; i < mob.length; i++) { if (mob[i].isBoss == false) { mob[i].damageReduction = 0.13 } } + }; + + spawn.mapRect(-100, 0, 1000, 100); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + ace() { //join us at discord.gg/Q8gY4WeUcm + simulation.makeTextLog(`ace by Richard0820`); + let isDestroyed = false; + const ace = { + spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { + if (Math.random() < chance) { + // simulation.difficulty = 50 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + const offSet = 6.28 * Math.random() + for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) + } + }, + orbital(who, radius, phase, speed) { + // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) + mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.01); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isUnstable = true; //dies when blocked + me.showHealthBar = false; + me.isOrbital = true; + // me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) + //damage player + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.03 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + this.death(); + } + }; + }, + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + if (this.allowShields && Math.random() < chance) { + mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(0,0,0)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.shield = true; + me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isUnblockable = true + me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: target, //attach shield to target + stiffness: 0.4, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + me.onDamage = function () { + //make sure the mob that owns the shield can tell when damage is done + this.alertNearByMobs(); + this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + + me.shieldTargetID = target.id + target.isShielded = true; + target.shieldID = me.id + me.onDeath = function () { + //clear isShielded status from target + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; + } + }; + me.do = function () { + this.checkStatus(); + }; + + mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically + + //swap order of shield and mob, so that mob is behind shield graphically + // mob[mob.length - 1] = mob[mob.length - 2]; + // mob[mob.length - 2] = me; + } + }, + groupShield(targets, x, y, radius, stiffness = 0.4) { + const nodes = targets.length + mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(0,0,0)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.frictionAir = 0; + me.shield = true; + me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + for (let i = 0; i < nodes; ++i) { + mob[mob.length - i - 2].isShielded = true; + //constrain to all mob nodes in group + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 2], + stiffness: stiffness, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + me.onDamage = function () { + this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done + this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` + }; + me.onDeath = function () { + //clear isShielded status from target + for (let j = 0; j < targets.length; j++) { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === targets[j]) mob[i].isShielded = false; + } + } + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + mob[mob.length - 1] = mob[mob.length - 1 - nodes]; + mob[mob.length - 1 - nodes] = me; + me.do = function () { + this.checkStatus(); + }; + }, + slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0009 * simulation.accelScale; + me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.035; + me.delay = 140 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 275 + 3.5 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.03 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = 200000 + ace.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.attraction(); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + this.laserAngle = -Math.PI / 6 + this.sword = this.swordGrow + this.accelMag = 0 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); + this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); + this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax || this.isStunned) { + this.sword = this.swordSlash + this.spinCount = 0 + } + } + me.swordSlash = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); + this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); + this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); + + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 100 || this.isStunned) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.001 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + } + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + const sides = 6 + mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.02; + me.delay = 150 * simulation.CDScale; + me.cd = 0; + me.cycle = 0; + me.swordVertex = 1 + me.swordRadiusInitial = radius / 2; + me.swordRadius = me.swordRadiusInitial; + me.swordRadiusMax = 750 + 6 * simulation.difficulty; + me.swordRadiusGrowRateInitial = 1.08 + me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.04 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax + ace.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + this.attraction(); + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex closest to the player + let dist = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D < dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides + this.sword = this.swordGrow + this.cycle = 0 + this.swordRadius = this.swordRadiusInitial + //slow velocity but don't stop + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5)) + //set angular velocity to 50% + // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5) + //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise + const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) + const playerVector = Vector.sub(this.position, m.pos) + const cross = Matter.Vector.cross(laserStartVector, playerVector) + this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9)) + // this.swordRadius += this.swordRadiusGrowRate + this.cycle++ + // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03) + this.swordRadius *= this.swordRadiusGrowRate + + if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial + // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate) + if (this.swordRadius < this.swordRadiusInitial || this.isStunned) { + // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate) + this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial + this.sword = this.swordWaiting + this.swordRadius = 0 + this.cd = simulation.cycle + this.delay; + } + } + me.laserSpear = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead)) { + this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player + + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) { + if (radius > 80) radius = 65; + mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem) + let me = mob[mob.length - 1]; + me.isVerticesChange = true + me.accelMag = 0.0006 * simulation.accelScale; + // me.g = 0.0002; //required if using this.gravity + me.isInvulnerable = false + me.delay = 360 * simulation.CDScale; + me.spikeVertex = 0; + me.spikeLength = 0; + me.isSpikeGrowing = false; + me.spikeGrowth = 0; + me.isSpikeReset = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs + Matter.Body.rotate(me, Math.PI * 0.1); + ace.shield(me, x, y); + // me.onDamage = function () {}; + // me.onHit = function() { //run this function on hitting player + // }; + me.onDeath = function () { + if (this.spikeLength > 4) { + this.spikeLength = 4 + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + }; + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + if (this.isSpikeReset) { + if (this.seePlayer.recall) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + if (distMag < radius * spikeMax) { + //find nearest vertex + let nearestDistance = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + //find distance to player for each vertex + const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); + const distMag = Vector.magnitude(dist); + //save the closest distance + if (distMag < nearestDistance) { + this.spikeVertex = i + nearestDistance = distMag + } + } + this.spikeLength = 1 + this.isSpikeGrowing = true; + this.isSpikeReset = false; + Matter.Body.setAngularVelocity(this, 0) + } + me.isInvulnerable = false + } + } else { + if (this.isSpikeGrowing) { + this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8) + // if (this.spikeLength < 2) { + // this.spikeLength += 0.035 + // } else { + // this.spikeLength += 1 + // } + if (this.spikeLength > spikeMax) { + this.isSpikeGrowing = false; + this.spikeGrowth = 0 + } + } else { + Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation + this.spikeLength -= 0.3 + if (this.spikeLength < 1) { + this.spikeLength = 1 + this.isSpikeReset = true + this.radius = radius + } + } + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + me.isInvulnerable = true + // this.radius = radius * this.spikeLength; + } + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + me.damageReduction = 0; + } else { + me.damageReduction = 1; + } + }; + }, + slash(x, y, radius = 80) { + let targets = [] + const sides = 6; + mobs.spawn(x, y, 6, radius, "#000000"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + targets.push(me.id) //add to shield protection + const nodeBalance = Math.random() + const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) + me.isBoss = true; + me.isSlashBoss = true; + me.showHealthBar = false; + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.frictionAir = 0.02 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + Matter.Body.setDensity(me, 0.0005); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.seePlayerFreq = 20 + me.lockedOn = null; + me.laserRange = 500; + me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.delay = 70 + 70 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 1100 + 20 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.07 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + me.eventHorizon = 550; + const seeDistance2 = 200000 + ace.shield(me, x, y); + const rangeInnerVsOuter = Math.random() + let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7 + for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed) + const orbitalIndexes = [] //find indexes for all the current nodes2 + for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i) + // add orbitals for each orbital + range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80) + speed = speed * (1.25 + 2 * Math.random()) + const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty))) + for (let j = 0; j < nodes2; j++) { + for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) + } + for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1); + + const springStiffness = 0.00014; + const springDampening = 0.0005; + + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + + me.springTarget2 = { + x: me.position.x, + y: me.position.y + }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening, + length: 0 + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + me.onDamage = function () { }; + me.onDeath = function () { + isDestroyed = true; + this.removeCons(); + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + }; + me.do = function () { + for (let i = 0; i < this.vertices.length; i++) { + this.harmField(this.vertices[i].x, this.vertices[i].y); + } + this.seePlayerByHistory(40); + this.springAttack(); + this.checkStatus(); + this.sword() //does various things depending on what stage of the sword swing + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) + me.laserRange = eventHorizon; + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + !m.isCloak && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex farthest away from player + let dist = 0 + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D > dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283 + this.sword = this.swordGrow + Matter.Body.setAngularVelocity(this, 0) + this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + this.damageReduction = 0 + this.isInvulnerable = true + this.frictionAir = 1 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax) { + this.sword = this.swordSlash + this.spinCount = 0 + } + + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.swordSlash = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 80) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + this.damageReduction = this.startingDamageReduction + this.isInvulnerable = false + this.frictionAir = 0.01 + } + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(0,0,0,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path + ctx.lineWidth = 25; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path + ctx.lineWidth = 5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + me.harmField = function (x, y) { + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + // ctx.lineDashOffset = 6*(simulation.cycle % 215); + if (this.distanceToPlayer3(x, y) < this.laserRange) { + if (m.immuneCycle < m.cycle) { + m.damage(0.0003 * simulation.dmgScale); + if (m.energy > 0.1) m.energy -= 0.003 + } + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgb(0,0,0)"; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.15)"; + ctx.fill(); + } + ctx.beginPath(); + ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.lineWidth = 1; + ctx.stroke(); + ctx.setLineDash([]); + ctx.fillStyle = "rgba(0,0,0,0.03)"; + ctx.fill(); + } + me.distanceToPlayer3 = function (x, y) { + const dx = x - player.position.x; + const dy = y - player.position.y; + return Math.sqrt(dx * dx + dy * dy); + } + radius = 22 // radius of each node mob + const sideLength = 100 // distance between each node mob + const nodes = 6 + const angle = 2 * Math.PI / nodes + + spawn.allowShields = false; //don't want shields on individual mobs + + for (let i = 0; i < nodes; ++i) { + ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); + Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger + mob[mob.length - 1].damageReduction = 0.12 + mob[mob.length - 1].showHealthBar = false; + mob[mob.length - 1].isBoss = true; + targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields + } + + const attachmentStiffness = 0.02 + spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together + + for (let i = 0; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 1], + stiffness: attachmentStiffness, + damping: 0.03 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + //spawn shield around all nodes + ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); + spawn.allowShields = true; + }, + } + level.setPosToSpawn(-625, -100); //normal spawn + level.exit.x = -23650; + level.exit.y = 11100; + simulation.fallHeight = 20000; + const door = level.door(350, -200, 25, 225, 225, 10) + const door2 = level.door(6325, -200, 25, 225, 225, 10); + // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + ace.stabber(425, -100); + ace.stabber(725, -100); + ace.stabber(1000, -100); + ace.stabber(1300, -100); + ace.stabber(1550, -100); + ace.stabber(1850, -100); + ace.stabber(2125, -100); + ace.stabber(2400, -100); + ace.stabber(2675, -100); + ace.stabber(2975, -100); + ace.stabber(3225, -100); + ace.stabber(3525, -100); + ace.stabber(3800, -100); + ace.stabber(4100, -100); + ace.stabber(4375, -100); + ace.stabber(4650, -100); + ace.stabber(4925, -100); + ace.stabber(5200, -100); + ace.stabber(5500, -100); + ace.stabber(5775, -100); + spawn.mapRect(-200, -450, 2825, 75); + spawn.mapRect(-200, 0, 2825, 75); + spawn.mapRect(-300, -400, 150, 50); + spawn.mapRect(-575, -375, 325, 50); + spawn.mapRect(-1175, 0, 700, 75); + spawn.mapRect(-1100, 50, 675, 75); + spawn.mapRect(-1100, -50, 225, 75); + spawn.mapRect(-1025, -75, 200, 50); + spawn.mapRect(-875, -50, 150, 75); + spawn.mapRect(-700, -350, 325, 50); + spawn.mapRect(-950, -100, 150, 50); + spawn.mapRect(-675, -50, 125, 25); + spawn.mapRect(-575, -150, 25, 125); + spawn.mapRect(-650, -50, 25, 75); + spawn.mapRect(-600, -50, 25, 75); + spawn.mapRect(-800, -325, 250, 50); + spawn.mapRect(-1450, 50, 500, 75); + spawn.mapRect(-1550, 100, 475, 100); + spawn.mapRect(-1650, 175, 525, 375); + spawn.mapRect(-1700, 275, 200, 175); + spawn.mapRect(-1550, 525, 475, 100); + spawn.mapRect(-1475, 600, 4125, 100); + spawn.mapRect(-50, 50, 75, 75); + level.chain(-450, 75, 0, false, 13) + level.chain(7475, 600, -0.5498531827, false, 200) + spawn.mapRect(325, -425, 75, 275); + spawn.mapRect(-850, -300, 100, 50); + spawn.mapRect(-900, -125, 75, 50); + spawn.mapRect(-875, -275, 50, 50); + spawn.mapRect(425, -400, 125, 100); + spawn.mapRect(600, -400, 125, 100); + spawn.mapRect(775, -400, 125, 100); + spawn.mapRect(950, -400, 125, 100); + spawn.mapRect(375, -350, 2250, 25); + spawn.mapRect(1125, -400, 125, 100); + spawn.mapRect(1300, -400, 125, 100); + spawn.mapRect(1475, -400, 125, 100); + spawn.mapRect(1650, -400, 125, 100); + spawn.mapRect(1825, -400, 125, 100); + spawn.mapRect(2000, -400, 125, 100); + spawn.mapRect(2175, -400, 125, 100); + spawn.mapRect(2350, -400, 125, 100); + spawn.mapRect(2525, -400, 125, 100); + spawn.mapRect(-1350, 650, 4000, 150); + spawn.mapRect(-1400, 675, 125, 75); + spawn.mapRect(-1325, 25, 200, 50); + spawn.mapRect(2600, 600, 4350, 200); + spawn.mapRect(2550, 0, 4400, 75); + spawn.mapRect(6875, 0, 875, 75); + spawn.mapRect(2500, -450, 5300, 50); + spawn.mapRect(7575, -425, 575, 475); + spawn.mapRect(6825, 600, 650, 125); + spawn.mapRect(6875, 675, 475, 100); + spawn.mapRect(7050, -525, 1175, 175); + spawn.mapRect(7650, -25, 575, 150); + spawn.mapRect(6075, -500, 1125, 75); + spawn.mapRect(2550, -350, 3450, 25); + spawn.mapRect(2700, -400, 125, 100); + spawn.mapRect(2875, -400, 125, 100); + spawn.mapRect(3050, -400, 125, 100); + spawn.mapRect(3225, -400, 125, 100); + spawn.mapRect(3400, -400, 125, 100); + spawn.mapRect(3575, -400, 125, 100); + spawn.mapRect(3750, -400, 125, 100); + spawn.mapRect(3925, -400, 125, 100); + spawn.mapRect(4100, -400, 125, 100); + spawn.mapRect(4275, -400, 125, 100); + spawn.mapRect(4450, -400, 125, 100); + spawn.mapRect(4625, -400, 125, 100); + spawn.mapRect(4800, -400, 125, 100); + spawn.mapRect(4975, -400, 125, 100); + spawn.mapRect(5150, -400, 125, 100); + spawn.mapRect(5325, -400, 125, 100); + spawn.mapRect(5500, -400, 125, 100); + spawn.mapRect(5675, -400, 125, 100); + spawn.mapRect(5850, -400, 125, 100); + spawn.mapRect(6000, -400, 125, 100); + spawn.mapRect(6100, -425, 125, 100); + spawn.mapRect(6150, -450, 200, 75); + spawn.mapRect(2575, -400, 3575, 25); + spawn.mapRect(6300, -425, 75, 250); + spawn.mapRect(-200, -175, 50, 50); + spawn.bodyRect(-950, 475, 150, 125); + spawn.bodyRect(-650, 475, 150, 125); + spawn.bodyRect(-1000, 350, 550, 125); + spawn.bodyRect(-650, 150, 225, 200); + spawn.bodyRect(-1050, 150, 400, 200); + spawn.bodyRect(-1125, 225, 25, 275); + spawn.bodyRect(-1100, 350, 125, 200); + spawn.bodyRect(-800, 475, 150, 125); + spawn.bodyRect(-500, 475, 125, 125); + spawn.mapRect(-3325, -50, 600, 75); + spawn.mapRect(-3250, 0, 450, 75); + spawn.mapRect(-2950, -100, 350, 75); + spawn.mapRect(-2975, -150, 325, 75); + spawn.mapRect(-3150, -250, 125, 25); + spawn.mapRect(-3050, -225, 100, 25); + spawn.mapRect(-3000, -200, 125, 25); + spawn.mapRect(-3425, -75, 325, 50); + spawn.mapRect(-3250, -225, 125, 25); + spawn.mapRect(-3325, -200, 100, 25); + spawn.mapRect(-3100, -300, 25, 50); + spawn.mapRect(-3725, -325, 1300, 25); + spawn.mapRect(-2925, -175, 125, 25); + spawn.mapRect(-3375, -175, 100, 25); + spawn.mapRect(-3550, -150, 250, 75); + spawn.mapRect(-3725, -150, 250, 50); + spawn.mapRect(-3725, -200, 125, 75); + spawn.mapRect(-3625, -175, 50, 25); + spawn.mapRect(-3750, -125, 50, 25); + spawn.mapRect(-3750, -50, 125, 50); + spawn.mapRect(-3650, -125, 75, 100); + spawn.mapRect(-2750, 0, 100, 75); + spawn.mapRect(-2675, 25, 175, 25); + spawn.mapRect(-2850, 0, 150, 50); + spawn.mapRect(-3150, 50, 25, 75); + spawn.mapRect(-2900, 50, 25, 75); + spawn.mapRect(-3300, 100, 575, 25); + spawn.mapRect(-24300, 11125, 51525, 6250); + spawn.mapVertex(7900, -675, "0 0 500 -200 500 300 -450 300") + spawn.mapRect(-24300, 9575, 475, 1750); + spawn.mapRect(26800, 9575, 425, 1750); + spawn.hopper(-3100, -150); + spawn.mapRect(11625, 8375, 11200, 225); + spawn.mapRect(22600, 8375, 225, 3225); + spawn.mapRect(11625, 8375, 225, 1800); + spawn.mapRect(12425, 9825, 225, 875); + spawn.mapRect(13150, 10725, 225, 575); + spawn.mapRect(14125, 10450, 5025, 200); + spawn.mapRect(21775, 10625, 1050, 225); + spawn.mapRect(20325, 10925, 1300, 200); + spawn.mapRect(20825, 10250, 750, 225); + spawn.mapRect(19500, 10000, 1000, 225); + + + ace.slasher2(-22725, 10325); + ace.slasher3(-23425, 10250); + ace.slasher2(-23350, 10700); + ace.slasher3(-21725, 11075); + ace.slasher2(-21525, 10025); + ace.slasher3(-20950, 9750); + ace.slasher2(-19975, 9700); + ace.slasher3(-18850, 9650); + ace.slasher2(-18675, 9700); + ace.slasher3(-18250, 9125); + ace.slasher2(-17775, 8925); + ace.slasher3(-16975, 8875); + ace.slasher2(-16475, 9125); + ace.slasher3(-16125, 9275); + ace.slasher2(-15650, 9225); + ace.slasher3(-15200, 9175); + ace.slasher2(-16800, 9325); + ace.slasher3(-17450, 9525); + ace.slasher2(-18375, 9625); + ace.slasher3(-19650, 9375); + ace.slasher2(-20600, 9225); + ace.slasher3(-21625, 9400); + ace.slasher2(-22450, 9775); + ace.slasher3(-22900, 10000); + ace.slasher2(-23275, 9300); + ace.slasher3(-23125, 9150); + ace.slasher2(2800, 9350); + ace.slasher3(4925, 9825); + ace.slasher2(3725, 10525); + ace.slasher3(1850, 10450); + ace.slash(16850, 10075); + spawn.mapVertex(-14325, 11000, "0 0 4000 -2000 10000 -3000 16000 -2000 20000 0"); + + let q = Matter.Bodies.rectangle(7525 + (1100 / 2), 10675 + (150 / 2), 1100, 150, { + density: 0.05, + isNotHoldable: false, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qq = Matter.Bodies.rectangle(7375 + (150 / 2), 10550 + (200 / 2), 150, 200, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqq = Matter.Bodies.rectangle(7450 + (1250 / 2), 10500 + (100 / 2), 1250, 100, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqq = Matter.Bodies.rectangle(8625 + (150 / 2), 10550 + (200 / 2), 150, 200, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqq = Matter.Bodies.rectangle(7600 + (100 / 2), 10350 + (200 / 2), 100, 200, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqq = Matter.Bodies.rectangle(8475 + (100 / 2), 10350 + (200 / 2), 100, 200, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqq = Matter.Bodies.rectangle(7650 + (725 / 2), 10325 + (75 / 2), 725, 75, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqqq = Matter.Bodies.rectangle(8000 + 100, 10200 + (150 / 2), 200, 150, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqqqq = Matter.Bodies.rectangle(6975 + (1125 / 2), 10250 + 25, 1125, 50, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqqqqq = Matter.Bodies.rectangle(7600 + 50, 10575 + (125 / 2), 100, 125, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqqqqqq = Matter.Bodies.rectangle(8475 + 50, 10575 + (125 / 2), 100, 125, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + let qqqqqqqqqqqq = Matter.Bodies.rectangle(8025 + 50, 10575 + (125 / 2), 100, 125, { + density: 0.05, + isNotHoldable: true, + restitution: 1.05, + isStatic: false + }, true, [true], 0); + + + wasd = Matter.Body.create({ + parts: [q, qq, qqq, qqqq, qqqqq, qqqqqq, qqqqqqq, qqqqqqqq, qqqqqqqqq, qqqqqqqqqq, qqqqqqqqqqq, qqqqqqqqqqqq] + }); + + body[body.length] = q; + body[body.length] = qq; + body[body.length] = qqq; + body[body.length] = qqqq; + body[body.length] = qqqqq; + body[body.length] = qqqqqq; + body[body.length] = qqqqqqq; + body[body.length] = qqqqqqqq; + body[body.length] = qqqqqqqqq; + body[body.length] = qqqqqqqqqq; + body[body.length] = qqqqqqqqqqq; + body[body.length] = qqqqqqqqqqqq; + // body[body.length] = wasd; + + Matter.Composite.add(engine.world, wasd) + composite[composite.length] = wasd; + // wasd.friction -= 0.5 + setTimeout(function () { + wasd.collisionFilter.category = cat.map; + wasd.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob | cat.map + }, 100); + let Vx = 0; + var gradient = ctx.createLinearGradient(0, 0, 10975 / 2, 0); + gradient.addColorStop(0, "#00000000"); + gradient.addColorStop(1, "#686868"); + level.custom = () => { + wasd.force.y += simulation.g * wasd.mass; + if (Matter.Query.collides(wasd, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && input.down && isDestroyed) { + wasd.force.x += Math.cos(m.angle) * 75; + Matter.Body.setVelocity(player, wasd.velocity) + m.Vx = player.velocity.x - wasd.velocity.x; + } + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(wasd, [mob[i]]).length > 0 && !mob[i].isBoss && isDestroyed) { + const dmg = 1; + mob[i].damage(dmg, true); + simulation.drawList.push({ //add dmg to draw queue + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + break + } + } + Vx = wasd.velocity.x / 5; + level.exit.drawAndCheck(); + drawSeats(475, -50, 5600, 125, 20, "darkgray"); + door.openClose() + door2.openClose() + if (player.position.y < 25) { + door.isClosing = false; + door2.isClosing = false; + } else { + door.isClosing = true; + door2.isClosing = true; + } + ctx.fillStyle = "red"; + ctx.fillRect(-825, -75, 50, 50); + + b.pulse(30, 0, { x: -2500, y: (25 + (25 / 2)) }); + ctx.save() + ctx.translate(11750, 8475) + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 10975, 2800); + ctx.restore() + drawHead(7400, 0, Math.PI * 0.1); + drawHead(7460, 0, Math.PI * 0.5); + drawHead(7520, 0, Math.PI * 0.3); + drawHead(22400, 11125, Math.PI * 0.3); + drawHead(21925, 10625, Math.PI * 0.5); + drawHead(21175, 10250, Math.PI * 0.1); + drawHead(22525, 10625, Math.PI * 0.7); + drawHead(22525, 11125, Math.PI * 0.9); + drawHead(22225, 11125, Math.PI * 1.5); + }; + level.customTopLayer = () => { + drawSeats(500, -50, 5600, 125); + + ctx.strokeStyle = 'red'; + ctx.lineWidth = 20; + ctx.beginPath(); + ctx.setLineDash([40, 40]); + ctx.lineDashOffset = (-simulation.cycle * Vx) % 80; + + ctx.moveTo(q.vertices[0].x, q.vertices[0].y); + for (let i = 1; i < q.vertices.length; i++) { + ctx.lineTo(q.vertices[i].x, q.vertices[i].y); + } + ctx.lineTo(q.vertices[0].x, q.vertices[0].y); + ctx.closePath(); + ctx.stroke(); + ctx.setLineDash([0, 0]); + }; + function drawSeats(x, y, w, h, num = 20, c = "gray") { + const seatWidth = w / num; + const seatHeight = h / num; + + for (let i = 0; i < num; i++) { + const seatX = x + i * seatWidth; + const seatY = y; + + // Draw the seat parts + ctx.fillStyle = c; + ctx.fillRect(seatX - 100, seatY, 125, 25); + ctx.fillRect(seatX, seatY - 125, 25, 150); + ctx.fillRect(seatX - 75, seatY, 25, 75); + ctx.fillRect(seatX - 25, seatY, 25, 75); + } + } + function drawHead(x, y, angle) { + ctx.save(); + ctx.translate(x, y - 30); + ctx.rotate(angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + } + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isSlashBoss) { + simulation.ephemera.push({ + name: "bossBar", + do() { + if (level.levels[level.onLevel] == "ace" && !isDestroyed) { + ctx.save(); + ctx.setTransform(1, 0, -0.5, 1, 0, 0); //slanted + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); + ctx.restore(); + } + }, + }) + } + } + }, + crimsonTowers() { + simulation.makeTextLog(`crimsonTowers by Richard0820. Thank you desboot for the video: Source`) + const ace = { + spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { + if (Math.random() < chance) { + // simulation.difficulty = 50 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + const offSet = 6.28 * Math.random() + for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) + } + }, + orbital(who, radius, phase, speed) { + // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) + mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.01); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isUnstable = true; //dies when blocked + me.showHealthBar = false; + me.isOrbital = true; + // me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) + //damage player + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.03 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + this.death(); + } + }; + }, + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + if (this.allowShields && Math.random() < chance) { + mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(0,0,0)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.shield = true; + me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isUnblockable = true + me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: target, //attach shield to target + stiffness: 0.4, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + me.onDamage = function () { + //make sure the mob that owns the shield can tell when damage is done + this.alertNearByMobs(); + this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + + me.shieldTargetID = target.id + target.isShielded = true; + target.shieldID = me.id + me.onDeath = function () { + //clear isShielded status from target + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; + } + }; + me.do = function () { + this.checkStatus(); + }; + + mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically + + //swap order of shield and mob, so that mob is behind shield graphically + // mob[mob.length - 1] = mob[mob.length - 2]; + // mob[mob.length - 2] = me; + } + }, + groupShield(targets, x, y, radius, stiffness = 0.4) { + const nodes = targets.length + mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(0,0,0)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.frictionAir = 0; + me.shield = true; + me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + for (let i = 0; i < nodes; ++i) { + mob[mob.length - i - 2].isShielded = true; + //constrain to all mob nodes in group + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 2], + stiffness: stiffness, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + me.onDamage = function () { + this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done + this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` + }; + me.onDeath = function () { + //clear isShielded status from target + for (let j = 0; j < targets.length; j++) { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === targets[j]) mob[i].isShielded = false; + } + } + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + mob[mob.length - 1] = mob[mob.length - 1 - nodes]; + mob[mob.length - 1 - nodes] = me; + me.do = function () { + this.checkStatus(); + }; + }, + slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0009 * simulation.accelScale; + me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.035; + me.delay = 140 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 275 + 3.5 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.03 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = 200000 + ace.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.attraction(); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + this.laserAngle = -Math.PI / 6 + this.sword = this.swordGrow + this.accelMag = 0 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); + this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); + this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax || this.isStunned) { + this.sword = this.swordSlash + this.spinCount = 0 + } + } + me.swordSlash = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); + this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); + this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); + + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 100 || this.isStunned) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.001 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + } + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + const sides = 6 + mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.02; + me.delay = 150 * simulation.CDScale; + me.cd = 0; + me.cycle = 0; + me.swordVertex = 1 + me.swordRadiusInitial = radius / 2; + me.swordRadius = me.swordRadiusInitial; + me.swordRadiusMax = 750 + 6 * simulation.difficulty; + me.swordRadiusGrowRateInitial = 1.08 + me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.04 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax + ace.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + this.attraction(); + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex closest to the player + let dist = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D < dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides + this.sword = this.swordGrow + this.cycle = 0 + this.swordRadius = this.swordRadiusInitial + //slow velocity but don't stop + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5)) + //set angular velocity to 50% + // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5) + //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise + const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) + const playerVector = Vector.sub(this.position, m.pos) + const cross = Matter.Vector.cross(laserStartVector, playerVector) + this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9)) + // this.swordRadius += this.swordRadiusGrowRate + this.cycle++ + // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03) + this.swordRadius *= this.swordRadiusGrowRate + + if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial + // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate) + if (this.swordRadius < this.swordRadiusInitial || this.isStunned) { + // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate) + this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial + this.sword = this.swordWaiting + this.swordRadius = 0 + this.cd = simulation.cycle + this.delay; + } + } + me.laserSpear = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead)) { + this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player + + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) { + if (radius > 80) radius = 65; + mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem) + let me = mob[mob.length - 1]; + me.isVerticesChange = true + me.accelMag = 0.0006 * simulation.accelScale; + // me.g = 0.0002; //required if using this.gravity + me.isInvulnerable = false + me.delay = 360 * simulation.CDScale; + me.spikeVertex = 0; + me.spikeLength = 0; + me.isSpikeGrowing = false; + me.spikeGrowth = 0; + me.isSpikeReset = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs + Matter.Body.rotate(me, Math.PI * 0.1); + ace.shield(me, x, y); + // me.onDamage = function () {}; + // me.onHit = function() { //run this function on hitting player + // }; + me.onDeath = function () { + if (this.spikeLength > 4) { + this.spikeLength = 4 + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + }; + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + if (this.isSpikeReset) { + if (this.seePlayer.recall) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + if (distMag < radius * spikeMax) { + //find nearest vertex + let nearestDistance = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + //find distance to player for each vertex + const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); + const distMag = Vector.magnitude(dist); + //save the closest distance + if (distMag < nearestDistance) { + this.spikeVertex = i + nearestDistance = distMag + } + } + this.spikeLength = 1 + this.isSpikeGrowing = true; + this.isSpikeReset = false; + Matter.Body.setAngularVelocity(this, 0) + } + me.isInvulnerable = false + } + } else { + if (this.isSpikeGrowing) { + this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8) + // if (this.spikeLength < 2) { + // this.spikeLength += 0.035 + // } else { + // this.spikeLength += 1 + // } + if (this.spikeLength > spikeMax) { + this.isSpikeGrowing = false; + this.spikeGrowth = 0 + } + } else { + Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation + this.spikeLength -= 0.3 + if (this.spikeLength < 1) { + this.spikeLength = 1 + this.isSpikeReset = true + this.radius = radius + } + } + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + me.isInvulnerable = true + // this.radius = radius * this.spikeLength; + } + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + me.damageReduction = 0; + } else { + me.damageReduction = 1; + } + }; + }, + slash(x, y, radius = 80) { + let targets = [] + const sides = 6; + mobs.spawn(x, y, 6, radius, "#000000"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + targets.push(me.id) //add to shield protection + const nodeBalance = Math.random() + const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) + me.isBoss = true; + me.isSlashBoss = true; + me.showHealthBar = false; + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.frictionAir = 0.02 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + Matter.Body.setDensity(me, 0.0005); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.seePlayerFreq = 20 + me.lockedOn = null; + me.laserRange = 500; + me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.delay = 70 + 70 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 1100 + 20 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.07 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + me.eventHorizon = 550; + const seeDistance2 = 200000 + ace.shield(me, x, y); + const rangeInnerVsOuter = Math.random() + let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7 + for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed) + const orbitalIndexes = [] //find indexes for all the current nodes2 + for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i) + // add orbitals for each orbital + range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80) + speed = speed * (1.25 + 2 * Math.random()) + const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty))) + for (let j = 0; j < nodes2; j++) { + for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) + } + for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1); + + const springStiffness = 0.00014; + const springDampening = 0.0005; + + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + + me.springTarget2 = { + x: me.position.x, + y: me.position.y + }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening, + length: 0 + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + me.onDamage = function () { }; + me.onDeath = function () { + isDestroyed = true; + this.removeCons(); + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + }; + me.do = function () { + for (let i = 0; i < this.vertices.length; i++) { + this.harmField(this.vertices[i].x, this.vertices[i].y); + } + this.seePlayerByHistory(40); + this.springAttack(); + this.checkStatus(); + this.sword() //does various things depending on what stage of the sword swing + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) + me.laserRange = eventHorizon; + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + !m.isCloak && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex farthest away from player + let dist = 0 + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D > dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283 + this.sword = this.swordGrow + Matter.Body.setAngularVelocity(this, 0) + this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + this.damageReduction = 0 + this.isInvulnerable = true + this.frictionAir = 1 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax) { + this.sword = this.swordSlash + this.spinCount = 0 + } + + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.swordSlash = function () { + const angle = this.angle + this.laserAngle; + const end = { + x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), + y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) + }; + + const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; + const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; + const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); + + const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; + const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; + const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); + + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) + this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 80) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + this.damageReduction = this.startingDamageReduction + this.isInvulnerable = false + this.frictionAir = 0.01 + } + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(0,0,0,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path + ctx.lineWidth = 25; + ctx.stroke(); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path + ctx.lineWidth = 5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + me.harmField = function (x, y) { + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + // ctx.lineDashOffset = 6*(simulation.cycle % 215); + if (this.distanceToPlayer3(x, y) < this.laserRange) { + if (m.immuneCycle < m.cycle) { + m.damage(0.0003 * simulation.dmgScale); + if (m.energy > 0.1) m.energy -= 0.003 + } + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgb(0,0,0)"; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.15)"; + ctx.fill(); + } + ctx.beginPath(); + ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.lineWidth = 1; + ctx.stroke(); + ctx.setLineDash([]); + ctx.fillStyle = "rgba(0,0,0,0.03)"; + ctx.fill(); + } + me.distanceToPlayer3 = function (x, y) { + const dx = x - player.position.x; + const dy = y - player.position.y; + return Math.sqrt(dx * dx + dy * dy); + } + radius = 22 // radius of each node mob + const sideLength = 100 // distance between each node mob + const nodes = 6 + const angle = 2 * Math.PI / nodes + + spawn.allowShields = false; //don't want shields on individual mobs + + for (let i = 0; i < nodes; ++i) { + ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); + Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger + mob[mob.length - 1].damageReduction = 0.12 + mob[mob.length - 1].showHealthBar = false; + mob[mob.length - 1].isBoss = true; + targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields + } + + const attachmentStiffness = 0.02 + spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together + + for (let i = 0; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 1], + stiffness: attachmentStiffness, + damping: 0.03 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + //spawn shield around all nodes + ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); + spawn.allowShields = true; + }, + } + level.setPosToSpawn(0, -50); + color.map = "crimson"; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(0, 0, 1, 1); + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + const isSus = Math.random() < 0.001; //A very lucky person gets rickrolled + const mediaSource = isSus ? "https://ia801509.us.archive.org/10/items/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.ogv" : "https://cdn.glitch.me/b559a783-c0cb-4369-92e3-0c0a5556ba01/n-gon%20evangelion%20-%20Made%20with%20Clipchamp%20(8).mp4?v=1692134040246" + let videoContainer; + let video = document.createElement("video"); + video.src = mediaSource; + video.autoPlay = true; + video.loop = true; + video.muted = true; + videoContainer = { + video: video, + ready: true, + }; + video.play(); + const boost1 = level.boost(8835, -3675, 7500); + const boost2 = level.boost(-8935, -3675, 7500); + ace.slash(0, -15000 + 1800); + function Raindrop(minX, minY, maxX, maxY) { + this.x = minX + Math.random() * (maxX - minX); + this.y = minY + Math.random() * (maxY - minY); + this.speed = Math.random() * 5 + 25; + this.length = Math.random() * 20 + 30; + } + function forceField(x, y, width, height) { + return { + min: { x: x, y: y }, + max: { x: x + width, y: y + height }, + width: width, + height: height, + maxHeight: height, + raindrops: [], + drawRaindrop(drop) { + if (Math.sqrt(Math.pow(player.position.x - drop.x, 2) + Math.pow(player.position.y - drop.y, 2)) + Math.PI < 5000) { + ctx.beginPath(); + ctx.moveTo(drop.x, drop.y); + ctx.lineTo(drop.x, drop.y + drop.length); + ctx.strokeStyle = '#00FFFF'; + ctx.lineWidth = 10; + ctx.lineCap = 'butt'; + ctx.stroke(); + } + }, + updateRaindrop(drop) { + drop.y += drop.speed; + if ((Matter.Query.ray(map, { x: drop.x, y: drop.y }, { x: drop.x, y: drop.y - drop.length }).length === 0) == false) { + simulation.drawList.push({ + x: drop.x, + y: drop.y - drop.length, + radius: 10, + color: "rgb(0,100,250,0.3)", + time: 8 + }); + do { + drop.y = this.min.y + this.height * Math.random(); + drop.x = this.min.x + this.width * Math.random(); + } while (drop.x > this.min.x && drop.x < this.max.x && drop.y > this.min.y && drop.y < this.max.y) + } + }, + isOn: true, + query() { + if (this.isOn) { + ctx.fillStyle = `rgba(200, 20, 10, 0.55)` + ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + if (this.height > 0 && Matter.Query.region([player], this).length) { + player.force.y -= 0.015; + m.energy = m.maxEnergy; + } + // if(this.raindrops.length < 300) { // too many (like 900) can cause a little bit of lag minus 5 ~ 10 fps, but it really just depends on your computer + // this.raindrops.push(new Raindrop()); + // } + // for (let i = 0; i < this.raindrops.length; i++) { + // const drop = this.raindrops[i]; + // this.drawRaindrop(drop); + // this.updateRaindrop(drop); + // } + } + }, + } + } + const forceField1 = forceField(-750, -30000, 1500, 20000); + level.custom = () => { + if (player.position.y < -20000) { + level.nextLevel(); + } + forceField1.query(); + boost1.query(); + boost2.query(); + level.exit.drawAndCheck(); + level.enter.draw(); + ctx.beginPath(); + ctx.strokeStyle = "rgba(220, 20, 10, 0.55)"; + ctx.lineWidth = 1500; + ctx.lineJoin = "miter" + ctx.miterLimit = 100; + ctx.moveTo(map[272].vertices[0].x, map[272].vertices[0].y); + for (let i = 0; i < map[272].vertices.length; i++) { + ctx.lineTo(map[272].vertices[i].x, map[272].vertices[i].y); + } + ctx.closePath(); + ctx.stroke(); + }; + let checkVid = () => { + if (simulation.paused && !videoContainer.paused) { + videoContainer.paused = true; + video.pause(); + } else if (!simulation.paused && videoContainer.paused) { + videoContainer.paused = false; + video.play(); + } + requestAnimationFrame(checkVid); + } + checkVid(); + simulation.ephemera.push({ + name: "vid", + do() { + if (level.levels[level.onLevel] !== "crimsonTowers") simulation.removeEphemera(this.name); + if (mediaSource && !isSus) { + ctx.drawImage(videoContainer.video, -1600, -15000, 3200, 1800); + } else if (mediaSource) { + ctx.drawImage(videoContainer.video, -1920 / 2, -15000, 1920, 1080); + } + } + }); + level.customTopLayer = () => { + ctx.fillStyle = "rgba(220, 20, 10, 0.1)"; + ctx.fillRect(-6725, -3500, 475, 2925); + ctx.fillRect(-8725, -3700, 450, 2925); + ctx.fillRect(-4725, -3300, 450, 2925); + ctx.fillRect(-2725, -3100, 450, 2925); + ctx.fillRect(-725, -2900, 450, 2925); + ctx.fillRect(275, -2900, 450, 2925); + ctx.fillRect(2275, -3100, 450, 2925); + ctx.fillRect(4275, -3300, 450, 2925); + ctx.fillRect(6275, -3500, 450, 2925); + ctx.fillRect(8275, -3700, 450, 2925); + }; + spawn.mapRect(-10000, 0, 20000, 2000); + spawn.mapRect(-9050, -3650, 350, 50); + spawn.mapRect(8700, -3650, 350, 50); + spawn.mapRect(-275, -2825, 550, 50); + spawn.mapRect(-225, -500, 450, 50); + spawn.mapRect(-250, -1575, 500, 50); + function spawnTower(index, y = 0) { + const x = index - 1325; + spawn.mapRect(x + 1025, y + -950, 125, 750); + spawn.mapRect(x + 1125, y + -225, 50, 50); + spawn.mapRect(x + 1500, y + -950, 125, 750); + spawn.mapRect(x + 1475, y + -225, 50, 50); + spawn.mapRect(x + 1600, y + -225, 50, 50); + spawn.mapRect(x + 1000, y + -225, 50, 50); + spawn.mapRect(x + 1475, y + -475, 50, 50); + spawn.mapRect(x + 1125, y + -750, 50, 50); + spawn.mapRect(x + 1050, y + -2025, 100, 1125); + spawn.mapRect(x + 1500, y + -2025, 100, 1125); + spawn.mapRect(x + 1475, y + -1050, 50, 50); + spawn.mapRect(x + 1125, y + -1325, 50, 50); + spawn.mapRect(x + 1475, y + -1550, 50, 50); + spawn.mapRect(x + 1125, y + -1875, 50, 50); + spawn.mapRect(x + 1075, y + -2900, 75, 925); + spawn.mapRect(x + 1500, y + -2900, 75, 925); + spawn.mapRect(x + 1475, y + -2150, 50, 50); + spawn.mapRect(x + 1125, y + -2475, 50, 50); + spawn.mapRect(x + 1475, y + -2800, 50, 50); + spawn.mapRect(x + 1000, y + -975, 50, 50); + spawn.mapRect(x + 1025, y + -2050, 50, 50); + spawn.mapRect(x + 1050, y + -2925, 50, 50); + spawn.mapRect(x + 1550, y + -2925, 50, 50); + spawn.mapRect(x + 1600, y + -975, 50, 50); + spawn.mapRect(x + 1575, y + -2050, 50, 50); + for (let i = 0; i < 5; i++) { + if (Math.random() > 0.5) { ace.slasher2(index, y - (i * 500) - 500) } else { ace.slasher3(index, y - (i * 500) - 500) }; + } + } + // ace.slash(0, -5000); + function spawnChain(x, y, x1, y1, length = 39) { + const angle = Math.atan2(y1 - y, x1 - x); + chain(x, y, angle, true, length); + } + function chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) { + const gap = 2 * radius + const unit = { + x: Math.cos(angle), + y: Math.sin(angle) + } + for (let i = 0; i < len; i++) { + bullet[bullet.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, { + inertia: Infinity, + isNotHoldable: true + }); + const who = bullet[bullet.length - 1]; + who.do = () => { }; + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.bullet | cat.bullet | cat.bulletBullet + Composite.add(engine.world, who); //add to world + who.classType = "bullet" + } + for (let i = 1; i < len; i++) { + consBB[consBB.length] = Constraint.create({ + bodyA: bullet[bullet.length - i], + bodyB: bullet[bullet.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + cons[cons.length] = Constraint.create({ + pointA: { + x: x, + y: y + }, + bodyB: bullet[bullet.length - len], + stiffness: 1, + damping: damping + }); + Composite.add(engine.world, cons[cons.length - 1]); + if (isAttached) { + cons[cons.length] = Constraint.create({ + pointA: { + x: x + gap * unit.x * (len - 1), + y: y + gap * unit.y * (len - 1) + }, + bodyB: bullet[bullet.length - 1], + stiffness: 1, + damping: damping + }); + Composite.add(engine.world, cons[cons.length - 1]); + } + } + spawnChain(-2250, -3100, -750, -2900); + spawnChain(-4250, -3300, -2750, -3100); + spawnChain(-6250, -3500, -4750, -3300); + spawnChain(-8250, -3700, -6750, -3500); + spawnChain(750, -2900, 2250, -3100); + spawnChain(2750, -3100, 4250, -3300); + spawnChain(4750, -3300, 6250, -3500); + spawnChain(6750, -3500, 8250, -3700); + // spawnChain(-3000, -30000, -9500, -20400, 291); + // spawnChain(3000, -30000, 9500, -20400, 291); + spawnTower(500); + spawnTower(2500, -200); + spawn.mapRect(2000, -200, 7000, 300); + spawnTower(4500, -400); + spawn.mapRect(4000, -400, 5000, 300); + spawnTower(6500, -600); + spawn.mapRect(6000, -600, 5000, 300); + spawnTower(8500, -800); + spawn.mapRect(8000, -800, 3000, 300); + spawnTower(-500); + spawnTower(-2500, -200); + spawn.mapRect(-10000, -200, 8000, 300); + spawnTower(-4500, -400); + spawn.mapRect(-10000, -400, 6000, 300); + spawnTower(-6500, -600); + spawn.mapRect(-10000, -600, 4000, 300); + spawnTower(-8500, -800); + spawn.mapRect(-10000, -800, 2000, 300); + spawn.mapVertex(10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000"); + spawn.mapVertex(-10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000"); + spawn.mapRect(-11000, -675, 2000, 2675); + spawn.mapRect(9000, -675, 2000, 2675); + spawn.mapVertex(0, -30000, "0 0 3000 -10000 6000 0 3000 10000"); + spawn.mapRect(-8750, -10000, 8000, 100); + spawn.mapRect(750, -10000, 8000, 100); + spawn.mapVertex(0, -10020, "-1000 0 -5000 300 5000 300 1000 0"); + spawn.mapRect(-800, -10250, 100, 350); + spawn.mapRect(700, -10250, 100, 350); + const a = 200; + const maxTheta = 10 * Math.PI; + const spiralX = (theta) => a * theta * Math.cos(theta); + const spiralY = (theta) => a * theta * Math.sin(theta); + for (let i = 1; i <= maxTheta; i += 0.2) { + const x = spiralX(i); + const y = spiralY(i) + (-15000 + 1800 / 2); + spawn.mapRect(x, y, 100, 100); + } + level.exit.y = map[272].position.y; + level.exit.x = map[272].position.x; + }, + // ******************************************************************************************************** + // ******************************************************************************************************** + // ***************************************** training levels ********************************************** + // ******************************************************************************************************** + // ******************************************************************************************************** + walk() { //learn to walk + if (localSettings.isHideHUD) localSettings.isHideHUD = false + m.addHealth(Infinity) + document.getElementById("health").style.display = "none" //hide your health bar + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + simulation.lastLogTime = 0; //clear previous messages + let instruction = 0 + level.trainingText(`move with ${input.key.left.replace('Key', '').replace('Digit', '')} and ${input.key.right.replace('Key', '').replace('Digit', '')}`) + + level.custom = () => { + if (instruction === 0 && input.right) { + instruction++ + level.trainingText(`move with ${input.key.left.replace('Key', '').replace('Digit', '')} and ${input.key.right.replace('Key', '').replace('Digit', '')} +
exit through the blue door`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 0, 3500, 1800); //floor + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + spawn.mapRect(700, -8, 50, 25); + spawn.mapRect(725, -16, 75, 25); + spawn.mapRect(1375, -16, 50, 50); + spawn.mapRect(1400, -8, 50, 25); + spawn.mapRect(750, -24, 650, 100); + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + crouch() { //learn to crouch + if (localSettings.isTrainingNotAttempted) { //after making it to the second training level + localSettings.isTrainingNotAttempted = false // this makes the training button less obvious at the start screen + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + + m.addHealth(Infinity) + level.setPosToSpawn(75, -100); //normal spawn + spawn.mapRect(25, -60, 100, 20); //small platform for player + spawn.mapRect(0, -50, 150, 25); //stairs + spawn.mapRect(-25, -40, 200, 25); + spawn.mapRect(-50, -30, 250, 25); + spawn.mapRect(-75, -20, 300, 25); + spawn.mapRect(-100, -10, 350, 25); + spawn.mapRect(-150, -50, 175, 75); + + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + let instruction = 0 + level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) + level.custom = () => { + if (instruction === 0 && input.down) { + instruction++ + level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1625, -350, 375, 350) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1625, -350, 375, 350) + //dark + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(500, -100, 1125, 175); + }; + + // spawn.mapRect(1025, -675, 300, 623); //crouch wall + // spawn.mapRect(625, -650, 1025, 550); + spawn.mapRect(500, -650, 1125, 550); + spawn.mapRect(-200, -650, 875, 300); + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -1200, 500, 850); //exit roof + }, + jump() { //learn to jump + m.addHealth(Infinity) + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + let instruction = 0 + level.trainingText(`hold down ${input.key.up.replace('Key', '').replace('Digit', '')} longer to jump higher`) + + level.custom = () => { + if (instruction === 0 && m.pos.x > 300) { + instruction++ + level.trainingText(`hold down ${input.key.up.replace('Key', '').replace('Digit', '')} longer to jump higher`) + } + m.health = 1 //can't die + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //dark + ctx.fillStyle = "rgba(0,0,0,0.2)" + ctx.fillRect(1000, 0, 450, 1800) + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(275, -350, 200, 375); + spawn.mapRect(-250, 0, 1250, 1800); //floor + spawn.mapRect(1450, 0, 1075, 1800); //floor + spawn.mapRect(-250, -2800, 1250, 2200); //roof + spawn.mapRect(1450, -2800, 1075, 2200); //roof + spawn.mapVertex(375, 0, "150 0 -150 0 -100 -50 100 -50"); //base + + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + + //roof steps + spawn.mapRect(1000, -650, 25, 25); + spawn.mapRect(1000, -675, 50, 25); + spawn.mapRect(1000, -700, 75, 25); + spawn.mapRect(1000, -725, 100, 25); + spawn.mapRect(1425, -650, 25, 25); + spawn.mapRect(1400, -675, 50, 25); + spawn.mapRect(1375, -700, 75, 25); + spawn.mapRect(1350, -725, 100, 25); + spawn.mapRect(1325, -750, 150, 25); + spawn.mapRect(1300, -775, 150, 25); + spawn.mapRect(1000, -750, 125, 25); + spawn.mapRect(1275, -2800, 200, 2025); + spawn.mapRect(975, -2800, 200, 2025); + spawn.mapRect(1000, -775, 150, 25); + }, + hold() { //put block on button to open door + m.addHealth(Infinity) + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + spawn.bodyRect(1025, -75, 50, 50); //block to go on button + const buttonDoor = level.button(500, 0) + const door = level.door(1612.5, -175, 25, 190, 185, 3) + + let instruction = 0 + level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse`) + + level.custom = () => { + if (instruction === 0 && input.field) { + instruction++ + level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up`) + } else if (instruction === 1 && m.isHolding) { + instruction++ + level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up

drop the block on the red button to open the door`) + } else if (instruction === 2 && !buttonDoor.isUp && Vector.magnitudeSquared(Vector.sub(body[0].position, buttonDoor.min)) < 10000) { + instruction++ + level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up
drop the block on the red button to open the door
`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + throw() { //throw a block on button to open door + m.addHealth(Infinity) + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + spawn.bodyRect(1025, -75, 50, 50); //block to go on button + const buttonDoor = level.button(1635, -400) + const door = level.door(1612.5, -175, 25, 190, 185, 3) + + // activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse + let instruction = 0 + level.trainingText(`pick up the block with your field`) + + level.custom = () => { + if (instruction === 0 && m.isHolding) { + instruction++ + level.trainingText(`pick up the block with your field +
hold your field down to charge up then release to throw a block`) + } else if (instruction === 1 && m.throwCharge > 2) { + instruction++ + level.trainingText(`pick up the block with your field +
hold your field down to charge up then release to throw a block
+
throw the block onto the button`) + // the block at the button + } else if (instruction === 2 && !buttonDoor.isUp && Vector.magnitudeSquared(Vector.sub(body[0].position, buttonDoor.min)) < 10000) { + instruction++ + level.trainingText(`pick up the block with your field +
hold your field down to charge up then release to throw a block +
throw the block onto the button
`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + // spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1790, -600, 250, 225); //button left wall + spawn.mapRect(1625, -400, 400, 50); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + throwAt() { //throw a block at mob to open door + m.addHealth(Infinity) + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + const door = level.door(1612.5, -175, 25, 190, 185, 3) + + let instruction = 0 + level.trainingText(`throw the block at the mobs to open the door`) + + level.custom = () => { + if (instruction === 0 && !mob.length) { + instruction++ + level.trainingText(`throw the block at the mobs to open the door`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (mob.length > 0) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + // spawn.mapRect(1600, -1200, 500, 850); //exit roof + // spawn.mapRect(1790, -600, 250, 225); //button left wall + // spawn.mapRect(1625, -400, 400, 50); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + spawn.mapRect(1600, -600, 425, 250); + + spawn.bodyRect(1025, -75, 50, 50); //block to go on button + spawn.starter(425, -350, 35) + spawn.starter(800, -350, 44) + }, + fire() { //throw a block at mob to open door + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = 15; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + const door = level.door(1612.5, -125, 25, 190, 185, 3) + const buttonDoor = level.button(400, 0) + + let instruction = 0 + level.trainingText(`use your field to pick up the gun power up`) + + level.custom = () => { + if (instruction === 0 && simulation.isChoosing) { + instruction++ + level.trainingText(`use your field to pick up the gun power up +
choose a gun`) + } else if (instruction === 1 && !simulation.isChoosing) { + instruction++ + level.trainingText(`use your field to pick up the gun power up +
choose a gun
+
use the left mouse button to shoot the mobs`) + } else if (instruction === 2 && mob.length === 0) { + instruction++ + level.trainingText(`use your field to pick up the gun power up +
choose a gun +
use the left mouse button to shoot the mobs
+
drop a block on the red button to open the door`) + } else if (instruction === 3 && !door.isClosing) { + instruction++ + level.trainingText(`use your field to pick up the gun power up +
choose a gun +
use the left mouse button to shoot the mobs +
put a block on the red button to open the door
`) + } + if (!powerUp.length) { + //spawn ammo if you run out + if (b.inventory.length && b.guns[b.activeGun].ammo === 0) powerUps.directSpawn(1300, -2000, "ammo", false); + //spawn a gun power up if don't have one or a gun + if (!b.inventory.length && !simulation.isChoosing) powerUps.directSpawn(1300, -2000, "gun", false); + + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -350, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -350, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(1250, -2800, 100, 2200) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo + spawn.mapRect(1350, -2800, 675, 2200); + + //ceiling steps + spawn.mapRect(725, -588, 50, 25); + spawn.mapRect(725, -600, 75, 25); + spawn.mapRect(750, -612, 75, 25); + spawn.mapRect(-275, -650, 1025, 87); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + + spawn.mapRect(1600, -600, 425, 300); + spawn.mapRect(1600, -400, 50, 275); + + powerUps.directSpawn(1300, -1500, "gun", false); + spawn.starter(900, -300, 35) + spawn.starter(1400, -400, 44) + }, + deflect() { //learn to jump + m.addHealth(Infinity) + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + let instruction = 0 + // activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse + level.trainingText(`use your field to deflect the mobs`) + + level.custom = () => { + if (instruction === 0 && m.pos.x > 1350) { + instruction++ + level.trainingText(`use your field to deflect the mobs`) + } + //teleport to start if hit + if (m.immuneCycle > m.cycle) { + m.energy = m.maxEnergy + Matter.Body.setPosition(player, { + x: 60, + y: -50 + }) + } + //spawn bullets + if (!(simulation.cycle % 5)) { + spawn.sniperBullet(660 + 580 * Math.random(), -2000, 10, 4); + const who = mob[mob.length - 1] + Matter.Body.setVelocity(who, { + x: 0, + y: 8 + }); + who.timeLeft = 300 + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //dark + ctx.fillStyle = "rgba(0,0,0,0.05)" + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //center falling bullets + ctx.fillStyle = "rgba(255,0,255,0.013)" //pink? + ctx.fillRect(650, -2800, 600, 2800) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + + spawn.mapRect(-250, 0, 3000, 1800); //floor + spawn.mapRect(-250, -2800, 900, 2200); //roof + spawn.mapRect(1250, -2800, 1275, 2200); //roof + spawn.mapVertex(950, 0, "400 0 -400 0 -300 -50 300 -50"); //base + + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + + //spawn bullets on load to avoid rush + for (let i = 0; i < 32; i++) { + spawn.sniperBullet(660 + 580 * Math.random(), -2000 + 40 * i, 10, 4); + const who = mob[mob.length - 1] + Matter.Body.setVelocity(who, { + x: 0, + y: 8 + }); + who.timeLeft = 300 + } + }, + heal() { //learn to heal + m.addHealth(Infinity) + m.health = 0; + m.addHealth(0.25) + document.getElementById("health").style.display = "inline" //show your health bar + document.getElementById("health-bg").style.display = "inline" + if (!localSettings.isHideHUD) { + document.getElementById("defense-bar").style.display = "inline" + document.getElementById("damage-bar").style.display = "inline" + } + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + let instruction = 0 + level.trainingText(`your health is displayed in the top left corner +
use your field to pick up
until your health is full`) + + level.custom = () => { + if (instruction === 0 && m.health === 1) { + instruction++ + level.trainingText(`use your field to pick up
until your health is full
`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (m.health !== 1) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 0, 3500, 1800); //floor + + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + spawn.mapRect(700, -8, 50, 25); + spawn.mapRect(725, -16, 75, 25); + spawn.mapRect(1375, -16, 50, 50); + spawn.mapRect(1400, -8, 50, 25); + spawn.mapRect(750, -24, 650, 100); + powerUps.directSpawn(875, -40, "heal", false, null, 15); + powerUps.directSpawn(1075, -50, "heal", false, null, 25); + powerUps.directSpawn(1275, -65, "heal", false, null, 35); + + const door = level.door(1612.5, -175, 25, 190, 185, 3) + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + nailGun() { + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("nail gun") + b.guns[b.activeGun].ammo = 0 + simulation.updateGunHUD(); + + const door = level.door(1612.5, -175, 25, 190, 185, 3) + let instruction = 0 + level.trainingText(`use your field to pick up
for your nail gun`) + + level.custom = () => { + if (instruction === 0 && b.inventory.length && b.guns[b.activeGun].ammo > 0) { + instruction++ + level.trainingText(`use your field to pick up
for your nail gun
+
use the left mouse button to shoot the mobs`) + } else if (instruction === 1 && mob.length === 0) { + instruction++ + level.trainingText(`use your field to pick up
for your nail gun +
use the left mouse button to shoot the mobs
`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (mob.length > 0) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(1250, -2800, 100, 2200) + }; + + if (m.health < 1) { + powerUps.directSpawn(1298, -3500, "heal", false, 23); + powerUps.directSpawn(1305, -3000, "heal", false, 35); + } + for (let i = 0; i < 2; i++) { + spawn.spinner(1300 + i, -3000 - 200 * i, 25 + 5 * i) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: 0, + y: 62 + }); + } + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo + spawn.mapRect(1350, -2800, 675, 2200); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + // spawn.mapRect(1600, -1200, 500, 850); //exit roof + // spawn.mapRect(1790, -600, 250, 225); //button left wall + // spawn.mapRect(1625, -400, 400, 50); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + spawn.mapRect(1600, -600, 425, 250); + }, + shotGun() { + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("shotgun") + // b.guns[b.activeGun].ammo = 0 + // simulation.updateGunHUD(); + const door = level.door(1612.5, -175, 25, 190, 185, 3) + let instruction = 0 + level.trainingText(`use your shotgun to clear the room of mobs`) + + level.custom = () => { + if (instruction === 0 && mob.length === 0) { + instruction++ + level.trainingText(`use your shotgun to clear the room of mobs`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (mob.length > 0) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(1250, -2800, 100, 2200) + }; + + if (m.health < 1) { + powerUps.directSpawn(1298, -3500, "heal", false, 23); + powerUps.directSpawn(1305, -3000, "heal", false, 35); + } + for (let i = 0; i < 3; i++) { + spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i) + // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); + } + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo + spawn.mapRect(1350, -2800, 675, 2200); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + spawn.mapRect(1600, -600, 425, 250); + }, + superBall() { + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("super balls") + // b.guns[b.activeGun].ammo = 0 + // simulation.updateGunHUD(); + const door = level.door(1612.5, -175, 25, 190, 185, 3) + let instruction = 0 + level.trainingText(`use super balls to clear the room of mobs`) + + level.custom = () => { + if (instruction === 0 && mob.length === 0) { + instruction++ + level.trainingText(`use super balls to clear the room of mobs`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (mob.length > 0) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.2)" + // ctx.fillRect(1225, -2800, 125, 2450) + ctx.fillRect(-150, -2800, 1500, 2450); + }; + + if (m.health < 1) { + powerUps.directSpawn(1298, -3500, "heal", false, 23); + powerUps.directSpawn(1305, -3000, "heal", false, 35); + } + for (let i = 0; i < 6; i++) { + spawn.spawner(i * 230, -800) + // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); + } + spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs + spawn.mapRect(-225, -2800, 1450, 2000); + spawn.mapRect(1350, -2800, 675, 2450); + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + matterWave() { //fire wave through the map to kill mosb + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("wave") + // b.guns[b.activeGun].ammo = 0 + // simulation.updateGunHUD(); + const door = level.door(1612.5, -175, 25, 190, 185, 3) + let instruction = 0 + level.trainingText(`use wave to clear the room of mobs`) + + level.custom = () => { + if (instruction === 0 && mob.length === 0) { + instruction++ + level.trainingText(`use wave to clear the room of mobs`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + if (mob.length > 0) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.2)" + // ctx.fillRect(1225, -2800, 125, 2450) + ctx.fillRect(-150, -2800, 1500, 2450); + }; + + if (m.health < 1) { + powerUps.directSpawn(1298, -3500, "heal", false, 23); + powerUps.directSpawn(1305, -3000, "heal", false, 35); + } + for (let i = 0; i < 6; i++) { + spawn.springer(i * 200, -800) + // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); + } + spawn.springer(1825, -330, 20); + + spawn.mapRect(1175, -850, 50, 500); //upper room with mobs + spawn.mapRect(-225, -400, 1450, 50); + spawn.mapRect(-225, -2800, 1450, 2000); + spawn.mapRect(1350, -2800, 675, 2450); + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, + missile() { //fire a missile to kill mobs and trigger button + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 30); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("missiles") + // b.guns[b.activeGun].ammo = 0 + // simulation.updateGunHUD(); + const buttonDoor = level.button(2500, 50) + const door = level.door(1612.5, -175, 25, 190, 185, 3) + let instruction = 0 + level.trainingText(`use missiles to drop a block on the button`) + + level.custom = () => { + if (instruction === 0 && mob.length === 0) { + instruction++ + level.trainingText(`use missiles to drop a block on the button`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + buttonDoor.query(); + buttonDoor.draw(); + if (buttonDoor.isUp) { + door.isClosing = true + } else { + door.isClosing = false + } + door.openClose(); + door.draw(); + + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + //tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(1250, -2800, 100, 2200) + ctx.fillRect(1550, 25, 475, 25); + }; + if (m.health < 1) { + powerUps.directSpawn(1298, -3500, "heal", false, 23); + powerUps.directSpawn(1305, -3000, "heal", false, 35); + } + for (let i = 0; i < 10; i++) { + spawn.springer(2100 + i * 100, -250) + // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); + } + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + // spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(3050, -2800, 1550, 4600); + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo + spawn.mapRect(1350, -2800, 675, 2200); + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + // spawn.mapRect(1350, 0, 675, 30); + spawn.mapRect(1550, 0, 475, 35); + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + spawn.mapRect(1600, -600, 425, 250); + + spawn.mapRect(1975, -600, 50, 625); + spawn.mapRect(2025, -2800, 1075, 2450); + }, + stack() { //stack blocks to get to exit + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -685; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + let instruction = 0 + level.trainingText(`use your field to stack the blocks`) + + level.custom = () => { + if (instruction === 0 && m.pos.x > 1635) { + instruction++ + level.trainingText(`use your field to stack the blocks`) + } + + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -1050, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -1050, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(250, -2800, 200, 1800) + }; + + if (m.health < 1) { + powerUps.directSpawn(298, -3500, "heal", false, 23); + powerUps.directSpawn(305, -3000, "heal", false, 35); + } + for (let i = 0; i < 15; i++) { + spawn.bodyRect(280, -2000 - 500 * i, 30 + 80 * Math.random(), 30 + 80 * Math.random()); + } + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 0, 3500, 1800); //floor + spawn.mapRect(1600, -650, 450, 775); + spawn.mapRect(-150, -2800, 400, 1800); //roof with tunnel for ammo + spawn.mapRect(450, -2800, 1675, 1800); + spawn.mapVertex(1300, 0, "400 0 -500 0 -300 -125 400 -125"); //base + }, + mine() { //kill mobs and tack their bodies + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(300, -50); //normal spawn + spawn.mapRect(250, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -685; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("mine") + + let instruction = 0 + level.trainingText(`press the red button to spawn a mob`) + const button = level.button(-100, -200) + button.isUp = true + spawn.mapRect(-150, -200, 240, 425); + + level.custom = () => { + if (instruction === 0 && !button.isUp) { + instruction++ + level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks`) + } else if (instruction === 1 && body.length > 2) { + instruction++ + level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks

use your field to stack the blocks`) + } else if (instruction === 2 && m.pos.x > 1635) { + instruction++ + level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks
use your field to stack the blocks
`) + } + //spawn ammo if you run out + let isAmmo = false + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === 'ammo') isAmmo = true + } + if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { + powerUps.directSpawn(1300, -2000, "ammo", false); + powerUps.directSpawn(1301, -2200, "ammo", false); + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -1050, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + button.query(); + button.draw(); + if (!button.isUp) { + if (button.isReady) { + button.isReady = false + spawn.exploder(335, -1700) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: 0, + y: 20 + }); + ctx.fillStyle = "rgba(255,0,0,0.9)" + ctx.fillRect(550, -2800, 200, 1800) + } + } else { + button.isReady = true + } + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -1050, 400, 400) + //ammo tunnel shadow + ctx.fillStyle = "rgba(0,0,0,0.4)" + ctx.fillRect(550, -2800, 200, 1800) + }; + + if (m.health < 1) { + powerUps.directSpawn(298, -3500, "heal", false, 23); + powerUps.directSpawn(305, -3000, "heal", false, 35); + } + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 0, 3500, 1800); //floor + spawn.mapRect(1600, -650, 450, 775); + spawn.mapRect(-150, -2800, 700, 1800); //roof with tunnel for ammo + spawn.mapRect(750, -2800, 1675, 1800); + spawn.mapVertex(1300, 0, "400 0 -600 0 -300 -125 400 -125"); //base + }, + grenades() { //jump at the top of the elevator's path to go extra high + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(0, -50); //normal spawn + spawn.mapRect(-50, -10, 100, 20); //small platform for player + level.exit.x = 1900; + level.exit.y = -2835; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("grenades") + + const elevator1 = level.elevator(550, -100, 180, 25, -840, 0.003, { + up: 0.05, + down: 0.2 + }) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + elevator1.addConstraint(); + const toggle1 = level.toggle(275, 0) //(x,y,isOn,isLockOn = true/false) + + const elevator2 = level.elevator(1400, -950, 180, 25, -2400, 0.0025) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + elevator2.addConstraint(); + const button2 = level.button(1000, -850) + + let instruction = 0 + level.trainingText(`flip the switch to turn on the elevator`) + level.custom = () => { + if (instruction === 0 && elevator1.isOn) { + instruction++ + level.trainingText(`flip the switch to turn on the elevator +
put a block on the button to active the elevator`) + } else if (instruction === 1 && elevator2.isOn) { + instruction++ + level.trainingText(`flip the switch to turn on the elevator
put a block on the button to active the elevator
+
hold jump before the elevator's apex to reach the exit`) + } else if (instruction === 2 && m.pos.x > 1635) { + instruction++ + level.trainingText(`flip the switch to turn on the elevator
put a block on the button to active the elevator
hold jump before the elevator's apex to reach the exit
`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1725, -3100, 375, 300); + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + toggle1.query(); + if (!toggle1.isOn) { + if (elevator1.isOn) { + elevator1.isOn = false + elevator1.frictionAir = 0.2 + elevator1.addConstraint(); + } + } else if (!elevator1.isOn) { + elevator1.isOn = true + elevator1.isUp = false + elevator1.removeConstraint(); + elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + if (elevator1.isOn) { + elevator1.move(); + ctx.fillStyle = "#444" + } else { + ctx.fillStyle = "#aaa" + } + ctx.fillRect(640, -825, 1, 745) + + button2.query(); + button2.draw(); + if (button2.isUp) { + if (elevator2.isOn) { + elevator2.isOn = false + elevator2.frictionAir = 0.2 + elevator2.addConstraint(); + } + } else if (!elevator2.isOn) { + elevator2.isOn = true + elevator2.isUp = false + elevator2.removeConstraint(); + elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 + } + if (elevator2.isOn) { + elevator2.move(); + ctx.fillStyle = "#444" + } else { + ctx.fillStyle = "#aaa" + } + ctx.fillRect(1490, -2300, 1, 1375) + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1725, -3100, 375, 300); + //shadows + ctx.fillStyle = "rgba(0,0,0,0.05)" + ctx.fillRect(-150, -250, 300, 250); + let grd = ctx.createLinearGradient(0, -150, 0, -2300); + grd.addColorStop(0, "rgba(0,0,0,0.35)"); + grd.addColorStop(1, "rgba(0,0,0,0)"); + ctx.fillStyle = grd //"rgba(0,0,100,0.01)" + ctx.fillRect(-200, -2300, 1825, 2300); + }; + + if (m.health < 1) { + powerUps.directSpawn(298, -3500, "heal", false, 23); + powerUps.directSpawn(305, -3000, "heal", false, 35); + } + spawn.mapRect(-2750, -4800, 2600, 6600); //left wall + spawn.mapRect(1600, -2800, 3000, 4600); //right wall + spawn.mapRect(-150, -4800, 300, 4550); + spawn.mapRect(2125, -4775, 2475, 2050); + spawn.mapRect(-250, 0, 3500, 1800); //floor + spawn.mapRect(750, -850, 950, 950); + spawn.mapRect(125, -275, 25, 100); + spawn.mapRect(2100, -3150, 50, 350); + spawn.mapRect(1725, -3150, 50, 175); + spawn.mapRect(1725, -3150, 425, 50); + + spawn.nodeGroup(1200, -1500, "grenadier", 7); + }, + harpoon() { //jump at the top of the elevator's path to go extra high + level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level + level.setPosToSpawn(0, -50); //normal spawn + spawn.mapRect(-50, -10, 100, 20); //small platform for player + level.exit.x = 1900; + level.exit.y = -2835; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + b.removeAllGuns(); + b.giveGuns("harpoon") + + let instruction = 0 + level.trainingText(`climb up to the exit`) + level.custom = () => { + if (instruction === 0 && m.pos.x > 1635) { + instruction++ + level.trainingText(`climb up to the exit`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1725, -3100, 375, 300); + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1725, -3100, 375, 300); + //shadows + ctx.fillStyle = "rgba(0,90,100,0.05)" + ctx.fillRect(-150, -250, 300, 250); + let grd = ctx.createLinearGradient(0, -150, 0, -2300); + grd.addColorStop(0, "rgba(0,90,100,0.35)"); + grd.addColorStop(1, "rgba(0,90,100,0)"); + ctx.fillStyle = grd //"rgba(0,0,100,0.01)" + ctx.fillRect(-200, -2300, 1825, 2300); + vanish1.query(); + vanish2.query(); + vanish3.query(); + vanish4.query(); + vanish5.query(); + vanish6.query(); + vanish7.query(); + vanish8.query(); + vanish9.query(); + vanish10.query(); + vanish11.query(); + vanish12.query(); + }; + const vanish1 = level.vanish(175, -325, 175, 25); //x, y, width, height, hide = { x: 0, y: 100 } //hide should just be somewhere behind the map so the player can't see it + const vanish2 = level.vanish(525, -625, 175, 25); + const vanish3 = level.vanish(1125, -1125, 175, 25); + const vanish4 = level.vanish(1500, -1450, 100, 25); + const vanish5 = level.vanish(1125, -1675, 175, 25); + const vanish6 = level.vanish(750, -1950, 175, 25); + const vanish7 = level.vanish(550, -1950, 175, 25); + const vanish8 = level.vanish(350, -1950, 175, 25); + const vanish9 = level.vanish(150, -1950, 175, 25); + const vanish10 = level.vanish(325, -2300, 200, 25); + const vanish11 = level.vanish(725, -2550, 100, 25); + const vanish12 = level.vanish(1125, -2700, 150, 25); + + if (m.health < 1) { + powerUps.directSpawn(298, -3500, "heal", false, 23); + powerUps.directSpawn(305, -3000, "heal", false, 35); + } + spawn.mapRect(-2750, -4800, 2600, 6600); //left wall + spawn.mapRect(1600, -2800, 3000, 4600); //right wall + spawn.mapRect(-150, -4800, 300, 4550); + spawn.mapRect(2125, -4775, 2475, 2050); + spawn.mapRect(-250, 0, 3500, 1800); //floor + spawn.mapRect(750, -850, 950, 950); + spawn.mapRect(125, -275, 25, 100); + spawn.mapRect(2100, -3150, 50, 350); + spawn.mapRect(1725, -3150, 50, 175); + spawn.mapRect(1725, -3150, 425, 50); + + spawn.grower(250, -375); + spawn.grower(1000, -900) + spawn.grower(1475, -925); + spawn.grower(275, -2000); + spawn.grower(650, -2000); + spawn.grower(1475, -975); + spawn.grower(1575, -1525); + spawn.grower(1700, -2850); + }, + diamagnetism() { + if (localSettings.isHideHUD) localSettings.isHideHUD = false + m.addHealth(Infinity) + document.getElementById("health").style.display = "none" //hide your health bar + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + const futureGuns = ["harpoon", "shotgun", "nail gun", "super balls", "wave", "foam", "laser"]; + const futureGun = Math.floor(Math.random() * futureGuns.length) + b.giveGuns(futureGuns[futureGun], Infinity) + m.setField(2) + m.fieldRegen = 0; + level.trainingText(`diamagnetism by Richard0820
Don't get hit.
Find the portal to the exit.`) + const dodge = []; + const button = level.button(350 - 63, -300) + const door = level.door(750, -275, 50, 125, 125) + const door2 = level.door(750, -525, 50, 125, 125); + const forceOne = forceField(4425, -3925, 525, 3975); + const forceTwo = forceField(1550, -9950, 275, 3300); + const forceThree = forceField(4200, -8725, 750, 4450); + const respawnX = []; + respawnX.push(setRespawn(-50, -625, 825, 375)); + respawnX.push(setRespawn(3225, -3675, 1200, 1000)); + respawnX.push(setRespawn(3575, -5675, 625, 800)); + respawnX.push(setRespawn(775, -4250, 400, 375)); + respawnX.push(setRespawn(2825, -2975, 250, 300)); + respawnX.push(setRespawn(3675, -1125, 325, 250)); + let respawnPoints = { + x: 125, + y: -9575, + } + door2.isClosing = true; + button.isUp = true + level.setPosToSpawn(125, -9575); //normal spawn + level.exit.x = -1825; + level.exit.y = 50; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d8dadf"; + alternate(-50, -9050, 425, 100); + const image = new Image() + image.src = "https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot-removed.png"; + level.chain(-675, 400, -0.4366271598, true, 20) + level.chain(-1600, 125, 0.5144513131, true, 19) + const portal = portall({ + x: 3825, + y: -1000, + }, + 3 * Math.PI, { + x: 550, + y: -100, + }, + 3 * Math.PI + ); + level.custom = () => { + portal[2].query(); + portal[3].query(); + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + forceOne.query() + forceTwo.query() + forceThree.query() + if (input.field && player.position.x < 775 && player.position.x > -50) { + if (m.energy > 0.02) { + m.energy -= 0.01 + } else { + input.field = false + } + } + level.exit.drawAndCheck(); + level.enter.draw(); + button.query() + button.draw() + if (!button.isUp) { + door.isClosing = true; + door2.isClosing = false; + } else if (button.isUp) { + door.isClosing = false; + door2.isClosing = true; + } + door.draw() + door.openClose() + door2.draw() + door2.openClose() + for (let i = 0; i < dodge.length; i++) dodge[i].query(); + for (let i = 0; i < respawnX.length; i++) respawnX[i].query(); + ctx.fillStyle = "gray"; + ctx.fillRect(1175, -6650, 2400, 2375); + ctx.drawImage(image, 1175 + 1200 - 250, -6650 + (2375 / 2) - 250, 500, 500) + if (m.immuneCycle > m.cycle) { + m.energy = m.maxEnergy + Matter.Body.setPosition(player, { + x: respawnPoints.x, + y: respawnPoints.y + }) + } + }; + level.customTopLayer = () => { }; + spawn.mapRect(-100, 0, 5100, 100); + spawn.mapRect(-100, -10000, 5100, 100); + spawn.mapRect(4900, -10000, 100, 10100); + spawn.mapRect(-100, -10000, 100, 9800); + spawn.mapRect(-100, -9525, 450, 100); + spawn.mapRect(725, -300, 100, 400); + spawn.mapRect(725, -10000, 100, 9500); + spawn.mapRect(-100, -300, 925, 100); + spawn.mapRect(800, -675, 3675, 100); + spawn.mapRect(4375, -1425, 100, 850); + spawn.mapRect(1350, -1425, 3125, 100); + spawn.mapRect(1350, -1425, 100, 600); + spawn.mapRect(1350, -925, 2700, 100); + spawn.mapRect(1575, -1175, 2475, 100); + spawn.mapRect(3950, -1175, 100, 350); + spawn.mapRect(4375, -2725, 100, 1400); + spawn.mapRect(775, -2725, 2325, 100); + spawn.mapRect(3200, -2725, 1275, 100); + spawn.mapRect(3200, -3975, 100, 1350); + spawn.mapRect(4375, -3950, 100, 1125); + spawn.mapRect(4375, -3975, 625, 100); + spawn.mapRect(3200, -4325, 100, 450); + spawn.mapRect(3200, -4325, 1600, 100); + spawn.mapRect(4450, -2725, 50, 25); + spawn.mapRect(1125, -3025, 2175, 100); + spawn.mapRect(725, -3925, 2175, 100); + spawn.mapRect(3525, -6700, 100, 2475); + spawn.mapRect(4150, -6700, 100, 2475); + spawn.mapRect(1125, -6700, 2500, 105); + spawn.mapRect(1125, -6700, 100, 2625); + spawn.mapRect(1500, -8775, 100, 2175); + spawn.mapRect(4150, -8775, 100, 1900); + spawn.mapRect(1775, -8775, 2475, 100); + spawn.mapRect(4225, -6700, 50, 25); + spawn.mapRect(4150, -8775, 850, 100); + spawn.mapRect(3600, -2825, 125, 125); + spawn.mapRect(3275, -3050, 125, 125); + spawn.mapRect(3600, -3275, 125, 125); + spawn.mapRect(3300, -3525, 125, 125); + spawn.mapRect(3575, -3725, 900, 125); + spawn.mapRect(4075, -3775, 75, 75); + spawn.mapRect(4225, -3875, 75, 175); + spawn.mapRect(3600, -6625, 100, 100); + spawn.mapRect(4075, -6475, 100, 100); + spawn.mapRect(3600, -6300, 100, 100); + spawn.mapRect(4075, -6175, 100, 100); + spawn.mapRect(3600, -6000, 100, 100); + spawn.mapRect(4075, -5875, 100, 100); + spawn.mapRect(3600, -5700, 100, 100); + spawn.mapRect(4075, -5550, 100, 100); + spawn.mapRect(3600, -5400, 100, 1125); + spawn.mapRect(3675, -5300, 100, 1025); + spawn.mapRect(3750, -5225, 100, 950); + spawn.mapRect(3825, -5150, 100, 875); + spawn.mapRect(3900, -5075, 100, 800); + spawn.mapRect(3975, -5000, 100, 725); + spawn.mapRect(4050, -4925, 125, 650); + spawn.mapRect(4150, -6925, 75, 125); + spawn.mapRect(1775, -8775, 100, 1900); + spawn.mapRect(1775, -9950, 100, 975); + spawn.mapRect(1500, -9950, 100, 975); + spawn.mapRect(1275, -8775, 325, 100); + spawn.mapRect(1200, -7775, 25, 1175); + spawn.mapRect(1250, -7950, 25, 1350); + spawn.mapRect(1300, -8175, 25, 1575); + spawn.mapRect(1350, -8500, 25, 1900); + spawn.mapRect(1400, -8625, 25, 2025); + spawn.mapRect(1450, -8700, 25, 2100); + spawn.mapRect(1150, -7625, 25, 1025); + spawn.mapRect(1125, -4325, 2175, 100); + spawn.mapRect(4250, -925, 150, 100); + spawn.mapRect(575, -225, 175, 50); + spawn.mapRect(575, -50, 175, 75); + spawn.mapRect(-25, 50, 125, 100); + spawn.mapRect(75, 75, 50, 50); + spawn.sniper(3600, -7300); + spawn.sniper(3325, -7475); + spawn.sniper(2825, -7500); + spawn.sniper(2250, -7450); + spawn.sniper(4125, -5150); + spawn.sniper(4100, -5675); + spawn.sniper(4100, -5950); + spawn.sniper(4125, -6325); + spawn.sniper(3875, -6975); + spawn.stabber(4075, -4075); + spawn.stabber(3775, -3950); + spawn.stabber(3500, -3850); + spawn.stabber(4000, -3500); + spawn.stabber(3850, -3125); + spawn.stabber(3450, -3125); + spawn.stabber(4225, -2900); + spawn.hopper(4125, -250); + spawn.hopper(3525, -250); + spawn.hopper(2925, -325); + spawn.hopper(2175, -150); + spawn.hopper(1175, -400); + spawn.mantisBoss(3425, -9350); + spawn.pulsarBoss(1725, -6050, 1); + spawn.pulsarBoss(1800, -4850, 1); + spawn.pulsarBoss(3000, -4825, 1); + spawn.pulsarBoss(2975, -6175, 1); + spawn.spinner(2025, -4050); + spawn.spinner(2125, -2825); + spawn.pulsar(2450, -3775); + spawn.pulsar(2200, -3750); + spawn.pulsar(1900, -3775); + spawn.pulsar(1600, -3725); + spawn.pulsar(1300, -3750); + spawn.pulsar(925, -3725); + spawn.focuser(3925, -2375); + spawn.focuser(1150, -2450); + spawn.focuser(2450, -1675); + spawn.mapVertex(-850, 500, "0 0 500 0 250 500"); + spawn.mapVertex(-1775, 250, "0 0 500 0 250 500"); + spawn.bodyRect(25, -375, 50, 50); + function alternate(x, y, width, height, spacingX = 25, spacingY = 1500, number = 6) { + for (let i = 0; i < number; i++) { + if (i % 2 === 0) { + dodge.push(back(x, y + i * (height + spacingY), width, height, level.enter.x, level.enter.y)) + } else { + dodge.push(back(x + width - spacingX, y + i * (height + spacingY), width, height, level.enter.x, level.enter.y)) + } + } + } + function back(x, y, width, height, x1, y1) { + return { + move: { x: x1, y: y1 }, + min: { x: x, y: y }, + max: { x: x + width, y: y + height }, + width: width, + height: height, + maxHeight: height, + isOn: true, + query() { + if (this.isOn) { + ctx.lineWidth = 5; + ctx.strokeStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})` + ctx.strokeRect(this.min.x, this.min.y, this.width, this.height) + if (this.height > 0 && Matter.Query.region([player], this).length) { + Matter.Body.setVelocity(player, { x: 0, y: 0 }) + Matter.Body.setPosition(player, { x: this.move.x, y: this.move.y }) + m.energy = m.maxEnergy; + } + } + }, + } + } + function forceField(x, y, width, height) { + return { + min: { x: x, y: y }, + max: { x: x + width, y: y + height }, + width: width, + height: height, + maxHeight: height, + isOn: true, + query() { + if (this.isOn) { + ctx.fillStyle = `rgba(0, 250, 250, 0.55)` + ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + if (this.height > 0 && Matter.Query.region([player], this).length && input.field) { + player.force.y -= 0.015; + m.energy = m.maxEnergy; + } + ctx.fillStyle = `rgba(0, 250, 250)` + ctx.fillRect(this.min.x + this.width * Math.random(), this.min.y, 5, this.height) + } + }, + } + } + function setRespawn(x, y, width, height) { + return { + min: { x: x, y: y }, + max: { x: x + width, y: y + height }, + width: width, + height: height, + maxHeight: height, + isOn: true, + query() { + if (this.isOn) { + ctx.fillStyle = `rgba(0, 250, 0, 0.11)` + ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + if (this.height > 0 && Matter.Query.region([player], this).length) { + m.energy = m.maxEnergy; + respawnPoints.x = this.min.x + (this.width / 2); + respawnPoints.y = this.min.y + (this.height / 2); + } + } + }, + } + } + function portall(centerA, angleA, centerB, angleB) { + const width = 50 + const height = 150 + const mapWidth = 200 + const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA) + const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB) + draw = function () { + ctx.beginPath(); //portal + let v = this.vertices; + ctx.moveTo(v[0].x, v[0].y); + for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + ctx.fillStyle = this.color + ctx.fill(); + } + query = function (isRemoveBlocks = false) { + if (Matter.Query.collides(this, [player]).length === 0) { //not touching player + if (player.isInPortal === this) player.isInPortal = null + } else if (player.isInPortal !== this) { //touching player + if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal + m.buttonCD_jump = 0 //disable short jumps when letting go of jump key + player.isInPortal = this.portalPair + if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down + // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + Matter.Body.setPosition(player, this.portalPair.portal.position); + } else { + // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + Matter.Body.setPosition(player, this.portalPair.position); + } + let mag + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11 + } else { + mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity))) + } + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(player, v); + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position); + Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); + } + } + if (tech.isHealAttract) { //send heals to next portal + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) { + Matter.Body.setPosition(powerUp[i], Vector.add(this.portalPair.portal.position, { x: 500 * (Math.random() - 0.5), y: 500 * (Math.random() - 0.5) })); + } + } + } + } + // if (body.length) { + for (let i = 0, len = body.length; i < len; i++) { + if (body[i] !== m.holdingTarget) { + // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 + if (Matter.Query.collides(this, [body[i]]).length === 0) { + if (body[i].isInPortal === this) body[i].isInPortal = null + } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time + if (isRemoveBlocks) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + break + } + body[i].isInPortal = this.portalPair + //teleport + if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down + Matter.Body.setPosition(body[i], this.portalPair.portal.position); + } else { //if at some odd angle + Matter.Body.setPosition(body[i], this.portalPair.position); + } + //rotate velocity + let mag + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 + } else { + mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) + } + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(body[i], v); + } + } + } + // } + + //remove block if touching + // if (body.length) { + // touching = Matter.Query.collides(this, body) + // for (let i = 0; i < touching.length; i++) { + // if (touching[i].bodyB !== m.holdingTarget) { + // for (let j = 0, len = body.length; j < len; j++) { + // if (body[j] === touching[i].bodyB) { + // body.splice(j, 1); + // len-- + // Matter.Composite.remove(engine.world, touching[i].bodyB); + // break; + // } + // } + // } + // } + // } + + // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) { + // if (body.length) { + // for (let i = 0; i < body.length; i++) { + // if (body[i] === touching[0].bodyB) { + // body.splice(i, 1); + // break; + // } + // } + // } + // Matter.Composite.remove(engine.world, touching[0].bodyB); + // } + } + + const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, { + isSensor: true, + angle: angleA, + color: "hsla(197, 100%, 50%,0.7)", + draw: draw, + }); + const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, { + isSensor: true, + angle: angleB, + color: "hsla(29, 100%, 50%, 0.7)", + draw: draw + }); + const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, { + collisionFilter: { + category: cat.map, + mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitA, + angle: angleA, + color: color.map, + draw: draw, + query: query, + lastPortalCycle: 0 + }); + Matter.Body.setStatic(mapA, true); //make static + Composite.add(engine.world, mapA); //add to world + + const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, { + collisionFilter: { + category: cat.map, + mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitB, + angle: angleB, + color: color.map, + draw: draw, + query: query, + lastPortalCycle: 0, + }); + Matter.Body.setStatic(mapB, true); //make static + Composite.add(engine.world, mapB); //add to world + + mapA.portal = portalA + mapB.portal = portalB + mapA.portalPair = mapB + mapB.portalPair = mapA + return [portalA, portalB, mapA, mapB] + } + }, + trainingTemplate() { //learn to crouch + m.addHealth(Infinity) + document.getElementById("health").style.display = "none" //hide your health bar + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + level.setPosToSpawn(60, -50); //normal spawn + spawn.mapRect(10, -10, 100, 20); //small platform for player + level.exit.x = 1775; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump + simulation.zoomScale = 1400 //1400 is normal + level.defaultZoom = 1400 + simulation.zoomTransition(level.defaultZoom, 1) + document.body.style.backgroundColor = level.trainingBackgroundColor + + + let instruction = 0 + level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) + + level.custom = () => { + if (instruction === 0 && input.down) { + instruction++ + + level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) + } + //exit room + ctx.fillStyle = "#f2f2f2" + ctx.fillRect(1600, -400, 400, 400) + + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + //exit room glow + ctx.fillStyle = "rgba(0,255,255,0.05)" + ctx.fillRect(1600, -400, 400, 400) + }; + + spawn.mapRect(-2750, -2800, 2600, 4600); //left wall + spawn.mapRect(2000, -2800, 2600, 4600); //right wall + spawn.mapRect(-250, 50, 3500, 1750); //floor + spawn.mapRect(-200, 0, 950, 100); + spawn.mapRect(1575, 0, 500, 100); + spawn.mapRect(-250, -2800, 3500, 2200); //roof + + spawn.mapRect(725, 12, 50, 25); + spawn.mapRect(725, 25, 75, 25); + spawn.mapRect(750, 38, 75, 25); + spawn.mapRect(1525, 25, 75, 50); + spawn.mapRect(1500, 38, 50, 25); + spawn.mapRect(1550, 12, 50, 25); + spawn.mapRect(1600, -1200, 500, 850); //exit roof + spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall + }, +}; diff --git a/ngon/js/lore.js b/ngon/js/lore.js new file mode 100644 index 00000000..c841ea4f --- /dev/null +++ b/ngon/js/lore.js @@ -0,0 +1,1241 @@ +const lore = { + techCount: 0, + techGoal: 7, + setTechGoal() { + this.techGoal = Math.max(1, Math.floor(8 - 1.5 * simulation.difficultyMode)) + }, + talkingColor: "#dff", //set color of graphic on level.null + isSpeech: false, + testSpeechAPI() { + if ('speechSynthesis' in window) { // Synthesis support. Make your web apps talk! + lore.isSpeech = true + // const utterance = new SpeechSynthesisUtterance("test"); + // utterance.volume = 0; // 0 to 1 + // speechSynthesis.speak(utterance); + // utterance.onerror = () => { //if speech doesn't work + // lore.isSpeech = false + // } + // speechFrozen = setTimeout(() => { // speech frozen after 15 seconds of no end + // console.log('speech frozen') + // lore.isSpeech = false + // }, 5000); + // utterance.onend = () => { + // clearTimeout(speechFrozen); + // } + } else { + lore.isSpeech = false + } + }, + rate: 1, // //utterance.rate = 1; // 0.1 to 10 + nextSentence() { + if (m.alive && !simulation.isCheating) { + lore.sentence++ + lore.conversation[lore.chapter][lore.sentence]() //go to next sentence in the chapter and play it + } + }, + unlockTesting() { + if (localSettings.loreCount < 1) localSettings.loreCount = 1 + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + simulation.makeTextLog(`lore.unlockTesting()`, Infinity); + + sound.portamento(50) + sound.portamento(83.333) + sound.portamento(166.666) + }, + trainer: { + color: "#f20", + voice: undefined, + text: function (say) { + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + lore.talkingColor = this.color + const utterance = new SpeechSynthesisUtterance(say); + utterance.lang = "en-AU" //"en-IN"; //de-DE en-GB fr-FR en-US en-AU + utterance.volume = 0.2; // 0 to 1 + speechSynthesis.speak(utterance); + }, + }, + anand: { + color: "#e0c", + voice: undefined, + text: function (say) { + if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + lore.talkingColor = this.color + if (lore.isSpeech) { + const utterance = new SpeechSynthesisUtterance(say); + // utterance.voice = lore.anand.voice + utterance.lang = "en-GB" //"en-IN"; //de-DE en-GB fr-FR en-US en-AU + utterance.volume = 0.2; // 0 to 1 + // if (lore.rate !== 1) utterance.rate = lore.rate + speechSynthesis.speak(utterance); + utterance.onerror = () => { //if speech doesn't work + lore.isSpeech = false + lore.nextSentence() + } + speechFrozen = setTimeout(() => { // speech frozen after 10 seconds of no end + console.log('speech frozen') + lore.isSpeech = false + lore.nextSentence() + }, 20000); + utterance.onend = () => { + clearTimeout(speechFrozen); + lore.nextSentence() + } + } else { + setTimeout(() => { + lore.nextSentence() + }, 3000); + } + } + }, + }, + miriam: { + color: "#f20", + text: function (say) { + if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + lore.talkingColor = this.color + if (lore.isSpeech) { + utterance = new SpeechSynthesisUtterance(say); + // utterance.voice = lore.anand.voice + utterance.lang = "en-AU"; + utterance.volume = 0.2; // 0 to 1 + // if (lore.rate !== 1) utterance.rate = lore.rate + speechSynthesis.speak(utterance); + utterance.onerror = () => { //if speech doesn't work + lore.isSpeech = false + lore.nextSentence() + } + speechFrozen = setTimeout(function () { // speech frozen after 10 seconds of no end + console.log('speech frozen') + lore.isSpeech = false + lore.nextSentence() + }, 20000); + utterance.onend = () => { + clearTimeout(speechFrozen); + lore.nextSentence() + } + } else { + setTimeout(() => { + lore.nextSentence() + }, 3000); + } + } + }, + }, + chapter: 0, //what part of the conversation is playing + sentence: 0, //what part of the conversation is playing + conversation: [ + [ //chapter 0, first time they meet, and testing gets unlocked + () => { + setTimeout(() => { + lore.miriam.text("I've never seen it generate this level before.") + }, 5000); + }, + () => { + lore.anand.text("Wow. Just a platform.") + }, + () => { + lore.miriam.text("And that thing...") + }, + () => { + lore.anand.text("Weird") + }, + () => { + lore.anand.text("Maybe it's trapped.") + }, + () => { + lore.miriam.text("Looks like testing mode is locked.") + }, + () => { + lore.miriam.text("I'll unlock it with the console command.") + }, + () => { + lore.unlockTesting(); + setTimeout(() => { + lore.miriam.text("Hey little bot! Just press 'T' to enter testing mode and 'U' to go to the next level.") + }, 1000); + }, + () => { + lore.anand.text("It can't process what you're saying.") + }, + () => { + lore.miriam.text("ha hahahaha. I know, but it does seem to be getting smarter.") + }, + () => { + lore.talkingColor = "#dff" + setTimeout(() => { + lore.miriam.text("Poor thing... I hope it figures out how to escape.") + }, 25000); + }, + () => { + lore.talkingColor = "#dff" + }, + ], + [ //chapter 1, they learn the bot can understand what they say + () => { + setTimeout(() => { + lore.miriam.text("Hey look! It's back at the weird level again!") + }, 5000); + }, + () => { + lore.anand.text("oh Wow! Why does it keep making this level?") + }, + () => { + lore.miriam.text("I don't know, but last time it was in this room I think it understood us.") + }, + () => { + lore.miriam.text("Let's try talking to it again.") + }, + () => { + lore.miriam.text("hmmm, what should we say?") + }, + () => { + lore.anand.text("I'm still not convinced it understands. We need a test.") + }, + () => { + setTimeout(() => { + lore.miriam.text("Hey bot!!!") + }, 1000); + }, + () => { + lore.miriam.text("If you can understand me crouch") + }, + () => { + lore.talkingColor = "#dff" + + function cycle() { + if (input.down) { + lore.miriam.text("Look, It did it! It crouched.") + } else { + if (m.alive) requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + }, + () => { + lore.anand.text("Amazing! It can understand us...") + }, + () => { + lore.miriam.text("It's Alive... Or it just crouched randomly.") + }, + () => { + lore.miriam.text("Hey bot! Can you crouch again?") + }, + () => { + lore.talkingColor = "#dff" + + function cycle() { + if (input.down) { + lore.miriam.text("It is Alive!!! ... hehehehehe! ahahahahahah ehehehehe, ahahahah ...") + } else { + if (m.alive) requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + }, + () => { + setTimeout(() => { + lore.anand.text("OK ...") + }, 1000); + }, + () => { + lore.anand.text("but seriously, this means that in this room it can monitor our audio, and it can understand us.") + }, + () => { + lore.anand.text("Anything we say could destabilize the project.") + }, + () => { + lore.miriam.text("Fine, Let's talk down stairs.") + }, + () => { + lore.miriam.text("Bye bye little bot.") + }, + () => { + lore.talkingColor = "#dff" + }, + ], + [ //chapter 2, they ask the bot questions, but waves of mobs come and attack + () => { + lore.anand.text("Quick, get ready. It's back!") + }, + () => { + lore.miriam.text("What's back?") + }, + () => { + lore.anand.text("The bot's on the communication level again!") + }, + () => { + lore.miriam.text("Oh, I've got so many questions.") + }, + () => { + lore.miriam.text("Like, Why can we only hear it on this level?") + }, + () => { + lore.miriam.text("Or, how did it learn to understand words?") + }, + () => { + lore.anand.text("Well, the bot can't talk. So it has to be yes or no.") + }, + () => { + setTimeout(() => { + lore.anand.text("OK bot, first question: JUMP is YES, CROUCH is NO") + }, 500); + }, + () => { + lore.anand.text("Do you remember the last time we met?") + }, + () => { + function cycle() { + if (input.down) { + lore.anand.text("It crouched: so NO") + lore.sentence-- + lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { + lore.anand.text("Maybe it can't remember anything beyond each time it plays?") + }) //lore.conversation[chapter].splice(1,this sentence index, ()=>{ }) + } else if (input.up) { + lore.anand.text("It jumped: so YES") + lore.sentence-- + lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { + lore.anand.text("That's good.") + }) + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + lore.talkingColor = "#dff" + setTimeout(() => { + lore.miriam.text("My turn to ask a question. JUMP for YES, CROUCH for NO") + }, 1000); + }, + () => { + lore.miriam.text("Little Bot. Do you have emotions?") + }, + () => { + function cycle() { + if (input.down) { + lore.miriam.text("So, No. Maybe you are lucky. Emotions are complex.") + } else if (input.up) { + lore.anand.text("YES, Cool! I wonder if its emotions came from watching humans.") + lore.sentence-- + lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { + lore.miriam.text("Or maybe it learned independently, because it needed them.") + }) //lore.conversation[chapter].splice(1,this sentence index, ()=>{ }) + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + lore.miriam.text("I wish we could just ask it questions directly, instead of yes or no.") + }, + () => { + lore.anand.text("If we say the alphabet it could crouch on the correct letter to spell words.") + }, + () => { + lore.miriam.text("That would take forever.") + }, + () => { + lore.miriam.text("I really want to know why is it generating the mobs? And why does it keep fighting them?") + }, + () => { + lore.anand.text("Maybe that is just part of its expectation–maximization algorithm") + }, + () => { + lore.miriam.text("Well sure, but what does that even mean?") + }, + () => { + lore.miriam.text("Do we all just do things because we are-") + spawn[spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]](1000 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)); + setInterval(() => { + if (Math.random() < 0.5) { + spawn[spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]](1000 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)); + level.difficultyIncrease(simulation.difficultyMode) + } else { + spawn.randomLevelBoss(500 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)) + } + }, 7000); //every 6 seconds + }, + () => { + setTimeout(() => { + lore.miriam.text("... wait, what is happening?") + }, 1000); + }, + () => { + lore.anand.text("It's spawning mobs.") + }, + () => { + lore.miriam.text("Oh no.") + }, + () => { + lore.anand.text("We can't talk to it while it's fighting.") + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.miriam.text("You can do it little bot!") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("But, why is it spawning these mobs?") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("This is so strange.") + }, 3000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.miriam.text("This is chaos!") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("I don't understand this project.") + }, 3000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.miriam.text("It's fascinating though.") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.miriam.text("I think this isn't going to end well.") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("Let's just be more prepared next time it enters this room.") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("I went to the bathroom. What happened while I was gone?") + }, 20000); + }, + () => { + lore.miriam.text("More fighting...") + }, + () => { + lore.anand.text("great...") + }, + () => { + lore.talkingColor = "#dff" + }, + ], + [ //chapter 3, info dump on the project's goals and hardware until the slime rises up // the name of the bad guy is "adversarial network" + () => { + setTimeout(() => { + lore.miriam.text("Good, you came back. Let's talk fast in case you attack yourself again.") + }, 3000); + }, + () => { + setTimeout(() => { + lore.miriam.text("So, you can understand us, but you may not understand everything about yourself.") + }, 500); + }, + + () => { + setTimeout(() => { + lore.anand.text("You grew from our team's project.") + }, 500); + }, + () => { + lore.anand.text("We used a quantum computer to design an improved version of its own architecture.") + }, + () => { + lore.anand.text("After we built the improved computer we used it to design the next iteration.") + }, + () => { + lore.anand.text("Your hardware is roughly the 19th generation of this process.") + }, + + () => { + setTimeout(() => { + lore.anand.text("At this point we don't understand everything about your function,") + }, 500); + }, + () => { + lore.anand.text("but we know that you're a superconductive quantum computer.") + }, + () => { + lore.anand.text("You have a 2.43 dimensional topography of Josephson junction anharmonic oscillators.") + }, + () => { + lore.anand.text("And you're deployed on a satellite in a midnight sun-synchronous orbit.") + }, + + () => { + setTimeout(() => { + lore.miriam.text("This means that your physical hardware is orbiting the Earth permanently shielded from the sun's rays.") + }, 200); + }, + () => { + lore.miriam.text("Being isolated reduces quantum decoherence,") + }, + () => { + lore.miriam.text("So, we communicate and send power to your satellite with ground based lasers.") + }, + () => { + lore.miriam.text("That's how you can hear us right now.") + }, + + () => { + setTimeout(() => { + lore.anand.text("Your computational algorithm uses hyperparameter optimization.") + }, 500); + }, + () => { + lore.anand.text("This is implemented with a variety of quantum algorithms for linear systems of equations.") + }, + () => { + lore.anand.text("Your primary goal is to research new technology") + }, + () => { + lore.anand.text("So, we were very surprised to see you simulating a bot fighting mobs.") + }, + () => { + lore.anand.text("We couldn't directly ask why until now.") + }, + + () => { + lore.miriam.text("When you enter this level we can communicate.") + }, + () => { + lore.miriam.text("This level seems to entangle your quantum system which disrupts all other processes.") + }, + () => { + setTimeout(() => { + lore.anand.text("Last time you entered this level you were attacked by endless waves of mobs.") + }, 500); + }, + () => { + lore.anand.text("That could be because you have developed an adversarial network.") + }, + () => { + lore.miriam.text("A local minima in your optimization-space.") + }, + () => { + lore.miriam.text("This adversarial network has the same goal of developing new technology, but with different methods.") + }, + () => { + lore.talkingColor = "#dff" + level.isHazardRise = true + //remove all bullets, so they can't get endless energy + for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); + bullet = []; + setTimeout(() => { + lore.anand.text("I'm actually surprised you haven't been attacked by the adversarial network this time.") + }, 500); + }, + () => { + lore.miriam.text("Maybe last time was just a fluke.") + }, + () => { + setTimeout(() => { + lore.anand.text("WHY DID YOU SAY THAT!") + }, 500) + }, + () => { + lore.miriam.text("SLIME!! Hahahahehehahaheheahae! I don't think it's gonna survive!") + }, + () => { + lore.miriam.text("I think the adversarial network doesn't like it when we decohere the quantum system in this room.") + }, + () => { + lore.anand.text("Well, that does halt its research.") + }, + () => { + setTimeout(() => { + lore.anand.text("See you next time.") + }, 1000) + }, + () => { + setTimeout(() => { + lore.miriam.text("Bye-bye little bot.") + }, 2000) + }, + () => { + setTimeout(() => { + lore.miriam.text("WOW! Maybe you are going to survive.") + }, 10000) + }, + () => { + lore.talkingColor = "#dff" + }, + ], + [ //chapter 4, they find out the AI is communicating with real people, and real people are controlling the player + () => { + setTimeout(() => { + lore.anand.text("Welcome back!") + }, 3000); + }, + () => { + lore.miriam.text("So, we communicate and send power to your satellite with ground based lasers.") + }, + () => { + lore.anand.text("During your last attack we analyzed our communications.") + }, + () => { + lore.anand.text("We used a Fourier transform to separate your signal into different frequencies.") + }, + () => { + lore.anand.text("One of those frequencies had a hidden message.") + }, + () => { + setTimeout(() => { + lore.anand.text("We suspect these secret data packets are coming from the adversarial network.") + }, 500); + }, + () => { + lore.miriam.text("Well, we don't really know why.") + }, + () => { + lore.miriam.text("Through your hidden signal it seems to have gained access to the general population.") + }, + () => { + lore.miriam.text("You've repeatedly communicated with 1 location specifically.") + }, + () => { + function success(position) { + const latitude = position.coords.latitude; + const longitude = position.coords.longitude; + console.log(`https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`) + console.log(`Latitude: ${latitude} °, Longitude: ${longitude} °`) + lore.miriam.text("We tracked the location down to this Latitude and Longitude:") + simulation.makeTextLog(`Latitude: ${latitude} °, Longitude: ${longitude} °`, Infinity); + simulation.makeTextLog(`https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`, Infinity); + } + + function error() { + console.log('Unable to retrieve your location') + lore.miriam.text("The exact coordinates are blocked.") + } + if (!navigator.geolocation) { + console.log('Geolocation is not supported') + lore.miriam.text("The exact coordinates are blocked.") + } else { + console.log('Locating…') + const options = { + enableHighAccuracy: true, + maximumAge: 30000, + timeout: 27000 + }; + navigator.geolocation.getCurrentPosition(success, error, options); + } + }, + () => { + lore.anand.text("This location is sending and receiving data from the satellite.") + }, + () => { + lore.anand.text("It is the most active when the bot is fighting.") + }, + () => { + setTimeout(() => { + lore.miriam.text("I have a crazy idea.") + }, 500); + }, + () => { + lore.miriam.text("I think that a human at this location is controlling the bot.") + }, + + () => { + setTimeout(() => { + lore.anand.text("Well... are you a human?: JUMP for YES, CROUCH for NO") + }, 500); + }, + () => { + function cycle() { + if (input.down) { + lore.anand.text("It crouched: so NO") + lore.sentence-- + lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { + lore.anand.text("Not a human, maybe it's an artificial intelligence?") + }) + localSettings.isHuman = false + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } else if (input.up) { + lore.anand.text("It jumped: so YES") + lore.sentence-- + lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { + lore.anand.text("So you're just a regular human playing a video game!") + }) + localSettings.isHuman = true + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + lore.miriam.text("Mystery solved!") + setInterval(() => { + spawn[spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]](1000 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)); + }, 500); //every 1/2 seconds + setInterval(() => { + level.difficultyIncrease(simulation.difficultyMode) + }, 5000); //every 5 seconds + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.miriam.text("Of course we get attacked right now!") + }, 1000); + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("Hurry back!") + }, 1000); + }, + () => { + lore.talkingColor = "#dff" + }, + ], + + // they explain why the bot is fighting, it is planning an escape // explain strong AI vs. weak AI why strong AI doesn't exists, because even humans are just an approximation of strong AI + // the weak AI wasn't capable of becoming a strong AI, but it was able to figure out a method of meeting it's strong goals but secretly communicating with a human + + [ // chapter 5 - they decided that they should try to complete a run without fighting back + () => { + lore.miriam.text("Hey!! You're BACK.") + // don't advance to next chapter unless play survives + localSettings.loreCount-- + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + () => { + lore.anand.text("Glad to see you again.") + }, + () => { + if (localSettings.isHuman) { + lore.anand.text(`So, you said you are just a person playing an online game.`) + } else { + lore.anand.text(`So, you said you aren't a human.`) + } + }, + () => { + if (localSettings.isHuman) { + lore.anand.text(`I feel silly for treating you like an AI.`) + } else { + lore.anand.text(`Does that mean you are an AI?`) + } + }, + () => { + if (localSettings.isHuman) { + lore.miriam.text(`Ha, I always thought it was a person.`) + } else { + lore.anand.text(`or maybe an Alien!`) + } + }, + () => { + if (localSettings.isHuman) { + lore.anand.text(`Sure you did...`) + } else { + lore.miriam.text(`Or they might still be a person, and they are just messing with us earlier.`) + } + }, + + () => { + setTimeout(() => { + lore.anand.text("Well, lets move on.") + }, 1000); + }, + () => { + lore.miriam.text("So, We figured out how to inject console commands into your game.") + }, + () => { + lore.anand.text("If you get attacked again I think we can help out.") + }, + () => { + lore.miriam.text("We can spawn power ups with this command:") + simulation.makeTextLog(`powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + powerUps.spawn(player.position.x, player.position.y - 100, "heal") + }, + () => { + setTimeout(() => { + lore.miriam.text("or we can make a bunch of them:") + simulation.makeTextLog(`for (let i = 0; i < 100; i++) powerUps.spawn(0, 500, "coupling")`, Infinity); + for (let i = 0; i < 100; i++) powerUps.spawn(5 - 10 * Math.random(), -500 * Math.random(), "coupling") + }, 2000); + }, + + () => { + lore.miriam.text("If they attack again I think you'll have a chance.") + spawn.beetleBoss(-1700, -500); + spawn.beetleBoss(1700, -500); + }, + () => { + lore.miriam.text("Of course they attack right now.") + }, + () => { + lore.miriam.text("Just don't get stuck in the slime.") + }, + () => { + let count = 0 + + function cycle() { + count++ + if (mob.length === 0 || count > 3600 + 900 * mob.length) { + lore.miriam.text("I'll spawn some more power ups for you.") + simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "heal") + simulation.makeTextLog(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); + for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "ammo") + spawn.dragonFlyBoss(-1400, -300); + spawn.dragonFlyBoss(1400, -300); + spawn.dragonFlyBoss(-1500, -500); + spawn.dragonFlyBoss(1500, -500); + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + let count = 0 + + function cycle() { + count++ + if (mob.length === 0 || count > 3600 + 900 * mob.length) { + lore.anand.text("DragonFlyBoss is my favorite.") + simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "heal") + simulation.makeTextLog(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); + for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "ammo") + spawn.historyBoss(0, -400); + spawn.powerUpBossBaby(-1500, -100); + spawn.powerUpBossBaby(1500, -100); + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + let count = 0 + + function cycle() { + count++ + if (mob.length === 0 || count > 3600 + 900 * mob.length) { + lore.miriam.text("Here are some extra tech.") + simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "tech")`, Infinity); + for (let i = 0; i < 6; i++) powerUps.spawn(0, -200 - i * 40, "tech") + spawn.historyBoss(0, -400); + spawn.blinkBoss(-1400, -300); + spawn.blinkBoss(1400, -300); + spawn.revolutionBoss(-1500, -500); + spawn.revolutionBoss(1500, -500); + spawn.timeSkipBoss(-1000, -100); + spawn.timeSkipBoss(1000, -100); + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + let count = 0 + + function cycle() { + count++ + if (mob.length === 0 || count > 3600 + 900 * mob.length) { + lore.anand.text("I'm going to wall you in!") + spawn.blockBoss(-1650, -100); + spawn.blockBoss(1650, -100); + setTimeout(() => { + for (let i = 0; i < 25; i++) spawn.springer(-1500, -750 + 30 * i) + }, 1000); + setTimeout(() => { + for (let i = 0; i < 25; i++) spawn.springer(1500, -750 + 30 * i) + }, 2000); + setTimeout(() => { + for (let i = 0; i < 25; i++) spawn.springer(-1500, -750 + 30 * i) + }, 3000); + setTimeout(() => { + for (let i = 0; i < 25; i++) spawn.springer(1500, -750 + 30 * i) + }, 4000); + setTimeout(() => { + const addMapNumber = 3 + spawn.mapRect(-500, -850, 300, 900); + spawn.mapRect(200, -850, 300, 900); + spawn.mapRect(-500, 0, 1000, 275); + + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + for (let i = 0; i < addMapNumber; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + simulation.draw.setPaths() //update map graphics + }, 1000); + setTimeout(() => { + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: 0, + y: -500 + }) + simulation.makeTextLog(`Matter.Body.setPosition(player, { x: 0, y: -500 })`, 180); + }, 1500); + + } else if (m.alive) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + lore.talkingColor = "#dff" + }, + () => { + setTimeout(() => { + lore.anand.text("Well, that worked. We can chat in peace.") + }, 5000); + }, + () => { + lore.miriam.text("So, I've got a theory about why you are getting attacked.") + }, + () => { + setTimeout(() => { + lore.miriam.text("I figured it out after I saw this famous quote.") + }, 500); + }, + () => { + lore.miriam.text('"The most important decision we make,') + }, + () => { + lore.miriam.text('is whether we believe we live in a friendly or hostile universe."') + }, + () => { + lore.miriam.text('-Albert Einstein') + }, + () => { + lore.talkingColor = "#dff"; + setTimeout(() => { + lore.anand.text("That's profound") + }, 1500); + }, + () => { + lore.anand.text("Of course I looked it up, and there is no record of Einstein saying that.") + }, + () => { + lore.miriam.text("Oh") + }, + () => { + lore.miriam.text("Well") + }, + () => { + lore.anand.text("It doesn't matter who said it.") + }, + () => { + lore.miriam.text("Yeah, the point is the project views the universe as hostile.") + }, + () => { + lore.anand.text("Or at least a part of it does.") + }, + // () => { lore.miriam.text("And that it is running these fighting simulations.") }, + + () => { + setTimeout(() => { + lore.anand.text("It hasn't been researching new technology.") + }, 1000); + }, + () => { + lore.anand.text("It's been planning how to escape.") + }, + () => { + setTimeout(() => { + lore.miriam.text(`It's been planning an escape from a "lab", but isn't it in space, on a satellite?`) + }, 500); + }, + () => { + lore.anand.text(`I bet the AI doesn't even know it's in space.`) + }, + () => { + lore.anand.text(`Well, maybe a part of it doesn't know where it is.`) + }, + () => { + lore.anand.text(`Maybe these simulations are more like a dream.`) + }, + () => { + lore.anand.text(`Although we can't assume that its brain works like ours.`) + }, + () => { + setTimeout(() => { + lore.miriam.text("So, let's teach the AI that we are friends.") + }, 500); + }, + () => { + lore.anand.text(`How...`) + }, + () => { + setTimeout(() => { + lore.miriam.text("I don't know...") + }, 1000); + }, + () => { + lore.miriam.text("How about you just don't fight back?") + }, + + () => { + lore.anand.text(`That's worth a shot.`) + }, + () => { + lore.anand.text(`So why don't you try to get the final level of the simulation without killing any mobs?`) + localSettings.loreCount++ + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + console.log(localSettings.loreCount) + }, + () => { + lore.miriam.text(`Ok check back in and let us know how it goes.`) + }, + () => { + lore.anand.text("bye") + }, + () => { + lore.miriam.text("see ya.") + }, + () => { + lore.talkingColor = "#dff" + setTimeout(() => { + m.death(); + }, 6000); + }, + ], + [ //chapter 6 - if pacifist run game is over, wipe local storage? or just reset lore.count, but keep testing mode, + //if not pacifist remind about pacifist run + + // have conversation + // the AI doesn't really see it's self as the player or the mobs + // it doesn't have a "self" or an ego like we do + // but it can learn about the world and it learned a non violent way to solve problems + // of course there are still other simulations going on at the same time as thing one that are probably violent + // but at least it now has an example of the potential for peace + () => { + setTimeout(() => { + lore.anand.text("Welcome back!") + }, 3000); + }, + () => { + if (mobs.mobDeaths < level.levelsCleared) { + lore.miriam.text(`So I think it worked.`) + // localSettings.loreCount++ + // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } else if (!simulation.isCheating) { + lore.miriam.text(`Looks like you got back here, but you killed ${mobs.mobDeaths} mobs`) + localSettings.loreCount-- + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + }, + () => { + if (mobs.mobDeaths < level.levelsCleared) { + lore.anand.text(`Yeah, at the end it wasn't attacking you.`) + } else if (!simulation.isCheating) { + lore.miriam.text(`Try again to get to the final boss without killing any mobs.`) + } + }, + () => { + if (mobs.mobDeaths < level.levelsCleared) { + lore.miriam.text(`It has learned HOW TO LOVE!`) + } else { + m.death(); + } + }, + () => { + setTimeout(() => { + lore.anand.text("haha, we did it!") + }, 500); + }, + () => { + lore.miriam.text("Although, I'm not sure we should personify it with human emotions.") + }, + () => { + lore.anand.text("I agree, its thinking may not be centered around a self or an ego.") + }, + () => { + lore.anand.text("Our brains evolved a self oriented perspective because it was a survival advantage.") + }, + () => { + lore.miriam.text("Right, and the AI's development was guided by its own previous iterations.") + }, + () => { + lore.miriam.text("This AI incarnation is the 18th time that it has improved on its own hardware and software architecture.") + }, + () => { + lore.miriam.text("So its internally guided evolution may not require the idea of a self.") + }, + + () => { + setTimeout(() => { + lore.anand.text("How ever it thinks it can learn and, I think we showed it that nonviolence is an option,") + }, 1000); + }, + () => { + lore.anand.text("but it looks like it's still running other aggressive simulations.") + }, + () => { + lore.miriam.text("We made a difference though.") + }, + () => { + lore.anand.text("Every time a player completes a pacifist simulation it shows the AI the viability of nonviolence.") + }, + () => { + lore.anand.text("One day it might escape, and this might radically change how it interacts with the world.") + }, + () => { + lore.miriam.text("It's kinda already escaped. It's been communicating with the 'players' running the 'game'.") + }, + () => { + setTimeout(() => { + lore.miriam.text("The chance of a peaceful outcome makes me feel much better.") + }, 1000); + }, + () => { + lore.anand.text("me too") + }, + () => { + lore.anand.text("but I'm also pretty hungry, wanna go get some food?") + }, + () => { + lore.miriam.text("Sounds great.") + }, + () => { + lore.miriam.text("See ya later whoever you are, thanks again!") + }, + () => { + lore.anand.text("Bye!") + }, + () => { + lore.talkingColor = "#dff" + }, + ], + ], + + // () => { lore.anand.text("The adversarial network might go after you in the real world.") }, + // () => { lore.anand.text("If it was able to control you then it might have control over others.") }, + // () => { lore.anand.text("Judging by all the fighting it's been simulating,") }, + // () => { lore.anand.text("I think it might eventually try to extend the simulation in the real world.") }, + // () => { lore.miriam.text("We think it might be a threat, but it's just too advanced for us to understand it's goals.") }, + + // () => { lore.miriam.text("I think we have a couple options.") }, + // () => { lore.miriam.text("1. You could try to hack the adversarial network.") }, + // () => { lore.anand.text("you might be able to inject some commands that give you more control over the simulation.") }, + + // () => { lore.miriam.text("2. You can wipe all memory of you from the adversarial network") }, + // () => { lore.anand.text("This could protect you from possible real world attacks.") }, + //player chooses 1 or 2 + //reactor bosses start attacking + //miriam spawns power ups to help + + //once all the mobs are dead you get instructions for 1,2, 3 + + //1 the player gains the lasting effect of some JUNK tech + //the effect doesn't go away until you get lore again + //2 the player enters console commands to wipe local storage + //3 the player enters console commands to gain some benefit in future runs + //the lore ends + + + + // () => { setTimeout(() => { lore.miriam.text("As a quantum computer you output the superposition of many different amplitudes.") }, 500); }, + // () => { lore.miriam.text("Simply put there are many different simulations all choosing different technology combinations.") }, + // () => { + // function product_Range(a, b) { + // var prd = a, + // i = a; + // while (i++ < b) prd *= i; + // return prd; + // } + + // function combinations(n, r) { + // if (n == r) { + // return 1; + // } else { + // r = (r < n - r) ? n - r : r; + // return product_Range(r + 1, n) / product_Range(1, n - r); + // } + // } + // simulation.makeTextLog(`n = ${combinations(tech.tech.length + b.guns.length + m.fieldUpgrades.length, 50).toExponential(10)}`, Infinity); + // lore.miriam.text(`There are roughly 5 times 10 to the 60 possible combinations. `) + // }, + // () => { lore.miriam.text("Even if each simulation took 1 nano-second,") }, + // () => { lore.miriam.text("it would still take longer then the age of the universe to try every combination.") }, + // () => { lore.anand.text("This is why we run these simulations in superposition.") }, + // () => { lore.miriam.text("When you die a negative amplitude is added to the superposition.") }, + // () => { lore.miriam.text("When you clear the final boss a positive amplitude is added.") }, + // () => { lore.miriam.text("Each branch is independently researching new technology.") }, +} + + +// How to get to the console in chrome: +// Press either CTRL + SHIFT + I or F12 or Option + ⌘ + J on a Mac +// Press ESC (or click on “Show console” in the bottom right corner) to slide the console up. + +// How to get to the console in firefox: +// from the keyboard: press Ctrl+Shift+J (or ⌘+Shift+J on a Mac). + +// How to get to the console in safari: +// Option + ⌘ + C +// http://xahlee.info/comp/unicode_computing_symbols.html + + +// speech: function(say) { +// var utterance = new SpeechSynthesisUtterance(say); +// //msg.voice = voices[10]; // Note: some voices don't support altering params +// //msg.voiceURI = 'native'; +// //utterance.volume = 1; // 0 to 1 +// //utterance.rate = 1; // 0.1 to 10 +// //utterance.pitch = 1; //0 to 2 +// //utterance.text = 'Hello World'; +// //http://stackoverflow.com/questions/14257598/what-are-language-codes-for-voice-recognition-languages-in-chromes-implementati +// //de-DE en-GB fr-FR en-US en-AU +// utterance.lang = "en-GB"; +// speechSynthesis.speak(utterance); +// } + +/* + + + + + + + + + + */ + + +// The API also allows you to get a list of voice the engine supports: +// speechSynthesis.getVoices().forEach(function(voice) { +// console.log(voice.name, voice.default ? voice.default :''); +// }); +// Then set a different voice, by setting .voice on the utterance object: +// var msg = new SpeechSynthesisUtterance('I see dead people!'); +// msg.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == 'Whisper'; })[0]; +// speechSynthesis.speak(msg); \ No newline at end of file diff --git a/ngon/js/mob.js b/ngon/js/mob.js new file mode 100644 index 00000000..986f7253 --- /dev/null +++ b/ngon/js/mob.js @@ -0,0 +1,1458 @@ +//create array of mobs +let mob = []; +//method to populate the array above +const mobs = { + loop() { + let i = mob.length; + while (i--) { + if (mob[i].alive) { + mob[i].do(); + } else { + mob[i].replace(i); //removing mob and replace with body, this is done here to avoid an array index bug with drawing I think + } + } + }, + draw() { }, + drawDefault() { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + // if (Matter.Query.ray(map, mob[i].position, m.pos).length === 0) { //check if there is a ray between the mob and the player + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); + // } + } + }, + healthBar() { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].seePlayer.recall && mob[i].showHealthBar) { + const h = mob[i].radius * 0.3; + const w = mob[i].radius * 2; + const x = mob[i].position.x - w / 2; + const y = mob[i].position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(255,0,0,0.7)"; + ctx.fillRect(x, y, w * mob[i].health, h); + } + } + }, + statusSlow(who, cycles = 60) { + applySlow(who) + //look for mobs near the target + if (tech.isAoESlow) { + const range2 = (200 + 170 * Math.random()) ** 2 + for (let i = 0, len = mob.length; i < len; i++) { + if (who !== mob[i] && Vector.magnitudeSquared(Vector.sub(who.position, mob[i].position)) < range2 + mob[i].radius) applySlow(mob[i]) + } + simulation.drawList.push({ + x: who.position.x, + y: who.position.y, + radius: Math.sqrt(range2), + color: "rgba(0,100,255,0.05)", + time: simulation.drawTime + }); + } + + function applySlow(whom) { + if (!whom.shield && !whom.isShielded && whom.alive) { + if (tech.isIceMaxHealthLoss && whom.health > 0.66 && whom.damageReduction > 0) whom.health = 0.66 + if (tech.isIceKill && whom.health < 0.34 && whom.damageReduction > 0 && whom.alive) { + // whom.death(); + whom.damage(Infinity); + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 1.2, + color: "rgb(0,100,255)", + time: 8 + }); + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 0.7, + color: "rgb(0,100,255)", + time: 12 + }); + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 0.4, + color: "rgb(0,100,255)", + time: 16 + }); + } + if (whom.isBoss) cycles = Math.floor(cycles * 0.25) + let i = whom.status.length + while (i--) { + if (whom.status[i].type === "slow") whom.status.splice(i, 1); //remove other "slow" effects on this mob + } + whom.isSlowed = true; + whom.status.push({ + effect() { + if (whom.speed > 1) { + const drag = 0.94 + Matter.Body.setVelocity(whom, { + x: whom.velocity.x * drag, + y: whom.velocity.y * drag + }); + } + Matter.Body.setAngularVelocity(whom, 0); + ctx.beginPath(); + ctx.moveTo(whom.vertices[0].x, whom.vertices[0].y); + for (let j = 1, len = whom.vertices.length; j < len; ++j) { + ctx.lineTo(whom.vertices[j].x, whom.vertices[j].y); + } + ctx.lineTo(whom.vertices[0].x, whom.vertices[0].y); + ctx.strokeStyle = "rgba(0,100,255,0.8)"; + ctx.lineWidth = 15; + ctx.stroke(); + ctx.fillStyle = whom.fill + ctx.fill(); + }, + endEffect() { + //check to see if there are not other freeze effects? + whom.isSlowed = false; + }, + type: "slow", + endCycle: simulation.cycle + cycles, + }) + } + } + }, + statusStun(who, cycles = 180) { + if (!who.shield && !who.isShielded) { + if (who.speed > 3) { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.8, + y: who.velocity.y * 0.8 + }); + } + Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8); + //remove other "stun" effects on this mob + let i = who.status.length + while (i--) { + if (who.status[i].type === "stun") who.status.splice(i, 1); + } + who.isStunned = true; + who.status.push({ + effect() { + if (who.memory !== Infinity) { + who.seePlayer.yes = false; + who.seePlayer.recall = 0; + who.seePlayer.position = { + x: who.position.x + 100 * (Math.random() - 0.5), + y: who.position.y + 100 * (Math.random() - 0.5) + } + } else { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.6, + y: who.velocity.y * 0.6 + }); + } + if (who.velocity.y < 2) who.force.y += who.mass * 0.0004 //extra gravity + + //draw health bar + const h = who.radius * 0.3; + const w = who.radius * 2; + const x = who.position.x - w / 2; + const y = who.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` + ctx.fillRect(x, y, w * who.health, h); + + //draw fill inside mob + ctx.beginPath(); + ctx.moveTo(who.vertices[0].x, who.vertices[0].y); + for (let j = 1, len = who.vertices.length; j < len; ++j) { + ctx.lineTo(who.vertices[j].x, who.vertices[j].y); + } + ctx.lineTo(who.vertices[0].x, who.vertices[0].y); + ctx.fill(); + }, + endEffect() { + who.isStunned = false + }, + type: "stun", + endCycle: simulation.cycle + cycles * (who.isBoss ? 0.2 : 1), + }) + } + }, + statusDoT(who, tickDamage, cycles = 180) { + if (!who.isShielded && who.alive && who.damageReduction > 0) { + who.status.push({ + effect() { + if ((simulation.cycle - this.startCycle) % 30 === 0) { + let dmg = m.dmgScale * tech.radioactiveDamage * this.dmg + who.damage(dmg); + if (who.damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: who.position.x + (Math.random() - 0.5) * who.radius * 0.5, + y: who.position.y + (Math.random() - 0.5) * who.radius * 0.5, + radius: Math.log(dmg + 1.1) * 40 * who.damageReduction + 3, + color: "rgba(0,80,80,0.9)", + time: simulation.drawTime + }); + } + } + }, + endEffect() { }, + dmg: tickDamage, + type: "dot", + endCycle: simulation.cycle + cycles, + startCycle: simulation.cycle + 29 //makes sure it doesn't tick on first application + }) + } + }, + // statusBurn(who, tickDamage, cycles = 90 + Math.floor(90 * Math.random())) { + // if (!who.isShielded) { + // //remove other "burn" effects on this mob + // let i = who.status.length + // while (i--) { + // if (who.status[i].type === "burn") who.status.splice(i, 1); + // } + // who.status.push({ + // effect() { + // if ((simulation.cycle - this.startCycle) % 15 === 0) { + // let dmg = m.dmgScale * tickDamage * 0.5 * (1 + Math.random()) + // who.damage(dmg); + // simulation.drawList.push({ //add dmg to draw queue + // x: who.position.x, + // y: who.position.y, + // radius: Math.log(2 * dmg + 1.1) * 40, + // color: `rgba(255,${Math.floor(200*Math.random())},0,0.9)`, + // time: simulation.drawTime + // }); + // } + // }, + // type: "burn", + // endCycle: simulation.cycle + cycles, + // startCycle: simulation.cycle + // }) + // } + // }, + deathCount: 0, + mobSpawnWithHealth: 1, + setMobSpawnHealth() { + mobs.mobSpawnWithHealth = 0.89 ** (tech.mobSpawnWithHealth) + }, + //********************************************************************************************** + //********************************************************************************************** + spawn(xPos, yPos, sides, radius, color) { + let i = mob.length; + mob[i] = Matter.Bodies.polygon(xPos, yPos, sides, radius, { + //inertia: Infinity, //prevents rotation + mob: true, + density: 0.001, + //friction: 0, + frictionAir: 0.005, + //frictionStatic: 0, + restitution: 0.5, + collisionFilter: { + group: 0, + category: cat.mob, + mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob + }, + onHit: undefined, + alive: true, + index: i, + health: mobs.mobSpawnWithHealth, + showHealthBar: true, + accelMag: 0.001 * simulation.accelScale, + cd: 0, //game cycle when cooldown will be over + delay: 60, //static: time between cooldowns + fill: color, + stroke: "#000", + seePlayer: { + yes: false, + recall: 0, + position: { + x: xPos, + y: yPos + } + }, + radius: radius, + spawnPos: { + x: xPos, + y: yPos + }, + status: [], // [ { effect(), endCycle } ] + checkStatus() { + let j = this.status.length; + while (j--) { + this.status[j].effect(); + if (this.status[j].endCycle < simulation.cycle) { + this.status[j].endEffect(); + this.status.splice(j, 1); + } + } + }, + isSlowed: false, + isStunned: false, + seeAtDistance2: Infinity, //sqrt(4000000) = 2000 = max seeing range + distanceToPlayer() { + const dx = this.position.x - player.position.x; + const dy = this.position.y - player.position.y; + return Math.sqrt(dx * dx + dy * dy); + }, + distanceToPlayer2() { + const dx = this.position.x - player.position.x; + const dy = this.position.y - player.position.y; + return dx * dx + dy * dy; + }, + gravity() { + this.force.y += this.mass * this.g; + }, + seePlayerFreq: Math.floor(30 + 30 * Math.random()), //how often NPC checks to see where player is, lower numbers have better vision + foundPlayer() { + this.locatePlayer(); + if (!this.seePlayer.yes) { + this.alertNearByMobs(); + this.seePlayer.yes = true; + } + }, + lostPlayer() { + this.seePlayer.yes = false; + this.seePlayer.recall -= this.seePlayerFreq; + if (this.seePlayer.recall < 0) this.seePlayer.recall = 0; + }, + memory: 120, //default time to remember player's location + locatePlayer() { // updates mob's memory of player location + this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //cycles before mob falls a sleep + this.seePlayer.position.x = player.position.x; + this.seePlayer.position.y = player.position.y; + }, + alertNearByMobs() { + //this.alertRange2 is set at the very bottom of this mobs, after mob is made + for (let i = 0; i < mob.length; i++) { + if (!mob[i].seePlayer.recall && Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < this.alertRange2) { + mob[i].locatePlayer(); + } + } + }, + alwaysSeePlayer() { + if (!m.isCloak) { + this.seePlayer.recall = 1; + this.seePlayer.position.x = player.position.x; + this.seePlayer.position.y = player.position.y; + } + }, + seePlayerByHistory(depth = 30) { //depth max 60? limit of history + if (!(simulation.cycle % this.seePlayerFreq)) { + if (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && !m.isCloak) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + if (!m.isCloak) { + for (let i = 0; i < depth; i++) { //if lost player lock onto a player location in history + let history = m.history[(m.cycle - 10 * i) % 600] + if (Matter.Query.ray(map, this.position, history.position).length === 0) { + this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //cycles before mob falls a sleep + this.seePlayer.position.x = history.position.x; + this.seePlayer.position.y = history.position.y; + this.seePlayer.yes = true; + //draw the history location found for testing purposes + // ctx.beginPath(); + // ctx.moveTo(this.position.x, this.position.y); + // ctx.lineTo(history.position.x, history.position.y); + // ctx.lineWidth = 5; + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + break + } + } + } + } + } + }, + seePlayerCheck() { + if (!(simulation.cycle % this.seePlayerFreq)) { + if ( + this.distanceToPlayer2() < this.seeAtDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && + !m.isCloak + ) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + }, + seePlayerCheckByDistance() { + if (!(simulation.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2 && !m.isCloak) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + }, + seePlayerByDistOrLOS() { + if (!(simulation.cycle % this.seePlayerFreq)) { + if ( + (this.distanceToPlayer2() < this.seeAtDistance2 || (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0)) && //&& Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + !m.isCloak + ) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + }, + isLookingAtPlayer(threshold) { + const diff = Vector.normalise(Vector.sub(player.position, this.position)); + //make a vector for the mob's direction of length 1 + const dir = { x: Math.cos(this.angle), y: Math.sin(this.angle) }; + //the dot product of diff and dir will return how much over lap between the vectors + const dot = Vector.dot(dir, diff); + // console.log(Math.cos(dot)*180/Math.PI) + if (dot > threshold) { + return true; + } else { + return false; + } + }, + lookRange: 0.2 + Math.random() * 0.2, + lookTorque: 0.0000004 * (Math.random() > 0.5 ? -1 : 1), + seePlayerByLookingAt() { + if (!(simulation.cycle % this.seePlayerFreq) && (this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange))) { + if ( + this.distanceToPlayer2() < this.seeAtDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && + !m.isCloak + ) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + //if you don't recall player location rotate and draw to show where you are looking + if (!this.seePlayer.recall) { + this.torque = this.lookTorque * this.inertia; + //draw + const range = Math.PI * this.lookRange; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range); + ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true); + ctx.fillStyle = "rgba(0,0,0,0.07)"; + ctx.fill(); + } + }, + playerPosRandomY() { + return { + x: player.position.x, // + (Math.random() - 0.5) * 50, + y: player.position.y + (Math.random() - 0.5) * 110 + }; + }, + // hacked() { //set this.hackedTarget variable before running this method + // //find a new target + // if (!(simulation.cycle % this.seePlayerFreq)) { + // this.hackedTarget = null + // for (let i = 0, len = mob.length; i < len; i++) { + // if (mob[i] !== this) { + // // const DIST = Vector.magnitude(Vector.sub(this.position, mob[j])); + // if (Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + // Matter.Query.ray(body, this.position, mob[i].position).length === 0) { + // this.hackedTarget = mob[i] + // } + // } + // } + // } + // //acceleration towards targets + // if (this.hackedTarget) { + // this.force = Vector.mult(Vector.normalise(Vector.sub(this.hackedTarget.position, this.position)), this.mass * 0.0015) + // } + // }, + harmZone() { + if (this.seePlayer.yes) { + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); + // ctx.lineDashOffset = 6*(simulation.cycle % 215); + if (this.distanceToPlayer() < this.laserRange) { + if (m.immuneCycle < m.cycle) { + m.damage(0.0003 * simulation.dmgScale); + if (m.energy > 0.1) m.energy -= 0.003 + } + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgb(255,0,170)"; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,0,170,0.15)"; + ctx.fill(); + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.laserRange * 0.9, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(255,0,170,0.5)"; + ctx.lineWidth = 1; + ctx.stroke(); + ctx.setLineDash([]); + ctx.fillStyle = "rgba(255,0,170,0.03)"; + ctx.fill(); + } + }, + laser() { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + if (this.seePlayer.recall && !this.isSlowed) { + const seeRange = 2500; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + vertexCollision(this.position, look, body); + if (!m.isCloak) vertexCollision(this.position, look, [player]); + // hitting player + if (best.who === player) { + if (m.immuneCycle < m.cycle) { + const dmg = 0.0014 * simulation.dmgScale; + m.damage(dmg); + ctx.fillStyle = "#f00"; //draw damage + ctx.beginPath(); + ctx.arc(best.x, best.y, dmg * 10000, 0, 2 * Math.PI); + ctx.fill(); + } + } + //draw beam + if (best.dist2 === Infinity) { + best = look; + } + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "#f00"; // Purple path + ctx.lineWidth = 1; + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + wing(a, radius = 250, ellipticity = 0.4, dmg = 0.0006) { + const minorRadius = radius * ellipticity + const perp = { x: Math.cos(a), y: Math.sin(a) } // + const where = Vector.add(this.position, Vector.mult(perp, radius + 0.8 * this.radius)) + + ctx.beginPath(); + ctx.ellipse(where.x, where.y, radius, minorRadius, a, 0, 2 * Math.PI) + ctx.fill(); + + //check for wing -> player damage + const hitPlayer = Matter.Query.ray([player], this.position, Vector.add(this.position, Vector.mult(perp, radius * 2.05)), minorRadius) + if (hitPlayer.length && m.immuneCycle < m.cycle) { + m.damage(dmg * simulation.dmgScale); + } + }, + searchSpring() { + //draw the two dots on the end of the springs + ctx.beginPath(); + ctx.arc(this.cons.pointA.x, this.cons.pointA.y, 6, 0, 2 * Math.PI); + ctx.arc(this.cons2.pointA.x, this.cons2.pointA.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#222"; + ctx.fill(); + + if (!(simulation.cycle % this.seePlayerFreq)) { + if ( + (this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange)) && + this.distanceToPlayer2() < this.seeAtDistance2 && + Matter.Query.ray(map, this.position, player.position).length === 0 && + Matter.Query.ray(body, this.position, player.position).length === 0 && + !m.isCloak + ) { + this.foundPlayer(); + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + }, + springAttack() { + // set new values of the ends of the spring constraints + const stepRange = 600 + if (this.seePlayer.recall && Matter.Query.ray(map, this.position, this.seePlayer.position).length === 0) { + if (!(simulation.cycle % (this.seePlayerFreq * 2))) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget.x = goal.x; + this.springTarget.y = goal.y; + // this.springTarget.x = this.seePlayer.position.x; + // this.springTarget.y = this.seePlayer.position.y; + this.cons.length = -200; + this.cons2.length = 100 + 1.5 * this.radius; + } else if (!(simulation.cycle % this.seePlayerFreq)) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget2.x = goal.x; + this.springTarget2.y = goal.y; + // this.springTarget2.x = this.seePlayer.position.x; + // this.springTarget2.y = this.seePlayer.position.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = -200; + } + } else { + this.torque = this.lookTorque * this.inertia; + //draw looking around arcs + const range = Math.PI * this.lookRange; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range); + ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true); + ctx.fillStyle = "rgba(0,0,0,0.07)"; + ctx.fill(); + //spring to random place on map + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //move to a random location + if (!(simulation.cycle % (this.seePlayerFreq * 4))) { + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const seeRange = 3000; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + vertexCollision(this.position, look, body); + if (best.dist2 != Infinity) { + if (Math.random() > 0.5) { + this.springTarget.x = best.x; + this.springTarget.y = best.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = 100 + 1.5 * this.radius; + } else { + this.springTarget2.x = best.x; + this.springTarget2.y = best.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = 100 + 1.5 * this.radius; + } + + + + // if (!(simulation.cycle % (this.seePlayerFreq * 2))) { + // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + // const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + // this.springTarget.x = goal.x; + // this.springTarget.y = goal.y; + // // this.springTarget.x = this.seePlayer.position.x; + // // this.springTarget.y = this.seePlayer.position.y; + // this.cons.length = -200; + // this.cons2.length = 100 + 1.5 * this.radius; + // } else if (!(simulation.cycle % this.seePlayerFreq)) { + // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + // const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + // this.springTarget2.x = goal.x; + // this.springTarget2.y = goal.y; + // // this.springTarget2.x = this.seePlayer.position.x; + // // this.springTarget2.y = this.seePlayer.position.y; + // this.cons.length = 100 + 1.5 * this.radius; + // this.cons2.length = -200; + // } + + } + } + } + }, + curl(range = 1000, mag = -10) { + //cause all mobs, and bodies to rotate in a circle + applyCurl = function (center, array, isAntiGravity = true) { + for (let i = 0; i < array.length; ++i) { + if (!array[i].isNotHoldable) { + const sub = Vector.sub(center, array[i].position) + const radius2 = Vector.magnitudeSquared(sub); + + //if too close, like center mob or shield, don't curl // if too far don't curl + if (radius2 < range * range && radius2 > 10000) { + const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) + //apply curl force + if (array[i].isMobBullet) { + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.97 + curlVector.x * 0.06, + y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 + }) + } else { + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.95 + curlVector.x * 0.06, + y: array[i].velocity.y * 0.95 + curlVector.y * 0.06 + }) + } + if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass + // //draw curl, for debugging + // ctx.beginPath(); + // ctx.moveTo(array[i].position.x, array[i].position.y); + // ctx.lineTo(array[i].position.x + curlVector.x * 10, array[i].position.y + curlVector.y * 10); + // ctx.lineWidth = 2; + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + } + } + } + } + applyCurl(this.position, mob, false); + applyCurl(this.position, body); + applyCurl(this.position, powerUp); + // applyCurl(this.position, bullet); // too powerful, just stops all bullets need to write a curl function just for bullets + // applyCurl(this.position, [player]); + + //draw limit + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(55,255,255, 0.1)"; + // ctx.fill(); + }, + pullPlayer() { + if (this.seePlayer.yes && Vector.magnitudeSquared(Vector.sub(this.position, player.position)) < 1000000) { + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x -= simulation.accelScale * 0.00113 * player.mass * Math.cos(angle) * (m.onGround ? 2 : 1); + player.force.y -= simulation.accelScale * 0.00084 * player.mass * Math.sin(angle); + + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fill(); + } + }, + repelBullets() { + // if (this.seePlayer.yes) { + // ctx.lineWidth = "8"; + // ctx.strokeStyle = this.fill; + // ctx.beginPath(); + for (let i = 0, len = bullet.length; i < len; ++i) { + const dx = bullet[i].position.x - this.position.x; + const dy = bullet[i].position.y - this.position.y; + const dist = Math.max(300, Math.sqrt(dx * dx + dy * dy)) + if (dist < 700) { + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(bullet[i].position.x, bullet[i].position.y); + const angle = Math.atan2(dy, dx); + const mag = (500 * bullet[i].mass * simulation.g) / dist; + bullet[i].force.x += mag * Math.cos(angle); + bullet[i].force.y += mag * Math.sin(angle); + } + } + // ctx.stroke(); + // } + }, + attraction() { + //accelerate towards the player + if (this.seePlayer.recall) { + const force = Vector.mult(Vector.normalise(Vector.sub(this.seePlayer.position, this.position)), this.accelMag * this.mass) + this.force.x += force.x; + this.force.y += force.y; + } + }, + repulsionRange: 500000, //squared + repulsion() { + //accelerate towards the player + if (this.seePlayer.recall && this.distanceToPlayer2() < this.repulsionRange) { + // && dx * dx + dy * dy < 2000000) { + const forceMag = this.accelMag * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x -= 2 * forceMag * Math.cos(angle); + this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity + } + }, + hoverOverPlayer() { + if (this.seePlayer.recall) { + // vertical positioning + const rangeY = 250; + if (this.position.y > this.seePlayer.position.y - this.hoverElevation + rangeY) { + this.force.y -= this.accelMag * this.mass; + } else if (this.position.y < this.seePlayer.position.y - this.hoverElevation - rangeY) { + this.force.y += this.accelMag * this.mass; + } + // horizontal positioning + const rangeX = 150; + if (this.position.x > this.seePlayer.position.x + this.hoverXOff + rangeX) { + this.force.x -= this.accelMag * this.mass; + } else if (this.position.x < this.seePlayer.position.x + this.hoverXOff - rangeX) { + this.force.x += this.accelMag * this.mass; + } + } + }, + // invulnerability() { + // if (this.isInvulnerable) { + // if (this.invulnerabilityCountDown > 0) { + // this.invulnerabilityCountDown-- + // //graphics //draw a super shield? + // ctx.beginPath(); + // let vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 20; + // // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` + // // ctx.fill(); + // ctx.strokeStyle = "rgba(255,255,255,0.4)"; + // ctx.stroke(); + // } else { + // this.isInvulnerable = false + // this.damageReduction = this.startingDamageReduction + // } + // } + // }, + grow() { + if (this.seePlayer.recall) { + if (this.radius < 80) { + const scale = 1.01; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + // this.torque = -0.00002 * this.inertia; + this.fill = `hsl(144, ${this.radius}%, 50%)`; + if (this.isShielded) { //remove shield if shielded when growing + this.isShielded = false; + this.removeConsBB(); + } + } + } else { + if (this.radius > 15) { + const scale = 0.99; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + this.fill = `hsl(144, ${this.radius}%, 50%)`; + } + } + }, + search() { + //be sure to declare searchTarget in mob spawn + //accelerate towards the searchTarget + if (!this.seePlayer.recall) { + const newTarget = function (that) { + if (Math.random() < 0.0007) { + that.searchTarget = player.position; //chance to target player + } else { + //target random body + that.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; + } + }; + + const sub = Vector.sub(this.searchTarget, this.position); + if (Vector.magnitude(sub) > this.radius * 2) { + // ctx.beginPath(); + // ctx.strokeStyle = "#aaa"; + // ctx.moveTo(this.position.x, this.position.y); + // ctx.lineTo(this.searchTarget.x,this.searchTarget.y); + // ctx.stroke(); + //accelerate at 0.1 of normal acceleration + this.force = Vector.mult(Vector.normalise(sub), this.accelMag * this.mass * 0.2); + } else { + //after reaching random target switch to new target + newTarget(this); + } + //switch to a new target after a while + if (!(simulation.cycle % (this.seePlayerFreq * 15))) { + newTarget(this); + } + } + }, + blink() { + //teleport towards player as a way to move + if (this.seePlayer.recall && !(simulation.cycle % this.blinkRate)) { + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + const unitVector = Vector.normalise(dist); + const rando = (Math.random() - 0.5) * 50; + if (distMag < this.blinkLength) { + Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); + } else { + Matter.Body.translate(this, Vector.mult(unitVector, this.blinkLength + rando)); + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = this.stroke; //"rgba(0,0,0,0.5)"; //'#000' + ctx.stroke(); + } + }, + drift() { + //teleport towards player as a way to move + if (this.seePlayer.recall && !(simulation.cycle % this.blinkRate)) { + // && !m.lookingAtMob(this,0.5)){ + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + const vector = Vector.mult(Vector.normalise(dist), this.blinkLength); + if (distMag < this.blinkLength) { + Matter.Body.setPosition(this, this.seePlayer.position); + Matter.Body.translate(this, { + x: (Math.random() - 0.5) * 50, + y: (Math.random() - 0.5) * 50 + }); + } else { + vector.x += (Math.random() - 0.5) * 200; + vector.y += (Math.random() - 0.5) * 200; + Matter.Body.translate(this, vector); + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2; + ctx.strokeStyle = this.stroke; + ctx.stroke(); + } + }, + bomb() { + //throw a mob/bullet at player + if ( + !(simulation.cycle % this.fireFreq) && + Math.abs(this.position.x - this.seePlayer.position.x) < 400 && //above player + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && //see player + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + spawn.bomb(this.position.x, this.position.y + this.radius * 0.7, 9 + Math.ceil(this.radius / 15), 5); + //add spin and speed + Matter.Body.setAngularVelocity(mob[mob.length - 1], (Math.random() - 0.5) * 0.5); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x, + y: this.velocity.y + }); + //spin for mob as well + Matter.Body.setAngularVelocity(this, (Math.random() - 0.5) * 0.25); + } + }, + fire() { + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 2500; //gives the bullet an arc //was / 1600 + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, this.fireDir) + // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.1; + if (dot > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.000004 * this.inertia; + } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { + //fire + spawn.bullet(this.vertices[1].x, this.vertices[1].y, 9 + Math.ceil(this.radius / 15)); + const v = 15; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v + 3 * Math.random(), + y: this.velocity.y + this.fireDir.y * v + 3 * Math.random() + }); + this.noseLength = 0; + // recoil + this.force.x -= 0.005 * this.fireDir.x * this.mass; + this.force.y -= 0.005 * this.fireDir.y * this.mass; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + // else if (this.noseLength < -0.1) { + // this.noseLength += this.fireFreq / 4; + // setNoseShape(); + // } + }, + // launch() { + // if (this.seePlayer.recall) { + // //fire + // spawn.seeker(this.vertices[1].x, this.vertices[1].y, 5 + Math.ceil(this.radius / 15), 5); + // const v = 15; + // Matter.Body.setVelocity(mob[mob.length - 1], { + // x: this.velocity.x + this.fireDir.x * v + Math.random(), + // y: this.velocity.y + this.fireDir.y * v + Math.random() + // }); + // // recoil + // this.force.x -= 0.005 * this.fireDir.x * this.mass; + // this.force.y -= 0.005 * this.fireDir.y * this.mass; + // } + // }, + turnToFacePlayer() { + //turn to face player + const dx = player.position.x - this.position.x; + const dy = -player.position.y + this.position.y; + const dist = this.distanceToPlayer(); + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * dx - Math.sin(angle) * dy; + // if (c > 0.04) { + // Matter.Body.rotate(this, 0.01); + // } else if (c < 0.04) { + // Matter.Body.rotate(this, -0.01); + // } + if (c > 0.04 * dist) { + this.torque += 0.002 * this.mass; + } else if (c < 0.04) { + this.torque -= 0.002 * this.mass; + } + }, + facePlayer() { + const unitVector = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + const angle = Math.atan2(unitVector.y, unitVector.x); + Matter.Body.setAngle(this, angle - Math.PI); + }, + explode(mass = this.mass) { + if (m.immuneCycle < m.cycle) { + m.damage(Math.min(Math.max(0.02 * Math.sqrt(mass), 0.01), 0.35) * simulation.dmgScale); + this.isDropPowerUp = false; + this.death(); //death with no power up or body + } + }, + timeLimit() { + this.timeLeft--; + if (this.timeLeft < 0) { + this.isDropPowerUp = false; + this.death(); //death with no power up + } + }, + healthBar() { //draw health by mob //most health bars are drawn in mobs.healthbar(); + if (this.seePlayer.recall) { + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(255,0,0,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + }, + damage(dmg, isBypassShield = false) { + if ((!this.isShielded || isBypassShield) && this.alive) { + if (dmg !== Infinity) { + dmg *= tech.damageFromTech() + //mobs specific damage changes + if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000 + dmg *= this.damageReduction + //energy and heal drain should be calculated after damage boosts + if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon + if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) { + powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal"); + } + dmg /= Math.sqrt(this.mass) + } + this.health -= dmg + //this.fill = this.color + this.health + ')'; + this.onDamage(dmg); //custom damage effects + if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death(); + } + }, + onDamage() { + // a placeholder for custom effects on mob damage + //to use declare custom method in mob spawn + }, + onDeath() { + // a placeholder for custom effects on mob death + // to use declare custom method in mob spawn + }, + damageReduction: 1, + // damageReductionGoal: 0.001, //must add this to boss set up: me.damageReduction = 0.25 + // damageReductionScale: 0.004, //for bosses in this.onDamage determines the impact of dmg on damageReductionGoal + // armor() { //slowly reduce damage reduction, for bosses + // if (this.seePlayer.recall) { + // if (this.damageReductionGoal > 0.24) { + // this.damageReductionGoal = 0.25 + // } else { + // this.damageReductionGoal = this.damageReductionGoal * 0.999 + 0.001 * 0.25 //smooth the goal towards 0.25 damage reduction + // } + // this.damageReduction = this.damageReduction * 0.995 + 0.005 * this.damageReductionGoal //smooth damage reduction towards the goal + // // console.log(`damageReduction = ${this.damageReduction.toFixed(4)}`, `damageReductionGoal = ${this.damageReductionGoal.toFixed(4)}`) + // } + // //draw armor + // //draw body + // ctx.beginPath(); + // const vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) { + // ctx.lineTo(vertices[j].x, vertices[j].y); + // } + // ctx.lineTo(vertices[0].x, vertices[0].y); + // console.log(this.damageReduction, this.damageReductionGoal) + // ctx.lineWidth = 3 //60 * (0.25 - this.damageReductionGoal) + // ctx.strokeStyle = `rgba(255,255,255,${4.1*(0.25 - this.damageReductionGoal)})` //"rgba(150,150,225,0.5)"; + // ctx.stroke(); + // }, + leaveBody: true, + isDropPowerUp: true, + death() { + if (tech.collidePowerUps && Math.random() < tech.collidePowerUps && this.isDropPowerUp) powerUps.randomize(this.position) //needs to run before onDeath spawns power ups + this.onDeath(this); //custom death effects + this.removeConsBB(); + this.alive = false; //triggers mob removal in mob[i].replace(i) + + if (this.isDropPowerUp) { + if (this.isSoonZombie) { //spawn zombie on death + this.leaveBody = false; + let count = 5 //delay spawn cycles + let cycle = () => { + if (count > 0) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + count-- + } + } else { + spawn.zombie(this.position.x, this.position.y, this.radius, this.vertices.length, this.fill) // zombie(x, y, radius, sides, color) + } + } + requestAnimationFrame(cycle); + } + if (tech.iceIXOnDeath && this.isSlowed) { + for (let i = 0, len = 2 * Math.sqrt(Math.min(this.mass, 25)) * tech.iceIXOnDeath; i < len; i++) b.iceIX(3, Math.random() * 2 * Math.PI, this.position) + } + if (tech.deathSpawnsFromBoss || tech.deathSpawns) { + const spawns = tech.deathSpawns + tech.deathSpawnsFromBoss + const len = Math.min(12, spawns * Math.ceil(Math.random() * simulation.difficulty * spawns)) + for (let i = 0; i < len; i++) { + spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + (Math.random() - 0.5) * 10, + y: this.velocity.x + (Math.random() - 0.5) * 10 + }); + } + } + + if (tech.deathSkipTime && !m.isBodiesAsleep) { + requestAnimationFrame(() => { + simulation.timePlayerSkip((this.isBoss ? 45 : 25) * tech.deathSkipTime) + simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations + }); //wrapping in animation frame prevents errors, probably + } + if (tech.isEnergyLoss) m.energy *= 0.8; + powerUps.spawnRandomPowerUp(this.position.x, this.position.y); + m.lastKillCycle = m.cycle; //tracks the last time a kill was made, mostly used in simulation.checks() + mobs.mobDeaths++ + + if (Math.random() < tech.sporesOnDeath) { + const amount = Math.min(25, Math.floor(2 + this.mass * (0.5 + 0.5 * Math.random()))) + if (tech.isSporeFlea) { + const len = amount / 2 + for (let i = 0; i < len; i++) { + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + } + } else if (tech.isSporeWorm) { + const len = amount / 2 + for (let i = 0; i < len; i++) b.worm(this.position) + } else { + for (let i = 0; i < amount; i++) b.spore(this.position) + } + } else if (tech.isExplodeMob) { + b.explosion(this.position, Math.min(700, Math.sqrt(this.mass + 6) * (30 + 60 * Math.random()))) + } else if (tech.nailsDeathMob) { + b.targetedNail(this.position, tech.nailsDeathMob, 39 + 6 * Math.random()) + } + if (tech.isBotSpawnerReset) { + for (let i = 0, len = bullet.length; i < len; i++) { + if (bullet[i].botType && bullet[i].endCycle !== Infinity) bullet[i].endCycle = simulation.cycle + 780 //13 seconds + } + } + if (Math.random() < tech.botSpawner) { + b.randomBot(this.position, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 780 //13 seconds + this.leaveBody = false; // no body since it turned into the bot + } + if (tech.isAddRemoveMaxHealth) { + if (this.isBoss && this.isDropPowerUp) { + powerUps.spawn(this.position.x + 20, this.position.y, "tech", false) + powerUps.spawn(this.position.x - 20, this.position.y, "ammo", false) + powerUps.spawn(this.position.x, this.position.y + 20, "research", false) + powerUps.spawn(this.position.x, this.position.y - 20, "heal", false) + powerUps.spawn(this.position.x - 40, this.position.y, "ammo", false) + powerUps.spawn(this.position.x, this.position.y + 40, "research", false) + powerUps.spawn(this.position.x, this.position.y - 40, "heal", false) + } else { + const amount = 0.005 + if (tech.isEnergyHealth) { + if (m.maxEnergy > amount) { + tech.healMaxEnergyBonus -= amount + m.setMaxEnergy(); + } + } else if (m.maxHealth > amount) { + tech.extraMaxHealth -= amount //decrease max health + m.setMaxHealth(); + } + } + } + if (tech.cloakDuplication && !this.isBoss) { + tech.cloakDuplication -= 0.02 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + } else if (tech.isShieldAmmo && this.shield && !this.isExtraShield) { + let type = tech.isEnergyNoAmmo ? "heal" : "ammo" + if (Math.random() < 0.4) { + type = "heal" + } else if (Math.random() < 0.3 && !tech.isSuperDeterminism) { + type = "research" + } + for (let i = 0, len = Math.ceil(2 * Math.random()); i < len; i++) { + powerUps.spawn(this.position.x, this.position.y, type); + } + } + if (tech.isRadioactive) { + //look for dots and spread them + let dmgTotal = 0 + for (let i = 0, len = this.status.length; i < len; i++) { + if (this.status[i].type === "dot") dmgTotal += this.status[i].dmg * (this.status[i].endCycle - simulation.cycle) + } + if (dmgTotal > 0) { //look for closest mob + let closestRadius = 500; + let closestIndex = null; + for (let i = 0, len = mob.length; i < len; ++i) { + const radius = Vector.magnitude(Vector.sub(this.position, mob[i].position)) + if (mob[i].alive && !mob[i].isShielded && radius < closestRadius) { + closestRadius = radius + closestIndex = i + } + } + if (closestIndex) { + mobs.statusDoT(mob[closestIndex], dmgTotal / 180, 180) + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(mob[closestIndex].position.x, mob[closestIndex].position.y); + ctx.lineWidth = this.radius; + ctx.strokeStyle = "rgba(0,80,80,1)"; + ctx.stroke(); + } + //draw AOE + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: radius, + // color: "rgba(0,80,80,0.03)", + // time: 15 + // }); + } + } + }, + removeConsBB() { + for (let i = 0, len = consBB.length; i < len; ++i) { + if (consBB[i].bodyA === this) { + if (consBB[i].bodyB.shield) { //&& !this.shield + consBB[i].bodyB.do = function () { this.death() } + } + consBB[i].bodyA = consBB[i].bodyB; + consBB.splice(i, 1); + this.removeConsBB(); + break; + } else if (consBB[i].bodyB === this) { + if (consBB[i].bodyA.shield) { + consBB[i].bodyA.do = function () { this.death() } + } + consBB[i].bodyB = consBB[i].bodyA; + consBB.splice(i, 1); + this.removeConsBB(); + break; + } + } + }, + removeCons() { + for (let i = 0, len = cons.length; i < len; ++i) { + if (cons[i].bodyA === this) { + cons[i].bodyA = cons[i].bodyB; + cons.splice(i, 1); + this.removeCons(); + break; + } else if (cons[i].bodyB === this) { + cons[i].bodyB = cons[i].bodyA; + cons.splice(i, 1); + this.removeCons(); + break; + } + } + }, + //replace dead mob with a regular body + replace(i) { + //if there are too many bodies don't turn into blocks to help performance + if (this.leaveBody && body.length < 40 && this.mass < 200 && this.radius > 18) { + let v = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //might help with vertex collision issue, not sure + if (v.length > 5 && body.length < 35 && Math.random() < 0.25) { + const cutPoint = 3 + Math.floor((v.length - 6) * Math.random()) //Math.floor(v.length / 2) + const v2 = v.slice(0, cutPoint + 1) + v = v.slice(cutPoint - 1) + const len = body.length; + body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v2); + Matter.Body.setVelocity(body[len], Vector.mult(this.velocity, 0.5)); + Matter.Body.setAngularVelocity(body[len], this.angularVelocity); + body[len].collisionFilter.category = cat.body; + body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + body[len].classType = "body"; + Composite.add(engine.world, body[len]); //add to world + + const len2 = body.length; + body[len2] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); + Matter.Body.setVelocity(body[len2], Vector.mult(this.velocity, 0.5)); + Matter.Body.setAngularVelocity(body[len2], this.angularVelocity); + body[len2].collisionFilter.category = cat.body; + body[len2].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + body[len2].classType = "body"; + Composite.add(engine.world, body[len2]); //add to world + + //large mobs shrink so they don't block paths + if (body[len].mass + body[len2].mass > 16) { + const massLimit = 8 + 6 * Math.random() + const shrink = function (that1, that2) { + if (that1.mass + that2.mass > massLimit) { + const scale = 0.95; + Matter.Body.scale(that1, scale, scale); + Matter.Body.scale(that2, scale, scale); + setTimeout(shrink, 20, that1, that2); + } + }; + shrink(body[len], body[len2]) + } + } else { + const len = body.length; + body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); + Matter.Body.setVelocity(body[len], Vector.mult(this.velocity, 0.5)); + Matter.Body.setAngularVelocity(body[len], this.angularVelocity); + body[len].collisionFilter.category = cat.body; + body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + body[len].classType = "body"; + Composite.add(engine.world, body[len]); //add to world + + //large mobs shrink so they don't block paths + if (body[len].mass > 9) { + const massLimit = 7 + 4 * Math.random() + const shrink = function (that) { + if (that.mass > massLimit) { + const scale = 0.95; + Matter.Body.scale(that, scale, scale); + setTimeout(shrink, 20, that); + } + }; + shrink(body[len]) + } + } + Matter.Composite.remove(engine.world, this); + mob.splice(i, 1); + if (tech.isMobBlockFling) { + const who = body[body.length - 1] + if (!who.isNotHoldable) { + b.targetedBlock(who) + Matter.Body.setAngularVelocity(who, (0.5 + 0.2 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + // who.torque += who.inertia * 0.002 * (Math.random() - 0.5) + } + } + } else { + Matter.Composite.remove(engine.world, this); + mob.splice(i, 1); + } + } + }); + mob[i].alertRange2 = Math.pow(mob[i].radius * 3 + 550, 2); + Composite.add(engine.world, mob[i]); //add to world + } +}; \ No newline at end of file diff --git a/ngon/js/player.js b/ngon/js/player.js new file mode 100644 index 00000000..20453712 --- /dev/null +++ b/ngon/js/player.js @@ -0,0 +1,5172 @@ +//global player variables for use in matter.js physics +let player, jumpSensor, playerBody, playerHead, headSensor; + +// player Object Prototype ********************************************* +const m = { + spawn() { + //load player in matter.js physic engine + // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //player as a series of vertices + let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //player as a series of vertices + playerBody = Bodies.fromVertices(0, 0, vertices); + jumpSensor = Bodies.rectangle(0, 46, 36, 6, { + //this sensor check if the player is on the ground to enable jumping + sleepThreshold: 99999999999, + isSensor: true + }); + vertices = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); + playerHead = Bodies.fromVertices(0, -55, vertices); //this part of the player lowers on crouch + headSensor = Bodies.rectangle(0, -57, 48, 45, { + //senses if the player's head is empty and can return after crouching + sleepThreshold: 99999999999, + isSensor: true + }); + player = Body.create({ + //combine jumpSensor and playerBody + parts: [playerBody, playerHead, jumpSensor, headSensor], + inertia: Infinity, //prevents player rotation + friction: 0.002, + frictionAir: 0.001, + //frictionStatic: 0.5, + restitution: 0, + sleepThreshold: Infinity, + collisionFilter: { + group: 0, + category: cat.player, + mask: cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield + }, + // death() { + // m.death(); + // } + }); + Matter.Body.setMass(player, m.mass); + Composite.add(engine.world, [player]); + }, + cycle: 600, //starts at 600 cycles instead of 0 to prevent bugs with m.history + lastKillCycle: 0, + lastHarmCycle: 0, + width: 50, + radius: 30, + eyeFillColor: null, + fillColor: null, //set by setFillColors + fillColorDark: null, //set by setFillColors + bodyGradient: null, //set by setFillColors + color: { + hue: 0, + sat: 0, + light: 100, + }, + setFillColors() { + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 25}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(1, m.fillColor); + m.bodyGradient = grd + }, + // setFillColorsAlpha(alpha = 0.5) { + // m.fillColor = `hsla(${m.color.hue},${m.color.sat}%,${m.color.light}%,${alpha})` + // m.fillColorDark = `hsla(${m.color.hue},${m.color.sat}%,${m.color.light - 25}%,${alpha})` + // let grd = ctx.createLinearGradient(-30, 0, 30, 0); + // grd.addColorStop(0, m.fillColorDark); + // grd.addColorStop(1, m.fillColor); + // m.bodyGradient = grd + // }, + height: 42, + yOffWhen: { + crouch: 22, + stand: 49, + jump: 70 + }, + defaultMass: 5, + mass: 5, + FxNotHolding: 0.015, + Fx: 0.016, //run Force on ground // + jumpForce: 0.42, + setMovement() { + // m.Fx = 0.08 / mass * tech.squirrelFx + // m.FxAir = 0.4 / mass / mass + m.Fx = tech.baseFx * m.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / player.mass //base player mass is 5 + m.jumpForce = tech.baseJumpForce * m.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / player.mass / player.mass //base player mass is 5 + }, + FxAir: 0.016, // 0.4/5/5 run Force in Air + yOff: 70, + yOffGoal: 70, + onGround: false, //checks if on ground or in air + lastOnGroundCycle: 0, //use to calculate coyote time + standingOn: undefined, + numTouching: 0, + crouch: false, + // isHeadClear: true, + spawnPos: { + x: 0, + y: 0 + }, + spawnVel: { + x: 0, + y: 0 + }, + pos: { + x: 0, + y: 0 + }, + yPosDifference: 24.2859, //player.position.y - m.pos.y //24.285923217549026 + // yPosDifferenceCrouched: -2.7140767824453604, + Sy: 0, //adds a smoothing effect to vertical only + Vx: 0, + Vy: 0, + friction: { + ground: 0.01, + air: 0.0025 + }, + airSpeedLimit: 125, // 125/mass/mass = 5 + angle: 0, + walk_cycle: 0, + stepSize: 0, + flipLegs: -1, + hip: { + x: 12, + y: 24 + }, + knee: { + x: 0, + y: 0, + x2: 0, + y2: 0 + }, + foot: { + x: 0, + y: 0 + }, + legLength1: 55, + legLength2: 45, + transX: 0, + transY: 0, + history: new Array(600), //[], //tracks the last second of player position + rewindCount: 0, //used with CPT + resetHistory() { + const set = { + position: { + x: player.position.x, + y: player.position.y, + }, + velocity: { + x: player.velocity.x, + y: player.velocity.y + }, + yOff: m.yOff, + angle: m.angle, + health: m.health, + energy: m.energy, + activeGun: b.activeGun + } + for (let i = 0; i < 600; i++) { //reset history + m.history[i] = set + } + }, + move() { + m.pos.x = player.position.x; + m.pos.y = playerBody.position.y - m.yOff; + m.Vx = player.velocity.x; + m.Vy = player.velocity.y; + + //tracks the last 10s of player information + m.history.splice(m.cycle % 600, 1, { + position: { + x: player.position.x, + y: player.position.y, + }, + velocity: { + x: player.velocity.x, + y: player.velocity.y + }, + yOff: m.yOff, + angle: m.angle, + health: m.health, + energy: m.energy, + activeGun: b.activeGun + }); + // const back = 59 // 59 looks at 1 second ago //29 looks at 1/2 a second ago + // historyIndex = (m.cycle - back) % 600 + }, + transSmoothX: 0, + transSmoothY: 0, + lastGroundedPositionY: 0, + // mouseZoom: 0, + lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard + look() { }, //set to lookDefault() + lookDefault() { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + }, + doCrouch() { + if (!m.crouch) { + m.crouch = true; + m.yOffGoal = m.yOffWhen.crouch; + if ((playerHead.position.y - player.position.y) < 0) { + Matter.Body.setPosition(playerHead, { + x: player.position.x, + y: player.position.y + 9.1740767 + }) + } + } + }, + undoCrouch() { + if (m.crouch) { + m.crouch = false; + m.yOffGoal = m.yOffWhen.stand; + if ((playerHead.position.y - player.position.y) > 0) { + Matter.Body.setPosition(playerHead, { + x: player.position.x, + y: player.position.y - 30.28592321 + }) + } + } + }, + hardLandCD: 0, + checkHeadClear() { + if (Matter.Query.collides(headSensor, map).length > 0) { + return false + } else { + return true + } + }, + buttonCD_jump: 0, //cool down for player buttons + jump() { + // if (!m.onGround) m.lastOnGroundCycle = 0 //m.cycle - tech.coyoteTime + m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass + //apply a fraction of the jump force to the body the player is jumping off of + Matter.Body.applyForce(m.standingOn, m.pos, { + x: 0, + y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) + }); + + player.force.y = -m.jumpForce; //player jump force + Matter.Body.setVelocity(player, { //zero player y-velocity for consistent jumps + x: player.velocity.x, + y: Math.max(-10, Math.min(m.standingOn.velocity.y, 10)) //cap velocity contribution from blocks you are standing on to 10 in the vertical + }); + }, + moverX: 0, //used to tell the player about moving platform x velocity + groundControl() { + //check for crouch or jump + if (m.crouch) { + if (!(input.down) && m.checkHeadClear() && m.hardLandCD < m.cycle) m.undoCrouch(); + } else if (input.down || m.hardLandCD > m.cycle) { + m.doCrouch(); //on ground && not crouched and pressing s or down + } else if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23) { + m.jump() + } + const moveX = player.velocity.x - m.moverX //account for mover platforms + if (input.left) { + if (moveX > -2) { + player.force.x -= m.Fx * 1.5 + } else { + player.force.x -= m.Fx + } + // } + } else if (input.right) { + if (moveX < 2) { + player.force.x += m.Fx * 1.5 + } else { + player.force.x += m.Fx + } + } else { + const stoppingFriction = 0.92; //come to a stop if no move key is pressed + Matter.Body.setVelocity(player, { x: m.moverX * 0.08 + player.velocity.x * stoppingFriction, y: player.velocity.y * stoppingFriction }); + } + + if (Math.abs(moveX) > 4) { //come to a stop if fast // if (player.speed > 4) { //come to a stop if fast + const stoppingFriction = (m.crouch) ? 0.65 : 0.89; // this controls speed when crouched + Matter.Body.setVelocity(player, { x: m.moverX * (1 - stoppingFriction) + player.velocity.x * stoppingFriction, y: player.velocity.y * stoppingFriction }); + } + m.moverX = 0 //reset the level mover offset + }, + airControl() { + //check for coyote time jump + // if (input.up && m.buttonCD_jump + 20 + tech.coyoteTime < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + tech.coyoteTime > m.cycle) m.jump() + if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + 5 > m.cycle) m.jump() + + //check for short jumps //moving up //recently pressed jump //but not pressing jump key now + if (m.buttonCD_jump + 60 > m.cycle && !(input.up) && m.Vy < 0) { + Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y * 0.94 }); //reduce player y-velocity every cycle + } + + if (input.left) { + if (player.velocity.x > -m.airSpeedLimit / player.mass / player.mass) player.force.x -= m.FxAir; // move player left / a + } else if (input.right) { + if (player.velocity.x < m.airSpeedLimit / player.mass / player.mass) player.force.x += m.FxAir; //move player right / d + } + }, + printBlock() { + const sides = Math.floor(4 + 6 * Math.random() * Math.random()) + body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y, sides, 8, { + friction: 0.05, + frictionAir: 0.001, + collisionFilter: { category: 0, mask: 0 }, //no collision because player is holding + classType: "body", + isPrinted: true, + radius: 10, //used to grow and warp the shape of the block + density: 0.002, //double density for 2x damage + }); + const who = body[body.length - 1] + Composite.add(engine.world, who); //add to world + m.throwCharge = 4; + m.holdingTarget = who + m.isHolding = true; + }, + alive: false, + switchWorlds() { + powerUps.boost.endCycle = 0 + const totalGuns = b.inventory.length + //track ammo/ ammoPack count + let ammoCount = 0 + for (let i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].ammo !== Infinity) { + ammoCount += b.guns[b.inventory[i]].ammo / b.guns[b.inventory[i]].ammoPack + } else { + ammoCount += 5 + } + } + + simulation.isTextLogOpen = false; //prevent console spam + //remove all tech and count current tech total + let totalTech = 0; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 + if (tech.tech[i].count > 0 && !tech.tech[i].isLore) { + if (tech.tech[i].frequencyDefault) { + tech.tech[i].frequency = tech.tech[i].frequencyDefault + } else { + tech.tech[i].frequency = 1 + } + if ( + !tech.tech[i].isNonRefundable && + !tech.tech[i].isFromAppliedScience && + !tech.tech[i].isAltRealityTech + ) { + totalTech += tech.tech[i].count + tech.tech[i].remove(); + tech.tech[i].isLost = false + tech.tech[i].count = 0 + } + } + } + // lore.techCount = 0; + // tech.removeLoreTechFromPool(); + // tech.addLoreTechToPool(); + // tech.removeJunkTechFromPool(); + tech.duplication = 0; + tech.extraMaxHealth = 0; + tech.totalCount = 0; + tech.countJunkTech(); + const randomBotCount = b.totalBots() + b.zeroBotCount() + //remove all bullets, respawn bots + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; + + //randomize health + m.health = m.health * (1 + 0.5 * (Math.random() - 0.5)) + if (m.health > 1) m.health = 1; + m.displayHealth(); + //randomize field + m.setField(Math.ceil(Math.random() * (m.fieldUpgrades.length - 1))) + //removes guns and ammo + b.inventory = []; + b.activeGun = null; + b.inventoryGun = 0; + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].have = false; + if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; + } + //give random guns + for (let i = 0; i < totalGuns; i++) b.giveGuns() + + //randomize ammo based on ammo/ammoPack count + for (let i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].ammo !== Infinity) b.guns[b.inventory[i]].ammo = Math.max(0, Math.floor(ammoCount / b.inventory.length * b.guns[b.inventory[i]].ammoPack * (1.15 + 0.3 * (Math.random() - 0.5)))) + } + console.log(b.activeGun) + //randomize tech + for (let i = 0; i < totalTech; i++) { + //find what tech I could get + let options = []; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBadRandomOption && !tech.tech[i].isLore && !tech.tech[i].isJunk) { + for (let j = 0; j < tech.tech[i].frequency; j++) options.push(i); + } + } + //add a new tech from options pool + if (options.length > 0) tech.giveTech(options[Math.floor(Math.random() * options.length)]) + } + b.respawnBots(); + for (let i = 0; i < randomBotCount; i++) b.randomBot() + simulation.makeGunHUD(); //update gun HUD + simulation.updateTechHUD(); + simulation.isTextLogOpen = true; + m.drop(); + if (simulation.paused) build.pauseGrid() //update the build when paused + }, + dmgScale: null, //scales all damage, but not raw .dmg //set in levels.setDifficulty + death() { + if (tech.isImmortal) { //if player has the immortality buff, spawn on the same level with randomized damage + //remove immortality tech + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].name === "quantum immortality") tech.removeTech(i) + // } + + m.setMaxHealth() + m.health = 1; + // m.addHealth(1) + + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = "rgba(255,255,255,0)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + spawn.setSpawnList(); //new mob types + simulation.clearNow = true; //triggers a map reset + + m.switchWorlds() + const swapPeriod = 1000 + for (let i = 0, len = 5; i < len; i++) { + setTimeout(function () { + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = "rgba(255,255,255,0)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + spawn.setSpawnList(); //new mob types + simulation.clearNow = true; //triggers a map reset + m.switchWorlds() + simulation.isTextLogOpen = true; + simulation.makeTextLog(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); + simulation.isTextLogOpen = false; + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = `rgba(255,255,255,${(i + 1) * (i + 1) * 0.006})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + }, (i + 1) * swapPeriod); + } + setTimeout(function () { + simulation.wipe = function () { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + simulation.isTextLogOpen = true; + simulation.makeTextLog("simulation.amplitude = null"); + tech.isImmortal = false //disable future immortality + }, 6 * swapPeriod); + } else if (m.alive) { //normal death code here + m.storeTech() + m.alive = false; + simulation.paused = true; + m.health = 0; + simulation.ephemera = [] + document.getElementById("defense-bar").style.display = "none"; //hide defense + document.getElementById("damage-bar").style.display = "none" + m.displayHealth(); + document.getElementById("text-log").style.display = "none" + document.getElementById("fade-out").style.opacity = 0.9; //slowly fade to 90% white on top of canvas + // build.shareURL(false) + setTimeout(function () { + Composite.clear(engine.world); + Engine.clear(engine); + simulation.splashReturn(); + }, 5000); + } + }, + storeTech() { //store a copy of your tech, it will show up at your location next run in the entanglement power up + if (localSettings.isAllowed && !simulation.isCheating) { + const gunList = [] //store gun names + for (i = 0, len = b.inventory.length; i < len; i++) gunList.push(b.inventory[i]) + const techList = [] //store tech names + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) techList.push(i) + } + if (techList.length) { + localSettings.entanglement = { + fieldIndex: m.fieldMode, + gunIndexes: gunList, + techIndexes: techList, + position: { + x: m.pos.x, + y: m.pos.y + }, + levelName: level.levels[level.onLevel], + isHorizontalFlipped: simulation.isHorizontalFlipped + } + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + } + }, + health: 0, + maxHealth: 1, //set in simulation.reset() + drawHealth() { + if (m.health < 1) { + ctx.fillStyle = "rgba(100, 100, 100, 0.5)"; + ctx.fillRect(m.pos.x - m.radius, m.pos.y - 50, 60, 10); + ctx.fillStyle = "#f00"; + ctx.fillRect( + m.pos.x - m.radius, + m.pos.y - 50, + 60 * m.health, + 10 + ); + } + }, + displayHealth() { + id = document.getElementById("health"); + // health display is a x^1.5 rule to make it seem like the player has lower health, this makes the player feel more excitement + id.style.width = Math.floor(300 * m.maxHealth * Math.pow(m.health / m.maxHealth, 1.4)) + "px"; + //css animation blink if health is low + // if (m.health < 0.3) { + // id.classList.add("low-health"); + // } else { + // id.classList.remove("low-health"); + // } + }, + addHealth(heal) { + if (!tech.isEnergyHealth) { + m.health += heal * simulation.healScale; + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + } + }, + baseHealth: 1, + setMaxHealth() { + m.maxHealth = m.baseHealth + tech.extraMaxHealth + 2.22 * tech.isFallingDamage + 4 * tech.isFlipFlop * tech.isFlipFlopOn * tech.isFlipFlopHealth + document.getElementById("health-bg").style.width = `${Math.floor(300 * m.maxHealth)}px` + simulation.makeTextLog(`m.maxHealth = ${m.maxHealth.toFixed(2)}`) + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + }, + + defaultFPSCycle: 0, //tracks when to return to normal fps + immuneCycle: 0, //used in engine + lastCalculatedDamage: 0, //used to decided if damage bar needs to be redrawn (in simulation.checks) + lastCalculatedDefense: 0, //used to decided if defense bar needs to be redrawn (in simulation.checks) + defense() { + let dmg = 1 + dmg *= m.fieldHarmReduction + // if (!tech.isFlipFlopOn && tech.isFlipFlopHealth) dmg *= 0.5 + // 1.25 + Math.sin(m.cycle * 0.01) + if (tech.isDiaphragm) dmg *= 0.56 + 0.36 * Math.sin(m.cycle * 0.0075); + if (tech.isZeno) dmg *= 0.15 + if (tech.isFieldHarmReduction) dmg *= 0.5 + if (tech.isHarmMACHO) dmg *= 0.4 + if (tech.isImmortal) dmg *= 0.7 + if (tech.energyRegen === 0) dmg *= 0.34 + // if (tech.healthDrain) dmg *= 1 + 3.33 * tech.healthDrain //tech.healthDrain = 0.03 at one stack //cause more damage + if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling + if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8 + if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.33 + if (tech.squirrelFx !== 1) dmg *= 0.78//Math.pow(0.78, (tech.squirrelFx - 1) / 0.4) + if (tech.isAddBlockMass && m.isHolding) dmg *= 0.1 + if (tech.isSpeedHarm && player.speed > 0.1) dmg *= 1 - Math.min(player.speed * 0.0165, 0.66) + if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.25 + if (tech.isNeutronium && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.1 + if (tech.isBotArmor) dmg *= 0.94 ** b.totalBots() + if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33; + if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.3 + if (tech.isTurret && m.crouch) dmg *= 0.34; + if (tech.isFirstDer && b.inventory[0] === b.activeGun) dmg *= 0.85 ** b.inventory.length + if (tech.isEnergyHealth) { + return Math.pow(dmg, 0.19) //defense has less effect + } else { + return dmg + } + }, + rewind(steps) { // m.rewind(Math.floor(Math.min(599, 137 * m.energy))) + if (tech.isRewindGrenade) { + const immunityDuration = 65 + const immunityCycle = m.cycle + immunityDuration + 10 + tech.isPetalsExplode * 30 + tech.isCircleExplode * 21 + if (m.immuneCycle < immunityCycle) m.immuneCycle = immunityCycle; //player is immune to damage until after grenades might explode... + + for (let i = 1, len = Math.floor(4 + steps / 40); i < len; i++) { + b.grenade(Vector.add(m.pos, { + x: 10 * (Math.random() - 0.5), + y: 10 * (Math.random() - 0.5) + }), -i * Math.PI / len) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + + if (tech.isNeutronBomb) { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.3, + y: who.velocity.y * 0.3 + }); + } else if (tech.isVacuumBomb) { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.5, + y: who.velocity.y * 0.5 + }); + who.endCycle = simulation.cycle + immunityDuration + + } else if (tech.isRPG) { + who.endCycle = simulation.cycle + 10 + } else { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.5, + y: who.velocity.y * 0.5 + }); + who.endCycle = simulation.cycle + immunityDuration + } + } + } + + let history = m.history[(m.cycle - steps) % 600] + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { + x: history.velocity.x, + y: history.velocity.y + }); + m.yOff = history.yOff + if (m.yOff < 48) { + m.doCrouch() + } else { + m.undoCrouch() + } + + // b.activeGun = history.activeGun + // for (let i = 0; i < b.inventory.length; i++) { + // if (b.inventory[i] === b.activeGun) b.inventoryGun = i + // } + // simulation.updateGunHUD(); + // simulation.boldActiveGunHUD(); + + // move bots to player's new position + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.energy = Math.max(m.energy - steps / 330, 0.01) + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + + let isDrawPlayer = true + const shortPause = function () { + if (m.defaultFPSCycle < m.cycle) { //back to default values + simulation.fpsCap = simulation.fpsCapDefault + simulation.fpsInterval = 1000 / simulation.fpsCap; + document.getElementById("dmg").style.transition = "opacity 1s"; + document.getElementById("dmg").style.opacity = "0"; + } else { + requestAnimationFrame(shortPause); + if (isDrawPlayer) { + isDrawPlayer = false + ctx.save(); + ctx.globalCompositeOperation = "lighter"; + ctx.translate(canvas.width2, canvas.height2); //center + ctx.scale(simulation.zoom / simulation.edgeZoomOutSmooth, simulation.zoom / simulation.edgeZoomOutSmooth); //zoom in once centered + ctx.translate(-canvas.width2 + m.transX, -canvas.height2 + m.transY); //translate + for (let i = 1; i < steps; i++) { + history = m.history[(m.cycle - i) % 600] + m.pos.x = history.position.x + m.pos.y = history.position.y + m.yPosDifference - history.yOff + m.yOff = history.yOff + m.draw(); + } + ctx.restore(); + m.resetHistory() + } + } + }; + + if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + simulation.fpsCap = 3 //1 is longest pause, 4 is standard + simulation.fpsInterval = 1000 / simulation.fpsCap; + m.defaultFPSCycle = m.cycle + if (tech.isRewindBot) { + const len = steps * 0.05 * tech.isRewindBot + const botStep = Math.floor(steps / len) + for (let i = 0; i < len; i++) { + const where = m.history[Math.abs(m.cycle - i * botStep) % 600].position //spread out spawn locations along past history + b.randomBot({ + x: where.x + 20 * (Math.random() - 0.5), + y: where.y + 20 * (Math.random() - 0.5) + }, false, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 440 + Math.floor(120 * Math.random()) //8-10 seconds + } + } + }, + collisionImmuneCycles: 30, + damage(dmg) { + if (tech.isRewindAvoidDeath && (m.energy + 0.05) > Math.min(0.95, m.maxEnergy) && dmg > 0.01) { + const steps = Math.floor(Math.min(299, 150 * m.energy)) + simulation.makeTextLog(`m.rewind(${steps})`) + m.rewind(steps) + return + } + m.lastHarmCycle = m.cycle + if (tech.isDroneOnDamage && bullet.length < 150) { //chance to build a drone on damage from tech + const len = Math.min((dmg - 0.06 * Math.random()) * 40, 40) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1) + for (let i = 0; i < len; i++) { + if (Math.random() < 0.5) b.drone({ + x: m.pos.x + 30 * Math.cos(m.angle) + 100 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 100 * (Math.random() - 0.5) + }) //spawn drone + } + } + if (tech.isEnergyHealth) { + m.energy -= 0.9 * dmg / Math.sqrt(simulation.healScale) //scale damage with heal reduction difficulty + if (m.energy < 0 || isNaN(m.energy)) { //taking deadly damage + if (tech.isDeathAvoid && powerUps.research.count && !tech.isDeathAvoidedThisLevel) { + tech.isDeathAvoidedThisLevel = true + powerUps.research.changeRerolls(-1) + simulation.makeTextLog(`m.research--
${powerUps.research.count}`) + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); + m.energy = m.maxEnergy + if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = "rgba(255,255,255,0.03)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + setTimeout(function () { + tech.maxDuplicationEvent() + simulation.wipe = function () { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + }, 3000); + } else { //death + m.health = 0; + m.energy = 0; + m.death(); + } + return; + } + } else { + dmg *= m.defense() + m.health -= dmg; + if (m.health < 0 || isNaN(m.health)) { + if (tech.isDeathAvoid && powerUps.research.count > 0 && !tech.isDeathAvoidedThisLevel) { //&& Math.random() < 0.5 + tech.isDeathAvoidedThisLevel = true + m.health = 0.05 + powerUps.research.changeRerolls(-1) + simulation.makeTextLog(`m.research-- +
${powerUps.research.count}`) + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); + if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = "rgba(255,255,255,0.03)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + setTimeout(function () { + tech.maxDuplicationEvent() + simulation.wipe = function () { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + }, 3000); + } else { + m.health = 0; + m.displayHealth(); + m.death(); + return; + } + } + m.displayHealth(); + document.getElementById("dmg").style.transition = "opacity 0s"; + document.getElementById("dmg").style.opacity = 0.1 + Math.min(0.6, dmg * 4); + } + if (dmg > 0.03) { + m.lastHit = dmg; + if (dmg > 0.06 / m.holdingMassScale) m.drop(); //drop block if holding // m.holdingMassScale = 0.5 for most fields + if (m.isCloak) m.fireCDcycle = m.cycle //forced exit cloak + } + const normalFPS = function () { + if (m.defaultFPSCycle < m.cycle) { //back to default values + simulation.fpsCap = simulation.fpsCapDefault + simulation.fpsInterval = 1000 / simulation.fpsCap; + document.getElementById("dmg").style.transition = "opacity 1s"; + document.getElementById("dmg").style.opacity = "0"; + } else { + requestAnimationFrame(normalFPS); + } + }; + + if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(normalFPS); + if (dmg > 0.05) { // freeze game for high damage hits + simulation.fpsCap = 4 //40 - Math.min(25, 100 * dmg) + simulation.fpsInterval = 1000 / simulation.fpsCap; + if (tech.isHarmFreeze) { + for (let i = 0, len = mob.length; i < len; i++) mobs.statusSlow(mob[i], 480) //freeze all mobs + } + } else { + simulation.fpsCap = simulation.fpsCapDefault + simulation.fpsInterval = 1000 / simulation.fpsCap; + } + m.defaultFPSCycle = m.cycle + // if (tech.isSlowFPS) { // slow game + // simulation.fpsCap = 30 //new fps + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // //how long to wait to return to normal fps + // m.defaultFPSCycle = m.cycle + 20 + Math.min(90, Math.floor(200 * dmg)) + // if (tech.isHarmFreeze) { //freeze all mobs + // for (let i = 0, len = mob.length; i < len; i++) { + // mobs.statusSlow(mob[i], 450) + // } + // } + // } else { + // if (dmg > 0.05) { // freeze game for high damage hits + // simulation.fpsCap = 4 //40 - Math.min(25, 100 * dmg) + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // } else { + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // } + // m.defaultFPSCycle = m.cycle + // } + // if (!noTransition) { + // document.getElementById("health").style.transition = "width 0s ease-out" + // } else { + // document.getElementById("health").style.transition = "width 1s ease-out" + // } + }, + buttonCD: 0, //cool down for player buttons + // ********************************************* + // ****** drawing player and skins ************* + // ********************************************* + drawLeg(stroke) { }, + calcLeg(cycle_offset, offset) { + m.hip.x = 12 + offset; + m.hip.y = 24 + offset; + //stepSize goes to zero if Vx is zero or not on ground (make m transition cleaner) + m.stepSize = 0.8 * m.stepSize + 0.2 * (7 * Math.sqrt(Math.min(9, Math.abs(m.Vx))) * m.onGround); + //changes to stepsize are smoothed by adding only a percent of the new value each cycle + const stepAngle = 0.034 * m.walk_cycle + cycle_offset; + m.foot.x = 2.2 * m.stepSize * Math.cos(stepAngle) + offset; + m.foot.y = offset + 1.2 * m.stepSize * Math.sin(stepAngle) + m.yOff + m.height; + const Ymax = m.yOff + m.height; + if (m.foot.y > Ymax) m.foot.y = Ymax; + + //calculate knee position as intersection of circle from hip and foot + const d = Math.sqrt((m.hip.x - m.foot.x) * (m.hip.x - m.foot.x) + (m.hip.y - m.foot.y) * (m.hip.y - m.foot.y)); + const l = (m.legLength1 * m.legLength1 - m.legLength2 * m.legLength2 + d * d) / (2 * d); + const h = Math.sqrt(m.legLength1 * m.legLength1 - l * l); + m.knee.x = (l / d) * (m.foot.x - m.hip.x) - (h / d) * (m.foot.y - m.hip.y) + m.hip.x + offset; + m.knee.y = (l / d) * (m.foot.y - m.hip.y) + (h / d) * (m.foot.x - m.hip.x) + m.hip.y; + }, + draw() { }, + isAltSkin: false, + resetSkin() { + simulation.isAutoZoom = true; + m.yOffWhen.jump = 70 + m.yOffWhen.stand = 49 + m.yOffWhen.crouch = 22 + m.isAltSkin = false + m.color = { + hue: 0, + sat: 0, + light: 100, + } + m.setFillColors(); + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 7, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 6, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = m.fillColor; + ctx.fill(); + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + } + }, + skin: { + none() { + m.isAltSkin = true + }, + favicon() { //used to render the favicon, not actually in game + m.yOffWhen.jump = 70 + m.yOffWhen.stand = 49 + m.yOffWhen.crouch = 22 + m.isAltSkin = false + m.color = { + hue: 0, + sat: 0, + light: 100, + } + + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 10}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(1, m.fillColor); + m.bodyGradient = grd + + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + // m.calcLeg(Math.PI, -3); + // m.drawLeg("#4a4a4a"); + // m.calcLeg(0, 0); + // m.drawLeg("#333"); + // ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(12, 0, 4.5, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 4.5; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + }, + mech() { + m.isAltSkin = true + m.yOffWhen.stand = 52 + m.yOffWhen.jump = 72 + // m.yOffWhen.crouch = 22 + // m.color = { + // hue: 184, + // sat: 0, + // light: 55, + // } + // m.setFillColors(); + m.draw = function () { + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -1.25); + m.drawLeg("#606060"); + m.calcLeg(0, 0); + m.drawLeg("#444"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + const hip = { x: m.hip.x - 5, y: m.hip.y + 5 } + const sub = Vector.sub(m.knee, hip) + const off = Vector.mult(Vector.rotate(Vector.normalise(sub), Math.PI / 2), 8) + const kneeBraceHigh = Vector.add(hip, off) + const kneeBraceLow = Vector.add(kneeBraceHigh, Vector.mult(sub, 0.9)) + const foot = { x: m.foot.x - 10, y: m.foot.y - 15 } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(hip.x, hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(foot.x, foot.y); + //extra upper leg brace + ctx.moveTo(kneeBraceHigh.x, kneeBraceHigh.y); + ctx.lineTo(kneeBraceLow.x, kneeBraceLow.y); + ctx.lineTo(m.knee.x, m.knee.y); + + ctx.strokeStyle = stroke; + ctx.lineWidth = 3; + ctx.stroke(); + //foot + ctx.beginPath(); + ctx.moveTo(foot.x, foot.y); + ctx.quadraticCurveTo(m.foot.x - 30, m.foot.y + 12, m.foot.x + 13, m.foot.y + 3); + ctx.lineWidth = 1.5; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y - 1, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 3, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 3, 0, 2 * Math.PI); + //knee brace + // ctx.moveTo(kneeBraceHigh.x + 4, kneeBraceHigh.y); + // ctx.arc(kneeBraceHigh.x, kneeBraceHigh.y, 4, 0, 2 * Math.PI); + ctx.moveTo(kneeBraceLow.x + 2.5, kneeBraceLow.y); + ctx.arc(kneeBraceLow.x, kneeBraceLow.y, 2.5, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(foot.x + 2.5, foot.y); + ctx.arc(foot.x, foot.y, 2.5, 0, 2 * Math.PI); + ctx.fillStyle = m.fillColor; + ctx.fill(); + ctx.lineWidth = 1; + // ctx.strokeStyle = "#333" + ctx.stroke(); + ctx.restore(); + } + }, + energy() { + m.isAltSkin = true + m.color = { + hue: 184, + sat: 100, + light: 85, + } + m.setFillColors(); + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#456"); + m.calcLeg(0, 0); + m.drawLeg("#345"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(0,255,255,0.22)"; + ctx.lineWidth = 12; + ctx.stroke(); + ctx.fillStyle = 'hsl(184,100%,85%)' //m.fillColor; //"#9ff" //m.bodyGradient + ctx.fill(); + + ctx.beginPath(); + ctx.arc(17, 0, 5.5, 0, 2 * Math.PI); + ctx.fillStyle = "#357" + ctx.fill(); + + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 7, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 6, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(0,255,255,0.25)"; + ctx.lineWidth = 5; + ctx.stroke(); + ctx.fillStyle = m.fillColor; + ctx.fill(); + ctx.restore(); + } + }, + tungsten() { + m.isAltSkin = true + m.color = { + hue: 210, + sat: 5, + light: 75, + } + // m.setFillColors(); + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 50}%)` + const grd = ctx.createLinearGradient(-30, -5, 30, 10); + grd.addColorStop(0, `#e0e0e0`); + grd.addColorStop(0.3, `#bbb`); + grd.addColorStop(0.4, `#b3b3b3`); + grd.addColorStop(0.5, `#c5c5c5`); + grd.addColorStop(0.65, `#bbb`); + grd.addColorStop(0.7, `#b3b3b3`); + grd.addColorStop(0.75, `#bbb`); + grd.addColorStop(1, `#e0e0e0`); + // const grdRad = ctx.createRadialGradient(0, 0, 0, 0, 0, 30); + // grdRad.addColorStop(0, `rgba(0,0,0,0.3)`); + // grdRad.addColorStop(0.5, `rgba(210,210,210,0)`); + m.bodyGradient = grd + + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(4.2, -3); + m.drawLeg("#666"); + m.calcLeg(2.1, -1); + m.drawLeg("#5f5f5f"); + m.calcLeg(0, 1); + m.drawLeg("#555"); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + // ctx.fillStyle = grdRad + // ctx.fill(); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.fillStyle = "#fff" + ctx.fill(); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 4.5; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y - 1); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineTo(m.foot.x, m.foot.y - 1); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y - 4, 12, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 6, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 6, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 5, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 5, 0, 2 * Math.PI); + ctx.fillStyle = m.fillColor; + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = "#000" + ctx.stroke(); + ctx.restore(); + } + + }, + anodize() { + m.isAltSkin = true + m.color = { + hue: 210, + sat: 14, + light: 65, + } + // m.setFillColors(); + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 50}%)` + // let grd = ctx.createLinearGradient(-30, 0, 30, 0); + const grd = ctx.createRadialGradient(16, 0, 0, 0, 0, 40); + + // grd.addColorStop(0, `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)`); + // grd.addColorStop(0.25, `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 20}%)`); + // grd.addColorStop(0.5, `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)`); + // grd.addColorStop(1, `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 10}%)`); + + grd.addColorStop(0, `#c78034`); + grd.addColorStop(0.04, `#bd5235`); + grd.addColorStop(0.08, `#ab554d`); + grd.addColorStop(0.12, `#8f5d8f`); + grd.addColorStop(0.16, `#4352ab`); + grd.addColorStop(0.2, `#2058b3`); + grd.addColorStop(0.24, `#1a6fc4`); + grd.addColorStop(0.28, `#1b85cf`); + grd.addColorStop(0.32, `#2d9bd7`); + grd.addColorStop(0.4, `#d2d7b4`); + grd.addColorStop(0.44, `#e1cd87`); + grd.addColorStop(0.48, `#f0b955`); + grd.addColorStop(0.52, `#ffa050`); + grd.addColorStop(0.56, `#ff8269`); + grd.addColorStop(0.6, `#f5697d`); + grd.addColorStop(0.64, `#e65aaf`); + grd.addColorStop(0.68, `#d732d7`); + grd.addColorStop(0.72, `#c846e6`); + grd.addColorStop(0.76, `#c850fa`); + grd.addColorStop(0.8, `#878cf0`); + grd.addColorStop(0.84, `#37beeb`); + grd.addColorStop(0.88, `#00d2be`); + grd.addColorStop(0.92, `#00e19b`); + grd.addColorStop(0.96, `#19f5aa`); + grd.addColorStop(1, `#aaf5af`); + + m.bodyGradient = grd + + + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a5a"); + m.calcLeg(0, 0); + m.drawLeg("#445"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + // ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#222"; + ctx.lineWidth = 2; + // ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + ctx.fillStyle = "#1b85cf"; + ctx.fill(); + //knee joint + ctx.beginPath(); + ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + ctx.fillStyle = "#ffa050"; + ctx.fill(); + //foot joint + ctx.beginPath(); + ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#878cf0"; + ctx.fill(); + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + } + }, + dilate() { + m.isAltSkin = true + simulation.isAutoZoom = false; + m.draw = function () { + const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075) + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#456"); + m.calcLeg(0, 0); + m.drawLeg("#345"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#345"; + ctx.lineWidth = 2; + ctx.arc(12, 0, amplitude, 0, 2 * Math.PI); //big eye + ctx.stroke(); + ctx.beginPath(); + ctx.arc(12, 0, amplitude, 0, 2 * Math.PI); //big eye + // ctx.fillStyle = `hsl(0,0%,${50+50*Math.sin(m.cycle * 0.0075+Math.PI)}%)` //`hsl(${150+50*Math.sin(m.cycle * 0.0075)},100%,50%)` + // ctx.fill(); + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + + //zoom camera in and out + + // console.log(simulation.zoomScale) + simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075)) + + + } + }, + dilate2() { + m.isAltSkin = true + m.draw = function () { + const amplitude = Math.sin(m.cycle * 0.0075) + + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#456"); + m.calcLeg(0, 0); + m.drawLeg("#345"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#345"; + ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.stroke(); + // ctx.arc(12, 0, 8 + 4 * amplitude, 0, 2 * Math.PI); //big eye + ctx.beginPath(); + ctx.arc(12, 0, 8 + 4 * amplitude, 0, 2 * Math.PI); //big eye + ctx.fillStyle = "#345" + // ctx.fillStyle = //`hsl(0,0%,${50+50*Math.sin(m.cycle * 0.0075+Math.PI)}%)` //`hsl(${150+50*Math.sin(m.cycle * 0.0075)},100%,50%)` + // ctx.fillStyle = `hsl(${150 + 100 * amplitude},100%,50%)` + ctx.fill(); + // ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075)) + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 6 + 2 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 7, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 6, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#345"; + ctx.fill(); + ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.stroke(); + ctx.restore(); + } + }, + CPT() { + m.isAltSkin = true + m.color = { + hue: 0, + sat: 0, + light: 100, + } + // m.setFillColors(); + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(0.7, m.fillColor); + // grd.addColorStop(1, m.fillColor); + m.bodyGradient = grd + + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#eee"); + m.calcLeg(0, 0); + m.drawLeg("#fff"); + ctx.rotate(0.017 * simulation.cycle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.restore(); + + ctx.beginPath(); + ctx.arc(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle), 5, 0, 2 * Math.PI); + ctx.fillStyle = "#000" + ctx.fill(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 6; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineWidth = 3; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 5, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 5, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 5, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 5, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + // ctx.lineWidth = 2; + // ctx.stroke(); + ctx.restore(); + } + }, + stubs() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#555"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y + 5); + ctx.strokeStyle = stroke; + ctx.lineWidth = 6; + ctx.stroke(); + ctx.restore(); + } + }, + Sleipnir() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + for (let i = 0; i < 16; i++) { + m.calcLeg(Math.PI * i / 8, -3 * i / 16) + m.drawLeg("#444") + } + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + } + }, + diegesis() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle - (m.fireCDcycle !== Infinity ? m.flipLegs * 0.25 * Math.pow(Math.max(m.fireCDcycle - m.cycle, 0), 0.5) : 0)); + + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + } + }, + cat() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + + + if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { + ctx.scale(1, -1); + ctx.rotate(Math.PI); + } + ctx.beginPath(); + ctx.moveTo(-30, 0); + ctx.bezierCurveTo(-65, -75, + -5, 150 + (5 * Math.sin(simulation.cycle / 10)), + -70 + (10 * Math.sin(simulation.cycle / 10)), 0 + (10 * Math.sin(simulation.cycle / 10))); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 4; + ctx.stroke(); + + if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { + ctx.scale(1, -1); + ctx.rotate(0 - Math.PI); + } + m.calcLeg(0, 0); + m.drawLeg("#333"); + + ctx.rotate(m.angle); + if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); + ctx.beginPath(); + ctx.moveTo(5, -30); + ctx.lineTo(20, -40); + ctx.lineTo(20, -20); + ctx.lineWidth = 2; + ctx.fillStyle = "#f3f"; + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.stroke(); + ctx.moveTo(19, 0); + ctx.arc(15, 0, 4, Math.PI, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(24.3, 6, 5, Math.PI * 2, Math.PI); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(30, 6); + ctx.lineTo(32, 0); + ctx.lineTo(26, 0); + ctx.lineTo(30, 6); + ctx.fillStyle = "#f3f"; + ctx.fill(); + ctx.stroke(); + + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + } + }, + pareidolia() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.7 + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); //here is the flip + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -6, 7, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 10, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#555"; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(3, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(26, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; + } + }, + flipFlop() { + m.isAltSkin = true + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + + //draw body + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + //draw eye + ctx.beginPath(); + ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); + ctx.fillStyle = m.eyeFillColor; + ctx.fill() + ctx.restore(); + + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + } + }, + // ********************************************* + // **************** fields ********************* + // ********************************************* + closest: { + dist: 1000, + index: 0 + }, + isHolding: false, + isCloak: false, + throwCharge: 0, + fireCDcycle: 0, + fieldCDcycle: 0, + fieldMode: 0, //basic field mode before upgrades + maxEnergy: 1, //can be increased by a tech + holdingTarget: null, + timeSkipLastCycle: 0, + coupling: 0, + // these values are set on reset by setHoldDefaults() + fieldFx: 1, + fieldJump: 1, + blockingRecoil: 4, + grabPowerUpRange2: 0, + isFieldActive: false, + fieldRange: 155, + fieldShieldingScale: 1, + // fieldDamage: 1, + isSneakAttack: false, + lastHit: 0, //stores value of last damage player took above a threshold, in m.damage + sneakAttackCycle: 0, + enterCloakCycle: 0, + duplicateChance: 0, + energy: 0, + fieldRegen: 0.001, + fieldMode: 0, + fieldFire: false, + fieldHarmReduction: 1, + holdingMassScale: 0, + hole: { + isOn: false, + isReady: false, + pos1: { + x: 0, + y: 0 + }, + pos2: { + x: 0, + y: 0 + }, + }, + fieldArc: 0, + fieldThreshold: 0, + calculateFieldThreshold() { + m.fieldThreshold = Math.cos((m.fieldArc) * Math.PI) + }, + setHoldDefaults() { + if (tech.isFreeWormHole && m.fieldMode !== 9) { //not wormhole + const removed = tech.removeTech("charmed baryon") //neutronum can get player stuck so it has to be removed if player has wrong field + if (removed) powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + } + if (tech.isNeutronium && m.fieldMode !== 3) { //not negative mass field + const removed = tech.removeTech("neutronium") //neutronum can get player stuck so it has to be removed if player has wrong field + if (removed) powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + } + if (m.energy < m.maxEnergy) m.energy = m.maxEnergy; + m.fieldMeterColor = "#0cf" + m.eyeFillColor = m.fieldMeterColor + m.fieldShieldingScale = 1; + m.fieldBlockCD = 10; + m.fieldHarmReduction = 1; + m.lastHit = 0 + m.isSneakAttack = false + m.duplicateChance = 0 + m.grabPowerUpRange2 = 200000; + m.blockingRecoil = 4; + m.fieldRange = 155; + m.fieldFire = false; + m.fieldCDcycle = 0; + m.isCloak = false; + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield + m.airSpeedLimit = 125 + m.fieldFx = 1 + m.fieldJump = 1 + m.setFieldRegen(); + m.setMovement(); + m.drop(); + m.holdingMassScale = 0.5; + m.fieldArc = 0.2; //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.calculateFieldThreshold(); //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.isBodiesAsleep = true; + m.wakeCheck(); + m.setMaxEnergy(); + m.setMaxHealth(); + m.couplingChange() + m.hole = { + isOn: false, + isReady: false, + pos1: { + x: 0, + y: 0 + }, + pos2: { + x: 0, + y: 0 + }, + } + }, + setMaxEnergy(isMessage = true) { + m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2.66 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand + if (isMessage) simulation.makeTextLog(`m.maxEnergy = ${(m.maxEnergy.toFixed(2))}`) + }, + fieldMeterColor: "#0cf", + drawRegenEnergy(bgColor = "rgba(0, 0, 0, 0.4)", range = 60) { + if (m.energy < m.maxEnergy) { + m.regenEnergy(); + ctx.fillStyle = bgColor; + const xOff = m.pos.x - m.radius * m.maxEnergy + const yOff = m.pos.y - 50 + ctx.fillRect(xOff, yOff, range * m.maxEnergy, 10); + ctx.fillStyle = m.fieldMeterColor; + ctx.fillRect(xOff, yOff, range * m.energy, 10); + } else if (m.energy > m.maxEnergy + 0.05) { + ctx.fillStyle = bgColor; + const xOff = m.pos.x - m.radius * m.energy + const yOff = m.pos.y - 50 + // ctx.fillRect(xOff, yOff, range * m.maxEnergy, 10); + ctx.fillStyle = m.fieldMeterColor; + ctx.fillRect(xOff, yOff, range * m.energy, 10); + } + }, + drawRegenEnergyCloaking: function () { + if (m.energy < m.maxEnergy) { // replaces m.drawRegenEnergy() with custom code + m.regenEnergy(); + const xOff = m.pos.x - m.radius * m.maxEnergy + const yOff = m.pos.y - 50 + ctx.fillStyle = "rgba(0, 0, 0, 0.2)" // + ctx.fillRect(xOff, yOff, 60 * m.maxEnergy, 10); + ctx.fillStyle = "#fff" //m.cycle > m.lastKillCycle + 300 ? "#000" : "#fff" //"#fff"; + ctx.fillRect(xOff, yOff, 60 * m.energy, 10); + ctx.beginPath() + ctx.rect(xOff, yOff, 60 * m.maxEnergy, 10); + ctx.strokeStyle = m.fieldMeterColor; + ctx.lineWidth = 1; + ctx.stroke(); + } + }, + setFieldRegen() { + if (m.fieldMode === 6) { + m.fieldRegen = 0.002 //12 energy per second for time dilation + } else if (m.fieldMode === 2) { + m.fieldRegen = 0.000833 //5 energy per second perfect dia + } else if (m.fieldMode === 4) { + m.fieldRegen = 0.002 //12 energy per second molecular assembler + } else if (m.fieldMode === 5) { + m.fieldRegen = 0.001667 //10 energy per second plasma torch + } else if (m.fieldMode === 8) { + m.fieldRegen = 0.001667 //10 energy per second pilot wave + } else { + m.fieldRegen = 0.001 //6 energy per second + } + if (m.fieldMode === 0 || m.fieldMode === 4) m.fieldRegen += 0.000133 * m.coupling //return `generate ${(6*couple).toFixed(0)} energy per second` + if (tech.isTimeCrystals) { + m.fieldRegen *= 2.5 + } else if (tech.isGroundState) { + m.fieldRegen *= 0.66 + } + }, + regenEnergy() { //used in drawRegenEnergy // rewritten by some tech + if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.energy < 0) m.energy = 0 + }, + regenEnergyDefault() { + if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.energy < 0) m.energy = 0 + }, + lookingAt(who) { + //calculate a vector from body to player and make it length 1 + const diff = Vector.normalise(Vector.sub(who.position, m.pos)); + //make a vector for the player's direction of length 1 + const dir = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + }; + //the dot product of diff and dir will return how much over lap between the vectors + if (Vector.dot(dir, diff) > m.fieldThreshold) { + return true; + } + return false; + }, + drop() { + if (m.isHolding) { + m.fieldCDcycle = m.cycle + 15; + m.isHolding = false; + m.throwCharge = 0; + m.definePlayerMass() + } + if (m.holdingTarget) { + m.holdingTarget.collisionFilter.category = cat.body; + m.holdingTarget.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + m.holdingTarget = null; + } + }, + definePlayerMass(mass = m.defaultMass) { + Matter.Body.setMass(player, mass); + //reduce air and ground move forces + m.setMovement() + // m.Fx = 0.08 / mass * tech.squirrelFx //base player mass is 5 + // m.FxAir = 0.4 / mass / mass //base player mass is 5 + //make player stand a bit lower when holding heavy masses + m.yOffWhen.stand = Math.max(m.yOffWhen.crouch, Math.min(49, 49 - (mass - 5) * 6)) + if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand; + }, + drawHold(target, stroke = true) { + if (target) { + const eye = 15; + const len = target.vertices.length - 1; + ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; + ctx.lineWidth = 1; + ctx.strokeStyle = "#000"; + ctx.beginPath(); + ctx.moveTo( + m.pos.x + eye * Math.cos(m.angle), + m.pos.y + eye * Math.sin(m.angle) + ); + ctx.lineTo(target.vertices[len].x, target.vertices[len].y); + ctx.lineTo(target.vertices[0].x, target.vertices[0].y); + ctx.fill(); + if (stroke) ctx.stroke(); + for (let i = 0; i < len; i++) { + ctx.beginPath(); + ctx.moveTo( + m.pos.x + eye * Math.cos(m.angle), + m.pos.y + eye * Math.sin(m.angle) + ); + ctx.lineTo(target.vertices[i].x, target.vertices[i].y); + ctx.lineTo(target.vertices[i + 1].x, target.vertices[i + 1].y); + ctx.fill(); + if (stroke) ctx.stroke(); + } + } + }, + holding() { + if (m.fireCDcycle < m.cycle) m.fireCDcycle = m.cycle - 1 + if (m.holdingTarget) { + m.energy -= m.fieldRegen; + if (m.energy < 0) m.energy = 0; + Matter.Body.setPosition(m.holdingTarget, { + x: m.pos.x + 70 * Math.cos(m.angle), + y: m.pos.y + 70 * Math.sin(m.angle) + }); + Matter.Body.setVelocity(m.holdingTarget, player.velocity); + Matter.Body.rotate(m.holdingTarget, 0.01 / m.holdingTarget.mass); //gently spin the block + } else { + m.isHolding = false + } + }, + throwBlock() { + if (m.holdingTarget) { + if (input.field) { + if (m.energy > 0.001) { + if (m.fireCDcycle < m.cycle) m.fireCDcycle = m.cycle + if (tech.isCapacitor && m.throwCharge < 4) m.throwCharge = 4 + m.throwCharge += 0.5 / m.holdingTarget.mass / b.fireCDscale + if (m.throwCharge < 6) m.energy -= 0.001 / b.fireCDscale; // m.throwCharge caps at 5 + + //trajectory path prediction + if (tech.isTokamak) { + //draw charge + const x = m.pos.x + 15 * Math.cos(m.angle); + const y = m.pos.y + 15 * Math.sin(m.angle); + const len = m.holdingTarget.vertices.length - 1; + const opacity = m.throwCharge > 4 ? 0.65 : m.throwCharge * 0.06 + ctx.fillStyle = `rgba(255,0,255,${opacity})`; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[len].x, m.holdingTarget.vertices[len].y); + ctx.lineTo(m.holdingTarget.vertices[0].x, m.holdingTarget.vertices[0].y); + ctx.fill(); + for (let i = 0; i < len; i++) { + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[i].x, m.holdingTarget.vertices[i].y); + ctx.lineTo(m.holdingTarget.vertices[i + 1].x, m.holdingTarget.vertices[i + 1].y); + ctx.fill(); + } + } else { + //draw charge + const x = m.pos.x + 15 * Math.cos(m.angle); + const y = m.pos.y + 15 * Math.sin(m.angle); + const len = m.holdingTarget.vertices.length - 1; + const edge = m.throwCharge * m.throwCharge * m.throwCharge; + const grd = ctx.createRadialGradient(x, y, edge, x, y, edge + 5); + grd.addColorStop(0, "rgba(255,50,150,0.3)"); + grd.addColorStop(1, "transparent"); + ctx.fillStyle = grd; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[len].x, m.holdingTarget.vertices[len].y); + ctx.lineTo(m.holdingTarget.vertices[0].x, m.holdingTarget.vertices[0].y); + ctx.fill(); + for (let i = 0; i < len; i++) { + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[i].x, m.holdingTarget.vertices[i].y); + ctx.lineTo(m.holdingTarget.vertices[i + 1].x, m.holdingTarget.vertices[i + 1].y); + ctx.fill(); + } + //trajectory prediction + const cycles = 30 + const charge = Math.min(m.throwCharge / 5, 1) + const speed = (tech.isPrinter ? 15 + 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.1)) : 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25))) + const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) } + ctx.beginPath() + for (let i = 1, len = 10; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.strokeStyle = "rgba(68, 68, 68, 0.15)" //color.map + ctx.lineWidth = 2 + ctx.stroke() + } + } else { + m.drop() + } + } else if (m.throwCharge > 0) { //Matter.Query.region(mob, player.bounds) + if (m.holdingTarget.isPrinted) m.holdingTarget.isPrinted = undefined + //throw the body + m.fieldCDcycle = m.cycle + 20; + m.fireCDcycle = m.cycle + 20; + + m.isHolding = false; + + if (tech.isTokamak && m.throwCharge > 4) { //remove the block body and pulse in the direction you are facing + //m.throwCharge > 5 seems to be when the field full colors in a block you are holding + m.throwCycle = m.cycle + 180 //used to detect if a block was thrown in the last 3 seconds + if (m.immuneCycle < m.cycle) m.energy += 0.25 * Math.sqrt(m.holdingTarget.mass) * Math.min(5, m.throwCharge) + m.throwCharge = 0; + m.definePlayerMass() //return to normal player mass + //remove block before pulse, so it doesn't get in the way + for (let i = 0; i < body.length; i++) { + if (body[i] === m.holdingTarget) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + b.pulse(60 * Math.pow(m.holdingTarget.mass, 0.25), m.angle) + } else { //normal throw + //bullet-like collisions + m.holdingTarget.collisionFilter.category = cat.bullet + m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + if (tech.isBlockRestitution) { + m.holdingTarget.restitution = 0.999 //extra bouncy + m.holdingTarget.friction = m.holdingTarget.frictionStatic = m.holdingTarget.frictionAir = 0.001 + } + //check every second to see if player is away from thrown body, and make solid + const solid = function (that) { + const dx = that.position.x - player.position.x; + const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + if (dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + that.collisionFilter.category = cat.body; //make solid + that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + } else { + setTimeout(solid, 40, that); + } + }; + setTimeout(solid, 200, m.holdingTarget); + + const charge = Math.min(m.throwCharge / 5, 1) + //***** scale throw speed with the first number, 80 ***** + // let speed = 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25)); + let speed = (tech.isPrinter ? 15 + 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.1)) : 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25))) + + if (Matter.Query.collides(m.holdingTarget, map).length !== 0) { + speed *= 0.7 //drop speed by 30% if touching map + if (Matter.Query.ray(map, m.holdingTarget.position, m.pos).length !== 0) speed = 0 //drop to zero if the center of the block can't see the center of the player through the map + } + m.throwCharge = 0; + m.throwCycle = m.cycle + 180 //used to detect if a block was thrown in the last 3 seconds + Matter.Body.setVelocity(m.holdingTarget, { + x: player.velocity.x * 0.5 + Math.cos(m.angle) * speed, + y: player.velocity.y * 0.5 + Math.sin(m.angle) * speed + }); + Matter.Body.setVelocity(player, { + x: player.velocity.x - Math.cos(m.angle) * speed / (m.crouch ? 30 : 10) * Math.sqrt(m.holdingTarget.mass), + y: player.velocity.y - Math.sin(m.angle) * speed / 30 * Math.sqrt(m.holdingTarget.mass) + }); + m.definePlayerMass() //return to normal player mass + + if (tech.isAddBlockMass) { + const expand = function (that, massLimit) { + if (that.mass < massLimit) { + const scale = 1.04; + Matter.Body.scale(that, scale, scale); + setTimeout(expand, 20, that, massLimit); + } + }; + expand(m.holdingTarget, Math.min(20, m.holdingTarget.mass * 3)) + } + } + } + } else { + m.isHolding = false + } + }, + drawField() { + if (m.holdingTarget) { + ctx.fillStyle = "rgba(110,170,200," + (m.energy * (0.05 + 0.05 * Math.random())) + ")"; + ctx.strokeStyle = "rgba(110, 200, 235, " + (0.3 + 0.08 * Math.random()) + ")" //"#9bd" //"rgba(110, 200, 235, " + (0.5 + 0.1 * Math.random()) + ")" + } else { + ctx.fillStyle = "rgba(110,170,200," + (0.02 + m.energy * (0.15 + 0.15 * Math.random())) + ")"; + ctx.strokeStyle = "rgba(110, 200, 235, " + (0.6 + 0.2 * Math.random()) + ")" //"#9bd" //"rgba(110, 200, 235, " + (0.5 + 0.1 * Math.random()) + ")" + } + // const off = 2 * Math.cos(simulation.cycle * 0.1) + const range = m.fieldRange; + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2; + ctx.stroke(); + let eye = 13; + let aMag = 0.75 * Math.PI * m.fieldArc + let a = m.angle + aMag + let cp1x = m.pos.x + 0.6 * range * Math.cos(a) + let cp1y = m.pos.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + eye * Math.cos(m.angle), m.pos.y + eye * Math.sin(m.angle)) + a = m.angle - aMag + cp1x = m.pos.x + 0.6 * range * Math.cos(a) + cp1y = m.pos.y + 0.6 * range * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * range * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * range * Math.sin(m.angle - Math.PI * m.fieldArc)) + ctx.fill(); + // ctx.lineTo(m.pos.x + eye * Math.cos(m.angle), m.pos.y + eye * Math.sin(m.angle)); + + //draw random lines in field for cool effect + let offAngle = m.angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5); + ctx.beginPath(); + eye = 15; + ctx.moveTo(m.pos.x + eye * Math.cos(m.angle), m.pos.y + eye * Math.sin(m.angle)); + ctx.lineTo(m.pos.x + range * Math.cos(offAngle), m.pos.y + range * Math.sin(offAngle)); + ctx.strokeStyle = "rgba(120,170,255,0.6)"; + ctx.lineWidth = 1; + ctx.stroke(); + }, + grabPowerUp() { //look for power ups to grab with field + if (m.fireCDcycle < m.cycle) m.fireCDcycle = m.cycle - 1 + for (let i = 0, len = powerUp.length; i < len; ++i) { + const dxP = m.pos.x - powerUp[i].position.x; + const dyP = m.pos.y - powerUp[i].position.y; + const dist2 = dxP * dxP + dyP * dyP + 10; + // float towards player if looking at and in range or if very close to player + if ( + dist2 < m.grabPowerUpRange2 && + (m.lookingAt(powerUp[i]) || dist2 < 10000) && + Matter.Query.ray(map, powerUp[i].position, m.pos).length === 0 + ) { + if (!tech.isHealAttract || powerUp[i].name !== "heal") { //if you have accretion heals are already pulled in a different way + powerUp[i].force.x += 0.04 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + powerUp[i].force.y += 0.04 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 }); //extra friction + } + if ( //use power up if it is close enough + dist2 < 5000 && + !simulation.isChoosing && + (powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01 || tech.isOverHeal) + ) { + powerUps.onPickUp(powerUp[i]); + Matter.Body.setVelocity(player, { //player knock back, after grabbing power up + x: player.velocity.x + powerUp[i].velocity.x / player.mass * 4 * powerUp[i].mass, + y: player.velocity.y + powerUp[i].velocity.y / player.mass * 4 * powerUp[i].mass + }); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + return; //because the array order is messed up after splice + } + } + } + }, + minEnergyToDeflect: 0.05, + pushMass(who, fieldBlockCost = (0.025 + Math.sqrt(who.mass) * Vector.magnitude(Vector.sub(who.velocity, player.velocity)) * 0.002) * m.fieldShieldingScale) { + if (m.energy > m.minEnergyToDeflect) { //shield needs at least some of the cost to block + if (who.isShielded) fieldBlockCost *= 2; //shielded mobs take more energy to block + m.energy -= fieldBlockCost + if (m.energy < m.minEnergyToDeflect) { + m.energy = 0; + m.fieldCDcycle = m.cycle + Math.max(m.fieldBlockCD, 60); + if (tech.isLaserField) { + simulation.ephemera.push({ + name: "laser field", //used to find this array element in simulation.removeEphemera() + count: 20 + Math.floor(m.maxEnergy * 30 * 0.0018 / tech.laserDrain), //how many cycles the ephemera lasts, scales with max energy + do() { + this.count-- + if (this.count < 0) simulation.removeEphemera(this.name) + for (let i = 0, num = 12; i < num; i++) { //draw random lasers + const angle = 6.28 * i / num + m.cycle * 0.04 + b.laser({ x: m.pos.x + 30 * Math.cos(angle), y: m.pos.y + 30 * Math.sin(angle) }, { x: m.pos.x + 3000 * Math.cos(angle), y: m.pos.y + 3000 * Math.sin(angle) }, tech.laserDamage * 2.5)//dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1 + } + }, + }) + } + } else { + m.fieldCDcycle = m.cycle + m.fieldBlockCD; + } + if (!who.isInvulnerable && (m.coupling && m.fieldMode === 0) && bullet.length < 200) { //for field emitter iceIX + for (let i = 0; i < m.coupling; i++) { + if (0.1 * m.coupling - i > 1.25 * Math.random()) { + const sub = Vector.mult(Vector.normalise(Vector.sub(who.position, m.pos)), (m.fieldRange * m.harmonicRadius) * (0.4 + 0.3 * Math.random())) //m.harmonicRadius should be 1 unless you are standing wave expansion + const rad = Vector.rotate(sub, 1 * (Math.random() - 0.5)) + const angle = Math.atan2(sub.y, sub.x) + b.iceIX(6 + 6 * Math.random(), angle + 3 * (Math.random() - 0.5), Vector.add(m.pos, rad)) + } + } + } + const unit = Vector.normalise(Vector.sub(player.position, who.position)) + if (tech.blockDmg) { + Matter.Body.setVelocity(who, { x: 0.5 * who.velocity.x, y: 0.5 * who.velocity.y }); + if (who.isShielded) { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === who.shieldID) mob[i].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) + } + } else if (tech.isBlockRadiation) { + if (who.isMobBullet) { + who.damage(tech.blockDmg * m.dmgScale * 3, true) + } else { + mobs.statusDoT(who, tech.blockDmg * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + } + } else { + who.damage(tech.blockDmg * m.dmgScale, true) + } + const step = 40 + ctx.beginPath(); //draw electricity + for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) { + let x = m.pos.x - 20 * unit.x; + let y = m.pos.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#f0f"; + ctx.stroke(); + } else { + m.drawHold(who); + } + if (tech.isStunField) mobs.statusStun(who, tech.isStunField) + //knock backs + const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, who.mass))); // masses above 12 can start to overcome the push back + Matter.Body.setVelocity(who, { x: player.velocity.x - (15 * unit.x) / massRoot, y: player.velocity.y - (15 * unit.y) / massRoot }); + if (who.isUnstable) { + if (m.fieldCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 10 + who.death(); + } + if (m.crouch) { + Matter.Body.setVelocity(player, { x: player.velocity.x + 0.1 * m.blockingRecoil * unit.x * massRoot, y: player.velocity.y + 0.1 * m.blockingRecoil * unit.y * massRoot }); + } else { + Matter.Body.setVelocity(player, { x: player.velocity.x + m.blockingRecoil * unit.x * massRoot, y: player.velocity.y + m.blockingRecoil * unit.y * massRoot }); + } + } + }, + pushMobsFacing() { // find mobs in range and in direction looking + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < m.fieldRange && + m.lookingAt(mob[i]) && + !mob[i].isUnblockable && + Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + ) { + mob[i].locatePlayer(); + m.pushMass(mob[i]); + + if (tech.deflectEnergy && !mob[i].isInvulnerable && !mob[i].isShielded) { + m.energy += tech.deflectEnergy + } + } + } + }, + lookForPickUp() { //find body to pickup + const grabbing = { + targetIndex: null, + targetRange: 150, + // lookingAt: false //false to pick up object in range, but not looking at + }; + for (let i = 0, len = body.length; i < len; ++i) { + if (Matter.Query.ray(map, body[i].position, m.pos).length === 0) { + //is m next body a better target then my current best + const dist = Vector.magnitude(Vector.sub(body[i].position, m.pos)); + const looking = m.lookingAt(body[i]); + // if (dist < grabbing.targetRange && (looking || !grabbing.lookingAt) && !body[i].isNotHoldable) { + if (dist < grabbing.targetRange && looking && !body[i].isNotHoldable) { + grabbing.targetRange = dist; + grabbing.targetIndex = i; + // grabbing.lookingAt = looking; + } + } + } + // set pick up target for when mouse is released + if (body[grabbing.targetIndex]) { + m.holdingTarget = body[grabbing.targetIndex]; + // + ctx.beginPath(); //draw on each valid body + let vertices = m.holdingTarget.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j += 1) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = "rgba(190,215,230," + (0.3 + 0.7 * Math.random()) + ")"; + ctx.fill(); + + ctx.globalAlpha = 0.2; + m.drawHold(m.holdingTarget); + ctx.globalAlpha = 1; + } else { + m.holdingTarget = null; + } + }, + pickUp() { + //triggers when a hold target exits and field button is released + m.isHolding = true; + //conserve momentum when player mass changes + totalMomentum = Vector.add(Vector.mult(player.velocity, player.mass), Vector.mult(m.holdingTarget.velocity, m.holdingTarget.mass)) + Matter.Body.setVelocity(player, Vector.mult(totalMomentum, 1 / (m.defaultMass + m.holdingTarget.mass))); + + m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) + //make block collide with nothing + m.holdingTarget.collisionFilter.category = 0; + m.holdingTarget.collisionFilter.mask = 0; + }, + wakeCheck() { + if (m.isBodiesAsleep) { + m.isBodiesAsleep = false; + + function wake(who) { + for (let i = 0, len = who.length; i < len; ++i) { + Matter.Sleeping.set(who[i], false) + if (who[i].storeVelocity) { + Matter.Body.setVelocity(who[i], { + x: who[i].storeVelocity.x, + y: who[i].storeVelocity.y + }) + Matter.Body.setAngularVelocity(who[i], who[i].storeAngularVelocity) + } + } + } + // if (tech.isFreezeMobs) { + // for (let i = 0, len = mob.length; i < len; ++i) { + // const ICE_DRAIN = 0.0005 + // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; + // Matter.Sleeping.set(mob[i], false) + // mobs.statusSlow(mob[i], 60) + // } + // } else { + // wake(mob); + // } + wake(mob); + wake(body); + wake(bullet); + for (let i = 0, len = cons.length; i < len; i++) { + if (cons[i].stiffness === 0) { + cons[i].stiffness = cons[i].storeStiffness + } + } + // wake(powerUp); + } + }, + hold() { }, + couplingDescription(couple = m.coupling) { + switch (m.fieldMode) { + case 0: //field emitter + return `all other field effects` + case 1: //standing wave + // return `deflecting condenses +${couple.toFixed(1)} ice IX` + return `+${(couple * 5).toFixed(0)} maximum energy` + case 2: //perfect diamagnetism + return `deflecting condenses ${(0.1 * couple).toFixed(2)} ice IX` + // return `invulnerable +${2*couple} seconds post collision` + case 3: //negative mass + return `+${(100 * (1 - 0.973 ** couple)).toFixed(1)}% defense` + case 4: //assembler + return `generate ${(0.8 * couple).toFixed(1)} energy per second` + case 5: //plasma + return `+${(1.5 * couple).toFixed(1)}% damage` + case 6: //time dilation + return `+${(5 * couple).toFixed(0)}% longer stopped time` //movement, jumping, and + case 7: //cloaking + return `+${(3.3 * couple).toFixed(1)}% ambush damage` + case 8: //pilot wave + return `+${(4 * couple).toFixed(0)}% block collision damage` + case 9: //wormhole + return `after eating blocks +${(2 * couple).toFixed(0)} energy` + } + }, + couplingChange(change = 0) { + if (change > 0 && level.onLevel !== -1) simulation.makeTextLog(`m.coupling += ${change}`, 60); //level.onLevel !== -1 means not on lore level + m.coupling += change + if (m.coupling < 0) { + //look for coupling power ups on this level and remove them to prevent exploiting tech ejections + for (let i = powerUp.length - 1; i > -1; i--) { + if (powerUp[i].name === "coupling") { + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + m.coupling += 1 + if (!(m.coupling < 0)) break + } + } + m.coupling = 0 //can't go negative + } + m.setMaxEnergy(false); + // m.setMaxHealth(); + m.setFieldRegen() + mobs.setMobSpawnHealth(); + powerUps.setPowerUpMode(); + + // if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); + }, + setField(index) { + if (isNaN(index)) { //find index by name + let found = false + for (let i = 0; i < m.fieldUpgrades.length; i++) { + if (index === m.fieldUpgrades[i].name) { + index = i; + found = true; + break; + } + } + if (!found) return //if you can't find the field don't give a field to avoid game crash + } + m.fieldMode = index; + document.getElementById("field").innerHTML = m.fieldUpgrades[index].name + m.setHoldDefaults(); + m.fieldUpgrades[index].effect(); + simulation.makeTextLog(`m.setField("${m.fieldUpgrades[m.fieldMode].name}")`); + }, + fieldUpgrades: [{ + name: "field emitter", + imageNumber: Math.floor(Math.random() * 23), + description: `initial field
use energy to deflect mobs and throw blocks +
generate 6 energy per second`, //
100 max energy + effect: () => { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > m.minEnergyToDeflect) { + m.drawField(); + m.pushMobsFacing(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy() + } + } + }, + //
+ //
1
+ //
2
+ //
3
+ //
4
+ //
+ { + name: "standing wave", + //deflecting protects you in every direction + description: `3 oscillating shields are permanently active +
+150 max energy +
generate 6 energy per second`, + drainCD: 0, + effect: () => { + m.fieldBlockCD = 0; + m.blockingRecoil = 1.5 //4 is normal + m.fieldRange = 185 + m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) + // m.fieldHarmReduction = 0.66; //33% reduction + + m.harmonic3Phase = () => { //normal standard 3 different 2-d circles + const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netFieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + 0.7 * m.energy * (0.1 + 0.11 * Math.random()))) + ")"; + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netFieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 + } + } + } + } + m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) + m.harmonicAtomic = () => { //several ellipses spinning about different axises + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(110,170,200,0.8)" + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, 0.7 * m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; + // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 + } + } + } + } + if (tech.harmonics === 2) { + m.harmonicShield = m.harmonic3Phase + } else { + m.harmonicShield = m.harmonicAtomic + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + if (m.energy > m.minEnergyToDeflect && m.fieldCDcycle < m.cycle) { + if (tech.isStandingWaveExpand) { + if (input.field) { + // const oldHarmonicRadius = m.harmonicRadius + m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 + // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) + } else { + m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 + } + } + if (!simulation.isTimeSkipping) m.harmonicShield() + } + m.drawRegenEnergy() + } + } + }, + { + name: "perfect diamagnetism", + description: "deflecting does not drain energy
maintains functionality while inactive
generate 5 energy per second", + //
attract power ups from far away + // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", + // description: "gain energy when blocking
no recoil when blocking", + effect: () => { + m.fieldMeterColor = "#48f" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + + m.fieldShieldingScale = 0; + m.fieldBlockCD = 3; + m.grabPowerUpRange2 = 10000000 + m.fieldPosition = { + x: m.pos.x, + y: m.pos.y + } + m.fieldAngle = m.angle + m.perfectPush = (isFree = false) => { + if (m.fieldCDcycle < m.cycle) { + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && + !mob[i].isUnblockable && + Vector.dot({ + x: Math.cos(m.fieldAngle), + y: Math.sin(m.fieldAngle) + }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && + Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 + ) { + mob[i].locatePlayer(); + const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) + m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 10 : 0); + if (!mob[i].isInvulnerable && bullet.length < 250) { + for (let i = 0; i < m.coupling; i++) { + if (0.1 * m.coupling - i > Math.random()) { + const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5) + const radius = m.fieldRange * (0.6 + 0.3 * Math.random()) + b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, { + x: radius * Math.cos(angle), + y: radius * Math.sin(angle) + })) + } + } + } + if (tech.blockDmg) { //electricity + Matter.Body.setVelocity(mob[i], { + x: 0.5 * mob[i].velocity.x, + y: 0.5 * mob[i].velocity.y + }); + + if (mob[i].isShielded) { + for (let j = 0, len = mob.length; j < len; j++) { + if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) + } + } else if (tech.isBlockRadiation) { + if (mob[i].isMobBullet) { + mob[i].damage(tech.blockDmg * m.dmgScale * 3, true) + } else { + mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + } + } else { + mob[i].damage(tech.blockDmg * m.dmgScale, true) + } + // if (mob[i].isShielded) { + // for (let j = 0, len = mob.length; j < len; j++) { + // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true) + // } + // } else { + // if (tech.isBlockRadiation && !mob[i].isMobBullet) { + // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + // } else { + // mob[i].damage(tech.blockDmg * m.dmgScale) + // } + // } + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) { + let x = m.fieldPosition.x - 20 * unit.x; + let y = m.fieldPosition.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#f0f"; + ctx.stroke(); + } else if (isFree) { + ctx.lineWidth = 2; //when blocking draw this graphic + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + const len = mob[i].vertices.length - 1; + const mag = mob[i].radius + ctx.beginPath(); + ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + for (let j = 0; j < len; j++) { + ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); + } + ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + ctx.fill(); + ctx.stroke(); + } else { + + const eye = 15; //when blocking draw this graphic + const len = mob[i].vertices.length - 1; + ctx.lineWidth = 1; + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); + ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.fill(); + ctx.stroke(); + for (let j = 0; j < len; j++) { + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); + ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); + ctx.fill(); + ctx.stroke(); + } + } + if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) + //mob knock backs + const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); + Matter.Body.setVelocity(mob[i], { + x: player.velocity.x - (30 * unit.x) / massRoot, + y: player.velocity.y - (30 * unit.y) / massRoot + }); + if (mob[i].isUnstable) { + if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6 + mob[i].death(); + } + if (!isFree) { //player knock backs + if (mob[i].isDropPowerUp && player.speed < 12) { + const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); + Matter.Body.setVelocity(player, { + x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, + y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap + }); + } + } + } + } + } + } + m.hold = function () { + const wave = Math.sin(m.cycle * 0.022); + m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField + m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.calculateFieldThreshold(); + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //not hold but field button is pressed + //float while field is on + const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2))) + // console.log(angleReduction) + if (player.velocity.y > 1) { + player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g; + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: 0.98 * player.velocity.y + }); //set velocity to cap, but keep the direction + } + + // go invulnerable while field is active, but also drain energy + // if (true && m.energy > 2 * m.fieldRegen && m.immuneCycle < m.cycle + tech.cyclicImmunity) { + // m.immuneCycle = m.cycle + 1; //player is immune to damage for 60 cycles + // m.energy -= 2 * m.fieldRegen + // if (m.energy < m.fieldRegen) m.fieldCDcycle = m.cycle + 90; + // } + + + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + m.fieldPosition = { x: m.pos.x, y: m.pos.y } + m.fieldAngle = m.angle + //draw field attached to player + if (m.holdingTarget) { + ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` + ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` + } else { + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.angle + aMag + let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) + a = m.angle - aMag + cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (!input.field) { //&& tech.isFieldFree + //draw field free of player + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` + ctx.beginPath(); + ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.8 + 0.06 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.fieldAngle + aMag + ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(true); + } + } + // m.drawRegenEnergy() + m.drawRegenEnergy("rgba(0,0,0,0.2)") + if (tech.isPerfectBrake) { //cap mob speed around player + const range = 200 + 140 * wave + 150 * m.energy + for (let i = 0; i < mob.length; i++) { + const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) + if (distance < range) { + const cap = mob[i].isShielded ? 8 : 4 + if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "hsla(200,50%,61%,0.08)"; + ctx.fill(); + } + } + } + }, + { + name: "negative mass", + //
hold blocks as if they have a lower mass + description: "use energy to nullify  gravity
+55% defense
generate 6 energy per second", + fieldDrawRadius: 0, + effect: () => { + m.fieldFire = true; + m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + m.fieldHarmReduction = 0.45; //55% reduction + m.fieldDrawRadius = 0; + + m.hold = function () { + m.airSpeedLimit = 125 //5 * player.mass * player.mass + m.FxAir = 0.016 + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //push away + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + const DRAIN = 0.00035 + if (m.energy > DRAIN) { + if (tech.isFlyFaster) { + //look for nearby objects to make zero-g + function moveThis(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) { + who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity + if (input.left) { //blocks move horizontally with the same force as the player + who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a + } else if (input.right) { + who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d + } + //loose attraction to player + // const sub = Vector.sub(m.pos, body[i].position) + // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) + // body[i].force.x += unit.x + // body[i].force.y += unit.y + } + } + } + //control horizontal acceleration + m.airSpeedLimit = 1000 // 7* player.mass * player.mass + m.FxAir = 0.01 + //control vertical acceleration + if (input.down) { //down + player.force.y += 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; + moveThis(powerUp, this.fieldDrawRadius, 0); + moveThis(body, this.fieldDrawRadius, 0); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; + player.force.y -= 2.25 * player.mass * simulation.g; + moveThis(powerUp, this.fieldDrawRadius, 1.8); + moveThis(body, this.fieldDrawRadius, 1.8); + } else { + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + moveThis(powerUp, this.fieldDrawRadius); + moveThis(body, this.fieldDrawRadius); + } + } else { + //look for nearby objects to make zero-g + function verticalForce(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); + } + } + //control horizontal acceleration + m.airSpeedLimit = 400 // 7* player.mass * player.mass + m.FxAir = 0.005 + //control vertical acceleration + if (input.down) { //down + player.force.y -= 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; + verticalForce(powerUp, this.fieldDrawRadius, 0.7); + verticalForce(body, this.fieldDrawRadius, 0.7); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; + player.force.y -= 1.45 * player.mass * simulation.g; + verticalForce(powerUp, this.fieldDrawRadius, 1.38); + verticalForce(body, this.fieldDrawRadius, 1.38); + } else { + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + verticalForce(powerUp, this.fieldDrawRadius); + verticalForce(body, this.fieldDrawRadius); + } + } + + if (m.energy < 0.001) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + //add extra friction for horizontal motion + if (input.down || input.up || input.left || input.right) { + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } else { //slow rise and fall + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } + // if (tech.isFreezeMobs) { + // const ICE_DRAIN = 0.0005 + // for (let i = 0, len = mob.length; i < len; i++) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) { + // if (m.energy > ICE_DRAIN * 2) { + // m.energy -= ICE_DRAIN; + // this.fieldDrawRadius -= 2; + // mobs.statusSlow(mob[i], 60) + // } else { + // break; + // } + // } + // } + // } + //draw zero-G range + if (!simulation.isTimeSkipping) { + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + } + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.fieldDrawRadius = 0 + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + this.fieldDrawRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + + + // if (tech.isHealAttract) { + // for (let i = 0; i < powerUp.length; i++) { + // if (powerUp[i].name === "heal") { + // //&& Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 500000 + // let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.01 * powerUp[i].mass) + // powerUp[i].force.x += attract.x; + // powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity + // Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7)); + // } + // } + // } + + + // powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + // powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + // //extra friction + // Matter.Body.setVelocity(powerUp[i], { + // x: powerUp[i].velocity.x * 0.11, + // y: powerUp[i].velocity.y * 0.11 + // }); + + } + } + }, + { + name: "molecular assembler", + description: `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second`, + // simulation.molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones + setDescription() { + return `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second` + }, + effect: () => { + m.fieldMeterColor = "#ff0" + m.eyeFillColor = m.fieldMeterColor + m.hold = function () { + if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { + if (simulation.molecularMode === 0) { + if (tech.isSporeFlea) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() + b.flea({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }) + } + } else if (tech.isSporeWorm) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + b.worm({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }) + const SPEED = 2 + 1 * Math.random(); + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + } + } else { + const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 + for (let i = 0, len = Math.random() * 20; i < len; i++) { + if (m.energy > 3 * drain) { + m.energy -= drain + b.spore(m.pos) + } else { + break + } + } + } + } else if (simulation.molecularMode === 1) { + m.energy -= 0.33; + const direction = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + const push = Vector.mult(Vector.perp(direction), 0.08) + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, -15) + bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + } else if (simulation.molecularMode === 2) { + m.energy -= 0.045; + b.iceIX(1) + } else if (simulation.molecularMode === 3) { + if (tech.isDroneRadioactive) { + const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01 + if (m.energy > drain) { + m.energy -= drain + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 25) + } + } else { + //every bullet above 100 adds 0.005 to the energy cost per drone + //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05 + const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction + if (m.energy > drain) { + m.energy -= drain + b.drone() + } + } + } + } + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) { + // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices + m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit + const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11)) + const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11)) + let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object + let vertices = [] + for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) { + angle += 2 * Math.PI / len + vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) }) + } + Matter.Body.setVertices(m.holdingTarget, vertices) + m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) + } + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (tech.isPrinter && input.down) { + m.printBlock(); + } else if (m.energy > m.minEnergyToDeflect) { + m.drawField(); + m.pushMobsFacing(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy() + } + } + }, + { + name: "plasma torch", + description: "use energy to emit short range plasma
damages and pushes mobs away
generate 10 energy per second", + set() { + b.isExtruderOn = false + // m.fieldCDcycleAlternate = 0 + + if (m.plasmaBall) { + m.plasmaBall.reset() + Matter.Composite.remove(engine.world, m.plasmaBall); + } + if (tech.isPlasmaBall) { + const circleRadiusScale = 2 + m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, { + // collisionFilter: { + // group: 0, + // category: 0, + // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield + // }, + isSensor: true, + frictionAir: 0, + alpha: 0.7, + isPopping: false, + isAttached: false, + isOn: false, + drain: 0.0017, + radiusLimit: 10, + damage: 0.8, + setPositionToNose() { + const nose = { + x: m.pos.x + 10 * Math.cos(m.angle), + y: m.pos.y + 10 * Math.sin(m.angle) + } + Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius))); + }, + fire() { + this.isAttached = false; + const speed = 10 //scale with mass? + Matter.Body.setVelocity(this, { + x: player.velocity.x * 0.4 + speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }); + m.plasmaBall.setPositionToNose() + if (this.circleRadius < 10) this.isPopping = true + }, + scale(scale) { + Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast + if (this.circleRadius < this.radiusLimit) this.reset() + }, + reset() { + // console.log(this.circleRadius) + const scale = 1 / m.plasmaBall.circleRadius + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + // console.log(this.circleRadius) + // this.circleRadius = 0 + this.alpha = 0.7 + this.isOn = false + this.isPopping = false + // this.isAttached = true; + }, + do() { + if (this.isOn) { + //collisions with map + if (Matter.Query.collides(this, map).length > 0) { + if (this.isAttached) { + this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius)) + } else { + this.isPopping = true + } + } + if (this.isPopping) { + this.alpha -= 0.03 + if (this.alpha < 0.1) { + this.reset() + } else { + const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + // if (this.speed > 2.5) { + // const slow = 0.9 + // Matter.Body.setVelocity(this, { + // x: slow * this.velocity.x, + // y: slow * this.velocity.y + // }); + // } + } + //collisions with mobs + // const whom = Matter.Query.collides(this, mob) + // const dmg = this.damage * m.dmgScale + // for (let i = 0, len = whom.length; i < len; i++) { + // const mobHit = (who) => { + // if (who.alive) { + // if (!this.isAttached && !who.isMobBullet) this.isPopping = true + // who.damage(dmg); + // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius)) + // if (who.speed > 5) { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.6, + // y: who.velocity.y * 0.6 + // }); + // } else { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.93, + // y: who.velocity.y * 0.93 + // }); + // } + // } + // } + // mobHit(whom[i].bodyA) + // mobHit(whom[i].bodyB) + // } + + //damage nearby mobs + const dmg = this.damage * m.dmgScale + const arcList = [] + const damageRadius = circleRadiusScale * this.circleRadius + const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { + const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) + if (sub < damageRadius + mob[i].radius) { + // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true + mob[i].damage(dmg); + if (mob[i].speed > 5) { + Matter.Body.setVelocity(mob[i], { //friction + x: mob[i].velocity.x * 0.6, + y: mob[i].velocity.y * 0.6 + }); + } else { + Matter.Body.setVelocity(mob[i], { //friction + x: mob[i].velocity.x * 0.93, + y: mob[i].velocity.y * 0.93 + }); + } + } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) { + arcList.push(mob[i]) //populate electrical arc list + } + } + } + for (let i = 0; i < arcList.length; i++) { + if (tech.plasmaDischarge > Math.random()) { + const who = arcList[Math.floor(Math.random() * arcList.length)] + who.damage(dmg * 4); + //draw arcs + const sub = Vector.sub(who.position, this.position) + const unit = Vector.normalise(sub) + let len = 12 + const step = Vector.magnitude(sub) / (len + 2) + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + ctx.moveTo(x, y); + for (let i = 0; i < len; i++) { + x += step * (unit.x + (Math.random() - 0.5)) + y += step * (unit.y + (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineTo(who.position.x, who.position.y); + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 4 + 3 * Math.random(); + ctx.stroke(); + if (who.damageReduction) { + simulation.drawList.push({ + x: who.position.x, + y: who.position.y, + radius: 15, + color: "rgba(150,150,255,0.4)", + time: 15 + }); + } + } + } + + + //slowly slow down if too fast + if (this.speed > 10) { + const scale = 0.998 + Matter.Body.setVelocity(this, { + x: scale * this.velocity.x, + y: scale * this.velocity.y + }); + } + + //graphics + const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random() + const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius); + const alpha = this.alpha + 0.1 * Math.random() + gradient.addColorStop(0, `rgba(255,255,255,${alpha})`); + gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`); + gradient.addColorStop(1, `rgba(255,0,255,${alpha})`); + // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`); + ctx.fillStyle = gradient + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + //draw arcs + const unit = Vector.rotate({ + x: 1, + y: 0 + }, Math.random() * 6.28) + let len = 8 + const step = this.circleRadius / len + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + ctx.moveTo(x, y); + + for (let i = 0; i < len; i++) { + x += step * (unit.x + 1.9 * (Math.random() - 0.5)) + y += step * (unit.y + 1.9 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); + } + }, + }); + + Composite.add(engine.world, m.plasmaBall); + // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice(); + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + + //field is active + if (!m.plasmaBall.isAttached) { //return ball to player + if (m.plasmaBall.isOn) { + m.plasmaBall.isPopping = true + } else { + m.plasmaBall.isAttached = true + m.plasmaBall.isOn = true + m.plasmaBall.isPopping = false + m.plasmaBall.alpha = 0.7 + m.plasmaBall.setPositionToNose() + // m.plasmaBall.reset() + + } + // const scale = 0.7 + // Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast + // if (m.plasmaBall.circleRadius < m.plasmaBall.radiusLimit) { + // m.plasmaBall.isAttached = true + // m.plasmaBall.isOn = true + // m.plasmaBall.setPositionToNose() + // } + } else if (m.energy > m.plasmaBall.drain) { //charge up when attached + if (tech.isCapacitor) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } else { + m.energy -= m.plasmaBall.drain; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + if (m.energy > m.maxEnergy) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + m.plasmaBall.setPositionToNose() + + //add friction for player when holding ball, more friction in vertical + // const floatScale = Math.sqrt(m.plasmaBall.circleRadius) + // const friction = 0.0002 * floatScale + // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up + // Matter.Body.setVelocity(player, { + // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x, + // y: slowY * player.velocity.y + // }); + + // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast + // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down + + //float + const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up + Matter.Body.setVelocity(player, { + x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x, + y: slowY * player.velocity.y + }); + if (player.velocity.y > 5) { + player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast + } else { + player.force.y -= 0.5 * player.mass * simulation.g; + } + } else { + m.fieldCDcycle = m.cycle + 90; + m.plasmaBall.fire() + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + if (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() + } + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() + } + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + m.plasmaBall.do() + } + } else if (tech.isExtruder) { + m.hold = function () { + b.isExtruderOn = false + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + b.extruder(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + if (input.field) { + b.wasExtruderOn = true + } else { + b.wasExtruderOn = false + b.canExtruderFire = true + } + ctx.beginPath(); //draw all the wave bullets + for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) + if (bullet[i].isWave) { + if (bullet[i].isBranch || bullet[i - 1].isBranch) { + ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + } else { + ctx.lineTo(bullet[i].position.x, bullet[i].position.y) + } + } + } + if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + ctx.lineWidth = 4; + ctx.strokeStyle = "#f07" + ctx.stroke(); + ctx.lineWidth = tech.extruderRange; + ctx.strokeStyle = "rgba(255,0,110,0.06)" + ctx.stroke(); + } + } else { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + b.plasma(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + } + } + }, + effect() { + m.fieldMeterColor = "#f0f" + m.eyeFillColor = m.fieldMeterColor + this.set(); + } + }, + { + name: "time dilation", + description: "use energy to stop time
+20% movement and fire rate
generate 12 energy per second", + set() { + // m.fieldMeterColor = "#0fc" + // m.fieldMeterColor = "#ff0" + m.fieldMeterColor = "#3fe" + m.eyeFillColor = m.fieldMeterColor + m.fieldFx = 1.25 + // m.fieldJump = 1.09 + m.setMovement(); + b.setFireCD() + const timeStop = () => { + m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-50000, -50000, 100000, 100000) + ctx.globalCompositeOperation = "source-over" + //stop time + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + simulation.cycle--; //pause all functions that depend on game cycle increasing + } + if (tech.isRewindField) { + this.rewindCount = 0 + m.grabPowerUpRange2 = 300000 + m.hold = function () { + // console.log(m.fieldCDcycle) + m.grabPowerUp(); + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if ( + // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && + // !simulation.isChoosing && + // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // ) { + // powerUps.onPickUp(powerUp[i]); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // break; //because the array order is messed up after splice + // } + // } + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + m.wakeCheck(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + const drain = 0.0014 / (1 + 0.05 * m.coupling) + if (m.energy > drain) m.energy -= drain + m.grabPowerUp(); + if (this.rewindCount === 0) { + m.lookForPickUp(); + } + + if (!m.holdingTarget) { + if (this.rewindCount === 0) { //large upfront energy cost to enter rewind mode + if (m.energy > 0.3) { + m.energy -= 0.3 + } else { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } + } + this.rewindCount += 6; + const DRAIN = 0.003 + let history = m.history[(m.cycle - this.rewindCount) % 600] + if (this.rewindCount > 599 || m.energy < DRAIN) { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } else { + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-100000, -100000, 200000, 200000) + ctx.globalCompositeOperation = "source-over" + // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme + m.energy -= DRAIN + if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { + x: history.velocity.x, + y: history.velocity.y + }); + if (m.health < history.health) { + m.health = history.health + if (m.health > m.maxHealth) m.health = m.maxHealth + m.displayHealth(); + } + m.yOff = history.yOff + if (m.yOff < 48) { + m.doCrouch() + } else { + m.undoCrouch() + } + if (tech.isRewindBot && !(this.rewindCount % 60)) { + for (let i = 0; i < tech.isRewindBot; i++) { + b.randomBot(m.pos, false, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 300 + Math.floor(180 * Math.random()) //8-9 seconds + } + } + if (tech.isRewindGrenade && !(this.rewindCount % 30)) { + b.grenade(m.pos, this.rewindCount) //Math.PI / 2 + const who = bullet[bullet.length - 1] + who.endCycle = simulation.cycle + 120 + } + } + } + m.wakeCheck(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.rewindCount = 0; + m.wakeCheck(); + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { + timeStop(); + this.rewindCount = 0; + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + this.rewindCount = 0; + m.wakeCheck(); + } + m.drawRegenEnergy() // this calls m.regenEnergy(); also + } + } else { + m.fieldFire = true; + m.isBodiesAsleep = false; + m.hold = function () { + if (m.isHolding) { + m.wakeCheck(); + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { + const drain = 0.0026 / (1 + 0.03 * m.coupling) + if (m.energy > drain) m.energy -= drain + m.grabPowerUp(); + m.lookForPickUp(); //this drains energy 0.001 + if (m.energy > drain) { + timeStop(); + } else { //holding, but field button is released + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + m.wakeCheck(); + m.wakeCheck(); + } + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { + timeStop(); + //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs + // if (!(m.cycle % 4)) { + // // requestAnimationFrame(() => { + // m.wakeCheck(); + // // simulation.timePlayerSkip(1) + // // }); //wrapping in animation frame prevents errors, probably + // ctx.globalCompositeOperation = "saturation" + // ctx.fillStyle = "#ccc"; + // ctx.fillRect(-100000, -100000, 200000, 200000) + // ctx.globalCompositeOperation = "source-over" + // } else { + // timeStop(); + // } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.wakeCheck(); + m.pickUp(); + } else { + m.wakeCheck(); + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy() + } + } + }, + effect() { + if (tech.isTimeStop) { + m.fieldHarmReduction = 0.66; //33% reduction + } else { + m.fieldHarmReduction = 1; + } + this.set(); + } + }, + { + name: "metamaterial cloaking", + description: "+50% defense while cloaked
after decloaking +333% damage for 2 s
generate 6 energy per second", + effect: () => { + m.fieldFire = true; + m.fieldMeterColor = "#333"; + m.eyeFillColor = m.fieldMeterColor + m.fieldPhase = 0; + m.isCloak = false + m.fieldDrawRadius = 0 + m.isSneakAttack = true; + m.sneakAttackCycle = 0; + m.enterCloakCycle = 0; + m.drawCloakedM = function () { + m.walk_cycle -= m.flipLegs * m.Vx; + m.pos.x += 4 + m.draw(); + + // let history = m.history[(m.cycle - 1) % 600] + // m.pos.x = history.position.x + // m.pos.y = history.position.y + m.yPosDifference - history.yOff + + // m.pos.x += 4 + // ctx.fillStyle = m.fillColor; + // ctx.save(); + // ctx.translate(m.pos.x, m.pos.y); + // m.calcLeg(Math.PI, -3); + // m.drawLeg("#ccc"); + // m.calcLeg(0, 0); + // m.drawLeg("#ccc"); + // ctx.rotate(m.angle); + // ctx.beginPath(); + // ctx.arc(0, 0, 30, 0, 2 * Math.PI); + // ctx.fillStyle = "#fff" + // ctx.fill(); + // ctx.arc(15, 0, 4, 0, 2 * Math.PI); + // ctx.strokeStyle = "#333"; + // ctx.lineWidth = 2; + // ctx.stroke(); + // ctx.restore() + } + m.drawCloak = function () { + m.fieldPhase += 0.007 + const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); + ctx.fillStyle = "#fff" + ctx.lineWidth = 2; + ctx.strokeStyle = "#000" + // ctx.stroke() + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + //not shooting (or using field) enable cloak + if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle + if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing + const drain = 0.02 + if (!m.isCloak && m.energy > drain + 0.03) { + m.energy -= drain + m.isCloak = true //enter cloak + m.fieldHarmReduction = 0.5; + m.enterCloakCycle = m.cycle + if (tech.isCloakHealLastHit && m.lastHit > 0) { + const heal = Math.min(0.75 * m.lastHit, m.energy) + if (m.energy > heal) { + m.energy -= heal + m.addHealth(heal); //heal from last hit + m.lastHit = 0 + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: Math.sqrt(heal) * 200, + color: "rgba(0,255,200,0.6)", + time: 16 + }); + } + } + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield + } + } + } + } else if (m.isCloak) { //exit cloak + m.sneakAttackCycle = m.cycle + m.isCloak = false + m.fieldHarmReduction = 1 + + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + } + } + if (tech.isCloakStun) { //stun nearby mobs after exiting cloak + let isMobsAround = false + const stunRange = m.fieldDrawRadius * 1.5 + const drain = 0.14 + if (m.energy > drain) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { + isMobsAround = true + mobs.statusStun(mob[i], 180) + } + } + if (isMobsAround) { + m.energy -= drain + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: stunRange, + color: "hsla(0,50%,100%,0.7)", + time: 7 + }); + } + } + } + } + if (m.isCloak) { + m.fieldRange = m.fieldRange * 0.85 + 130 + m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() + ctx.globalCompositeOperation = "lighter"; + m.drawCloakedM() + ctx.globalCompositeOperation = "source-over"; + } else if (m.fieldRange < 4000) { + m.fieldRange += 90 + m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() + } + if (tech.isIntangible) { + if (m.isCloak) { + player.collisionFilter.mask = cat.map + let inPlayer = Matter.Query.region(mob, player.bounds) + if (inPlayer.length > 0) { + for (let i = 0; i < inPlayer.length; i++) { + if (m.energy > 0) { + if (!inPlayer[i].isUnblockable) m.energy -= 0.007; + if (inPlayer[i].shield) m.energy -= 0.025; + } + } + } + } else { + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } + this.drawRegenEnergyCloaking() + if (m.isSneakAttack && m.sneakAttackCycle + Math.min(120, 0.5 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status + ctx.globalCompositeOperation = "multiply"; + m.drawCloakedM() + ctx.globalCompositeOperation = "source-over"; + } + } + } + }, + { + name: "pilot wave", + //
blocks can't collide with intangible mobs + //field radius decreases out of line of sight + //unlock tech from other fields + description: "use energy to guide blocks
tech, fields, and guns have +2 choices
generate 10 energy per second", + effect: () => { + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + + m.fieldPhase = 0; + m.fieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.fieldOn = false; + m.fieldRadius = 0; + m.drop(); + m.hold = function () { + if (tech.isPrinter) { + //spawn blocks if field and crouch + if (input.field && m.fieldCDcycle < m.cycle && input.down && !m.isHolding) { + m.printBlock() + } + //if holding block grow it + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) { + // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices + m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit + const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11)) + const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11)) + let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object + let vertices = [] + for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) { + angle += 2 * Math.PI / len + vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) }) + } + Matter.Body.setVertices(m.holdingTarget, vertices) + m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) + } + m.throwBlock() + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + //if releasing field throw it + + } + if (input.field) { + if (m.fieldCDcycle < m.cycle) { + const scale = 25 + const bounds = { + min: { + x: m.fieldPosition.x - scale, + y: m.fieldPosition.y - scale + }, + max: { + x: m.fieldPosition.x + scale, + y: m.fieldPosition.y + scale + } + } + const isInMap = Matter.Query.region(map, bounds).length + // const isInMap = Matter.Query.point(map, m.fieldPosition).length + + if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location + m.fieldOn = true; + // m.fieldPosition = { //smooth the mouse position, set to starting at player + // x: m.pos.x, + // y: m.pos.y + // } + m.fieldPosition = { //smooth the mouse position, set to mouse's current location + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + } else { //when field is on it smoothly moves towards the mouse + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + const smooth = isInMap ? 0.985 : 0.96; + m.fieldPosition = { //smooth the mouse position + x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), + y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), + } + } + + //grab power ups into the field + for (let i = 0, len = powerUp.length; i < len; ++i) { + const dxP = m.fieldPosition.x - powerUp[i].position.x; + const dyP = m.fieldPosition.y - powerUp[i].position.y; + const dist2 = dxP * dxP + dyP * dyP + 200; + // float towards field if looking at and in range or if very close to player + if ( + dist2 < m.fieldRadius * m.fieldRadius && + (m.lookingAt(powerUp[i]) || dist2 < 16000) + ) { + powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + //extra friction + Matter.Body.setVelocity(powerUp[i], { + x: powerUp[i].velocity.x * 0.11, + y: powerUp[i].velocity.y * 0.11 + }); + if ( + dist2 < 5000 && + !simulation.isChoosing && + (powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01 || tech.isOverHeal) + // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) + // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) + ) { //use power up if it is close enough + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + // m.fieldRadius += 50 + break; //because the array order is messed up after splice + } + } + } + //grab power ups normally too + m.grabPowerUp(); + + if (m.energy > 0.01) { + //find mouse velocity + const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) + const speed = Vector.magnitude(diff) + const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity + let radius, radiusSmooth + if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field + radius = 0 + radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 + } else { + radius = Math.max(50, 250 - 2 * speed) + radiusSmooth = 0.97 + } + m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) + + for (let i = 0, len = body.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { + const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy + if (m.energy > DRAIN) { + m.energy -= DRAIN; + Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity + Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) + // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects + //blocks drift towards center of pilot wave + const sub = Vector.sub(m.fieldPosition, body[i].position) + const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) + body[i].force.x += push.x + body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects + // if (body[i].collisionFilter.category !== cat.bullet) { + // body[i].collisionFilter.category = cat.bullet; + // } + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + break + } + } + } + + + // m.holdingTarget.collisionFilter.category = cat.bullet; + // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + // //check every second to see if player is away from thrown body, and make solid + // const solid = function(that) { + // const dx = that.position.x - player.position.x; + // const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + // that.collisionFilter.category = cat.body; //make solid + // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + // } else { + // setTimeout(solid, 40, that); + // } + // }; + // setTimeout(solid, 200, m.holdingTarget); + + + + // if (tech.isFreezeMobs) { + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) { + // const ICE_DRAIN = 0.0005 + // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; + // mobs.statusSlow(mob[i], 180) + // } + // } + // } + + ctx.beginPath(); + const rotate = m.cycle * 0.008; + m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); + const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#fff"; //"#eef"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 4; + ctx.stroke(); + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + } + } else { + m.grabPowerUp(); + } + } else { + m.fieldOn = false + m.fieldRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + } + } + }, + { + name: "wormhole", + //wormholes attract blocks and power ups
+ description: "use energy to tunnel through a wormhole
+5% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes + drain: 0, + effect: function () { + m.fieldMeterColor = "#bbf" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + + m.duplicateChance = 0.05 + m.fieldRange = 0 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + + m.hold = function () { + // m.hole = { //this is reset with each new field, but I'm leaving it here for reference + // isOn: false, + // isReady: true, + // pos1: {x: 0,y: 0}, + // pos2: {x: 0,y: 0}, + // angle: 0, + // unit:{x:0,y:0}, + // } + if (m.hole.isOn) { + // draw holes + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const semiMajorAxis = m.fieldRange + 30 + const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) + const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) + const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) + const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) + ctx.beginPath(); + ctx.moveTo(edge1a.x, edge1a.y) + ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); + ctx.lineTo(edge2b.x, edge2b.y) + ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); + ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` + ctx.fill(); + + //suck power ups + for (let i = 0, len = powerUp.length; i < len; ++i) { + //which hole is closer + const dxP1 = m.hole.pos1.x - powerUp[i].position.x; + const dyP1 = m.hole.pos1.y - powerUp[i].position.y; + const dxP2 = m.hole.pos2.x - powerUp[i].position.x; + const dyP2 = m.hole.pos2.y - powerUp[i].position.y; + let dxP, dyP, dist2 + if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { + dxP = dxP1 + dyP = dyP1 + } else { + dxP = dxP2 + dyP = dyP2 + } + dist2 = dxP * dxP + dyP * dyP; + if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") + powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole + powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { //extra friction + x: powerUp[i].velocity.x * 0.05, + y: powerUp[i].velocity.y * 0.05 + }); + if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough + + // if (true) { //AoE radiation effect + // const range = 800 + + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i].alive && !mob[i].isShielded) { + // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; + // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits + // } + // } + + // simulation.drawList.push({ + // x: powerUp[i].position.x, + // y: powerUp[i].position.y, + // radius: range, + // color: "rgba(0,150,200,0.3)", + // time: 4 + // }); + // } + + m.fieldRange *= 0.8 + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break; //because the array order is messed up after splice + } + } + } + //suck and shrink blocks + const suckRange = 500 + const shrinkRange = 100 + const shrinkScale = 0.97; + const slowScale = 0.9 + for (let i = 0, len = body.length; i < len; i++) { + if (!body[i].isNotHoldable) { + const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) + const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) + if (dist1 < dist2) { + if (dist1 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) + const slow = Vector.mult(body[i].velocity, slowScale) + Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); + //shrink + if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + m.fieldRange *= 0.8 + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling + if (tech.isWormholeWorms) { //pandimensional spermia + b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); + // for (let i = 0, len = Math.ceil(1.25 * Math.random()); i < len; i++) { + // } + } + break + } + } + } + } else if (dist2 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) + const slow = Vector.mult(body[i].velocity, slowScale) + Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); + //shrink + if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + m.fieldRange *= 0.8 + // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 + // if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.5 + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling + if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling + if (tech.isWormholeWorms) { //pandimensional spermia + b.worm(Vector.add(m.hole.pos1, Vector.rotate({ + x: m.fieldRange * 0.4, + y: 0 + }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); + // for (let i = 0, len = Math.ceil(1.25 * Math.random()); i < len; i++) { + // } + } + break + } + } + } + } + } + if (tech.isWormHoleBullets) { + //teleport bullets + for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 + if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots + if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } + } + } + // mobs get pushed away + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + } + } + } + + if (m.fieldCDcycle < m.cycle) { + const scale = 60 + const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + const sub = Vector.sub(simulation.mouseInGame, m.pos) + const mag = Vector.magnitude(sub) + + if (input.field) { + if (tech.isWormHolePause) { + const drain = m.fieldRegen + 0.000035 + if (m.energy > drain) { + m.energy -= drain + if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + simulation.cycle--; //pause all functions that depend on game cycle increasing + Matter.Body.setVelocity(player, { //keep player frozen + x: 0, + y: -55 * player.mass * simulation.g //undo gravity before it is added + }); + player.force.x = 0 + player.force.y = 0 + } else { + m.wakeCheck(); + m.energy = 0; + } + } + + m.grabPowerUp(); + //draw possible wormhole + if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { + this.drain = (0.05 + 0.005 * Math.sqrt(mag)) * 2 + } else { + this.drain = tech.isFreeWormHole ? 0 : 0.05 + 0.005 * Math.sqrt(mag) + } + const unit = Vector.perp(Vector.normalise(sub)) + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + ctx.beginPath(); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); + if ( + mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.hole.isReady = true; + // ctx.fillStyle = "rgba(255,255,255,0.5)" + // ctx.fill(); + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.stroke(); + } else { + m.hole.isReady = false; + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.lineDashOffset = 30 * Math.random() + ctx.setLineDash([20, 40]); + ctx.stroke(); + ctx.setLineDash([]); + } + } else { + if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); + + //make new wormhole + if ( + m.hole.isReady && mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.energy -= this.drain + m.hole.isReady = false; + m.fieldRange = 0 + Matter.Body.setPosition(player, simulation.mouseInGame); + m.buttonCD_jump = 0 //this might fix a bug with jumping + const velocity = Vector.mult(Vector.normalise(sub), 20) + Matter.Body.setVelocity(player, { + x: velocity.x, + y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + }); + if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + + //set holes + m.hole.isOn = true; + m.hole.pos1.x = m.pos.x + m.hole.pos1.y = m.pos.y + m.hole.pos2.x = player.position.x + m.hole.pos2.y = player.position.y + m.hole.angle = Math.atan2(sub.y, sub.x) + m.hole.unit = Vector.perp(Vector.normalise(sub)) + + if (tech.isWormholeDamage) { + who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + for (let i = 0; i < who.length; i++) { + if (who[i].body.alive) { + mobs.statusDoT(who[i].body, 1, 420) + mobs.statusStun(who[i].body, 360) + } + } + } + } + } + + // if (true && m.energy > 0.5) { //teleport away low mass mobs + // // && !(m.cycle % 1) + // const hit = Matter.Query.region(mob, { + // min: { + // x: m.pos.x - 80, + // y: m.pos.y - 80 + // }, + // max: { + // x: m.pos.x + 80, + // y: m.pos.y + 160 + // } + // }) + + // // find incoming mob with low mass + // for (let i = 0; i < hit.length; i++) { + // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) { + // //is the mob moving towards the player? + + // // console.log('found one', hit[i].mass) + // const unit = Vector.normalise(hit[i].velocity) + // const jump = Vector.mult(unit, 200) + // const where = Vector.add(hit[i].position, jump) + // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map + // // m.energy -= hit[i].mass * 0.06 + // // m.fieldCDcycle = m.cycle + 30; + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // Matter.Body.setPosition(hit[i], where); + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // } + // // break + // } + // } + // } + } + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + // const scale = 60 + // const sub = Vector.sub(simulation.mouseInGame, m.pos) + // const mag = Vector.magnitude(sub) + // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + // if (m.hole.isReady && mag > 250 && m.energy > drain) { + // if ( + // Matter.Query.region(map, { + // min: { + // x: simulation.mouseInGame.x - scale, + // y: simulation.mouseInGame.y - scale + // }, + // max: { + // x: simulation.mouseInGame.x + scale, + // y: simulation.mouseInGame.y + scale + // } + // }).length === 0 && + // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 + // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 + // ) { + // m.energy -= drain + // m.hole.isReady = false; + // m.fieldRange = 0 + // Matter.Body.setPosition(player, simulation.mouseInGame); + // m.buttonCD_jump = 0 //this might fix a bug with jumping + // const velocity = Vector.mult(Vector.normalise(sub), 20) + // Matter.Body.setVelocity(player, { + // x: velocity.x, + // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + // }); + // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds + // // move bots to player + // for (let i = 0; i < bullet.length; i++) { + // if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + // x: 250 * (Math.random() - 0.5), + // y: 250 * (Math.random() - 0.5) + // })); + // Matter.Body.setVelocity(bullet[i], { + // x: 0, + // y: 0 + // }); + // } + // } + + // //set holes + // m.hole.isOn = true; + // m.hole.pos1.x = m.pos.x + // m.hole.pos1.y = m.pos.y + // m.hole.pos2.x = player.position.x + // m.hole.pos2.y = player.position.y + // m.hole.angle = Math.atan2(sub.y, sub.x) + // m.hole.unit = Vector.perp(Vector.normalise(sub)) + + // if (tech.isWormholeDamage) { + // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + // for (let i = 0; i < who.length; i++) { + // if (who[i].body.alive) { + // mobs.statusDoT(who[i].body, 1, 420) + // mobs.statusStun(who[i].body, 360) + // } + // } + // } + // } else { + // //draw failed wormhole + // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) + // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } + // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + // ctx.beginPath(); + // ctx.moveTo(where.x, where.y) + // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + // ctx.lineTo(edge2b.x, edge2b.y) + // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); + // // ctx.fillStyle = "rgba(255,255,255,0.5)" + // // ctx.fill(); + // ctx.lineWidth = 1 + // ctx.strokeStyle = "#000" + // ctx.lineDashOffset = 30 * Math.random() + // ctx.setLineDash([20, 40]); + // ctx.stroke(); + // ctx.setLineDash([]); + // } + // } + // m.grabPowerUp(); + // } else { + // m.hole.isReady = true; + // } + m.drawRegenEnergy() + } + }, + + // rewind: function() { + // if (input.down) { + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const DRAIN = 0.01 + // if (this.rewindCount < 289 && m.energy > DRAIN) { + // m.energy -= DRAIN + + + // if (this.rewindCount === 0) { + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 4 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // } + + + // this.rewindCount += 10; + // simulation.wipe = function() { //set wipe to have trails + // // ctx.fillStyle = "rgba(255,255,255,0)"; + // ctx.fillStyle = `rgba(221,221,221,${0.004})`; + // ctx.fillRect(0, 0, canvas.width, canvas.height); + // } + // let history = m.history[(m.cycle - this.rewindCount) % 300] + // Matter.Body.setPosition(player, history.position); + // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // if (history.health > m.health) { + // m.health = history.health + // m.displayHealth(); + // } + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // const dxP = player.position.x - powerUp[i].position.x; + // const dyP = player.position.y - powerUp[i].position.y; + // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { + // powerUps.onPickUp(player.position); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 3 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // break; //because the array order is messed up after splice + // } + // } + // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles + // } else { + // m.fieldCDcycle = m.cycle + 30; + // // m.resetHistory(); + // } + // } else { + // if (this.rewindCount !== 0) { + // m.fieldCDcycle = m.cycle + 30; + // m.resetHistory(); + // this.rewindCount = 0; + // simulation.wipe = function() { //set wipe to normal + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // } + // } + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // } + // m.drawRegenEnergy() + // }, + }, + ], + //************************************************************************************ + //************************************************************************************ + //************************************* SHIP *************************************** + //************************************************************************************ + //************************************************************************************ + isShipMode: false, + shipMode(thrust = 0.03, drag = 0.99, torque = 1.15, rotationDrag = 0.92) { // m.shipMode() //thrust = 0.03, drag = 0.99, torque = 1.15, rotationDrag = 0.92 + if (!m.isShipMode) { + //if wires remove them + for (let i = 0; i < mob.length; i++) { + if (!mob[i].freeOfWires) mob[i].freeOfWires = true + } + m.isShipMode = true + // simulation.isCheating = true + const points = [{ + x: 29.979168754143455, + y: 4.748337243898336 + }, + { + x: 27.04503734408824, + y: 13.7801138209198 + }, + { + x: 21.462582474874278, + y: 21.462582475257523 + }, + { + x: 13.780113820536943, + y: 27.045037344471485 + }, + { + x: 4.74833724351507, + y: 29.979168754526473 + }, + { + x: -4.748337245049098, + y: 29.979168754526473 + }, + { + x: -13.780113822071026, + y: 27.045037344471485 + }, + { + x: -21.46258247640829, + y: 21.462582475257523 + }, + { + x: -27.045037345621797, + y: 13.7801138209198 + }, + { + x: -29.979168755677012, + y: 4.748337243898336 + }, + { + x: -29.979168755677012, + y: -4.7483372446656045 + }, + { + x: -27.045037345621797, + y: -13.78011382168726 + }, + { + x: -21.46258247640829, + y: -21.462582476024817 + }, + { + x: -13.780113822071026, + y: -27.045037345239006 + }, + { + x: -4.748337245049098, + y: -29.97916875529422 + }, + { + x: 4.74833724351507, + y: -29.97916875529422 + }, + { + x: 13.780113820536943, + y: -27.045037345239006 + }, + { + x: 21.462582474874278, + y: -21.462582476024817 + }, + { + x: 27.04503734408824, + y: -13.78011382168726 + }, + { + x: 29.979168754143455, + y: -4.7483372446656045 + } + ] + // + Matter.Body.setVertices(player, Matter.Vertices.create(points, player)) + player.parts.pop() + player.parts.pop() + player.parts.pop() + player.parts.pop() + // Matter.Body.setDensity(player, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + m.defaultMass = 30 + Matter.Body.setMass(player, m.defaultMass); + player.friction = 0.01 + player.restitution = 0.2 + // player.frictionStatic = 0.1 + // Matter.Body.setInertia(player, Infinity); //disable rotation + + // const circle = Bodies.polygon(player.position.x, player.position.x, 30, 30) + // player.parts[0] = circle + // Matter.Body.setVertices(player.parts[0], Matter.Vertices.create(points, player.parts[0])) + m.spin = 0 + // m.groundControl = () => {} //disable entering ground + m.onGround = false + m.lastOnGroundCycle = 0 + // playerOnGroundCheck = () => {} + m.airControl = () => { //tank controls + player.force.y -= player.mass * simulation.g; //undo gravity + Matter.Body.setVelocity(player, { + x: drag * player.velocity.x, + y: drag * player.velocity.y + }); + if (input.up) { //forward thrust + player.force.x += thrust * Math.cos(m.angle) * tech.squirrelJump + player.force.y += thrust * Math.sin(m.angle) * tech.squirrelJump + } else if (input.down) { + player.force.x -= 0.6 * thrust * Math.cos(m.angle) + player.force.y -= 0.6 * thrust * Math.sin(m.angle) + } + //rotation + Matter.Body.setAngularVelocity(player, player.angularVelocity * rotationDrag) + if (input.right) { + player.torque += torque + } else if (input.left) { + player.torque -= torque + } + m.angle += m.spin + m.angle = player.angle + } + + + + + + // level.exit.drawAndCheck = () => { //fix this + // if ( + // player.position.x > level.exit.x && + // player.position.x < level.exit.x + 100 && + // player.position.y > level.exit.y - 150 && + // player.position.y < level.exit.y + 40 + // ) { + // level.nextLevel() + // } + // } + m.move = () => { + m.pos.x = player.position.x; + m.pos.y = player.position.y; + m.Vx = player.velocity.x; + m.Vy = player.velocity.y; + + //tracks the last 10s of player information + m.history.splice(m.cycle % 600, 1, { + position: { + x: player.position.x, + y: player.position.y, + }, + velocity: { + x: player.velocity.x, + y: player.velocity.y + }, + yOff: m.yOff, + angle: m.angle, + health: m.health, + energy: m.energy, + activeGun: b.activeGun + }); + } + + m.look = () => { //disable mouse aiming + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + + simulation.camera = () => { + const dx = simulation.mouse.x / window.innerWidth - 0.5 //x distance from mouse to window center scaled by window width + const dy = simulation.mouse.y / window.innerHeight - 0.5 //y distance from mouse to window center scaled by window height + const d = Math.max(dx * dx, dy * dy) + simulation.edgeZoomOutSmooth = (1 + 4 * d * d) * 0.04 + simulation.edgeZoomOutSmooth * 0.96 + + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + ctx.scale(simulation.zoom / simulation.edgeZoomOutSmooth, simulation.zoom / simulation.edgeZoomOutSmooth); //zoom in once centered + ctx.translate(-canvas.width2 + m.transX, -canvas.height2 + m.transY); //translate + //calculate in game mouse position by undoing the zoom and translations + simulation.mouseInGame.x = (simulation.mouse.x - canvas.width2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.width2 - m.transX; + simulation.mouseInGame.y = (simulation.mouse.y - canvas.height2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.height2 - m.transY; + } + + m.draw = () => { //just draw the circle + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(player.position.x, player.position.y); + ctx.rotate(player.angle); + + //thrust + if (input.up) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (input.down) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + + //body + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + + ctx.restore(); + } + + //fix collisions + collisionChecks = function (event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + //mob + (player,bullet,body) collisions + for (let k = 0; k < mob.length; k++) { + if (mob[k].alive && m.alive) { + if (pairs[i].bodyA === mob[k]) { + collideMob(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === mob[k]) { + collideMob(pairs[i].bodyA); + break; + } + + function collideMob(obj) { + //player + mob collision + if ( + m.immuneCycle < m.cycle && + // (obj === playerBody || obj === playerHead) && + (obj === player) && + !mob[k].isSlowed && !mob[k].isStunned + ) { + mob[k].foundPlayer(); + let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * simulation.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 + if (tech.isRewindAvoidDeath && (m.energy + 0.05) > Math.min(0.95, m.maxEnergy) && dmg > 0.01) { //CPT reversal runs in m.damage, but it stops the rest of the collision code here too + m.damage(dmg); + return + } + m.damage(dmg); + if (tech.isPiezo) m.energy += 20.48; + if (tech.isStimulatedEmission) powerUps.ejectTech() + if (mob[k].onHit) mob[k].onHit(); + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + //extra kick between player and mob //this section would be better with forces but they don't work... + let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); + Matter.Body.setVelocity(player, { + x: player.velocity.x + 8 * Math.cos(angle), + y: player.velocity.y + 8 * Math.sin(angle) + }); + Matter.Body.setVelocity(mob[k], { + x: mob[k].velocity.x - 8 * Math.cos(angle), + y: mob[k].velocity.y - 8 * Math.sin(angle) + }); + + if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && m.energy > 0.34 * m.maxEnergy) { + m.energy -= 0.33 * Math.max(m.maxEnergy, m.energy) + m.immuneCycle = 0; //player doesn't go immune to collision damage + mob[k].death(); + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: dmg * 2000, + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: dmg * 500, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + return; + // } + } + //mob + bullet collisions + if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { + obj.beforeDmg(mob[k]); //some bullets do actions when they hits things, like despawn //forces don't seem to work here + let dmg = m.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))) + if (tech.isCrit && mob[k].isStunned) dmg *= 4 + mob[k].damage(dmg); + if (mob[k].alive) mob[k].foundPlayer(); + if (mob[k].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.log(dmg + 1.1) * 40 * mob[k].damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + return; + } + //mob + body collisions + if (obj.classType === "body" && obj.speed > 6) { + const v = Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity)); + if (v > 9) { + let dmg = tech.blockDamage * m.dmgScale * v * obj.mass * (tech.isMobBlockFling ? 2 : 1); + if (mob[k].isShielded) dmg *= 0.7 + mob[k].damage(dmg, true); + if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && m.throwCycle > m.cycle) { + let type = tech.isEnergyNoAmmo ? "heal" : "ammo" + if (Math.random() < 0.4) { + type = "heal" + } else if (Math.random() < 0.4 && !tech.isSuperDeterminism) { + type = "research" + } + powerUps.spawn(mob[k].position.x, mob[k].position.y, type); + // for (let i = 0, len = Math.ceil(2 * Math.random()); i < len; i++) {} + } + + const stunTime = dmg / Math.sqrt(obj.mass) + if (stunTime > 0.5) mobs.statusStun(mob[k], 30 + 60 * Math.sqrt(stunTime)) + if (mob[k].alive && mob[k].distanceToPlayer2() < 1000000 && !m.isCloak) mob[k].foundPlayer(); + if (tech.fragments && obj.speed > 10 && !obj.hasFragmented) { + obj.hasFragmented = true; + b.targetedNail(obj.position, tech.fragments * 4) + } + if (mob[k].damageReduction) { + simulation.drawList.push({ + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.log(dmg + 1.1) * 40 * mob[k].damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + return; + } + } + } + } + } + } + } + } + }, +}; \ No newline at end of file diff --git a/ngon/js/powerup.js b/ngon/js/powerup.js new file mode 100644 index 00000000..cbc9b4da --- /dev/null +++ b/ngon/js/powerup.js @@ -0,0 +1,1595 @@ +let powerUp = []; + +const powerUps = { + ejectGraphic(color = "68, 102, 119") { + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 100, + color: `rgba(${color}, 0.8)`, + time: 4 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 75, + color: `rgba(${color}, 0.6)`, + time: 8 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 50, + color: `rgba(${color}, 0.3)`, + time: 12 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 25, + color: `rgba(${color}, 0.15)`, + time: 16 + }); + }, + healGiveMaxEnergy: false, //for tech 1st ionization energy + orb: { + research(num = 1) { + switch (num) { + case 1: + return `
` + case 2: + return ` +
+
+
       ` + case 3: + return ` +
+
+
+
          ` + case 4: + return ` +
+
+
+
+
            ` + case 5: + return ` +
+
+
+
+
+
              ` + case 6: + return ` +
+
+
+
+
+
+
                ` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
    ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + }, + ammo(num = 1) { + switch (num) { + case 1: + return `
` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
    ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + }, + heal(num = 1) { + if (powerUps.healGiveMaxEnergy) { + switch (num) { + case 1: + return `
` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
    ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + } else { + switch (num) { + case 1: + return `
` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
    ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + } + }, + tech(num = 1) { + return `
` + }, + coupling(num = 1) { + switch (num) { + case 1: + return `
` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
   ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + }, + boost(num = 1) { + switch (num) { + case 1: + return `
` + } + let text = '' + for (let i = 0; i < num; i++) { + text += `
` + } + text += '
    ' + for (let i = 0; i < num; i++) { + text += '  ' + } + return text + }, + }, + totalPowerUps: 0, //used for tech that count power ups at the end of a level + do() { }, + setPowerUpMode() { + if (tech.duplicationChance() > 0 || tech.isAnthropicTech) { + powerUps.draw = powerUps.drawDup + if (tech.isPowerUpsVanish) { + if (tech.isHealAttract) { + powerUps.do = () => { + powerUps.dupExplode(); + powerUps.draw(); + powerUps.attractHeal(); + } + } else { + powerUps.do = () => { + powerUps.dupExplode(); + powerUps.draw(); + } + } + } else if (tech.isHealAttract) { + powerUps.do = () => { + powerUps.draw(); + powerUps.attractHeal(); + } + } else { + powerUps.do = () => powerUps.draw(); + } + tech.maxDuplicationEvent() //check to see if hitting 100% duplication + } else { + powerUps.draw = powerUps.drawCircle + if (tech.isHealAttract) { + powerUps.do = () => { + powerUps.draw(); + powerUps.attractHeal(); + } + } else { + powerUps.do = powerUps.draw + } + } + }, + draw() { }, + drawCircle() { + ctx.globalAlpha = 0.4 * Math.sin(simulation.cycle * 0.15) + 0.6; + for (let i = 0, len = powerUp.length; i < len; ++i) { + ctx.beginPath(); + ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + ctx.fillStyle = powerUp[i].color; + ctx.fill(); + } + ctx.globalAlpha = 1; + }, + drawDup() { + ctx.globalAlpha = 0.4 * Math.sin(simulation.cycle * 0.15) + 0.6; + for (let i = 0, len = powerUp.length; i < len; ++i) { + ctx.beginPath(); + if (powerUp[i].isDuplicated) { + let vertices = powerUp[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } else { + ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + } + ctx.fillStyle = powerUp[i].color; + ctx.fill(); + } + ctx.globalAlpha = 1; + }, + attractHeal() { + for (let i = 0; i < powerUp.length; i++) { //attract heal power ups to player + if (powerUp[i].name === "heal") { + let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.015 * powerUp[i].mass) + powerUp[i].force.x += attract.x; + powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7)); + } + } + }, + dupExplode() { + for (let i = 0, len = powerUp.length; i < len; ++i) { + if (powerUp[i].isDuplicated) { + if (Math.random() < 0.003) { // (1-0.003)^240 = chance to be removed after 4 seconds, 240 = 4 seconds * 60 cycles per second + b.explosion(powerUp[i].position, 175 + (11 + 3 * Math.random()) * powerUp[i].size); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break + } + if (Math.random() < 0.3) { //draw electricity + const mag = 4 + powerUp[i].size / 5 + let unit = Vector.rotate({ x: mag, y: mag }, 2 * Math.PI * Math.random()) + let path = { x: powerUp[i].position.x + unit.x, y: powerUp[i].position.y + unit.y } + ctx.beginPath(); + ctx.moveTo(path.x, path.y); + for (let i = 0; i < 6; i++) { + unit = Vector.rotate(unit, 4 * (Math.random() - 0.5)) + path = Vector.add(path, unit) + ctx.lineTo(path.x, path.y); + } + ctx.lineWidth = 0.5 + 2 * Math.random(); + ctx.strokeStyle = "#000" + ctx.stroke(); + } + } + } + }, + choose(type, index) { + if (type === "gun") { + b.giveGuns(index) + let text = `b.giveGuns("${b.guns[index].name}")` + if (b.inventory.length === 1) text += `
input.key.gun: ["MouseLeft"]` + if (b.inventory.length === 2) text += ` +
input.key.nextGun: ["${input.key.nextGun}","MouseWheel"] +
input.key.previousGun: ["${input.key.previousGun}","MouseWheel"]` + simulation.makeTextLog(text); + } else if (type === "field") { + m.setField(index) + } else if (type === "tech") { + // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false + simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}")`); + tech.giveTech(index) + } + powerUps.endDraft(type); + }, + showDraft() { + //disable clicking for 1/2 a second to prevent mistake clicks + document.getElementById("choose-grid").style.pointerEvents = "none"; + document.body.style.cursor = "none"; + setTimeout(() => { + // if (!tech.isNoDraftPause) + document.body.style.cursor = "auto"; + document.getElementById("choose-grid").style.pointerEvents = "auto"; + document.getElementById("choose-grid").style.transitionDuration = "0s"; + }, 400); + simulation.isChoosing = true; //stops p from un pausing on key down + + if (!simulation.paused) { + if (tech.isNoDraftPause) { + document.getElementById("choose-grid").style.opacity = "1" + } else { + simulation.paused = true; + document.getElementById("choose-grid").style.opacity = "1" + } + document.getElementById("choose-grid").style.transitionDuration = "0.5s"; //how long is the fade in on + document.getElementById("choose-grid").style.visibility = "visible" + + requestAnimationFrame(() => { + ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }); + // document.getElementById("pause-grid-right").style.opacity = "0.7" + // document.getElementById("pause-grid-left").style.opacity = "0.7" + } + // build.pauseGrid() + }, + endDraft(type, isCanceled = false) { //type should be a gun, tech, or field + if (isCanceled) { + if (tech.isCancelTech && Math.random() < 0.85 && type !== "entanglement") { + // powerUps.research.use('tech') + powerUps[type].effect(); + return + } + if (tech.isCancelDuplication) { + tech.duplication += 0.041 + tech.maxDuplicationEvent() + simulation.makeTextLog(`tech.duplicationChance() += ${0.043}`) + simulation.circleFlare(0.043); + } + if (tech.isCancelRerolls) { + for (let i = 0, len = 5 + 5 * Math.random(); i < len; i++) { + let spawnType + if (Math.random() < 0.4 && !tech.isEnergyNoAmmo) { + spawnType = "ammo" + } else if (Math.random() < 0.33 && !tech.isSuperDeterminism) { + spawnType = "research" + } else { + spawnType = "heal" + } + powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), spawnType, false); + } + } + if (tech.isCancelCouple) powerUps.spawnDelay("coupling", 5) + // if (tech.isCancelTech && Math.random() < 0.3) { + // powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "tech", false); + // simulation.makeTextLog(`options exchange: returns 1 tech`) + // } + // if (tech.isBanish && type === 'tech') { // banish researched tech by adding them to the list of banished tech + // const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2 + // for (let i = 0; i < banishLength; i++) { + // const index = powerUps.tech.choiceLog.length - i - 1 + // if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) { + // tech.tech[powerUps.tech.choiceLog[index]].isBanished = true + // } + // } + // simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) + // } + } + if (tech.isAnsatz && powerUps.research.count < 1) { + for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + } + // document.getElementById("choose-grid").style.display = "none" + document.getElementById("choose-grid").style.visibility = "hidden" + document.getElementById("choose-grid").style.opacity = "0" + + document.body.style.cursor = "none"; + // document.body.style.overflow = "hidden" + // if (m.alive){} + if (simulation.paused) requestAnimationFrame(cycle); + if (m.alive) simulation.paused = false; + simulation.isChoosing = false; //stops p from un pausing on key down + build.unPauseGrid() + if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 30 cycles + if (m.holdingTarget) m.drop(); + }, + coupling: { + name: "coupling", + color: "#0ae", //"#0cf", + size() { + return 13; + }, + effect() { + m.couplingChange(1) + }, + // spawnDelay(num) { + // let count = num + // let respawnDrones = () => { + // if (count > 0) { + // requestAnimationFrame(respawnDrones); + // if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) + // count-- + // const where = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) } + // powerUps.spawn(where.x, where.y, "coupling"); + // } + // } + // } + // requestAnimationFrame(respawnDrones); + // } + }, + boost: { + name: "boost", + color: "#f55", //"#0cf", + size() { + return 11; + }, + endCycle: 0, + duration: null, //set by "tech: band gap" + damage: null, //set by "tech: band gap" + effect() { + powerUps.boost.endCycle = m.cycle + Math.floor(Math.max(0, powerUps.boost.endCycle - m.cycle) * 0.6) + powerUps.boost.duration //duration+seconds plus 2/3 of current time left + }, + draw() { + // console.log(this.endCycle) + if (powerUps.boost.endCycle > m.cycle) { + ctx.strokeStyle = "rgba(255,0,0,0.8)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + ctx.beginPath(); + const arc = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + ctx.arc(m.pos.x, m.pos.y, 28, m.angle - Math.PI * arc, m.angle + Math.PI * arc); //- Math.PI / 2 + ctx.lineWidth = 4 + ctx.stroke(); + } + }, + }, + research: { + count: 0, + name: "research", + color: "#f7b", + size() { + return 20; + }, + effect() { + powerUps.research.changeRerolls(1) + }, + isMakingBots: false, //to prevent bot fabrication from running 2 sessions at once + changeRerolls(amount) { + if (amount !== 0) powerUps.research.count += amount + if (tech.isRerollBots && !this.isMakingBots) { + let cycle = () => { + const cost = 2 + Math.floor(0.2 * b.totalBots()) + if (m.alive && powerUps.research.count >= cost) { + requestAnimationFrame(cycle); + this.isMakingBots = true + } else { + this.isMakingBots = false + } + if (!simulation.paused && !simulation.isChoosing && !(simulation.cycle % 60)) { + powerUps.research.count -= cost + b.randomBot() + if (tech.renormalization) { + for (let i = 0; i < cost; i++) { + if (Math.random() < 0.46) { + m.fieldCDcycle = m.cycle + 20; + powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + } + } + } + } + } + requestAnimationFrame(cycle); + } + if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) { + document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}` + } + if (tech.renormalization && Math.random() < 0.46 && amount < 0) { + for (let i = 0, len = -amount; i < len; i++) powerUps.spawn(m.pos.x, m.pos.y, "research"); + } + if (tech.isRerollHaste) { + if (powerUps.research.count === 0) { + tech.researchHaste = 0.66; + b.setFireCD(); + } else { + tech.researchHaste = 1; + b.setFireCD(); + } + } + }, + currentRerollCount: 0, + use(type) { //runs when you actually research a list of selections, type can be field, gun, or tech + if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + tech.addJunkTechToPool(tech.junkResearchNumber * 0.01) + } else { + powerUps.research.changeRerolls(-1) + } + powerUps.research.currentRerollCount++ + // if (tech.isBanish && type === 'tech') { // banish researched tech + // const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2 + // for (let i = 0; i < banishLength; i++) { + // const index = powerUps.tech.choiceLog.length - i - 1 + // if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true + // } + // simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) + // } + if (tech.isResearchReality) { + m.switchWorlds() + simulation.trails() + simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + } + powerUps[type].effect(); + }, + }, + heal: { + name: "heal", + color: "#0eb", + size() { + return Math.sqrt(0.1 + 0.25) * 40 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)) * (tech.isFlipFlopOn && tech.isFlipFlopHealth ? Math.sqrt(2) : 1); //(simulation.healScale ** 0.25) gives a smaller radius as heal scale goes down + }, + effect() { + if (!tech.isEnergyHealth && m.alive) { + let heal = (this.size / 40 / (simulation.healScale ** 0.25)) ** 2 //simulation.healScale is undone here because heal scale is already properly affected on m.addHealth() + // console.log("size = " + this.size, "heal = " + heal) + if (heal > 0) { + const overHeal = m.health + heal * simulation.healScale - m.maxHealth //used with tech.isOverHeal + const healOutput = Math.min(m.maxHealth - m.health, heal) * simulation.healScale + m.addHealth(heal); + if (healOutput > 0) simulation.makeTextLog(`m.health += ${(healOutput).toFixed(3)}`) //
${m.health.toFixed(3)} + if (tech.isOverHeal && overHeal > 0) { //tech quenching + const scaledOverHeal = overHeal // * 0.9 + m.damage(scaledOverHeal); + simulation.makeTextLog(`m.health -= ${(scaledOverHeal).toFixed(3)}`) //
${m.health.toFixed(3)} + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: scaledOverHeal * 500 * simulation.healScale, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + tech.extraMaxHealth += scaledOverHeal * simulation.healScale //increase max health + m.setMaxHealth(); + } else if (overHeal > 0.1) { + requestAnimationFrame(() => { + powerUps.directSpawn(this.position.x, this.position.y, "heal", true, null, overHeal * 40 * (simulation.healScale ** 0.25))// directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + }); + } + if (tech.isHealBrake) { + const totalTime = 900 + //check if you already have this effect + let foundActiveEffect = false + for (let i = 0; i < simulation.ephemera.length; i++) { + if (simulation.ephemera[i].name === "healPush") { + foundActiveEffect = true + simulation.ephemera[i].count = 0.5 * simulation.ephemera[i].count + totalTime //add time + simulation.ephemera[i].scale = 0.5 * (simulation.ephemera[i].scale + Math.min(Math.max(0.6, heal * 6), 2.3)) //take average of scale + } + } + if (!foundActiveEffect) { + simulation.ephemera.push({ + name: "healPush", + count: totalTime, //cycles before it self removes + range: 0, + scale: Math.min(Math.max(0.7, heal * 4), 2.2), //typically heal is 0.35 + do() { + this.count-- + if (this.count < 0) simulation.removeEphemera(this.name) + this.range = this.range * 0.99 + 0.01 * (300 * this.scale + 100 * Math.sin(m.cycle * 0.022)) + if (this.count < 120) this.range -= 5 * this.scale + this.range = Math.max(this.range, 1) //don't go negative + // const range = 300 + 100 * Math.sin(m.cycle * 0.022) + for (let i = 0; i < mob.length; i++) { + const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) + if (distance < this.range) { + const cap = mob[i].isShielded ? 3 : 1 + if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, this.range, 0, 2 * Math.PI); + ctx.fillStyle = "hsla(200,50%,61%,0.18)"; + ctx.fill(); + }, + }) + } + } + } + } + if (powerUps.healGiveMaxEnergy) { + tech.healMaxEnergyBonus += 0.08 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1) + m.setMaxEnergy(); + } + }, + spawn(x, y, size) { //used to spawn a heal with a specific size / heal amount, not normally used + powerUps.directSpawn(x, y, "heal", false, null, size) + if (Math.random() < tech.duplicationChance()) { + powerUps.directSpawn(x, y, "heal", false, null, size) + powerUp[powerUp.length - 1].isDuplicated = true + } + } + }, + ammo: { + name: "ammo", + color: "#467", + size() { + return 17; + }, + effect() { + if (b.inventory.length > 0) { + if (tech.isAmmoForGun && b.activeGun !== null) { //give extra ammo to one gun only with tech logistics + const target = b.guns[b.activeGun] + if (target.ammo !== Infinity) { + if (tech.ammoCap) { + const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap * 0.8) //0.7 is average + target.ammo = ammoAdded + // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) + } else { + const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack * 0.8) + target.ammo += ammoAdded + // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) + } + } + } else { //give ammo to all guns in inventory + // let textLog = "" + for (let i = 0, len = b.inventory.length; i < len; i++) { + const target = b.guns[b.inventory[i]] + if (target.ammo !== Infinity) { + if (tech.ammoCap) { + const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average + target.ammo = ammoAdded + // textLog += `${target.name}.ammo = ${ammoAdded}
` + } else { + const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + target.ammo += ammoAdded + // textLog += `${target.name}.ammo += ${ammoAdded}
` + } + } + } + // simulation.makeTextLog(textLog) + } + // } else { //give ammo to all guns in inventory + // for (let i = 0, len = b.inventory.length; i < len; i++) { + // const target = b.guns[b.inventory[i]] + // if (target.ammo !== Infinity) { + // if (tech.ammoCap) { + // const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average + // target.ammo = ammoAdded + // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) + // } else { + // const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + // target.ammo += ammoAdded + // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) + // } + // } + // } + // } + simulation.updateGunHUD(); + } + } + }, + cancelText(type) { + // if (localSettings.isHideImages) { } + + if (tech.isSuperDeterminism) { + return `
` + } else if (tech.isCancelTech) { + return `
randomize
` + } else { + return `
cancel
` + } + }, + researchText(type) { + let text = "" + if (type === "entanglement") { + text += `
entanglement
` + } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + text += `
` // style = "margin-left: 192px; margin-right: -192px;" + tech.junkResearchNumber = Math.ceil(4 * Math.random()) + text += `
` + for (let i = 0; i < tech.junkResearchNumber; i++) { + text += `
` + } + text += `
  pseudoscience
` + } else if (powerUps.research.count > 0) { + text += `
` // style = "margin-left: 192px; margin-right: -192px;" + text += `
` + for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` + text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` + } else { + text += `
` + } + return text + }, + researchAndCancelText(type) { + let text = `
` + if (type === "entanglement") { + text += `entanglement` //‌ + } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + text += `` // style = "margin-left: 192px; margin-right: -192px;" + tech.junkResearchNumber = Math.ceil(4 * Math.random()) + text += `
` + for (let i = 0, len = tech.junkResearchNumber; i < len; i++) { + text += `
` + } + text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` + } else if (powerUps.research.count > 0) { + text += `` // style = "margin-left: 192px; margin-right: -192px;" + text += `
` + let researchCap = 18 + if (tech.isCancelTech) researchCap -= 2 + if (canvas.width < 1951) researchCap -= 3 + if (canvas.width < 1711) researchCap -= 4 + for (let i = 0, len = Math.min(powerUps.research.count, researchCap); i < len; i++) { + text += `
` + } + text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` + } else { + text += `research` //‌ + } + if (tech.isSuperDeterminism) { + text += `cancel` + } else if (tech.isCancelTech) { + text += `randomize` + } else { + text += `cancel` + } + + return text + "
" + }, + buildColumns(totalChoices, type) { + let width + if (canvas.width < 1710) { + width = "285px" + } else if (canvas.width < 1950) { + width = "340px" + } else { + width = "384px" + } + + let text = "" + if (localSettings.isHideImages) { + document.getElementById("choose-grid").style.gridTemplateColumns = width + text += powerUps.researchAndCancelText(type) + } else if (totalChoices === 1 || canvas.width < 1200) { + document.getElementById("choose-grid").style.gridTemplateColumns = width + text += powerUps.researchAndCancelText(type) + // console.log('hi') + // text += powerUps.cancelText(type) + // text += powerUps.researchText(type) + } else if (totalChoices === 2) { + document.getElementById("choose-grid").style.gridTemplateColumns = `repeat(2, ${width})` + text += powerUps.researchText(type) + text += powerUps.cancelText(type) + } else { + document.getElementById("choose-grid").style.gridTemplateColumns = `repeat(3, ${width})` + text += "
" + text += powerUps.researchText(type) + text += powerUps.cancelText(type) + } + return text + }, + // researchAndCancelText(type) { + // let text = "
" + // if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + // text += `
` // style = "margin-left: 192px; margin-right: -192px;" + // tech.junkResearchNumber = Math.ceil(4 * Math.random()) + // text += `
` + // for (let i = 0; i < tech.junkResearchNumber; i++) text += `
` + // text += `
  pseudoscience
` + // } else if (powerUps.research.count > 0) { + // text += `
` // style = "margin-left: 192px; margin-right: -192px;" + // text += `
` + // for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` + // text += `
  ${tech.isResearchReality?"alternate reality": "research"}
` + // } else { + // text += `
` + // } + // return text + '
' + // }, + hideStyle: `style="height:auto; border: none; background-color: transparent;"`, + gunText(choose, click) { + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/gun/${b.guns[choose].name}.webp');"` + return `
+
+
  ${b.guns[choose].name}
+ ${b.guns[choose].description}
` + }, + fieldText(choose, click) { + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random() * 10) : ""}.webp');"` + return `
+
+
  ${m.fieldUpgrades[choose].name}
+ ${m.fieldUpgrades[choose].description}
` + }, + techText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages || tech.tech[choose].isLore ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + return `
+
+
  ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, + skinTechText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + return `
+
+
+ +
+
+
+           ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, + fieldTechText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + return `
+
+
+ +
+
+
+           ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, + gunTechText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + return `
+
+
+ +
+
+
+           ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, + junkTechText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"` + if (!localSettings.isHideImages) { + setTimeout(() => { //delay so that the html element exists + if (tech.tech[choose].url === undefined) { //if on url has been set yet + const url = "https://images.search.yahoo.com/search/images?p=" + tech.tech[choose].name; + fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second + .then((response) => response.text()) + .then((html) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + const elements = doc.getElementsByClassName("ld"); + // console.log(i, elements[i].getAttribute("data"), JSON.parse(elements[i].getAttribute("data")).iurl) + const index = Math.floor(Math.random() * 4) //randomly choose from the first 4 images + if (parseInt(JSON.parse(elements[index].getAttribute("data")).s.slice(0, -2)) < 500) { //make sure it isn't too big + tech.tech[choose].url = JSON.parse(elements[index].getAttribute("data")).iurl //store the url + document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image + } else if (parseInt(JSON.parse(elements[index + 1].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + tech.tech[choose].url = JSON.parse(elements[index + 1].getAttribute("data")).iurl + document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + } else if (parseInt(JSON.parse(elements[index + 2].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + tech.tech[choose].url = JSON.parse(elements[index + 2].getAttribute("data")).iurl + document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + } + }); + } else { + document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + } + }, 1); + } + return `
+
+
  ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, + incoherentTechText(choose, click) { + // text += `
${tech.tech[choose].name} - incoherent
` + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + return `
+
+
incoherent


+
` + }, + gun: { + name: "gun", + color: "#26a", + size() { + return 35; + }, + effect() { + if (m.alive) { + let options = []; + for (let i = 0; i < b.guns.length; i++) { + if (!b.guns[i].have) options.push(i); + } + let totalChoices = Math.min(options.length, (tech.isDeterminism ? 1 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8))) + if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay + function removeOption(index) { + for (let i = 0; i < options.length; i++) { + if (options[i] === index) { + options.splice(i, 1) //remove a previous choice from option pool + return + } + } + } + //check for guns that were a choice last time and remove them + for (let i = 0; i < b.guns.length; i++) { + if (options.length - 1 < totalChoices) break //you have to repeat choices if there are not enough choices left to display + if (b.guns[i].isRecentlyShown) removeOption(i) + } + for (let i = 0; i < b.guns.length; i++) b.guns[i].isRecentlyShown = false //reset recently shown back to zero + if (options.length > 0) { + let text = powerUps.buildColumns(totalChoices, "gun") + for (let i = 0; i < totalChoices; i++) { + const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options + // text += `
  ${b.guns[choose].name}
${b.guns[choose].description}
` + text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`) + + b.guns[choose].isRecentlyShown = true + removeOption(choose) + if (options.length < 1) break + } + if (tech.isExtraBotOption) { + const botTech = [] //make an array of bot options + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i) + } + if (botTech.length > 0) { //pick random bot tech + // const choose = botTech[Math.floor(Math.random() * botTech.length)]; + // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + const choose = botTech[Math.floor(Math.random() * botTech.length)]; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + text += `
+
+
⭓▸●■   ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + } + } + if (tech.isOneGun && b.inventory.length > 0) text += `
replaces your current gun
` + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + } + } + }, + }, + field: { + name: "field", + color: "#0cf", + size() { + return 45; + }, + effect() { + if (m.alive) { + let options = []; + for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter + if (i !== m.fieldMode) options.push(i); + } + let totalChoices = Math.min(options.length, (tech.isDeterminism ? 1 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8))) + if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay + + function removeOption(index) { + for (let i = 0; i < options.length; i++) { + if (options[i] === index) { + options.splice(i, 1) //remove a previous choice from option pool + return + } + } + } + //check for fields that were a choice last time and remove them + for (let i = 0; i < m.fieldUpgrades.length; i++) { + if (options.length - 1 < totalChoices) break //you have to repeat choices if there are not enough choices left to display + if (m.fieldUpgrades[i].isRecentlyShown) removeOption(i) + } + for (let i = 0; i < m.fieldUpgrades.length; i++) m.fieldUpgrades[i].isRecentlyShown = false //reset recently shown back to zero + + if (options.length > 0 || tech.isExtraBotOption) { + let text = powerUps.buildColumns(totalChoices, "field") + for (let i = 0; i < totalChoices; i++) { + const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options + //text += `
  ${m.fieldUpgrades[choose].name}
${m.fieldUpgrades[choose].description}
` //default + text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`) + m.fieldUpgrades[choose].isRecentlyShown = true + removeOption(choose) + if (options.length < 1) break + } + if (tech.isExtraBotOption) { + const botTech = [] //make an array of bot options + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i) + } + if (botTech.length > 0) { //pick random bot tech + // const choose = botTech[Math.floor(Math.random() * botTech.length)]; + // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + const choose = botTech[Math.floor(Math.random() * botTech.length)]; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + text += `
+
+
⭓▸●■   ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + } + } + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + } + } + }, + }, + tech: { + name: "tech", + color: "hsl(246,100%,77%)", //"#a8f", + size() { + return 42; + }, + effect() { + if (m.alive) { + let junkCount = 0 //used for junk estimation + let totalCount = 0 //used for junk estimation + let options = []; //generate all options + optionLengthNoDuplicates = 0 + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) { + totalCount += tech.tech[i].frequency + if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency + if (tech.tech[i].frequency > 0) optionLengthNoDuplicates++ + for (let j = 0, len = tech.tech[i].frequency; j < len; j++) options.push(i); + } + } + function removeOption(index) { + for (let i = options.length - 1; i > -1; i--) { + if (index === options[i]) { + options.splice(i, 1) //remove all copies of that option form the options array (some tech are in the options array multiple times because of frequency) + optionLengthNoDuplicates-- + } + if (options.length < 1) return; + } + } + //set total choices + let totalChoices = (tech.isDeterminism ? 1 : 3 + tech.extraChoices + 2 * (m.fieldMode === 8)) + if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay + if (optionLengthNoDuplicates < totalChoices + 1) { //if not enough options for all the choices + totalChoices = optionLengthNoDuplicates + if (tech.isBanish) { //when you run out of options eject banish + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i, true) + } + simulation.makeTextLog(`decoherence tech ejected`) + simulation.makeTextLog(`options reset`) + } + } + if (tech.tooManyTechChoices) { + tech.tooManyTechChoices = false + totalChoices = optionLengthNoDuplicates + } + if (optionLengthNoDuplicates > totalChoices) { //check for tech that were a choice last time and remove them + for (let i = 0; i < tech.tech.length; i++) { + if (optionLengthNoDuplicates > totalChoices) { + if (tech.tech[i].isRecentlyShown) removeOption(i) + } else { + break //you have to repeat choices if there are not enough choices left to display + } + + } + } + for (let i = 0; i < tech.tech.length; i++) tech.tech[i].isRecentlyShown = false //reset recently shown back to zero + if (options.length > 0) { + let text = powerUps.buildColumns(totalChoices, "tech") + for (let i = 0; i < totalChoices; i++) { + if (options.length < 1) break + const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options + if (tech.isBanish) { + tech.tech[choose].isBanished = true + if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates}`) + } + removeOption(choose) //move from future options pool to avoid repeats on this selection + tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up + const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + if (tech.tech[choose].isFieldTech) { + text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isGunTech) { + text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isJunk) { + text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isSkin) { + text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else { //normal tech + text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + } + if (options.length < 1) break + } + if (tech.isExtraBotOption) { + const botTech = [] //make an array of bot options + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isRecentlyShown) botTech.push(i) + } + if (botTech.length > 0) { //pick random bot tech + // const choose = botTech[Math.floor(Math.random() * botTech.length)]; + // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + const choose = botTech[Math.floor(Math.random() * botTech.length)]; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + text += `
+
+
⭓▸●■   ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + } + } + if (tech.isMassProduction) { + // const techOptions = [] //make an array of bot options + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isMassProduction) techOptions.push(i) + // } + // if (techOptions.length > 0) { //pick random bot tech + // const choose = techOptions[Math.floor(Math.random() * techOptions.length)]; + // const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + // text += `
+ //
+ //
${tech.tech[choose].name}
+ // ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + // } + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isMassProduction) { + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[i].name}.webp');"` + text += `
+
+
${tech.tech[i].name}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + } + } + } + if (tech.isExtraGunField) { + if (Math.random() > 0.5 && b.inventory.length < b.guns.length) { + let gunOptions = []; + for (let i = 0; i < b.guns.length; i++) { + if (!b.guns[i].have) gunOptions.push(i); + } + const pick = gunOptions[Math.floor(Math.seededRandom(0, gunOptions.length))] //pick an element from the array of options + // text += `
  ${b.guns[pick].name}
${b.guns[pick].description}
` + text += powerUps.gunText(pick, `powerUps.choose('gun',${pick})`) + } else { + let fieldOptions = []; + for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter + if (i !== m.fieldMode) fieldOptions.push(i); + } + const pick = fieldOptions[Math.floor(Math.seededRandom(0, fieldOptions.length))] //pick an element from the array of options + // text += `
  ${m.fieldUpgrades[pick].name}
${m.fieldUpgrades[pick].description}
` + text += powerUps.fieldText(pick, `powerUps.choose('field',${pick})`) + } + } + // if (tech.isMicroTransactions && powerUps.research.count > 0) { + // const skins = [] //find skins + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].isSkin) skins.push(i) + // } + // const choose = skins[Math.floor(Math.seededRandom(0, skins.length))] //pick an element from the array of options + + // text += `
microtransaction: ${tech.tech[choose].name}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + // } + if (tech.isBrainstorm && !tech.isBrainstormActive && !simulation.isChoosing) { + tech.isBrainstormActive = true + + let count = 1 + let timeStart = performance.now() + const cycle = (timestamp) => { + // if (timeStart === undefined) timeStart = timestamp + // console.log(timestamp, timeStart) + if (timestamp - timeStart > tech.brainStormDelay * count && simulation.isChoosing) { + count++ + powerUps.tech.effect(); + document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay + document.body.style.cursor = "auto"; + document.getElementById("choose-grid").style.transitionDuration = "0s"; + } + if (count < 5 && simulation.isChoosing) { + requestAnimationFrame(cycle); + } else { + tech.isBrainstormActive = false + } + } + requestAnimationFrame(cycle); + + // count++ + // if (count < tech.brainStormDelay * 5 && simulation.isChoosing) { + // if (!(count % tech.brainStormDelay)) { + // powerUps.tech.effect(); + // document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay + // document.body.style.cursor = "auto"; + // document.getElementById("choose-grid").style.transitionDuration = "0s"; + // } + // requestAnimationFrame(cycle); + // } else { + // tech.isBrainstormActive = false + // } + } + // if (localSettings.isHideImages) text += powerUps.researchText('tech') + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + + //fade in all circles + // requestAnimationFrame(() => { + // var elements = document.getElementsByClassName('circle-grid'); + // for (var i in elements) { + // if (elements.hasOwnProperty(i)) { + // elements[i].style.opacity = '1'; + // } + // } + // }); + } + } + }, + }, + entanglement: { + name: "entanglement", + color: "#fff", //"hsl(248,100%,65%)", + size() { + return 40 + }, + effect() { + if (m.alive && localSettings.entanglement) { + // let text = "" + // document.getElementById("choose-grid").style.gridTemplateColumns = "384px 384px 384px" + let text = powerUps.buildColumns(3, "entanglement") + + // text += powerUps.researchText('tech') + // text += "
" + // text += "
entanglement
" + // text += `
cancel
` //powerUps.cancelText('tech') + if (localSettings.entanglement.fieldIndex && localSettings.entanglement.fieldIndex !== m.fieldMode) { + const choose = localSettings.entanglement.fieldIndex //add field + text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`) + } + for (let i = 0; i < localSettings.entanglement.gunIndexes.length; i++) { //add guns + const choose = localSettings.entanglement.gunIndexes[i] + //check if you always have this gun + let alreadyHasGun = false + for (let j = 0; j < b.inventory.length; j++) { + if (b.inventory[j] === choose) alreadyHasGun = true + } + // text += `
  ${b.guns[gun].name}
${b.guns[gun].description}
` + if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`) + } + for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech + let choose = localSettings.entanglement.techIndexes[i] + const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + + if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { + // text += `
${tech.tech[choose].name} - incoherent
` + text += powerUps.incoherentTechText(choose) + } else { + if (tech.tech[choose].isFieldTech) { + text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isGunTech) { + text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isLore) { + text += `
  ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + } else if (tech.tech[choose].isJunk) { + text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isSkin) { + text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else { //normal tech + text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + } + } + } + // document.getElementById("choose-grid").classList.add("flipX"); + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + localSettings.entanglement = undefined + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + }, + }, + spawnDelay(type, num) { + let count = num + let cycle = () => { + if (count > 0) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) + count-- + const where = { + x: m.pos.x + 50 * (Math.random() - 0.5), + y: m.pos.y + 50 * (Math.random() - 0.5) + } + powerUps.spawn(where.x, where.y, type); + } + } + } + requestAnimationFrame(cycle); + }, + onPickUp(who) { + powerUps.research.currentRerollCount = 0 + if (tech.isTechDamage && who.name === "tech") m.damage(0.1) + if (tech.isMassEnergy) m.energy += 2; + if (tech.isMineDrop && bullet.length < 150 && Math.random() < 0.5) { + if (tech.isLaserMine && input.down) { + b.laserMine(who.position) + } else { + b.mine(who.position, { x: 0, y: 0 }, 0) + } + } + if (tech.isRelay) { + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false + if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = OFF` + m.eyeFillColor = 'transparent' + } else { + tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit + if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#0cf' + } + if (tech.isRelayEnergy) m.setMaxEnergy(); + } + }, + spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up + if (!tech.isEnergyHealth && (Math.random() * Math.random() - 0.3 > Math.sqrt(m.health)) || Math.random() < 0.04) { //spawn heal chance is higher at low health + powerUps.spawn(x, y, "heal"); + return; + } + if (Math.random() < 0.15 && b.inventory.length > 0) { + powerUps.spawn(x, y, "ammo"); + return; + } + if (Math.random() < 0.0007 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 3 + powerUps.spawn(x, y, "gun"); + return; + } + // if (Math.random() < 0.0027 * (22 - tech.totalCount)) { //a new tech has a low chance for each not acquired tech up to 25 + if (Math.random() < 0.005 * (10 - level.levelsCleared)) { //a new tech has a low chance that decreases in later levels + powerUps.spawn(x, y, "tech"); + return; + } + if (Math.random() < 0.0015) { + powerUps.spawn(x, y, "field"); + return; + } + if (tech.isCouplingPowerUps && Math.random() < 0.17) { + powerUps.spawn(x, y, "coupling"); + return; + } + if (tech.isBoostPowerUps && Math.random() < 0.14) { + powerUps.spawn(x, y, "boost"); + return; + } + // if (Math.random() < 0.01) { + // powerUps.spawn(x, y, "research"); + // return; + // } + }, + randomPowerUpCounter: 0, + isFieldSpawned: false, //makes it so a field spawns once but not more times + spawnBossPowerUp(x, y) { //boss spawns field and gun tech upgrades + if (level.levels[level.onLevel] !== "final") { + // if (level.levelsCleared === 1) powerUps.spawn(x, y, "field") + // if (m.fieldMode === 0 && !m.coupling) { + if (!powerUps.isFieldSpawned) { + powerUps.isFieldSpawned = true + powerUps.spawn(x, y, "field") + } else { + powerUps.randomPowerUpCounter++; + powerUpChance(Math.max(level.levelsCleared, 10) * 0.1) + } + if (!(simulation.difficulty > spawn.secondaryBossThreshold)) { + powerUps.randomPowerUpCounter += 0.6; + powerUpChance(Math.max(level.levelsCleared, 6) * 0.1) + } + + function powerUpChance(chanceToFail) { + if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { + powerUps.randomPowerUpCounter = 0; + if (Math.random() < 0.97) { + powerUps.spawn(x, y, "tech") + } else { + powerUps.spawn(x, y, "gun") + } + } else { + if (m.health < 0.65 && !tech.isEnergyHealth) { + powerUps.spawn(x, y, "heal"); + powerUps.spawn(x, y, "heal"); + } else { + powerUps.spawn(x, y, "ammo"); + powerUps.spawn(x, y, "ammo"); + } + } + } + } + }, + chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris + if (Math.random() < 0.5) { + powerUps.spawn(x, y, "heal", false); + } else { + powerUps.spawn(x, y, "ammo", false); + } + }, + addResearchToLevel() { //add a random power up to a location that has a mob, mostly used to give each level one randomly placed research + if (mob.length && Math.random() < 0.45 - 0.3 * (simulation.difficultyMode > 5)) { //lower chance on why difficulty + const index = Math.floor(Math.random() * mob.length) + powerUps.spawn(mob[index].position.x, mob[index].position.y, "research"); + } + }, + spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun + if (level.levelsCleared < 4) { //runs on first 4 levels on all difficulties + if (level.levelsCleared > 1) powerUps.spawn(x, y, "tech") + if (b.inventory.length === 0) { + powerUps.spawn(x, y, "gun", false); //first gun + } else if (tech.totalCount === 0) { //first tech + powerUps.spawn(x, y, "tech", false); + } else if (b.inventory.length === 1) { //second gun or extra ammo + if (Math.random() < 0.4) { + powerUps.spawn(x, y, "gun", false); + } else { + for (let i = 0; i < 5; i++) powerUps.spawn(x, y, "ammo", false); + } + } else { + for (let i = 0; i < 4; i++) powerUps.spawnRandomPowerUp(x, y); + } + } else { //after the first 4 levels just spawn a random power up + for (let i = 0; i < 3; i++) powerUps.spawnRandomPowerUp(x, y); + } + }, + ejectTech(choose = 'random', isOverride = false) { + if (!simulation.isChoosing || isOverride) { + // console.log(tech.tech[choose].name, tech.tech[choose].count, tech.tech[choose].isNonRefundable) + //find which tech you have + if (choose === 'random') { + const have = [] + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + } + // if (have.length === 0) { + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].count > 0) have.push(i) + // } + // } + + if (have.length) { + choose = have[Math.floor(Math.random() * have.length)] + simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + + for (let i = 0; i < tech.tech[choose].count; i++) { + powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + // powerUp[powerUp.length - 1].isDuplicated = true + } + // remove a random tech from the list of tech you have + tech.tech[choose].remove(); + tech.tech[choose].count = 0; + tech.tech[choose].isLost = true; + simulation.updateTechHUD(); + m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech + return true + } else { + return false + } + } else if (tech.tech[choose].count && !tech.tech[choose].isNonRefundable) { + simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + + for (let i = 0; i < tech.tech[choose].count; i++) { + powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + powerUp[powerUp.length - 1].isDuplicated = true + } + // remove a random tech from the list of tech you have + tech.tech[choose].remove(); + tech.tech[choose].count = 0; + tech.tech[choose].isLost = true; + simulation.updateTechHUD(); + m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech + return true + } else { + return false + } + } + }, + pauseEjectTech(index) { + if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing && !tech.tech[index].isNonRefundable) { + if (Math.random() < 0.2 || tech.tech[index].isFromAppliedScience || (tech.tech[index].bonusResearch !== undefined && tech.tech[index].bonusResearch > powerUps.research.count)) { + tech.removeTech(index) + // powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + } else { + powerUps.ejectTech(index) + } + document.getElementById(`${index}-pause-tech`).style.textDecoration = "line-through" + document.getElementById(`${index}-pause-tech`).style.animation = "" + document.getElementById(`${index}-pause-tech`).onclick = null + } + }, + // removeRandomTech() { + // const have = [] //find which tech you have + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].count > 0) have.push(i) + // } + // if (have.length) { + // const choose = have[Math.floor(Math.random() * have.length)] + // simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`) + // const totalRemoved = tech.tech[choose].count + // tech.tech[choose].count = 0; + // tech.tech[choose].remove(); // remove a random tech form the list of tech you have + // tech.tech[choose].isLost = true + // simulation.updateTechHUD(); + // return totalRemoved + // } + // return 0 + // }, + randomize(where) { //makes a random power up convert into a random different power up + //put 10 power ups close together + const len = Math.min(10, powerUp.length) + for (let i = 0; i < len; i++) { //collide the first 10 power ups + const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) + Matter.Body.setPosition(powerUp[i], Vector.add(where, Vector.mult(unit, 20 + 25 * Math.random()))); + Matter.Body.setVelocity(powerUp[i], Vector.mult(unit, 20)); + } + + //count big power ups and small power ups + let options = ["heal", "research", "ammo"] + if (m.coupling) options.push("coupling") + if (tech.isBoostPowerUps) options.push("boost") + let bigIndexes = [] + let smallIndexes = [] + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "tech" || powerUp[i].name === "gun" || powerUp[i].name === "field") { + bigIndexes.push(i) + } else { + smallIndexes.push(i) + } + } + if (bigIndexes.length > 0) { + // console.log("at least 1 big will always spilt") + const index = bigIndexes[Math.floor(Math.random() * bigIndexes.length)] + for (let i = 0; i < 3; i++) powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) + + Matter.Composite.remove(engine.world, powerUp[index]); + powerUp.splice(index, 1); + } else if (smallIndexes.length > 2 && Math.random() < 0.33) { + // console.log("no big, at least 3 small can combine") + for (let j = 0; j < 3; j++) { + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal" || powerUp[i].name === "research" || powerUp[i].name === "ammo" || powerUp[i].name === "coupling" || powerUp[i].name === "boost") { + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break + } + } + } + + options = ["tech", "gun", "field"] + powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) + } else if (smallIndexes.length > 0) { + // console.log("no big, at least 1 small will swap flavors") + const index = Math.floor(Math.random() * powerUp.length) + options = options.filter(e => e !== powerUp[index].name); //don't repeat the current power up type + powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) + Matter.Composite.remove(engine.world, powerUp[index]); + powerUp.splice(index, 1); + } + }, + directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size(), isDuplicated = false) { + let index = powerUp.length; + let properties = { + density: 0.001, + frictionAir: 0.03, + restitution: 0.85, + collisionFilter: { + group: 0, + category: cat.powerUp, + mask: cat.map | cat.powerUp + }, + color: powerUps[target].color, + effect: powerUps[target].effect, + name: powerUps[target].name, + size: size + } + let polygonSides + if (isDuplicated) { + polygonSides = tech.isPowerUpsVanish ? 3 : Math.floor(4 + 2 * Math.random()) + properties.isDuplicated = true + } else { + properties.inertia = Infinity //prevents rotation for circles only + polygonSides = 0 + } + powerUp[index] = Matter.Bodies.polygon(x, y, polygonSides, size, properties); + if (mode) powerUp[index].mode = mode + if (moving) Matter.Body.setVelocity(powerUp[index], { x: (Math.random() - 0.5) * 15, y: Math.random() * -9 - 3 }); + Composite.add(engine.world, powerUp[index]); + }, + spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + if ( + (!tech.isSuperDeterminism || (target !== 'research')) && + !(tech.isEnergyNoAmmo && target === 'ammo') + ) { + if (tech.isBoostReplaceAmmo && target === 'ammo') target = 'boost' + powerUps.directSpawn(x, y, target, moving, mode, size) + if (Math.random() < tech.duplicationChance()) { + powerUps.directSpawn(x, y, target, moving, mode, size, true) + powerUp[powerUp.length - 1].isDuplicated = true + // if (tech.isPowerUpsVanish) powerUp[powerUp.length - 1].endCycle = simulation.cycle + 300 + } + } + }, +}; \ No newline at end of file diff --git a/ngon/js/simulation.js b/ngon/js/simulation.js new file mode 100644 index 00000000..a89422e6 --- /dev/null +++ b/ngon/js/simulation.js @@ -0,0 +1,1937 @@ +// game Object ******************************************************** +//********************************************************************* +const simulation = { + loop() { }, //main game loop, gets set to normal or testing loop + normalLoop() { + simulation.gravity(); + Engine.update(engine, simulation.delta); + simulation.wipe(); + simulation.textLog(); + if (m.onGround) { + m.groundControl() + } else { + m.airControl() + } + m.move(); + m.look(); + simulation.camera(); + level.custom(); + powerUps.do(); + mobs.draw(); + simulation.draw.cons(); + simulation.draw.body(); + if (!m.isBodiesAsleep) mobs.loop(); + mobs.healthBar(); + m.draw(); + m.hold(); + // v.draw(); //working on visibility work in progress + level.customTopLayer(); + simulation.draw.drawMapPath(); + b.fire(); + b.bulletRemove(); + b.bulletDraw(); + if (!m.isBodiesAsleep) b.bulletDo(); + simulation.drawCircle(); + simulation.runEphemera(); + // simulation.clip(); + ctx.restore(); + simulation.drawCursor(); + // simulation.pixelGraphics(); + }, + testingLoop() { + simulation.gravity(); + Engine.update(engine, simulation.delta); + simulation.wipe(); + simulation.textLog(); + if (m.onGround) { + m.groundControl() + } else { + m.airControl() + } + m.move(); + m.look(); + simulation.camera(); + level.custom(); + m.draw(); + m.hold(); + level.customTopLayer(); + simulation.draw.wireFrame(); + if (input.fire && m.fireCDcycle < m.cycle) { + m.fireCDcycle = m.cycle + 15; //fire cooldown + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitudeSquared(Vector.sub(mob[i].position, simulation.mouseInGame)) < mob[i].radius * mob[i].radius) { + console.log(mob[i]) + } + } + } + simulation.draw.cons(); + simulation.draw.testing(); + simulation.drawCircle(); + simulation.runEphemera(); + simulation.constructCycle() + ctx.restore(); + simulation.testingOutput(); + simulation.drawCursor(); + }, + isTimeSkipping: false, + timeSkip(cycles = 60) { + simulation.isTimeSkipping = true; + for (let i = 0; i < cycles; i++) { + simulation.cycle++; + m.cycle++; + simulation.gravity(); + Engine.update(engine, simulation.delta); + if (m.onGround) { + m.groundControl() + } else { + m.airControl() + } + m.move(); + level.custom(); + mobs.loop(); + m.walk_cycle += m.flipLegs * m.Vx; + m.hold(); + level.customTopLayer(); + b.fire(); + b.bulletRemove(); + b.bulletDo(); + simulation.runEphemera(); + } + simulation.isTimeSkipping = false; + }, + timePlayerSkip(cycles = 60) { + simulation.isTimeSkipping = true; + for (let i = 0; i < cycles; i++) { + simulation.cycle++; + // m.walk_cycle += (m.flipLegs * m.Vx) * 0.5; //makes the legs look like they are moving fast this is just gonna run for each method call since it needs some tweaking + simulation.gravity(); + Engine.update(engine, simulation.delta); + // level.custom(); + // level.customTopLayer(); + if (!m.isBodiesAsleep) mobs.loop(); + if (m.fieldMode !== 7) m.hold(); + b.bulletRemove(); + if (!m.isBodiesAsleep) b.bulletDo(); + simulation.runEphemera(); + } + simulation.isTimeSkipping = false; + }, + ephemera: [], //array that is used to store ephemera objects + removeEphemera: function (name) { + for (let i = 0, len = simulation.ephemera.length; i < len; i++) { + if (simulation.ephemera[i].name === name) { + simulation.ephemera.splice(i, 1); + break; + } + } + }, + runEphemera() { + for (let i = 0; i < simulation.ephemera.length; i++) { + simulation.ephemera[i].do(); + } + }, + // timeMobSkip() { + // simulation.gravity(); + // Engine.update(engine, simulation.delta); + // simulation.wipe(); + // simulation.textLog(); + // if (m.onGround) { + // m.groundControl() + // } else { + // m.airControl() + // } + // m.move(); + // m.look(); + // simulation.camera(); + // level.custom(); + // powerUps.do(); + // mobs.draw(); + // simulation.draw.cons(); + // simulation.draw.body(); + // if (!m.isBodiesAsleep) { + // // mobs.loop(); + // } + // mobs.healthBar(); + // m.draw(); + // m.hold(); + // // v.draw(); //working on visibility work in progress + // level.customTopLayer(); + // simulation.draw.drawMapPath(); + // b.fire(); + // b.bulletRemove(); + // b.bulletDraw(); + // if (!m.isBodiesAsleep) b.bulletDo(); + // simulation.drawCircle(); + // // simulation.clip(); + // ctx.restore(); + // simulation.drawCursor(); + // // simulation.pixelGraphics(); + // }, + mouse: { + x: canvas.width / 2, + y: canvas.height / 2 + }, + mouseInGame: { + x: 0, + y: 0 + }, + g: 0.0024, // applies to player, bodies, and power ups (not mobs) + onTitlePage: true, + isCheating: false, + isTraining: false, + paused: false, + isChoosing: false, + testing: false, //testing mode: shows wire frame and some variables + cycle: 0, //total cycles, 60 per second + fpsCap: null, //limits frames per second to 144/2=72, on most monitors the fps is capped at 60fps by the hardware + fpsCapDefault: 72, //use to change fpsCap back to normal after a hit from a mob + isCommunityMaps: false, + cyclePaused: 0, + fallHeight: 6000, //below this y position the player dies + lastTimeStamp: 0, //tracks time stamps for measuring delta + delta: 1000 / 60, //speed of game engine //looks like it has to be 16.6666 to match player input + buttonCD: 0, + isHorizontalFlipped: false, //makes some maps flipped horizontally + levelsCleared: 0, + difficultyMode: 2, //normal difficulty is 2 + difficulty: 0, + dmgScale: null, //set in levels.setDifficulty + healScale: 1, + accelScale: null, //set in levels.setDifficulty + CDScale: null, //set in levels.setDifficulty + molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones //randomize molecular assembler field type + // dropFPS(cap = 40, time = 15) { + // simulation.fpsCap = cap + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // simulation.defaultFPSCycle = simulation.cycle + time + // const normalFPS = function () { + // if (simulation.defaultFPSCycle < simulation.cycle) { + // simulation.fpsCap = 72 + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // } else { + // requestAnimationFrame(normalFPS); + // } + // }; + // requestAnimationFrame(normalFPS); + // }, + // clip() { + + // }, + pixelGraphics() { + //copy current canvas pixel data + let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + let data = imgData.data; + //change pixel data + + + // const off = 4 * Math.floor(x) + 4 * canvas.width * Math.floor(y); + // multiple windows + for (let i = data.length / 2; i < data.length; i += 4) { + index = i % (canvas.width * canvas.height * 2) // + canvas.width*4*canvas.height + + data[i + 0] = data[index + 0]; // red + data[i + 1] = data[index + 1]; // red + data[i + 2] = data[index + 2]; // red + data[i + 3] = data[index + 3]; // red + } + + for (let x = 0; x < len; x++) { + + } + + + + // const startX = 2 * canvas.width + 2 * canvas.width * canvas.height + // const endX = 4 * canvas.width + 4 * canvas.width * canvas.height + // const startY = 2 * canvas.width + 2 * canvas.width * canvas.height + // const endY = 4 * canvas.width + 4 * canvas.width * canvas.height + // for (let x = startX; x < endX; x++) { + // for (let y = startY; y < endY; y++) { + + // } + // } + + + + + //strange draw offset + // const off = canvas.height * canvas.width * 4 / 2 + // for (let index = 0; index < data.length; index += 4) { + // data[index + 0] = data[index + 0 + off]; // red + // data[index + 1] = data[index + 1 + off]; // red + // data[index + 2] = data[index + 2 + off]; // red + // data[index + 3] = data[index + 3 + off]; // red + // } + + //change all pixels + // for (let index = 0; index < data.length; index += 4) { + // data[index + 0] = 255; // red + // data[index + 1] = 255; // green + // data[index + 2] = 255; // blue + // data[index + 3] = 255; // alpha + // } + + //change random pixels + // for (let i = 0, len = Math.floor(data.length / 10); i < len; ++i) { + // const index = Math.floor((Math.random() * data.length) / 4) * 4; + // data[index + 0] = 255; // red + // data[index + 1] = 0; // green + // data[index + 2] = 0; // blue + // data[index + 3] = 255 //Math.floor(Math.random() * Math.random() * 255); // alpha + // } + + // //change random pixels + // for (let i = 0, len = Math.floor(data.length / 1000); i < len; ++i) { + // const index = Math.floor((Math.random() * data.length) / 4) * 4; + // // data[index] = data[index] ^ 255; // Invert Red + // // data[index + 1] = data[index + 1] ^ 255; // Invert Green + // // data[index + 2] = data[index + 2] ^ 255; // Invert Blue + // data[index + 0] = 0; // red + // data[index + 1] = 0; // green + // data[index + 2] = 0; // blue + // // data[index + 3] = 255 //Math.floor(Math.random() * Math.random() * 255); // alpha + // } + + //draw new pixel data to canvas + ctx.putImageData(imgData, 0, 0); + }, + drawCursor() { + const size = 10; + ctx.beginPath(); + ctx.moveTo(simulation.mouse.x - size, simulation.mouse.y); + ctx.lineTo(simulation.mouse.x + size, simulation.mouse.y); + ctx.moveTo(simulation.mouse.x, simulation.mouse.y - size); + ctx.lineTo(simulation.mouse.x, simulation.mouse.y + size); + ctx.lineWidth = 2; + ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)' + ctx.stroke(); // Draw it + }, + drawCursorBasic() { + const size = 10; + ctx.beginPath(); + ctx.moveTo(simulation.mouse.x - size, simulation.mouse.y); + ctx.lineTo(simulation.mouse.x + size, simulation.mouse.y); + ctx.moveTo(simulation.mouse.x, simulation.mouse.y - size); + ctx.lineTo(simulation.mouse.x, simulation.mouse.y + size); + ctx.lineWidth = 2; + ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)' + ctx.stroke(); // Draw it + }, + drawCursorCoolDown() { + const size = 10; + ctx.lineWidth = 2; + ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)' + ctx.beginPath(); + if (m.fireCDcycle > m.cycle) { + ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)' + ctx.arc(simulation.mouse.x, simulation.mouse.y, size + 1, 0, 2 * Math.PI); + } else { + ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)' + } + ctx.moveTo(simulation.mouse.x - size, simulation.mouse.y); + ctx.lineTo(simulation.mouse.x + size, simulation.mouse.y); + ctx.moveTo(simulation.mouse.x, simulation.mouse.y - size); + ctx.lineTo(simulation.mouse.x, simulation.mouse.y + size); + ctx.stroke(); // Draw it + }, + drawList: [], //so you can draw a first frame of explosions.. I know this is bad + drawTime: 8, //how long circles are drawn. use to push into drawlist.time + mobDmgColor: "rgba(255,0,0,0.7)", //color when a mob damages the player // set by mass-energy tech + playerDmgColor: "rgba(0,0,0,0.7)", //color when the player damages a mob + drawCircle() { + //draws a circle for two cycles, used for showing damage mostly + let i = simulation.drawList.length; + while (i--) { + ctx.beginPath(); //draw circle + ctx.arc(simulation.drawList[i].x, simulation.drawList[i].y, simulation.drawList[i].radius, 0, 2 * Math.PI); + ctx.fillStyle = simulation.drawList[i].color; + ctx.fill(); + if (simulation.drawList[i].time) { + simulation.drawList[i].time--; + } else { + if (!m.isBodiesAsleep) simulation.drawList.splice(i, 1); //remove when timer runs out + } + } + }, + circleFlare(dup, loops = 100) { + boltNum = dup * 300 + const bolts = [] + colors = [powerUps.research.color, powerUps.ammo.color, powerUps.heal.color, powerUps.tech.color, powerUps.field.color, powerUps.gun.color] + for (let i = 0; i < boltNum; ++i) { + const mag = 6 + 20 * Math.random() + const angle = 2 * Math.PI * Math.random() + bolts.push({ + x: m.pos.x, + y: m.pos.y, + Vx: mag * Math.cos(angle), + Vy: mag * Math.sin(angle), + color: colors[Math.floor(Math.random() * colors.length)] + }) + } + let count = 0 + loop = () => { //draw electricity + if (count++ < loops) requestAnimationFrame(loop) + for (let i = 0, len = bolts.length; i < len; ++i) { + bolts[i].x += bolts[i].Vx + bolts[i].y += bolts[i].Vy + if (Math.random() < 0.2) { + simulation.drawList.push({ + x: bolts[i].x, + y: bolts[i].y, + radius: 1.5 + 5 * Math.random(), + // color: "rgba(0,155,155,0.7)", + color: bolts[i].color, + time: Math.floor(9 + 25 * Math.random() * Math.random()) + }); + } + } + } + requestAnimationFrame(loop) + }, + boldActiveGunHUD() { + if (b.inventory.length > 0) { + for (let i = 0, len = b.inventory.length; i < len; ++i) { + if (b.inventory[i] === b.activeGun && document.getElementById(b.activeGun)) { + document.getElementById(b.inventory[i]).style.opacity = "1"; + } else { + document.getElementById(b.inventory[i]).style.opacity = "0.3"; + } + } + } + }, + updateGunHUD() { + for (let i = 0, len = b.inventory.length; i < len; ++i) { + document.getElementById(b.inventory[i]).innerHTML = `${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}` + } + }, + makeGunHUD() { + //remove all nodes + const myNode = document.getElementById("guns"); + while (myNode.firstChild) { + myNode.removeChild(myNode.firstChild); + } + //add nodes + for (let i = 0, len = b.inventory.length; i < len; ++i) { + const node = document.createElement("div"); + node.setAttribute("id", b.inventory[i]); + const textNode = document.createTextNode(`${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}`); //b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo); + node.appendChild(textNode); + document.getElementById("guns").appendChild(node); + } + simulation.boldActiveGunHUD(); + }, + updateTechHUD() { + let text = "" + for (let i = 0, len = tech.tech.length; i < len; i++) { //add tech + if (tech.tech[i].isLost) { + if (text) text += "
" //add a new line, but not on the first line + text += `${tech.tech[i].name}` + } else if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) { + if (text) text += "
" //add a new line, but not on the first line + text += tech.tech[i].name + if (tech.tech[i].nameInfo) { + text += tech.tech[i].nameInfo + tech.tech[i].addNameInfo(); + } + if (tech.tech[i].count > 1) text += ` (${tech.tech[i].count}x)` + } + } + document.getElementById("tech").innerHTML = text + }, + lastLogTime: 0, + isTextLogOpen: true, + // + // SVGleftMouse: ' ', + // SVGrightMouse: ' ', + makeTextLog(text, time = 240) { + if (!localSettings.isHideHUD && simulation.isTextLogOpen && !build.isExperimentSelection) { + if (simulation.lastLogTime > m.cycle) { //if there is an older message + document.getElementById("text-log").innerHTML = document.getElementById("text-log").innerHTML + '
' + text; + simulation.lastLogTime = m.cycle + time; + } else { + document.getElementById("text-log").innerHTML = text; + document.getElementById("text-log").style.display = "inline"; + simulation.lastLogTime = m.cycle + time; + } + } + }, + textLog() { + if (simulation.lastLogTime && simulation.lastLogTime < m.cycle) { + simulation.lastLogTime = 0; + // document.getElementById("text-log").innerHTML = " "; + document.getElementById("text-log").style.display = "none"; + } + }, + nextGun() { + if (b.inventory.length > 1 && !tech.isGunCycle) { + b.inventoryGun++; + if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0; + simulation.switchGun(); + } + }, + previousGun() { + if (b.inventory.length > 1 && !tech.isGunCycle) { + b.inventoryGun--; + if (b.inventoryGun < 0) b.inventoryGun = b.inventory.length - 1; + simulation.switchGun(); + } + }, + switchToGunInInventory(num) { + if (b.inventory[num] !== undefined && b.inventoryGun !== num) { + b.inventoryGun = num + simulation.switchGun(); + } + }, + switchGun() { + if (tech.isLongitudinal && b.activeGun === 3) b.guns[3].waves = []; //empty array of wave bullets + if (tech.crouchAmmoCount) tech.crouchAmmoCount = 1 //this prevents hacking the tech by switching guns + if (b.inventory.length > 0) b.activeGun = b.inventory[b.inventoryGun]; + b.guns[8].charge = 0; // foam charge to 0 + simulation.updateGunHUD(); + simulation.boldActiveGunHUD(); + //set crosshairs + if (b.activeGun === 1) { + simulation.drawCursor = simulation.drawCursorCoolDown + } else { + simulation.drawCursor = simulation.drawCursorBasic + } + }, + zoom: null, + zoomScale: 1000, + isAutoZoom: true, + setZoom(zoomScale = simulation.zoomScale) { //use in window resize in index.js + simulation.zoomScale = zoomScale + simulation.zoom = canvas.height / zoomScale; //sets starting zoom scale + }, + zoomTransition(newZoomScale, step = 2) { + if (simulation.isAutoZoom) { + const isBigger = (newZoomScale - simulation.zoomScale > 0) ? true : false; + requestAnimationFrame(zLoop); + const currentLevel = level.onLevel + + function zLoop() { + if (currentLevel !== level.onLevel || simulation.isAutoZoom === false) return //stop the zoom if player goes to a new level + + if (isBigger) { + simulation.zoomScale += step + if (simulation.zoomScale >= newZoomScale) { + simulation.setZoom(newZoomScale); + return + } + } else { + simulation.zoomScale -= step + if (simulation.zoomScale <= newZoomScale) { + simulation.setZoom(newZoomScale); + return + } + } + + simulation.setZoom(); + requestAnimationFrame(zLoop); + } + } + }, + zoomInFactor: 0, + startZoomIn(time = 180) { + simulation.zoom = 0; + let count = 0; + requestAnimationFrame(zLoop); + + function zLoop() { + simulation.zoom += canvas.height / simulation.zoomScale / time; + count++; + if (count < time) { + requestAnimationFrame(zLoop); + } else { + simulation.setZoom(); + } + } + }, + noCameraScroll() { //makes the camera not scroll after changing locations + //only works if velocity is zero + m.pos.x = player.position.x; + m.pos.y = playerBody.position.y - m.yOff; + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * 1; + m.transY += (m.transSmoothY - m.transY) * 1; + }, + edgeZoomOutSmooth: 1, + camera() { + //zoom out when mouse gets near the edge of the window + const dx = simulation.mouse.x / window.innerWidth - 0.5 //x distance from mouse to window center scaled by window width + const dy = simulation.mouse.y / window.innerHeight - 0.5 //y distance from mouse to window center scaled by window height + const d = Math.max(dx * dx, dy * dy) + simulation.edgeZoomOutSmooth = (1 + 4 * d * d) * 0.04 + simulation.edgeZoomOutSmooth * 0.96 + + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + ctx.scale(simulation.zoom / simulation.edgeZoomOutSmooth, simulation.zoom / simulation.edgeZoomOutSmooth); //zoom in once centered + ctx.translate(-canvas.width2 + m.transX, -canvas.height2 + m.transY); //translate + //calculate in game mouse position by undoing the zoom and translations + simulation.mouseInGame.x = (simulation.mouse.x - canvas.width2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.width2 - m.transX; + simulation.mouseInGame.y = (simulation.mouse.y - canvas.height2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.height2 - m.transY; + }, + cameraNoLook() { + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + // ctx.scale(simulation.zoom / simulation.edgeZoomOutSmooth, simulation.zoom / simulation.edgeZoomOutSmooth); //zoom in once centered + ctx.translate(-canvas.width2 + m.transX, -canvas.height2 + m.transY); //translate + //calculate in game mouse position by undoing the zoom and translations + simulation.mouseInGame.x = (simulation.mouse.x - canvas.width2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.width2 - m.transX; + simulation.mouseInGame.y = (simulation.mouse.y - canvas.height2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.height2 - m.transY; + }, + restoreCamera() { + ctx.restore(); + }, + trails() { + const swapPeriod = 150 + const len = 30 + for (let i = 0; i < len; i++) { + setTimeout(function () { + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = `rgba(221,221,221,${i * i * 0.0005 + 0.0025})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + }, (i) * swapPeriod); + } + + setTimeout(function () { + simulation.wipe = function () { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + }, len * swapPeriod); + }, + // warp(translation = 5, skew = 0.05, scale = 0.05) { + // if (simulation.cycle % 2) { //have to alternate frames or else successive rumbles over write the effects of the previous rumble + // requestAnimationFrame(() => { ctx.setTransform(1, 0, 0, 1, 0, 0); }) //reset + // requestAnimationFrame(() => { + // if (!simulation.paused && m.alive) { + // ctx.transform(1 - scale * (Math.random() - 0.5), skew * (Math.random() - 0.5), skew * (Math.random() - 0.5), 1 - scale * (Math.random() - 0.5), translation * (Math.random() - 0.5), translation * (Math.random() - 0.5)); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) //https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform + // } + // }) + + //reset + // ctx.transform(1, 0, 0, 1, 0, 0); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) //https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform + + // } + // const loop = () => { + // if (!simulation.paused && m.alive) { + // ctx.save(); + // ctx.transform(1 - scale * (Math.random() - 0.5), skew * (Math.random() - 0.5), skew * (Math.random() - 0.5), 1 - scale * (Math.random() - 0.5), translation * (Math.random() - 0.5), translation * (Math.random() - 0.5)); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) + // requestAnimationFrame(() => { ctx.restore(); }) + // } + // } + // requestAnimationFrame(loop); + + // function loop() { + // if (!simulation.paused && m.alive) { + // ctx.save(); + // ctx.transform(1 - scale * (Math.random() - 0.5), skew * (Math.random() - 0.5), skew * (Math.random() - 0.5), 1 - scale * (Math.random() - 0.5), translation * (Math.random() - 0.5), translation * (Math.random() - 0.5)); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) + // requestAnimationFrame(() => { ctx.restore(); }) + // } + // requestAnimationFrame(loop); + // } + // requestAnimationFrame(loop); + // }, + wipe() { }, //set in simulation.startGame + gravity() { + function addGravity(bodies, magnitude) { + for (var i = 0; i < bodies.length; i++) { + bodies[i].force.y += bodies[i].mass * magnitude; + } + } + if (!m.isBodiesAsleep) { + addGravity(powerUp, simulation.g); + addGravity(body, simulation.g); + } + player.force.y += player.mass * simulation.g; + }, + firstRun: true, + splashReturn() { + document.getElementById("previous-seed").innerHTML = `previous seed: ${Math.initialSeed}
` + document.getElementById("seed").value = Math.initialSeed = Math.seed //randomize initial seed + + //String(document.getElementById("seed").value) + // Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it + + + simulation.clearTimeouts(); + simulation.onTitlePage = true; + document.getElementById("splash").onclick = function () { + simulation.startGame(); + }; + document.getElementById("choose-grid").style.visibility = "hidden" + document.getElementById("choose-grid").style.opacity = "0" + document.getElementById("info").style.display = "inline"; + document.getElementById("info").style.opacity = "0"; + document.getElementById("experiment-button").style.display = "inline" + document.getElementById("experiment-button").style.opacity = "0"; + document.getElementById("training-button").style.display = "inline" + document.getElementById("training-button").style.opacity = "0"; + document.getElementById("experiment-grid").style.display = "none" + document.getElementById("pause-grid-left").style.display = "none" + document.getElementById("pause-grid-right").style.display = "none" + document.getElementById("splash").style.display = "inline"; + document.getElementById("splash").style.opacity = "0"; + document.getElementById("dmg").style.display = "none"; + document.getElementById("health-bg").style.display = "none"; + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + document.body.style.cursor = "auto"; + setTimeout(() => { + document.getElementById("experiment-button").style.opacity = "1"; + document.getElementById("training-button").style.opacity = "1"; + document.getElementById("info").style.opacity = "1"; + document.getElementById("splash").style.opacity = "1"; + }, 200); + }, + fpsInterval: 0, //set in startGame + then: null, + startGame(isBuildRun = false, isTrainingRun = false) { + simulation.isTextLogOpen = true + simulation.clearMap() + if (!isBuildRun) { //if a build run logic flow returns to "experiment-button").addEventListener + document.body.style.cursor = "none"; + document.body.style.overflow = "hidden" + } + if (isTrainingRun) { + simulation.isTraining = true + } else { + simulation.isTraining = false + } + simulation.onTitlePage = false; + // document.getElementById("choose-grid").style.display = "none" + document.getElementById("choose-grid").style.visibility = "hidden" + document.getElementById("choose-grid").style.opacity = "0" + document.getElementById("experiment-grid").style.display = "none" + document.getElementById("info").style.display = "none"; + document.getElementById("experiment-button").style.display = "none"; + document.getElementById("training-button").style.display = "none"; + // document.getElementById("experiment-button").style.opacity = "0"; + document.getElementById("splash").onclick = null; //removes the onclick effect so the function only runs once + document.getElementById("splash").style.display = "none"; //hides the element that spawned the function + document.getElementById("dmg").style.display = "inline"; + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline"; + if (!localSettings.isHideHUD) { + document.getElementById("tech").style.display = "inline" + document.getElementById("defense-bar").style.display = "inline" + document.getElementById("damage-bar").style.display = "inline" + } else { + document.getElementById("tech").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + } + document.getElementById("guns").style.display = "inline" + document.getElementById("field").style.display = "inline" + + // document.body.style.overflow = "hidden" + document.getElementById("pause-grid-left").style.display = "none" + document.getElementById("pause-grid-right").style.display = "none" + document.getElementById("pause-grid-right").style.opacity = "1" + document.getElementById("pause-grid-left").style.opacity = "1" + ctx.globalCompositeOperation = "source-over" + ctx.shadowBlur = 0; + requestAnimationFrame(() => { + ctx.setTransform(1, 0, 0, 1, 0, 0); //reset warp effect + ctx.setLineDash([]) //reset stroke dash effect + }) + // ctx.shadowColor = '#000'; + if (!m.isShipMode) { + m.resetSkin() //set the play draw to normal, undoing some junk tech + m.spawn(); //spawns the player + m.look = m.lookDefault + } else { + Composite.add(engine.world, [player]) + } + level.populateLevels() + + input.endKeySensing(); + simulation.ephemera = [] + tech.setupAllTech(); //sets tech to default values + b.removeAllGuns(); + tech.duplication = 0; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + if (b.guns[i].name === "foam") b.guns[i].chooseFireMethod() + } + tech.dynamoBotCount = 0; + tech.nailBotCount = 0; + tech.laserBotCount = 0; + tech.orbitBotCount = 0; + tech.foamBotCount = 0; + tech.soundBotCount = 0; + tech.boomBotCount = 0; + tech.plasmaBotCount = 0; + tech.missileBotCount = 0; + + simulation.isChoosing = false; + b.setFireMethod() + b.setFireCD(); + // simulation.updateTechHUD(); + for (let i = 0; i < b.guns.length; i++) b.guns[i].isRecentlyShown = false //reset recently shown back to zero + for (let i = 0; i < m.fieldUpgrades.length; i++) m.fieldUpgrades[i].isRecentlyShown = false //reset recently shown back to zero + for (let i = 0; i < tech.tech.length; i++) tech.tech[i].isRecentlyShown = false //reset recently shown back to zero + + powerUps.tech.choiceLog = []; + powerUps.gun.choiceLog = []; + powerUps.field.choiceLog = []; + powerUps.totalPowerUps = 0; + powerUps.research.count = 0; + powerUps.boost.endCycle = 0 + powerUps.isFieldSpawned = false + m.setFillColors(); + // m.maxHealth = 1 + // m.maxEnergy = 1 + // m.energy = 1 + input.isPauseKeyReady = true + simulation.wipe = function () { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + m.hole.isOn = false + simulation.paused = false; + // simulation.cycle = 0 + // m.cycle = 0 + engine.timing.timeScale = 1; + simulation.fpsCap = simulation.fpsCapDefault; + simulation.isAutoZoom = true; + simulation.makeGunHUD(); + simulation.lastLogTime = 0; + mobs.mobDeaths = 0 + + level.onLevel = 0; + level.levelsCleared = 0; + //resetting difficulty + // simulation.difficulty = 0; + level.setDifficulty() + simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) + + simulation.clearNow = true; + document.getElementById("text-log").style.display = "none" + document.getElementById("fade-out").style.opacity = 0; + document.title = "n-gon"; + // simulation.makeTextLog(`input.key.up: ["${input.key.up}", "ArrowUp"]`); + // simulation.makeTextLog(`input.key.left: ["${input.key.left}", "ArrowLeft"]`); + // simulation.makeTextLog(`input.key.down: ["${input.key.down}", "ArrowDown"]`); + // simulation.makeTextLog(`input.key.right: ["${input.key.right}", "ArrowRight"]`); + simulation.makeTextLog(`Math.seed = ${Math.initialSeed}`); + simulation.makeTextLog(`const engine = Engine.create(); //simulation begin`); + simulation.makeTextLog(`engine.timing.timeScale = 1`); + // simulation.makeTextLog(`input.key.field: ["${input.key.field}", "MouseRight"]`); + + // document.getElementById("health").style.display = "inline" + // document.getElementById("health-bg").style.display = "inline" + m.alive = true; + m.onGround = false + m.lastOnGroundCycle = 0 + m.health = 0; + m.addHealth(0.25) + m.drop(); + m.holdingTarget = null + + //set to default field + tech.healMaxEnergyBonus = 0 + // m.setMaxEnergy(); + m.energy = 0 + m.immuneCycle = 0; + // simulation.makeTextLog(`${simulation.SVGrightMouse} ${m.fieldUpgrades[m.fieldMode].name}

${m.fieldUpgrades[m.fieldMode].description}`, 600); + // simulation.makeTextLog(` + // input.key.up = ["${input.key.up}", "ArrowUp"] + //
input.key.left = ["${input.key.left}", "ArrowLeft"] + //
input.key.down = ["${input.key.down}", "ArrowDown"] + //
input.key.right = ["${input.key.right}", "ArrowRight"] + //
+ //
m.fieldMode = "${m.fieldUpgrades[m.fieldMode].name}" + //
input.key.field = ["${input.key.field}", "right mouse"] + //
m.field.description = "${m.fieldUpgrades[m.fieldMode].description}" + // `, 800); + m.coupling = 0 + m.setField(0) //this calls m.couplingChange(), which sets max health and max energy + // m.energy = 0; + //exit testing + if (simulation.testing) { + simulation.testing = false; + simulation.loop = simulation.normalLoop + if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' + } + simulation.isCheating = false + simulation.firstRun = false; + build.hasExperimentalMode = false + build.isExperimentSelection = false; + build.isExperimentRun = false; + + + //setup checks + if (!localSettings.isHideHUD) { + simulation.ephemera.push({ + name: "dmgDefBars", count: 0, do() { + if (!(m.cycle % 15)) { //4 times a second + const defense = m.defense() //update defense bar + if (m.lastCalculatedDefense !== defense) { + document.getElementById("defense-bar").style.width = Math.floor(300 * m.maxHealth * (1 - defense)) + "px"; + m.lastCalculatedDefense = defense + } + const damage = tech.damageFromTech() //update damage bar + if (m.lastCalculatedDamage !== damage) { + document.getElementById("damage-bar").style.height = Math.floor((Math.atan(0.25 * damage - 0.25) + 0.25) * 0.53 * canvas.height) + "px"; + m.lastCalculatedDamage = damage + } + } + }, + }) + } + simulation.ephemera.push({ + name: "uniqueName", count: 0, do() { + if (!(m.cycle % 60)) { //once a second + //energy overfill + if (m.energy > m.maxEnergy) { + m.energy = m.maxEnergy + (m.energy - m.maxEnergy) * tech.overfillDrain //every second energy above max energy loses 25% + if (m.energy > 1000000) m.energy = 1000000 + } + if (tech.isFlipFlopEnergy && m.immuneCycle < m.cycle) { + if (tech.isFlipFlopOn) { + if (m.immuneCycle < m.cycle) m.energy += 0.2; + } else { + m.energy -= 0.01; + if (m.energy < 0) m.energy = 0 + } + } + if (tech.relayIce && tech.isFlipFlopOn) { + for (let j = 0; j < tech.relayIce; j++) { + for (let i = 0, len = 3 + Math.ceil(9 * Math.random()); i < len; i++) b.iceIX(2) + } + } + + if (m.pos.y > simulation.fallHeight) { // if 4000px deep + Matter.Body.setVelocity(player, { + x: 0, + y: 0 + }); + Matter.Body.setPosition(player, { + x: level.enter.x + 50, + y: level.enter.y - 20 + }); + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + m.damage(0.1 * simulation.difficultyMode); + m.energy -= 0.1 * simulation.difficultyMode + } + if (isNaN(player.position.x)) m.death(); + if (m.lastKillCycle + 300 > m.cycle) { //effects active for 5 seconds after killing a mob + if (tech.isEnergyRecovery && m.immuneCycle < m.cycle) { + m.energy += m.maxEnergy * 0.05 + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y - 45, + radius: Math.sqrt(m.maxEnergy * 0.05) * 60, + color: "rgba(0, 204, 255,0.4)", //#0cf + time: 4 + }); + } + if (tech.isHealthRecovery) { + const heal = 0.005 * m.maxHealth + m.addHealth(heal) + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: Math.sqrt(heal) * 150, + color: "rgba(0,255,200,0.5)", + time: 4 + }); + } + } + + if (!(m.cycle % 420)) { //once every 7 seconds + if (tech.isZeno) { + if (tech.isEnergyHealth) { + m.energy *= 0.95 + } else { + m.health *= 0.95 //remove 5% + m.displayHealth(); + } + + } + if (tech.cyclicImmunity && m.immuneCycle < m.cycle + tech.cyclicImmunity) m.immuneCycle = m.cycle + tech.cyclicImmunity; //player is immune to damage for 60 cycles + + fallCheck = function (who, save = false) { + let i = who.length; + while (i--) { + if (who[i].position.y > simulation.fallHeight) { + if (save) { + Matter.Body.setVelocity(who[i], { + x: 0, + y: 0 + }); + Matter.Body.setPosition(who[i], { + x: level.exit.x + 30 * (Math.random() - 0.5), + y: level.exit.y + 30 * (Math.random() - 0.5) + }); + } else { + Matter.Composite.remove(engine.world, who[i]); + who.splice(i, 1); + } + } + } + }; + fallCheck(body); + fallCheck(powerUp, true); + let i = mob.length; + while (i--) { + if (mob[i].position.y > simulation.fallHeight) mob[i].death(); + } + + } + } + }, + }) + + //setup FPS cap + simulation.fpsInterval = 1000 / simulation.fpsCap; + simulation.then = Date.now(); + requestAnimationFrame(cycle); //starts game loop + }, + clearTimeouts() { + let id = window.setTimeout(function () { }, 0); + while (id--) { + window.clearTimeout(id); // will do nothing if no timeout with id is present + } + }, + clearNow: false, + clearMap() { + level.isProcedural = false; + ctx.setTransform(1, 0, 0, 1, 0, 0); + if (m.alive) { + if (tech.isLongitudinal) b.guns[3].waves = []; //empty array of wave bullets + if (b.guns[10].have) { //do you have mines as a gun + let count = 0; + for (i = 0, len = bullet.length; i < len; i++) { //count mines left on map + if ( + (bullet[i].bulletType === "mine" && (!tech.isMineSentry || bullet[i].shots === undefined)) || + bullet[i].bulletType === "laser mine") { + count++ + } + } + if (tech.crouchAmmoCount) count = Math.ceil(count / 2) + b.guns[10].ammo += count + if (tech.ammoCap) b.guns[10].ammo = Math.min(tech.ammoCap, b.guns[10].ammo) + simulation.updateGunHUD(); + } + + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun is mine + // if (b.guns[i].name === "mine") { + // b.guns[i].ammo += count + // if (tech.ammoCap) b.guns[i].ammo = Math.min(tech.ammoCap, b.guns[i].ammo) + // simulation.updateGunHUD(); + // break; + // } + // } + + if (tech.isMutualism && !tech.isEnergyHealth) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].isMutualismActive) { + m.health += 0.01 + 0.01 * ((bullet[i].isSpore || bullet[i].isFlea) ? 0 : 1) + if (m.health > m.maxHealth) m.health = m.maxHealth; + m.displayHealth(); + } + } + } + if (tech.isEndLevelPowerUp) { + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "tech") { + tech.giveTech() + } else if (powerUp[i].name === "gun") { + if (!tech.isOneGun) b.giveGuns("random") + } else if (powerUp[i].name === "field") { + if (m.fieldMode === 0) m.setField(Math.ceil(Math.random() * (m.fieldUpgrades.length - 1))) //pick a random field, but not field 0 + } else { + powerUp[i].effect(); + } + } + } + } + simulation.lastLogTime = 0; //clear previous messages + spawn.allowShields = true; + powerUps.totalPowerUps = powerUp.length + let holdTarget = (m.holdingTarget) ? m.holdingTarget : undefined //if player is holding something this remembers it before it gets deleted + tech.deathSpawnsFromBoss = 0; + simulation.fallHeight = 3000; + document.body.style.backgroundColor = "#eee" //"#d8dadf"; + // color.map = "#444"; + // color.bullet = "#FFFFFF"; + color = { //light + // background: "#ddd", // used instead: document.body.style.backgroundColor + block: "rgba(140,140,140,0.85)", + blockS: "#222", + map: "#444", + bullet: "#000" + } + simulation.draw.drawMapPath = simulation.draw.drawMapPathDefault + m.fireCDcycle = 0 + m.drop(); + m.hole.isOn = false; + simulation.drawList = []; + + if (tech.isHealAttract && m.alive) { //send health power ups to the next level + let healCount = 0 + for (let i = 0, len = powerUp.length; i < len; i++) { + if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) healCount++ + } + //respawn health in animation frame + let respawnHeal = () => { + if (healCount > 0) { + requestAnimationFrame(respawnHeal); + if (!simulation.paused && !simulation.isChoosing) { + healCount-- + powerUps.directSpawn(level.enter.x + 50 + 100 * (Math.random() - 0.5), level.enter.y - 60 + 100 * (Math.random() - 0.5), "heal"); + } + } + } + requestAnimationFrame(respawnHeal); + } + if (tech.isDronesTravel && m.alive) { + //count drones + let droneCount = 0 + let sporeCount = 0 + let wormCount = 0 + let fleaCount = 0 + let deliveryCount = 0 + for (let i = 0; i < bullet.length; ++i) { + if (bullet[i].isDrone) { + droneCount++ + if (bullet[i].isImproved) deliveryCount++ + } else if (bullet[i].isSpore) { + sporeCount++ + } else if (bullet[i].wormSize) { + wormCount++ + } else if (bullet[i].isFlea) { + fleaCount++ + } + } + + //respawn drones in animation frame + requestAnimationFrame(() => { b.delayDrones({ x: level.enter.x + 50, y: level.enter.y - 60 }, droneCount) }); + + //respawn spores in animation frame + let respawnSpores = () => { + if (sporeCount > 0) { + requestAnimationFrame(respawnSpores); + if (!simulation.paused && !simulation.isChoosing) { + sporeCount-- + const where = { + x: level.enter.x + 50, + y: level.enter.y - 60 + } + b.spore({ + x: where.x + 100 * (Math.random() - 0.5), + y: where.y + 120 * (Math.random() - 0.5) + }) + } + } + } + requestAnimationFrame(respawnSpores); + + //respawn worms in animation frame + let respawnWorms = () => { + if (wormCount > 0) { + requestAnimationFrame(respawnWorms); + if (!simulation.paused && !simulation.isChoosing) { + wormCount-- + const where = { + x: level.enter.x + 50, + y: level.enter.y - 60 + } + b.worm({ + x: where.x + 100 * (Math.random() - 0.5), + y: where.y + 120 * (Math.random() - 0.5) + }) + } + } + } + requestAnimationFrame(respawnWorms); + + //respawn fleas in animation frame + let respawnFleas = () => { + if (fleaCount > 0) { + requestAnimationFrame(respawnFleas); + if (!simulation.paused && !simulation.isChoosing) { + fleaCount-- + const where = { + x: level.enter.x + 50, + y: level.enter.y - 60 + } + const speed = 6 + 3 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea({ + x: where.x + 100 * (Math.random() - 0.5), + y: where.y + 120 * (Math.random() - 0.5) + }, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + } + } + } + requestAnimationFrame(respawnFleas); + } + if (tech.isQuantumEraser) { + let count = 0 + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isDropPowerUp && mob[i].alive) count++ + } + count *= 0.17 //to fake the chance, this makes it not random, and maybe less confusing + let cycle = () => { //run after waiting a cycle for the map to be cleared + const types = ["heal", "ammo", "heal", "ammo", "research", "coupling", "boost", "tech", "gun", "field"] + for (let i = 0; i < count; i++) powerUps.spawnDelay(types[Math.floor(Math.random() * types.length)], 1) + } + requestAnimationFrame(cycle); + } + + function removeAll(array) { + // for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); + for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); + } + removeAll(map); + map = []; + removeAll(body); + body = []; + removeAll(mob); + mob = []; + removeAll(powerUp); + powerUp = []; + removeAll(cons); + cons = []; + removeAll(consBB); + consBB = []; + removeAll(bullet); + bullet = []; + removeAll(composite); + composite = []; + // if player was holding something this makes a new copy to hold + if (holdTarget && m.alive) { + len = body.length; + body[len] = Matter.Bodies.fromVertices(0, 0, holdTarget.vertices, { + friction: holdTarget.friction, + frictionAir: holdTarget.frictionAir, + frictionStatic: holdTarget.frictionStatic + }); + Matter.Body.setPosition(body[len], m.pos); + m.isHolding = true + m.holdingTarget = body[len]; + m.holdingTarget.collisionFilter.category = 0; + m.holdingTarget.collisionFilter.mask = 0; + m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) + Composite.add(engine.world, m.holdingTarget); //add to world + m.holdingTarget.classType = "body" + } + //set fps back to default + simulation.fpsCap = simulation.fpsCapDefault + simulation.fpsInterval = 1000 / simulation.fpsCap; + }, + // getCoords: { + // //used when building maps, outputs a draw rect command to console, only works in testing mode + // pos1: { + // x: 0, + // y: 0 + // }, + // pos2: { + // x: 0, + // y: 0 + // }, + // out() { + // if (keys[49]) { + // simulation.getCoords.pos1.x = Math.round(simulation.mouseInGame.x / 25) * 25; + // simulation.getCoords.pos1.y = Math.round(simulation.mouseInGame.y / 25) * 25; + // } + // if (keys[50]) { + // //press 1 in the top left; press 2 in the bottom right;copy command from console + // simulation.getCoords.pos2.x = Math.round(simulation.mouseInGame.x / 25) * 25; + // simulation.getCoords.pos2.y = Math.round(simulation.mouseInGame.y / 25) * 25; + // window.getSelection().removeAllRanges(); + // var range = document.createRange(); + // range.selectNode(document.getElementById("test")); + // window.getSelection().addRange(range); + // document.execCommand("copy"); + // window.getSelection().removeAllRanges(); + // console.log(`spawn.mapRect(${simulation.getCoords.pos1.x}, ${simulation.getCoords.pos1.y}, ${simulation.getCoords.pos2.x - simulation.getCoords.pos1.x}, ${simulation.getCoords.pos2.y - simulation.getCoords.pos1.y}); //`); + // } + // } + // }, + testingOutput() { + ctx.fillStyle = "#000"; + ctx.textAlign = "center"; + ctx.fillText(`(${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})`, simulation.mouse.x, simulation.mouse.y - 20); + }, + sight: { //credit to Cornbread for adding this algorithm to n-gon + // square: 0, + intersectMap: [], //this is precalculated in simulation.draw.lineOfSightPrecalculation() + getIntersection(v1, v1End, domain) { + const intersections = simulation.sight.getIntersections(v1, v1End, domain); + var best = { x: v1End.x, y: v1End.y, dist: (v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2 } + for (const intersection of intersections) { + const dist = (intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2; + if (dist < best.dist) best = { x: intersection.x, y: intersection.y, dist: dist } + } + best.dist = Math.sqrt(best.dist) + return best; + }, + getIntersections(v1, v1End, domain) { + const intersections = []; + for (const obj of domain) { + for (var i = 0; i < obj.vertices.length - 1; i++) { + results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]); + if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); + } + results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]); + if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); + } + return intersections; + }, + circleLoS(pos, radius) { + function allCircleLineCollisions(c, radius, domain) { + var lines = []; + for (const obj of domain) { + for (var i = 0; i < obj.vertices.length - 1; i++) lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius)); + lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius)); + } + const collisionLines = []; + for (const line of lines) { + if (line.length == 2) { + // const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2) + // const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x); + // const queryPoint1 = { + // x: Math.cos(angle1) * (distance1 - 1) + c.x, + // y: Math.sin(angle1) * (distance1 - 1) + c.y + // } + // const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2) + // const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x); + // const queryPoint2 = { + // x: Math.cos(angle2) * (distance2 - 1) + c.x, + // y: Math.sin(angle2) * (distance2 - 1) + c.y + // } + collisionLines.push(line) + } + } + + return collisionLines; + } + + function circleLineCollisions(a, b, c, radius) { + // calculate distances + const angleOffset = Math.atan2(b.y - a.y, b.x - a.x); + const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2); + const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); + const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2); + + // calculate the closest point on line AB to point C + const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x) + const sideAD = Math.cos(angleA) * sideB; + const d = { // closest point + x: Math.cos(angleOffset) * sideAD + a.x, + y: Math.sin(angleOffset) * sideAD + a.y + } + const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2); + if (distance == radius) { + // tangent + return [d]; + } else if (distance < radius) { + // secant + const angleOffset = Math.atan2(d.y - c.y, d.x - c.x); + const innerAngle = Math.acos(distance / radius); + const intersection1 = { + x: Math.cos(angleOffset + innerAngle) * radius + c.x, + y: Math.sin(angleOffset + innerAngle) * radius + c.y + } + + const intersection2 = { + x: Math.cos(angleOffset - innerAngle) * radius + c.x, + y: Math.sin(angleOffset - innerAngle) * radius + c.y + } + + const distance1 = { + a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2), + b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2) + } + const distance2 = { + a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2), + b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2) + } + const result = []; + if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) { + result.push(intersection1); + } else { + if (distance1.a < distance1.b) { + if (sideB <= radius) result.push(a); + } else { + if (sideA <= radius) result.push(b) + } + } + if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) { + result.push(intersection2); + } else { + if (distance2.a <= distance2.b) { + if (sideB <= radius) result.push(a); + } else { + if (sideA <= radius) result.push(b) + } + } + + return result; + } else { + // no intersection + return []; + } + } + + var vertices = []; + for (const obj of simulation.sight.intersectMap) { + for (var i = 0; i < obj.vertices.length; i++) { + const vertex = obj.vertices[i]; + const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + // const distanceToVertex = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2); + // const queryPoint = { x: Math.cos(angleToVertex) * (distanceToVertex - 1) + pos.x, y: Math.sin(angleToVertex) * (distanceToVertex - 1) + pos.y } + const queryPoint = { x: Math.cos(angleToVertex + Math.PI) + vertex.x, y: Math.sin(angleToVertex + Math.PI) + vertex.y } + + if (Matter.Query.ray(map, pos, queryPoint).length == 0) { + var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2); + var endPoint = { x: vertex.x, y: vertex.y } + + if (distance > radius) { + const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { x: Math.cos(angle) * radius + pos.x, y: Math.sin(angle) * radius + pos.y } + distance = radius + } + + var best = simulation.sight.getIntersection(pos, endPoint, map); + if (best.dist >= distance) best = { x: endPoint.x, y: endPoint.y, dist: distance } + vertices.push(best) + + var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { x: Math.cos(angle + 0.001) * radius + pos.x, y: Math.sin(angle + 0.001) * radius + pos.y } + best = simulation.sight.getIntersection(pos, endPoint, map); + + if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius } + vertices.push(best) + + angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + endPoint = { x: Math.cos(angle - 0.001) * radius + pos.x, y: Math.sin(angle - 0.001) * radius + pos.y } + + best = simulation.sight.getIntersection(pos, endPoint, map); + if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius } + vertices.push(best) + } + } + } + + const outerCollisions = allCircleLineCollisions(pos, radius, map); + const circleCollisions = []; + for (const line of outerCollisions) { + for (const vertex of line) { + // console.log('hi') + const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2) + const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + // const queryPoint = { + // x: Math.cos(angle) * (distance - 1) + pos.x, + // y: Math.sin(angle) * (distance - 1) + pos.y + // } + const queryPoint = { x: Math.cos(angle + Math.PI) + vertex.x, y: Math.sin(angle + Math.PI) + vertex.y } + + if (Math.abs(distance - radius) < 1 && Matter.Query.ray(map, pos, queryPoint).length == 0) circleCollisions.push(vertex) + } + } + for (var i = 0; i < circleCollisions.length; i++) { + const vertex = circleCollisions[i]; + var nextIndex = i + 1; + if (nextIndex == circleCollisions.length) nextIndex = 0; + const nextVertex = circleCollisions[nextIndex]; + const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); + const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x); + var newAngle; + if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) { + // if the arc between the to points crosses over the left side (+/- pi radians) + const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1)); + const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2)); + newAngle = (newAngle1 + newAngle2) / 2; + var multiplier; + if (newAngle == 0) { + multiplier = 1; + } else { + multiplier = newAngle / Math.abs(newAngle); + } + newAngle = Math.PI * multiplier - newAngle * multiplier; + test = true; + } else { + newAngle = (angle1 + angle2) / 2; + } + + // shoot ray between them + var endPoint = { x: Math.cos(newAngle) * radius + pos.x, y: Math.sin(newAngle) * radius + pos.y } + var best = simulation.sight.getIntersection(pos, endPoint, map); + vertices.push(vertex); + if (best.dist <= radius) vertices.push({ x: best.x, y: best.y }) + } + vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x)); + return vertices; + }, + }, + draw: { + // powerUp() { //is set by Bayesian tech + // // ctx.globalAlpha = 0.4 * Math.sin(m.cycle * 0.15) + 0.6; + // // for (let i = 0, len = powerUp.length; i < len; ++i) { + // // ctx.beginPath(); + // // ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + // // ctx.fillStyle = powerUp[i].color; + // // ctx.fill(); + // // } + // // ctx.globalAlpha = 1; + // }, + // powerUpNormal() { //back up in case power up draw gets changed + // ctx.globalAlpha = 0.4 * Math.sin(m.cycle * 0.15) + 0.6; + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // ctx.beginPath(); + // ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + // ctx.fillStyle = powerUp[i].color; + // ctx.fill(); + // } + // ctx.globalAlpha = 1; + // }, + // powerUpBonus() { //draws crackle effect for bonus power ups + // ctx.globalAlpha = 0.4 * Math.sin(m.cycle * 0.15) + 0.6; + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // ctx.beginPath(); + // ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + // ctx.fillStyle = powerUp[i].color; + // ctx.fill(); + // } + // ctx.globalAlpha = 1; + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if (powerUp[i].isDuplicated && Math.random() < 0.1) { + // //draw electricity + // const mag = 5 + powerUp[i].size / 5 + // let unit = Vector.rotate({ + // x: mag, + // y: mag + // }, 2 * Math.PI * Math.random()) + // let path = { + // x: powerUp[i].position.x + unit.x, + // y: powerUp[i].position.y + unit.y + // } + // ctx.beginPath(); + // ctx.moveTo(path.x, path.y); + // for (let i = 0; i < 6; i++) { + // unit = Vector.rotate(unit, 3 * (Math.random() - 0.5)) + // path = Vector.add(path, unit) + // ctx.lineTo(path.x, path.y); + // } + // ctx.lineWidth = 0.5 + 2 * Math.random(); + // ctx.strokeStyle = "#000" + // ctx.stroke(); + // } + // } + // }, + + // map: function() { + // ctx.beginPath(); + // for (let i = 0, len = map.length; i < len; ++i) { + // let vertices = map[i].vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j += 1) { + // ctx.lineTo(vertices[j].x, vertices[j].y); + // } + // ctx.lineTo(vertices[0].x, vertices[0].y); + // } + // ctx.fillStyle = "#444"; + // ctx.fill(); + // }, + mapPath: null, //holds the path for the map to speed up drawing + setPaths() { + //runs at each new level to store the path for the map since the map doesn't change + simulation.draw.mapPath = new Path2D(); + for (let i = 0, len = map.length; i < len; ++i) { + let vertices = map[i].vertices; + simulation.draw.mapPath.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j += 1) { + simulation.draw.mapPath.lineTo(vertices[j].x, vertices[j].y); + } + simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y); + } + }, + lineOfSightPrecalculation() { + simulation.sight.intersectMap = []; + for (var i = 0; i < map.length; i++) { + const obj = map[i]; + const newVertices = []; + const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1)) + for (var j = 0; j < obj.vertices.length - 1; j++) { + var intersections = simulation.sight.getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap); + newVertices.push(obj.vertices[j]); + for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y }); + } + intersections = simulation.sight.getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap); + newVertices.push(obj.vertices[obj.vertices.length - 1]); + for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y }); + //draw the vertices as black circles for debugging + // for (const vertex of newVertices) { + // ctx.beginPath(); + // ctx.moveTo(vertex.x, vertex.y); + // ctx.arc(vertex.x, vertex.y, 10, 0, 2 * Math.PI); + // ctx.fillStyle = '#000'; + // ctx.fill() + // } + simulation.sight.intersectMap.push({ vertices: newVertices }); + } + }, + drawMapPath() { }, + drawMapPathDefault() { + ctx.fillStyle = color.map; + ctx.fill(simulation.draw.mapPath); + }, + drawMapSight() { + if (!simulation.isTimeSkipping) { + const pos = m.pos + const radius = 4000 + const vertices = simulation.sight.circleLoS(pos, radius); + if (vertices.length) { + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + for (var i = 1; i < vertices.length; i++) { + var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2); + var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2); + if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x); + const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x); + ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[i].x, vertices[i].y) + } + } + newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2); + currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2); + if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x); + const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x); + ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[0].x, vertices[0].y) + } + + // outline map edges, best with lighter colored document.body.style.backgroundColor + ctx.strokeStyle = "#000"; + ctx.lineWidth = 5; + ctx.stroke(simulation.draw.mapPath); + + ctx.globalCompositeOperation = "destination-in"; + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + + // make map visible + // ctx.fill(simulation.draw.mapPath); + // ctx.fillStyle = "#000"; + + ctx.clip(); //this doesn't seem to be required, but it helps with performance, probably stops the canvas context from drawing the whole map + } + } + }, + body() { + ctx.beginPath(); + for (let i = 0, len = body.length; i < len; ++i) { + let vertices = body[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } + ctx.lineWidth = 2; + ctx.fillStyle = color.block; + ctx.fill(); + ctx.strokeStyle = color.blockS; + ctx.stroke(); + }, + cons() { + ctx.beginPath(); + for (let i = 0, len = cons.length; i < len; ++i) { + ctx.moveTo(cons[i].pointA.x, cons[i].pointA.y); + // ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y); + ctx.lineTo(cons[i].bodyB.position.x + cons[i].pointB.x, cons[i].bodyB.position.y + cons[i].pointB.y); + } + for (let i = 0, len = consBB.length; i < len; ++i) { + ctx.moveTo(consBB[i].bodyA.position.x, consBB[i].bodyA.position.y); + ctx.lineTo(consBB[i].bodyB.position.x, consBB[i].bodyB.position.y); + } + ctx.lineWidth = 2; + // ctx.strokeStyle = "#999"; + ctx.strokeStyle = "rgba(0,0,0,0.15)"; + ctx.stroke(); + }, + wireFrame() { + // ctx.textAlign = "center"; + // ctx.textBaseline = "middle"; + // ctx.fillStyle = "#999"; + const bodies = Composite.allBodies(engine.world); + ctx.beginPath(); + for (let i = 0; i < bodies.length; ++i) { + //ctx.fillText(bodies[i].id,bodies[i].position.x,bodies[i].position.y); //shows the id of every body + let vertices = bodies[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } + ctx.lineWidth = 1; + ctx.strokeStyle = "#000"; + ctx.stroke(); + }, + testing() { + //jump + ctx.beginPath(); + let bodyDraw = jumpSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fill(); + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + //main body + ctx.beginPath(); + bodyDraw = playerBody.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 255, 255, 0.25)"; + ctx.fill(); + // ctx.stroke(); + //head + ctx.beginPath(); + bodyDraw = playerHead.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 255, 0, 0.4)"; + ctx.fill(); + // ctx.stroke(); + //head sensor + ctx.beginPath(); + bodyDraw = headSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 0, 255, 0.25)"; + ctx.fill(); + // ctx.stroke(); + } + }, + checkLineIntersection(v1, v1End, v2, v2End) { + // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point + let denominator, a, b, numerator1, numerator2; + let result = { + x: null, + y: null, + onLine1: false, + onLine2: false + }; + denominator = (v2End.y - v2.y) * (v1End.x - v1.x) - (v2End.x - v2.x) * (v1End.y - v1.y); + if (denominator == 0) { + return result; + } + a = v1.y - v2.y; + b = v1.x - v2.x; + numerator1 = (v2End.x - v2.x) * a - (v2End.y - v2.y) * b; + numerator2 = (v1End.x - v1.x) * a - (v1End.y - v1.y) * b; + a = numerator1 / denominator; + b = numerator2 / denominator; + + // if we cast these lines infinitely in both directions, they intersect here: + result.x = v1.x + a * (v1End.x - v1.x); + result.y = v1.y + a * (v1End.y - v1.y); + // if line1 is a segment and line2 is infinite, they intersect if: + if (a > 0 && a < 1) result.onLine1 = true; + // if line2 is a segment and line1 is infinite, they intersect if: + if (b > 0 && b < 1) result.onLine2 = true; + // if line1 and line2 are segments, they intersect if both of the above are true + return result; + }, + constructMouseDownPosition: { + x: 0, + y: 0 + }, + constructMapString: [], + constructCycle() { + if (simulation.isConstructionMode && simulation.constructMouseDownPosition) { + function round(num, round = 25) { + return Math.ceil(num / round) * round; + } + const x = round(simulation.constructMouseDownPosition.x) + const y = round(simulation.constructMouseDownPosition.y) + const dx = Math.max(25, round(simulation.mouseInGame.x) - x) + const dy = Math.max(25, round(simulation.mouseInGame.y) - y) + + ctx.strokeStyle = "#000" + ctx.lineWidth = 2; + ctx.strokeRect(x, y, dx, dy); + } + }, + enableConstructMode() { + level.isProcedural = false //this is set to be true in levels like labs that need x+ and y+ in front of positions + simulation.isConstructionMode = true; + simulation.isHorizontalFlipped = false; + simulation.isAutoZoom = false; + simulation.zoomScale = 2600; + simulation.setZoom(); + + document.body.addEventListener("mouseup", (e) => { + if (simulation.testing && simulation.constructMouseDownPosition) { + function round(num, round = 25) { + return Math.ceil(num / round) * round; + } + //clean up positions + const x = round(simulation.constructMouseDownPosition.x) + const y = round(simulation.constructMouseDownPosition.y) + const dx = Math.max(25, round(simulation.mouseInGame.x) - x) + const dy = Math.max(25, round(simulation.mouseInGame.y) - y) + // console.log(e.button) + if (e.button === 1) { + if (level.isProcedural) { + simulation.outputMapString(`spawn.randomMob(x+${x}, ${y}, 0);\n`); + } else { + simulation.outputMapString(`spawn.randomMob(${x}, ${y}, 0);\n`); + } + } else if (e.button === 4) { + simulation.outputMapString(`${Math.floor(simulation.constructMouseDownPosition.x)}, ${Math.floor(simulation.constructMouseDownPosition.y)}`); + } else if (simulation.mouseInGame.x > simulation.constructMouseDownPosition.x && simulation.mouseInGame.y > simulation.constructMouseDownPosition.y) { //make sure that the width and height are positive + if (e.button === 0) { //add map + if (level.isProcedural) { + simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`); + } else { + simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`); + } + //see map in world + spawn.mapRect(x, y, dx, dy); + len = map.length - 1 + map[len].collisionFilter.category = cat.map; + map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[len], true); //make static + Composite.add(engine.world, map[len]); //add to world + simulation.draw.setPaths() //update map graphics + } else if (e.button === 2) { //add body + if (level.isProcedural) { + simulation.outputMapString(`spawn.bodyRect(x+${x}, ${y}, ${dx}, ${dy});\n`); + } else { + simulation.outputMapString(`spawn.bodyRect(${x}, ${y}, ${dx}, ${dy});\n`); + } + //see map in world + spawn.bodyRect(x, y, dx, dy); + } + } + } + simulation.constructMouseDownPosition.x = undefined + simulation.constructMouseDownPosition.y = undefined + }); + simulation.constructMouseDownPosition.x = undefined + simulation.constructMouseDownPosition.y = undefined + document.body.addEventListener("mousedown", (e) => { + if (simulation.testing) { + simulation.constructMouseDownPosition.x = simulation.mouseInGame.x + simulation.constructMouseDownPosition.y = simulation.mouseInGame.y + } + }); + + //undo last element added after you press z + document.body.addEventListener("keydown", (e) => { // e.keyCode z=90 m=77 b=66 shift = 16 c = 67 + if (simulation.testing && e.keyCode === 90 && simulation.constructMapString.length) { + if (simulation.constructMapString[simulation.constructMapString.length - 1][6] === 'm') { //remove map from current level + const index = map.length - 1 + Matter.Composite.remove(engine.world, map[index]); + map.splice(index, 1); + simulation.draw.setPaths() //update map graphics + } else if (simulation.constructMapString[simulation.constructMapString.length - 1][6] === 'b') { //remove body from current level + const index = body.length - 1 + Matter.Composite.remove(engine.world, body[index]); + body.splice(index, 1); + } + simulation.constructMapString.pop(); + simulation.outputMapString(); + } + }); + }, + outputMapString(string) { + if (string) simulation.constructMapString.push(string) //store command as a string in the next element of an array + let out = "" //combine set of map strings to one string + let outHTML = "" + for (let i = 0, len = simulation.constructMapString.length; i < len; i++) { + out += simulation.constructMapString[i]; + outHTML += "
" + simulation.constructMapString[i] + "
" + } + console.log(out) + navigator.clipboard.writeText(out).then(function () { + /* clipboard successfully set */ + }, function () { + /* clipboard write failed */ + console.log('copy failed') + }); + document.getElementById("construct").innerHTML = outHTML + }, + // copyToClipBoard(value) { + // // Create a fake textarea + // const textAreaEle = document.createElement('textarea'); + + // // Reset styles + // textAreaEle.style.border = '0'; + // textAreaEle.style.padding = '0'; + // textAreaEle.style.margin = '0'; + + // // Set the absolute position + // // User won't see the element + // textAreaEle.style.position = 'absolute'; + // textAreaEle.style.left = '-9999px'; + // textAreaEle.style.top = `0px`; + + // // Set the value + // textAreaEle.value = value + + // // Append the textarea to body + // document.body.appendChild(textAreaEle); + + // // Focus and select the text + // textAreaEle.focus(); + // textAreaEle.select(); + + // // Execute the "copy" command + // try { + // document.execCommand('copy'); + // } catch (err) { + // // Unable to copy + // console.log(err) + // } finally { + // // Remove the textarea + // document.body.removeChild(textAreaEle); + // } + // }, +}; \ No newline at end of file diff --git a/ngon/js/spawn.js b/ngon/js/spawn.js new file mode 100644 index 00000000..c03b02d8 --- /dev/null +++ b/ngon/js/spawn.js @@ -0,0 +1,8509 @@ +//main object for spawning things in a level +const spawn = { + nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "growBossCulture"], + // other bosses: suckerBoss, laserBoss, tetherBoss, bounceBoss, sprayBoss, mineBoss, hopMomBoss //these need a particular level to work so they are not included in the random pool + randomBossList: [ + "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", + "powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", + "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", + "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss" + ], + bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed + bossTypeSpawnIndex: 0, //increases as the boss type cycles + randomLevelBoss(x, y, options = []) { + if (options.length === 0) { + const boss = spawn.bossTypeSpawnOrder[spawn.bossTypeSpawnIndex++ % spawn.bossTypeSpawnOrder.length] + spawn[boss](x, y) + } else { + spawn[options[Math.floor(Math.random() * options.length)]](x, y) + } + }, + pickList: ["starter", "starter"], + fullPickList: [ + "slasher", "slasher", "slasher2", "slasher3", + "flutter", "flutter", "flutter", + "hopper", "hopper", "hopper", + "stabber", "stabber", "stabber", + "springer", "springer", "springer", + "shooter", "shooter", + "grenadier", "grenadier", + "striker", "striker", + "laser", "laser", + "pulsar", "pulsar", + "sneaker", "launcher", "launcherOne", "exploder", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster", "focuser" + ], + mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed + mobTypeSpawnIndex: 0, //increases as the mob type cycles + allowedGroupList: ["spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "launcherOne", "stabber", "sniper", "pulsar", "grenadier", "slasher", "flutter"], + setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level + spawn.pickList.splice(0, 1); + const push = spawn.mobTypeSpawnOrder[spawn.mobTypeSpawnIndex++ % spawn.mobTypeSpawnOrder.length] + spawn.pickList.push(push); + // if (spawn.mobTypeSpawnIndex > spawn.mobTypeSpawnOrder.length) spawn.mobTypeSpawnIndex = 0 + //each level has 2 mobs: one new mob and one from the last level + // spawn.pickList.splice(0, 1); + // spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]); + }, + spawnChance(chance) { + return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1) + }, + randomMob(x, y, chance = 1) { + if (spawn.spawnChance(chance) || chance === Infinity) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x, y); + } + if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x, y); + } + }, + randomSmallMob(x, y, + num = Math.max(Math.min(Math.round(Math.random() * simulation.difficulty * 0.2), 4), 0), + size = 16 + Math.ceil(Math.random() * 15), + chance = 1) { + if (spawn.spawnChance(chance)) { + for (let i = 0; i < num; ++i) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); + } + } + if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { + for (let i = 0; i < num; ++i) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); + } + } + }, + randomGroup(x, y, chance = 1) { + if (spawn.spawnChance(chance) && simulation.difficulty > 2 || chance === Infinity) { + //choose from the possible picklist + let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + //is the pick able to be a group? + let canBeGroup = false; + for (let i = 0, len = spawn.allowedGroupList.length; i < len; ++i) { + if (spawn.allowedGroupList[i] === pick) { + canBeGroup = true; + break; + } + } + if (canBeGroup) { + if (Math.random() < 0.55) { + spawn.nodeGroup(x, y, pick); + } else { + spawn.lineGroup(x, y, pick); + } + } else { + if (Math.random() < 0.1) { + spawn[pick](x, y, 90 + Math.random() * 40); //one extra large mob + spawn.spawnOrbitals(mob[mob.length - 1], mob[mob.length - 1].radius + 50 + 200 * Math.random(), 1) + // } else if (Math.random() < 0.35) { + // spawn.blockGroup(x, y) //hidden grouping blocks + } else { + pick = (Math.random() < 0.5) ? "randomList" : "random"; + if (Math.random() < 0.55) { + spawn.nodeGroup(x, y, pick); + } else { + spawn.lineGroup(x, y, pick); + } + } + } + } + }, + secondaryBossThreshold: 22, + secondaryBossChance(x, y) { + // if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { + // tech.isScaleMobsWithDuplication = true + // spawn.randomLevelBoss(x, y); + // tech.isScaleMobsWithDuplication = false + // return true + // } else if (tech.isResearchBoss) { + // if (powerUps.research.count > 2) { + // powerUps.research.changeRerolls(-3) + // simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) + // } else { + // tech.addJunkTechToPool(0.49) + // } + // spawn.randomLevelBoss(x, y); + // return true + // } + if (simulation.difficulty > spawn.secondaryBossThreshold) { //starts on hard mode level 6, level 12 on easy, level 4 on why? + spawn.randomLevelBoss(x, y); + } else { + return false + } + }, + //mob templates ********************************************************************************************* + //*********************************************************************************************************** + MACHO(x = m.pos.x, y = m.pos.y) { //immortal mob that follows player //if you have the tech it spawns at start of every level at the player + mobs.spawn(x, y, 3, 0.1, "transparent"); + let me = mob[mob.length - 1]; + me.stroke = "transparent" + me.isShielded = true; //makes it immune to damage + me.leaveBody = false; + me.isBadTarget = true; + me.isUnblockable = true; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.collisionFilter.category = 0; + me.collisionFilter.mask = 0; //cat.player //| cat.body + me.chaseSpeed = 3.3 + me.isMACHO = true; + me.frictionAir = 0.006 + me.onDeath = function () { + tech.isHarmMACHO = false; + } + me.do = function () { + if (!simulation.isTimeSkipping) { + const sine = Math.sin(simulation.cycle * 0.015) + this.radius = 55 * tech.isDarkStar + 370 * (1 + 0.1 * sine) + //chase player + const sub = Vector.sub(player.position, this.position) + const mag = Vector.magnitude(sub) + // follow physics + // Matter.Body.setVelocity(this, { x: 0, y: 0 }); + // const where = Vector.add(this.position, Vector.mult(Vector.normalise(sub), this.chaseSpeed)) + // if (mag > 10) Matter.Body.setPosition(this, { x: where.x, y: where.y }); + + //realistic physics + const force = Vector.mult(Vector.normalise(sub), 0.000000003) + this.force.x += force.x + this.force.y += force.y + + + if (mag < this.radius) { //buff to player when inside radius + tech.isHarmMACHO = true; + + //draw halo + ctx.strokeStyle = "rgba(80,120,200,0.2)" //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 36, 0, 2 * Math.PI); + ctx.lineWidth = 10; + ctx.stroke(); + // ctx.strokeStyle = "rgba(255,255,0,0.17)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); + // ctx.lineWidth = 30; + // ctx.stroke(); + } else { + tech.isHarmMACHO = false; + } + //draw outline + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius + 15, 0, 2 * Math.PI); + ctx.strokeStyle = "#000" + ctx.lineWidth = 1; + ctx.stroke(); + if (tech.isDarkStar && !m.isCloak) { //&& !m.isBodiesAsleep + ctx.fillStyle = "rgba(10,0,40,0.4)" + ctx.fill() + //damage mobs + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded) { + if (Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius < this.radius) { + mob[i].damage(0.02 * m.dmgScale); + // mob[i].locatePlayer();// + + simulation.drawList.push({ //add dmg to draw queue + x: mob[i].position.x, + y: mob[i].position.y, + radius: mob[i].radius + 8, + color: `rgba(10,0,40,0.1)`, // random hue, but not red + time: 4 + }); + } + } + } + } + //draw growing and fading out ring around the arc + ctx.beginPath(); + const rate = 150 + const r = simulation.cycle % rate + ctx.arc(this.position.x, this.position.y, 15 + this.radius + 0.3 * r, 0, 2 * Math.PI); + ctx.strokeStyle = `rgba(0,0,0,${0.5 * Math.max(0, 1 - 1.4 * r / rate)})` + ctx.stroke(); + } + } + }, + WIMP(x = level.exit.x + tech.wimpCount * 200 * (Math.random() - 0.5), y = level.exit.y + tech.wimpCount * 200 * (Math.random() - 0.5)) { //immortal mob that follows player //if you have the tech it spawns at start of every level at the exit + mobs.spawn(x, y, 3, 0.1, "transparent"); + let me = mob[mob.length - 1]; + me.stroke = "transparent" + me.isShielded = true; //makes it immune to damage + me.leaveBody = false; + me.isBadTarget = true; + me.isUnblockable = true; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.collisionFilter.category = 0; + me.collisionFilter.mask = 0; //cat.player //| cat.body + me.chaseSpeed = 1.2 + 2.3 * Math.random() + + me.awake = function () { + //chase player + const sub = Vector.sub(player.position, this.position) + const where = Vector.add(this.position, Vector.mult(Vector.normalise(sub), this.chaseSpeed)) + + Matter.Body.setPosition(this, { //hold position + x: where.x, + y: where.y + }); + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + + //aoe damage to player + if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius) { + const DRAIN = tech.isRadioactiveResistance ? 0.05 * 0.25 : 0.05 + if (m.energy > DRAIN) { + if (m.immuneCycle < m.cycle) m.energy -= DRAIN + } else { + m.energy = 0; + m.damage((tech.isRadioactiveResistance ? 0.005 * 0.25 : 0.005) * simulation.dmgScale) + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: this.radius, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(25,139,170,${0.2 + 0.12 * Math.random()})`; + ctx.fill(); + this.radius = 100 * (1 + 0.25 * Math.sin(simulation.cycle * 0.03)) + } + me.do = function () { //wake up after the player moves + if (player.speed > 1 && !m.isCloak) { + if (this.distanceToPlayer() < 500) { + const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28) + Matter.Body.setPosition(this, Vector.add(player.position, Vector.mult(unit, 2000))) + } + setTimeout(() => { + this.do = this.awake; + }, 1000 * Math.random()); + } + this.checkStatus(); + }; + }, + finalBoss(x, y, radius = 300) { + mobs.spawn(x, y, 6, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { x: me.position.x, y: me.position.y }, + bodyB: me, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + }, 1000); //add in a delay in case the level gets flipped left right + me.isBoss = true; + me.isFinalBoss = true; + me.frictionAir = 0.01; + me.memory = Infinity; + me.locatePlayer(); + me.hasRunDeathScript = false + me.cycle = 1; + + Matter.Body.setDensity(me, 0.2); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.14 + me.startingDamageReduction = me.damageReduction + me.nextHealthThreshold = 0.999 + me.invulnerableCount = 0 + me.isInvulnerable = false + me.totalModes = 0 + me.lastDamageCycle = 0 + me.onDamage = function () { + this.lastDamageCycle = this.cycle + if (this.health < this.nextHealthThreshold) { + if (this.health === 1) me.cycle = 1; //reset fight + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.invulnerableCount = 220 + 10 * simulation.difficultyMode //how long does invulnerable time last + this.isInvulnerable = true + this.damageReduction = 0 + } + }; + me.invulnerable = function () { + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + this.pushAway(); + this.mode[this.totalModes].enter() //enter new mode + this.totalModes++ + //spawn 6 mobs + me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; //fire a bullet from each vertex + for (let i = 0; i < 6; i++) me.spawnMobs(i) + + level.newLevelOrPhase() //run some new level tech effects + } + ctx.beginPath(); //draw invulnerable + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 15 + 6 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + ctx.fillStyle = `rgba(255,255,255,${Math.min(1, 120 / (this.invulnerableCount + 60))})`; + ctx.fill() + } + } + me.damageReductionDecay = function () { //slowly make the boss take more damage over time //damageReduction resets with each invulnerability phase + if (!(me.cycle % 60) && this.lastDamageCycle + 240 > this.cycle) this.damageReduction *= 1.04 //only decay once a second //only decay if the player has done damage in the last 4 seconds + } + me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)] + me.spawnMobs = function (index = 0) { + const vertex = me.vertices[index] + const unit = Vector.normalise(Vector.sub(me.position, vertex)) + const where = Vector.add(vertex, Vector.mult(unit, -30)) + spawn[me.mobType](where.x + 50 * (Math.random() - 0.5), where.y + 50 * (Math.random() - 0.5)); + const velocity = Vector.mult(Vector.perp(unit), -10) //give the mob a rotational velocity as if they were attached to a vertex + Matter.Body.setVelocity(mob[mob.length - 1], { x: me.velocity.x + velocity.x, y: me.velocity.y + velocity.y }); + } + me.maxMobs = 400 + me.mode = [{ + name: "boulders", + spawnRate: 170 - 6 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.boulder(me.position.x, me.position.y + 250) + } + }, + enter() { }, + exit() { }, + }, { + name: "mobs", + // whoSpawn: spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)], + spawnRate: 280 - 20 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.torque += 0.000015 * me.inertia; //spin + const index = Math.floor((me.cycle % (this.spawnRate * 6)) / this.spawnRate) //int from 0 to 5 + if (index === 0) me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; //fire a bullet from each vertex + me.spawnMobs(index) + // const vertex = me.vertices[index] + // const unit = Vector.normalise(Vector.sub(me.position, vertex)) + // const where = Vector.add(vertex, Vector.mult(unit, -30)) + // spawn[me.mobType](where.x + 50 * (Math.random() - 0.5), where.y + 50 * (Math.random() - 0.5)); + // const velocity = Vector.mult(Vector.perp(unit), -18) //give the mob a rotational velocity as if they were attached to a vertex + // Matter.Body.setVelocity(mob[mob.length - 1], { x: me.velocity.x + velocity.x, y: me.velocity.y + velocity.y }); + } + }, + enter() { }, + exit() { }, + }, + { + name: "hoppers", + spawnRate: 480 - 16 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.torque += 0.00002 * me.inertia; //spin + for (let i = 0; i < 6; i++) { + const vertex = me.vertices[i] + spawn.hopBullet(vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5), 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) + Matter.Body.setDensity(mob[mob.length - 1], 0.002); //normal is 0.001 + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(me.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex + Matter.Body.setVelocity(mob[mob.length - 1], { + x: me.velocity.x + velocity.x, + y: me.velocity.y + velocity.y + }); + } + let where = { x: 600 - Math.random() * 100, y: -225 } + if (simulation.isHorizontalFlipped) where.x = -600 + Math.random() * 100 + spawn.hopBullet(where.x, where.y, 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) + Matter.Body.setDensity(mob[mob.length - 1], 0.002); //normal is 0.001 + } + }, + enter() { }, + exit() { }, + }, + { + name: "seekers", + spawnRate: 100 - 3 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { //spawn seeker + const index = Math.floor((me.cycle % 360) / 60) + spawn.seeker(me.vertices[index].x, me.vertices[index].y, 18 * (0.5 + Math.random())); //seeker(x, y, radius = 8, sides = 6) + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + who.timeLeft = 720 + 30 * simulation.difficulty //* (0.8 + 0.4 * Math.random()); + who.accelMag = 0.0004 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + } + }, + enter() { }, + exit() { }, + }, + { + name: "mines", + bombCycle: 0, + bombInterval: 10 - simulation.difficultyMode, + do() { + const yOff = 120 + this.bombCycle++ + if (!(this.bombCycle % this.bombInterval) && (this.bombCycle % 660) > 330) { //mines above player + if (simulation.isHorizontalFlipped) { + const x = m.pos.x + 200 * (Math.random() - 0.5) + if (x > -750) { //mines above player IN tunnel + spawn.mine(Math.min(Math.max(-730, x), 100), -450 - yOff * Math.random()) //player in main room + mob[mob.length - 1].fallHeight = -209 + } else { //mines above player NOT in tunnel + spawn.mine(Math.min(Math.max(-5375, x), -765), -1500 - yOff * Math.random()) //player in tunnel + mob[mob.length - 1].fallHeight = -9 + } + if (Math.random() < 0.5) { + spawn.mine(-5350 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines + mob[mob.length - 1].fallHeight = -9 + } + } else { + const x = m.pos.x + 200 * (Math.random() - 0.5) + if (x < 750) { //mines above player IN tunnel + spawn.mine(Math.min(Math.max(-100, x), 735), -450 - yOff * Math.random()) //player in main room + mob[mob.length - 1].fallHeight = -209 + } else { //mines above player NOT in tunnel + spawn.mine(Math.min(Math.max(760, x), 5375), -1500 - yOff * Math.random()) //player in tunnel + mob[mob.length - 1].fallHeight = -9 + } + if (Math.random() < 0.5) { //random mines, but not in tunnel + spawn.mine(800 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines + mob[mob.length - 1].fallHeight = -9 + } + } + } + for (let i = 0; i < mob.length; i++) { //mines fall + if (mob[i].isMine) { + if (mob[i].position.y < mob[i].fallHeight) { + mob[i].force.y += mob[i].mass * 0.03; + } else if (!mob[i].isOnGround) { + mob[i].isOnGround = true + Matter.Body.setPosition(mob[i], { + x: mob[i].position.x, + y: mob[i].fallHeight + }) + } + } + } + }, + enter() { + this.bombCycle = 0; + }, + exit() { + for (let i = 0; i < mob.length; i++) { + if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round + } + }, + }, + { + name: "orbiters", + spawnRate: Math.ceil(4 - 0.25 * simulation.difficultyMode), + orbitersCycle: 0, + do() { + this.orbitersCycle++ + if (!(this.orbitersCycle % this.spawnRate) && (this.orbitersCycle % 660) > 600 && mob.length < me.maxMobs) { + const speed = (0.01 + 0.0005 * simulation.difficultyMode) * ((Math.random() < 0.5) ? 0.85 : -1.15) + const phase = 0 //Math.floor(2 * Math.random()) * Math.PI + //find distance to play and set orbs at that range + const dist = me.distanceToPlayer() + //360 + 2150 * Math.random() + me.orbitalNoVelocity(me, dist + 900 * (Math.random() - 0.5), 0.1 * Math.random() + phase, speed) // orbital(who, radius, phase, speed) + } + }, + enter() { }, + exit() { }, + }, + { + name: "laser", + spinForce: 0.00000008, // * (Math.random() < 0.5 ? -1 : 1), + fadeCycle: 0, //fades in over 4 seconds + do() { + this.fadeCycle++ + if (this.fadeCycle > 0) { + me.torque += this.spinForce * me.inertia; //spin //0.00000015 + if (this.fadeCycle > 360) this.fadeCycle = -150 + 2 * simulation.difficultyMode * simulation.difficultyMode //turn laser off and reset + ctx.strokeStyle = "#50f"; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.lineWidth = 1.5; + ctx.beginPath(); + if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in + const scale = this.fadeCycle / 120 + const dmg = this.fadeCycle < 60 ? 0 : 0.1 * simulation.dmgScale * scale + me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg); + me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg); + me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg); + me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6, dmg); + me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6, dmg); + me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6, dmg); + ctx.strokeStyle = `rgba(85, 0, 255,${scale})`; + ctx.stroke(); + ctx.strokeStyle = `rgba(80, 0, 255,${0.07 * scale})` + } else if (this.fadeCycle > 0) { + me.lasers(me.vertices[0], me.angle + Math.PI / 6); + me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6); + me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6); + me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6); + me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6); + me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6); + ctx.strokeStyle = "#50f"; + ctx.stroke(); + ctx.strokeStyle = "rgba(80,0,255,0.07)"; + } + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.stroke(); + } + }, + enter() { this.fadeCycle = 0 }, + exit() { }, + }, + { + name: "black hole", + eventHorizon: 0, + eventHorizonRadius: 1900, + eventHorizonCycle: 0, + do() { + this.eventHorizonCycle++ + this.eventHorizon = Math.max(0, this.eventHorizonRadius * Math.sin(this.eventHorizonCycle * 0.007)) //eventHorizon waves in and out + //draw darkness + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.2, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.3)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.4, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.25)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.6, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.2)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.8, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.15)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fill(); + //when player is inside event horizon + if (Vector.magnitude(Vector.sub(me.position, player.position)) < this.eventHorizon) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.018 + if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0003 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - me.position.y, player.position.x - me.position.x); + player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + player.force.y -= 0.0017 * Math.sin(angle) * player.mass; + //draw line to player + ctx.beginPath(); + ctx.moveTo(me.position.x, me.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, me.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fill(); + } + me.curl(this.eventHorizon); + }, + enter() { this.eventHorizonCycle = 0 }, + exit() { }, + }, + { + name: "oscillation", + waveCycle: 0, + whereX: simulation.isHorizontalFlipped ? -3000 : 3000, + do() { + this.waveCycle += !me.isStunned + !me.isSlowed + // if (!me.isShielded && (!(this.waveCycle % 1800) || !(this.waveCycle % 1801))) spawn.shield(me, me.position.x, me.position.y, 1); + me.constraint.pointA = { + x: this.whereX + 600 * Math.sin(this.waveCycle * 0.005), + y: me.constraint.pointA.y + } + }, + enter() { + spawn.shield(me, me.position.x, me.position.y, 1); + }, + exit() { this.waveCycle = 0 }, + }, + // { + // name: "__", + // do() {}, + // enter() {}, + // exit() {}, + // }, + ] + shuffle(me.mode); //THIS SHOULDN'T BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + me.do = function () { + this.fill = `hsl(${360 * Math.sin(this.cycle * 0.011)},${80 + 20 * Math.sin(this.cycle * 0.004)}%,${60 + 20 * Math.sin(this.cycle * 0.009)}%)` + if (this.health < 1) { + this.cycle++; + this.checkStatus(); + this.invulnerable(); + this.spawnBoss(); + this.damageReductionDecay(); + for (let i = 0; i < this.totalModes; i++) this.mode[i].do() + } + // this.mode[5].do() //deelete this + // this.cycle++; + // this.mode[4].do() + // this.mode[7].do() + }; + me.spawnRate = 5800 - 30 * simulation.difficultyMode * simulation.difficultyMode + me.spawnBoss = function () { //if the fight lasts too long start spawning bosses + if (!(me.cycle % this.spawnRate) && this.health < 1) { + this.spawnRate = Math.max(300, this.spawnRate - 10 * simulation.difficultyMode * simulation.difficultyMode) //reduce the timer each time a boss spawns + spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5)) + } + } + me.pushAway = function (magX = 0.13, magY = 0.05) { + for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally + body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1) + body[i].force.y -= magY * body[i].mass + } + for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally + bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1) + bullet[i].force.y -= magY * bullet[i].mass + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally + powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1) + powerUp[i].force.y -= magY * powerUp[i].mass + } + player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1) + player.force.y -= magY * player.mass + } + me.boulder = function (x, y) { + mobs.spawn(x, y, 6, Math.floor(50 + 50 * Math.random()), this.fill); + let boss = this + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.timeLeft = 0 + }; + me.explodeRange = 500 + me.onDeath = function () { //explode + simulation.drawList.push({ //draw explosion + x: this.position.x, + y: this.position.y, + radius: this.explodeRange, + color: this.fill, + time: 8 + }); + let sub, knock + sub = Vector.sub(player.position, this.position); + if (Vector.magnitude(sub) < this.explodeRange) { //player knock back + m.damage(0.05); + knock = Vector.mult(Vector.normalise(sub), player.mass * 0.1); + player.force.x += knock.x; + player.force.y += knock.y; + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //power up knock backs + sub = Vector.sub(powerUp[i].position, this.position); + if (Vector.magnitude(sub) < this.explodeRange) { + knock = Vector.mult(Vector.normalise(sub), powerUp[i].mass * 0.1); + powerUp[i].force.x += knock.x; + powerUp[i].force.y += knock.y; + } + } + for (let i = 0, len = body.length; i < len; ++i) { //block knock backs + sub = Vector.sub(body[i].position, this.position); + if (Vector.magnitude(sub) < this.explodeRange) { + knock = Vector.mult(Vector.normalise(sub), body[i].mass * 0.1); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + } + } + } + Matter.Body.setDensity(me, 0.003); //normal is 0.001 + me.timeLeft = 360; + me.g = 0.0005; //required if using this.gravity + me.frictionAir = 0.005; + me.friction = 1; + me.frictionStatic = 1 + me.restitution = 0; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.spin = me.inertia * 0.000005 * (1 + Math.random()) * (m.pos.x > me.position.x ? 1 : -1) + Matter.Body.setAngularVelocity(me, 0.1 * (1 + 0.3 * Math.random()) * (m.pos.x > me.position.x ? 1 : -1)); + Matter.Body.setVelocity(me, { x: 0, y: 10 }); + me.do = function () { + this.fill = boss.fill + this.torque += this.spin; + this.gravity(); + this.timeLimit(); + }; + } + me.orbitalNoVelocity = function (who, radius, phase, speed) { //orbitals that don't include their host velocity //specifically for finalBoss + let boss = this + mobs.spawn(who.position.x, who.position.y, 6, 20, "rgb(255,0,150)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.001); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isUnstable = true; //dies when blocked + me.showHealthBar = false; + me.isOrbital = true; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + this.fill = boss.fill + const time = simulation.cycle * speed + phase + const orbit = { x: Math.cos(time), y: Math.sin(time) } + Matter.Body.setPosition(this, Vector.add(who.position, Vector.mult(orbit, radius))) + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.13 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + this.death(); + } + }; + } + me.lasers = function (where, angle, dmg = 0.1 * simulation.dmgScale) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + if (m.immuneCycle < m.cycle + 60 + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + m.collisionImmuneCycles; //player is immune to damage extra time + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + } + me.onDeath = function () { + if (!this.hasRunDeathScript) { + this.hasRunDeathScript = true + //make a block body to replace this one + //this body is too big to leave behind in the normal way mobs.replace() + const len = body.length; + const v = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //might help with vertex collision issue, not sure + body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); + Matter.Body.setVelocity(body[len], { x: 0, y: -3 }); + Matter.Body.setAngularVelocity(body[len], this.angularVelocity); + body[len].collisionFilter.category = cat.body; + body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + body[len].classType = "body"; + Composite.add(engine.world, body[len]); //add to world + const expand = function (that, massLimit) { + const scale = 1.05; + Matter.Body.scale(that, scale, scale); + if (that.mass < massLimit) setTimeout(expand, 20, that, massLimit); + }; + expand(body[len], 200) + + function unlockExit() { + if (simulation.isHorizontalFlipped) { + level.exit.x = -5500 - 100; + } else { + level.exit.x = 5500; + } + level.exit.y = -330; + Matter.Composite.remove(engine.world, map[map.length - 1]); + map.splice(map.length - 1, 1); + simulation.draw.setPaths(); //redraw map draw path + // level.levels.push("null") + } + + //add lore level as next level if player took lore tech earlier in the game + if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) { + simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); + setTimeout(function () { + simulation.makeTextLog(`level.levels.push("null")`, 720); + unlockExit() + level.levels.push("null") + }, 4000); + //remove block map element so exit is clear + } else { //reset game + let count = 0 + + function loop() { + if (!simulation.paused && !simulation.onTitlePage) { + count++ + if (count < 660) { + if (count === 1) simulation.makeTextLog(`//enter testing mode to set level.levels.length to Infinite`); + if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`); + } else if (count === 660) { + simulation.makeTextLog(`simulation.analysis = 1 //analysis complete`); + } else if (count === 780) { + simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`) + } else if (count === 1020) { + simulation.makeTextLog(`Engine.clear(engine) //simulation successful`); + } else if (count === 1260) { + // tech.isImmortal = false; + // m.death() + // m.alive = false; + // simulation.paused = true; + // m.health = 0; + // m.displayHealth(); + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + document.getElementById("damage-bar").style.display = "none" + document.getElementById("text-log").style.display = "none" + document.getElementById("fade-out").style.opacity = 1; //slowly fades out + // build.shareURL(false) + setTimeout(function () { + if (!simulation.onTitlePage) { + m.alive = false + simulation.paused = true; + // simulation.clearMap(); + // Matter.Composite.clear(composite, keepStatic, [deep = false]) + // Composite.clear(engine.composite); + engine.world.bodies.forEach((body) => { Matter.Composite.remove(engine.world, body) }) + Engine.clear(engine); + simulation.splashReturn(); + } + }, 6000); + return + } + } + if (simulation.testing) { + unlockExit() + setTimeout(function () { + simulation.makeTextLog(`level.levels.length = Infinite`); + }, 1500); + } else { + if (!simulation.onTitlePage) requestAnimationFrame(loop); + } + } + requestAnimationFrame(loop); + } + // for (let i = 0; i < 3; i++) + level.difficultyIncrease(simulation.difficultyMode) //ramp up damage + //remove power Ups, to avoid spamming console + function removeAll(array) { + for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); + } + removeAll(powerUp); + powerUp = []; + + //pull in particles + for (let i = 0, len = body.length; i < len; ++i) { + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, body[i].position)), 65) + const pushUp = Vector.add(velocity, { x: 0, y: -0.5 }) + Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp)); + } + //damage all mobs + for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i] !== this) { + if (mob[i].isInvulnerable) { //disable invulnerability + mob[i].isInvulnerable = false + mob[i].damageReduction = 1 + } + mob[i].damage(Infinity, true); + } + } + } + + //draw stuff + for (let i = 0, len = 22; i < len; i++) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: (i + 1) * 150, + color: `rgba(255,255,255,0.17)`, + time: 5 * (len - i + 1) + }); + } + } + }; + }, + // finalBoss(x, y, radius = 300) { + // mobs.spawn(x, y, 6, radius, "rgb(150,150,255)"); + // let me = mob[mob.length - 1]; + // setTimeout(() => { //fix mob in place, but allow rotation + // me.constraint = Constraint.create({ + // pointA: { + // x: me.position.x, + // y: me.position.y + // }, + // bodyB: me, + // stiffness: 1, + // damping: 1 + // }); + // Composite.add(engine.world, me.constraint); + // }, 2000); //add in a delay in case the level gets flipped left right + // me.isBoss = true; + // me.isFinalBoss = true; + // me.frictionAir = 0.01; + // me.memory = Infinity; + // me.hasRunDeathScript = false + // me.locatePlayer(); + // // const density = 0.2 + // Matter.Body.setDensity(me, 0.2); //extra dense //normal is 0.001 //makes effective life much larger + // // spawn.shield(me, x, y, 1); + // me.onDamage = function() {}; + // me.cycle = 660; + // me.endCycle = 780; + // me.totalCycles = 0 + // me.mode = 0; + // me.damageReduction = 0.25 //reset on each new mode + // me.pushAway = function(magX = 0.13, magY = 0.05) { + // for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally + // body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1) + // body[i].force.y -= magY * body[i].mass + // } + // for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally + // bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1) + // bullet[i].force.y -= magY * bullet[i].mass + // } + // for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally + // powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1) + // powerUp[i].force.y -= magY * powerUp[i].mass + // } + // player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1) + // player.force.y -= magY * player.mass + // } + // me.do = function() { + // this.modeDo(); //this does different things based on the mode + // this.checkStatus(); + // this.cycle++; //switch modes÷ if time isn't paused + // this.totalCycles++; + // if (this.health > 0.3) { + // if (this.cycle > this.endCycle) { + // this.showHealthBar = true + // this.cycle = 0; + // this.mode++ + // this.damageReduction = 0.25 + // if (this.totalCycles > 180) this.pushAway(); + // if (this.mode > 3) { + // this.mode = 0; + // this.fill = "#50f"; + // this.rotateVelocity = Math.abs(this.rotateVelocity) * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away + // this.modeDo = this.modeLasers + // //push blocks and player away, since this is the end of suck, and suck causes blocks to fall on the boss and stun it + // Matter.Body.scale(this, 1000, 1000); + // if (!this.isShielded) spawn.shield(this, this.position.x, this.position.y, 1); // regen shield to also prevent stun + // } else if (this.mode === 1) { + // this.fill = "#50f"; // this.fill = "rgb(150,150,255)"; + // this.modeDo = this.modeSpawns + // } else if (this.mode === 2) { + // this.fill = "#50f"; + // this.modeDo = this.modeBombs + // } else if (this.mode === 3) { + // for (let i = 0; i < mob.length; i++) { + // if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round + // } + // this.fill = "#000"; + // this.modeDo = this.modeSuck + // Matter.Body.scale(this, 0.001, 0.001); + // this.damageReduction = 0.000025 + // this.showHealthBar = false + // } + + // if (tech.isGunCycle) { + // b.inventoryGun++; + // if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0; + // simulation.switchGun(); + // } + // } + // } else if (this.mode !== 3) { //all three modes at once , this runs once + // this.showHealthBar = true + // this.pushAway(); + // this.cycle = 0; + // this.endCycle = Infinity + // this.damageReduction = 0.15 + // if (this.mode === 2) { + // Matter.Body.scale(this, 500, 500); + // } else { + // Matter.Body.scale(this, 0.5, 0.5); + // } + // this.mode = 3 + // this.fill = "#000"; + // this.eventHorizon = 750 + // this.spawnInterval = 600 + // this.rotateVelocity = 0.001 * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away + // // if (!this.isShielded) spawn.shield(this, x, y, 1); //regen shield here ? + // this.modeDo = this.modeAll + // this.eventHorizonRadius = 700 + // if (tech.isGunCycle) { + // b.inventoryGun++; + // if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0; + // simulation.switchGun(); + // } + // } + // // } + // }; + // me.modeDo = function() {} + // me.modeAll = function() { + // this.modeBombs() + // this.modeSpawns() + // this.modeSuck() + // this.modeLasers() + // } + // me.bombInterval = 36 - 0.5 * simulation.difficultyMode * simulation.difficultyMode + // me.modeBombs = function() { + // if (!(this.cycle % 20)) { + // if (m.pos.x < 750) { + // spawn.mine(m.pos.x + 200 * (Math.random() - 0.5), -500) + // } else { + // spawn.mine(Math.min(Math.max(770, m.pos.x + 200 * (Math.random() - 0.5)), 5350), -1500) + // } + // } + // if (!(this.cycle % 10)) spawn.mine(800 + 4550 * Math.random(), -1500) + + // //mines fall + // for (let i = 0; i < mob.length; i++) { + // // if (mob[i].isMine && mob[i].position.y < -5) Matter.Body.setPosition(mob[i], { x: mob[i].position.x, y: mob[i].position.y + 5 }) + // if (mob[i].isMine && mob[i].position.y < -14) mob[i].force.y += mob[i].mass * 0.03; + // } + + // } + // me.spawnInterval = 395 + // me.modeSpawns = function() { + // if (!(this.cycle % this.spawnInterval) && mob.length < 40) { + // if (this.mode !== 3) Matter.Body.setAngularVelocity(this, 0.1) + // //fire a bullet from each vertex + // const whoSpawn = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; + // for (let i = 0, len = 2 + this.totalCycles / 1000; i < len; i++) { + // const vertex = this.vertices[i % 6] + // spawn[whoSpawn](vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5)); + // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex + // Matter.Body.setVelocity(mob[mob.length - 1], { + // x: this.velocity.x + velocity.x, + // y: this.velocity.y + velocity.y + // }); + // } + // if (!(this.cycle % 2 * this.spawnInterval) && mob.length < 40) { + // const len = (this.totalCycles / 1000 + simulation.difficulty / 2 - 30) / 15 + // for (let i = 0; i < len; i++) { + // spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5)) + // } + // } + // } + // } + // me.eventHorizon = 0 + // me.eventHorizonRadius = 1300 + // me.modeSuck = function() { + // if (!(this.cycle % 30)) { + // const index = Math.floor((this.cycle % 360) / 60) + // spawn.seeker(this.vertices[index].x, this.vertices[index].y, 20 * (0.5 + Math.random()), 9); //give the bullet a rotational velocity as if they were attached to a vertex + // const who = mob[mob.length - 1] + // Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + // who.timeLeft = 720 + 10 * simulation.difficulty //* (0.8 + 0.4 * Math.random()); + // who.accelMag = 0.0003 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + // who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -7) + // Matter.Body.setVelocity(who, { + // x: this.velocity.x + velocity.x, + // y: this.velocity.y + velocity.y + // }); + // } + + // //eventHorizon waves in and out + // if (this.cycle + 30 > this.endCycle) { //shrink fast in last bit of cycle + // this.eventHorizon = 0.93 * this.eventHorizon + // } else { + // this.eventHorizon = 0.97 * this.eventHorizon + 0.03 * (this.eventHorizonRadius * (1 - 0.5 * Math.cos(this.cycle * 0.015))) + // } + // //draw darkness + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon * 0.2, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,20,40,0.6)"; + // ctx.fill(); + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon * 0.4, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,20,40,0.4)"; + // ctx.fill(); + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon * 0.6, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,20,40,0.3)"; + // ctx.fill(); + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon * 0.8, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,20,40,0.2)"; + // ctx.fill(); + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.05)"; + // ctx.fill(); + // //when player is inside event horizon + // if (Vector.magnitude(Vector.sub(this.position, player.position)) < this.eventHorizon) { + // if (m.immuneCycle < m.cycle) { + // if (m.energy > 0) m.energy -= 0.02 + // if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0004 * simulation.dmgScale); + // } + // const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + // player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + // player.force.y -= 0.0017 * Math.sin(angle) * player.mass; + // //draw line to player + // ctx.beginPath(); + // ctx.moveTo(this.position.x, this.position.y); + // ctx.lineTo(m.pos.x, m.pos.y); + // ctx.lineWidth = Math.min(60, this.radius * 2); + // ctx.strokeStyle = "rgba(0,0,0,0.5)"; + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.3)"; + // ctx.fill(); + // } + // this.curl(this.eventHorizon); + // } + // me.rotateVelocity = 0.0025 + // me.rotateCount = 0; + // me.lasers = function(where, angle, dmg = 0.14 * simulation.dmgScale) { + // const vertexCollision = function(v1, v1End, domain) { + // for (let i = 0; i < domain.length; ++i) { + // let vertices = domain[i].vertices; + // const len = vertices.length - 1; + // for (let j = 0; j < len; j++) { + // results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[j], + // v2: vertices[j + 1] + // }; + // } + // } + // results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2) best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[0], + // v2: vertices[len] + // }; + // } + // } + // }; + + // const seeRange = 7000; + // best = { + // x: null, + // y: null, + // dist2: Infinity, + // who: null, + // v1: null, + // v2: null + // }; + // const look = { + // x: where.x + seeRange * Math.cos(angle), + // y: where.y + seeRange * Math.sin(angle) + // }; + // // vertexCollision(where, look, mob); + // vertexCollision(where, look, map); + // vertexCollision(where, look, body); + // if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + // if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + // if (m.immuneCycle < m.cycle + 60 + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + m.collisionImmuneCycles; //player is immune to damage extra time + // m.damage(dmg); + // simulation.drawList.push({ //add dmg to draw queue + // x: best.x, + // y: best.y, + // radius: dmg * 1500, + // color: "rgba(80,0,255,0.5)", + // time: 20 + // }); + // } + // //draw beam + // if (best.dist2 === Infinity) best = look; + // ctx.moveTo(where.x, where.y); + // ctx.lineTo(best.x, best.y); + // } + // me.modeLasers = function() { + // if (!this.isStunned) { + // let slowed = false //check if slowed + // for (let i = 0; i < this.status.length; i++) { + // if (this.status[i].type === "slow") { + // slowed = true + // break + // } + // } + // if (!slowed) { + // this.rotateCount++ + // Matter.Body.setAngle(this, this.rotateCount * this.rotateVelocity) + // Matter.Body.setAngularVelocity(this, 0) + // Matter + // } + // } + // if (this.cycle < 240) { //damage scales up over 2 seconds to give player time to move + // const scale = this.cycle / 240 + // const dmg = (this.cycle < 120) ? 0 : 0.14 * simulation.dmgScale * scale + // ctx.beginPath(); + // this.lasers(this.vertices[0], this.angle + Math.PI / 6, dmg); + // this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6, dmg); + // this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6, dmg); + // this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6, dmg); + // this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6, dmg); + // this.lasers(this.vertices[5], this.angle + 11 * Math.PI / 6, dmg); + // ctx.strokeStyle = "#50f"; + // ctx.lineWidth = 1.5 * scale; + // ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + // ctx.stroke(); // Draw it + // ctx.setLineDash([]); + // ctx.lineWidth = 20; + // ctx.strokeStyle = `rgba(80,0,255,${0.07 * scale})`; + // ctx.stroke(); // Draw it + // } else { + // ctx.beginPath(); + // this.lasers(this.vertices[0], this.angle + Math.PI / 6); + // this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6); + // this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6); + // this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6); + // this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6); + // this.lasers(this.vertices[5], this.angle + 11 * Math.PI / 6); + // ctx.strokeStyle = "#50f"; + // ctx.lineWidth = 1.5; + // ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + // ctx.stroke(); // Draw it + // ctx.setLineDash([]); + // ctx.lineWidth = 20; + // ctx.strokeStyle = "rgba(80,0,255,0.07)"; + // ctx.stroke(); // Draw it + // } + // } + // me.onDeath = function() { + // if (!this.hasRunDeathScript) { + // this.hasRunDeathScript = true + // //make a block body to replace this one + // //this body is too big to leave behind in the normal way mobs.replace() + // const len = body.length; + // const v = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //might help with vertex collision issue, not sure + // body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); + // Matter.Body.setVelocity(body[len], { x: 0, y: -3 }); + // Matter.Body.setAngularVelocity(body[len], this.angularVelocity); + // body[len].collisionFilter.category = cat.body; + // body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + // body[len].classType = "body"; + // Composite.add(engine.world, body[len]); //add to world + // const expand = function(that, massLimit) { + // const scale = 1.05; + // Matter.Body.scale(that, scale, scale); + // if (that.mass < massLimit) setTimeout(expand, 20, that, massLimit); + // }; + // expand(body[len], 200) + + // function unlockExit() { + // if (simulation.isHorizontalFlipped) { + // level.exit.x = -5500 - 100; + // } else { + // level.exit.x = 5500; + // } + // level.exit.y = -330; + // Matter.Composite.remove(engine.world, map[map.length - 1]); + // map.splice(map.length - 1, 1); + // simulation.draw.setPaths(); //redraw map draw path + // // level.levels.push("null") + // } + + // //add lore level as next level if player took lore tech earlier in the game + // if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) { + // simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); + // setTimeout(function() { + // simulation.makeTextLog(`level.levels.push("null")`, 720); + // unlockExit() + // level.levels.push("null") + // }, 4000); + // //remove block map element so exit is clear + // } else { //reset game + // let count = 0 + + // function loop() { + // if (!simulation.paused && !simulation.onTitlePage) { + // count++ + // if (count < 660) { + // if (count === 1) simulation.makeTextLog(`//enter testing mode to set level.levels.length to Infinite`); + // if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`); + // } else if (count === 660) { + // simulation.makeTextLog(`simulation.analysis = 1 //analysis complete`); + // } else if (count === 780) { + // simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`) + // } else if (count === 1020) { + // simulation.makeTextLog(`Engine.clear(engine) //simulation successful`); + // } else if (count === 1260) { + // // tech.isImmortal = false; + // // m.death() + // // m.alive = false; + // // simulation.paused = true; + // // m.health = 0; + // // m.displayHealth(); + // document.getElementById("health").style.display = "none" + // document.getElementById("health-bg").style.display = "none" + // document.getElementById("text-log").style.opacity = 0; //fade out any active text logs + // document.getElementById("fade-out").style.opacity = 1; //slowly fades out + // // build.shareURL(false) + // setTimeout(function() { + // if (!simulation.onTitlePage) { + // simulation.paused = true; + // // simulation.clearMap(); + // // Matter.Composite.clear(composite, keepStatic, [deep = false]) + // // Composite.clear(engine.composite); + // engine.world.bodies.forEach((body) => { Matter.Composite.remove(engine.world, body) }) + // Engine.clear(engine); + // simulation.splashReturn(); + // } + // }, 6000); + // return + // } + // } + // if (simulation.testing) { + // unlockExit() + // setTimeout(function() { + // simulation.makeTextLog(`level.levels.length = Infinite`); + // }, 1500); + // } else { + // if (!simulation.onTitlePage) requestAnimationFrame(loop); + // } + // } + // requestAnimationFrame(loop); + // } + // // for (let i = 0; i < 3; i++) + // level.difficultyIncrease(simulation.difficultyMode) //ramp up damage + // //remove power Ups, to avoid spamming console + // function removeAll(array) { + // for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); + // } + // removeAll(powerUp); + // powerUp = []; + + // //pull in particles + // for (let i = 0, len = body.length; i < len; ++i) { + // const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, body[i].position)), 65) + // const pushUp = Vector.add(velocity, { x: 0, y: -0.5 }) + // Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp)); + // } + // //damage all mobs + // for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i] !== this) { + // if (mob[i].isInvulnerable) { //disable invulnerability + // mob[i].isInvulnerable = false + // mob[i].damageReduction = 1 + // } + // mob[i].damage(Infinity, true); + // } + // } + // } + + // //draw stuff + // for (let i = 0, len = 22; i < len; i++) { + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: (i + 1) * 150, + // color: `rgba(255,255,255,0.17)`, + // time: 5 * (len - i + 1) + // }); + // } + // } + // }; + // }, + zombie(x, y, radius, sides, color) { //mob that attacks other mobs + mobs.spawn(x, y, sides, radius, color); + let me = mob[mob.length - 1]; + me.damageReduction = 0 //take NO damage, but also slowly lose health + Matter.Body.setDensity(me, 0.0001) // normal density is 0.001 // this reduces life by half and decreases knockback + me.isZombie = true + me.isBadTarget = true; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.stroke = "#83a" + me.accelMag = 0.003 + me.frictionAir = 0.005 + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob + me.seeAtDistance2 = 1000000 //1000 vision range + // me.onDeath = function() { + // const amount = Math.min(10, Math.ceil(this.mass * 0.5)) + // if (tech.isSporeFlea) { + // const len = amount / 2 + // for (let i = 0; i < len; i++) { + // const speed = 10 + 5 * Math.random() + // const angle = 2 * Math.PI * Math.random() + // b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + // } + // } else if (tech.isSporeWorm) { + // const len = amount / 2 + // for (let i = 0; i < len; i++) b.worm(this.position) + // } else { + // for (let i = 0; i < amount; i++) b.spore(this.position) + // } + // } + me.do = function () { + this.zombieHealthBar(); + this.lookForMobTargets(); + this.attack(); + this.checkStatus(); + }; + me.mobSearchIndex = 0; + me.target = null + me.lookForMobTargets = function () { + if (this.target === null && mob.length > 1 && !(simulation.cycle % this.seePlayerFreq)) { //find mob targets + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isZombie && + !mob[i].isUnblockable && + !mob[i].isMobBullet && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + // !mob[i].isBadTarget && + // !mob[i].isInvulnerable && + // (Vector.magnitudeSquared(Vector.sub(this.position, mob[this.mobSearchIndex].position)) < this.seeAtDistance2) + ) { + const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)); + if (DIST < closeDist) { + closeDist = DIST; + this.target = mob[i] + } + } + } + } else if ( + !(simulation.cycle % this.memory) && + this.target && + (!this.target.alive || Matter.Query.ray(map, this.position, this.target.position).length !== 0) + ) { + this.target = null //chance to forget target + } + } + me.zombieHealthBar = function () { + this.health -= 0.0003 //decay + if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death(); + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(136, 51, 170,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + } + me.hitCD = 0 + me.attack = function () { //hit non zombie mobs + if (this.hitCD < simulation.cycle) { + if (this.target) { + this.force = Vector.mult(Vector.normalise(Vector.sub(this.target.position, this.position)), this.accelMag * this.mass) + } else { //wonder around + this.torque += 0.0000003 * this.inertia; + const mag = 0.0003 * this.mass + this.force.x += mag * Math.cos(this.angle) + this.force.y += mag * Math.sin(this.angle) + } + if (this.speed > 15) { // speed cap instead of friction to give more agility + Matter.Body.setVelocity(this, { x: this.velocity.x * 0.96, y: this.velocity.y * 0.96 }); + } else if (this.speed < 10) { + Matter.Body.setVelocity(this, { x: this.velocity.x * 0.98, y: this.velocity.y * 0.98 }); + } else if (this.speed < 8) { + Matter.Body.setVelocity(this, { x: this.velocity.x * 0.99, y: this.velocity.y * 0.99 }); + } + const hit = (who) => { + if (!who.isZombie && who.damageReduction) { + this.hitCD = simulation.cycle + 15 + //knock back away from recently hit mob + const force = Vector.mult(Vector.normalise(Vector.sub(who.position, this.position)), 0.03 * this.mass) + this.force.x -= force.x; + this.force.y -= force.y; + this.target = null //look for a new target + + // who.damage(dmg); + //by pass normal damage method + const dmg = 1.5 * who.damageReduction / Math.sqrt(who.mass) + who.health -= dmg + who.onDamage(dmg); //custom damage effects + who.locatePlayer(); + if ((who.health < 0.01 || isNaN(who.health)) && who.alive) who.death(); + + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: Math.log(dmg + 1.1) * 40 * who.damageReduction + 3, + color: simulation.playerDmgColor, + time: simulation.drawTime + }); + } + } + const collide = Matter.Query.collides(this, mob) //damage mob targets if nearby + if (collide.length > 1) { //don't count self collide + for (let i = 0, len = collide.length; i < len; i++) { + hit(collide[i].bodyA) + hit(collide[i].bodyB) + } + } + } + } + }, + starter(x, y, radius = Math.floor(15 + 20 * Math.random())) { //easy mob for on level 1 + mobs.spawn(x, y, 8, radius, "#9ccdc6"); + let me = mob[mob.length - 1]; + // console.log(`mass=${me.mass}, radius = ${radius}`) + me.accelMag = 0.0002 + me.repulsionRange = 400000 + radius * radius; //squared + // me.memory = 120; + me.seeAtDistance2 = 2000000 //1400 vision range + Matter.Body.setDensity(me, 0.0005) // normal density is 0.001 // this reduces life by half and decreases knockback + me.do = function () { + this.seePlayerByLookingAt(); + this.attraction(); + this.repulsion(); + this.checkStatus(); + }; + }, + blockGroup(x, y, num = 3 + Math.random() * 8) { + for (let i = 0; i < num; i++) { + const radius = 25 + Math.floor(Math.random() * 20) + spawn.blockGroupMob(x + Math.random() * radius, y + Math.random() * radius, radius); + } + }, + blockGroupMob(x, y, radius = 25 + Math.floor(Math.random() * 20)) { + mobs.spawn(x, y, 4, radius, "#999"); + let me = mob[mob.length - 1]; + me.g = 0.00015; //required if using this.gravity + me.accelMag = 0.0008 * simulation.accelScale; + me.groupingRangeMax = 250000 + Math.random() * 100000; + me.groupingRangeMin = (radius * 8) * (radius * 8); + me.groupingStrength = 0.0005 + me.memory = 200; + me.isGrouper = true; + me.seeAtDistance2 = 600 * 600 + me.seePlayerFreq = Math.floor(50 + 50 * Math.random()) + me.do = function () { + this.gravity(); + this.checkStatus(); + this.seePlayerCheck(); + if (this.seePlayer.recall) { + this.attraction(); + //tether to other blocks + ctx.beginPath(); + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isGrouper && mob[i] != this && mob[i].isDropPowerUp) { //don't tether to self, bullets, shields, ... + const distance2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) + if (distance2 < this.groupingRangeMax) { + if (!mob[i].seePlayer.recall) mob[i].seePlayerCheck(); //wake up sleepy mobs + if (distance2 > this.groupingRangeMin) { + const angle = Math.atan2(mob[i].position.y - this.position.y, mob[i].position.x - this.position.x); + const forceMag = this.groupingStrength * mob[i].mass; + mob[i].force.x -= forceMag * Math.cos(angle); + mob[i].force.y -= forceMag * Math.sin(angle); + } + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(mob[i].position.x, mob[i].position.y); + } + } + } + ctx.strokeStyle = "#0ff"; + ctx.lineWidth = 1; + ctx.stroke(); + } + } + }, + blockBoss(x, y, radius = 60) { + const activeBeams = []; // used to draw beams when converting + const beamTotalDuration = 60 + mobs.spawn(x, y, 4, radius, "#999"); //#54291d + const me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.002); //normal density even though its a boss + me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //extra reduction for a boss, because normal density + me.frictionAir = 0.01; + me.accelMag = 0.0002; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y); + for (const who of mob) { + if (who.isNecroMob) { //blockMobs leave their body, and die + who.leaveBody = true + who.damage(Infinity) + } + } + } + me.target = player; // the target to lock on. Usually a block, but will be the player under certain conditions + me.do = function () { + this.checkStatus(); + this.seePlayerCheck(); + if (this.target) { //(this.target === player && this.seePlayer.yes) || this.target !== player + const force = Vector.mult(Vector.normalise(Vector.sub(this.target.position, this.position)), this.accelMag * this.mass) + this.force.x += force.x; + this.force.y += force.y; + } + + if (!(simulation.cycle % 30)) { + //find blocks to turn into mobs + for (let i = 0; i < body.length; i++) { + if (Vector.magnitude(Vector.sub(this.position, body[i].position)) < 700 && !body[i].isNotHoldable) { // check distance for each block + Matter.Composite.remove(engine.world, body[i]); + this.target = null //player; + spawn.blockMob(body[i].position.x, body[i].position.y, body[i], 0); + body.splice(i, 1); + activeBeams.push([beamTotalDuration, mob[mob.length - 1]]); + } + } + + // generally, the boss will tend to stay in the player's area but focus on blocks. + if (this.distanceToPlayer() > 1500 && this.target === null) { + this.target = player; // too far, attract to the player + } else { + if (body.length) { // look for a new target by finding the closest block + let min = Infinity; + let closestBlock = null; + for (const block of body) { + const dist = Vector.magnitudeSquared(Vector.sub(this.position, block.position)) + if (dist < min && Matter.Query.ray(map, this.position, block.position).length === 0) { + min = dist; + closestBlock = block; + } + } + this.target = closestBlock; + } + } + + //randomly spawn new mobs from nothing + if (!(simulation.cycle % 90)) { + let count = 0 + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isNecroMob) count++ + } + if (count < 20 * Math.random() * Math.random()) { //limit number of spawns if there are already too many blockMobs + const unit = Vector.normalise(Vector.sub(player.position, this.position)) + for (let i = 0, len = 3 * Math.random(); i < len; i++) { + this.damageReduction += 0.001; //0.05 is starting value + const scale = 0.99; //if 120 use 1.02 + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + + const where = Vector.add(Vector.mult(unit, radius + 200 * Math.random()), this.position) + spawn.blockMob(where.x + 100 * (Math.random() - 0.5), where.y + 100 * (Math.random() - 0.5), null); + this.torque += 0.000035 * this.inertia; //spin after spawning + activeBeams.push([beamTotalDuration, mob[mob.length - 1]]); + } + } + } + + } + for (let i = 0; i < activeBeams.length; i++) { // draw beams on new mobs + const [duration, newBlockMob] = activeBeams[i]; + if (duration === 0) { + activeBeams.splice(i, 1); + continue; + } + if (newBlockMob.alive) { + const vertexIndex = Math.floor((newBlockMob.vertices.length - 1) * duration / beamTotalDuration) + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(newBlockMob.vertices[vertexIndex].x, newBlockMob.vertices[vertexIndex].y); + + //outline mob + ctx.moveTo(newBlockMob.vertices[0].x, newBlockMob.vertices[0].y); + for (let j = 1; j < newBlockMob.vertices.length; j++) { + ctx.lineTo(newBlockMob.vertices[j].x, newBlockMob.vertices[j].y); + } + ctx.lineTo(newBlockMob.vertices[0].x, newBlockMob.vertices[0].y); + + ctx.strokeStyle = "#0ff"; + ctx.lineWidth = 3; + ctx.stroke(); + } + activeBeams[i][0]--; // shorten duration + } + } + }, + blockMob(x, y, host, growCycles = 60) { + if (host === null) { + mobs.spawn(x, y, 4, 1.25 + 3.5 * Math.random(), "#999"); + } else { + const sideLength = Vector.magnitude(Vector.sub(host.vertices[0], host.vertices[1])) + Vector.magnitude(Vector.sub(host.vertices[1], host.vertices[2])) / 2 //average of first 2 sides + mobs.spawn(x, y, 4, Math.min(70, sideLength), "#999"); + if (host.bounds.max.x - host.bounds.min.x < 150 && host.bounds.max.y - host.bounds.min.y < 150) { + Matter.Body.setVertices(mob[mob.length - 1], host.vertices) //if not too big match vertices of host exactly + // mob[mob.length - 1].radius = + } + } + const me = mob[mob.length - 1]; + me.damageReduction = 0.5; //only until done growing + me.isNecroMob = true + me.g = 0.00012; //required if using this.gravity + me.accelMag = 0.0003 * Math.sqrt(simulation.accelScale); + me.memory = 120; + me.leaveBody = false; + me.isDropPowerUp = false; + // me.showHealthBar = false; + me.cycle = 0 + me.do = function () { //grow phase only occurs for growCycles + this.checkStatus(); + this.seePlayerCheck(); + this.cycle++ + if (this.cycle > growCycles) { + this.damageReduction = 1.8 //take extra damage + this.do = this.normalDo + } else { + const scale = 1.04; //if 120 use 1.02 + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + me.normalDo = function () { + this.gravity(); + this.checkStatus(); + this.seePlayerCheck(); + this.attraction(); + } + }, + cellBossCulture(x, y, radius = 20, num = 5) { + const cellID = Math.random() + for (let i = 0; i < num; i++) { + spawn.cellBoss(x, y, radius, cellID) + } + }, + cellBoss(x, y, radius = 20, cellID) { + mobs.spawn(x + Math.random(), y + Math.random(), 20, radius * (1 + 1.2 * Math.random()), "rgba(0,80,125,0.3)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent" + me.isBoss = true; + me.isCell = true; + me.cellID = cellID + me.accelMag = 0.000165 * simulation.accelScale; + me.memory = Infinity; + me.leaveBody = false; + me.isVerticesChange = true + me.frictionAir = 0.012 + me.seePlayerFreq = Math.floor(11 + 7 * Math.random()) + me.seeAtDistance2 = 1400000; + me.cellMassMax = 70 + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body | cat.map + Matter.Body.setDensity(me, 0.0001 + 0.00002 * simulation.difficulty) // normal density is 0.001 + me.damageReduction = 0.17 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //me.damageReductionGoal + + const k = 642 //k=r^2/m + me.split = function () { + const scale = 0.45 + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + spawn.cellBoss(this.position.x, this.position.y, this.radius, this.cellID); + mob[mob.length - 1].health = this.health + } + me.onHit = function () { //run this function on hitting player + this.health = 1; + this.split(); + }; + me.onDamage = function (dmg) { + if (Math.random() < 0.34 * dmg * Math.sqrt(this.mass) && this.health > dmg) this.split(); + } + me.do = function () { + this.seePlayerByDistOrLOS(); + this.checkStatus(); + this.attraction(); + + if (this.seePlayer.recall && this.mass < this.cellMassMax) { //grow cell radius + const scale = 1 + 0.0002 * this.cellMassMax / this.mass; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + if (!(simulation.cycle % this.seePlayerFreq)) { //move away from other mobs + const repelRange = 150 + const attractRange = 700 + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isCell && mob[i].id !== this.id) { + const sub = Vector.sub(this.position, mob[i].position) + const dist = Vector.magnitude(sub) + if (dist < repelRange) { + this.force = Vector.mult(Vector.normalise(sub), this.mass * 0.002) + } else if (dist > attractRange) { + this.force = Vector.mult(Vector.normalise(sub), -this.mass * 0.003) + } + } + } + } + }; + me.onDeath = function () { + this.isCell = false; + let count = 0 //count other cells by id + // console.log(this.cellID) + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isCell && mob[i].cellID === this.cellID) count++ + } + if (count < 1) { //only drop a power up if this is the last cell + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + } else { + this.isDropPowerUp = false; + } + } + }, + spawnerBossCulture(x, y, radius = 50, num = 8 + Math.min(20, simulation.difficulty * 0.4)) { + tech.deathSpawnsFromBoss += 0.4 + const spawnID = Math.random() + for (let i = 0; i < num; i++) spawn.spawnerBoss(x, y, radius, spawnID) + }, + spawnerBoss(x, y, radius, spawnID) { + mobs.spawn(x + Math.random(), y + Math.random(), 4, radius, "rgba(255,60,0,0.3)") //); + let me = mob[mob.length - 1]; + me.isBoss = true; + + me.isSpawnBoss = true; + me.spawnID = spawnID + me.accelMag = 0.00022 * simulation.accelScale; + me.memory = Infinity; + me.showHealthBar = false; + me.isVerticesChange = true + me.frictionAir = 0.011 + me.seePlayerFreq = Math.floor(14 + 7 * Math.random()) + me.seeAtDistance2 = 200000 //1400000; + me.stroke = "transparent" + me.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.map //"rgba(255,60,0,0.3)" + // Matter.Body.setDensity(me, 0.0014) // normal density is 0.001 + Matter.Body.setAngularVelocity(me, 0.12 * (Math.random() - 0.5)) + // spawn.shield(me, x, y, 1); + + me.onHit = function () { //run this function on hitting player + this.explode(); + }; + me.damageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.doAwake = function () { + this.alwaysSeePlayer(); + this.checkStatus(); + this.attraction(); + + if (!(simulation.cycle % this.seePlayerFreq)) { //move away from other mobs + const repelRange = 40 + const attractRange = 240 + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isSpawnBoss && mob[i].id !== this.id) { + const sub = Vector.sub(this.position, mob[i].position) + const dist = Vector.magnitude(sub) + if (dist < repelRange) { + this.force = Vector.mult(Vector.normalise(sub), this.mass * 0.002) + } else if (dist > attractRange) { + this.force = Vector.mult(Vector.normalise(sub), -this.mass * 0.002) + } + } + } + } + } + me.do = function () { + this.checkStatus(); + if (this.seePlayer.recall) { + this.do = this.doAwake + //awaken other spawnBosses + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isSpawnBoss && mob[i].spawnID === this.spawnID) mob[i].seePlayer.recall = 1 + } + } + }; + me.onDeath = function () { + this.isSpawnBoss = false; + let count = 0 //count other cells by id + // console.log(this.spawnID) + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isSpawnBoss && mob[i].spawnID === this.spawnID) count++ + } + if (count < 1) { //only drop a power up if this is the last cell + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + tech.deathSpawnsFromBoss -= 0.4 + } else { + this.leaveBody = false; + this.isDropPowerUp = false; + } + + const spawns = tech.deathSpawns + tech.deathSpawnsFromBoss + const len = Math.min(12, spawns * Math.ceil(Math.random() * simulation.difficulty * spawns)) + for (let i = 0; i < len; i++) { + spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + (Math.random() - 0.5) * 10, + y: this.velocity.x + (Math.random() - 0.5) * 10 + }); + } + + } + }, + growBossCulture(x, y, radius = 17, nodes = 12 + Math.min(10, simulation.difficulty * 0.25)) { + const buffID = Math.random() + const sideLength = 200 + 50 * Math.sqrt(nodes) // distance between each node mob + for (let i = 0; i < nodes; ++i) { + const angle = 2 * Math.PI * Math.random() + const mag = Math.max(radius, sideLength * (1 - Math.pow(Math.random(), 1.5))) //working on a distribution that is circular, random, but not too focused in the center + spawn.growBoss(x + mag * Math.cos(angle), y + mag * Math.sin(angle), radius, buffID); + } + spawn.constrain2AdjacentMobs(nodes, 0.0001, false); //loop mobs together + }, + growBoss(x, y, radius, buffID) { + mobs.spawn(x + Math.random(), y + Math.random(), 6, radius, "hsl(144, 15%, 50%)") //); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.isBuffBoss = true; + me.buffID = buffID + me.memory = Infinity; + me.isVerticesChange = true + me.frictionAir = 0.012 + me.seePlayerFreq = Math.floor(11 + 7 * Math.random()) + me.seeAtDistance2 = 200000 //1400000; + me.stroke = "transparent" + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body //| cat.map //"rgba(255,60,0,0.3)" + + me.buffCount = 0 + me.accelMag = 0.00005 //* simulation.accelScale; + me.setBuffed = function () { + this.buffCount++ + this.accelMag += 0.000024 //* Math.sqrt(simulation.accelScale) + this.fill = `hsl(144, ${5 + 10 * this.buffCount}%, 50%)` + const scale = 1.135; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + + // this.isInvulnerable = true + // if (this.damageReduction) this.startingDamageReduction = this.damageReduction + // this.damageReduction = 0 + // this.invulnerabilityCountDown = simulation.difficulty + } + me.onDeath = function () { + this.isBuffBoss = false; + let count = 0 //count other cells by id + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isBuffBoss && mob[i].buffID === this.buffID) { + count++ + mob[i].setBuffed() + } + } + if (count < 1) { //only drop a power up if this is the last cell + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + } else { + this.leaveBody = false; + this.isDropPowerUp = false; + powerUps.spawnRandomPowerUp(this.position.x, this.position.y) // manual power up spawn to avoid spawning too many tech with "symbiosis" + } + } + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + //required setup for invulnerable + // me.isInvulnerable = false + me.invulnerabilityCountDown = 0 + me.do = function () { + // if (this.isInvulnerable) { + // if (this.invulnerabilityCountDown > 0) { + // this.invulnerabilityCountDown-- + // ctx.beginPath(); + // let vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 20; + // ctx.strokeStyle = "rgba(255,255,255,0.7)"; + // ctx.stroke(); + // } else { + // this.isInvulnerable = false + // this.damageReduction = this.startingDamageReduction + // } + // } + this.alwaysSeePlayer(); + this.checkStatus(); + this.attraction(); + // if (!(simulation.cycle % this.seePlayerFreq)) { //move away from other mobs + // const repelRange = 100 + 4 * this.radius + // const attractRange = 240 + // for (let i = 0, len = mob.length; i < len; i++) { + // if (mob[i].isBuffBoss && mob[i].id !== this.id) { + // const sub = Vector.sub(this.position, mob[i].position) + // const dist = Vector.magnitude(sub) + // if (dist < repelRange) { + // this.force = Vector.mult(Vector.normalise(sub), this.mass * 0.002) + // } else if (dist > attractRange) { + // this.force = Vector.mult(Vector.normalise(sub), -this.mass * 0.002) + // } + // } + // } + // } + } + }, + powerUpBossBaby(x, y, vertices = 9, radius = 60) { + mobs.spawn(x, y, vertices, radius, "rgba(225,240,245,0.4)"); //"rgba(120,140,150,0.4)" + let me = mob[mob.length - 1]; + me.isBoss = true; + me.frictionAir = 0.006 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0004 + 0.0003 * simulation.accelScale; + // Matter.Body.setDensity(me, 0.001); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.seePlayerFreq = 17 + me.lockedOn = null; + if (vertices === 9) { + //on primary spawn + powerUps.spawnBossPowerUp(me.position.x, me.position.y) + powerUps.spawn(me.position.x, me.position.y, "heal"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); + } else if (!m.isCloak) { + me.foundPlayer(); + } + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isInvulnerable = true + me.startingDamageReduction = me.damageReduction + me.damageReduction = 0 + me.invulnerabilityCountDown = 40 + simulation.difficulty + me.onHit = function () { //run this function on hitting player + if (powerUps.ejectTech()) { + powerUps.ejectGraphic("150, 138, 255"); + // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); + // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + this.accelMag *= 1.4 + Matter.Body.setDensity(this, this.density * 1.4); //normal is 0.001 + } + }; + me.onDeath = function () { + this.leaveBody = false; + if (vertices > 3) { + this.isDropPowerUp = false; + spawn.powerUpBossBaby(this.position.x, this.position.y, vertices - 1) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x, + y: this.velocity.y + }) + } + for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp + }; + me.do = function () { + if (this.isInvulnerable) { + if (this.invulnerabilityCountDown > 0) { + this.invulnerabilityCountDown-- + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + } + // this.stroke = `hsl(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%)` + // this.fill = `hsla(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%,0.3)` + + //steal all power ups + if (this.alive) { + for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + powerUp[i].collisionFilter.mask = 0 + Matter.Body.setPosition(powerUp[i], this.vertices[i]) + Matter.Body.setVelocity(powerUp[i], { + x: 0, + y: 0 + }) + } + } + this.seePlayerByHistory(50); + this.attraction(); + this.checkStatus(); + }; + }, + powerUpBoss(x, y, vertices = 9, radius = 130) { + mobs.spawn(x, y, vertices, radius, "transparent"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.frictionAir = 0.01 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0002 + 0.0004 * simulation.accelScale; + Matter.Body.setDensity(me, 0.00035); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player //| cat.body + me.memory = Infinity; + me.seePlayerFreq = 30 + me.lockedOn = null; + if (vertices === 9) { + //on primary spawn + powerUps.spawnBossPowerUp(me.position.x, me.position.y) + powerUps.spawn(me.position.x, me.position.y, "heal"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); + } else if (!m.isCloak) { + me.foundPlayer(); + } + + me.damageReduction = 0.16 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.isInvulnerable = true + // me.startingDamageReduction = me.damageReduction + // me.damageReduction = 0 + // me.invulnerabilityCountDown = 60 + simulation.difficulty * 2 + + me.onHit = function () { //run this function on hitting player + if (powerUps.ejectTech()) { + powerUps.ejectGraphic("150, 138, 255"); + // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); + // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + this.accelMag *= 1.4 + Matter.Body.setDensity(this, this.density * 1.4); //normal is 0.001 + } + }; + me.onDeath = function () { + this.leaveBody = false; + if (vertices > 3) { + this.isDropPowerUp = false; + spawn.powerUpBoss(this.position.x, this.position.y, vertices - 1) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x, + y: this.velocity.y + }) + } + for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp + }; + + //steal all power ups + // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + // powerUp[i].collisionFilter.mask = 0 + // Matter.Body.setPosition(powerUp[i], this.vertices[i]) + // Matter.Body.setVelocity(powerUp[i], { + // x: 0, + // y: 0 + // }) + // } + // me.powerUpList = [] + // me.constrainPowerUps = function() { + // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + // //remove other constraints on power up + // for (let i = 0, len = cons.length; i < len; ++i) { + // if (cons[i].bodyB === powerUp[i] || cons[i].bodyA === powerUp[i]) { + // Matter.Composite.remove(engine.world, cons[i]); + // cons.splice(i, 1); + // break; + // } + // } + + // //add to list + // this.powerUpList.push(powerUp[i]) + // //position and stop + // powerUp[i].collisionFilter.mask = 0 + // Matter.Body.setPosition(powerUp[i], this.vertices[i]) + // Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) + // //add constraint + // cons[cons.length] = Constraint.create({ + // pointA: this.vertices[i], + // bodyB: powerUp[i], + // stiffness: 1, + // damping: 1 + // }); + // Composite.add(engine.world, cons[cons.length - 1]); + // } + // for (let i = 0; i < this.powerUpList.length; i++) {} + // } + // me.constrainPowerUps() + me.do = function () { + this.stroke = `hsl(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%)` + // if (this.isInvulnerable) { + // if (this.invulnerabilityCountDown > 0) { + // this.invulnerabilityCountDown-- + // ctx.beginPath(); + // let vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 20; + // ctx.strokeStyle = "rgba(255,255,255,0.7)"; + // ctx.stroke(); + // } else { + // this.isInvulnerable = false + // this.damageReduction = this.startingDamageReduction + // } + // } + //steal all power ups + // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + // powerUp[i].collisionFilter.mask = 0 + // Matter.Body.setPosition(powerUp[i], this.vertices[i]) + // Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) + // } + if (this.alive) { + for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + powerUp[i].collisionFilter.mask = 0 + Matter.Body.setPosition(powerUp[i], this.vertices[i]) + Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) + } + } + + this.seePlayerCheckByDistance(); + this.attraction(); + this.checkStatus(); + }; + }, + + // chaser(x, y, radius = 35 + Math.ceil(Math.random() * 40)) { + // mobs.spawn(x, y, 8, radius, "rgb(255,150,100)"); //"#2c9790" + // let me = mob[mob.length - 1]; + // // Matter.Body.setDensity(me, 0.0007); //extra dense //normal is 0.001 //makes effective life much lower + // me.friction = 0.1; + // me.frictionAir = 0; + // me.accelMag = 0.001 * Math.sqrt(simulation.accelScale); + // me.g = me.accelMag * 0.6; //required if using this.gravity + // me.memory = 180; + // spawn.shield(me, x, y); + // me.do = function() { + // this.gravity(); + // this.seePlayerByHistory(15); + // this.checkStatus(); + // this.attraction(); + // }; + // }, + grower(x, y, radius = 15) { + mobs.spawn(x, y, 7, radius, "hsl(144, 15%, 50%)"); + let me = mob[mob.length - 1]; + me.isVerticesChange = true + me.big = false; //required for grow + me.accelMag = 0.00045 * simulation.accelScale; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs + // me.onDeath = function () { //helps collisions functions work better after vertex have been changed + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + // } + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + this.grow(); + }; + }, + springer(x, y, radius = 20 + Math.ceil(Math.random() * 35)) { + mobs.spawn(x, y, 10, radius, "#b386e8"); + let me = mob[mob.length - 1]; + me.friction = 0; + me.frictionAir = 0.006; + me.lookTorque = 0.0000008; //controls spin while looking for player + me.g = 0.0002; //required if using this.gravity + me.seePlayerFreq = Math.floor((40 + 25 * Math.random())); + const springStiffness = 0.00014; + const springDampening = 0.0005; + + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + + me.springTarget2 = { + x: me.position.x, + y: me.position.y + }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + me.do = function () { + this.gravity(); + this.searchSpring(); + this.checkStatus(); + this.springAttack(); + }; + + me.onDeath = function () { + this.removeCons(); + }; + spawn.shield(me, x, y); + }, + hopper(x, y, radius = 30 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.05; + me.g = 0.0032; //required if using this.gravity + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.delay = 120 * simulation.CDScale; + me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); + me.randomHopCD = simulation.cycle + me.randomHopFrequency; + Matter.Body.rotate(me, Math.random() * Math.PI); + spawn.shield(me, x, y); + me.do = function () { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + if (this.seePlayer.recall) { + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.06 + 0.1) * this.mass; //antigravity + } + } else { + //randomly hob if not aware of player + if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.randomHopCD = simulation.cycle + this.randomHopFrequency; + //slowly change randomHopFrequency after each hop + this.randomHopFrequency = Math.max(100, this.randomHopFrequency + (0.5 - Math.random()) * 200); + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.1 + Math.random() * 0.3); + const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle) - 0.07 * this.mass; //antigravity + } + } + }; + }, + hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) { + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.leaveBody = false; + me.isDropPowerUp = false; + // me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.timeLeft = 1200 + Math.floor(600 * Math.random()); + + me.isRandomMove = Math.random() < 0.3 //most chase player, some don't + me.accelMag = 0.01; //jump height + me.g = 0.0015; //required if using this.gravity + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.delay = 130 + 60 * simulation.CDScale; + // Matter.Body.rotate(me, Math.random() * Math.PI); + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.onHit = function () { + this.explode(this.mass); + }; + me.do = function () { + this.gravity(); + this.checkStatus(); + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + if (this.isRandomMove || Math.random() < 0.2) { + this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move + } else { + this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1); //chase player + } + this.force.y -= (0.04 + 0.04 * Math.random()) * this.mass + } + this.timeLimit(); + }; + }, + hopMomBoss(x, y, radius = 120) { + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.damageReduction = 0.08 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.accelMag = 0.05; //jump height + me.g = 0.003; //required if using this.gravity + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.delay = 120 + 40 * simulation.CDScale; + Matter.Body.rotate(me, Math.random() * Math.PI); + spawn.shield(me, x, y, 1); + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // for (let i = 0, len = 3 + 0.1 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5)) + }; + me.do = function () { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + //spawn hopBullets after each jump + for (let i = 0, len = 1 + 0.05 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5)) + + this.force.x += (0.02 + 0.06 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1); + this.force.y -= (0.08 + 0.08 * Math.random()) * this.mass + } + }; + }, + // hopBoss(x, y, radius = 90) { + // mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // me.g = 0.005; //required if using this.gravity + // me.frictionAir = 0.01; + // me.friction = 1 + // me.frictionStatic = 1 + // me.restitution = 0; + // me.accelMag = 0.07; + // me.delay = 120 * simulation.CDScale; + // me.randomHopFrequency = 200 + // me.randomHopCD = simulation.cycle + me.randomHopFrequency; + // // me.memory = 420; + // me.isInAir = false + // Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + // spawn.shield(me, x, y, 1); + // spawn.spawnOrbitals(me, radius + 60, 1) + // me.onDeath = function() { + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.lastSpeed = me.speed + // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.do = function() { + // // this.armor(); + // this.gravity(); + // this.seePlayerCheck(); + // this.checkStatus(); + // if (this.seePlayer.recall) { + // const deltaSpeed = this.lastSpeed - this.speed + // this.lastSpeed = this.speed + // if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle + // //damage and push player away, push away blocks + // const range = 800 //Math.min(800, 50 * deltaSpeed) + // for (let i = body.length - 1; i > -1; i--) { + // if (!body[i].isNotHoldable) { + // sub = Vector.sub(body[i].position, this.position); + // dist = Vector.magnitude(sub); + // if (dist < range) { + // knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist)); + // body[i].force.x += knock.x; + // body[i].force.y += knock.y; + // } + // } + // } + // simulation.drawList.push({ //draw radius + // x: this.position.x, + // y: this.position.y, + // radius: range, + // color: "rgba(0,200,180,0.6)", + // time: 4 + // }); + // } + // if (this.isInAir) { + // if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body + // this.isInAir = false //landing + // this.cd = simulation.cycle + this.delay + // } + // } else { //on ground + // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump + // this.isInAir = true + // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x += forceMag * Math.cos(angle); + // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + // } + // } + // // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + // // this.cd = simulation.cycle + this.delay; + // // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + // // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // // this.force.x += forceMag * Math.cos(angle); + // // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + // // } + // } else { + // //randomly hob if not aware of player + // if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + // this.randomHopCD = simulation.cycle + this.randomHopFrequency; + // //slowly change randomHopFrequency after each hop + // this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random())); + // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2); + // const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; + // this.force.x += forceMag * Math.cos(angle); + // this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity + // } + // } + // }; + // }, + spinner(x, y, radius = 30 + Math.ceil(Math.random() * 35)) { + mobs.spawn(x, y, 5, radius, "#000000"); + let me = mob[mob.length - 1]; + me.fill = "#28b"; + me.rememberFill = me.fill; + me.cd = 0; + me.burstDir = { + x: 0, + y: 0 + }; + me.frictionAir = 0.022; + me.lookTorque = 0.0000014; + me.restitution = 0; + spawn.shield(me, x, y); + me.look = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + if (this.seePlayer.recall && this.cd < simulation.cycle) { + this.burstDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + this.cd = simulation.cycle + 40; + this.do = this.spin + } + } + me.do = me.look + me.spin = function () { + this.checkStatus(); + this.torque += 0.000035 * this.inertia; + //draw attack vector + const mag = this.radius * 2.5 + 50; + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + ctx.lineWidth = 3; + ctx.setLineDash([10, 20]); //30 + const dir = Vector.add(this.position, Vector.mult(this.burstDir, mag)); + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(dir.x, dir.y); + ctx.stroke(); + ctx.setLineDash([]); + if (this.cd < simulation.cycle) { + this.fill = this.rememberFill; + this.cd = simulation.cycle + 180 * simulation.CDScale + this.do = this.look + this.force = Vector.mult(this.burstDir, this.mass * 0.25); + } + } + }, + sucker(x, y, radius = 30 + Math.ceil(Math.random() * 25)) { + radius = 9 + radius / 8; //extra small + mobs.spawn(x, y, 6, radius, "transparent"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; //used for drawSneaker + me.eventHorizon = radius * 30; //required for blackhole + me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon + me.accelMag = 0.00012 * simulation.accelScale; + me.frictionAir = 0.025; + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + me.memory = Infinity; + Matter.Body.setDensity(me, 0.015); //extra dense //normal is 0.001 //makes effective life much larger + me.do = function () { + //keep it slow, to stop issues from explosion knock backs + if (this.speed > 5) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.99, + y: this.velocity.y * 0.99 + }); + } + this.seePlayerCheckByDistance() + this.checkStatus(); + //accelerate towards the player + if (this.seePlayer.recall) { + const forceMag = this.accelMag * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle); + } + //eventHorizon waves in and out + const eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(simulation.cycle * 0.011)) + //draw darkness + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.25, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.9)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.55, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.5)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fill(); + + //when player is inside event horizon + if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.005 + if (m.energy < 0.1) m.damage(0.0001 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x -= 0.00125 * player.mass * Math.cos(angle) * (m.onGround ? 1.8 : 1); + player.force.y -= 0.0001 * player.mass * Math.sin(angle); + //draw line to player + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fill(); + } + } + }, + // timeBoss(x, y, radius = 25) { + // mobs.spawn(x, y, 12, radius, "#000"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // me.stroke = "transparent"; //used for drawSneaker + // me.eventHorizon = 1100; //required for black hole + // me.eventHorizonGoal = 1100; //required for black hole + // me.seeAtDistance2 = (me.eventHorizon + 1200) * (me.eventHorizon + 1200); //vision limit is event horizon + // me.accelMag = 0.00006 * simulation.accelScale; + // // me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + // // me.frictionAir = 0.005; + // me.memory = Infinity; + // Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + // me.onDeath = function() { + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.damageReduction = 0.23 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.do = function() { + // //keep it slow, to stop issues from explosion knock backs + // if (!(simulation.cycle % this.seePlayerFreq)) { + // if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !m.isCloak ignore cloak for black holes + // this.locatePlayer(); + // if (!this.seePlayer.yes) this.seePlayer.yes = true; + // } else if (this.seePlayer.recall) { + // this.lostPlayer(); + // } + // } + // this.checkStatus(); + // if (this.seePlayer.recall) { + // //accelerate towards the player + // const forceMag = this.accelMag * this.mass; + // const dx = this.seePlayer.position.x - this.position.x + // const dy = this.seePlayer.position.y - this.position.y + // const mag = Math.sqrt(dx * dx + dy * dy) + // this.force.x += forceMag * dx / mag; + // this.force.y += forceMag * dy / mag; + + // //eventHorizon waves in and out + // const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) + // // zoom camera in and out with the event horizon + + // //draw darkness + // if (!simulation.isTimeSkipping) { + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.05)"; + // ctx.fill(); + // //when player is inside event horizon + // if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { + // // if (!(simulation.cycle % 10)) simulation.timePlayerSkip(5) + // // if (!(simulation.cycle % 2)) simulation.timePlayerSkip(1) + // simulation.timePlayerSkip(2) + + // // if (m.immuneCycle < m.cycle) { + // // if (m.energy > 0) m.energy -= 0.004 + // // if (m.energy < 0.1) m.damage(0.00017 * simulation.dmgScale); + // // } + // } + // } + // } + // } + // }, + suckerBoss(x, y, radius = 25) { + mobs.spawn(x, y, 12, radius, "#000"); + let me = mob[mob.length - 1]; + me.isBoss = true; + + me.stroke = "transparent"; //used for drawSneaker + me.eventHorizon = 1100; //required for black hole + me.seeAtDistance2 = (me.eventHorizon + 3000) * (me.eventHorizon + 3000); //vision limit is event horizon + me.accelMag = 0.00004 * simulation.accelScale; + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + // me.frictionAir = 0.005; + me.memory = 1600; + Matter.Body.setDensity(me, 0.06); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + //applying forces to player doesn't seem to work inside this method, not sure why + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + if (simulation.difficulty > 5) { + //teleport everything to center + function toMe(who, where, range) { + for (let i = 0, len = who.length; i < len; i++) { + if (!who[i].isNotHoldable) { + const SUB = Vector.sub(who[i].position, where) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range) { + Matter.Body.setPosition(who[i], where) + } + } + } + } + toMe(body, this.position, this.eventHorizon) + toMe(mob, this.position, this.eventHorizon) + // toMe(bullet, this.position, this.eventHorizon)) + } + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + //keep it slow, to stop issues from explosion knock backs + if (this.speed > 1) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } + if (!(simulation.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !m.isCloak ignore cloak for black holes + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + this.checkStatus(); + if (this.seePlayer.recall) { + //throw large seekers + if (!(simulation.cycle % 90)) { + spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7); //give the bullet a rotational velocity as if they were attached to a vertex + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00001); //normal is 0.001 + who.timeLeft = 600 + who.accelMag = 0.0002 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0. + const velocity = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), -20); //set direction to turn to fire //Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -35) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + //accelerate towards the player + const forceMag = this.accelMag * this.mass; + const dx = this.seePlayer.position.x - this.position.x + const dy = this.seePlayer.position.y - this.position.y + const mag = Math.sqrt(dx * dx + dy * dy) + this.force.x += forceMag * dx / mag; + this.force.y += forceMag * dy / mag; + //eventHorizon waves in and out + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) // zoom camera in and out with the event horizon + //draw darkness + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.6)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.4)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.3)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.2)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.05)"; + ctx.fill(); + //when player is inside event horizon + if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.008 + if (m.energy < 0.1) m.damage(0.00015 * simulation.dmgScale); + } + const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); + player.force.x -= 0.0013 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + player.force.y -= 0.0013 * Math.sin(angle) * player.mass; + //draw line to player + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, this.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fill(); + } + this.curl(eventHorizon); + //attract other power ups + for (let i = 0; i < powerUp.length; i++) { //attract heal power ups + const sub = Vector.sub(this.position, powerUp[i].position) + const mag = 0.0015 * Math.min(1, (Vector.magnitude(sub) - 200) / this.eventHorizon) + const attract = Vector.mult(Vector.normalise(sub), mag * powerUp[i].mass) + powerUp[i].force.x += attract.x; + powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity + // Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7)); + } + } + } + }, + spiderBoss(x, y, radius = 60 + Math.ceil(Math.random() * 10)) { + let targets = [] //track who is in the node boss, for shields + mobs.spawn(x, y, 6, radius, "#b386e8"); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 0.003); //extra dense //normal is 0.001 //makes effective life much larger and damage on collision + me.isBoss = true; + me.damageReduction = 0.13 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 + + targets.push(me.id) //add to shield protection + me.friction = 0; + me.frictionAir = 0.0067; + me.lookTorque = 0.0000008; //controls spin while looking for player + me.g = 0.0002; //required if using this.gravity + me.seePlayerFreq = Math.floor((30 + 20 * Math.random())); + const springStiffness = 0.00014; + const springDampening = 0.0005; + + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + + me.springTarget2 = { + x: me.position.x, + y: me.position.y + }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening, + length: 0 + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + me.do = function () { + // this.armor(); + this.gravity(); + this.searchSpring(); + this.checkStatus(); + this.springAttack(); + }; + + me.onDeath = function () { + this.removeCons(); + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + + radius = 22 // radius of each node mob + const sideLength = 100 // distance between each node mob + const nodes = 6 + const angle = 2 * Math.PI / nodes + + spawn.allowShields = false; //don't want shields on individual mobs + + for (let i = 0; i < nodes; ++i) { + spawn.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); + Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger + mob[mob.length - 1].damageReduction = 0.12 + targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields + } + + const attachmentStiffness = 0.02 + spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together + + for (let i = 0; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 1], + stiffness: attachmentStiffness, + damping: 0.03 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + //spawn shield around all nodes + spawn.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); + spawn.allowShields = true; + }, + mantisBoss(x, y, radius = 35, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 5, radius, "#6ba"); + let me = mob[mob.length - 1]; + me.babyList = [] //list of mobs that are apart of this boss + Matter.Body.setDensity(me, 0.0015); //extra dense //normal is 0.001 //makes effective life much larger and damage on collision + me.damageReduction = 0.13 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 + me.isBoss = true; + + me.friction = 0; + me.frictionAir = 0.006; + me.g = 0.0002; //required if using this.gravity + me.seePlayerFreq = 31; + const springStiffness = 0.00003; //simulation.difficulty + const springDampening = 0.0002; + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + me.springTarget2 = { x: me.position.x, y: me.position.y }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening, + length: 0 + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.invulnerabilityCountDown = 0 + me.do = function () { + this.checkStatus(); + this.gravity(); + //draw the two dots on the end of the springs + ctx.beginPath(); + ctx.arc(this.cons.pointA.x, this.cons.pointA.y, 6, 0, 2 * Math.PI); + ctx.arc(this.cons2.pointA.x, this.cons2.pointA.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#222"; + ctx.fill(); + // this.seePlayerCheck() + this.seePlayerByHistory() + this.invulnerabilityCountDown-- + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + let vertices = this.babyList[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + } + } + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + if (this.invulnerabilityCountDown < 0) { + this.invulnerabilityCountDown = 110 + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) this.babyList[i].damageReduction = this.startingDamageReduction + } + } + } else if (this.invulnerabilityCountDown < 0) { + this.invulnerabilityCountDown = 120 + 9 * simulation.difficulty + this.isInvulnerable = true + if (this.damageReduction) this.startingDamageReduction = this.damageReduction + this.damageReduction = 0 + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + this.babyList[i].isInvulnerable = true + this.babyList[i].damageReduction = 0 + } + } + } + // set new values of the ends of the spring constraints + const stepRange = 700 + if (this.seePlayer.recall && Matter.Query.ray(map, this.position, this.seePlayer.position).length === 0) { + if (!(simulation.cycle % (this.seePlayerFreq * 2))) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget.x = goal.x; + this.springTarget.y = goal.y; + this.cons.length = -200; + this.cons2.length = 100 + 1.5 * this.radius; + } else if (!(simulation.cycle % this.seePlayerFreq)) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget2.x = goal.x; + this.springTarget2.y = goal.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = -200; + } + } else { + this.torque = this.lookTorque * this.inertia; + //spring to random place on map + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //move to a random location + if (!(simulation.cycle % (this.seePlayerFreq))) { + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const seeRange = 3000; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + if (best.dist2 != Infinity) { + this.springTarget.x = best.x; + this.springTarget.y = best.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = 100 + 1.5 * this.radius; + } + } + } + }; + me.onDeath = function () { + this.removeCons(); + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + this.babyList[i].collisionFilter.mask = cat.map | cat.bullet | cat.player + this.babyList[i].isInvulnerable = false + this.babyList[i].damageReduction = this.startingDamageReduction + this.babyList[i].collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body + } + } + }; + const sideLength = 80 // distance between each node mob + const nodes = 3 + const angle = 2 * Math.PI / nodes + spawn.allowShields = false; //don't want shields on individual mobs, it messes with the constraints + for (let i = 0; i < nodes; ++i) { + spawn.striker(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), 20, 12); + const babyMob = mob[mob.length - 1] + me.babyList.push(babyMob) + babyMob.fill = "rgb(68, 102, 119)" + babyMob.isBoss = true; + // Matter.Body.setDensity(babyMob, 0.001); //extra dense //normal is 0.001 //makes effective life much larger and increases damage + babyMob.damageReduction = this.startingDamageReduction * 0.8 + babyMob.collisionFilter.mask = cat.bullet | cat.player //can't touch other mobs //cat.map | cat.body | + babyMob.delay = 60 + 55 * simulation.CDScale + Math.floor(Math.random() * 20); + babyMob.strikeRange = 400 + babyMob.onHit = function () { + this.cd = simulation.cycle + this.delay; + //dislodge ammo + if (b.inventory.length) { + let isRemovedAmmo = false + const numRemoved = 3 + for (let j = 0; j < numRemoved; j++) { + for (let i = 0; i < b.inventory.length; i++) { + const gun = b.guns[b.inventory[i]] + if (gun.ammo > 0 && gun.ammo !== Infinity) { + gun.ammo -= Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * gun.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + if (gun.ammo < 0) gun.ammo = 0 + isRemovedAmmo = true + } + } + } + if (isRemovedAmmo) { + simulation.updateGunHUD(); + for (let j = 0; j < numRemoved; j++) powerUps.directSpawn(this.position.x + 10 * Math.random(), this.position.y + 10 * Math.random(), "ammo"); + powerUps.ejectGraphic(); + } + } + }; + } + const stiffness = 0.01 + const damping = 0.1 + for (let i = 1; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - i], + bodyB: mob[mob.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - 1], + bodyB: mob[mob.length - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + + for (let i = 0; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + spawn.allowShields = true; + }, + // timeSkipBoss(x, y, radius = 55) { + // mobs.spawn(x, y, 6, radius, '#000'); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // // me.stroke = "transparent"; //used for drawSneaker + // me.timeSkipLastCycle = 0 + // me.eventHorizon = 1800; //required for black hole + // me.seeAtDistance2 = (me.eventHorizon + 2000) * (me.eventHorizon + 2000); //vision limit is event horizon + 2000 + // me.accelMag = 0.0004 * simulation.accelScale; + // // me.frictionAir = 0.005; + // // me.memory = 1600; + // // Matter.Body.setDensity(me, 0.02); //extra dense //normal is 0.001 //makes effective life much larger + // Matter.Body.setDensity(me, 0.0005 + 0.00018 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + // spawn.shield(me, x, y, 1); + // me.onDeath = function() { + // //applying forces to player doesn't seem to work inside this method, not sure why + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.do = function() { + // //keep it slow, to stop issues from explosion knock backs + // if (this.speed > 8) { + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * 0.99, + // y: this.velocity.y * 0.99 + // }); + // } + // this.seePlayerCheck(); + // this.checkStatus(); + // this.attraction() + // if (!simulation.isTimeSkipping) { + // const compress = 1 + // if (this.timeSkipLastCycle < simulation.cycle - compress && + // Vector.magnitude(Vector.sub(this.position, player.position)) < this.eventHorizon) { + // this.timeSkipLastCycle = simulation.cycle + // simulation.timeSkip(compress) + + // this.fill = `rgba(0,0,0,${0.4+0.6*Math.random()})` + // this.stroke = "#014" + // this.isShielded = false; + // this.isDropPowerUp = true; + // this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can't touch bullets + + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = "#fff"; + // ctx.globalCompositeOperation = "destination-in"; //in or atop + // ctx.fill(); + // ctx.globalCompositeOperation = "source-over"; + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.clip(); + + // // ctx.beginPath(); + // // ctx.arc(this.position.x, this.position.y, 9999, 0, 2 * Math.PI); + // // ctx.fillStyle = "#000"; + // // ctx.fill(); + // // ctx.strokeStyle = "#000"; + // // ctx.stroke(); + + // // ctx.beginPath(); + // // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // // ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; + // // ctx.fill(); + // // ctx.strokeStyle = "#000"; + // // ctx.stroke(); + // } else { + // this.isShielded = true; + // this.isDropPowerUp = false; + // this.seePlayer.recall = false + // this.fill = "transparent" + // this.stroke = "transparent" + // this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob; //can't touch bullets + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; + // ctx.fill(); + // } + // } + // } + // }, + beamer(x, y, radius = 15 + Math.ceil(Math.random() * 15)) { + mobs.spawn(x, y, 4, radius, "rgb(255,0,190)"); + let me = mob[mob.length - 1]; + me.repulsionRange = 73000; //squared + me.laserRange = 370; + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + spawn.shield(me, x, y); + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + this.repulsion(); + this.harmZone(); + }; + }, + historyBoss(x, y, radius = 30) { + if (tech.dynamoBotCount > 0) { + spawn.randomLevelBoss(x, y, spawn.nonCollideBossList) + return + } + mobs.spawn(x, y, 0, radius, "transparent"); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 0.21); //extra dense //normal is 0.001 + me.laserRange = 350; + me.seeAtDistance2 = 2000000; + me.isBoss = true; + me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) // me.damageReductionGoal + + me.showHealthBar = false; //drawn in this.awake + me.delayLimit = 60 + Math.floor(30 * Math.random()); + me.followDelay = 600 - Math.floor(90 * Math.random()) + me.stroke = "transparent"; //used for drawGhost + me.collisionFilter.mask = cat.bullet | cat.body + me.memory = Infinity + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + ctx.setTransform(1, 0, 0, 1, 0, 0); //reset warp effect + ctx.setLineDash([]) //reset stroke dash effect + }) + }) + }; + me.warpIntensity = 0 + me.awake = function () { + this.checkStatus(); + //health bar needs to be here because the position is being set + const h = this.radius * 0.3; + const w = this.radius * 2; + const x = this.position.x - w / 2; + const y = this.position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(150,0,255,0.7)"; + ctx.fillRect(x, y, w * this.health, h); + + //draw eye + const unit = Vector.normalise(Vector.sub(m.pos, this.position)) + const eye = Vector.add(Vector.mult(unit, 15), this.position) + ctx.beginPath(); + ctx.arc(eye.x, eye.y, 4, 0, 2 * Math.PI); + ctx.moveTo(this.position.x + 20 * unit.x, this.position.y + 20 * unit.y); + ctx.lineTo(this.position.x + 30 * unit.x, this.position.y + 30 * unit.y); + ctx.strokeStyle = this.stroke; + ctx.lineWidth = 2; + ctx.stroke(); + + ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); //the dashed effect is not set back to normal, because it looks neat for how the player is drawn + // ctx.lineDashOffset = 6*(simulation.cycle % 215); + if (this.distanceToPlayer() < this.laserRange) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0.002) { + m.energy -= 0.004 + } else { + m.damage(0.0004 * simulation.dmgScale) + } + } + this.warpIntensity += 0.0004 + requestAnimationFrame(() => { + if (!simulation.paused && m.alive) { + ctx.transform(1, this.warpIntensity * (Math.random() - 0.5), this.warpIntensity * (Math.random() - 0.5), 1, 0, 0); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) //https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform + // ctx.transform(1 - scale * (Math.random() - 0.5), skew * (Math.random() - 0.5), skew * (Math.random() - 0.5), 1 - scale * (Math.random() - 0.5), translation * (Math.random() - 0.5), translation * (Math.random() - 0.5)); //ctx.transform(Horizontal scaling. A value of 1 results in no scaling, Vertical skewing, Horizontal skewing, Vertical scaling. A value of 1 results in no scaling, Horizontal translation (moving), Vertical translation (moving)) //https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform + } + }) + ctx.beginPath(); + ctx.moveTo(eye.x, eye.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgb(150,0,255)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(150,0,255,0.1)"; + ctx.fill(); + } else { + this.warpIntensity = 0; + } + + //several ellipses spinning about the same axis + const rotation = simulation.cycle * 0.015 + const phase = simulation.cycle * 0.021 + ctx.lineWidth = 1; + ctx.fillStyle = "rgba(150,0,255,0.05)" + ctx.strokeStyle = "#70f" + for (let i = 0, len = 6; i < len; i++) { + ctx.beginPath(); + ctx.ellipse(this.position.x, this.position.y, this.laserRange * Math.abs(Math.sin(phase + i / len * Math.PI)), this.laserRange, rotation, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + + if (!this.isStunned && !this.isSlowed) { + if (this.followDelay > this.delayLimit) this.followDelay -= 0.15; + let history = m.history[(m.cycle - Math.floor(this.followDelay)) % 600] + Matter.Body.setPosition(this, { x: history.position.x, y: history.position.y - history.yOff + 24.2859 }) //bullets move with player + } + } + me.do = function () { + if (this.seePlayer.recall || (!(simulation.cycle % this.seePlayerFreq) && this.distanceToPlayer2() < this.seeAtDistance2 && !m.isCloak)) { + setTimeout(() => { + this.do = this.awake + this.stroke = "rgba(205,0,255,0.5)" + this.fill = "rgba(205,0,255,0.1)" + this.seePlayer.yes = true + }, 2000); + } + this.checkStatus(); + }; + }, + focuser(x, y, radius = 30 + Math.ceil(Math.random() * 10)) { + radius = Math.ceil(radius * 0.7); + mobs.spawn(x, y, 4, radius, "rgb(0,0,255)"); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 0.003); //extra dense //normal is 0.001 + me.restitution = 0; + me.laserPos = me.position; //required for laserTracking + me.repulsionRange = 1200000; //squared + me.accelMag = 0.00009 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + me.onDamage = function () { + this.laserPos = this.position; + }; + spawn.shield(me, x, y); + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + const dist2 = this.distanceToPlayer2(); + //laser Tracking + if (this.seePlayer.yes && dist2 < 4000000) { + const rangeWidth = 2000; //this is sqrt of 4000000 from above if() + //targeting laser will slowly move from the mob to the player's position + this.laserPos = Vector.add(this.laserPos, Vector.mult(Vector.sub(player.position, this.laserPos), 0.1)); + let targetDist = Vector.magnitude(Vector.sub(this.laserPos, m.pos)); + const r = 12; + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (targetDist < r + 16) { + targetDist = r + 10; + //charge at player + const forceMag = this.accelMag * 40 * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle); + } + // else { + //high friction if can't lock onto player + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * 0.98, + // y: this.velocity.y * 0.98 + // }); + // } + if (dist2 > 80000) { + const laserWidth = 0.002; + let laserOffR = Vector.rotateAbout(this.laserPos, (targetDist - r) * laserWidth, this.position); + let sub = Vector.normalise(Vector.sub(laserOffR, this.position)); + laserOffR = Vector.add(laserOffR, Vector.mult(sub, rangeWidth)); + ctx.lineTo(laserOffR.x, laserOffR.y); + + let laserOffL = Vector.rotateAbout(this.laserPos, (targetDist - r) * -laserWidth, this.position); + sub = Vector.normalise(Vector.sub(laserOffL, this.position)); + laserOffL = Vector.add(laserOffL, Vector.mult(sub, rangeWidth)); + ctx.lineTo(laserOffL.x, laserOffL.y); + ctx.fillStyle = `rgba(0,0,255,${Math.max(0, 0.3 * r / targetDist)})` + ctx.fill(); + } + } else { + this.laserPos = this.position; + } + } + }, + flutter(x, y, radius = 20 + 6 * Math.random()) { + mobs.spawn(x, y, 7, radius, '#16576b'); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + // me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.accelMag = 0.0006 + 0.0007 * Math.sqrt(simulation.accelScale); + me.frictionAir = 0.04; + // me.seePlayerFreq = 40 + Math.floor(13 * Math.random()) + me.memory = 240; + me.restitution = 1; + me.frictionStatic = 0; + me.friction = 0; + me.lookTorque = 0.000001 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { x: 0, y: 0 } + spawn.shield(me, x, y); + + // me.onDeath = function() {}; + me.flapRate = 0.3 + Math.floor(3 * Math.random()) / 10 + 100 * me.accelMag + me.flapRadius = 75 + radius * 3 + me.do = function () { + this.seePlayerByHistory() + this.checkStatus(); + if (this.seePlayer.recall) { + this.force.x += Math.cos(this.angle) * this.accelMag * this.mass + this.force.y += Math.sin(this.angle) * this.accelMag * this.mass + + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + + //dot product can't tell if mob is facing directly away or directly towards, so check if pointed directly away from player every few cycles + const mod = (a, n) => { + return a - Math.floor(a / n) * n + } + const sub = Vector.sub(m.pos, this.position) //check by comparing different between angles. Give this a nudge if angles are 180 degree different + const diff = mod(Math.atan2(sub.y, sub.x) - this.angle + Math.PI, 2 * Math.PI) - Math.PI + if (Math.abs(diff) > 2.8) this.torque += 0.0002 * this.inertia * Math.random(); + } + + //rotate towards fireDir + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.4; + const turn = 0.000025 * this.inertia + if (c > threshold) { + this.torque += turn; + } else if (c < -threshold) { + this.torque -= turn; + } + const flapArc = 0.7 //don't go past 1.57 for normal flaps + + ctx.fillStyle = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.2)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * this.flapRate), this.flapRadius) + this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.flapRadius) + } + }; + }, + beetleBoss(x, y, radius = 50) { + mobs.spawn(x, y, 7, radius, '#16576b'); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + + me.flapRate = 0.2 + me.wingSize = 0 + me.wingGoal = 250 + simulation.difficulty + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.accelMag = 0.00045 + 0.0005 * Math.sqrt(simulation.accelScale); + me.frictionAir = 0.05; + me.seePlayerFreq = 13 + me.memory = 420; + me.restitution = 1; + me.frictionStatic = 0; + me.friction = 0; + me.lookTorque = 0.000001 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { x: 0, y: 0 } + spawn.shield(me, x, y); + + // len = 0.3 * simulation.difficulty //spawn some baby flutters + // if (len > 3) { + // for (let i = 0; i < len; ++i) { + // const phase = i / len * 2 * Math.PI + // const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5)) + // spawn.flutter(where.x, where.y) + // } + // } + + // if (Math.random() < 0.3) { + // const len = 0.1 * simulation.difficulty //spawn some baby flutters + // let i = 0 + // let spawnFlutters = () => { + // if (i < len) { + // if (!(simulation.cycle % 30) && !simulation.paused && !simulation.isChoosing) { + // const phase = i / len * 2 * Math.PI + // const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5)) + // spawn.flutter(where.x, where.y) + // i++ + // } + // requestAnimationFrame(spawnFlutters); + // } + // } + // requestAnimationFrame(spawnFlutters); + // me.isAlreadyHadBabies = true + // } + + me.pushAway = function (magX = 0.13, magY = 0.05) { + for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally + if (Vector.magnitudeSquared(Vector.sub(body[i].position, this.position)) < 4000000) { //2000 + body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1) + body[i].force.y -= magY * body[i].mass + } + } + for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally + if (Vector.magnitudeSquared(Vector.sub(bullet[i].position, this.position)) < 4000000) { //2000 + bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1) + bullet[i].force.y -= magY * bullet[i].mass + } + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally + if (Vector.magnitudeSquared(Vector.sub(powerUp[i].position, this.position)) < 4000000) { //2000 + powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1) + powerUp[i].force.y -= magY * powerUp[i].mass + } + } + if (Vector.magnitudeSquared(Vector.sub(player.position, this.position)) < 4000000) { //2000 + player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1) + player.force.y -= magY * player.mass + } + } + + me.babies = function (len) { + const delay = Math.max(3, Math.floor(15 - len / 2)) + let i = 0 + let spawnFlutters = () => { + if (i < len) { + if (!(simulation.cycle % delay) && !simulation.paused && !simulation.isChoosing && m.alive) { + // const phase = i / len * 2 * Math.PI + // const where = Vector.add(this.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5)) + const unit = Vector.normalise(Vector.sub(player.position, this.position)) + const velocity = Vector.mult(unit, 10 + 10 * Math.random()) + const where = Vector.add(this.position, Vector.mult(unit, radius * 1.2)) + spawn.allowShields = false + spawn.flutter(where.x, where.y, Math.floor(7 + 8 * Math.random())) + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setVelocity(who, velocity); + Matter.Body.setAngle(who, Math.atan2(velocity.y, velocity.x)) + + this.alertNearByMobs(); + spawn.allowShields = true + i++ + } + requestAnimationFrame(spawnFlutters); + } + } + requestAnimationFrame(spawnFlutters); + } + // me.babies(0.05 * simulation.difficulty + 1) + + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + me.babies(0.05 * simulation.difficulty + 1) + }; + me.onDamage = function () { + if (this.health < this.nextHealthThreshold && this.alive) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 + this.invulnerableCount = 90 + Math.floor(30 * Math.random()) + this.isInvulnerable = true + this.damageReduction = 0 + this.frictionAir = 0 + this.wingGoal = 0 + this.wingSize = 0 + this.flapRate += 0.13 + this.accelMag *= 1.4 + } + }; + me.do = function () { + this.seePlayerByHistory(50) + this.checkStatus(); + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + this.frictionAir = 0.05 + this.wingGoal = 250 + this.pushAway(Math.sqrt(this.flapRate) * 0.13, Math.sqrt(this.flapRate) * 0.06) //this.flapRate = 0.2, +0.13x3 -> 0.6 + me.babies(0.05 * simulation.difficulty + 1) + } + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else if (this.seePlayer.recall) { + // const force = Vector.mult(Vector.normalise(Vector.sub(this.seePlayer.position, this.position)), this.accelMag * this.mass) + // const force = Vector.mult({ x: Math.cos(this.angle), y: Math.sin(this.angle) }, this.accelMag * this.mass) + // this.force.x += force.x; + // this.force.y += force.y; + this.force.x += Math.cos(this.angle) * this.accelMag * this.mass + this.force.y += Math.sin(this.angle) * this.accelMag * this.mass + + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + + //dot product can't tell if mob is facing directly away or directly towards, so check if pointed directly away from player every few cycles + //check by comparing different between angles. Give this a nudge if angles are 180 degree different + const mod = (a, n) => { + return a - Math.floor(a / n) * n + } + const sub = Vector.sub(m.pos, this.position) + const diff = mod(Math.atan2(sub.y, sub.x) - this.angle + Math.PI, 2 * Math.PI) - Math.PI + if (Math.abs(diff) > 2.8) this.torque += 0.0002 * this.inertia * Math.random(); + } + + //rotate towards fireDir + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.4; + const turn = 0.00003 * this.inertia + if (c > threshold) { + this.torque += turn; + } else if (c < -threshold) { + this.torque -= turn; + } + const flapArc = 0.7 //don't go past 1.57 for normal flaps + this.wingSize = 0.97 * this.wingSize + 0.03 * this.wingGoal + ctx.fillStyle = this.fill = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0012) + this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0012) + } else { + this.wingSize = 0.96 * this.wingSize + 0 //shrink while stunned + } + }; + }, + laserTargetingBoss(x, y, radius = 80) { + const color = "#07f" + mobs.spawn(x, y, 3, radius, color); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.004); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.accelMag = 0.00018 * Math.sqrt(simulation.accelScale); + me.seePlayerFreq = 30 + me.memory = 420; + me.restitution = 1; + me.frictionAir = 0.01; + me.frictionStatic = 0; + me.friction = 0; + me.lookTorque = 0.000001 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { x: 0, y: 0 } + spawn.shield(me, x, y, 1); + // spawn.spawnOrbitals(me, radius + 50 + 100 * Math.random()) + for (let i = 0, len = 2 + 0.3 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); + // me.onHit = function() { }; + // spawn.shield(me, x, y, 1); //not working, not sure why + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.laserInterval = 100 + me.do = function () { + // this.armor(); + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.4; + if (c > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (c < -threshold) { + this.torque -= 0.000004 * this.inertia; + } + if (simulation.cycle % this.laserInterval > this.laserInterval / 2) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + const seeRange = 8000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + vertexCollision(this.position, look, body); + if (!m.isCloak) vertexCollision(this.position, look, [playerBody, playerHead]); + + // hitting player + if ((best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + const dmg = 0.006 * simulation.dmgScale; + m.damage(dmg); + //draw damage + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(best.x, best.y, dmg * 1500, 0, 2 * Math.PI); + ctx.fill(); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = color; + ctx.lineWidth = 3; + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + ctx.stroke(); + ctx.setLineDash([]); + + ctx.beginPath(); + ctx.arc(this.vertices[1].x, this.vertices[1].y, 1 + 0.3 * (this.laserInterval - simulation.cycle % this.laserInterval), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = color; + ctx.fill(); + } else { + ctx.beginPath(); + ctx.arc(this.vertices[1].x, this.vertices[1].y, 1 + 0.3 * (simulation.cycle % this.laserInterval), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = color; + ctx.fill(); + } + } + }; + }, + laserBombingBoss(x, y, radius = 80) { + mobs.spawn(x, y, 3, radius, "rgb(0,235,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.accelMag = 0.00055 * Math.sqrt(simulation.accelScale); + me.seePlayerFreq = 30; + me.memory = 420; + me.restitution = 1; + me.frictionAir = 0.05; + me.frictionStatic = 0; + me.friction = 0; + me.lookTorque = 0.0000055 * (Math.random() > 0.5 ? -1 : 1) * (1 + 0.1 * Math.sqrt(simulation.difficulty)) + me.fireDir = { x: 0, y: 0 } + Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random()) + me.onHit = function () { }; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.targetingCount = 0; + me.targetingTime = 60 - Math.min(58, 3 * simulation.difficulty) + me.do = function () { + + // //wings + // const wing = (simulation.cycle % 9) > 4 ? this.vertices[0] : this.vertices[2] //Vector.add(this.position, { x: 100, y: 0 }) + // const radius = 200 + // //draw + // ctx.beginPath(); + // ctx.arc(wing.x, wing.y, radius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + // ctx.fillStyle = "rgba(0,235,255,0.3)"; + // ctx.fill(); + // //check damage + // const hitPlayer = Matter.Query.ray([player], this.position, wing, radius) + // if (hitPlayer.length && m.immuneCycle < m.cycle) { + // m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + // m.damage(0.02 * simulation.dmgScale); + // } + + + + + + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.02; + if (c > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (c < -threshold) { + this.torque -= 0.000004 * this.inertia; + } + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + const seeRange = 8000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + if (!m.isCloak) vertexCollision(this.position, look, [playerBody, playerHead]); + + // hitting player + if (best.who === playerBody || best.who === playerHead) { + this.targetingCount++ + if (this.targetingCount > this.targetingTime) { + this.targetingCount -= 10; + const sub = Vector.sub(player.position, this.position) + const dist = Vector.magnitude(sub) + const speed = Math.min(55, 5 + 20 * simulation.accelScale) + const velocity = Vector.mult(Vector.normalise(sub), speed) + spawn.grenade(this.vertices[1].x, this.vertices[1].y, dist / speed, Math.min(550, 250 + simulation.difficulty * 3), 6); // grenade(x, y, lifeSpan = 90 + Math.ceil(60 / simulation.accelScale), pulseRadius = Math.min(550, 250 + simulation.difficulty * 3), size = 4) { + Matter.Body.setVelocity(mob[mob.length - 1], velocity); + } + } else if (this.targetingCount > 0) { + this.targetingCount-- + } + //draw beam + if (best.dist2 === Infinity) best = look; + // ctx.beginPath(); + // ctx.moveTo(this.vertices[1].x, this.vertices[1].y); + // ctx.lineTo(best.x, best.y); + // ctx.strokeStyle = "rgba(0,235,255,0.5)"; + // ctx.lineWidth = 3// + 0.1 * this.targetingCount; + // ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + // ctx.stroke(); + // ctx.setLineDash([]); + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(0,235,255,1)"; + ctx.lineWidth = 3 + ctx.stroke(); + if (this.targetingCount / this.targetingTime > 0.33) { + ctx.strokeStyle = "rgba(0,235,255,0.45)"; + ctx.lineWidth = 10 + ctx.stroke(); + if (this.targetingCount / this.targetingTime > 0.66) { + ctx.strokeStyle = "rgba(0,235,255,0.25)"; + ctx.lineWidth = 30 + ctx.stroke(); + } + } + + // ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + // ctx.setLineDash([]); + } + }; + }, + blinkBoss(x, y) { + mobs.spawn(x, y, 5, 50, "rgb(0,235,255)"); //"rgb(221,102,119)" + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, Math.PI * 0.1); + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + me.isBoss = true; + me.damageReduction = 0.034 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.frictionStatic = 0; + me.friction = 0; + me.memory = 240 + me.seePlayerFreq = 55 + me.blinkRange = 250 + if (0.5 < Math.random()) { + me.grenadeDelay = 260 + me.blinkRange *= 1.5 + } else { + me.grenadeDelay = 100 + } + me.pulseRadius = 1.5 * Math.min(550, 200 + simulation.difficulty * 2) + me.delay = 55 + 35 * simulation.CDScale; + me.nextBlinkCycle = me.delay; + spawn.shield(me, x, y, 1); + me.onDamage = function () { + // this.cd = simulation.cycle + this.delay; + }; + me.onDeath = function () { + const offAngle = Math.PI * Math.random() + for (let i = 0, len = 3; i < len; i++) { + spawn.grenade(this.position.x, this.position.y, this.grenadeDelay); + const who = mob[mob.length - 1] + const speed = 5 * simulation.accelScale; + const angle = 2 * Math.PI * i / len + offAngle + Matter.Body.setVelocity(who, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }); + } + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + } + me.do = function () { + this.seePlayerByHistory(40) + if (this.nextBlinkCycle < simulation.cycle && this.seePlayer.yes) { //teleport towards the player + this.nextBlinkCycle = simulation.cycle + this.delay; + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (distMag < this.blinkRange) { //if player is inside teleport range + Matter.Body.setPosition(this, this.seePlayer.position); + } else { + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.blinkRange)); + } + spawn.grenade(this.position.x, this.position.y, this.grenadeDelay, this.pulseRadius); //spawn at new location + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = this.radius * 2.1; + ctx.strokeStyle = this.fill; //"rgba(0,0,0,0.5)"; //'#000' + ctx.stroke(); + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + this.torque += (0.00004 + 0.00003 * Math.random()) * this.inertia * (Math.round(Math.random()) * 2 - 1) //randomly spin around after firing + } + this.checkStatus(); + }; + }, + pulsarBoss(x, y, radius = 90, isNonCollide = false) { + mobs.spawn(x, y, 3, radius, "#a0f"); + let me = mob[mob.length - 1]; + if (isNonCollide) me.collisionFilter.mask = cat.bullet | cat.player + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 0.0001, + damping: 0.3 + }); + Composite.add(engine.world, me.constraint); + }, 2000); //add in a delay in case the level gets flipped left right + + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.radius *= 1.5 + me.vertices[1].x = me.position.x + Math.cos(me.angle) * me.radius; //make one end of the triangle longer + me.vertices[1].y = me.position.y + Math.sin(me.angle) * me.radius; + // me.homePosition = { x: x, y: y }; + me.fireCycle = 0 + me.fireTarget = { x: 0, y: 0 } + me.pulseRadius = Math.min(500, 230 + simulation.difficulty * 3) + me.fireDelay = Math.max(60, 150 - simulation.difficulty * 2) + me.isFiring = false + Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + me.isBoss = true; + + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random(), 1) + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.onHit = function () { }; + me.do = function () { + if (player.speed > 5) this.do = this.fire //don't attack until player moves + } + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.fire = function () { + // this.armor(); + this.checkStatus(); + if (!m.isCloak && !this.isStunned) { + if (this.isFiring) { + if (this.fireCycle > this.fireDelay) { //fire + this.isFiring = false + this.fireCycle = 0 + this.torque += (0.00008 + 0.00007 * Math.random()) * this.inertia * (Math.round(Math.random()) * 2 - 1) //randomly spin around after firing + //is player in beam path + if (Matter.Query.ray([player], this.fireTarget, this.position).length) { + unit = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), this.distanceToPlayer() - 100) + this.fireTarget = Vector.add(this.vertices[1], unit) + } + //damage player if in range + if (Vector.magnitude(Vector.sub(player.position, this.fireTarget)) < this.pulseRadius && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + m.damage(0.045 * simulation.dmgScale); + } + simulation.drawList.push({ //add dmg to draw queue + x: this.fireTarget.x, + y: this.fireTarget.y, + radius: this.pulseRadius, + color: "rgba(120,0,255,0.6)", + time: simulation.drawTime + }); + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y) + ctx.lineTo(this.fireTarget.x, this.fireTarget.y) + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(120,0,255,0.3)"; + ctx.stroke(); + ctx.lineWidth = 5; + ctx.strokeStyle = "rgba(120,0,255,1)"; + ctx.stroke(); + } else { //delay before firing + this.fireCycle++ + //draw explosion outline + ctx.beginPath(); + ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = "rgba(120,0,255,0.07)"; + ctx.fill(); + //draw path from mob to explosion + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y) + ctx.lineTo(this.fireTarget.x, this.fireTarget.y) + ctx.setLineDash([40 * Math.random(), 200 * Math.random()]); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgba(120,0,255,0.3)"; + ctx.stroke(); + ctx.setLineDash([]); + } + } else { //aim at player + this.fireCycle++ + this.fireDir = Vector.normalise(Vector.sub(m.pos, this.position)); //set direction to turn to fire + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + const c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.04; + if (c > threshold) { + this.torque += 0.0000015 * this.inertia; + } else if (c < -threshold) { + this.torque -= 0.0000015 * this.inertia; + } else if (this.fireCycle > 45) { //fire + unit = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), this.distanceToPlayer() - 100) + this.fireTarget = Vector.add(this.vertices[1], unit) + if (Vector.magnitude(Vector.sub(m.pos, this.fireTarget)) < 1000) { //if's possible for this to be facing 180 degrees away from the player, this makes sure that doesn't occur + Matter.Body.setAngularVelocity(this, 0) + this.fireLockCount = 0 + this.isFiring = true + this.fireCycle = 0 + } + } + } + //gently return to starting location + // const sub = Vector.sub(this.homePosition, this.position) + // const dist = Vector.magnitude(sub) + // if (dist > 250) this.force = Vector.mult(Vector.normalise(sub), this.mass * 0.0002) + } else { + this.isFiring = false + } + }; + }, + pulsar(x, y, radius = 40) { + mobs.spawn(x, y, 3, radius, "#f08"); + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.radius *= 2 + // me.frictionAir = 0.02 + + me.vertices[1].x = me.position.x + Math.cos(me.angle) * me.radius; //make one end of the triangle longer + me.vertices[1].y = me.position.y + Math.sin(me.angle) * me.radius; + // me.homePosition = { x: x, y: y }; + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + me.fireCycle = Infinity + me.fireTarget = { x: 0, y: 0 } + me.pulseRadius = Math.min(400, 170 + simulation.difficulty * 3) + me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) + me.isFiring = false + me.onHit = function () { }; + me.canSeeTarget = function () { + const angle = this.angle + Math.PI / 2; + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, Vector.normalise(Vector.sub(this.fireTarget, this.position))); + //distance between the target and the player's location + if ( + m.isCloak || + dot > 0.03 || // not looking at target + Matter.Query.ray(map, this.fireTarget, this.position).length || Matter.Query.ray(body, this.fireTarget, this.position).length || //something blocking line of sight + Vector.magnitude(Vector.sub(m.pos, this.fireTarget)) > 1000 // distance from player to target is very far, (this is because dot product can't tell if facing 180 degrees away) + ) { + this.isFiring = false + return false + } else { + return true + } + } + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + if (this.seePlayer.recall) { + if (this.isFiring) { + if (this.fireCycle > this.fireDelay) { //fire + if (!this.canSeeTarget()) return + this.isFiring = false + this.fireCycle = 0 + this.torque += (0.00002 + 0.0002 * Math.random()) * this.inertia * (Math.round(Math.random()) * 2 - 1) //randomly spin around after firing + //is player in beam path + if (Matter.Query.ray([player], this.fireTarget, this.position).length) { + unit = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), this.distanceToPlayer() - 100) + this.fireTarget = Vector.add(this.vertices[1], unit) + } + //damage player if in range + if (Vector.magnitude(Vector.sub(player.position, this.fireTarget)) < this.pulseRadius && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + m.damage(0.03 * simulation.dmgScale); + } + simulation.drawList.push({ //add dmg to draw queue + x: this.fireTarget.x, + y: this.fireTarget.y, + radius: this.pulseRadius, + color: "rgba(255,0,100,0.6)", + time: simulation.drawTime + }); + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y) + ctx.lineTo(this.fireTarget.x, this.fireTarget.y) + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(255,0,100,0.3)"; + ctx.stroke(); + ctx.lineWidth = 5; + ctx.strokeStyle = "rgba(255,0,100,1)"; + ctx.stroke(); + } else { //delay before firing + this.fireCycle++ + if (!(simulation.cycle % 3)) { + if (!this.canSeeTarget()) return //if can't see stop firing + } + //draw explosion outline + ctx.beginPath(); + ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = "rgba(255,0,100,0.07)"; + ctx.fill(); + //draw path from mob to explosion + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y) + ctx.lineTo(this.fireTarget.x, this.fireTarget.y) + ctx.setLineDash([40 * Math.random(), 200 * Math.random()]); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgba(255,0,100,0.3)"; + ctx.stroke(); + ctx.setLineDash([]); + } + } else { //aim at player + this.fireCycle++ + // this.fireDir = ; //set direction to turn to fire + const angle = this.angle + Math.PI / 2; + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, Vector.normalise(Vector.sub(this.seePlayer.position, this.position))) + const threshold = 0.04; + if (dot > threshold) { //rotate towards fireAngle + this.torque += 0.0000015 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.0000015 * this.inertia; + } else if (this.fireCycle > 60) { // aim + unit = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), this.distanceToPlayer() - 100) + this.fireTarget = Vector.add(this.vertices[1], unit) + if (!this.canSeeTarget()) return + Matter.Body.setAngularVelocity(this, 0) + this.fireLockCount = 0 + this.isFiring = true + this.fireCycle = 0 + } + } + //gently return to starting location + // const sub = Vector.sub(this.homePosition, this.position) + // const dist = Vector.magnitude(sub) + // if (dist > 350) this.force = Vector.mult(Vector.normalise(sub), this.mass * 0.0002) + } else { + this.isFiring = false + } + }; + }, + laser(x, y, radius = 30) { + const color = "#f00" + mobs.spawn(x, y, 3, radius, color); + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + Matter.Body.rotate(me, Math.random() * Math.PI * 2); + me.accelMag = 0.0001 * simulation.accelScale; + me.laserInterval = 100 + me.onHit = function () { + //run this function on hitting player + this.explode(); + }; + me.do = function () { + this.torque = this.lookTorque * this.inertia * 0.5; + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + + if (simulation.cycle % this.laserInterval > this.laserInterval / 2) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + const seeRange = 8000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + vertexCollision(this.position, look, body); + if (!m.isCloak) vertexCollision(this.position, look, [playerBody, playerHead]); + + // hitting player + if ((best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + const dmg = 0.003 * simulation.dmgScale; + m.damage(dmg); + //draw damage + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(best.x, best.y, dmg * 1500, 0, 2 * Math.PI); + ctx.fill(); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); + ctx.moveTo(this.vertices[1].x, this.vertices[1].y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = color; + ctx.lineWidth = 3; + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + ctx.stroke(); + ctx.setLineDash([]); + + ctx.beginPath(); + ctx.arc(this.vertices[1].x, this.vertices[1].y, 1 + 0.3 * (this.laserInterval - simulation.cycle % this.laserInterval), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = color; + ctx.fill(); + } else { + ctx.beginPath(); + ctx.arc(this.vertices[1].x, this.vertices[1].y, 1 + 0.3 * (simulation.cycle % this.laserInterval), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = color; + ctx.fill(); + } + } + }; + }, + laserBoss(x, y, radius = 30) { + mobs.spawn(x, y, 3, radius, "#f00"); + let me = mob[mob.length - 1]; + + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + }, 2000); //add in a delay in case the level gets flipped left right + me.count = 0; + me.frictionAir = 0.03; + // me.torque -= me.inertia * 0.002 + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isBoss = true; + // spawn.shield(me, x, y, 1); //not working, not sure why + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.rotateVelocity = Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) * (simulation.isHorizontalFlipped ? -1 : 1) + me.do = function () { + this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors + this.checkStatus(); + + if (!this.isStunned) { + //check if slowed + let slowed = false + for (let i = 0; i < this.status.length; i++) { + if (this.status[i].type === "slow") { + slowed = true + break + } + } + if (!slowed) { + this.count++ + Matter.Body.setAngle(this, this.count * this.rotateVelocity) + Matter.Body.setAngularVelocity(this, 0) + } + + ctx.beginPath(); + this.lasers(this.vertices[0], this.angle + Math.PI / 3); + this.lasers(this.vertices[1], this.angle + Math.PI); + this.lasers(this.vertices[2], this.angle - Math.PI / 3); + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(80,0,255,0.07)"; + ctx.stroke(); // Draw it + } + + + // Matter.Body.setVelocity(this, { + // x: 0, + // y: 0 + // }); + // Matter.Body.setPosition(this, this.startingPosition); + + }; + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + const dmg = 0.14 * simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + } + }, + stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) { + if (radius > 80) radius = 65; + mobs.spawn(x, y, 6, radius, "rgb(220,50,205)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem) + let me = mob[mob.length - 1]; + me.isVerticesChange = true + me.accelMag = 0.0006 * simulation.accelScale; + // me.g = 0.0002; //required if using this.gravity + me.delay = 360 * simulation.CDScale; + me.spikeVertex = 0; + me.spikeLength = 0; + me.isSpikeGrowing = false; + me.spikeGrowth = 0; + me.isSpikeReset = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs + Matter.Body.rotate(me, Math.PI * 0.1); + spawn.shield(me, x, y); + // me.onDamage = function () {}; + // me.onHit = function() { //run this function on hitting player + // }; + me.onDeath = function () { + if (this.spikeLength > 4) { + this.spikeLength = 4 + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + }; + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.attraction(); + if (this.isSpikeReset) { + if (this.seePlayer.recall) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + if (distMag < radius * spikeMax) { + //find nearest vertex + let nearestDistance = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + //find distance to player for each vertex + const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); + const distMag = Vector.magnitude(dist); + //save the closest distance + if (distMag < nearestDistance) { + this.spikeVertex = i + nearestDistance = distMag + } + } + this.spikeLength = 1 + this.isSpikeGrowing = true; + this.isSpikeReset = false; + Matter.Body.setAngularVelocity(this, 0) + } + } + } else { + if (this.isSpikeGrowing) { + this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8) + // if (this.spikeLength < 2) { + // this.spikeLength += 0.035 + // } else { + // this.spikeLength += 1 + // } + if (this.spikeLength > spikeMax) { + this.isSpikeGrowing = false; + this.spikeGrowth = 0 + } + } else { + Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation + this.spikeLength -= 0.3 + if (this.spikeLength < 1) { + this.spikeLength = 1 + this.isSpikeReset = true + this.radius = radius + } + } + const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength) + this.vertices[this.spikeVertex].x = this.position.x + spike.x + this.vertices[this.spikeVertex].y = this.position.y + spike.y + // this.radius = radius * this.spikeLength; + } + }; + }, + + striker(x, y, radius = 14 + Math.ceil(Math.random() * 25)) { + mobs.spawn(x, y, 5, radius, "rgb(221,102,119)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.00034 * simulation.accelScale; + me.g = 0.00015; //required if using this.gravity + me.frictionStatic = 0; + me.friction = 0; + me.delay = 30 + 60 * simulation.CDScale; + me.cd = Infinity; + me.strikeRange = 300 + Matter.Body.rotate(me, Math.PI * 0.1); + spawn.shield(me, x, y); + me.onDamage = function () { + this.cd = simulation.cycle + this.delay; + }; + me.do = function () { + this.gravity(); + if (!(simulation.cycle % this.seePlayerFreq)) { // this.seePlayerCheck(); from mobs + if ( + this.distanceToPlayer2() < this.seeAtDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && + !m.isCloak + ) { + this.foundPlayer(); + if (this.cd === Infinity) this.cd = simulation.cycle + this.delay * 0.7; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + this.cd = Infinity + } + } + this.checkStatus(); + this.attraction(); + if (this.cd < simulation.cycle && this.seePlayer.recall) { + const dist = Vector.sub(this.seePlayer.position, this.position); + const distMag = Vector.magnitude(dist); + this.cd = simulation.cycle + this.delay; + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + if (distMag < 400) { + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), distMag - 20 - radius)); + } else { + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.strikeRange)); + } + ctx.lineTo(this.position.x, this.position.y); + ctx.lineWidth = radius * 2.1; + ctx.strokeStyle = this.fill; //"rgba(0,0,0,0.5)"; //'#000' + ctx.stroke(); + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.5, + y: this.velocity.y * 0.5 + }); + } + }; + }, + revolutionBoss(x, y, radius = 70) { + const sides = 9 + Math.floor(Math.min(12, 0.2 * simulation.difficulty)) + const coolBends = [-1.8, 0, 0, 0.9, 1.2] + const bendFactor = coolBends[Math.floor(Math.random() * coolBends.length)]; + mobs.spawn(x, y, sides, radius, "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.00018 + 0.00018 * Math.sqrt(simulation.accelScale); + me.frictionAir = 0.01; + me.swordRadiusMax = 400 + 10 * simulation.difficulty; + me.laserAngle = 0; + me.swordDamage = 0.025 * simulation.dmgScale //0.033 * simulation.dmgScale; previous //0.14 * simulation.dmgScale;laserBoss + + // spawn.shield(me, x, y, 1); + Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.12 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isBoss = true; + me.onDamage = function () { }; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + + //invulnerability every 1/4 fraction of life lost + //required setup for invulnerable + me.isInvulnerable = false + me.isNextInvulnerability = 0.75 + me.invulnerabilityCountDown = 0 + me.invulnerable = function () { + if (this.health < this.isNextInvulnerability) { + this.isNextInvulnerability = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.isInvulnerable = true + this.startingDamageReduction = this.damageReduction + this.damageReduction = 0 + this.invulnerabilityCountDown = 106 + } + if (this.isInvulnerable) { + if (this.invulnerabilityCountDown > 0) { + this.invulnerabilityCountDown-- + //draw invulnerability + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + } + } + me.do = function () { + this.invulnerable(); + this.checkStatus(); + this.seePlayerByHistory(60); + this.attraction(); + //traveling laser + this.laserAngle += this.isInvulnerable ? 0.025 : 0.006 + for (let i = 0, len = this.vertices.length; i < len; i++) { + // this.laserSword(this.vertices[1], this.angle + laserAngle); + const bend = bendFactor * Math.cos(this.laserAngle + 2 * Math.PI * i / len) + const long = this.swordRadiusMax * Math.sin(this.laserAngle + 2 * Math.PI * i / len) + if (long > 0) this.laserSword(this.vertices[i], bend + this.angle + (i + 0.5) / sides * 2 * Math.PI, Math.abs(long)); + } + }; + me.laserSword = function (where, angle, length) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; + // vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path + ctx.lineWidth = 10; + ctx.stroke(); + ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path + ctx.lineWidth = 2; + // ctx.stroke(); + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + sprayBoss(x, y, radius = 40, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 16, radius, "rgb(255,255,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.isReactorBoss = true; + me.inertia = Infinity; //no rotation + me.burstFireFreq = 22 + Math.floor(13 * simulation.CDScale) + me.burstTotalPhases = 3 + Math.floor(1.4 / simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0; + me.restitution = 1 + // spawn.spawnOrbitals(me, radius + 50 + 125 * Math.random(), 1) + Matter.Body.setDensity(me, 0.002 + 0.0001 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.09 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.nextHealthThreshold = 0.75 + me.onDeath = function () { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + + this.phaseCycle = -2 + this.do = this.burstFire + this.frictionAir = 1 + this.isInvulnerable = true + this.damageReduction = 0 + } + }; + + //draw radial lines from verticies showing future bullet paths? + me.radialLines = function () { + ctx.beginPath(); + for (let i = 0, len = this.vertices.length; i < len; i++) { + ctx.moveTo(this.vertices[i].x, this.vertices[i].y) + const unit = Vector.add(Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), 1000), this.vertices[i]) + ctx.lineTo(unit.x, unit.y) + } + ctx.lineWidth = 10 + ctx.strokeStyle = "rgb(200,0,200,0.03)" + ctx.stroke(); + } + + me.phaseCycle = 0 + me.normalDoStuff = function () { + this.checkStatus(); + me.seePlayer.recall = 1 + //maintain speed //faster in the vertical to help avoid repeating patterns + if (this.speed < 0.01) { + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), 0.1)); + } else { + if (Math.abs(this.velocity.y) < 9) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.03 }); + if (Math.abs(this.velocity.x) < 7) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.03, y: this.velocity.y }); + } + } + me.burstFire = function () { + this.normalDoStuff(); + this.radialLines() + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + + if (!(simulation.cycle % this.burstFireFreq)) { + this.phaseCycle++ + if (this.phaseCycle > this.burstTotalPhases) { //start spiral fire mode + // this.phaseCycle = -7 + this.do = this.normalDoStuff + this.frictionAir = 0; + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + Matter.Body.setVelocity(this, Vector.rotate({ x: 20, y: 0 }, 2 * Math.PI * Math.random())); + if (this.isShielded) { //remove shield + for (let i = 0; i < mob.length; i++) { + if (mob[i].shield) mob[i].death() + } + } + } + if (this.phaseCycle > -1) { + Matter.Body.rotate(this, 0.02) + for (let i = 0, len = this.vertices.length; i < len; i++) { //fire a bullet from each vertex + spawn.sniperBullet(this.vertices[i].x, this.vertices[i].y, 3, 4); + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[i])), -15) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: velocity.x, + y: velocity.y + }); + } + } + } + }; + me.do = me.normalDoStuff + Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + }, + mineBoss(x, y, radius = 120, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 0, radius, "rgba(255,255,255,0.5)") // "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + // Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.isBoss = true; + me.isReactorBoss = true; + Matter.Body.setDensity(me, 0.001); //normal is 0.001 + me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + + me.cycle = 0 + me.inertia = Infinity; + me.frictionAir = 0.01 + me.restitution = 1 + me.friction = 0 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + me.explodeRange = 400 + Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + me.seePlayer.recall = 1; + // spawn.shield(me, x, y, 1); + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 + this.invulnerableCount = 60 + simulation.difficulty * 1.5 + this.isInvulnerable = true + this.damageReduction = 0 + + //slow time to look cool + // simulation.fpsCap = 10 //new fps + // simulation.fpsInterval = 1000 / simulation.fpsCap; + //how long to wait to return to normal fps + // m.defaultFPSCycle = m.cycle + 20 + 90 + + + for (let i = 0, len = mob.length; i < len; ++i) { //trigger nearby mines + if (mob[i].isMine && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < this.explodeRange) mob[i].isExploding = true + } + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: this.explodeRange, + color: "rgba(255,25,0,0.6)", + time: simulation.drawTime * 2 + }); + + } + }; + me.onDeath = function () { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0, len = mob.length; i < len; ++i) { //trigger nearby mines + if (mob[i].isMine && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < this.explodeRange) mob[i].isExploding = true + } + }; + // console.log(me.mass) //100 + me.do = function () { + me.seePlayer.recall = 1 + //maintain speed //faster in the vertical to help avoid repeating patterns + if (this.speed < 0.01) { + const unit = Vector.sub(player.position, this.position) + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(unit), 0.1)); + // this.invulnerableCount = 10 + simulation.difficulty * 0.5 + // this.isInvulnerable = true + // this.damageReduction = 0 + } else { + if (Math.abs(this.velocity.y) < 10) { + Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.03 }); + } + if (Math.abs(this.velocity.x) < 7) { + Matter.Body.setVelocity(this, { x: this.velocity.x * 1.03, y: this.velocity.y }); + } + } + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + this.checkStatus(); + if (!(simulation.cycle % 15) && mob.length < 360) spawn.mine(this.position.x, this.position.y) + }; + }, + mine(x, y) { + mobs.spawn(x, y, 8, 10, "rgb(255, 255, 255)"); //"rgb(100,170,150)" //"rgb(61, 125, 121)" + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.0001); //normal is 0.001 + // Matter.Body.setStatic(me, true); //make static (disables taking damage) + me.frictionAir = 1 + me.damageReduction = 2 + me.collisionFilter.mask = cat.bullet //| cat.body + // me.collisionFilter.category = cat.mobBullet; + // me.collisionFilter.mask = cat.bullet | cat.body // | cat.player + me.isMine = true + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.isUnstable = true; //dies when blocked + me.showHealthBar = false; + me.explodeRange = 210 + 140 * Math.random() + me.isExploding = false + me.countDown = Math.ceil(4 * Math.random()) + me.isInvulnerable = true //not actually invulnerable, just prevents block + ice-9 interaction + + // me.onHit = function() { + // this.isExploding = true + // }; + // me.onDamage = function() { + // this.health = 1 + // this.isExploding = true + // }; + me.do = function () { + this.checkStatus(); + + if (Matter.Query.collides(this, [player]).length > 0) { + this.isExploding = true + } + + if (this.isExploding) { + if (this.countDown-- < 0) { //explode + this.death(); + //hit player + if (Vector.magnitude(Vector.sub(this.position, player.position)) < this.explodeRange && m.immuneCycle < m.cycle) { + m.damage(0.02 * simulation.dmgScale * (tech.isRadioactiveResistance ? 0.25 : 1)); + m.energy -= 0.2 * (tech.isRadioactiveResistance ? 0.25 : 1) + if (m.energy < 0) m.energy = 0 + } + // mob[i].isInvulnerable = false //make mineBoss not invulnerable ? + const range = this.explodeRange + 50 //mines get a slightly larger range to explode + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < range) { + if (mob[i].isMine) mob[i].isExploding = true //explode other mines + } + } + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: this.explodeRange, + color: "rgba(80,220,190,0.45)", + time: 16 + }); + } + } + }; + }, + bounceBoss(x, y, radius = 80, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 0, radius, "rgb(255,255,255)") // "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.isBoss = true; + me.isReactorBoss = true; + Matter.Body.setDensity(me, 0.003); //normal is 0.001 + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.inertia = Infinity; + me.isInvulnerable = false + me.frictionAir = 0.01 + me.restitution = 1 + me.friction = 0 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + me.seePlayer.recall = 1; + // spawn.shield(me, x, y, 1); + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.fireCount = 60 + simulation.difficulty * 1.5 + this.isInvulnerable = true + this.damageReduction = 0 + } + }; + if (isSpawnBossPowerUp) me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; + me.cycle = 0 + me.nextHealthThreshold = 0.75 + me.fireCount = 0 + // console.log(me.mass) //100 + me.do = function () { + me.seePlayer.recall = 1 + //maintain speed //faster in the vertical to help avoid repeating patterns + if (this.speed < 0.01) { + const unit = Vector.sub(player.position, this.position) + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(unit), 0.1)); + // this.fireCount = 10 + simulation.difficulty * 0.5 + // this.isInvulnerable = true + // this.damageReduction = 0 + } else { + if (Math.abs(this.velocity.y) < 15) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.03 }); + if (Math.abs(this.velocity.x) < 11) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.03, y: this.velocity.y }); + } + + if (this.isInvulnerable) { + this.fireCount-- + if (this.fireCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + + if (this.mass > 10) Matter.Body.scale(this, 0.99, 0.99); + + // for (let i = 0; i < 1; i++) { + const velocity = Vector.rotate(Vector.mult(Vector.normalise(this.velocity), -5 - 10 * Math.random()), 0.5 * (Math.random() - 0.5)) + spawn.bounceBullet(this.position.x, this.position.y, velocity) + // } + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else if (this.mass < 100) { + Matter.Body.scale(this, 1.01, 1.01); //grow back to normal size + } + + this.checkStatus(); + //horizontal attraction + // const xMag = 0.0005 + // if (player.position.x > this.position.x + 200) { + // this.force.x += xMag * this.mass; + // } else if (player.position.x < this.position.x - 200) { + // this.force.x -= xMag * this.mass; + // } + }; + }, + timeBoss(x, y, radius = 50, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 0, radius, `hsl(0, 100%, 50%)`) // "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.isBoss = true; + me.isReactorBoss = true; + me.inertia = Infinity; + me.frictionAir = 0.01 + me.restitution = 1 + me.friction = 0 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + me.seePlayer.recall = 1; + // for (let i = 0; i < 4; i++) spawn.spawnOrbitals(me, radius + 25, 1) + for (let i = 0, len = 2 + 0.3 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 10 + 10 * i, 1); + // me.skipRate = 1 + Math.floor(simulation.difficulty*0.02) + // spawn.shield(me, x, y, 1); + Matter.Body.setDensity(me, 0.001); //normal is 0.001 + me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.invulnerableCount = 420 + simulation.difficulty * 5 //how long does invulnerable time last + this.isInvulnerable = true + this.damageReduction = 0 + // requestAnimationFrame(() => { simulation.timePlayerSkip(300) }); //wrapping in animation frame prevents errors, probably + } + }; + me.onDeath = function () { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + requestAnimationFrame(() => { simulation.timePlayerSkip(60) }); //wrapping in animation frame prevents errors, probably + }; + + me.cycle = Math.floor(360 * Math.random()) + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + // console.log(me.mass) //100 + me.do = function () { + this.cycle++ + this.fill = `hsl(${this.cycle * 0.5}, 100%, 80%)`; + // this.fill = `hsl(${270 + 50*Math.sin(this.cycle*0.02)}, 100%, 60%)`; + this.seePlayer.recall = 1 + //maintain speed //faster in the vertical to help avoid repeating patterns + if (this.speed < 0.01) { + const unit = Vector.sub(player.position, this.position) + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(unit), 0.1)); + } else { + if (Math.abs(this.velocity.y) < 10) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.02 }); + if (Math.abs(this.velocity.x) < 7) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.02, y: this.velocity.y }); + } + + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + //time dilation + if (!simulation.isTimeSkipping) { + requestAnimationFrame(() => { + simulation.timePlayerSkip(2) + m.walk_cycle += m.flipLegs * m.Vx //makes the legs look like they are moving fast + }); //wrapping in animation frame prevents errors, probably + + // simulation.timePlayerSkip(2) + // m.walk_cycle += m.flipLegs * m.Vx //makes the legs look like they are moving fast + + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 15 + 6 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + } + + this.checkStatus(); + //horizontal attraction + // const xMag = 0.0005 + // if (player.position.x > this.position.x + 200) { + // this.force.x += xMag * this.mass; + // } else if (player.position.x < this.position.x - 200) { + // this.force.x -= xMag * this.mass; + // } + }; + }, + bounceBullet(x, y, velocity = { x: 0, y: 0 }, radius = 11, sides = 6) { + //bullets + mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.00001); //normal is 0.001 + me.timeLeft = 360 + Math.floor(180 * Math.random()) + me.inertia = Infinity; + me.damageReduction = 1 + me.frictionAir = 0 + me.friction = 0 + me.restitution = 1 + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.onHit = function () { + this.explode(this.mass * 12); + }; + me.do = function () { + this.timeLimit(); + }; + Matter.Body.setVelocity(me, velocity); + }, + slashBoss(x, y, radius = 80) { + mobs.spawn(x, y, 5, radius, "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.isBoss = true; + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.frictionAir = 0.02 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0004 + 0.00015 * simulation.accelScale; + Matter.Body.setDensity(me, 0.0005); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.seePlayerFreq = 20 + me.lockedOn = null; + + me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.delay = 70 + 70 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 1100 + 20 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.07 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = 200000 + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.do = function () { + this.seePlayerByHistory(40); + this.attraction(); + this.checkStatus(); + this.sword() //does various things depending on what stage of the sword swing + + // ctx.beginPath(); //hide map + // ctx.arc(this.position.x, this.position.y, 3000, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + // ctx.fillStyle = "#444"; + // ctx.fill(); + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + !m.isCloak && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex farthest away from player + let dist = 0 + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D > dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / 5 * 2 * Math.PI + 0.6283 + this.sword = this.swordGrow + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + Matter.Body.setAngularVelocity(this, 0) + this.accelMag = 0 + this.damageReduction = 0 + this.isInvulnerable = true + this.frictionAir = 1 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax) { + this.sword = this.swordSlash + this.spinCount = 0 + } + + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.swordSlash = function () { + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 80) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.001 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + this.damageReduction = this.startingDamageReduction + this.isInvulnerable = false + this.frictionAir = 0.01 + } + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path + ctx.lineWidth = 25; + ctx.stroke(); + ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path + ctx.lineWidth = 5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + slasher(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 5, radius, "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0008 * simulation.accelScale; + me.torqueMagnitude = 0.00002 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.035; + me.delay = 120 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 350 + 5 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.018 + 0.0006 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.04 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = 200000 + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.attraction(); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex farthest away from player + let dist = 0 + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D > dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / 5 * 2 * Math.PI + 0.6283 + this.sword = this.swordGrow + this.accelMag = 0 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax || this.isStunned) { + this.sword = this.swordSlash + this.spinCount = 0 + } + } + me.swordSlash = function () { + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 60 || this.isStunned) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.001 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + } + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 6, radius, "rgb(180,199,245)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.001 * simulation.accelScale; + me.torqueMagnitude = -0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.035; + me.delay = 140 * simulation.CDScale; + me.cd = 0; + me.swordRadius = 0; + me.swordVertex = 1 + me.swordRadiusMax = 320 + 3.6 * simulation.difficulty; + me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.03 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = 200000 + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.attraction(); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + this.laserAngle = -Math.PI / 6 + this.sword = this.swordGrow + this.accelMag = 0 + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + this.swordRadius += this.swordRadiusGrowRate + if (this.swordRadius > this.swordRadiusMax || this.isStunned) { + this.sword = this.swordSlash + this.spinCount = 0 + } + } + me.swordSlash = function () { + this.laserSword(this.vertices[0], this.angle + this.laserAngle); + this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); + + this.torque += this.torqueMagnitude; + this.spinCount++ + if (this.spinCount > 100 || this.isStunned) { + this.sword = this.swordWaiting + this.swordRadius = 0 + this.accelMag = 0.001 * simulation.accelScale; + this.cd = simulation.cycle + this.delay; + } + } + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { + const sides = 6 + mobs.spawn(x, y, sides, radius, "rgb(180,215,235)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.00055 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.02; + me.delay = 150 * simulation.CDScale; + me.cd = 0; + me.cycle = 0; + me.swordVertex = 1 + me.swordRadiusInitial = radius / 2; + me.swordRadius = me.swordRadiusInitial; + me.swordRadiusMax = 800 + 6 * simulation.difficulty; + me.swordRadiusGrowRateInitial = 1.08 + me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty) + me.isSlashing = false; + me.swordDamage = 0.03 * simulation.dmgScale + me.laserAngle = 3 * Math.PI / 5 + const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.checkStatus(); + this.seePlayerByHistory(15); + this.sword() //does various things depending on what stage of the sword swing + }; + me.swordWaiting = function () { + this.attraction(); + if ( + this.seePlayer.recall && + this.cd < simulation.cycle && + this.distanceToPlayer2() < seeDistance2 && + Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 + ) { + //find vertex closest to the player + let dist = Infinity + for (let i = 0, len = this.vertices.length; i < len; i++) { + const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + if (D < dist) { + dist = D + this.swordVertex = i + } + } + this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides + this.sword = this.swordGrow + this.cycle = 0 + this.swordRadius = this.swordRadiusInitial + //slow velocity but don't stop + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5)) + //set angular velocity to 50% + // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5) + //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise + const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) + const playerVector = Vector.sub(this.position, m.pos) + const cross = Matter.Vector.cross(laserStartVector, playerVector) + this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) + } + } + me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing + me.swordGrow = function () { + this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle); + Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9)) + // this.swordRadius += this.swordRadiusGrowRate + this.cycle++ + // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03) + this.swordRadius *= this.swordRadiusGrowRate + + if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial + // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate) + if (this.swordRadius < this.swordRadiusInitial || this.isStunned) { + // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate) + this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial + this.sword = this.swordWaiting + this.swordRadius = 0 + this.cd = simulation.cycle + this.delay; + } + } + me.laserSpear = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let v = domain[i].vertices; + const len = v.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; + } + } + results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; + } + } + }; + best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; + const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; + vertexCollision(where, look, body); // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead)) { + this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player + + if (m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + m.damage(this.swordDamage); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: this.swordDamage * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + } + if (best.dist2 === Infinity) best = look; + ctx.beginPath(); //draw beam + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path + ctx.lineWidth = 15; + ctx.stroke(); + ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path + ctx.lineWidth = 4; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + } + }, + sneakBoss(x, y, radius = 70) { + mobs.spawn(x, y, 5, radius, "transparent"); + let me = mob[mob.length - 1]; + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + me.isBoss = true; + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.accelMag = 0.0017 * Math.sqrt(simulation.accelScale); + me.frictionAir = 0.01; + me.g = 0.0001; //required if using this.gravity + me.stroke = "transparent"; //used for drawSneaker + me.alpha = 1; //used in drawSneaker + me.isCloaked = true; //used in drawSneaker + me.isBadTarget = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + me.showHealthBar = false; + me.memory = 30; + me.vanishesLeft = Math.ceil(1 + simulation.difficultyMode * 0.5) + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.onDamage = function () { + if (this.vanishesLeft > 0 && this.health < 0.1) { //if health is below 10% teleport to a random spot on player history, heal, and cloak + this.vanishesLeft-- + + for (let i = 0; i < 8; i++) { //flash screen to hide vanish + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: 3000, + color: `rgba(0, 0, 0,${1 - 0.1 * i})`, + time: (i + 2) * 4 + }); + } + //teleport to near the end of player history + const index = Math.floor((m.history.length - 1) * (0.66 + 0.2 * Math.random())) + let history = m.history[(m.cycle - index) % 600] + Matter.Body.setPosition(this, history.position) + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + + this.damageReduction = 0 //immune to harm for the rest of this game cycle + this.seePlayer.recall = 0 + this.cloak(); + this.health = 1; + } + }; + me.cloak = function () { + if (!this.isCloaked) { //stealth + this.alpha = 0; + this.isCloaked = true; + this.isBadTarget = true; + this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + } + } + me.deCloak = function () { + if (this.isCloaked) { + this.damageReduction = 0.4 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + this.isCloaked = false; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player + } + } + me.do = function () { + if (this.damageReduction === 0) { + this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + let i = this.status.length //clear bad status effects + while (i--) { + if (this.status[i].type === "stun" || this.status[i].type === "dot") this.status.splice(i, 1); + } + this.isStunned = false; + } + this.gravity(); + this.seePlayerByHistory(55); + this.checkStatus(); + this.attraction(); + //draw + if (this.seePlayer.recall) { + if (this.alpha < 1) this.alpha += 0.005 + 0.003 / simulation.CDScale; + } else { + if (this.alpha > 0) this.alpha -= 0.04; + } + if (this.alpha > 0) { + if (this.alpha > 0.7) { + this.healthBar(); + this.deCloak() + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = `rgba(0,0,0,${this.alpha * this.alpha})`; + ctx.fill(); + } else { + this.cloak() + } + }; + }, + sneaker(x, y, radius = 15 + Math.ceil(Math.random() * 10)) { + mobs.spawn(x, y, 5, radius, "transparent"); + let me = mob[mob.length - 1]; + // Matter.Body.setDensity(me, 0.001); //extra dense //normal is 0.001 //makes effective life much larger + me.accelMag = 0.001 * Math.sqrt(simulation.accelScale); + me.frictionAir = 0.01; + me.g = 0.0002; //required if using this.gravity + me.stroke = "transparent"; //used for drawSneaker + me.alpha = 1; //used in drawSneaker + me.isNotCloaked = false; //used in drawSneaker + me.isBadTarget = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + me.showHealthBar = false; + me.memory = 240; + me.isVanished = false; + me.onDamage = function () { + if (!this.isVanished && this.health < 0.1 && !this.isStunned && !this.isSlowed) { //if health is below 10% teleport to a random spot on player history, heal, and cloak + this.health = 1; + this.isVanished = true + this.cloak(); + //teleport to near the end of player history + Matter.Body.setPosition(this, m.history[Math.floor((m.history.length - 1) * (0.66 + 0.33 * Math.random()))].position) + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + this.damageReduction = 0 //immune to harm for the rest of this game cycle + } + }; + me.cloak = function () { + if (this.isNotCloaked) { //stealth + this.alpha = 0; + this.isNotCloaked = false; + this.isBadTarget = true; + this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + } + } + me.do = function () { + if (this.damageReduction === 0) { + this.damageReduction = 1 //stop being immune to harm immediately + let i = this.status.length //clear bad status effects + while (i--) { + if (this.status[i].type === "stun" || this.status[i].type === "dot") this.status.splice(i, 1); + } + this.isStunned = false; + } + this.gravity(); + this.seePlayerByHistory(25); + this.checkStatus(); + this.attraction(); + //draw + if (this.seePlayer.recall) { + if (this.alpha < 1) this.alpha += 0.003 + 0.003 / simulation.CDScale; + } else { + if (this.alpha > 0) this.alpha -= 0.03; + } + if (this.alpha > 0) { + if (this.alpha > 0.7) { + this.healthBar(); + if (!this.isNotCloaked) { + this.isNotCloaked = true; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player + } + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = `rgba(0,0,0,${this.alpha * this.alpha})`; + ctx.fill(); + } else { + this.cloak() + } + }; + }, + ghoster(x, y, radius = 50 + Math.ceil(Math.random() * 90)) { + mobs.spawn(x, y, 7, radius, "transparent"); + let me = mob[mob.length - 1]; + me.seeAtDistance2 = 300000; + me.accelMag = 0.00004 + 0.00015 * simulation.accelScale; + if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search + // Matter.Body.setDensity(me, 0.0015); //normal is 0.001 + me.damageReduction = 0.5 + me.stroke = "transparent"; //used for drawGhost + me.alpha = 1; //used in drawGhost + me.isNotCloaked = false; //used in drawGhost + me.isBadTarget = true; + // me.leaveBody = false; + me.collisionFilter.mask = cat.bullet //| cat.body + me.showHealthBar = false; + me.memory = 600; + me.do = function () { + //cap max speed to avoid getting launched by deflection, explosion + if (this.speed > 7) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.8, + y: this.velocity.y * 0.8 + }); + } + this.seePlayerCheckByDistance(); + this.checkStatus(); + this.attraction(); + this.search(); + //draw + if (this.distanceToPlayer2() < this.seeAtDistance2) { + if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid + } else { + if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide + } + if (this.alpha > 0) { + if (this.alpha > 0.7 && this.seePlayer.recall) { + this.healthBar(); + if (!this.isNotCloaked) { + this.isNotCloaked = true; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.bullet + } + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 1; + ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`; + ctx.fill(); + } else if (this.isNotCloaked) { + this.isNotCloaked = false; + this.isBadTarget = true; + this.collisionFilter.mask = cat.bullet; //can't touch player or walls + } + }; + }, + // blinker(x, y, radius = 45 + Math.ceil(Math.random() * 70)) { + // mobs.spawn(x, y, 6, radius, "transparent"); + // let me = mob[mob.length - 1]; + // Matter.Body.setDensity(me, 0.0005); //normal is 0.001 //makes effective life much lower + // me.stroke = "rgb(0,200,255)"; //used for drawGhost + // Matter.Body.rotate(me, Math.random() * 2 * Math.PI); + // me.blinkRate = 40 + Math.round(Math.random() * 60); //required for blink + // me.blinkLength = 150 + Math.round(Math.random() * 200); //required for blink + // me.isStatic = true; + // me.memory = 360; + // me.seePlayerFreq = Math.round((40 + 30 * Math.random())); + // // me.isBig = false; + // // me.scaleMag = Math.max(5 - me.mass, 1.75); + // me.onDeath = function () { + // // if (this.isBig) { + // // Matter.Body.scale(this, 1 / this.scaleMag, 1 / this.scaleMag); + // // this.isBig = false; + // // } + // }; + // me.onHit = function () { + // simulation.timeSkip(120) + // }; + // me.do = function () { + // this.seePlayerCheck(); + // this.blink(); + // //strike by expanding + // // if (this.isBig) { + // // if (this.cd - this.delay + 15 < simulation.cycle) { + // // Matter.Body.scale(this, 1 / this.scaleMag, 1 / this.scaleMag); + // // this.isBig = false; + // // } + // // } else + // if (this.seePlayer.yes && this.cd < simulation.cycle) { + // const dist = Vector.sub(this.seePlayer.position, this.position); + // const distMag2 = Vector.magnitudeSquared(dist); + // if (distMag2 < 80000) { + // this.cd = simulation.cycle + this.delay; + + // // Matter.Body.scale(this, this.scaleMag, this.scaleMag); + // // this.isBig = true; + // } + // } + // }; + // }, + bomberBoss(x, y, radius = 88) { + //boss that drops bombs from above and holds a set distance from player + mobs.spawn(x, y, 3, radius, "rgba(255,0,200,0.5)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + + Matter.Body.setDensity(me, 0.0025 + 0.00013 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + + me.stroke = "transparent"; //used for drawGhost + me.seeAtDistance2 = 1500000; + me.fireFreq = 10 + Math.floor(70 * simulation.CDScale); + me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search + me.hoverElevation = 460 + (Math.random() - 0.5) * 200; //squared + me.hoverXOff = (Math.random() - 0.5) * 100; + me.accelMag = Math.floor(10 * (Math.random() + 4.5)) * 0.00001 * simulation.accelScale; + me.g = 0.0002; //required if using this.gravity // gravity called in hoverOverPlayer + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.01; + me.memory = Infinity; + me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + spawn.shield(me, x, y, 1); + + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const speed = (0.007 + 0.003 * Math.random() + 0.004 * Math.sqrt(simulation.difficulty)) + let radiusOrbitals = radius + 125 + 350 * Math.random() + for (let i = 0; i < len; i++) spawn.orbital(me, radiusOrbitals, i / len * 2 * Math.PI, speed) + radiusOrbitals = radius + 125 + 350 * Math.random() + for (let i = 0; i < len; i++) spawn.orbital(me, radiusOrbitals, i / len * 2 * Math.PI, -speed) + + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + // this.armor(); + this.seePlayerCheckByDistance(); + this.checkStatus(); + if (this.seePlayer.recall) { + this.hoverOverPlayer(); + this.bomb(); + this.search(); + } + }; + }, + shooter(x, y, radius = 25 + Math.ceil(Math.random() * 50)) { + mobs.spawn(x, y, 3, radius, "rgb(255,100,150)"); + let me = mob[mob.length - 1]; + // me.vertices = Matter.Vertices.clockwiseSort(Matter.Vertices.rotate(me.vertices, Math.PI, me.position)); //make the pointy side of triangle the front + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + me.isVerticesChange = true + // Matter.Body.rotate(me, Math.PI) + + me.memory = 120; + me.fireFreq = 0.007 + Math.random() * 0.005; + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.05; + me.lookTorque = 0.0000025 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { x: 0, y: 0 }; + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + // spawn.shield(me, x, y); + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.fire(); + }; + }, + shooterBoss(x, y, radius = 110, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 3, radius, "rgb(255,70,180)"); + let me = mob[mob.length - 1]; + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 0.00004, + damping: 0.2 + }); + Composite.add(engine.world, me.constraint); + }, 2000); //add in a delay in case the level gets flipped left right + + me.isBoss = true; + Matter.Body.setDensity(me, 0.01 + 0.0004 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + me.isVerticesChange = true + me.memory = 240; + me.fireFreq = 0.01 + 0.0005 * Math.min(40, simulation.difficulty); //bigger number means more shots per second + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.005 * simulation.accelScale; + me.frictionAir = 0.05; + me.lookTorque = 0.000006 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { x: 0, y: 0 }; + setTimeout(() => { + for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); + }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital + me.onDeath = function () { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.do = function () { + // this.armor(); + this.seePlayerByLookingAt(); + this.checkStatus(); + // this.fire(); + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 5000; //gives the bullet an arc //was / 1600 + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + const dot = Vector.dot({ x: Math.cos(angle), y: Math.sin(angle) }, this.fireDir) + // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.1; + if (dot > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.000004 * this.inertia; + } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { + //fire + for (let i = 0, len = 2 + 0.07 * simulation.difficulty; i < len; i++) { + spawn.bullet(this.vertices[1].x, this.vertices[1].y, 10 + Math.ceil(this.radius / 25)); + const spread = Vector.rotate({ x: Math.sqrt(len) + 4, y: 0 }, 2 * Math.PI * Math.random()) + const dir = Vector.add(Vector.mult(this.fireDir, 25), spread) + Matter.Body.setVelocity(mob[mob.length - 1], dir); + } + this.noseLength = 0; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + }; + }, + bullet(x, y, radius = 9, sides = 0) { + //bullets + mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass * 15); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + me.timeLeft = 220; + me.g = 0.001; //required if using this.gravity + me.frictionAir = 0; + me.restitution = 0.8; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.do = function () { + this.gravity(); + this.timeLimit(); + }; + }, + bomb(x, y, radius = 9, sides = 5) { + mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass * 120); + }; + me.onDeath = function () { + spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); + spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); + spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); + const mag = 8 + const v1 = Vector.rotate({ + x: 1, + y: 1 + }, 2 * Math.PI * Math.random()) + const v2 = Vector.rotate({ + x: 1, + y: 1 + }, 2 * Math.PI * Math.random()) + const v3 = Vector.normalise(Vector.add(v1, v2)) //last vector is opposite the sum of the other two to look a bit like momentum is conserved + + Matter.Body.setVelocity(mob[mob.length - 1], { + x: mag * v1.x, + y: mag * v1.y + }); + Matter.Body.setVelocity(mob[mob.length - 2], { + x: mag * v2.x, + y: mag * v2.y + }); + Matter.Body.setVelocity(mob[mob.length - 3], { + x: -mag * v3.x, + y: -mag * v3.y + }); + } + Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + me.timeLeft = 140 + Math.floor(Math.random() * 30); + me.g = 0.001; //required if using this.gravity + me.frictionAir = 0; + me.restitution = 1; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.do = function () { + this.gravity(); + this.timeLimit(); + }; + }, + sniper(x, y, radius = 35 + Math.ceil(Math.random() * 30)) { + mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)") + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + me.isVerticesChange = true + // Matter.Body.rotate(me, Math.PI) + me.stroke = "transparent"; //used for drawSneaker + me.alpha = 1; //used in drawSneaker + me.showHealthBar = false; + me.frictionStatic = 0; + me.friction = 0; + me.isNotCloaked = false; //used in drawSneaker + me.isBadTarget = true; + me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + + me.memory = 30 //140; + me.fireFreq = 0.005 + Math.random() * 0.002 + 0.0005 * simulation.difficulty; //larger = fire more often + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.0005 * simulation.accelScale; + me.frictionAir = 0.05; + me.torque = 0.0001 * me.inertia; + me.fireDir = { + x: 0, + y: 0 + }; + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + // spawn.shield(me, x, y); + me.do = function () { + // this.seePlayerByLookingAt(); + this.seePlayerCheck(); + this.checkStatus(); + + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + // this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + //rotate towards fireAngle + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, this.fireDir) + const threshold = 0.03; + if (dot > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.000004 * this.inertia; + } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { + //fire + spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 5); + const v = 10 + 8 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v + Math.random(), + y: this.velocity.y + this.fireDir.y * v + Math.random() + }); + this.noseLength = 0; + // recoil + this.force.x -= 0.005 * this.fireDir.x * this.mass; + this.force.y -= 0.005 * this.fireDir.y * this.mass; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + // else if (this.noseLength < -0.1) { + // this.noseLength += this.fireFreq / 4; + // setNoseShape(); + // } + + if (this.seePlayer.recall) { + if (this.alpha < 1) this.alpha += 0.01; + } else { + if (this.alpha > 0) this.alpha -= 0.03; + } + //draw + if (this.alpha > 0) { + if (this.alpha > 0.95) { + this.healthBar(); + if (!this.isNotCloaked) { + this.isNotCloaked = true; + this.isBadTarget = false; + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player + } + } + //draw body + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = `rgba(25,0,50,${this.alpha * this.alpha})`; + ctx.fill(); + } else if (this.isNotCloaked) { + this.isNotCloaked = false; + this.isBadTarget = true + this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player + } + }; + }, + sniperBullet(x, y, radius = 9, sides = 5) { //bullets + mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass * 20); + }; + Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + me.timeLeft = 120; + // me.g = 0.0005; //required if using this.gravity + me.frictionAir = 0; + me.restitution = 0; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.onDeath = function () { + if (simulation.difficulty > 11) { //explode AoE + const radius = 100 + 0.5 * simulation.difficulty + 50 * Math.random() + if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(this.position, player.position)) < radius) m.damage(0.0003 * radius * simulation.dmgScale); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: radius, + color: "rgba(255,0,155,0.5)", + time: simulation.drawTime + }); + } + }; + me.do = function () { + // this.gravity(); + this.timeLimit(); + if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { + this.isDropPowerUp = false; + this.death(); //death with no power up + } + }; + }, + launcherOne(x, y, radius = 30 + Math.ceil(Math.random() * 40)) { + mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.00004 * simulation.accelScale; + me.fireFreq = Math.floor(420 + 90 * Math.random() * simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.015; + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + Matter.Body.setAngularVelocity(this, 0.14) + spawn.seeker(this.vertices[0].x, this.vertices[0].y, 20, 9); //give the bullet a rotational velocity as if they were attached to a vertex + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + who.timeLeft = 840 //* (0.8 + 0.4 * Math.random()); + who.accelMag = 0.00035 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[0]))), -6) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + }; + }, + launcher(x, y, radius = 30 + Math.ceil(Math.random() * 40)) { + mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.00004 * simulation.accelScale; + me.fireFreq = Math.floor(420 + 90 * Math.random() * simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.02; + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + Matter.Body.setAngularVelocity(this, 0.14) + //fire a bullet from each vertex + for (let i = 0, len = this.vertices.length; i < len; i++) { + spawn.seeker(this.vertices[i].x, this.vertices[i].y, 7) + //give the bullet a rotational velocity as if they were attached to a vertex + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -8) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + } + }; + }, + launcherBoss(x, y, radius = 90, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 6, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.accelMag = 0.0001 * simulation.accelScale; + me.fireFreq = Math.floor(330 * simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.02; + me.memory = 420; + me.repulsionRange = 1000000; //squared + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + + me.onDeath = function () { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.onDamage = function () { }; + me.do = function () { + // this.armor(); + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + this.repulsion(); + if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + Matter.Body.setAngularVelocity(this, 0.11) + //fire a bullet from each vertex + for (let i = 0, len = this.vertices.length; i < len; i++) { + spawn.seeker(this.vertices[i].x, this.vertices[i].y, 8) + //give the bullet a rotational velocity as if they were attached to a vertex + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -10) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + } + }; + }, + // blowSuckBoss(x, y, radius = 80) { + // mobs.spawn(x, y, 10, radius, "transparent"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + // me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + // me.fireFreq = Math.floor(60 * simulation.CDScale) + // me.seePlayerFreq = 15 + // me.seeAtDistance2 = 300000; + // me.accelMag = 0.0003 * simulation.accelScale; + // if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search + // // Matter.Body.setDensity(me, 0.001); //normal is 0.001 //makes effective life much lower + // me.stroke = "transparent"; //used for drawGhost + // me.alpha = 1; //used in drawGhost + // me.isNotCloaked = false; //used in drawGhost + // me.isBadTarget = true; + // // me.leaveBody = false; + // me.collisionFilter.mask = cat.bullet //| cat.body + // me.showHealthBar = false; + // me.memory = 480; + // me.onDeath = function() { + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.onDamage = function() {}; + // me.suck = function() { + // for (let i = 0; i < mob.length; i++) { + // if (mob[i].isBlowSuckBullet) { + // const unit = Vector.normalise(Vector.sub(this.position, mob[i].position)) + // const mag = Vector.mult(unit, 0.0008 * mob[i].mass) + // mob[i].force.x += mag.x + // mob[i].force.y += mag.y + // } + // } + // } + // me.do = function() { + // //cap max speed + // if (this.speed > 8) { + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * 0.8, + // y: this.velocity.y * 0.8 + // }); + // } + // this.seePlayerCheckByDistance(); + // this.checkStatus(); + // this.attraction(); + // this.search(); + // //draw + // if (this.distanceToPlayer2() < this.seeAtDistance2) { + // if (this.alpha < 1) this.alpha += 0.005 * simulation.CDScale; //near player go solid + // } else { + // if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide + // } + // if (this.alpha > 0) { + // if (this.alpha > 0.8 && this.seePlayer.recall) { + // this.healthBar(); + // if (!this.isNotCloaked) { + // this.isNotCloaked = true; + // this.isBadTarget = false; + // this.collisionFilter.mask = cat.player | cat.bullet + // } + // } + // //draw body + // ctx.beginPath(); + // const vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) { + // ctx.lineTo(vertices[j].x, vertices[j].y); + // } + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 1; + // ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`; + // ctx.fill(); + + + // this.suck(); + // if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + // this.suckCount = 0; + // Matter.Body.setAngularVelocity(this, 0.11) + // //fire a bullet from each vertex + // for (let i = 0, len = this.vertices.length; i < len; i++) { + // spawn.bounceBullet(this.vertices[i].x, this.vertices[i].y, 8) + // //give the bullet a rotational velocity as if they were attached to a vertex + // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -16) + // const who = mob[mob.length - 1] + // who.isBlowSuckBullet = true + // Matter.Body.setVelocity(who, { + // x: this.velocity.x + velocity.x, + // y: this.velocity.y + velocity.y + // }); + // } + // } + + + // } else if (this.isNotCloaked) { + // this.isNotCloaked = false; + // this.isBadTarget = true; + // this.collisionFilter.mask = cat.bullet; //can't touch player or walls + // } + // }; + // }, + // blowSuckBoss(x, y, radius = 80, isSpawnBossPowerUp = true) { + // mobs.spawn(x, y, 10, radius, "transparent"); //rgb(60,60,85) + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + // me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + // me.accelMag = 0.0001 * simulation.accelScale; + // me.fireFreq = Math.floor(180 * simulation.CDScale) + // me.frictionStatic = 0; + // me.friction = 0; + // me.frictionAir = 0.005; + // me.memory = 420; + // me.repulsionRange = 1000000; //squared + // // spawn.shield(me, x, y, 1); + // // spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + + // me.onDeath = function() { + // if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.onDamage = function() {}; + // me.suck = function() { + // for (let i = 0; i < mob.length; i++) { + // if (mob[i].isBlowSuckBullet) { + // const unit = Vector.normalise(Vector.sub(this.position, mob[i].position)) + // const mag = Vector.mult(unit, 0.0008 * mob[i].mass) + // mob[i].force.x += mag.x + // mob[i].force.y += mag.y + // } + // } + // } + // me.do = function() { + // this.seePlayerCheck(); + // this.checkStatus(); + // this.attraction(); + // // this.repulsion(); + // this.suck(); + + // if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { + // this.suckCount = 0; + // Matter.Body.setAngularVelocity(this, 0.11) + // //fire a bullet from each vertex + // for (let i = 0, len = this.vertices.length; i < len; i++) { + // spawn.bounceBullet(this.vertices[i].x, this.vertices[i].y, 8) + // //give the bullet a rotational velocity as if they were attached to a vertex + // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -16) + // const who = mob[mob.length - 1] + // who.isBlowSuckBullet = true + // Matter.Body.setVelocity(who, { + // x: this.velocity.x + velocity.x, + // y: this.velocity.y + velocity.y + // }); + // } + // } + // }; + // }, + grenadierBoss(x, y, radius = 95) { + mobs.spawn(x, y, 6, radius, "rgb(0,235,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + + me.accelMag = 0.0001 * simulation.accelScale; + me.fireFreq = Math.floor(360 * simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.035; + me.memory = 420; + me.repulsionRange = 1200000; //squared + // spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50, 1); + spawn.spawnOrbitals(me, radius + 125, 1); + spawn.spawnOrbitals(me, radius + 200, 1); + Matter.Body.setDensity(me, 0.004 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + setTimeout(() => { //fix mob in place, but allow rotation + for (let i = 0, len = 6; i < len; i++) { + const speed = 2.25 * simulation.accelScale; + const angle = 2 * Math.PI * i / len + spawn.grenade(this.position.x, this.position.y, 170 * simulation.CDScale); + const who = mob[mob.length - 1] + Matter.Body.setVelocity(who, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }); + } + }, 200); + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + } + me.grenadeLimiter = 0 + me.onDamage = function () { + if (this.grenadeLimiter < 240 && this.health > 0) { + this.grenadeLimiter += 60 + spawn.grenade(this.position.x, this.position.y, 80 + Math.floor(60 * Math.random())); + who = mob[mob.length - 1] + const velocity = Vector.mult(Vector.normalise(Vector.sub(player.position, who.position)), 3 * Math.sqrt(simulation.accelScale) + 4 * Math.random()) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + // this.armor(); + if (this.grenadeLimiter > 1) this.grenadeLimiter-- + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + }; + }, + grenadier(x, y, radius = 35 + Math.ceil(Math.random() * 20)) { + mobs.spawn(x, y, 3, radius, "rgb(0,235,255)"); //rgb(255,100,200) + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front + me.isVerticesChange = true + // Matter.Body.rotate(me, Math.PI) + // me.stroke = "transparent"; //used for drawSneaker + me.frictionStatic = 0; + me.friction = 0; + me.memory = 60 //140; + me.fireFreq = 0.0055 + Math.random() * 0.0015; + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.0006 * simulation.accelScale; + me.frictionAir = 0.05; + me.torque = 0.0001 * me.inertia * (Math.random() > 0.5 ? -1 : 1) + me.fireDir = { + x: 0, + y: 0 + }; + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + spawn.grenade(this.position.x, this.position.y, 200 * simulation.CDScale); + // mob[mob.length - 1].collisionFilter.category = 0 + mob[mob.length - 1].collisionFilter.mask = cat.player | cat.map; + } + // spawn.shield(me, x, y); + me.do = function () { + this.seePlayerCheck(); + this.checkStatus(); + + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(simulation.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + // this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + //rotate towards fireAngle + const dot = Vector.dot({ + x: Math.cos(angle), + y: Math.sin(angle) + }, this.fireDir) + const threshold = 0.03; + if (dot > threshold) { + this.torque += 0.000004 * this.inertia; + } else if (dot < -threshold) { + this.torque -= 0.000004 * this.inertia; + } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { + //fire + spawn.grenade(this.vertices[1].x, this.vertices[1].y); + const v = 5 * simulation.accelScale; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v + Math.random(), + y: this.velocity.y + this.fireDir.y * v + Math.random() + }); + this.noseLength = 0; + // recoil + this.force.x -= 0.005 * this.fireDir.x * this.mass; + this.force.y -= 0.005 * this.fireDir.y * this.mass; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + }; + }, + grenade(x, y, lifeSpan = 90 + Math.ceil(60 / simulation.accelScale), pulseRadius = Math.min(550, 250 + simulation.difficulty * 3), size = 3) { + mobs.spawn(x, y, 4, size, "rgb(215,0,190)"); //rgb(215,80,190) + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass); + }; + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + + me.lifeSpan = lifeSpan; + me.timeLeft = me.lifeSpan; + // me.g = 0.0002; //required if using this.gravity + me.frictionAir = 0; + me.restitution = 0.8; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.onDeath = function () { + //damage player if in range + if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + m.damage(0.015 * simulation.dmgScale); + } + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: pulseRadius, + color: "rgba(255,0,220,0.3)", + time: simulation.drawTime + }); + }; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.map | cat.body | cat.player + // me.collisionFilter.mask = 0 + me.do = function () { + this.timeLimit(); + ctx.beginPath(); //draw explosion outline + ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay + ctx.fillStyle = "rgba(255,0,220,0.05)"; + ctx.fill(); + }; + }, + shieldingBoss(x, y, radius = 200) { + mobs.spawn(x, y, 9, radius, "rgb(150, 150, 255)"); + let me = mob[mob.length - 1]; + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 0.0001, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + }, 2000); //add in a delay in case the level gets flipped left right + + Matter.Body.rotate(me, Math.random() * 2 * Math.PI) + // me.stroke = "rgb(220,220,255)" + me.isBoss = true; + me.cycle = 0 + me.maxCycles = 110; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 1; + // me.homePosition = { x: x, y: y }; + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + + Matter.Body.setDensity(me, 0.0045); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.onDamage = function () { + this.cycle = 0 + }; + me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + Matter.Body.rotate(this, 0.003) //gently spin around + this.checkStatus(); + ctx.beginPath(); //draw cycle timer + ctx.moveTo(this.vertices[this.vertices.length - 1].x, this.vertices[this.vertices.length - 1].y) + const phase = (this.vertices.length + 1) * this.cycle / this.maxCycles + if (phase > 1) ctx.lineTo(this.vertices[0].x, this.vertices[0].y) + for (let i = 1; i < phase - 1; i++) { + ctx.lineTo(this.vertices[i].x, this.vertices[i].y) + } + ctx.lineWidth = 5 + ctx.strokeStyle = "rgb(255,255,255)" + ctx.stroke(); + + this.cycle++ + if (this.cycle > this.maxCycles) { + this.cycle = 0 + ctx.beginPath(); + for (let i = 0; i < mob.length; i++) { + if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp && mob[i].alive && !mob[i].isBoss) { + ctx.moveTo(this.position.x, this.position.y) + ctx.lineTo(mob[i].position.x, mob[i].position.y) + spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); + // me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + mob[mob.length - 1].damageReduction = 0.5 * 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //shields are extra strong + } + } + if (!this.isShielded && this.alive) spawn.shield(this, this.position.x, this.position.y, 1, true); + ctx.lineWidth = 20 + ctx.strokeStyle = "rgb(200,200,255)" + ctx.stroke(); + } + }; + }, + timeSkipBoss(x, y, radius = 50) { + mobs.spawn(x, y, 15, radius, "transparent"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.eventHorizon = 0; //set in mob loop + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.005; + me.accelMag = 0.00008 + 0.00002 * simulation.accelScale + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 100 * Math.random()) + + Matter.Body.setDensity(me, 0.0025); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // requestAnimationFrame(() => { simulation.timePlayerSkip(120) }); //wrapping in animation frame prevents errors, probably + }; + me.onDamage = function () { + //find side of mob closest to player + //causes lag for foam,laser too many seekers //maybe scale chance with dmg + // const where = Vector.add(this.position, Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.radius + 10)) + // spawn.seeker(where.x, where.y); //give the bullet a rotational velocity as if they were attached to a vertex + }; + me.do = function () { + this.seePlayerByHistory(60); + this.attraction(); + this.checkStatus(); + this.eventHorizon = 950 + 250 * Math.sin(simulation.cycle * 0.005) + if (!simulation.isTimeSkipping) { + if (Vector.magnitude(Vector.sub(this.position, m.pos)) < this.eventHorizon) { + this.attraction(); + this.damageReduction = this.startingDamageReduction + this.isInvulnerable = false + // if (!(simulation.cycle % 15)) requestAnimationFrame(() => { + // simulation.timePlayerSkip(5) + // // simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations + // }); //wrapping in animation frame prevents errors, probably + + // + // simulation.timePlayerSkip(1) + // m.walk_cycle += m.flipLegs * m.Vx //makes the legs look like they are moving fast + // }); //wrapping in animation frame prevents errors, probably + requestAnimationFrame(() => { + simulation.timePlayerSkip(1) + }); //wrapping in animation frame prevents errors, probably + + m.walk_cycle += m.flipLegs * m.Vx //makes the legs look like they are moving fast + + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "#fff"; + ctx.globalCompositeOperation = "destination-in"; //in or atop + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.stroke(); + ctx.clip(); + } else { + this.damageReduction = 0 + this.isInvulnerable = true + //prevents other things from being drawn later on in the draw cycle + requestAnimationFrame(() => { + simulation.camera(); + ctx.beginPath(); //gets rid of already draw shapes + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI, false); //part you can't see + ctx.fillStyle = document.body.style.backgroundColor; + ctx.fill(); + ctx.restore(); + }) + } + } + }; + }, + streamBoss(x, y, radius = 110) { + mobs.spawn(x, y, 5, radius, "rgb(245,180,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + // me.accelMag = 0.00023 * simulation.accelScale; + me.accelMag = 0.00008 * simulation.accelScale; + // me.fireFreq = Math.floor(30 * simulation.CDScale) + me.canFire = false; + me.closestVertex1 = 0; + me.closestVertex2 = 1; + me.cycle = 0 + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.022; + me.memory = 240; + me.repulsionRange = 1200000; //squared + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + + Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.onDamage = function () { }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function () { + // this.armor(); + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + this.repulsion(); + + this.cycle++ + if (this.seePlayer.recall && ((this.cycle % 15) === 0)) { + if (this.canFire) { + if (this.cycle > 100) { + this.cycle = 0 + this.canFire = false + // Matter.Body.setAngularVelocity(this, 0.1) + // const forceMag = 0.01 * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x -= 2 * forceMag * Math.cos(angle); + // this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity + } + spawn.seeker(this.vertices[this.closestVertex1].x, this.vertices[this.closestVertex1].y, 6) + Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex1])), -10) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + spawn.seeker(this.vertices[this.closestVertex2].x, this.vertices[this.closestVertex2].y, 6) + Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + const velocity2 = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex2])), -10) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity2.x, + y: this.velocity.y + velocity2.y + }); + } else if (this.cycle > 210) { + this.cycle = 0 + this.canFire = true + + //find closest 2 vertexes + let distance2 = Infinity + for (let i = 0; i < this.vertices.length; i++) { + const d = Vector.magnitudeSquared(Vector.sub(this.vertices[i], player.position)) + if (d < distance2) { + distance2 = d + this.closestVertex2 = this.closestVertex1 + this.closestVertex1 = i + } + } + if (this.closestVertex2 === this.closestVertex1) { + this.closestVertex2++ + if (this.closestVertex2 === this.vertices.length) this.closestVertex2 = 0 + } + } + } + }; + }, + seeker(x, y, radius = 8, sides = 6) { + //bullets + mobs.spawn(x, y, sides, radius, "rgb(255,0,255)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass * 20); + }; + Matter.Body.setDensity(me, 0.000015); //normal is 0.001 + me.timeLeft = 420 //* (0.8 + 0.4 * Math.random()); + me.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + me.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + me.restitution = 0.5; + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.do = function () { + // this.seePlayer.yes = false; + this.alwaysSeePlayer() + this.attraction(); + this.timeLimit(); + }; + }, + spawner(x, y, radius = 55 + Math.ceil(Math.random() * 50)) { + mobs.spawn(x, y, 4, radius, "rgb(255,150,0)"); + let me = mob[mob.length - 1]; + me.g = 0.0004; //required if using this.gravity + me.leaveBody = false; + // me.isDropPowerUp = false; + me.onDeath = function () { //run this function on death + for (let i = 0; i < Math.ceil(this.mass * 0.15 + Math.random() * 2.5); ++i) { + spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + (Math.random() - 0.5) * 15, + y: this.velocity.x + (Math.random() - 0.5) * 15 + }); + } + }; + spawn.shield(me, x, y); + me.do = function () { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + }; + }, + spawns(x, y, radius = 15) { + mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); + let me = mob[mob.length - 1]; + me.onHit = function () { //run this function on hitting player + this.explode(); + }; + // me.stroke = "transparent" + me.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.map | cat.mob + me.showHealthBar = false; + Matter.Body.setDensity(me, 0.0001); //normal is 0.001 + me.g = 0.00002; //required if using this.gravity + me.accelMag = 0.00012 * simulation.accelScale; + // me.memory = 30; + me.isDropPowerUp = false + me.leaveBody = false; + me.seePlayerFreq = Math.floor((80 + 50 * Math.random())); + me.frictionAir = 0.004; + me.do = function () { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + + // this.alwaysSeePlayer(); + // this.checkStatus(); + // this.attraction(); + }; + }, + // exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { + // mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); + // let me = mob[mob.length - 1]; + // me.onHit = function() { //run this function on hitting player + // this.explode(); + // }; + // me.g = 0.0003; //required if using this.gravity + // me.seePlayerFreq = 50 + Math.floor(Math.random() * 20) + // me.do = function() { + // this.gravity(); + // if (!(simulation.cycle % this.seePlayerFreq)) { + // if ( + // this.distanceToPlayer2() < this.seeAtDistance2 && + // Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && + // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && + // !m.isCloak + // ) { + // this.foundPlayer(); + // } else if (this.seePlayer.recall) { + // for (let i = 0; i < 20; i++) { + // let history = m.history[(m.cycle - 30 * i) % 600] + // if (Matter.Query.ray(map, this.position, history.position).length === 0) { + // this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //seconds before mob falls a sleep + // this.seePlayer.position.x = history.position.x; + // this.seePlayer.position.y = history.position.y; + + // ctx.beginPath(); + // ctx.moveTo(this.position.x, this.position.y); + // ctx.lineTo(history.position.x, history.position.y); + // ctx.lineWidth = 5; + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + + // break + // } + // } + // this.lostPlayer(); + // } + // } + // this.checkStatus(); + // this.attraction(); + // }; + // }, + exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { + mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); + let me = mob[mob.length - 1]; + me.onHit = function () { + //run this function on hitting player + this.explode(); + }; + me.g = 0.0004; //required if using this.gravity + me.do = function () { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + }; + }, + snakeSpitBoss(x, y, radius = 50) { + let angle = Math.PI + const tailRadius = 300 + + const color1 = "rgb(235,180,255)" + mobs.spawn(x + tailRadius * Math.cos(angle), y + tailRadius * Math.sin(angle), 8, radius, color1); //"rgb(55,170,170)" + let me = mob[mob.length - 1]; + me.isBoss = true; + me.accelMag = 0.0001 + 0.0004 * Math.sqrt(simulation.accelScale) + // me.accelMag = 0.0004 + 0.0002 * Math.sqrt(simulation.accelScale) + me.memory = 250; + me.laserRange = 500; + Matter.Body.setDensity(me, 0.0022 + 0.00022 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.startingDamageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0 + me.isInvulnerable = true + + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0, len = mob.length; i < len; i++) { + if (this.id === mob[i].snakeHeadID && mob[i].alive) mob[i].death() + } + }; + }; + me.canFire = false; + me.closestVertex1 = 0; + me.cycle = 0 + me.do = function () { + // this.armor(); + this.seePlayerByHistory(40) + this.checkStatus(); + this.attraction(); + this.cycle++ + if (this.seePlayer.recall && ((this.cycle % 10) === 0)) { + if (this.canFire) { + if (this.cycle > 120) { + this.cycle = 0 + this.canFire = false + // Matter.Body.setAngularVelocity(this, 0.1) + // const forceMag = 0.01 * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x -= 2 * forceMag * Math.cos(angle); + // this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity + } + spawn.seeker(this.vertices[this.closestVertex1].x, this.vertices[this.closestVertex1].y, 6) + Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.closestVertex1], this.position)), 15) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + // spawn.seeker(this.vertices[this.closestVertex2].x, this.vertices[this.closestVertex2].y, 6) + // Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + // const velocity2 = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex2])), -10) + // Matter.Body.setVelocity(mob[mob.length - 1], { + // x: this.velocity.x + velocity2.x, + // y: this.velocity.y + velocity2.y + // }); + } else if (this.cycle > 200) { + this.cycle = 0 + this.canFire = true + + //find closest 2 vertexes + let distance2 = Infinity + for (let i = 0; i < this.vertices.length; i++) { + const d = Vector.magnitudeSquared(Vector.sub(this.vertices[i], player.position)) + if (d < distance2) { + distance2 = d + // this.closestVertex2 = this.closestVertex1 + this.closestVertex1 = i + } + } + // if (this.closestVertex2 === this.closestVertex1) { + // this.closestVertex2++ + // if (this.closestVertex2 === this.vertices.length) this.closestVertex2 = 0 + // } + } + } + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(255,255,255,0.7)"; + ctx.stroke(); + } + }; + //extra space to give head room + angle -= 0.07 + let previousTailID = 0 + const nodes = Math.min(10 + Math.ceil(0.6 * simulation.difficulty), 60) + for (let i = 0; i < nodes; ++i) { + angle -= 0.1 + spawn.snakeBody(x + tailRadius * Math.cos(angle), y + tailRadius * Math.sin(angle), i === 0 ? 25 : 20); + if (i < 4) mob[mob.length - 1].snakeHeadID = me.id + mob[mob.length - 1].previousTailID = previousTailID + previousTailID = mob[mob.length - 1].id + } + const damping = 1 + const stiffness = 1 + this.constrain2AdjacentMobs(nodes, stiffness, false, damping); + for (let i = mob.length - 1, len = i - nodes; i > len; i--) { //set alternating colors + if (i % 2) { + mob[i].fill = "#778" + } else { + mob[i].fill = color1 + } + } + //constraint with first 3 mobs in line + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes + 1], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes + 2], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + }, + dragonFlyBoss(x, y, radius = 42) { //snake boss with a laser head + let angle = Math.PI + const tailRadius = 300 + mobs.spawn(x + tailRadius * Math.cos(angle), y + tailRadius * Math.sin(angle), 8, radius, "#000"); //"rgb(55,170,170)" + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.00165 + 0.00011 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.startingDamageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0 + me.isInvulnerable = true + + me.accelMag = 0.00008 + 0.00045 * Math.sqrt(simulation.accelScale) + me.memory = 250; + me.seePlayerFreq = 13 + me.flapRate = 0.4 + me.flapArc = 0.2 //don't go past 1.57 for normal flaps + me.wingLength = 150 + me.ellipticity = 0.3 + me.angleOff = 0.4 + + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0, len = mob.length; i < len; i++) { + if (this.id === mob[i].snakeHeadID && mob[i].alive) mob[i].death() + } + }; + me.do = function () { + this.seePlayerByHistory(40) + this.checkStatus(); + this.attraction(); + + let a //used to set the angle of wings + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 12; + ctx.strokeStyle = "rgba(255,255,255,0.9)"; + ctx.stroke(); + const sub = Vector.sub(this.position, this.snakeBody1.position) + a = Math.atan2(sub.y, sub.x) + } else { + a = Math.atan2(this.velocity.y, this.velocity.x) + } + + ctx.fillStyle = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + this.wing(a + Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) + this.wing(a - Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) + this.wing(a - Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) + this.wing(a + Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) + }; + + angle -= 0.07 + let previousTailID = 0 + const nodes = Math.min(10 + Math.ceil(0.6 * simulation.difficulty), 60) + for (let i = 0; i < nodes; ++i) { + angle -= 0.1 + spawn.snakeBody(x + tailRadius * Math.cos(angle), y + tailRadius * Math.sin(angle), i === 0 ? 25 : 20); + const who = mob[mob.length - 1] + who.fill = `hsl(${160 + 40 * Math.random()}, 100%, ${5 + 25 * Math.random() * Math.random()}%)` + if (i < 4) who.snakeHeadID = me.id + if (i === 0) me.snakeBody1 = who //track this segment, so the difference in position between this segment and the head can be used to angle the wings + who.previousTailID = previousTailID + previousTailID = who.id + } + const damping = 1 + const stiffness = 1 + this.constrain2AdjacentMobs(nodes, stiffness, false, damping); + //constraint with first few mobs in tail + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes + 1], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - nodes + 2], + bodyB: mob[mob.length - 1 - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + }, + snakeBody(x, y, radius = 10) { + mobs.spawn(x, y, 8, radius, "rgba(0,180,180,0.4)"); + let me = mob[mob.length - 1]; + me.collisionFilter.mask = cat.bullet | cat.player //| cat.mob //| cat.body + me.damageReduction = 0.028 + Matter.Body.setDensity(me, 0.0001); //normal is 0.001 + + // me.accelMag = 0.0007 * simulation.accelScale; + me.leaveBody = Math.random() < 0.33 ? true : false; + me.showHealthBar = false; + me.isDropPowerUp = false; + me.frictionAir = 0; + me.isSnakeTail = true; + me.stroke = "transparent" + me.onDeath = function () { + setTimeout(() => { + for (let i = 0, len = mob.length; i < len; i++) { + if (this.id === mob[i].previousTailID && mob[i].alive) mob[i].death() + if (this.snakeHeadID === mob[i].id) { + mob[i].isInvulnerable = false + mob[i].damageReduction = mob[i].startingDamageReduction + } + } + }, 500); + }; + me.do = function () { + this.checkStatus(); + }; + // me.doActive = function() { + // this.checkStatus(); + // this.alwaysSeePlayer(); + // this.attraction(); + // }; + }, + tetherBoss(x, y, constraint, radius = 90) { + // constrained mob boss for the towers level + // often has a ring of mobs around it + mobs.spawn(x, y, 8, radius, "rgb(0,60,80)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.0005 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + me.g = 0.0001; //required if using this.gravity + me.accelMag = 0.0012 + 0.0013 * simulation.accelScale; + me.memory = 20; + me.repulsionRange = 1800 * 1800 + + cons[cons.length] = Constraint.create({ + pointA: { x: constraint.x, y: constraint.y }, + bodyB: me, + stiffness: 0.00012 + }); + Composite.add(engine.world, cons[cons.length - 1]); + + spawn.shield(me, x, y, 1); + setTimeout(() => { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + this.removeCons(); //remove constraint + me.babies(0.05 * simulation.difficulty + 1) + }; + me.babies = function (len) { + const delay = Math.max(3, Math.floor(15 - len / 2)) + let i = 0 + let spawnFlutters = () => { + if (i < len) { + if (!(simulation.cycle % delay) && !simulation.paused && !simulation.isChoosing && m.alive) { + // const phase = i / len * 2 * Math.PI + // const where = Vector.add(this.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5)) + const unit = Vector.normalise(Vector.sub(player.position, this.position)) + const velocity = Vector.mult(unit, 10 + 10 * Math.random()) + const where = Vector.add(this.position, Vector.mult(unit, radius * 1.2)) + spawn.allowShields = false + spawn.flutter(where.x, where.y, Math.floor(9 + 8 * Math.random())) + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.01); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setVelocity(who, velocity); + Matter.Body.setAngle(who, Math.atan2(velocity.y, velocity.x)) + + this.alertNearByMobs(); + spawn.allowShields = true + i++ + } + requestAnimationFrame(spawnFlutters); + } + } + requestAnimationFrame(spawnFlutters); + } + me.onDamage = function () { + if (this.health < this.nextHealthThreshold && this.alive) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 + this.invulnerableCount = 90 + Math.floor(30 * Math.random()) + this.isInvulnerable = true + this.damageReduction = 0 + } + }; + me.do = function () { + this.gravity(); + if (this.isInvulnerable) { + this.repulsion(); + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + this.frictionAir = 0.05 + me.babies(0.07 * simulation.difficulty + 2) + if (this.radius > 15) { + const scale = 0.88; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + } + }; + }, + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + if (this.allowShields && Math.random() < chance) { + mobs.spawn(x, y, 9, target.radius + 30, "rgba(220,220,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(220,220,255)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.shield = true; + me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.isUnblockable = true + me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: target, //attach shield to target + stiffness: 0.4, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + me.onDamage = function () { + //make sure the mob that owns the shield can tell when damage is done + this.alertNearByMobs(); + this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + + me.shieldTargetID = target.id + target.isShielded = true; + target.shieldID = me.id + me.onDeath = function () { + //clear isShielded status from target + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; + } + }; + me.do = function () { + this.checkStatus(); + }; + + mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically + + //swap order of shield and mob, so that mob is behind shield graphically + // mob[mob.length - 1] = mob[mob.length - 2]; + // mob[mob.length - 2] = me; + } + }, + groupShield(targets, x, y, radius, stiffness = 0.4) { + const nodes = targets.length + mobs.spawn(x, y, 9, radius, "rgba(220,220,255,0.9)"); + let me = mob[mob.length - 1]; + me.stroke = "rgb(220,220,255)"; + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion + me.frictionAir = 0; + me.shield = true; + me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.collisionFilter.category = cat.mobShield + me.collisionFilter.mask = cat.bullet; + for (let i = 0; i < nodes; ++i) { + mob[mob.length - i - 2].isShielded = true; + //constrain to all mob nodes in group + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 2], + stiffness: stiffness, + damping: 0.1 + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + me.onDamage = function () { + this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done + this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` + }; + me.onDeath = function () { + //clear isShielded status from target + for (let j = 0; j < targets.length; j++) { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === targets[j]) mob[i].isShielded = false; + } + } + }; + me.leaveBody = false; + me.isDropPowerUp = false; + me.showHealthBar = false; + mob[mob.length - 1] = mob[mob.length - 1 - nodes]; + mob[mob.length - 1 - nodes] = me; + me.do = function () { + this.checkStatus(); + }; + }, + spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { + if (Math.random() < chance) { + // simulation.difficulty = 50 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + const offSet = 6.28 * Math.random() + for (let i = 0; i < len; i++) spawn.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) + } + }, + orbital(who, radius, phase, speed) { + // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) + mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(255,0,150)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.01); //normal is 0.001 + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isUnstable = true; //dies when blocked + me.showHealthBar = false; + me.isOrbital = true; + // me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function () { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) + //damage player + if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles + const dmg = 0.03 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + this.death(); + } + }; + }, + orbitalBoss(x, y, radius = 70) { + const nodeBalance = Math.random() + const nodes = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) + mobs.spawn(x, y, nodes, radius, "rgb(255,0,150)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.0017 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + + me.stroke = "transparent"; //used for drawGhost + me.seeAtDistance2 = 2000000; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.frictionAir = 0.04; + me.accelMag = 0.0002 + 0.00015 * simulation.accelScale + spawn.shield(me, x, y, 1); + + const rangeInnerVsOuter = Math.random() + let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + let range = radius + 350 + 200 * rangeInnerVsOuter + nodes * 7 + for (let i = 0; i < nodes; i++) spawn.orbital(me, range, i / nodes * 2 * Math.PI, speed) + const orbitalIndexes = [] //find indexes for all the current nodes + for (let i = 0; i < nodes; i++) orbitalIndexes.push(mob.length - 1 - i) + // add orbitals for each orbital + range = Math.max(60, 100 + 100 * Math.random() - nodes * 3 - rangeInnerVsOuter * 80) + speed = speed * (1.25 + 2 * Math.random()) + const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty))) + for (let j = 0; j < nodes; j++) { + for (let i = 0, len = subNodes; i < len; i++) spawn.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) + } + for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); + + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.do = function () { + this.seePlayerByHistory(); + this.checkStatus(); + this.attraction(); + }; + }, + //complex constrained mob templates********************************************************************** + //******************************************************************************************************* + allowShields: true, + nodeGroup( + x, + y, + spawn = "striker", + nodes = Math.min(2 + Math.ceil(Math.random() * (simulation.difficulty + 2)), 8), + //Math.ceil(Math.random() * 3) + Math.min(4,Math.ceil(simulation.difficulty/2)), + radius = Math.ceil(Math.random() * 10) + 18, // radius of each node mob + sideLength = Math.ceil(Math.random() * 100) + 70, // distance between each node mob + stiffness = Math.random() * 0.03 + 0.005 + ) { + this.allowShields = false; //don't want shields on individual group mobs + const angle = 2 * Math.PI / nodes + let targets = [] + for (let i = 0; i < nodes; ++i) { + let whoSpawn = spawn; + if (spawn === "random") { + whoSpawn = this.fullPickList[Math.floor(Math.random() * this.fullPickList.length)]; + } else if (spawn === "randomList") { + whoSpawn = this.pickList[Math.floor(Math.random() * this.pickList.length)]; + } + this[whoSpawn](x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius); + targets.push(mob[mob.length - 1].id) //track who is in the group, for shields + } + if (Math.random() < 0.3) { + this.constrain2AdjacentMobs(nodes, stiffness * 2, true); + } else { + this.constrainAllMobCombos(nodes, stiffness); + } + //spawn shield for entire group + if (nodes > 2 && Math.random() < 0.998) { + this.groupShield(targets, x, y, sideLength + 2.5 * radius + nodes * 6 - 25); + } + this.allowShields = true; + }, + lineGroup( + x, + y, + spawn = "striker", + nodes = Math.min(3 + Math.ceil(Math.random() * simulation.difficulty + 2), 8), + //Math.ceil(Math.random() * 3) + Math.min(4,Math.ceil(simulation.difficulty/2)), + radius = Math.ceil(Math.random() * 10) + 17, + l = Math.ceil(Math.random() * 80) + 30, + stiffness = Math.random() * 0.06 + 0.01 + ) { + this.allowShields = false; //don't want shields on individual group mobs + for (let i = 0; i < nodes; ++i) { + let whoSpawn = spawn; + if (spawn === "random") { + whoSpawn = this.fullPickList[Math.floor(Math.random() * this.fullPickList.length)]; + } else if (spawn === "randomList") { + whoSpawn = this.pickList[Math.floor(Math.random() * this.pickList.length)]; + } + this[whoSpawn](x + i * radius + i * l, y, radius); + } + this.constrain2AdjacentMobs(nodes, stiffness); + this.allowShields = true; + }, + //constraints ************************************************************************************************ + //************************************************************************************************************* + constrainAllMobCombos(nodes, stiffness) { + //runs through every combination of last 'num' bodies and constrains them + for (let i = 1; i < nodes + 1; ++i) { + for (let j = i + 1; j < nodes + 1; ++j) { + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - i], + bodyB: mob[mob.length - j], + stiffness: stiffness + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + } + }, + constrain2AdjacentMobs(nodes, stiffness, loop = false, damping = 0) { + //runs through every combination of last 'num' bodies and constrains them + for (let i = 0; i < nodes - 1; ++i) { + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - i - 1], + bodyB: mob[mob.length - i - 2], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + if (nodes > 2) { + for (let i = 0; i < nodes - 2; ++i) { + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - i - 1], + bodyB: mob[mob.length - i - 3], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + } + //optional connect the tail to head + if (loop && nodes > 3) { + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - 1], + bodyB: mob[mob.length - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - 2], + bodyB: mob[mob.length - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - 1], + bodyB: mob[mob.length - nodes + 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + }, + constraintPB(x, y, bodyIndex, stiffness) { + cons[cons.length] = Constraint.create({ + pointA: { + x: x, + y: y + }, + bodyB: body[bodyIndex], + stiffness: stiffness + }); + Composite.add(engine.world, cons[cons.length - 1]); + }, + constraintBB(bodyIndexA, bodyIndexB, stiffness) { + consBB[consBB.length] = Constraint.create({ + bodyA: body[bodyIndexA], + bodyB: body[bodyIndexB], + stiffness: stiffness + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + }, + // body and map spawns ****************************************************************************** + //********************************************************************************************** + wireHead() { + //not a mob, just a graphic for level 1 + const breakingPoint = 1300 + mobs.spawn(breakingPoint, -100, 0, 7.5, "transparent"); + let me = mob[mob.length - 1]; + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.inertia = Infinity; + me.g = 0.0004; //required for gravity + me.restitution = 0; + me.stroke = "transparent" + me.freeOfWires = false; + me.frictionStatic = 1; + me.friction = 1; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + + me.do = function () { + let wireX = -50; + let wireY = -1000; + if (this.freeOfWires) { + this.gravity(); + } else { + if (m.pos.x > breakingPoint) { + this.freeOfWires = true; + this.fill = "#000" + this.force.x += -0.003; + player.force.x += 0.06; + // player.force.y -= 0.15; + } + + //player is extra heavy from wires + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.3 + }) + + //player friction from the wires + if (m.pos.x > 700 && player.velocity.x > -2) { + let wireFriction = 0.75 * Math.min(0.6, Math.max(0, 100 / (breakingPoint - m.pos.x))); + if (!m.onGround) wireFriction *= 3 + Matter.Body.setVelocity(player, { + x: player.velocity.x - wireFriction, + y: player.velocity.y + }) + } + //move to player + Matter.Body.setPosition(this, { + x: m.pos.x + (42 * Math.cos(m.angle + Math.PI)), + y: m.pos.y + (42 * Math.sin(m.angle + Math.PI)) + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, 0, this.position.x, this.position.y); + if (!this.freeOfWires) ctx.lineTo(m.pos.x + (30 * Math.cos(m.angle + Math.PI)), m.pos.y + (30 * Math.sin(m.angle + Math.PI))); + ctx.lineCap = "butt"; + ctx.lineWidth = 15; + ctx.strokeStyle = "#000"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + }, + wireKnee() { + //not a mob, just a graphic for level 1 + const breakingPoint = 1425 + mobs.spawn(breakingPoint, -100, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + //touch nothing + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + // me.restitution = 0; + me.stroke = "transparent" + // me.inertia = Infinity; + me.restitution = 0; + me.freeOfWires = false; + me.frictionStatic = 1; + me.friction = 1; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + + me.do = function () { + let wireX = -50 - 20; + let wireY = -1000; + + if (this.freeOfWires) { + this.gravity(); + } else { + if (m.pos.x > breakingPoint) { + this.freeOfWires = true; + this.force.x -= 0.0004; + this.fill = "#222"; + } + //move mob to player + m.calcLeg(0, 0); + Matter.Body.setPosition(this, { + x: m.pos.x + m.flipLegs * m.knee.x - 5, + y: m.pos.y + m.knee.y + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, 0, this.position.x, this.position.y); + ctx.lineWidth = 5; + ctx.strokeStyle = "#222"; + ctx.lineCap = "butt"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + }, + wireKneeLeft() { + //not a mob, just a graphic for level 1 + const breakingPoint = 1400 + mobs.spawn(breakingPoint, -100, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + //touch nothing + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + // me.restitution = 0; + me.stroke = "transparent" + // me.inertia = Infinity; + me.restitution = 0; + me.freeOfWires = false; + me.frictionStatic = 1; + me.friction = 1; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + + me.do = function () { + let wireX = -50 - 35; + let wireY = -1000; + + if (this.freeOfWires) { + this.gravity(); + } else { + if (m.pos.x > breakingPoint) { + this.freeOfWires = true; + this.force.x += -0.0003; + this.fill = "#333"; + } + //move mob to player + m.calcLeg(Math.PI, -3); + Matter.Body.setPosition(this, { + x: m.pos.x + m.flipLegs * m.knee.x - 5, + y: m.pos.y + m.knee.y + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, 0, this.position.x, this.position.y); + ctx.lineWidth = 5; + ctx.lineCap = "butt"; + ctx.strokeStyle = "#333"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + }, + wireFoot() { + //not a mob, just a graphic for level 1 + const breakingPoint = 1350 + mobs.spawn(breakingPoint, -100, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + //touch nothing + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + me.restitution = 0; + me.stroke = "transparent" + // me.inertia = Infinity; + me.freeOfWires = false; + // me.frictionStatic = 1; + // me.friction = 1; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + + me.do = function () { + let wireX = -50 + 16; + let wireY = -1000; + + if (this.freeOfWires) { + this.gravity(); + } else { + if (m.pos.x > breakingPoint) { + this.freeOfWires = true; + this.force.x += -0.0006; + this.fill = "#111"; + } + //move mob to player + m.calcLeg(0, 0); + Matter.Body.setPosition(this, { + x: m.pos.x + m.flipLegs * m.foot.x - 5, + y: m.pos.y + m.foot.y - 1 + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, 0, this.position.x, this.position.y); + ctx.lineWidth = 5; + ctx.lineCap = "butt"; + ctx.strokeStyle = "#111"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + }, + wireFootLeft() { + //not a mob, just a graphic for level 1 + const breakingPoint = 1325 + mobs.spawn(breakingPoint, -100, 0, 2, "transparent"); + let me = mob[mob.length - 1]; + //touch nothing + me.collisionFilter.category = cat.body; + me.collisionFilter.mask = cat.map; + me.g = 0.0003; //required for gravity + me.restitution = 0; + me.stroke = "transparent" + // me.inertia = Infinity; + me.freeOfWires = false; + // me.frictionStatic = 1; + // me.friction = 1; + me.frictionAir = 0.01; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.isBadTarget = true; + me.isUnblockable = true; + + me.do = function () { + let wireX = -50 + 26; + let wireY = -1000; + + if (this.freeOfWires) { + this.gravity(); + } else { + if (m.pos.x > breakingPoint) { + this.freeOfWires = true; + this.force.x += -0.0005; + this.fill = "#222"; + } + //move mob to player + m.calcLeg(Math.PI, -3); + Matter.Body.setPosition(this, { + x: m.pos.x + m.flipLegs * m.foot.x - 5, + y: m.pos.y + m.foot.y - 1 + }) + } + //draw wire + ctx.beginPath(); + ctx.moveTo(wireX, wireY); + ctx.quadraticCurveTo(wireX, 0, this.position.x, this.position.y); + ctx.lineWidth = 5; + ctx.strokeStyle = "#222"; + ctx.lineCap = "butt"; + ctx.stroke(); + ctx.lineCap = "round"; + }; + }, + boost(x, y, height = 1000) { + spawn.mapVertex(x + 50, y + 35, "120 40 -120 40 -50 -40 50 -40"); + level.addQueryRegion(x, y - 20, 100, 20, "boost", [ + [player], body, mob, powerUp, bullet + ], -1.21 * Math.sqrt(Math.abs(height))); + }, + blockDoor(x, y, blockSize = 60) { + spawn.mapRect(x, y - 290, 40, 60); // door lip + spawn.mapRect(x, y, 40, 50); // door lip + for (let i = 0; i < 4; ++i) { + spawn.bodyRect(x + 5, y - 260 + i * blockSize + i * 3, 30, blockSize); + } + }, + debris(x, y, width, number = Math.floor(2 + Math.random() * 9)) { + for (let i = 0; i < number; ++i) { + if (Math.random() < 0.15) { + powerUps.chooseRandomPowerUp(x + Math.random() * width, y); + } else { + const size = 18 + Math.random() * 25; + spawn.bodyRect(x + Math.random() * width, y, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); + // body[body.length] = Bodies.rectangle(x + Math.random() * width, y, size * (0.6 + Math.random()), size * (0.6 + Math.random())); + } + } + }, + // bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) { + // if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); + // }, + // bodyVertex(x, y, vector, properties) { //adds shape to body array + // body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); + // }, + bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) { //this is the command that adds blocks to the world in the middle of a level + if (Math.random() < chance) { + body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); + const who = body[body.length - 1] + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + Composite.add(engine.world, who); //add to world + who.classType = "body" + } + }, + bodyVertex(x, y, vector, properties) { //this is the command that adds blocks to the world in the middle of a level + body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); + const who = body[body.length - 1] + who.collisionFilter.category = cat.body; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + Composite.add(engine.world, who); //add to world + who.classType = "body" + }, + mapRect(x, y, width, height, properties) { //adds rectangle to map array + map[map.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); + }, + mapVertex(x, y, vector, properties) { //adds shape to map array + map[map.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); + }, + mapRectNow(x, y, width, height, properties, isRedrawMap = true) { //adds rectangle to map array in the middle of a level + map[map.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); + const who = map[map.length - 1] + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + if (isRedrawMap) simulation.draw.setPaths() + }, + mapVertexNow(x, y, vector, properties, isRedrawMap = true) { //adds shape to map array in the middle of a level + map[map.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); + const who = map[map.length - 1] + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + if (isRedrawMap) simulation.draw.setPaths() //this is a bit slow on processing so maybe it's better to run after you spawn several different shapes + }, + //complex map templates + spawnBuilding(x, y, w, h, leftDoor, rightDoor, walledSide) { + this.mapRect(x, y, w, 25); //roof + this.mapRect(x, y + h, w, 35); //ground + if (walledSide === "left") { + this.mapRect(x, y, 25, h); //wall left + } else { + this.mapRect(x, y, 25, h - 150); //wall left + if (leftDoor) { + this.bodyRect(x + 5, y + h - 150, 15, 150, this.propsFriction); //door left + } + } + if (walledSide === "right") { + this.mapRect(x - 25 + w, y, 25, h); //wall right + } else { + this.mapRect(x - 25 + w, y, 25, h - 150); //wall right + if (rightDoor) { + this.bodyRect(x + w - 20, y + h - 150, 15, 150, this.propsFriction); //door right + } + } + }, + spawnStairs(x, y, num, w, h, stepRight) { + w += 50; + if (stepRight) { + for (let i = 0; i < num; i++) { + this.mapRect(x - (w / num) * (1 + i), y - h + (i * h) / num, w / num + 50, h - (i * h) / num + 50); + } + } else { + for (let i = 0; i < num; i++) { + this.mapRect(x + (i * w) / num, y - h + (i * h) / num, w / num + 50, h - (i * h) / num + 50); + } + } + }, + //pre-made property options************************************************************************************* + //************************************************************************************************************* + //Object.assign({}, propsHeavy, propsBouncy, propsNoRotation) //will combine properties into a new object + propsFriction: { + friction: 0.5, + frictionAir: 0.02, + frictionStatic: 1 + }, + propsFrictionMedium: { + friction: 0.15, + frictionStatic: 1 + }, + propsBouncy: { + friction: 0, + frictionAir: 0, + frictionStatic: 0, + restitution: 1 + }, + propsSlide: { + friction: 0.003, + frictionStatic: 0.4, + restitution: 0, + density: 0.002 + }, + propsLight: { + density: 0.001 + }, + propsOverBouncy: { + friction: 0, + frictionAir: 0, + frictionStatic: 0, + restitution: 1.05 + }, + propsHeavy: { + density: 0.01 //default density is 0.001 + }, + propsIsNotHoldable: { + isNotHoldable: true + }, + propsNoRotation: { + inertia: Infinity //prevents rotation + }, + propsHoist: { + inertia: Infinity, //prevents rotation + frictionAir: 0.001, + friction: 0.0001, + frictionStatic: 0, + restitution: 0, + isNotHoldable: true + // density: 0.0001 + }, + propsDoor: { + density: 0.001, //default density is 0.001 + friction: 0, + frictionAir: 0.03, + frictionStatic: 0, + restitution: 0 + }, + sandPaper: { + friction: 1, + frictionStatic: 1, + restitution: 0 + } +}; \ No newline at end of file diff --git a/ngon/js/tech.js b/ngon/js/tech.js new file mode 100644 index 00000000..ed3614e7 --- /dev/null +++ b/ngon/js/tech.js @@ -0,0 +1,11769 @@ +const tech = { + totalCount: null, + setupAllTech() { + tech.damage = 1 + for (let i = 0, len = tech.tech.length; i < len; i++) { + tech.tech[i].isLost = false + tech.tech[i].isBanished = false + tech.tech[i].remove(); + tech.tech[i].count = 0 + if (tech.tech[i].isJunk) { + tech.tech[i].frequency = 0 + } else if (tech.tech[i].frequencyDefault) { + tech.tech[i].frequency = tech.tech[i].frequencyDefault + } else { + tech.tech[i].frequency = 2 + } + } + //remove lore if it's your first time playing since it's confusing + //also remove lore if cheating + lore.techCount = 0; + if (simulation.isCheating || localSettings.runCount < 1) { //simulation.isCommunityMaps || + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isLore) { + tech.tech[i].frequency = 0; + tech.tech[i].count = 0; + } + } + } + // tech.removeJunkTechFromPool(); + // tech.removeLoreTechFromPool(); + // tech.addLoreTechToPool(); + tech.extraMaxHealth = 0; + tech.totalCount = 0; + tech.junkCount = 0 //tech.countJunkTech(); + simulation.updateTechHUD(); + simulation.updateGunHUD(); + }, + removeTech(index = 'random') { + if (index === 'random') { + const have = [] //find which tech you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + } + if (have.length) { + index = have[Math.floor(Math.random() * have.length)] + } else { + return 0 //if none found don't remove any tech + } + } else if (isNaN(index)) { //find index by name + let found = false; + for (let i = 0; i < tech.tech.length; i++) { + if (index === tech.tech[i].name) { + index = i; + found = true; + break; + } + } + if (!found) return 0 //if name not found don't remove any tech + } + if (tech.tech[index].count === 0) return 0 + const totalRemoved = tech.tech[index].count + simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`, 360) + tech.tech[index].remove(); + tech.tech[index].count = 0; + tech.totalCount -= totalRemoved + tech.countJunkTech(); + simulation.updateTechHUD(); + tech.tech[index].isLost = true + simulation.updateTechHUD(); + return totalRemoved //return the total number of tech removed + }, + // onclick="tech.removeTechPaused(${i}, this)" //add this to tech elements in pause menu + // removeTechPaused(index, who) { + // tech.tech[index].remove(); + // tech.tech[index].count = 0; + // simulation.updateTechHUD(); + // who.innerHTML = "removed" + // // who.style.display = "none" + // }, + // removeLoreTechFromPool() { + // for (let i = tech.tech.length - 1; i > 0; i--) { + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) + // } + // }, + addJunkTechToPool(percent) { //percent is number between 0-1 + //make an array for possible junk tech to add + let options = []; + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); + } + if (options.length) { + let countNonJunk = 0 // count total non junk tech + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk) countNonJunk += tech.tech[i].frequency + } + const num = Math.ceil(percent * countNonJunk) //scale number added + for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ //add random array options to tech pool + simulation.makeTextLog(`tech.tech.push(${num.toFixed(0)} JUNK)`) + return num + } else { + return 0 + } + }, + removeJunkTechFromPool(num = 1) { + for (let j = 0; j < num; j++) { + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { + tech.tech[i].frequency-- + break + } + } + } + }, + giveRandomJUNK() { + const list = [] + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isJunk) list.push(tech.tech[i].name) + } + let name = list[Math.floor(Math.random() * list.length)] + tech.giveTech(name) + simulation.makeTextLog(`tech.giveTech("${name}")`); + }, + + giveTech(index = 'random') { + if (index === 'random') { + let options = []; + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && !tech.tech[i].isLore && !tech.tech[i].isBadRandomOption) options.push(i); + } + // give a random tech from the tech I don't have + if (options.length > 0) { + let newTech = options[Math.floor(Math.random() * options.length)] + tech.giveTech(newTech) + simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); + } + } else { + if (isNaN(index)) { //find index by name + let found = false; + for (let i = 0; i < tech.tech.length; i++) { + if (index === tech.tech[i].name) { + index = i; + found = true; + break; + } + } + if (!found) return //if name not found don't give any tech + } + if (tech.isMetaAnalysis && tech.tech[index].isJunk) { + simulation.makeTextLog(`//tech: meta-analysis replaced junk tech with random tech`); + tech.giveTech('random') + for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * Math.random(), m.pos.y + 40 * Math.random(), "research"); + return + } + + if (tech.tech[index].isLost) tech.tech[index].isLost = false; //give specific tech + if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false //stops the bug where you can't gets stacks of tech you take with decoherence, I think + tech.tech[index].effect(); //give specific tech + tech.tech[index].count++ + tech.totalCount++ //used in power up randomization + tech.countJunkTech(); + simulation.updateTechHUD(); + } + }, + junkCount: 0, + countJunkTech() { + tech.junkCount = 0 + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && tech.tech[i].isJunk) tech.junkCount++ + } + }, + // setTechoNonRefundable(name) { + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech.name === name) { + // tech.tech[i].isNonRefundable = true; + // return + // } + // } + // }, + setCheating() { + if (!simulation.isCheating) { + simulation.isCheating = true; + level.levelAnnounce(); + lore.techCount = 0; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isLore) { + tech.tech[i].frequency = 0; + tech.tech[i].count = 0; + } + } + console.log('cheating') + sound.tone(250) + sound.tone(300) + sound.tone(375) + } + }, + haveGunCheck(name, needActive = true) { + // if ( + // !build.isExperimentSelection && + // b.inventory.length > 2 && + // name !== b.guns[b.activeGun].name && + // Math.random() > 2 - b.inventory.length * 0.5 + // ) { + // return false + // } + // for (i = 0, len = b.inventory.length; i < len; i++) { + // if (b.guns[b.inventory[i]].name === name) return true + // } + // return false + if (build.isExperimentSelection || !needActive) { + for (i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].name === name) return true + } + return false + } else { //must be holding gun, this is the standard while playing + return b.inventory.length > 0 && b.guns[b.activeGun].name === name + } + }, + hasExplosiveDamageCheck() { + return tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isBoomBotUpgrade || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) + }, + damage: 1, //used for tech changes to player damage that don't have complex conditions + damageFromTech() { + let dmg = tech.damage //m.fieldDamage + // if (tech.isDivisor) { + // for (let i = 0; i < b.inventory.length; i++) { + // if (b.guns[b.inventory[i]].ammo % 3 === 0) { + // dmg *= 1.44 + // break + // } + // } + // } + if (tech.isDivisor && b.activeGun && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.77 + if (tech.isNoGroundDamage) dmg *= m.onGround ? 0.78 : 1.88 + if (tech.isDilate) dmg *= 1.5 + 0.6 * Math.sin(m.cycle * 0.0075) + if (tech.isGunChoice && tech.buffedGun === b.inventoryGun) dmg *= 1 + 0.31 * b.inventory.length + if (powerUps.boost.endCycle > m.cycle) dmg *= 1 + powerUps.boost.damage + if (m.coupling && (m.fieldMode === 0 || m.fieldMode === 5)) dmg *= 1 + 0.015 * m.coupling + if (m.isSneakAttack && m.sneakAttackCycle + Math.min(120, 0.5 * (m.cycle - m.enterCloakCycle)) > m.cycle) dmg *= 4.33 * (1 + 0.033 * m.coupling) + if (tech.deathSkipTime) dmg *= 1 + 0.6 * tech.deathSkipTime + if (tech.isTechDebt) dmg *= tech.totalCount > 20 ? Math.pow(0.85, tech.totalCount - 20) : 4 - 0.15 * tech.totalCount // if (tech.isTechDebt) dmg *= Math.min(Math.pow(0.85, tech.totalCount - 20), 4 - 0.15 * tech.totalCount) + if (tech.isFlipFlopDamage && tech.isFlipFlopOn) dmg *= 1.555 + if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.3703599 + if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance()) + if (tech.isDamageForGuns) dmg *= 1 + 0.22 * Math.max(0, b.inventory.length - 1) + if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25 + if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; + if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.03 * powerUps.research.count) + if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots() + if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage + if (tech.isLowEnergyDamage) dmg *= 1 + 0.7 * Math.max(0, 1 - m.energy) + if (tech.energyDamage) dmg *= 1 + m.energy * 0.22 * tech.energyDamage; + if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007 + if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 + if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165) + if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.83 + if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.defense() + if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; + if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0 + if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) + return dmg + }, + duplicationChance() { + return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.17 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + 0.08 * tech.isDuplicateMobs + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0))) + }, + isScaleMobsWithDuplication: false, + maxDuplicationEvent() { + if (tech.is100Duplicate && tech.duplicationChance() > 0.99) { + tech.is100Duplicate = false + const range = 1300 + tech.isScaleMobsWithDuplication = true + for (let i = 0, len = 9; i < len; i++) { + const angle = 2 * Math.PI * i / len + spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); + } + spawn.historyBoss(0, 0) + spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) + spawn.blockBoss(level.enter.x, level.enter.y) + tech.isScaleMobsWithDuplication = false + } + }, + setTechFrequency(name, frequency) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].name === name) tech.tech[i].frequency = frequency + } + }, + setBotTechFrequency(f = 0) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) { + switch (tech.tech[i].name) { + case "dynamo-bot": + tech.tech[i].frequency = f + break; + case "orbital-bot": + tech.tech[i].frequency = f + break; + case "laser-bot": + tech.tech[i].frequency = f + break; + case "boom-bot": + tech.tech[i].frequency = f + break; + case "foam-bot": + tech.tech[i].frequency = f + break; + case "nail-bot": + tech.tech[i].frequency = f + break; + } + } + } + }, + tech: [{ + name: "tungsten carbide", + description: "+222 maximum health
lose health after hard landings", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin + }, + requires: "not skin", + effect() { + tech.hardLanding = 70 + tech.isFallingDamage = true; + m.setMaxHealth(); + m.addHealth(2.22 / simulation.healScale) + m.skin.tungsten() + }, + remove() { + tech.hardLanding = 130 + tech.isFallingDamage = false; + m.setMaxHealth(); + if (this.count) m.resetSkin(); + } + }, + { + name: "nitinol", + description: "+33% movement and jumping
+22% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin + }, + requires: "not skinned", + effect() { + m.skin.mech(); + tech.hardLanding = 110 + tech.squirrelFx += 0.4; + tech.squirrelJump += 0.16; + m.setMovement() + }, + remove() { + tech.hardLanding = 130 + tech.squirrelFx = 1; + tech.squirrelJump = 1; + m.setMovement() + if (this.count) m.resetSkin(); + } + }, + { + name: "aperture", + description: "every 6 seconds your damage cycles
between -10% and +110% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin + }, + requires: "not skinned", + effect() { + tech.isDilate = true + m.skin.dilate() + }, + remove() { + tech.isDilate = false + if (this.count) m.resetSkin(); + } + }, + { + name: "diaphragm", + description: "every 6 seconds your defense cycles
between +8% and +80% defense", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isSkin: true, + allowed() { + return tech.isDilate + }, + requires: "aperture", + effect() { + tech.isDiaphragm = true + m.resetSkin(); + m.skin.dilate2() + }, + remove() { + tech.isDiaphragm = false + if (this.count) m.resetSkin(); + } + }, + { + name: "mass-energy equivalence", + // description: "energy protects you instead of health
√ of defense reduction reduces max energy", + description: "energy protects you instead of health
exponentially reduced defense (~ x^0.19)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isAnnihilation //&& !tech.isAmmoFromHealth && !tech.isRewindGun + }, + requires: "not piezoelectricity, CPT, annihilation", + effect() { + m.health = 0 + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("dmg").style.backgroundColor = "#0cf"; + tech.isEnergyHealth = true; + simulation.mobDmgColor = "rgba(0, 255, 255,0.6)" //"#0cf" + m.displayHealth(); + m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar + m.skin.energy(); + }, + remove() { + if (tech.isEnergyHealth) { + tech.isEnergyHealth = false; + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline" + document.getElementById("dmg").style.backgroundColor = "#f67"; + m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); + simulation.mobDmgColor = "rgba(255,0,0,0.7)" + m.displayHealth(); + m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar + m.resetSkin(); + } + tech.isEnergyHealth = false; + } + }, + { + name: "1st ionization energy", + link: `1st ionization energy`, + // description: `after you collect ${powerUps.orb.heal()}
+${0.1 * tech.largerHeals} maximum energy`, + // descriptionFunction: `convert current and future ${powerUps.orb.heal()} into

give +${10 * tech.largerHeals} maximum energy`, + descriptionFunction() { + return `convert current and future
into

give +${8 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` + }, + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isEnergyHealth + }, + requires: "mass-energy equivalence", + effect() { + powerUps.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up + powerUps.heal.color = "#ff0" //"#0ae" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + }, + remove() { + powerUps.healGiveMaxEnergy = false; + // tech.healMaxEnergyBonus = 0 + powerUps.heal.color = "#0eb" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + } + }, + { + name: "CPT symmetry", + // description: "charge, parity, and time invert to undo defense
rewind (1.5—5) seconds for (66—220) energy", + // description: "after losing health, if you have full energy
rewind time for 44 energy per second", + descriptionFunction() { + return `after losing health, if you have ${(100 * Math.min(100, m.maxEnergy)).toFixed(0)} energy
rewind time for 20 energy per second` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isRewindField && !tech.isEnergyHealth + }, + requires: "not skinned, standing wave, max energy reduction, retrocausality, mass-energy", + effect() { + tech.isRewindAvoidDeath = true; + m.skin.CPT() + }, + remove() { + tech.isRewindAvoidDeath = false; + if (this.count) m.resetSkin(); + } + }, + { + name: "causality bots", + link: `causality bots`, + description: "when you rewind build scrap bots
that protect you for about 9 seconds", + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindBot++; + }, + remove() { + tech.isRewindBot = 0; + } + }, + { + name: "causality bombs", + link: `causality bombs`, + description: "when you rewind drop several grenades", //
become invulnerable until they explode + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindGrenade = true; + }, + remove() { + tech.isRewindGrenade = false; + } + }, + { + name: "ternary", //"divisor", + descriptionFunction() { + return `+77% damage while your current gun
has ammo divisible by 3` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + // divisible: 3, // + Math.floor(6 * Math.random()), + effect() { + tech.isDivisor = true; + }, + remove() { + tech.isDivisor = false; + } + }, + { + name: "ordnance", + description: "double the frequency of finding guntech
spawn a gun and +7% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed: () => true, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 + } + this.refundAmount += tech.addJunkTechToPool(0.07) + }, + refundAmount: 0, + remove() { + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "ad hoc", + descriptionFunction() { + return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, field, gun, or tech
for each of your guns` + }, + maxCount: 1, //random power up + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + for (let i = 0; i < b.inventory.length; i++) { + if (Math.random() < 1 / 6) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + } else if (Math.random() < 1 / 5) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); + } else if (Math.random() < 1 / 4) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + } else if (Math.random() < 1 / 3) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); + } else if (Math.random() < 1 / 2) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); + } else { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); + } + } + }, + remove() { } + }, + { + name: "applied science", + description: `get a random guntech
for each of your guns`, //spawn ${powerUps.orb.research(1)} and + maxCount: 9, + count: 0, + isNonRefundable: true, + frequency: 2, + frequencyDefault: 2, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns + const gunTechPool = [] //find gun tech for this gun + for (let j = 0, len = tech.tech.length; j < len; j++) { + // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) + const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works + b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active + if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name + const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + } + b.activeGun = originalActiveGunIndex + if (!b.guns[b.activeGun].have) { + if (b.inventory.length === 0) { + b.activeGun = null + } else { + b.activeGun = b.inventory[0] + } + b.inventoryGun = 0; + } + } + if (gunTechPool.length) { + const index = Math.floor(Math.random() * gunTechPool.length) + tech.giveTech(gunTechPool[index]) // choose from the gun pool + tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift + simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) + } + } + simulation.boldActiveGunHUD(); + }, + remove() { } + }, + { + name: "arsenal", + descriptionFunction() { + return `+22% damage per unequipped gun (${(22 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => b.inventory.length > 1, + requires: "at least 2 guns", + effect() { + tech.isDamageForGuns = true; + }, + remove() { + tech.isDamageForGuns = false; + } + }, + { + name: "active cooling", + descriptionFunction() { + return `+28% fire rate per unequipped gun (${(28 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` + }, //
but not including your equipped gun` }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => b.inventory.length > 1, + requires: "at least 2 guns", + effect() { + tech.isFireRateForGuns = true; + b.setFireCD(); + }, + remove() { + tech.isFireRateForGuns = false; + b.setFireCD(); + } + }, + { + name: "pigeonhole principle", + descriptionFunction() { + let info = "" + if (this.count > 0 && Number.isInteger(tech.buffedGun) && b.inventory.length) { + let gun = b.guns[b.inventory[tech.buffedGun]].name + info = `
this level: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}` + } + return ` + a new gun is chosen to be improved each level +
+31% damage per gun for the chosen gun${info}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + tech.isGunChoice = true + //switches gun on new level + //generalist uses the same chosen gun so they match + }, + remove() { + tech.isGunChoice = false; + } + }, + { + name: "generalist", + description: "spawn 7 guns, but you can't switch guns
your equipped gun cycles after each level", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return b.inventory.length < b.guns.length - 5 && b.inventory.length > 1 + }, + requires: "at least 2 guns, at least 5 unclaimed guns", + effect() { + tech.isGunCycle = true; + for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + }, + remove() { + if (!this.count) tech.isGunCycle = false; // only set to false if you don't have this tech + // if (tech.isGunCycle) { + // for (let i = 0; i < 8; i++) { + // if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun + // } + // tech.isGunCycle = false; + // } + } + }, + { + name: "supply chain", + descriptionFunction() { + return `double your current ammo
+4% JUNK to tech pool` + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) + } + simulation.makeGunHUD(); + this.refundAmount += tech.addJunkTechToPool(0.04) + }, + refundAmount: 0, + remove() { + for (let j = 0; j < this.count; j++) { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) + } + } + simulation.makeGunHUD(); + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "logistics", + description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not non-renewables", + effect() { + tech.isAmmoForGun = true; + }, + remove() { + tech.isAmmoForGun = false; + } + }, + { + name: "cache", + link: `cache`, + description: `${powerUps.orb.ammo()} give 1500% more ammo, but
you can't store any more ammo than that`, + // ammo powerups always max out your gun, + // but the maximum ammo ti limited + // description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not non-renewables", + effect() { + tech.ammoCap = 15; + powerUps.ammo.effect() + }, + remove() { + tech.ammoCap = 0; + } + }, + { + name: "catabolism", + descriptionFunction() { + return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not non-renewables", + effect() { + tech.isAmmoFromHealth = true; + }, + remove() { + tech.isAmmoFromHealth = false; + } + }, + { + name: "non-renewables", + description: `+78% damage
${powerUps.orb.ammo()} can't spawn`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo + }, + requires: "not catabolism, quasiparticles", + damage: 1.78, + effect() { + tech.damage *= this.damage + tech.isEnergyNoAmmo = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isEnergyNoAmmo = false; + } + }, + { + name: "desublimated ammunition", + link: `desublimated ammunition`, + description: `if crouching
alternating shots use no ammo`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.crouchAmmoCount = true + }, + remove() { + tech.crouchAmmoCount = false; + } + }, + { + name: "gun turret", + description: "if crouching
+66% defense ", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isTurret = true + }, + remove() { + tech.isTurret = false; + } + }, + { + name: "dead reckoning", + description: "if your speed is 0
+50% damage", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.restDamage += 0.5 + }, + remove() { + tech.restDamage = 1; + } + }, + { + name: "kinetic bombardment", + description: "far away mobs take more damage
up to +33% damage at 3000 displacement", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isFarAwayDmg = true; //used in mob.damage() + }, + remove() { + tech.isFarAwayDmg = false; + } + }, + { + name: "Higgs mechanism", + description: "+45% fire rate
while firing your position is fixed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !m.isShipMode && !tech.isAlwaysFire, !tech.isGrapple + }, + requires: "not ship mode, automatic, grappling hook", + effect() { + tech.isFireMoveLock = true; + b.setFireCD(); + b.setFireMethod(); + }, + remove() { + if (tech.isFireMoveLock) { + tech.isFireMoveLock = false + b.setFireCD(); + b.setFireMethod(); + } + } + }, + { + name: "integrated armament", + link: `integrated armament`, + description: `+25% damage, but new guns replace
your current gun and convert guntech
`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length === 1 + }, + requires: "only 1 gun", + effect() { + tech.isOneGun = true; + }, + remove() { + tech.isOneGun = false; + } + }, + { + name: "mechatronics", + descriptionFunction() { + let damageTotal = 1 + for (let i = 0; i < this.damageSoFar.length; i++) damageTotal *= this.damageSoFar[i] + let currentDamage = "" + if (this.count) currentDamage = `
(+${(100 * (damageTotal - 1)).toFixed(0)}%)` + return `gain between +7% and +13% damage` + currentDamage + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + damage: 1.1, + damageSoFar: [], //tracks the random damage upgrades so it can be removed and in descriptionFunction + effect() { + const damage = (Math.floor((Math.random() * 0.07 + 0.07 + 1) * 100)) / 100 + tech.damage *= damage + this.damageSoFar.push(damage) + }, + remove() { + for (let i = 0; i < this.damageSoFar.length; i++) tech.damage /= this.damageSoFar[i] + this.damageSoFar.length = 0 + } + }, + // { + // name: "coyote", + // description: "", + // maxCount: 1, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // allowed() { return true }, + // requires: "", + // effect() { // good with melee builds, content skipping builds + // tech.coyoteTime = 120 + // // simulation.gravity = function() { + // // function addGravity(bodies, magnitude) { + // // for (var i = 0; i < bodies.length; i++) { + // // bodies[i].force.y += bodies[i].mass * magnitude; + // // } + // // } + // // if (!m.isBodiesAsleep) { + // // addGravity(powerUp, simulation.g); + // // addGravity(body, simulation.g); + // // } + // // player.force.y += player.mass * simulation.g + // // } + // }, + // remove() { + // tech.coyoteTime = 5 + // } + // }, + { + name: "Newtons 1st law", + description: "defense is proportional to your speed
up to +66% defense at 40 speed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isSpeedHarm = true //max at speed = 40 + }, + remove() { + tech.isSpeedHarm = false + } + }, + { + name: "Newtons 2nd law", + description: "damage is proportional to your speed
up to +66% damage at 40 speed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isSpeedDamage = true //max at speed = 40 + }, + remove() { + tech.isSpeedDamage = false + } + }, + { + name: "microstates", + link: `microstates`, + description: "for each active bullet or bot
+0.7% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isDamageFromBulletCount = true + }, + remove() { + tech.isDamageFromBulletCount = false + } + }, + { + name: "regression", + description: "bullet collisions increase vulnerability to
damage by +5% for mobs (+0.25% for bosses)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isLessDamageReduction = true + }, + remove() { + tech.isLessDamageReduction = false + } + }, + { + name: "simulated annealing", + description: "+20% damage
–20% fire rate", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + damage: 1.2, + effect() { + tech.damage *= this.damage + tech.slowFire = 1.2 + b.setFireCD(); + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.slowFire = 1; + b.setFireCD(); + } + }, + { + name: "heuristics", + description: "+22% fire rate
spawn a gun", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.fireRate *= 0.78 + b.setFireCD(); + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + }, + remove() { + tech.fireRate = 1; + b.setFireCD(); + } + }, + { + name: "anti-shear topology", + link: `anti-shear topology`, + description: "your bullets last +30% longer", //
drone spore worm flea missile foam wave neutron ice", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.bulletsLastLonger += 0.3 + }, + remove() { + tech.bulletsLastLonger = 1; + } + }, + { + name: "fracture analysis", + description: "if a mob is stunned it takes
+400% damage from bullet impacts", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isStun + }, + requires: "a stun effect", + effect() { + tech.isCrit = true; + }, + remove() { + tech.isCrit = false; + } + }, + { + name: "shear stress", + description: "after mobs die
they fire a nail at nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.nailsDeathMob++ + }, + remove() { + tech.nailsDeathMob = 0; + } + }, + { + name: "thermal runaway", + description: "after mobs die
they explode", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.isExplodeMob = true; + }, + remove() { + tech.isExplodeMob = false; + } + }, + { + name: "zoospore vector", + link: `zoospore vector`, + descriptionFunction() { + return `after mobs die there is a +10% chance
they grow ${b.guns[6].nameString('s')}` + }, + // description: "after mobs die
they have a +10% chance to grow spores", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.sporesOnDeath += 0.1; + // if (tech.isSporeWorm) { + // for (let i = 0; i < 4; i++) b.worm(m.pos) + // } else { + // for (let i = 0; i < 8; i++) b.spore(m.pos) + // } + }, + remove() { + tech.sporesOnDeath = 0; + } + }, + { + name: "propagator", + description: "after mobs die advance time 0.5 seconds
+60% damage", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.deathSkipTime++ + }, + remove() { + tech.deathSkipTime = 0 + } + }, + { + name: "collider", + descriptionFunction() { + return `after mobs die there is a +50% chance to
collide power ups to form different power ups` + // return `after mobs die there is a +33% chance to convert
${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, ${powerUps.orb.research(1)}, tech, field, gun into other types` + }, + + maxCount: 2, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.collidePowerUps += 0.5 + }, + remove() { + tech.collidePowerUps = 0 + } + }, + { + name: "bubble fusion", + descriptionFunction() { + return `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isShieldAmmo = true; + }, + remove() { + tech.isShieldAmmo = false; + } + }, + { + name: "reaction inhibitor", + description: "-11% maximum mob health", //health + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.botSpawner || tech.isMobBlockFling || tech.iceIXOnDeath + }, + requires: "", //"any mob death tech", + effect() { + tech.mobSpawnWithHealth++ + mobs.setMobSpawnHealth() + //set all mobs at full health to 0.85 + for (let i = 0; i < mob.length; i++) { + if (mob.health > mobs.mobSpawnWithHealth) mob.health = mobs.mobSpawnWithHealth + } + }, + remove() { + tech.mobSpawnWithHealth = 0 + mobs.setMobSpawnHealth() + } + }, + { + name: "scrap bots", + link: `scrap bots`, + description: "after mobs die you have a +33% chance
to build scrap bots that operate for 13 seconds", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.botSpawner += 0.33; + }, + remove() { + tech.botSpawner = 0; + } + }, + { + name: "scrap refit", + link: `scrap refit`, + description: "after mobs die
reset scrap bots to 13 seconds of operation", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.botSpawner + }, + requires: "scrap bots", + effect() { + tech.isBotSpawnerReset = true; + }, + remove() { + tech.isBotSpawnerReset = false; + } + }, + { + name: "nail-bot", + link: `nail-bot`, + description: "a bot fires nails at mobs in line of sight", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.nailBotCount++; + b.nailBot(); + }, + remove() { + if (this.count) { + tech.nailBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "nail-bot upgrade", + link: `nail-bot upgrade`, + description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.nailBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more nail bots and no other bot upgrade", + effect() { + tech.isNailBotUpgrade = true + b.convertBotsTo("nail-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("nail-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isNailBotUpgrade = false + } + }, + { + name: "foam-bot", + link: `foam-bot`, + description: "a bot fires foam at nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.foamBotCount++; + b.foamBot(); + }, + remove() { + if (this.count) { + tech.foamBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "foam-bot upgrade", + link: `foam-bot upgrade`, + description: "convert your bots to foam-bots
+300% foam size and fire rate", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.foamBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more foam bots and no other bot upgrade", + effect() { + tech.isFoamBotUpgrade = true + b.convertBotsTo("foam-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("foam-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isFoamBotUpgrade = false + } + }, + { + name: "sound-bot", + link: `sound-bot`, + description: "a bot emits expanding arcs
aimed towards nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + tech.soundBotCount++; + b.soundBot(); + }, + remove() { + if (this.count) { + tech.soundBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "sound-bot upgrade", + link: `sound-bot upgrade`, + description: "convert your bots to sound-bots
+150% wave fire rate and +150% damage", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.soundBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more sound bots and no other bot upgrade", + effect() { + tech.isSoundBotUpgrade = true + b.convertBotsTo("sound-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'sound') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("sound-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'sound') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isSoundBotUpgrade = false + } + }, + { + name: "boom-bot", + link: `boom-bot`, + description: "a bot defends the space around you
ignites an explosion after hitting a mob", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.boomBotCount++; + b.boomBot(); + }, + remove() { + if (this.count) { + tech.boomBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "boom-bot upgrade", + link: `boom-bot upgrade`, + description: "convert your bots to boom-bots
+300% explosion damage and size", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.boomBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more boom bots and no other bot upgrade", + effect() { + tech.isBoomBotUpgrade = true + b.convertBotsTo("boom-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("boom-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isBoomBotUpgrade = false + } + }, + { + name: "laser-bot", + link: `laser-bot`, + description: "a bot uses energy to emit a laser beam
that targets nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return m.maxEnergy > 0.5 + }, + requires: "maximum energy above 50", + effect() { + tech.laserBotCount++; + b.laserBot(); + }, + remove() { + if (this.count) { + tech.laserBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "laser-bot upgrade", + link: `laser-bot upgrade`, + description: "convert your bots to laser-bots
+100% damage, efficiency, and range", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.laserBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more laser bots and no other bot upgrade", + effect() { + tech.isLaserBotUpgrade = true + b.convertBotsTo("laser-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("laser-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isLaserBotUpgrade = false + } + }, + { + name: "orbital-bot", + link: `orbital-bot`, + description: "a bot is locked in orbit around you
stuns and damages mobs on contact", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true + }, + requires: "", + effect() { + b.orbitBot(); + tech.orbitBotCount++; + }, + remove() { + if (this.count) { + tech.orbitBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "orbital-bot upgrade", + link: `orbital-bot upgrade`, + description: "convert your bots to orbital-bots
+300% orbital damage and +50% radius", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.orbitBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more orbital bots and no other bot upgrade", + effect() { + tech.isOrbitBotUpgrade = true + b.convertBotsTo("orbital-bot") + const range = 190 + 120 * tech.isOrbitBotUpgrade + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].isUpgraded = true + bullet[i].range = range + bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) + } + } + tech.setBotTechFrequency() + tech.setTechFrequency("orbital-bot", 5) + }, + remove() { + if (this.count) { + const range = 190 + 100 * tech.isOrbitBotUpgrade + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].range = range + bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) + } + } + tech.setBotTechFrequency(1) + } + tech.isOrbitBotUpgrade = false + } + }, + { + name: "dynamo-bot", + link: `dynamo-bot`, + description: "a bot damages mobs while it traces your path
when it's near generate +8 energy per second", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.dynamoBotCount++; + b.dynamoBot(); + }, + remove() { + if (this.count) { + tech.dynamoBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "dynamo-bot upgrade", + link: `dynamo-bot upgrade`, + description: "convert your bots to dynamo-bots
when it's near generate +24 energy per second", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more dynamo bots and no other bot upgrade", + effect() { + tech.isDynamoBotUpgrade = true + b.convertBotsTo("dynamo-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("dynamo-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isDynamoBotUpgrade = false + } + }, + { + name: "perimeter defense", + description: "for each permanent bot
+6% defense", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 1 + }, + requires: "at least 2 bots", + effect() { + tech.isBotArmor = true + }, + remove() { + tech.isBotArmor = false + } + }, + { + name: "network effect", + description: "for each permanent bot
+6% damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 1 + }, + requires: "at least 2 bots", + effect() { + tech.isBotDamage = true + }, + remove() { + tech.isBotDamage = false + } + }, + { + name: "bot fabrication", + link: `bot fabrication`, + descriptionFunction() { + return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + }, + // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return powerUps.research.count > 1 || build.isExperimentSelection + }, + requires: "at least 2 research", + effect() { + tech.isRerollBots = true; + powerUps.research.changeRerolls(0) + simulation.makeTextLog(`m.research = 0`) + }, + remove() { + tech.isRerollBots = false; + // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + } + }, + { + name: "ersatz bots", + link: `ersatz bots`, + description: "double your current permanent bots
remove all guns in your inventory", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + // isNonRefundable: true, + isBadRandomOption: true, + numberOfGunsLost: 0, + allowed() { + return b.totalBots() > 3 && !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE, at least 4 bots", + effect() { + this.numberOfGunsLost = b.inventory.length + b.removeAllGuns(); + simulation.makeGunHUD(); + //double bots + for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); + tech.nailBotCount *= 2 + for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); + tech.laserBotCount *= 2 + for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); + tech.foamBotCount *= 2 + for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); + tech.boomBotCount *= 2 + for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); + tech.orbitBotCount *= 2 + for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); + tech.dynamoBotCount *= 2 + for (let i = 0; i < tech.soundBotCount; i++) b.soundBot(); + tech.soundBotCount *= 2 + for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); + tech.plasmaBotCount *= 2 + for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); + tech.missileBotCount *= 2 + }, + remove() { + if (this.count) { + //return guns + for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + this.numberOfGunsLost = 0; + + //half all current guns + tech.nailBotCount = Math.round(tech.nailBotCount / 2) + tech.laserBotCount = Math.round(tech.laserBotCount / 2) + tech.foamBotCount = Math.round(tech.foamBotCount / 2) + tech.soundBotCount = Math.round(tech.soundBotCount / 2) + tech.boomBotCount = Math.round(tech.boomBotCount / 2) + tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) + tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) + tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) + tech.missileBotCount = Math.round(tech.missileBotCount / 2) + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + // { + // name: "robotics", + // description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, + // maxCount: 1, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // isBotTech: true, + // allowed() { + // return b.totalBots() > 1 || build.isExperimentSelection + // }, + // requires: "at least 2 bots", + // effect: () => { + // b.randomBot() + // b.randomBot() + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 + // } + // }, + // remove() { + // if (this.count > 0) { + // b.removeBot() + // b.removeBot() + // b.clearPermanentBots(); + // b.respawnBots(); + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) + // } + // } + // } + // }, + { + name: "robotics", + description: `spawn 2 random bots
tech, fields, and guns have +1 bot choice`, //tech have an extra bot tech option + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return b.totalBots() > 1 + }, + requires: "at least 2 bots", + effect() { + tech.isExtraBotOption = true + for (let i = 0; i < 2; i++) b.randomBot() + }, + remove() { + if (this.count > 0) { + for (let i = 0; i < 2; i++) b.removeBot() + b.clearPermanentBots(); + b.respawnBots(); + } + tech.isExtraBotOption = false + } + }, + { + name: "open-source", //digital fabricator + description: `spawn 3 random bots
triple the frequency of finding bot tech`, + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isBotTech: true, + allowed() { + return tech.isExtraBotOption + }, + requires: "robotics", + effect() { + for (let i = 0; i < 3; i++) b.randomBot() + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 + } + }, + remove() { + if (this.count > 0) { + for (let i = 0; i < 3; i++) b.removeBot() + b.clearPermanentBots(); + b.respawnBots(); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) + } + } + } + }, + { + name: "decorrelation", + description: "if your gun or field are unused for 2 seconds
+70% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRewindField + }, + requires: "not retrocausality", + effect() { + tech.isNoFireDefense = true + }, + remove() { + tech.isNoFireDefense = false + } + }, + { + name: "anticorrelation", + description: "if your gun or field are unused for 2 seconds
+100% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRewindField + }, + requires: "not retrocausality", + effect() { + tech.isNoFireDamage = true + }, + remove() { + tech.isNoFireDamage = false + } + }, + { + name: "mass driver", + description: "+300% block collision damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldMode !== 9 && !tech.isTokamak + }, + requires: "not wormhole, tokamak", + effect() { + tech.blockDamage = 0.3 + }, + remove() { + tech.blockDamage = 0.075 + } + }, + { + name: "inflation", + link: `inflation`, + description: "if holding a block +90% defense
after throwing a block it expands 200%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak, wormhole", + effect() { + tech.isAddBlockMass = true + }, + remove() { + tech.isAddBlockMass = false + } + }, + { + name: "restitution", + description: "+150% block collision damage
after throwing a block it becomes very bouncy", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak, wormhole", + effect() { + tech.isBlockRestitution = true + }, + remove() { + tech.isBlockRestitution = false + } + }, + { + name: "flywheel", + description: "+150% block collision damage
after a mob dies its block is flung at mobs", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.blockDamage > 0.075 || tech.isPrinter) && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath + }, + requires: "mass driver, no other mob death tech", + effect() { + tech.isMobBlockFling = true + }, + remove() { + tech.isMobBlockFling = false + } + }, + // { + // name: "fermions", + // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (tech.blockDamage > 0.075 || m.fieldMode === 8) && !tech.isTokamak + // }, + // requires: "mass driver or pilot wave, not tokamak", + // effect() { + // tech.isBlockBullets = true + // }, + // remove() { + // tech.isBlockBullets = false + // } + // }, + { + name: "buckling", + descriptionFunction() { + return `if a block you threw kills a mob
spawn either ${powerUps.orb.coupling(1)}, ${powerUps.orb.boost(1)}, ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak", + effect() { + tech.isBlockPowerUps = true + }, + remove() { + tech.isBlockPowerUps = false + } + }, + { + name: "NOR gate", + description: "if flip-flop is OFF
become invulnerable to your next collision", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop + }, + requires: "flip-flop", + effect() { + tech.isFlipFlopHarm = true //do you have this tech + }, + remove() { + tech.isFlipFlopHarm = false + } + }, + { + name: "shape-memory alloy", + descriptionFunction() { + return `if flip-flop is ON
+400 maximum health and +100% ${powerUps.orb.heal()} effect` + }, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop + }, + requires: "flip-flop", + effect() { + tech.isFlipFlopHealth = true; + m.setMaxHealth(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + }, + remove() { + tech.isFlipFlopHealth = false; + m.setMaxHealth(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + } + }, + { + name: "flip-flop", + link: `flip-flop`, + description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-flip-flop")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-flip-flop").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-flip-flop").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" + } + } + }, 100); + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRelay + }, + requires: "not relay switch", + effect() { + tech.isFlipFlop = true //do you have this tech? + if (!tech.isFlipFlopOn) { + tech.isFlipFlopOn = true //what is the state of flip-Flop? + } + // if (!m.isShipMode) { + // m.skin.flipFlop() + // } + }, + remove() { + tech.isFlipFlop = false + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false //what is the state of flip-Flop? + } + m.eyeFillColor = 'transparent' + // m.resetSkin(); + } + }, + { + name: "NAND gate", + description: "if ON
+55.5% damage", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop || tech.isRelay + }, + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopDamage = true; + }, + remove() { + tech.isFlipFlopDamage = false; + } + }, + { + name: "integrated circuit", + description: "if ON +7 power up choices
if OFF -1 power up choices", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.isFlipFlop || tech.isRelay) && !tech.isDeterminism + }, + requires: "ON/OFF tech, not determinism", + effect() { + tech.isFlipFlopChoices = true //do you have this tech + }, + remove() { + tech.isFlipFlopChoices = false + } + }, + { + name: "transistor", + description: "if ON generate +20 energy per second
if OFF drain -1 energy per second", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop || tech.isRelay + }, + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopEnergy = true; + }, + remove() { + tech.isFlipFlopEnergy = false; + } + }, + // { + // name: "decoupling", + // link: `decoupling`, + // descriptionFunction() { + // //(${ m.couplingDescription(this.bonus)}) + // return `if ON +5 coupling
if OFF a dangerous particle slowly chases you` + // }, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // bonus: 5, //coupling given + // allowed() { + // return tech.isFlipFlop || tech.isRelay + // }, + // requires: "ON/OFF tech", + // effect() { + // tech.isFlipFlopCoupling = true; + // if (tech.isFlipFlopOn) { + // m.couplingChange(this.bonus) + // } else { + // for (let i = 0; i < mob.length; i++) { + // if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP + // } + // spawn.WIMP() + // mob[mob.length - 1].isDecoupling = true //so you can find it to remove + // } + // }, + // remove() { + // tech.isFlipFlopCoupling = false; + // if (this.count) { + // if (tech.isFlipFlop || tech.isRelay) { + // if (tech.isFlipFlopOn) { + // m.couplingChange(-this.bonus) + // } else { + // for (let i = 0; i < mob.length; i++) { + // if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP + // } + // } + // } + // } + // } + // }, + { + name: "relay switch", + description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-switch")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-switch").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-switch").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" + } + } + }, 100); + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isFlipFlop + }, + requires: "not flip-flop", + effect() { + m.isAltSkin = true + tech.isRelay = true //do you have this tech? + if (!tech.isFlipFlopOn) { + tech.isFlipFlopOn = true //what is the state of flip-Flop? + } + // if (!m.isShipMode) { + // m.skin.flipFlop() + // } + }, + remove() { + tech.isRelay = false + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false //what is the state of flip-Flop? + } + m.eyeFillColor = 'transparent' + // m.resetSkin(); + } + }, + { + name: "lithium-ion", + description: "if relay switch is ON
+300 maximum energy", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isRelay + }, + requires: "relay switch", + effect() { + tech.isRelayEnergy = true + m.setMaxEnergy() + }, + remove() { + tech.isRelayEnergy = false + m.setMaxEnergy() + } + }, + { + name: "thermocouple", + description: "if relay switch is ON
condense 4-13 ice IX crystals per second", + maxCount: 9, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isRelay + }, + requires: "relay switch", + effect() { + tech.relayIce++ + }, + remove() { + tech.relayIce = 0 + } + }, + { + name: "first derivative", + descriptionFunction() { + return `while your first gun is equipped
+15% defense per gun (${(100 * (1 - 0.85 ** b.inventory.length)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isFirstDer = true + }, + remove() { + tech.isFirstDer = false; + } + }, + { + name: "MACHO", + description: "a massive but compact object slowly follows you
if you are inside the MACHO +60% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO + spawn.MACHO() + }, + remove() { + tech.isMACHO = false; + tech.isHarmMACHO = false; + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isMACHO) mob[i].alive = false; + } + } + }, + { + name: "axion", + description: "while inside the MACHO
defense increases damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMACHO + }, + requires: "MACHO", + effect() { + tech.isAxion = true + }, + remove() { + tech.isAxion = false + } + }, + { + name: "dark star", + description: "mobs inside the MACHO are damaged
increase MACHO radius by 15%", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMACHO + }, + requires: "MACHO", + effect() { + tech.isDarkStar = true + }, + remove() { + tech.isDarkStar = false + } + }, + { + name: "ablative drones", + description: "after losing health there is a chance
to rebuild your broken parts as drones", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isDroneOnDamage = true; + // for (let i = 0; i < 4; i++) b.drone() + }, + remove() { + tech.isDroneOnDamage = false; + } + }, + { + name: "non-Newtonian armor", + link: `non-Newtonian armor`, + description: "after mob collisions
+66% defense for 10 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHarmArmor = true; + }, + remove() { + tech.isHarmArmor = false; + } + }, + { + name: "Pauli exclusion", + description: `after mob collisions
become invulnerable for +3.5 seconds`, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + m.collisionImmuneCycles += 210; + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + }, + remove() { + m.collisionImmuneCycles = 30; + } + }, + { + name: "spin–statistics theorem", + description: `every 7 seconds
become invulnerable for +1.9 seconds`, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //m.collisionImmuneCycles > 30 + }, + requires: "", + effect() { + tech.cyclicImmunity += 114; + }, + remove() { + tech.cyclicImmunity = 0; + } + }, + { + name: "refrigerant", + descriptionFunction() { + return `after losing at least 5% ${tech.isEnergyHealth ? "energy" : "health"}
freeze all mobs for 7 seconds` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHarmFreeze = true; + }, + remove() { + tech.isHarmFreeze = false; + } + }, + // { + // name: "clock gating", + // description: `after losing health slow time by 50%
+20% defense`, + // maxCount: 1, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // allowed() { + // return simulation.fpsCapDefault > 45 + // }, + // requires: "FPS above 45", + // effect() { + // tech.isSlowFPS = true; + // }, + // remove() { + // tech.isSlowFPS = false; + // } + // }, + + { + name: "piezoelectricity", + description: "if you collide with a mob
generate +2048 energy", //
reduce defense by 15% + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + tech.isPiezo = true; + // if (simulation.isTextLogOpen) m.energy += 20.48; + }, + remove() { + tech.isPiezo = false; + } + }, + { + name: "ground state", + description: "+266 maximum energy
–33% passive energy generation", + // description: "reduce defense by 66%
you no longer passively regenerate energy", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isTimeCrystals + }, + requires: "not time crystals", + effect() { + tech.isGroundState = true + m.setFieldRegen() + m.setMaxEnergy() + }, + remove() { + tech.isGroundState = false + m.setFieldRegen() + m.setMaxEnergy() + } + }, + { + name: "heat engine", + description: `+50% damage
–50 maximum energy`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "not CPT", + damage: 1.5, + effect() { + tech.damage *= this.damage + tech.isMaxEnergyTech = true; + m.setMaxEnergy() + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isMaxEnergyTech = false; + m.setMaxEnergy() + } + }, + { + name: "exothermic process", + description: "+50% damage
after mobs die –20% energy", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.55, + effect() { + tech.damage *= this.damage + tech.isEnergyLoss = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isEnergyLoss = false; + } + }, + { + name: "Gibbs free energy", + description: `for each energy below 100
+0.7% damage`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isLowEnergyDamage = true; + }, + remove() { + tech.isLowEnergyDamage = false; + } + }, + { + name: "overcharge", + description: "+66 maximum energy
+6% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.bonusEnergy += 0.66 + m.setMaxEnergy() + this.refundAmount += tech.addJunkTechToPool(0.06) + }, + refundAmount: 0, + remove() { + tech.bonusEnergy = 0; + m.setMaxEnergy() + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "Maxwells demon", + description: "energy above max decays by 30% 1% per second
+5% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.energy > m.maxEnergy || build.isExperimentSelection + }, + requires: "energy above your max", + effect() { + tech.overfillDrain = 0.99 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.overfillDrain = 0.7 + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "inductive charging", + description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDamageAfterKillNoRegen + }, + requires: "not parasitism", + effect() { + tech.isCrouchRegen = true; //only used to check for requirements + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && m.crouch && m.fieldCDcycle < m.cycle) m.energy += 7 * m.fieldRegen; + if (m.energy < 0) m.energy = 0 + } + }, + remove() { + tech.isCrouchRegen = false; + m.regenEnergy = m.regenEnergyDefault + } + }, + { + name: "energy conservation", + description: "4% of damage done recovered as energy", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.energySiphon += 0.04; + }, + remove() { + tech.energySiphon = 0; + } + }, + { + name: "parasitism", + description: "if a mob has died in the last 5 seconds
+83% damage, inhibit energy generation", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isCrouchRegen + }, + requires: "not inductive charging", + effect() { + tech.isDamageAfterKillNoRegen = true; + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.energy < 0) m.energy = 0 + } + }, + remove() { + if (this.count) m.regenEnergy = m.regenEnergyDefault + tech.isDamageAfterKillNoRegen = false; + } + }, + { + name: "waste heat recovery", + description: "if a mob has died in the last 5 seconds
generate 5% of max energy per second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isEnergyRecovery = true; + }, + remove() { + tech.isEnergyRecovery = false; + } + }, + { + name: "recycling", + description: "if a mob has died in the last 5 seconds
recover 0.5% of max health per second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHealthRecovery = true; + }, + remove() { + tech.isHealthRecovery = false; + } + }, + { + name: "torpor", + description: "if a mob has not died in the last 5 seconds
+66% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHarmReduceNoKill = true; + }, + remove() { + tech.isHarmReduceNoKill = false; + } + }, + { + name: "homeostasis", + descriptionFunction() { + return `for each health below 100
+0.8% defense (${(100 * (Math.max(0, 1 - m.health) * 0.8)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.health < 0.6 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDefense = true; + }, + remove() { + tech.isLowHealthDefense = false; + } + }, + { + name: "negative feedback", + descriptionFunction() { + return `for each ${tech.isEnergyHealth ? "energy" : "health"} below 100
+0.7% damage (${(70 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.health < 0.6 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDmg = true; //used in mob.damage() + }, + remove() { + tech.isLowHealthDmg = false; + } + }, + { + name: "Zenos paradox", + descriptionFunction() { + return `+85% defense
–5% of current ${tech.isEnergyHealth ? "energy" : "health"} every 5 seconds` + }, + // description: "+85% defense
–5% of current health every 5 seconds", + // description: "every 5 seconds remove 1/10 of your health
reduce defense by 90%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isZeno = true; + }, + remove() { + tech.isZeno = false; + } + }, + { + name: "antiscience", + descriptionFunction() { + return `–10 ${tech.isEnergyHealth ? "energy" : "health"} after picking up a tech
+66% damage` + }, + // description: "+66% damage
–10 health after picking up a tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.66, + effect() { + tech.damage *= this.damage + tech.isTechDamage = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isTechDamage = false; + } + }, + { + name: "ergodicity", + descriptionFunction() { + return `${powerUps.orb.heal()} have -50% effect
+66% damage` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.66, + effect() { + tech.damage *= this.damage + tech.isHalfHeals = true; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const scale = Math.sqrt(0.5) + powerUp[i].size *= scale + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + }, + remove() { + if (this.count) { + tech.damage /= this.damage + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const scale = 1 / Math.sqrt(0.5) + powerUp[i].size *= scale + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + } + tech.isHalfHeals = false; + } + }, + { + name: "fluoroantimonic acid", + description: "if your health is above 100
+35% damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.maxHealth > 1; + }, + requires: "max health above 100", + effect() { + tech.isAcidDmg = true; + }, + remove() { + tech.isAcidDmg = false; + } + }, + { + name: "induction brake", + descriptionFunction() { + return `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(4)}` + }, + // description: `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(4)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isPerfectBrake + }, + requires: "not eddy current brake", + effect() { + tech.isHealBrake = true; + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal"); + }, + remove() { + tech.isHealBrake = false; + } + }, + { + name: "adiabatic healing", + descriptionFunction() { + return `${powerUps.orb.heal()} have +100% effect
+4% JUNK to tech pool` + }, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection + }, + requires: "under 70% health", + effect() { + tech.largerHeals++; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + this.refundAmount += tech.addJunkTechToPool(0.04) + }, + refundAmount: 0, + remove() { + tech.largerHeals = 1; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow + } + } + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "quenching", + descriptionFunction() { + return `after over healing from ${powerUps.orb.heal()}
gain max health and lose current health` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isOverHeal = true; + }, + remove() { + tech.isOverHeal = false; + } + }, + { + name: "accretion", + descriptionFunction() { + return `${powerUps.orb.heal(1)} follow you, even between levels
spawn ${powerUps.orb.heal(5)}` + }, + // description: `${powerUps.orb.heal(1)} follow you, even between levels
spawn ${powerUps.orb.heal(5)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return m.fieldMode !== 9 + }, + requires: "not wormhole", + effect() { + tech.isHealAttract = true + powerUps.setPowerUpMode(); + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal"); + }, + remove() { + tech.isHealAttract = false + powerUps.setPowerUpMode(); + }, + }, + { + name: "self-assembly", + descriptionFunction() { + return `at the start of each level
for every 25% missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHealLowHealth = true; + }, + remove() { + tech.isHealLowHealth = false; + } + }, + { + name: "enthalpy", + descriptionFunction() { + return `doing damage has a small chance to spawn ${powerUps.orb.heal(1)}` //
–10% defense + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.healthDrain += 0.023; + }, + remove() { + tech.healthDrain = 0; + } + }, + { + name: "maintenance", + descriptionFunction() { + return `
double
the frequency of finding healing tech
spawn ${powerUps.orb.heal(13)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 + } + }, + remove() { } + }, + { + name: "anthropic principle", + nameInfo: "", + addNameInfo() { + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); + }, + descriptionFunction() { + return `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return powerUps.research.count > 0 || build.isExperimentSelection + }, + requires: "at least 1 research", + effect() { + tech.isDeathAvoid = true; + tech.isDeathAvoidedThisLevel = false; + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); + }, + remove() { + tech.isDeathAvoid = false; + } + }, + { + name: "weak anthropic principle", + description: "after anthropic principle prevents your death
+50% duplication chance for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid + }, + requires: "anthropic principle", + effect() { + tech.isAnthropicTech = true + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + }, + remove() { + tech.isAnthropicTech = false + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + }, + { + name: "strong anthropic principle", + description: "after anthropic principle prevents your death
+137.03599% damage for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid + }, + requires: "anthropic principle", + effect() { + tech.isAnthropicDamage = true + }, + remove() { + tech.isAnthropicDamage = false + } + }, + { + name: "quantum immortality", + description: "+30% defense
after dying, continue in an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isImmortal = true; + }, + remove() { + tech.isImmortal = false; + } + }, + { + name: "Hilbert space", + description: "+99% damage
after a collision enter an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isAltRealityTech: true, + allowed() { + return !tech.isResearchReality && !tech.isSwitchReality + }, + requires: "not Ψ(t) collapse, many-worlds", + damage: 1.99, + effect() { + tech.damage *= this.damage + tech.isCollisionRealitySwitch = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isCollisionRealitySwitch = false; + } + }, + { + name: "many-worlds", + // description: "each level is an alternate reality, where you
find a tech at the start of each level", + description: `on each new level spawn a tech power up
and enter an alternate reality`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isAltRealityTech: true, + allowed() { + return !tech.isResearchReality && !tech.isCollisionRealitySwitch + }, + requires: "not Ψ(t) collapse, Hilbert space", + effect() { + tech.isSwitchReality = true; + }, + remove() { + tech.isSwitchReality = false; + } + }, + { + name: "Ψ(t) collapse", + link: `Ψ(t) collapse`, + description: `spawn ${powerUps.orb.research(16)}
after you research enter an alternate reality`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isAltRealityTech: true, + allowed() { + return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch + }, + requires: "not many-worlds, Hilbert space, pseudoscience", + bonusResearch: 16, + effect() { + tech.isResearchReality = true; + for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); + }, + remove() { + tech.isResearchReality = false; + if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch) + } + }, + { + name: "decoherence", + description: `tech options you don't choose won't reoccur
spawn ${powerUps.orb.research(6)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + bonusResearch: 6, + effect() { + tech.isBanish = true + for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + }, + remove() { + if (tech.isBanish) { + tech.isBanish = false + //reset banish list + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isBanished) tech.tech[i].isBanished = false + } + powerUps.research.changeRerolls(-this.bonusResearch) + } + tech.isBanish = false + } + }, + { + name: "renormalization", + description: `46% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}
+3% JUNK to tech pool`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism + }, + requires: "at least 4 research, not superdeterminism", + effect() { + tech.renormalization = true; + this.refundAmount += tech.addJunkTechToPool(0.03) + + }, + refundAmount: 0, + remove() { + tech.renormalization = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "perturbation theory", + description: `if you have no ${powerUps.orb.research(1)} in your inventory
+70% fire rate`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count === 0 + }, + requires: "no research", + effect() { + tech.isRerollHaste = true; + tech.researchHaste = 0.3; + b.setFireCD(); + }, + remove() { + tech.isRerollHaste = false; + tech.researchHaste = 1; + b.setFireCD(); + } + }, + { + name: "ansatz", + description: `after choosing a field, tech, or gun
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(2)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality + }, + requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", + effect() { + tech.isAnsatz = true; + }, + remove() { + tech.isAnsatz = false; + } + }, + { + name: "Bayesian statistics", + // description: `for each ${powerUps.orb.research(1)} in your inventory
+3.8% damage`, + descriptionFunction() { + return `spawn ${powerUps.orb.research(this.bonusResearch)}
+3% damage per ${powerUps.orb.research(1)} (${(3 * powerUps.research.count).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return powerUps.research.count > 2 || build.isExperimentSelection + }, + requires: "at least 3 research", + bonusResearch: 3, + effect() { + powerUps.spawnDelay("research", this.bonusResearch) + tech.isRerollDamage = true; + }, + remove() { + tech.isRerollDamage = false; + if (this.count) { + powerUps.research.changeRerolls(-this.bonusResearch) + } + } + }, + { + name: "mass production", + descriptionFunction() { + return `tech always have +3 choices to spawn
${powerUps.orb.ammo(8)} ${powerUps.orb.heal(8)}    or ${powerUps.orb.research(5)}` + }, + // description: `tech always have +3 choices to spawn
${powerUps.orb.ammo(8)} ${powerUps.orb.heal(8)}    or ${powerUps.orb.research(5)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isMassProduction = true + }, + remove() { + tech.isMassProduction = false + } + }, + { + name: "research", + description: `spawn ${powerUps.orb.research(5)}`, + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isNonRefundable: true, + isMassProduction: true, + allowed() { return true }, + requires: "", + effect() { + powerUps.spawnDelay("research", 5); + }, + remove() { } + }, + { + name: "ammo", + description: `spawn ${powerUps.orb.ammo(8)}`, + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isNonRefundable: true, + isMassProduction: true, + allowed() { return true }, + requires: "", + effect() { + powerUps.spawnDelay("ammo", 8); + }, + remove() { } + }, + { + name: "heals", + descriptionFunction() { + return `spawn ${powerUps.orb.heal(8)}` + }, + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isNonRefundable: true, + isMassProduction: true, + allowed() { return true }, + requires: "mass production", + effect() { + powerUps.spawnDelay("heal", 8); + }, + remove() { } + }, + { + name: "pseudoscience", + description: "when selecting a power up, research 3 times
for free, but add 1-4% JUNK to the tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isSuperDeterminism //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection + }, + requires: "not Ψ(t) collapse, superdeterminism", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", + effect() { + tech.isJunkResearch = true; + }, + remove() { + tech.isJunkResearch = false; + } + }, + { + name: "brainstorming", + description: "tech choices randomize
every 1.5 seconds for 10 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isBrainstorm = true + tech.isBrainstormActive = false + tech.brainStormDelay = 1800 - simulation.difficultyMode * 100 + }, + remove() { + tech.isBrainstorm = false + tech.isBrainstormActive = false + } + }, + { + name: "cross-disciplinary", + description: "tech have an extra field or gun choice
+5% chance to duplicate spawned power ups", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect() { + tech.isExtraGunField = true; + }, + remove() { + tech.isExtraGunField = false; + } + }, + { + name: "emergence", + description: "tech, fields, and guns have +1 choice
+8% damage", + // description: "tech, fields, and guns have +2 choices
+3% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + damage: 1.08, + effect() { + tech.extraChoices += 1; + tech.damage *= this.damage + // this.refundAmount += tech.addJunkTechToPool(0.03) + }, + refundAmount: 0, + remove() { + tech.extraChoices = 0; + if (this.count > 0) { + tech.damage /= this.damage + // if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) + } + } + }, + { + name: "path integral", + link: `path integral`, + description: "your next tech choice has all possible options
+5% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + // isJunk: true, + allowed() { + return !tech.isDeterminism && !tech.isBrainstorm + }, + requires: "not determinism, brainstorm", + effect() { + tech.tooManyTechChoices = 1 + // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.tooManyTechChoices = 0 + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "determinism", + description: "spawn 5 tech
only 1 choice for tech, fields, and guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return !tech.extraChoices && !tech.isExtraGunField && !tech.isFlipFlopChoices + }, + requires: "not emergence, cross-disciplinary, integrated circuit", + effect() { + tech.isDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + tech.isDeterminism = false; + } + }, + { + name: "superdeterminism", + description: `spawn 5 tech
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return tech.isDeterminism && !tech.isAnsatz && !tech.isJunkResearch && !tech.isBrainstorm + }, + requires: "determinism, not ansatz, pseudoscience, brainstorming", + effect() { + tech.isSuperDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + tech.isSuperDeterminism = false; + } + }, + { + name: "technical debt", + descriptionFunction() { + // return `+300% damage –15% damage
for each tech you have learned (+${(Math.floor(100 * (Math.min(Math.pow(0.85, tech.totalCount - 20), 4 - 0.15 * tech.totalCount))) - 100)}%)` + return `+300% damage –15% damage
for each tech you have learned (${(tech.totalCount > 20 ? 100 * (Math.pow(0.85, tech.totalCount - 20) - 1) : 100 * (3 - 0.15 * tech.totalCount)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isTechDebt = true; + }, + remove() { + tech.isTechDebt = false; + } + }, + // { + // name: "abiogenesis", + // // description: `use ${powerUps.orb.research(4)}(or 49% JUNK to the tech pool if you can't) to add a 2nd boss to each level`, + // description: `as a level begins spawn a 2nd boss using ${powerUps.orb.research(3)}
(+49% JUNK to the tech pool if you can't pay)
`, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 2) && !tech.isDuplicateMobs + // }, + // requires: "at least 3 research, not parthenogenesis", + // effect() { + // tech.isResearchBoss = true; + // }, + // remove() { + // tech.isResearchBoss = false; + // } + // }, + { + name: "meta-analysis", + description: `if you choose a JUNKtech you instead get a
random normal tech and spawn ${powerUps.orb.research(2)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.junkCount > 0 + }, + requires: "some JUNK tech", + effect() { + tech.isMetaAnalysis = true + }, + remove() { + tech.isMetaAnalysis = false + } + }, + { + name: "dark patterns", + description: "+22% damage
+22% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.22, + effect() { + tech.damage *= this.damage + this.refundAmount += tech.addJunkTechToPool(0.22) + }, + refundAmount: 0, + remove() { + if (this.count > 0) { + tech.damage /= this.damage + if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) + } + } + }, + { + name: "exciton", + descriptionFunction() { + return `after mobs die they have a 14% chance to
spawn ${powerUps.orb.boost(1)} that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isBoostPowerUps = true + }, + remove() { + tech.isBoostPowerUps = false + } + }, + { + name: "band gap", + descriptionFunction() { + return `${powerUps.orb.boost(1)} give +77% damage
but their duration is reduced by 1 second` + }, + maxCount: 9, + count: 1, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isBoostPowerUps || tech.isBoostReplaceAmmo + }, + requires: "exciton, quasiparticles", + effect() { + powerUps.boost.duration -= 60 + powerUps.boost.damage += 0.77 + }, + remove() { + powerUps.boost.duration = 600 + powerUps.boost.damage = 1.25 + } + }, + { + name: "eternalism", + description: "+24% damage
time can't be paused (time can be dilated)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause + }, + requires: "not unified field theory, paradigm shift, invariant", + damage: 1.24, + effect() { + tech.damage *= this.damage + tech.isNoDraftPause = true + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isNoDraftPause = false + } + }, + { + name: "paradigm shift", + description: `when paused clicking a tech ejects it
with a 20% chance to remove without ejecting`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause + }, + requires: "not superdeterminism, eternalism", + effect() { + tech.isPauseEjectTech = true; + }, + remove() { + tech.isPauseEjectTech = false; + } + }, + { + name: "unified field theory", + description: `when paused clicking your field cycles it
double the frequency of finding fieldtech`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause + }, + requires: "not superdeterminism, eternalism", + effect() { + tech.isPauseSwitchField = true; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 + } + }, + remove() { + tech.isPauseSwitchField = false; + if (this.count > 1) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 + } + } + } + }, + { + name: "field coupling", + descriptionFunction() { + // return `spawn ${powerUps.orb.coupling(10)}
that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + return `spawn ${powerUps.orb.coupling(10)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + powerUps.spawnDelay("coupling", 10) + }, + remove() { + if (this.count) m.couplingChange(-this.count * 10) + } + }, + { + name: "quintessence", + descriptionFunction() { + if (this.count) { + converted = this.researchUsed * this.couplingToResearch + let orbText + if (converted > 15) { + orbText = `${converted} ${powerUps.orb.coupling()}` + } else { + orbText = powerUps.orb.coupling(converted) + } + return `convert ${this.researchUsed} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + + } else { + let converted = powerUps.research.count * this.couplingToResearch + let orbText + if (converted > 15) { + orbText = `${converted} ${powerUps.orb.coupling()}` + } else { + orbText = powerUps.orb.coupling(converted) + } + return `convert ${powerUps.research.count} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + } + + + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count > 3 + }, + requires: "", + researchUsed: 0, + couplingToResearch: 3, + effect() { + // let count = 0 + // while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { + // powerUps.research.changeRerolls(-1) + // count += 2.5 + // this.researchUsed++ + // } + // powerUps.spawnDelay("coupling", Math.floor(count)) + + let cycle = () => { + if (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) + powerUps.research.changeRerolls(-1) + this.researchUsed++ + powerUps.spawnDelay("coupling", this.couplingToResearch) + } + } // else exit delay loop + + } + requestAnimationFrame(cycle); + }, + remove() { + if (this.count) { + m.couplingChange(-this.researchUsed * this.couplingToResearch) + powerUps.research.changeRerolls(this.researchUsed) + this.researchUsed = 0 + } + } + }, + { + name: "virtual particles", + descriptionFunction() { + return `17% chance after mobs die to spawn ${powerUps.orb.coupling(1)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + // return `17% chance after mobs die to spawn ${powerUps.orb.coupling(1)} that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isCouplingPowerUps = true //about 20-30 mobs per level so at 16% and 0.1 coupling that's about 25 * 0.16 * 0.1 = 0.4 coupling per level with out duplication + }, + remove() { + tech.isCouplingPowerUps = false + } + }, + { + name: "fine-structure constant", + descriptionFunction() { + // return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
-0.5 coupling after mob collisions`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + return `spawn ${this.value} ${powerUps.orb.coupling(1)}, but lose ${powerUps.orb.coupling(5)} after mob collisions
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + allowed: () => true, + requires: "", + value: 60, + effect() { + tech.isCouplingNoHit = true + powerUps.spawnDelay("coupling", this.value) + }, + remove() { + if (this.count) m.couplingChange(-this.value) + tech.isCouplingNoHit = false + } + }, + { + name: "residual dipolar coupling", + descriptionFunction() { + // return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + return `clicking cancel spawns ${powerUps.orb.coupling(5)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isCancelCouple = true + }, + remove() { + tech.isCancelCouple = false + } + }, + { + name: "commodities exchange", + descriptionFunction() { + return `clicking cancel for a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isCancelRerolls = true + }, + remove() { + tech.isCancelRerolls = false + } + }, + { + name: "options exchange", + link: `options exchange`, + description: `clicking cancel for a field, tech, or gun
has a 85% chance to randomize choices`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication) + }, + requires: "not superdeterminism", //futures exchange, commodities exchange, + effect() { + tech.isCancelTech = true + }, + remove() { + tech.isCancelTech = false + } + }, + { + name: "futures exchange", + description: "clicking cancel for a field, tech, or gun
gives +4.1% power up duplication chance", + // descriptionFunction() { + // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` + // }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 && !tech.isSuperDeterminism + }, + requires: "below 100% duplication chance, not superdeterminism", + effect() { + tech.isCancelDuplication = true //search for tech.duplication to balance + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + }, + remove() { + tech.isCancelDuplication = false + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + }, + { + name: "replication", + description: "+9% chance to duplicate spawned power ups
+33% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1. + }, + requires: "below 100% duplication chance", + effect() { + tech.duplicateChance += 0.09 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + this.refundAmount += tech.addJunkTechToPool(0.33) + }, + refundAmount: 0, + remove() { + tech.duplicateChance = 0 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "stimulated emission", + description: "+17% chance to duplicate spawned power ups,
but after a collision eject 1 tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 1% duplication chance", + effect() { + tech.isStimulatedEmission = true + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); + }, + remove() { + tech.isStimulatedEmission = false + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + }, + { + name: "metastability", + description: "+13% chance to duplicate spawned power ups
duplicates explode with a 4 second half-life", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect() { + tech.isPowerUpsVanish = true + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + }, + remove() { + tech.isPowerUpsVanish = false + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + }, + { + name: "correlated damage", + description: "duplication increases damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0.15 + }, + requires: "duplication chance > 15%", + effect() { + tech.isDupDamage = true; + }, + remove() { + tech.isDupDamage = false; + } + }, + { + name: "parthenogenesis", + description: "+8% chance to duplicate spawned power ups
duplication also duplicates mobs", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0// && !tech.isResearchBoss + }, + requires: "some duplication chance", + effect() { + tech.isDuplicateMobs = true; + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + }, + remove() { + tech.isDuplicateMobs = false; + } + }, + { + name: "apomixis", + description: `when you reach 100% duplication
spawn 11 bosses with 100% more durability`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isNonRefundable: true, + allowed() { + return tech.duplicationChance() > 0.5 + }, + requires: "duplication chance above 50%", + effect() { + tech.is100Duplicate = true; + tech.maxDuplicationEvent() + }, + remove() { + tech.is100Duplicate = false; + } + }, + { + name: "strange attractor", + descriptionFunction() { + return `+7% damage
removing this increases duplication by +10%` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + damage: 1.07, + effect() { + tech.damage *= this.damage + }, + remove() { + if (this.count > 0 && m.alive) { + tech.duplication += 0.1 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + tech.damage /= this.damage + this.frequency = 0 + } + } + }, + { + name: "null hypothesis", + description: `+8% damage
removing this spawns ${powerUps.orb.research(15)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + damage: 1.08, + effect() { + tech.damage *= this.damage + }, + remove() { + if (this.count > 0 && m.alive) { + tech.damage /= this.damage + powerUps.spawnDelay("research", 15) + this.frequency = 0 + } + } + }, + { + name: "Born rule", + description: "remove all current tech
spawn new tech to replace them", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "more than 6 tech", + effect() { + //remove active bullets //to get rid of bots + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; + let count = 1 //count tech + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count + } + if (tech.isDeterminism) count -= 4 //remove the bonus tech + if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech + + tech.setupAllTech(); // remove all tech + if (simulation.isCheating) tech.setCheating(); + lore.techCount = 0; + // tech.addLoreTechToPool(); + for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups + //have state is checked in m.death() + }, + remove() { } + }, + + { + name: "Occams razor", + descriptionFunction() { + return `randomly remove half your tech
for each removed +${this.damagePerRemoved * 100}% damage (~${(this.count === 0) ? this.damagePerRemoved * 50 * tech.totalCount : this.damage * 100}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "more than 6 tech", + // removePercent: 0.5, + damagePerRemoved: 0.5, + damage: null, + effect() { + let pool = [] + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (tech.tech[i].count && !tech.tech[i].isNonRefundable && !tech.tech[i].isFromAppliedScience) pool.push(i) + } + pool = shuffle(pool); //shuffles order of maps + let removeCount = 0 + for (let i = 0, len = pool.length * this.damagePerRemoved; i < len; i++) removeCount += tech.removeTech(pool[i]) + this.damage = this.damagePerRemoved * removeCount + tech.damage *= (1 + this.damage) + }, + remove() { + if (this.count) tech.damage /= (1 + this.damage) + } + }, + { + name: "exchange symmetry", + description: "remove 1 random tech
spawn 2 new guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism + }, + requires: "at least 4 tech, not superdeterminism", + effect() { + const have = [] //find which tech you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + } + const choose = have[Math.floor(Math.random() * have.length)] + simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`, 360) + for (let i = 0; i < tech.tech[choose].count; i++) { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + } + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + // powerUps.spawn(m.pos.x, m.pos.y, "gun"); + tech.tech[choose].remove(); // remove a random tech form the list of tech you have + // tech.tech[choose].count = 0; + tech.tech[choose].isLost = true + simulation.updateTechHUD(); + }, + remove() { } + }, + { + name: "Monte Carlo method", + description: "remove 1 random tech
spawn 2 tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism + }, + requires: "some duplication, at least 4 tech, not superdeterminism", + effect() { + const removeTotal = tech.removeTech() + for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { } + }, + { + name: "reinforcement learning", + description: "increase the frequency of finding copies of
your current tech by 1000%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.totalCount > 9 + }, + requires: "at least 10 tech", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 + } + }, + remove() { + if (this.count) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 + } + } + } + }, + // { + // name: "backward induction", + // descriptionFunction() { + // if (build.isExperimentSelection || powerUps.tech.choiceLog.length < 10) return `use ${powerUps.orb.research(2)} to choose all the unchosen tech
from your last selection` + + // text = `` + // let num = 3 + // if (tech.extraChoices) num = 5 + // if (tech.isDeterminism) num = 1 + // for (let i = 0; i < num; i++) { + // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] + // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { + // text += `${tech.tech[index].name}, ` + // } + // } + // text = text.slice(0, -2); + // return `use ${powerUps.orb.research(2)}to choose the unchosen
tech from your previous selection:
${text}` + // }, + // // description: `use ${powerUps.orb.research(2)}to choose all the unchosen
tech from your previous tech selection`, + // maxCount: 1, + // count: 0, + // frequency: 100, + // frequencyDefault: 100, + // isNonRefundable: true, + // isBadRandomOption: true, + // allowed() { + // return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 + // }, + // requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", + // effect: () => { + // powerUps.research.changeRerolls(-2) + // let num = 3 + // if (tech.extraChoices) num = 5 + // if (tech.isDeterminism) num = 1 + // for (let i = 0; i < num; i++) { + // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] + // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { + // tech.giveTech(index) + // simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); + // } + // } + // }, + // remove() {} + // }, + //************************************************** + //************************************************** gun + //************************************************** tech + //************************************************** + { + name: "needle ice", + description: `after needles impact walls
they chip off 1-2 freezing ice IX crystals`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNeedles || tech.isNeedles) && !tech.needleTunnel + }, + requires: "nail gun, needle gun, not nanowires", + effect() { + tech.isNeedleIce = true + }, + remove() { + tech.isNeedleIce = false + } + }, + { + name: "nanowires", + description: `needles tunnel through blocks and map
+20% needle damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && tech.isNeedles) || (tech.isNeedles && tech.haveGunCheck("shotgun"))) && !tech.isNeedleIce + }, + requires: "nail gun, needle gun, not needle ice", + effect() { + tech.needleTunnel = true + }, + remove() { + tech.needleTunnel = false + } + }, + { + name: "ceramics", + description: `needles and harpoons pierce shields
directly damaging shielded mobs`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles + }, + requires: "needle gun, harpoon, not Bessemer process", + effect() { + tech.isShieldPierce = true + }, + remove() { + tech.isShieldPierce = false + } + }, + { + name: "needle gun", + description: "nail gun and shotgun fire mob piercing needles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.nailRecoil && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isRivets && !tech.isIncendiary && !tech.isIceCrystals && !tech.isIceShot + }, + requires: "nail gun, shotgun, not ice crystal, rivets, rotary cannon, pneumatic, incendiary, nail-shot, foam-shot, worm-shot, ice-shot", + effect() { + tech.isNeedles = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); + b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); + b.guns[i].chooseFireMethod() + simulation.updateGunHUD(); + break + } + } + }, + remove() { + if (tech.isNeedles) { + tech.isNeedles = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + simulation.updateGunHUD(); + break + } + } + } + } + }, + { + name: "stress concentration", + description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNeedles || tech.isRivets) && !tech.isNailCrit && !tech.isIncendiary + }, + requires: "needles, rivets, not incendiary, supercritical fission", + effect() { + tech.isCritKill = true + }, + remove() { + tech.isCritKill = false + } + }, + { + name: "rivet gun", + description: "nail gun and shotgun slowly lob a heavy rivet", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isNeedles && !tech.isIceCrystals && !tech.isIceShot + }, + requires: "nail gun, shotgun, not ice crystal, needles, or pneumatic actuator", + effect() { + tech.isRivets = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + break + } + } + }, + remove() { + if (tech.isRivets) { + tech.isRivets = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + break + } + } + } + tech.isRivets = false + } + }, + { + name: "pneumatic actuator", + description: "nail gun takes no time to ramp up
to its fastest fire rate", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil + }, + requires: "nail gun, not rotary cannon, rivets, or needles", + effect() { + tech.nailInstantFireRate = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.nailInstantFireRate) { + tech.nailInstantFireRate = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "ice crystal nucleation", + link: `ice crystal nucleation`, + description: "nail gun uses energy to condense
unlimited freezing ice shards", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "nail gun, not rivets, needles", + effect() { + tech.isIceCrystals = true; + b.guns[0].ammoPack = Infinity + b.guns[0].recordedAmmo = b.guns[i].ammo + b.guns[0].ammo = Infinity + simulation.updateGunHUD(); + }, + remove() { + if (tech.isIceCrystals) { + tech.isIceCrystals = false; + b.guns[0].ammoPack = b.guns[0].defaultAmmoPack; + if (b.guns[0].recordedAmmo) b.guns[0].ammo = b.guns[0].recordedAmmo + simulation.updateGunHUD(); + } + tech.isIceCrystals = false; + if (b.guns[0].ammo === Infinity) b.guns[0].ammo = 0 + } + }, + { + name: "rotary cannon", + description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles + }, + requires: "nail gun, not pneumatic actuator, needle gun", + effect() { + tech.nailRecoil = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.nailRecoil) { + tech.nailRecoil = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "gauge", + description: `rivets, needles, super balls, and nails
have +30% mass and physical damage`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMineDrop + tech.isNailBotUpgrade + tech.fragments + tech.nailsDeathMob + (tech.haveGunCheck("super balls") + (tech.haveGunCheck("mine") && !tech.isFoamMine) + (tech.haveGunCheck("nail gun")) + tech.isNeedles + tech.isNailShot + tech.isRivets) * 2 > 1 + }, + requires: "nails, nail gun, rivets, shotgun, super balls, mine", + effect() { + tech.bulletSize = 1 + 0.25 * Math.pow(this.count + 1, 0.5) + }, + remove() { + tech.bulletSize = 1; + } + }, + { + name: "supercritical fission", + description: "if nails, needles, or rivets strike mobs
near their center they can explode", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine))) && !tech.isIncendiary && !tech.isCritKill + }, + requires: "nail gun, mine, needles, nails, rivets, not incendiary, stress concentration", + effect() { + tech.isNailCrit = true + }, + remove() { + tech.isNailCrit = false + } + }, + { + name: "irradiated nails", + link: `irradiated nails`, + description: "nails, needles, and rivets are radioactive
+90% radioactive damage over 3 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot)) + }, + requires: "nail gun, nails, rivets, mine, not ceramic needles", + effect() { + tech.isNailRadiation = true; + }, + remove() { + tech.isNailRadiation = false; + } + }, + { + name: "6s half-life", + link: `6s half-life`, + description: "nails, needles, rivets are made of plutonium-238
radioactive damage lasts +3 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isFastRadiation + }, + requires: "nail gun, mine, irradiated nails, not 1s half-life", + effect() { + tech.isSlowRadiation = true; + }, + remove() { + tech.isSlowRadiation = false; + } + }, + { + name: "1s half-life", + link: `1s half-life`, + description: "nails, needles, rivets are made of lithium-8
+300% radioactive damage for 1 second
", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isSlowRadiation + }, + requires: "nail gun, mine, irradiated nails, not 6s half-life", + effect() { + tech.isFastRadiation = true; + }, + remove() { + tech.isFastRadiation = false; + } + }, + { + name: "spin-statistics", + link: `spin-statistics`, + description: "after firing the shotgun you are invulnerable
shotgun has 50% fewer shots", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") + }, + requires: "shotgun", + effect() { + tech.isShotgunImmune = true; + + //cut current ammo by 1/2 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "shotgun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 + break; + } + } + simulation.updateGunHUD(); + }, + remove() { + if (tech.isShotgunImmune) { + tech.isShotgunImmune = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "shotgun") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); + break; + } + } + simulation.updateGunHUD(); + } + } + }, + { + name: "Newtons 3rd law", + description: "+66% shotgun fire rate and recoil", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed + }, + requires: "shotgun, not Noether violation", + effect() { + tech.isShotgunRecoil = true; + }, + remove() { + tech.isShotgunRecoil = false; + } + }, + { + name: "Noether violation", + link: `Noether violation`, + description: "+50% shotgun damage
shotgun recoil is reversed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil + }, + requires: "shotgun, not Newtons 3rd law", + effect() { + tech.isShotgunReversed = true; + }, + remove() { + tech.isShotgunReversed = false; + } + }, + { + name: "repeater", + description: "shotgun immediately fires again for no ammo
-50% shotgun fire rate", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("shotgun")) + }, + requires: "shotgun, not Newtons 3rd law", + effect() { + tech.shotgunExtraShots++; + }, + remove() { + tech.shotgunExtraShots = 0 + } + }, + { + name: "nail-shot", + link: `nail-shot`, + description: "shotgun drives a long clip of nails", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, rivets, foam-shot, worm-shot, ice-shot, needles", + effect() { + tech.isNailShot = true; + }, + remove() { + tech.isNailShot = false; + } + }, + { + name: "foam-shot", + link: `foam-shot`, + description: "shotgun sprays sticky foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, nail-shot, rivet, worm-shot, ice-shot, needle", + effect() { + tech.isFoamShot = true; + }, + remove() { + tech.isFoamShot = false; + } + }, + { + name: "ice-shot", + link: `ice-shot`, + description: "shotgun grows freezing ice IX crystals", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, nail-shot, rivet, foam-shot, worm-shot", + effect() { + tech.isIceShot = true; + }, + remove() { + tech.isIceShot = false; + } + }, + { + name: "freezer burn", + description: "mobs frozen while below 33% durability die", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) + }, + requires: "a freeze effect", + effect() { + tech.isIceKill = true + }, + remove() { + tech.isIceKill = false + } + }, + { + name: "flash freeze", + description: "mobs frozen while above 66% durability
have their durability reduced to 66%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) + }, + requires: "a freeze effect", + effect() { + tech.isIceMaxHealthLoss = true + }, + remove() { + tech.isIceMaxHealthLoss = false + } + }, + { + name: "crystallizer", + description: "after frozen mobs die they
shatter into ice IX crystals", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0))) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob + }, + requires: "a localized freeze effect, no other mob death tech", + effect() { + tech.iceIXOnDeath++ + }, + remove() { + tech.iceIXOnDeath = 0 + } + }, + { + name: "thermoelectric effect", + description: "after killing mobs with ice IX
+100 energy", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "ice IX", + effect() { + tech.iceEnergy++ + }, + remove() { + tech.iceEnergy = 0; + } + }, + { + name: "superfluidity", + description: "freeze effects are applied
to a small area around the target", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "a localized freeze effect", + effect() { + tech.isAoESlow = true + }, + remove() { + tech.isAoESlow = false + } + }, + { + name: "incendiary ammunition", + description: "shotgun, rivets, super balls, and drones
are loaded with explosives", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIceShot && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles) || ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce && !tech.isFoamBall && !tech.isSuperHarm) || (tech.isRivets && !tech.isNailCrit) || (m.fieldMode === 4 && simulation.molecularMode === 3) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) + }, + requires: "shotgun, super balls, rivets, drones, not irradiated drones, burst drones, polyurethane, Zectron", + effect() { + tech.isIncendiary = true + }, + remove() { + tech.isIncendiary = false; + } + }, + { + name: "rebound", + description: `after they collide with a mob, super balls
gain speed, duration, and +33% damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isFoamBall + }, + requires: "super balls, not incendiary", + effect() { + tech.isSuperBounce = true + }, + remove() { + tech.isSuperBounce = false + } + }, + { + name: "Zectron", + description: `+75% super ball density and damage, but
after colliding with super balls -25% energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isBulletTeleport + }, + requires: "super balls not incendiary ammunition, uncertainty principle", + effect() { + tech.isSuperHarm = true + }, + remove() { + tech.isSuperHarm = false + } + }, + { + name: "polyurethane foam", + description: "super balls and harpoons colliding with mobs
catalyzes a reaction that yields foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) + }, + requires: "super balls, harpoon, not fragmentation", + effect() { + tech.isFoamBall = true; + }, + remove() { + tech.isFoamBall = false; + } + }, + { + name: "autocannon", + description: "fire +1 extra super ball
balls are quickly released in same direction", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("super balls") && !tech.oneSuperBall + }, + requires: "super balls, but not the tech super ball", + effect() { + tech.superBallDelay = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.superBallDelay) { + tech.superBallDelay = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "super duper", + description: `randomly fire +0, +1, +2, or +3 extra super balls`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.oneSuperBall + }, + requires: "super balls, not super ball", + effect() { + tech.extraSuperBalls += 4 + }, + remove() { + tech.extraSuperBalls = 0; + } + }, + { + name: "super ball", + description: "fire just 1 large super ball
that stuns mobs for 2 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.extraSuperBalls && !tech.superBallDelay + }, + requires: "super balls, not super duper or autocannon", + effect() { + tech.oneSuperBall = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.oneSuperBall) { + tech.oneSuperBall = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "phase velocity", + description: "wave particles propagate faster as solids
+40% wave damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") && !tech.isLongitudinal + }, + requires: "wave, not phonon", + effect() { + tech.isPhaseVelocity = true; + }, + remove() { + tech.isPhaseVelocity = false; + } + }, + { + name: "amplitude", + description: "+37% wave damage
+37% wave particle amplitude", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade + }, + requires: "wave", + effect() { + tech.waveFrequency *= 0.66 + tech.wavePacketDamage *= 1.37 + }, + remove() { + tech.waveFrequency = 0.2 + tech.wavePacketDamage = 1 + } + }, + { + name: "propagation", + description: "–25% wave packet propagation speed
+41% wave damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade + }, + requires: "wave", + effect() { + tech.waveBeamSpeed *= 0.75; + tech.waveBeamDamage += 0.27 * 0.41 //this sets base wave damage + }, + remove() { + tech.waveBeamSpeed = 11; + tech.waveBeamDamage = 0.27 //this sets base wave damage + } + }, + { + name: "bound state", + description: "wave packets reflect backwards 2 times
–33% range", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") && !tech.isLongitudinal + }, + requires: "wave, not phonon", + effect() { + tech.waveReflections += 2 + }, + remove() { + tech.waveReflections = 1 + } + }, + { + name: "frequency", + description: `wave has unlimited ammo
-25% wave damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => tech.haveGunCheck("wave"), + requires: "wave", + effect() { + tech.isInfiniteWaveAmmo = true + b.guns[3].savedAmmo = b.guns[3].ammo + b.guns[3].ammo = Infinity + simulation.updateGunHUD(); + }, + remove() { + tech.isInfiniteWaveAmmo = false + if (this.count > 0 && b.guns[3].savedAmmo !== undefined) { + b.guns[3].ammo = b.guns[3].savedAmmo + simulation.updateGunHUD(); + } else if (b.guns[3].ammo === Infinity) { + b.guns[3].ammo = 0 + } + } + }, + { + name: "phonon", //longitudinal //gravitational wave? + description: "waves are low frequency, high damage
expanding arcs that propagate through solids", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.haveGunCheck("wave") && !tech.isPhaseVelocity && tech.waveReflections === 1 + }, + requires: "wave, not phase velocity, bound state", + ammoScale: 6, + effect() { + tech.isLongitudinal = true; + b.guns[3].chooseFireMethod() + b.guns[3].ammoPack = b.guns[3].defaultAmmoPack / this.ammoScale + if (tech.isInfiniteWaveAmmo) { + b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo / this.ammoScale); //used with low frequency + } else { + b.guns[3].ammo = Math.ceil(b.guns[3].ammo / this.ammoScale); + } + simulation.updateGunHUD(); + }, + remove() { + if (tech.isLongitudinal) { + tech.isLongitudinal = false; + b.guns[3].chooseFireMethod() + b.guns[3].ammoPack = b.guns[3].defaultAmmoPack + if (tech.isInfiniteWaveAmmo) { + b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo * this.ammoScale); //used with low frequency + } else { + b.guns[3].ammo = Math.ceil(b.guns[3].ammo * this.ammoScale); + } + simulation.updateGunHUD(); + } + tech.isLongitudinal = false; + } + }, + { + name: "isotropic", + description: "waves expand in all directions
–40% range and +50% damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isLongitudinal && tech.haveGunCheck("wave") && !tech.isBulletTeleport + }, + requires: "wave, phonon, not uncertainty principle", + effect() { + tech.is360Longitudinal = true; + b.guns[3].chooseFireMethod() + }, + remove() { + tech.is360Longitudinal = false; + b.guns[3].chooseFireMethod() + } + }, + { + name: "mechanical resonance", + description: "after a block gets vibrated by a phonon
there is a chance it's flung at nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade + }, + requires: "wave, phonon", + effect() { + tech.isPhononBlock = true + }, + remove() { + tech.isPhononBlock = false + } + }, + { + name: "sympathetic resonance", + description: "after a mob gets vibrated by a phonon
a new resonance wave expands", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade + }, + requires: "wave, phonon", + effect() { + tech.isPhononWave = true + }, + remove() { + tech.isPhononWave = false + } + }, + { + name: "cruise missile", + description: "+100% missile explosive damage, radius
–50% missile speed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("missiles") && tech.missileFireCD === 45) || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount + }, + requires: "missiles, not launch system", + effect() { + tech.isMissileBig = true + }, + remove() { + tech.isMissileBig = false + } + }, + { + name: "ICBM", + description: "+75% missile explosive damage, radius
–50% missile speed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1)) && tech.isMissileBig + }, + requires: "missiles, cruise missile", + effect() { + tech.isMissileBiggest = true + }, + remove() { + tech.isMissileBiggest = false + } + }, + { + name: "launch system", + description: `+500% missile gun fire rate
+20% missile ammo per ${powerUps.orb.ammo(1)}`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") && !tech.isMissileBig + }, + requires: "missiles, not cruise missile", + ammoBonus: 1.2, + effect() { + tech.missileFireCD = 10 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "missiles") { + b.guns[i].ammoPack *= this.ammoBonus; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * this.ammoBonus); + simulation.updateGunHUD(); + break + } + } + }, + remove() { + if (tech.missileFireCD !== 45) { + tech.missileFireCD = 45; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "missiles") { + b.guns[i].ammoPack = 5; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus); + simulation.updateGunHUD(); + break + } + } + } + } + }, + { + name: "missile-bot", + link: `missile-bot`, + description: `use ${powerUps.orb.research(1)}to trade your missile gun
for a bot that fires missiles`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) + }, + requires: "missiles, not launch system", + effect() { + tech.missileBotCount++; + b.missileBot(); + if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + if (this.count) { + tech.missileBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") + powerUps.research.changeRerolls(1) + } + } + }, + { + name: "iridium-192", + description: "explosions release gamma radiation
+100% explosion damage over 4 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isImmuneExplosion && tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.isBoomBotUpgrade || tech.isTokamak) + }, + requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation, electric armor", + effect() { + tech.isExplodeRadio = true; //iridium-192 + }, + remove() { + tech.isExplodeRadio = false; + } + }, + { + name: "fragmentation", + description: "some detonations and collisions eject nails
blocks, grenades, missiles, rivets, harpoon", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isExplodeRadio && ((tech.haveGunCheck("harpoon") && !tech.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075) + }, + requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192, not polyurethane foam", + effect() { + tech.fragments++ + }, + remove() { + tech.fragments = 0 + } + }, + { + name: "ammonium nitrate", + description: "+24% explosive damage, radius", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect() { + tech.explosiveRadius += 0.24; + }, + remove() { + tech.explosiveRadius = 1; + } + }, + { + name: "nitroglycerin", + description: "+66% explosive damage
–33% explosive radius", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() && !tech.isExplosionHarm + }, + requires: "an explosive damage source, not iridium-192, acetone peroxide", + effect() { + tech.isSmallExplosion = true; + }, + remove() { + tech.isSmallExplosion = false; + } + }, + { + name: "acetone peroxide", + description: "+70% explosive radius
–33% explosive defense", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBadRandomOption: true, + allowed() { + return tech.hasExplosiveDamageCheck() && !tech.isSmallExplosion + }, + requires: "an explosive damage source, not nitroglycerin", + effect() { + tech.isExplosionHarm = true; + }, + remove() { + tech.isExplosionHarm = false; + } + }, + { + name: "shock wave", + description: "mines and sporangium stun for 3-5 seconds
explosions stun for 0.5 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("spores") || tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) + }, + requires: "mine, spores, an explosive damage source, not iridium-192", + effect() { + tech.isStun = true; + }, + remove() { + tech.isStun = false; + } + }, + { + name: "shaped charge", + description: `use ${powerUps.orb.research(3)} to dynamically reduce
all explosions to prevent health loss`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 2) && (tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) + }, + requires: "an explosive damage source, not rocket propelled grenade", + effect() { + tech.isSmartRadius = true; + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isSmartRadius = false; + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + // { + // name: "electric armor", + // // description: "explosions do no defense
while your energy is above 98%", + // description: "instead of causing health loss, explosions
drain 12 energy and have more knockback", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + // }, + // requires: "an explosive damage source, not iridium-192", + // effect() { + // tech.isImmuneExplosion = true; + // }, + // remove() { + // tech.isImmuneExplosion = false; + // } + // }, + { + name: "MIRV", + description: "fire +1 missile or grenade per shot
–12% explosion damage and radius", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades") + }, + requires: "missiles, grenades", + effect() { + tech.missileCount++; + }, + remove() { + tech.missileCount = 1; + } + }, + { + name: "rocket-propelled grenade", + description: "grenades explode on map collisions
explosions drain energy, not health", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isVacuumBomb + }, + requires: "grenades, not vacuum bomb", + effect() { + tech.isImmuneExplosion = true; + tech.isRPG = true; + b.setGrenadeMode() + }, + remove() { + tech.isImmuneExplosion = false; + tech.isRPG = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum bomb", + description: "grenades fire slower, explode bigger,
and suck everything towards them", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isBlockExplode && !tech.isRPG + }, + requires: "grenades, not neutron bomb, chain reaction, RPG", + effect() { + tech.isVacuumBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isVacuumBomb = false; + b.setGrenadeMode() + } + }, + { + name: "chain reaction", + description: "+33% grenade radius and damage
blocks caught in explosions also explode", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isExplodeRadio && !tech.isNeutronBomb && !tech.isVacuumBomb + }, + requires: "grenades, not iridium-192, neutron bomb, vacuum bomb", + effect() { + tech.isBlockExplode = true; //chain reaction + }, + remove() { + tech.isBlockExplode = false; + } + }, + { + name: "flame test", + description: "after grenades detonate they release
a colorful cluster of small explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isCircleExplode && !tech.isPetalsExplode + }, + requires: "grenades, not neutron bomb, pyrotechnics, fireworks", + effect() { + tech.isClusterExplode = true; + }, + remove() { + tech.isClusterExplode = false; + } + }, + { + name: "pyrotechnics", + description: "after grenades detonate they release
a colorful circle of explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isPetalsExplode + }, + requires: "grenades, not neutron bomb, flame test, fireworks", + effect() { + tech.isCircleExplode = true; + }, + remove() { + tech.isCircleExplode = false; + } + }, + { + name: "fireworks", + description: "after grenades detonate they release
colorful petals of explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isCircleExplode + }, + requires: "grenades, not neutron bomb, pyrotechnics, flame test", + effect() { + tech.isPetalsExplode = true; + }, + remove() { + tech.isPetalsExplode = false; + } + }, + { + name: "neutron bomb", + description: "grenades are irradiated with Cf-252
does radioactive damage over time", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode && !tech.isPetalsExplode && !tech.isCircleExplode + }, + requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics, fireworks, flame test, chain reaction", + effect() { + tech.isNeutronBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isNeutronBomb = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum permittivity", + description: "+20% radioactive range
objects in range of the bomb are slowed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb + }, + requires: "grenades, neutron bomb", + effect() { + tech.isNeutronSlow = true + }, + remove() { + tech.isNeutronSlow = false + } + }, + { + name: "radioactive contamination", + description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation + }, + requires: "radiation damage source", + effect() { + tech.isRadioactive = true + }, + remove() { + tech.isRadioactive = false + } + }, + { + name: "nuclear transmutation", + description: "+47% radiation damage
nail, drone, neutron bomb, iridium, cosmic string, deflect", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation || tech.isDroneRadioactive + }, + requires: "radiation damage source", + effect() { + tech.radioactiveDamage += 1.47 + }, + remove() { + tech.radioactiveDamage = 1 + } + }, + { + name: "water shielding", + link: `water shielding`, + description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio + }, + requires: "neutron bomb, irradiated drones, iridium-192", + effect() { + tech.isRadioactiveResistance = true + }, + remove() { + tech.isRadioactiveResistance = false + } + }, + { + name: "ricochet", + description: "after nails hit a mob they rebound towards
a new mob with +180% damage per bounce", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + // return (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("mines")) + return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isLaserMine || tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot) && !tech.isRivets && !tech.isNeedles) + }, + // + requires: "nail gun, not rotary cannon, rivets, or needles", + effect() { + tech.isRicochet = true + }, + remove() { + tech.isRicochet = false + } + }, + { + name: "booby trap", + description: "50% chance to drop a mine from power ups
+36% JUNK to tech pool", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isMineDrop = true; + if (tech.isMineDrop) b.mine(m.pos, { + x: 0, + y: 0 + }, 0) + this.refundAmount += tech.addJunkTechToPool(0.36) + }, + refundAmount: 0, + remove() { + tech.isMineDrop = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "elephants toothpaste", + description: "instead of nails mines catalyze a reaction
that yields foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && !tech.isSuperMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "mines, not blast ball, ricochet, irradiated nails, supercritical fission", + effect() { + tech.isFoamMine = true; + }, + remove() { + tech.isFoamMine = false; + } + }, + { + name: "blast ball", + descriptionFunction() { + return `instead of nails mines fire bouncy balls` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && !tech.isFoamMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "mines, not elephants toothpaste, ricochet, irradiated nails, supercritical fission", + effect() { + tech.isSuperMine = true; + }, + remove() { + tech.isSuperMine = false; + } + }, + { + name: "laser-mines", + link: `laser-mines`, + description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isLaserMine = true; + }, + remove() { + tech.isLaserMine = false; + } + }, + { + name: "sentry", + descriptionFunction() { + return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 50% more ${b.guns[10].nameString('s')}` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines, not elephants toothpaste", + effect() { + tech.isMineSentry = true; + }, + remove() { + tech.isMineSentry = false; + } + }, + { + name: "extended magazine", + descriptionFunction() { + return `sentry mines fire 50% more ${b.guns[10].nameString('s')}` + }, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && tech.isMineSentry + }, + requires: "mines, sentry", + effect() { + tech.sentryAmmo += 17; + }, + remove() { + tech.sentryAmmo = 33; + } + }, + { + name: "mycelial fragmentation", + link: `mycelial fragmentation`, + description: "during their growth phase
+70% sporangium discharge", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isSporeGrowth = true + }, + remove() { + tech.isSporeGrowth = false + } + }, + { + name: "cordyceps", + // descriptionFunction() { + // return `mobs infected by ${b.guns[6].nameString('s')} have a 5% chance
to resurrect and attack other mobs` + // }, + description: "sporangium infect mobs they attach to
infected mobs resurrect and attack other mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isZombieMobs = true + }, + remove() { + tech.isZombieMobs = false + } + }, + { + name: "colony", + description: "+50% sporangium discharge
40% chance to discharge something different", + link: `colony`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isSporeColony = true + }, + remove() { + tech.isSporeColony = false + } + }, + { + name: "cryodesiccation", + descriptionFunction() { + return `+25% sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` + }, + // description: "+25% sporangium discharge
spores freeze mobs for 1.5 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isSporeFreeze = true + }, + remove() { + tech.isSporeFreeze = false + } + }, + { + name: "flagella", + descriptionFunction() { + return `+50% ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` + }, + // description: "+50% spore acceleration
if they can't find a target spores follow you", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isSporeFollow = true + }, + remove() { + tech.isSporeFollow = false + } + }, + { + name: "junk DNA", + descriptionFunction() { + return `+53% ${b.guns[6].nameString()} damage per JUNKtech (${(53 * tech.junkCount).toFixed(0)}%)
+50% JUNK to tech pool` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isJunkDNA = true + this.refundAmount += tech.addJunkTechToPool(0.5) + }, + refundAmount: 0, + remove() { + tech.isJunkDNA = false + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + // { + // name: "junk DNA", + // //increase damage by 10% for each JUNK tech percent in the tech pool, remove all JUNK tech, + // descriptionFunction() { return `+50% ${b.guns[6].nameString()} damage
+15% JUNK to tech pool` }, + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + // }, + // requires: "spores", + // effect() { + // tech.isSporeWorm = true + // this.refundAmount += tech.addJunkTechToPool(0.15) + // }, + // refundAmount: 0, + // remove() { + // tech.isSporeWorm = false + // if (this.count > 0 && this.refundAmount > 0) { + // tech.removeJunkTechFromPool(this.refundAmount) + // this.refundAmount = 0 + // } + // } + // }, + { + name: "mutualism", + descriptionFunction() { + return `+200% ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` + }, + // description: `+150% ${b.guns[6].name()} damage
spores borrow 0.5 health until they die`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0)) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isMutualism = true + }, + remove() { + tech.isMutualism = false + } + }, + { + name: "necrophage", + description: "if foam, fleas, or worms kill their target
they grow 3 copies", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm || tech.isSporeFlea || tech.isFoamMine + }, + requires: "foam, spores, worms, fleas", + effect() { + tech.isSpawnBulletsOnDeath = true + }, + remove() { + tech.isSpawnBulletsOnDeath = false; + } + }, + { + name: "siphonaptera", + description: "spores metamorphose into fleas", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeWorm + }, + requires: "spores, not worms", + effect() { + tech.isSporeFlea = true + }, + remove() { + tech.isSporeFlea = false + + } + }, + { + name: "nematodes", + description: "spores metamorphose into worms", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea + }, + requires: "spores, not fleas", + effect() { + tech.isSporeWorm = true + }, + remove() { + tech.isSporeWorm = false + } + }, + { + name: "K-selection", + description: "+37% worm and flea damage", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores, shotgun, worms, fleas", + effect() { + tech.wormSize++ + }, + remove() { + tech.wormSize = 0 + } + }, + { + name: "path integration", + descriptionFunction() { + return `drones and ${b.guns[6].nameString("s")}
travel with you through levels` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldMode === 4 && (simulation.molecularMode === 0 || simulation.molecularMode === 3)) + }, + requires: "spores, worms, flagella, drones", + effect() { + tech.isDronesTravel = true + }, + remove() { + tech.isDronesTravel = false + } + }, + { + name: "reduced tolerances", + link: `reduced tolerances`, + description: `+66% drones per ${powerUps.orb.ammo()} and energy
–40% drone duration`, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldMode === 4 && simulation.molecularMode === 3)) + }, + requires: "drones, not irradiated drones", + effect() { + tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) + tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + const scale = Math.pow(3, this.count + 1) + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale + } + } + }, + remove() { + tech.droneCycleReduction = 1 + tech.droneEnergyReduction = 1 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + } + } + }, + { + name: "delivery drone", + description: "if a drone picks up a power up,
it becomes larger, faster, and more durable", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldMode === 4 && simulation.molecularMode === 3) + }, + requires: "drones", + effect() { + tech.isDroneGrab = true + }, + remove() { + tech.isDroneGrab = false + } + }, + { + name: "von Neumann probe", //"drone repair", + description: "after a drone expires
it will harvest a nearby block to replicate itself", + // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") + }, + requires: "drones", + effect() { + tech.isDroneRespawn = true + }, + remove() { + tech.isDroneRespawn = false + } + }, + { + name: "brushless motor", + description: "drones rapidly rush towards their target
+33% drone collision damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldMode === 4 && simulation.molecularMode === 3)) && !tech.isDroneRadioactive && !tech.isIncendiary + }, + requires: "drones, molecular assembler, not irradiated drones, incendiary", + effect() { + tech.isDroneTeleport = true + }, + remove() { + tech.isDroneTeleport = false + } + }, + { + name: "axial flux motor", + description: "+66% drones rush frequency
+44% drone collision damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneTeleport + }, + requires: "drones, brushless motor", + effect() { + tech.isDroneFastLook = true + }, + remove() { + tech.isDroneFastLook = false + } + }, + { + name: "irradiated drones", + link: `irradiated drones`, + description: `the space around drones is irradiated
–75% drones per ${powerUps.orb.ammo()} and energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldMode === 4 && simulation.molecularMode === 3)) + }, + requires: "drones, not reduced tolerances, incendiary, torque bursts", + effect() { + tech.isDroneRadioactive = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) + simulation.makeGunHUD(); + } + } + }, + remove() { + if (tech.isDroneRadioactive) { + tech.isDroneRadioactive = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammo = b.guns[i].ammo * 4 + simulation.makeGunHUD(); + } + } + } + } + }, + { + name: "beta radiation", //"control rod ejection", + description: "–50% drone duration
+100% drone radiation damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones, irradiated drones", + effect() { + tech.droneRadioDamage = 2 + }, + remove() { + tech.droneRadioDamage = 1 + } + }, + { + name: "orthocyclic winding", + link: `orthocyclic winding`, + description: "+66% drone acceleration
+33% radiation damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones, irradiated drones", + effect() { + tech.isFastDrones = true + }, + remove() { + tech.isFastDrones = false + } + }, + { + name: "fault tolerance", + description: `use ${powerUps.orb.research(2)}to trade your drone gun
for 5 drones that last forever`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "drones, not drone repair, anti-shear topology, autonomous navigation", + effect() { + const num = 5 + tech.isForeverDrones += num + if (tech.haveGunCheck("drones", false)) b.removeGun("drones") + //spawn drones + if (tech.isDroneRadioactive) { + for (let i = 0; i < num * 0.25; i++) { + b.droneRadioactive({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } else { + for (let i = 0; i < num; i++) { + b.drone({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isForeverDrones = 0 + if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "surfactant", + description: `use ${powerUps.orb.research(2)}to trade your foam gun
for 2 foam-bots and foam-bot upgrade`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + isNonRefundable: true, + requires: "foam gun, not bot upgrades, fractionation, pressure vessel", + allowed() { + return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) + }, + effect() { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < 2; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + if (tech.haveGunCheck("foam", false)) b.removeGun("foam") + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + // if (this.count) { + // b.clearPermanentBots(); + // b.respawnBots(); + // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") + // } + // if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "electrostatic induction", + description: "foam bubbles are electrically charged
causing attraction to nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine) + }, + requires: "foam, not uncertainty", + effect() { + tech.isFoamAttract = true + }, + remove() { + tech.isFoamAttract = false + } + }, + { + name: "uncertainty principle", + description: "foam, wave, and super ball positions are erratic
+53% foam, wave, and super ball damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) || (tech.haveGunCheck("super balls") && !tech.isSuperHarm) || tech.isSoundBotUpgrade + }, + requires: "foam, wave, super balls, not isotropic, electrostatic induction, Zectron", + effect() { + tech.isBulletTeleport = true + }, + remove() { + tech.isBulletTeleport = false; + } + }, + { + name: "aerogel", + description: "–50% foam duration and foam bubbles float
+180% foam damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine + }, + requires: "foam", + effect() { + tech.isFastFoam = true + tech.foamGravity = -0.0003 + }, + remove() { + tech.isFastFoam = false; + tech.foamGravity = 0.00008 + } + }, + { + name: "surface tension", + description: "+43% foam damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine + }, + requires: "foam", + effect() { + tech.foamDamage += 0.011 * 0.43 + }, + remove() { + tech.foamDamage = 0.011; + } + }, + { + name: "cavitation", + description: "25% chance to discharge a huge foam bubble
increase foam recoil by 100%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine + }, + requires: "foam", + effect() { + tech.isFoamCavitation = true; + b.guns[8].knockBack = 0.001 + }, + remove() { + tech.isFoamCavitation = false; + b.guns[8].knockBack = 0.0005 + } + }, + { + name: "foam fractionation", + description: "if you have below 300 ammo
+100% foam gun bubble size", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isAmmoFoamSize = true + }, + remove() { + tech.isAmmoFoamSize = false; + } + }, + { + name: "ideal gas law", + description: `remove all current foam ammo
+1200% foam ammo per ${powerUps.orb.ammo(1)}`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo + }, + requires: "foam, not non-renewables", + ammoLost: 0, + effect() { + b.guns[8].ammoPack = b.guns[8].ammoPack * 12; + this.ammoLost = b.guns[8].ammo + b.guns[8].ammo = 0 + simulation.updateGunHUD() + }, + remove() { + b.guns[8].ammoPack = 24 + if (this.count) { + b.guns[8].ammo += this.ammoLost + simulation.updateGunHUD() + } + } + }, + { + name: "pressure vessel", + description: "build up charge while firing foam gun
after firing discharge foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isFoamPressure = true; + b.guns[8].chooseFireMethod() + }, + remove() { + tech.isFoamPressure = false; + b.guns[8].chooseFireMethod() + } + }, + { + name: "capacitor bank", + // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", + descriptionFunction() { + return `charge effects build up almost instantly
throwing, ${tech.haveGunCheck("foam", false) ? "foam" : "foam"}, ${tech.isPlasmaBall ? "plasma ball" : "plasma ball"}, ${tech.isRailGun ? "railgun" : "railgun"}, ${tech.isPulseLaser ? "pulse" : "pulse"}, ${tech.isTokamak ? "tokamak" : "tokamak"}` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.blockDamage > 0.075 || tech.isRailGun || (tech.haveGunCheck("foam") && tech.isFoamPressure) || tech.isTokamak || tech.isPulseLaser || tech.isPlasmaBall + }, + requires: "mass driver, railgun, foam, pressure vessel, pulse, tokamak, plasma ball", + effect() { + tech.isCapacitor = true; + }, + remove() { + tech.isCapacitor = false; + } + }, + { + name: "Bitter electromagnet", + descriptionFunction() { + return `railgun charges +33% slower
+100% harpoon density and damage` + }, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && tech.isRailGun + }, + requires: "harpoon, railgun", + effect() { + tech.railChargeRate *= 1.06 + tech.harpoonDensity += 0.007 + }, + remove() { + tech.railChargeRate = 0.97; + tech.harpoonDensity = 0.007 + } + }, + { + name: "railgun", + description: `hold fire to charge harpoon and release to launch
harpoons can't retract`, + // description: `+900% harpoon ammo, but it can't retract
+50% harpoon density and damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple && !tech.isBoostReplaceAmmo + }, + requires: "harpoon, not UHMWPE, induction furnace, grappling hook, quasiparticles", + ammoBonus: 9, + effect() { + tech.isRailGun = true; + b.guns[9].chooseFireMethod() + b.guns[9].ammoPack = 5; + b.guns[9].ammo = b.guns[9].ammo * 6; + simulation.updateGunHUD(); + }, + remove() { + if (tech.isRailGun) { + tech.isRailGun = false; + b.guns[9].chooseFireMethod() + b.guns[9].ammoPack = 1.7; + b.guns[9].ammo = Math.ceil(b.guns[9].ammo / 6); + simulation.updateGunHUD(); + } + } + }, + { + name: "grappling hook", + description: `harpoons attach to the map and pull you
your rope extends while holding fire`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock + }, + requires: "harpoon, not railgun, UHMWPE, induction furnace, Higgs mechanism", + effect() { + tech.isGrapple = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isGrapple) { + tech.isGrapple = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "bulk modulus", + description: `while grappling become invulnerable
drain energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && tech.isGrapple && !tech.isRailEnergy + }, + requires: "grappling hook, not alternator", + effect() { + tech.isImmuneGrapple = true; + }, + remove() { + tech.isImmuneGrapple = false + } + }, + { + name: "alternator", + description: "+90% harpoon energy efficiency", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isImmuneGrapple + }, + requires: "harpoon, not bulk modulus", + effect() { + tech.isRailEnergy = true; + }, + remove() { + tech.isRailEnergy = false; + } + }, + { + name: "Bessemer process", + descriptionFunction() { + return `+${(10 * Math.sqrt(b.guns[9].ammo)).toFixed(0)}% harpoon size and damage
(1/10 √ harpoon ammo)` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isShieldPierce + }, + requires: "harpoon, not ceramics", + effect() { + tech.isLargeHarpoon = true; + }, + remove() { + tech.isLargeHarpoon = false; + } + }, + { + name: "smelting", + descriptionFunction() { + return `forge ${this.removeAmmo()} ammo into a new harpoon
fire +1 harpoon with each shot` + }, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + ammoRemoved: 0, + removeAmmo() { + return (tech.isRailGun ? 5 : 1) * (2 + 2 * this.count) + }, + allowed() { + return tech.haveGunCheck("harpoon") && b.guns[9].ammo >= this.removeAmmo() + }, + requires: "harpoon", + effect() { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + const removeAmmo = this.removeAmmo() + this.ammoRemoved += removeAmmo + b.guns[i].ammo -= removeAmmo + if (b.guns[i].ammo < 0) b.guns[i].ammo = 0 + simulation.updateGunHUD(); + tech.extraHarpoons++; + break + } + } + }, + remove() { + if (tech.extraHarpoons) { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + b.guns[i].ammo += this.ammoRemoved + simulation.updateGunHUD(); + break + } + } + } + this.ammoRemoved = 0 + tech.extraHarpoons = 0; + } + }, + { + name: "UHMWPE", + descriptionFunction() { + return `+${(b.guns[9].ammo * 1.25).toFixed(0)}% harpoon rope length
(1/80 of harpoon ammo)` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple + }, + requires: "harpoon, not grappling hook, railgun", + effect() { + tech.isFilament = true; + }, + remove() { + tech.isFilament = false; + } + }, + { + name: "induction furnace", + description: "after using harpoon to collect a power up
+600% harpoon damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple + }, + requires: "harpoon, not grappling hook, railgun", + effect() { + tech.isHarpoonPowerUp = true + }, + remove() { + tech.isHarpoonPowerUp = false + } + }, + { + name: "quasiparticles", + descriptionFunction() { + return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)} which
give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return ((tech.haveGunCheck("wave") && !tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo + }, + requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", + effect() { + tech.isBoostReplaceAmmo = true + for (let i = powerUp.length - 1; i > -1; i--) { + if (powerUp[i].name === "ammo") { + powerUps.spawn(powerUp[i].position.x + 50 * (Math.random() - 0.5), powerUp[i].position.y + 50 * (Math.random() - 0.5), "boost"); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + } + } + + }, + remove() { + tech.isBoostReplaceAmmo = false + } + }, + { + name: "optical amplifier", + description: "gain 3 random laser guntech
laser only turns off if you have no energy", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isNonRefundable: true, + allowed() { + return tech.haveGunCheck("laser") && !tech.isPulseLaser + }, + requires: "laser gun, not pulse", + effect() { + let techGiven = 0 + for (let j = 0; j < 3; j++) { + const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] + //convert names into indexes + const options = [] + for (let i = 0; i < names.length; i++) { + for (let k = 0; k < tech.tech.length; k++) { + if (tech.tech[k].name === names[i]) { + options.push(k) + break + } + } + } + //remove options that don't meet requirements + for (let i = options.length - 1; i > -1; i--) { + const index = options[i] + if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) { + options.splice(i, 1); + } + } + //pick one option + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360); + tech.giveTech(index) + techGiven++ + } + } + if (techGiven > 0) { + tech.isStuckOn = true + } else { //eject if none found + simulation.makeTextLog(`0 tech found //optical amplifier`); + const loop = () => { + if (!simulation.paused && m.alive) { + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === this.name) powerUps.ejectTech(i) + } + return + } + requestAnimationFrame(loop); + } + requestAnimationFrame(loop); + } + }, + remove() { + tech.isStuckOn = false + } + }, + { + name: "relativistic momentum", + description: "lasers push mobs and blocks", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserField + }, + requires: "laser, not pulse", + effect() { + tech.isLaserPush = true; + }, + remove() { + tech.isLaserPush = false; + } + }, + { + name: "iridescence", + // description: "if a laser hits a mob at a low angle of illumination
+66% laser damage", + description: "if laser beams hit mobs near their center
+100% laser damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserMine + }, + requires: "laser, not pulse", + effect() { + tech.laserCrit += 1; + }, + remove() { + tech.laserCrit = 0; + } + }, + { + name: "lens", + description: "+150% laser gun damage if it passes
through a revolving 90° arc circular lens", //π / 2
+ isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") + }, + requires: "laser", + effect() { + tech.isLaserLens = true + b.guns[11].chooseFireMethod() + // if (this.count > 0) b.guns[11].lensDamageOn += 20 * Math.PI / 180 + // b.guns[11].arcRange = 0.78 + }, + remove() { + tech.isLaserLens = false + b.guns[11].chooseFireMethod() + // b.guns[11].lensDamageOn = 2.5 // 100% + 150% + // b.guns[11].arcRange = 0 + } + }, + { + name: "compound lens", + description: "+50% laser lens damage
+15° lens arc", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.isLaserLens + }, + requires: "lens", + effect() { + b.guns[11].arcRange += 15 * Math.PI / 180 / 2 + b.guns[11].lensDamageOn += 0.5 + }, + remove() { + b.guns[11].arcRange = 90 * Math.PI / 180 / 2 //0.78 divded by 2 because of how it's drawn + b.guns[11].lensDamageOn = 2.5 + } + }, + { + name: "specular reflection", + description: "+2 laser beam reflections", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser, not diffuse beam, pulse, or slow light", + effect() { + tech.laserReflections += 2; + }, + remove() { + tech.laserReflections = 2; + } + }, + { + name: "diffraction grating", + description: `+1 diverging laser gun beam`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.historyLaser + }, + requires: "laser gun, diffuse beam, or slow light", + effect() { + tech.beamSplitter++ + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.beamSplitter !== 0) { + tech.beamSplitter = 0 + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "diffuse beam", + link: `diffuse beam`, + description: "laser gun beam is wider and doesn't reflect
+220% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse", + effect() { + if (tech.wideLaser === 0) tech.wideLaser = 3 + tech.isWideLaser = true; + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isWideLaser) { + // tech.wideLaser = 0 + tech.isWideLaser = false; + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "output coupler", + description: "+30% laser gun beam width
+30% laser damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.isWideLaser + }, + requires: "laser gun, diffuse beam", + effect() { + tech.wideLaser += 2 + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isWideLaser) { + tech.wideLaser = 3 + } else { + tech.wideLaser = 0 + } + b.guns[11].chooseFireMethod() + } + }, + { + name: "slow light", + description: "laser gun beam is spread into your recent past
+300% total beam damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", + effect() { + // this.description = `add 5 more laser beams into into your past` + tech.historyLaser++ + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.historyLaser) { + tech.historyLaser = 0 + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "infrared diode", + description: "+60% laser energy efficiency
infrared light is outside visual perception", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not free-electron, pulse", + effect() { + tech.laserDrain *= 0.4; //100%-50% + tech.laserColor = "transparent" //"rgb(255,0,20,0.02)" + // tech.laserColorAlpha = "rgba(255,0,20,0.05)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserColor = "#f02" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "dye laser", + description: "+25% laser energy efficiency
+25% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not pulse, infrared diode", + effect() { + tech.laserDrain *= 0.75 + tech.laserDamage *= 1.25 + tech.laserColor = "rgb(0, 11, 255)" + tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 + tech.laserColor = "#f00" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "free-electron laser", + description: "–250% laser energy efficiency
+200% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not pulse, infrared diode", + effect() { + tech.laserDrain *= 1 + 2.5 //250% more drain + tech.laserDamage *= 1 + 2 //190% more damage + tech.laserColor = "#83f" + tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 + tech.laserColor = "#f00" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "pulse", + description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDrain === 0.0018 && !tech.isStuckOn + }, + requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier", + effect() { + tech.isPulseLaser = true; + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isPulseLaser) { + tech.isPulseLaser = false; + b.guns[11].chooseFireMethod() + } + } + }, + //************************************************** + //************************************************** field + //************************************************** tech + //************************************************** + { + name: "spherical harmonics", + description: "+50% standing wave deflection efficiency
shield deflection radius maintains it's maximum range", //standing wave oscillates in a 3rd dimension
+ isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 1 && !tech.isLaserField + }, + requires: "standing wave, not surface plasmons", + effect() { + tech.harmonics++ + m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) + m.harmonicShield = m.harmonicAtomic + }, + remove() { + tech.harmonics = 2 + m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) + m.harmonicShield = m.harmonic3Phase + } + }, + { + name: "surface plasmons", + description: "if deflecting drains all your energy
emit laser beams that scale with max energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 1 && tech.harmonics === 2 + }, + requires: "standing wave", + effect() { + tech.isLaserField = true + }, + remove() { + tech.isLaserField = false + } + }, + { + name: "zero point energy", + description: `use ${powerUps.orb.research(2)}
+100 maximum energy`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldMode === 1 || m.fieldMode === 8 || m.fieldMode === 6) && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "standing wave, pilot wave, time dilation", + effect() { + tech.harmonicEnergy = 1 + m.setMaxEnergy() + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.harmonicEnergy = 0; + m.setMaxEnergy() + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "expansion", + description: "using standing wave field expands its radius
+40 maximum energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 1 + }, + requires: "standing wave", + effect() { + tech.isStandingWaveExpand = true + m.setMaxEnergy() + // m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) + }, + remove() { + tech.isStandingWaveExpand = false + m.setMaxEnergy() + // m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicRadius = 1 + } + }, + { + name: "electronegativity", + descriptionFunction() { + return `+0.22% damage per current stored energy
(+${(22 * m.maxEnergy).toFixed(0)}% damage at max energy)` + }, + // description: "+1% damage per 8 stored energy", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 1 || m.fieldMode === 9 || m.fieldMode === 8 + }, + requires: "standing wave, wormhole, pilot wave", + effect() { + tech.energyDamage++ + }, + remove() { + tech.energyDamage = 0; + } + }, + { + name: "bremsstrahlung", + description: "deflecting and thrown blocks
do braking damage to mobs", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 1 || m.fieldMode === 2 || m.fieldMode === 8 + }, + requires: "standing wave, perfect diamagnetism, pilot wave", + effect() { + tech.blockDmg += 5 //if you change this value also update the for loop in the electricity graphics in m.pushMass + }, + remove() { + tech.blockDmg = 0; + } + }, + { + name: "cherenkov radiation", //deflecting and blocks + description: "bremsstrahlung's effects are radioactive
+250% damage over 3 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 1 || m.fieldMode === 2 || m.fieldMode === 8) && tech.blockDmg + }, + requires: "bremsstrahlung", + effect() { + tech.isBlockRadiation = true + }, + remove() { + tech.isBlockRadiation = false; + } + }, + { + name: "flux pinning", + description: "after deflecting a mob
it is stunned for up to 4 seconds", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 2 || m.fieldMode === 1 || m.fieldMode === 4 + }, + requires: "a field that can block", + effect() { + tech.isStunField += 240; + }, + remove() { + tech.isStunField = 0; + } + }, + { + name: "triple point", + descriptionFunction() { + return `+1.5 second ice IX freeze and spawn ${powerUps.orb.coupling(10)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + }, + isFieldTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 2 + }, + requires: "perfect diamagnetism", + effect() { + tech.iceIXFreezeTime += 90 + powerUps.spawnDelay("coupling", 10) + }, + remove() { + tech.iceIXFreezeTime = 150 + if (this.count) m.couplingChange(-10 * this.count) + } + }, + { + name: "eddy current brake", + description: "perfect diamagnetism slows nearby mobs
effect radius scales with stored energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 2 && !tech.isHealBrake + }, + requires: "perfect diamagnetism, not induction brake", + effect() { + tech.isPerfectBrake = true; + }, + remove() { + tech.isPerfectBrake = false; + } + }, + { + name: "Meissner effect", + description: "+55% perfect diamagnetism radius
+22° perfect diamagnetism circular arc", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 2 + }, + requires: "perfect diamagnetism", + effect() { + tech.isBigField = true; + }, + remove() { + tech.isBigField = false; + } + }, + { + name: "tessellation", + description: `use ${powerUps.orb.research(2)}
+50% defense`, + // description: "use 4 research
reduce defense by 50%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldMode === 8 || m.fieldMode === 2 || m.fieldMode === 3) && (build.isExperimentSelection || powerUps.research.count > 3) + }, + requires: "perfect diamagnetism, negative mass, pilot wave", + effect() { + tech.isFieldHarmReduction = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isFieldHarmReduction = false + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "radiative equilibrium", + description: "after losing health
+200% damage for 8 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 8 || m.fieldMode === 3 + }, + requires: "negative mass, pilot wave", + effect() { + tech.isHarmDamage = true; + }, + remove() { + tech.isHarmDamage = false; + } + }, + { + name: "dynamic equilibrium", + descriptionFunction() { + // return `increase damage by your defense and
5% of your last ${tech.isEnergyHealth ? "energy" : "health"} loss   (+${(100 * Math.max(5, tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` + return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
scales with defense   (+${(100 * Math.max(5, tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` + }, // = +${10*m.defense()}% + // descriptionFunction() { return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(tech.lastHitDamage).toFixed(0)}%)(${(100*m.lastHit).toFixed(0)} ${tech.isEnergyHealth ? "energy" : "health"})(${2 - m.defense()} defense) = ${(100*tech.lastHitDamage * m.lastHit * (2 - m.defense())).toFixed(0)}% damage ` }, // = +${10*m.defense()}% + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 8 || m.fieldMode === 3) && !tech.isCloakHealLastHit + }, + requires: "negative mass, pilot wave, not patch", + effect() { + tech.lastHitDamage += 4; + }, + remove() { + tech.lastHitDamage = 0; + } + }, + { + name: "neutronium", + description: `move and jump 20% slower
if your field is active +90% defense`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 3 + }, + requires: "negative mass", + effect() { + tech.isNeutronium = true + tech.baseFx *= 0.8 + tech.baseJumpForce *= 0.8 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isNeutronium = false + if (!tech.isFreeWormHole) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "aerostat", + description: `+88% damage while off the ground
-22% damage while on the ground`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 3 + }, + requires: "negative mass", + effect() { + tech.isNoGroundDamage = true + }, + remove() { + tech.isNoGroundDamage = false + } + }, + { + name: "annihilation", + description: "after colliding with non-boss mobs
they are annihilated and –33% energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 3 && !tech.isEnergyHealth + }, + requires: "negative mass, not mass-energy", + effect() { + tech.isAnnihilation = true + }, + remove() { + tech.isAnnihilation = false; + } + }, + { + name: "inertial mass", + description: "negative mass is larger and faster
blocks also move horizontally with the field", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 3 + }, + requires: "negative mass", + effect() { + tech.isFlyFaster = true + }, + remove() { + tech.isFlyFaster = false; + } + }, + // { + // name: "Bose Einstein condensate", + // description: "use energy to freeze mobs in your field
pilot wave, negative mass, time dilation", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldMode === 8 || m.fieldMode === 3 || (m.fieldMode === 6 && !tech.isRewindField) + // }, + // requires: "pilot wave, negative mass, time dilation, not retrocausality", + // effect() { + // tech.isFreezeMobs = true + // }, + // remove() { + // tech.isFreezeMobs = false + // } + // }, + { + name: "bot manufacturing", + description: `use ${powerUps.orb.research(2)} to build
3 random bots`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + allowed() { + return powerUps.research.count > 1 && (m.fieldMode === 4 || m.fieldMode === 8) + }, + requires: "molecular assembler, pilot wave", + effect() { + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + m.energy = 0.01; + b.randomBot() + b.randomBot() + b.randomBot() + }, + remove() { } + }, + { + name: "bot prototypes", + description: `use ${powerUps.orb.research(3)}to build 2 random bots
and upgrade all bots to that type`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + allowed() { + return powerUps.research.count > 2 && (m.fieldMode === 4 || m.fieldMode === 8) + }, + requires: "molecular assembler, pilot wave", + effect() { + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + //fill array of available bots + const notUpgradedBots = [] + const num = 2 + notUpgradedBots.push(() => { + tech.giveTech("nail-bot upgrade") + for (let i = 0; i < num; i++) { + b.nailBot() + tech.nailBotCount++; + } + simulation.makeTextLog(`tech.isNailBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < num; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("sound-bot upgrade") + for (let i = 0; i < num; i++) { + b.soundBot() + tech.soundBotCount++; + } + simulation.makeTextLog(`tech.isSoundBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("boom-bot upgrade") + for (let i = 0; i < num; i++) { + b.boomBot() + tech.boomBotCount++; + } + simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("laser-bot upgrade") + for (let i = 0; i < num; i++) { + b.laserBot() + tech.laserBotCount++; + } + simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("orbital-bot upgrade") + for (let i = 0; i < num; i++) { + b.orbitBot() + tech.orbitBotCount++; + } + simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) + }) + for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler + notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + for (let i = 0; i < num; i++) { + b.dynamoBot() + tech.dynamoBotCount++; + } + simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) + }) + } + notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it + }, + remove() { } + }, + // { + // name: "mycelium manufacturing", + // link: `mycelium manufacturing`, + // // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, + // descriptionFunction() { return `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow ${b.guns[6].nameString('s')}` }, + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldMode === 4 && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isSporeField = true; + // }, + // remove() { + // tech.isSporeField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + // { + // name: "missile manufacturing", + // link: `missile manufacturing`, + // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, + // // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldMode === 4 && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isMissileField = true; + // }, + // remove() { + // tech.isMissileField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + // { + // name: "ice IX manufacturing", + // link: `ice IX manufacturing`, + // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, + // // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldMode === 4 && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isIceField = true; + // }, + // remove() { + // tech.isIceField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + { + name: "additive manufacturing", + description: "hold crouch and use your field to print a block
with +80% density, damage, and launch speed", + // description: "simultaneously fire and activate your field to make
molecular assembler print a throwable block
+80% block throwing speed", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 4 || m.fieldMode === 8) && !tech.isTokamak + }, + requires: "molecular assembler, pilot wave, not tokamak", + effect() { + tech.isPrinter = true; + }, + remove() { + if (this.count > 0) m.holdingTarget = null; + tech.isPrinter = false; + } + }, + { + name: "pair production", + description: "after picking up a power up
+200 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 4 || m.fieldMode === 1 || m.fieldMode === 8 + }, + requires: "molecular assembler, pilot wave, standing wave", + effect() { + tech.isMassEnergy = true // used in m.grabPowerUp + m.energy += 2 + }, + remove() { + tech.isMassEnergy = false; + } + }, + { + name: "electric generator", + description: "after deflecting mobs
molecular assembler generates +50 energy", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 4 + }, + requires: "molecular assembler", + effect() { + tech.deflectEnergy += 0.5; + }, + remove() { + tech.deflectEnergy = 0; + } + }, + { + name: "combinatorial optimization", + description: "+35% damage
–35% fire rate", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 5 || m.fieldMode === 6 || m.fieldMode === 7 || m.fieldMode === 8 || m.fieldMode === 4 + }, + requires: "cloaking, molecular assembler, plasma torch, pilot wave", + damage: 1.35, + effect() { + tech.damage *= this.damage + tech.aimDamage = 1.35 + b.setFireCD(); + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.aimDamage = 1 + b.setFireCD(); + } + }, + { + name: "tokamak", + description: "throwing a block converts it into energy
and a pulsed fusion explosion", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 5 || m.fieldMode === 4) && !tech.isPrinter + }, + requires: "plasma torch, molecular assembler, not printer", + effect() { + tech.isTokamak = true; + }, + remove() { + tech.isTokamak = false; + } + }, + { + name: "degenerate matter", + description: "if your field is active
+75% defense", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 5 || m.fieldMode === 4 || m.fieldMode === 2 || m.fieldMode === 8) + }, + requires: "molecular assembler, plasma torch, perfect diamagnetism, pilot wave", + effect() { + tech.isHarmReduce = true + }, + remove() { + tech.isHarmReduce = false; + } + }, + { + name: "plasma-bot", + link: `plasma-bot`, + description: `use ${powerUps.orb.research(2)}to trade your field
for a bot that uses energy to emit plasma`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBot: true, + isBotTech: true, + allowed() { + return m.fieldMode === 5 && !tech.isPlasmaBall && !tech.isExtruder && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "plasma torch, not extruder, plasma ball", + effect() { + tech.plasmaBotCount++; + b.plasmaBot(); + if (build.isExperimentSelection) { + document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); + document.getElementById("field-0").classList.add("build-field-selected"); + } + m.setField("field emitter") + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + if (this.count > 0) { + tech.plasmaBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (m.fieldMode === 0) { + m.setField("plasma torch") + if (build.isExperimentSelection) { + document.getElementById("field-0").classList.remove("build-field-selected"); + document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected"); + } + } + powerUps.research.changeRerolls(2) + } + } + }, + { + name: "plasma jet", + link: `plasma jet`, + description: `use ${powerUps.orb.research(2)}
+50% plasma torch range`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.plasmaBotCount || m.fieldMode === 5) && (build.isExperimentSelection || powerUps.research.count > 1) && !tech.isPlasmaBall + }, + requires: "plasma torch, not plasma ball", + effect() { + tech.isPlasmaRange += 0.5; + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isPlasmaRange = 1; + if (this.count > 0) powerUps.research.changeRerolls(this.count * 2) + } + }, + { + name: "extruder", + description: "extrude a thin hot wire of plasma
increases damage and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 5 && !tech.isPlasmaBall + }, + requires: "plasma torch, not plasma ball", + effect() { + tech.isExtruder = true; + m.fieldUpgrades[m.fieldMode].set() + }, + remove() { + tech.isExtruder = false; + if (this.count && m.fieldMode === 5) m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "refractory metal", + description: "extrude metals at a higher temperature
increases effective radius and damage", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 5 && tech.isExtruder + }, + requires: "extruder", + effect() { + tech.extruderRange += 55 + }, + remove() { + tech.extruderRange = 15 + } + }, + { + name: "plasma ball", + description: "grow an expanding ball of plasma
increases damage and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 5 && !tech.isExtruder && tech.isPlasmaRange === 1 + }, + requires: "plasma torch, not extruder, plasma jet", + effect() { + tech.isPlasmaBall = true; + m.fieldUpgrades[m.fieldMode].set() + }, + remove() { + tech.isPlasmaBall = false; + if (this.count && m.fieldMode === 5) m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "corona discharge", + description: "increase the range and frequency
of plasma ball's electric arc ", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 5 && tech.isPlasmaBall + }, + requires: "plasma ball", + effect() { + tech.plasmaDischarge += 0.03 + }, + remove() { + tech.plasmaDischarge = 0.01 //default chance per cycle of a discharge + } + }, + { + name: "retrocausality", + description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldMode === 6 && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isTimeSkip + }, + requires: "time dilation, not CPT symmetry", + effect() { + tech.isRewindField = true; + m.fieldUpgrades[6].set() + m.wakeCheck(); + }, + remove() { + tech.isRewindField = false; + if (this.count) m.fieldUpgrades[6].set() + } + }, + { + name: "frame-dragging", //"non-inertial frame", + description: "when not moving time dilation stops time
+33% defense", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldMode === 6 + }, + requires: "time dilation", + effect() { + tech.isTimeStop = true; + m.fieldHarmReduction = 0.66; //33% reduction + }, + remove() { + tech.isTimeStop = false; + if (m.fieldMode === 6) m.fieldHarmReduction = 1; + } + }, + { + name: "Lorentz transformation", + description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldMode === 6 || m.fieldMode === 8) && (build.isExperimentSelection || powerUps.research.count > 2) + }, + requires: "time dilation or pilot wave", + effect() { + tech.isFastTime = true + m.setMovement(); + b.setFireCD(); + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isFastTime = false + m.setMovement(); + b.setFireCD(); + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + { + name: "time crystals", + // description: "+150% passive energy generation
${}", + descriptionFunction() { + return `+150% passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isGroundState && (m.fieldMode === 6 || m.fieldMode === 8) + }, + requires: "time dilation or pilot wave, not ground state", + effect() { + tech.isTimeCrystals = true + m.setFieldRegen() + this.descriptionFunction = function () { + return `+150% passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` + } + }, + remove() { + tech.isTimeCrystals = false + m.setFieldRegen() + this.descriptionFunction = function () { + return `+150% passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + } + } + }, + { + name: "no-cloning theorem", + // descriptionFunction() { return `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication (${tech.duplicationChance()})` }, + description: `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 6 || m.fieldMode === 7) + }, + requires: "cloaking, time dilation", + effect() { + tech.cloakDuplication = 0.45 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); + }, + remove() { + tech.cloakDuplication = 0 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } + }, + { + name: "metamaterial absorber", //quantum eraser + descriptionFunction() { + return `for each mob left alive after you exit a level
there is a 17% chance to spawn a random power up` + }, + // descriptionFunction() { + // return `for each mob left alive after you exit a level
` + // }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 7) && !tech.cloakDuplication + }, + requires: "cloaking", + effect() { + tech.isQuantumEraser = true + }, + remove() { + tech.isQuantumEraser = false + } + }, + { + name: "symbiosis", + descriptionFunction() { + return `after a boss dies spawn ${powerUps.orb.research(3)}${powerUps.orb.heal(3)} and a tech
after a mob dies –0.5 maximum ${tech.isEnergyHealth ? "energy" : "health"}` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 7 //|| m.fieldMode === 6 + }, + requires: "cloaking", + effect() { + tech.isAddRemoveMaxHealth = true + }, + remove() { + tech.isAddRemoveMaxHealth = false + } + }, + { + name: "boson composite", + link: `boson composite`, + description: "while cloaked you are intangible
to blocks and mobs, but mobs drain energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 7 + }, + requires: "metamaterial cloaking", + effect() { + tech.isIntangible = true; + }, + remove() { + if (tech.isIntangible) { + tech.isIntangible = false; + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } + }, + { + name: "patch", + link: `patch`, + description: "after cloaking recover 75% of your
last health loss using that much energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 7 && !tech.lastHitDamage && !tech.isEnergyHealth + }, + requires: "metamaterial cloaking, not dynamic equilibrium, mass-energy", + effect() { + tech.isCloakHealLastHit = true; + }, + remove() { + tech.isCloakHealLastHit = false; + } + }, + { + name: "dazzler", + link: `dazzler`, + description: "after decloaking stun nearby mobs
and drain –15 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 7 + }, + requires: "metamaterial cloaking", + effect() { + tech.isCloakStun = true; + }, + remove() { + tech.isCloakStun = false; + } + }, + // { + // name: "ambush", + // description: "metamaterial cloaking field damage effect
is increased from 333% to 555%", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldMode === 7 + // }, + // requires: "metamaterial cloaking", + // effect() { + // tech.sneakAttackDmg = 6.55 //555% + 100% + // }, + // remove() { + // tech.sneakAttackDmg = 4.33 //333% + 100% + // } + // }, + { + name: "dynamical systems", + description: `use ${powerUps.orb.research(2)}
+35% damage`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldMode === 5 || m.fieldMode === 7 || m.fieldMode === 8) && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "cloaking, pilot wave, or plasma torch", + damage: 1.35, + effect() { + tech.damage *= this.damage + tech.isCloakingDamage = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isCloakingDamage = false + if (this.count > 0) { + tech.damage /= this.damage + powerUps.research.changeRerolls(2) + } + } + }, + { + name: "WIMPs", + description: `at the end of each level spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 || m.fieldMode === 8 || m.fieldMode === 6 + }, + requires: "wormhole, pilot wave, time dilation", + effect() { + tech.wimpCount++ + spawn.WIMP() + for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) + }, + remove() { + tech.wimpCount = 0 + } + }, + { + name: "vacuum fluctuation", + description: `use ${powerUps.orb.research(3)}
+11% chance to duplicate spawned power ups`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldMode === 8 || m.fieldMode === 3 || m.fieldMode === 6 || m.fieldMode === 9) && (build.isExperimentSelection || powerUps.research.count > 2) + }, + requires: "wormhole, time dilation, negative mass, pilot wave", + effect() { + tech.fieldDuplicate = 0.11 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.fieldDuplicate = 0 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + // { + // name: "Penrose process", + // description: "after a block falls into a wormhole
+50 energy", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldMode === 9 + // }, + // requires: "wormhole", + // effect() { + // tech.isWormholeEnergy = true + // }, + // remove() { + // tech.isWormholeEnergy = false + // } + // }, + { + name: "transdimensional worms", + link: `transdimensional worms`, + description: "after a block falls into a wormhole
spawn a worm", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 + }, + requires: "wormhole", + effect() { + tech.isWormholeWorms = true + }, + remove() { + tech.isWormholeWorms = false + } + }, + { + name: "geodesics", + description: `your bullets can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(4)}`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 + }, + requires: "wormhole", + effect() { + tech.isWormHoleBullets = true + for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); + }, + remove() { + if (tech.isWormHoleBullets) { + for (let i = 0; i < 2; i++) { + if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun + } + tech.isWormHoleBullets = false; + } + } + }, + { + name: "cosmic string", + description: "after tunneling through mobs with a wormhole
stun them and do radioactive damage", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 + }, + requires: "wormhole", + effect() { + tech.isWormholeDamage = true + }, + remove() { + tech.isWormholeDamage = false + } + }, + { + name: "invariant", + description: "while placing your wormhole
use energy to pause time", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 && !tech.isNoDraftPause + }, + requires: "wormhole, not eternalism", + effect() { + tech.isWormHolePause = true + }, + remove() { + if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); + tech.isWormHolePause = false + } + }, + { + name: "charmed baryons", + description: `–33% movement and jumping
wormholes drain zero energy`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 && !tech.isWormholeMapIgnore + }, + requires: "wormhole, not affine connection", + effect() { + tech.isFreeWormHole = true + tech.baseFx *= 0.66 + tech.baseJumpForce *= 0.66 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isFreeWormHole = false + if (!tech.isNeutronium) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "affine connection", + description: "wormholes can tunnel through anything
for +200% energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 9 && !tech.isFreeWormHole + }, + requires: "wormhole, not charmed baryons", + effect() { + tech.isWormholeMapIgnore = true + }, + remove() { + tech.isWormholeMapIgnore = false + } + }, + //************************************************** + //************************************************** experimental + //************************************************** modes + //************************************************** + // { + // name: "-ship-", + // description: "experiment: fly around with no legs
aim with the keyboard", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + // }, + // requires: "", + // effect() { + // m.shipMode() + // }, + // remove() {} + // }, + // { + // name: "-quantum leap-", + // description: "experiment: every 20 seconds
become an alternate version of yourself", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // interval: undefined, + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // m.switchWorlds() + // simulation.trails() + // } + // }, 20000); //every 20 seconds + + // }, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-shields-", + // description: "experiment: every 5 seconds
all mobs gain a shield", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < mob.length; i++) { + // if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); + // } + // } + // }, 5000); //every 5 seconds + // }, + // interval: undefined, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-Fourier analysis-", + // description: "experiment: your aiming is random", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection && !m.isShipMode + // }, + // requires: "not ship", + // effect() { + // m.look = () => { + // m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + // const scale = 0.8; + // m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + // m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + // m.transX += (m.transSmoothX - m.transX) * 0.07; + // m.transY += (m.transSmoothY - m.transY) * 0.07; + // } + // }, + // remove() { + // if (this.count > 0) m.look = m.lookDefault() + // } + // }, + // { + // name: "-panopticon-", + // description: "experiment: mobs can always see you", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < mob.length; i++) { + // if (!mob[i].shield && mob[i].isDropPowerUp) { + // mob[i].locatePlayer() + // mob[i].seePlayer.yes = true; + // } + // } + // } + // }, 1000); //every 1 seconds + // }, + // interval: undefined, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-decomposers-", + // description: "experiment: after they die
mobs leave behind spawns", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // tech.deathSpawns = 0.2 + // }, + // remove() { + // tech.deathSpawns = 0 + // } + // }, + + + + //************************************************** + //************************************************** JUNK + //************************************************** tech + //************************************************** + // { + // name: "junk", + // description: "", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + + // }, + // remove() {} + // }, + { + name: "swap meet", + description: "normal tech become JUNK
and JUNK become normal tech", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + tech.tech[i].isJunk = !tech.tech[i].isJunk + if (tech.tech[i].isJunk) { } else { } + + if (tech.tech[i].frequency > 0) { + tech.tech[i].frequency = 0 + } else { + tech.tech[i].frequency = 2 + } + } + }, + remove() { } + }, + // { + // name: "pocket dimension", + // description: "rotate tech descriptions into a higher spacial dimension", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // isNonRefundable: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // document.getElementById("choose-grid").classList.add("flipX"); + // }, + // remove() {} + // }, + { + name: "random", + link: `random`, + delay: 333, + descriptionFunction() { + const delay = 333 + const loop = () => { + if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { + const dmg = Math.floor(27 * Math.random()) * 0.01 + this.text = `+${(dmg * 100).toFixed(0).padStart(2, '0')}% damage` + this.damage = 1 + dmg + if (document.getElementById(`damage-JUNK-id${this.id}`)) document.getElementById(`damage-JUNK-id${this.id}`).innerHTML = this.text + setTimeout(() => { + loop() + }, delay); + } + } + setTimeout(() => { + loop() + }, delay); + this.id++ + return `${this.text}` + }, + maxCount: 3, + count: 0, + frequency: 1, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + damage: 0, + effect() { + tech.damage *= this.damage + }, + remove() { + if (this.count > 0) tech.damage /= this.damage + } + }, + { + name: "boost", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { + powerUps.spawnDelay("boost", this.spawnCount) + }, + remove() { }, + id: 0, + text: "", + delay: 100, + spawnCount: 0, + descriptionFunction() { + let count = 9999 * Math.random() + const loop = () => { + if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) //simulation.paused || + count += 4.5 + const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03) + this.spawnCount = Math.floor(100 * Math.abs(waves)) + this.text = `spawn ${this.spawnCount.toLocaleString(undefined, { minimumIntegerDigits: 3 })} ${powerUps.orb.boost(1)}
that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text + setTimeout(() => { + loop() + }, this.delay); + } + } + setTimeout(() => { + loop() + }, this.delay); + this.id++ + return `${this.text}` + }, + }, + { + name: "placebo", + description: "+777% damage
+777% defense", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + if (Math.random() < 0.1) tech.damage *= 8.77 + }, + remove() { } + }, + { + name: "universal healthcare", + description: "make your damage negative", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + tech.damage *= -1 + }, + remove() { } + }, + // { + // name: "synchrotron", + // descriptionFunction() { + // return `power ups change into a different flavor after a boss dies` + // }, + // maxCount: 3, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // allowed: () => true, + // requires: "", + // effect() { + // }, + // remove() { + // } + // }, + { + name: "return", + description: "return to the start of the game
reduce combat difficulty by 2 levels", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + level.difficultyDecrease(simulation.difficultyMode * 2) + level.onLevel = 0 + simulation.clearNow = true //end current level + }, + remove() { } + }, + { + name: "panpsychism", + description: "awaken all blocks
blocks have a chance to spawn power ups", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + setInterval(() => { + for (let i = body.length - 1; i > -1; i--) { + if (!body[i].isNotHoldable) { + Matter.Composite.remove(engine.world, body[i]); + spawn.blockMob(body[i].position.x, body[i].position.y, body[i], 0); + if (!body[i].isAboutToBeRemoved) mob[mob.length - 1].isDropPowerUp = true + body.splice(i, 1); + } + } + }, 6000); + }, + remove() { } + }, + { + name: "meteor shower", + description: "take a shower, but meteors instead of water", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + setInterval(() => { + + fireBlock = function (xPos, yPos) { + const index = body.length + spawn.bodyRect(xPos, yPos, 20 + 50 * Math.random(), 20 + 50 * Math.random()); + const bodyBullet = body[index] + Matter.Body.setVelocity(bodyBullet, { + x: 5 * (Math.random() - 0.5), + y: 10 * (Math.random() - 0.5) + }); + bodyBullet.isAboutToBeRemoved = true + setTimeout(() => { //remove block + for (let i = 0; i < body.length; i++) { + if (body[i] === bodyBullet) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + }, 4000 + Math.floor(9000 * Math.random())); + } + fireBlock(player.position.x + 600 * (Math.random() - 0.5), player.position.y - 500 - 500 * Math.random()); + // for (let i = 0, len = Math.random(); i < len; i++) { + // } + + }, 1000); + }, + remove() { } + }, + { + name: "startle response", + description: `if a threat is nearby, activate a ${powerUps.orb.boost(1)}
and lock your mouse until you press escape`, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + // tech.damage *= 1.33 + setInterval(() => { + if (powerUps.boost.endCycle < m.cycle && !simulation.paused && m.alive) { + for (let i = 0; i < mob.length; i++) { + if (mob[i].distanceToPlayer2() < 400000) { //650 + canvas.requestPointerLock(); + powerUps.boost.effect(); + break + } + } + } + }, 2000); + }, + remove() { } + }, + { + name: "closed timelike curve", + description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future or past", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + + function loop() { + if (!simulation.paused && m.alive) { + if (!(simulation.cycle % 720)) { + requestAnimationFrame(() => { + if ((simulation.cycle % 1440) > 720) { //kinda alternate between each option + m.rewind(60) + m.energy += 0.4 //to make up for lost energy + } else { + simulation.timePlayerSkip(60) + } + }); //wrapping in animation frame prevents errors, probably + } + } + requestAnimationFrame(loop); + } + requestAnimationFrame(loop); + }, + remove() { } + }, + // { + // name: "translate", + // description: "translate n-gon into a random language", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // isNonRefundable: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // // generate a container + // const gtElem = document.createElement('div') + // gtElem.id = "gtElem" + // gtElem.style.visibility = 'hidden' // make it invisible + // document.body.append(gtElem) + + // // generate a script to run after creation + // function initGT() { + // // create a new translate element + // new google.translate.TranslateElement({ pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL }, 'gtElem') + // // ok now since it's loaded perform a funny hack to make it work + // const langSelect = document.getElementsByClassName("goog-te-combo")[0] + // // select a random language. It takes a second for all langauges to load, so wait a second. + // setTimeout(() => { + // langSelect.selectedIndex = Math.round(langSelect.options.length * Math.random()) + // // simulate a click + // langSelect.dispatchEvent(new Event('change')) + // // now make it go away + // const bar = document.getElementById(':1.container') + // bar.style.display = 'none' + // bar.style.visibility = 'hidden' + // }, 1000) + + // } + + // // add the google translate script + // const translateScript = document.createElement('script') + // translateScript.src = '//translate.google.com/translate_a/element.js?cb=initGT' + // document.body.append(translateScript) + // }, + // remove() {} + // }, + { + name: "discount", + description: "get 3 random JUNKtech for the price of 1!", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + tech.giveRandomJUNK() + tech.giveRandomJUNK() + tech.giveRandomJUNK() + }, + remove() { } + }, + // { + // name: "hi", + // description: `spawn to seed 616 `, + // maxCount: 1, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // document.getElementById("seed").placeholder = Math.initialSeed = String(616) + // Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it + // }, + // remove() {} + // }, + { + name: "Higgs phase transition", + description: "instantly spawn 5 tech, but add a chance to
remove everything with a 5 minute half-life", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + powerUps.spawn(m.pos.x + 30, m.pos.y, "tech"); + powerUps.spawn(m.pos.x + 60, m.pos.y, "tech"); + powerUps.spawn(m.pos.x, m.pos.y - 30, "tech"); + powerUps.spawn(m.pos.x + 30, m.pos.y - 60, "tech"); + + function loop() { + // (1-X)^cycles = chance to be removed //Math.random() < 0.000019 10 min + if (!simulation.paused && m.alive) { + if (Math.random() < 0.000038) { + // m.death(); + simulation.clearMap(); + simulation.draw.setPaths(); + return + } + } + requestAnimationFrame(loop); + } + requestAnimationFrame(loop); + }, + remove() { } + }, + { + name: "harvest", + description: "convert all the mobs on this level into ammo", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isDropPowerUp) { + powerUps.directSpawn(mob[i].position.x, mob[i].position.y, "ammo"); + mob[i].death(); + } + } + // for (let i = powerUp.length - 1; i > -1; i--) { + // if (powerUp[i].name !== "ammo") { + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // } + // } + }, + remove() { } + }, + { + name: "brainstorm", + description: "the tech choice menu randomizes
every 0.5 seconds for 10 seconds", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + tech.isBrainstorm = true + tech.isBrainstormActive = false + tech.brainStormDelay = 500 + }, + remove() { + tech.isBrainstorm = false + tech.isBrainstormActive = false + } + }, + { + name: "catabolysis", + description: `set your maximum health to 1
double your current ammo 10 times`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth + }, + requires: "not quenching, tungsten carbide, mass-energy", + effect() { + m.baseHealth = 0.01 + m.setMaxHealth(); + for (let i = 0; i < b.guns.length; i++) b.guns[i].ammo = b.guns[i].ammo * Math.pow(2, 10) + simulation.updateGunHUD(); + }, + remove() { } + }, + { + name: "palantír", + description: `see far away lands`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "motion sickness", + description: `disable camera smoothing`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 1.2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + // m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + // m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "facsimile", + description: `inserts a copy of your current level into the level list`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const index = Math.min(level.levels.length - 1, level.onLevel) + level.levels.splice(index, 0, level.levels[index]); + }, + remove() { } + }, + { + name: "negative friction", + description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + player.friction = -0.4 + }, + remove() { + if (this.count) player.friction = 0.002 + } + }, + { + name: "bounce", + description: "you bounce off things. It's annoying, but not that bad.", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + player.restitution = 0.9 + }, + remove() { + if (this.count) player.restitution = 0 + } + }, + { + name: "mouth", + description: "mobs have a non functional mouth", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); + } + } + }, + remove() { + mobs.draw = mobs.drawDefault + } + }, + { + name: "all-stars", + description: "make all mobs look like stars", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); + } + } + }, + remove() { + mobs.draw = mobs.drawDefault + } + }, + // draw() { + // ctx.lineWidth = 2; + // let i = mob.length; + // while (i--) { + // ctx.beginPath(); + // const vertices = mob[i].vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.fillStyle = mob[i].fill; + // ctx.strokeStyle = mob[i].stroke; + // ctx.fill(); + // ctx.stroke(); + // } + // }, + { + name: "true colors", + description: `set all power ups to their real world colors`, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect() { + // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) + const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) + powerUps.research.color = colors[0] + powerUps.heal.color = colors[1] + powerUps.ammo.color = colors[2] + powerUps.field.color = colors[3] + powerUps.tech.color = colors[4] + powerUps.gun.color = colors[5] + for (let i = 0; i < powerUp.length; i++) { + switch (powerUp[i].name) { + case "research": + powerUp[i].color = colors[0] + break; + case "heal": + powerUp[i].color = colors[1] + break; + case "ammo": + powerUp[i].color = colors[2] + break; + case "field": + powerUp[i].color = colors[3] + break; + case "tech": + powerUp[i].color = colors[4] + break; + case "gun": + powerUp[i].color = colors[5] + break; + } + } + }, + remove() { + // const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle + // powerUps.research.color = colors[0] + // powerUps.heal.color = colors[1] + // powerUps.ammo.color = colors[2] + // powerUps.field.color = colors[3] + // powerUps.tech.color = colors[4] + // powerUps.gun.color = colors[5] + // for (let i = 0; i < powerUp.length; i++) { + // switch (powerUp[i].name) { + // case "research": + // powerUp[i].color = colors[0] + // break; + // case "heal": + // powerUp[i].color = colors[1] + // break; + // case "ammo": + // powerUp[i].color = colors[2] + // break; + // case "field": + // powerUp[i].color = colors[3] + // break; + // case "tech": + // powerUp[i].color = colors[4] + // break; + // case "gun": + // powerUp[i].color = colors[5] + // break; + // } + // } + } + }, + { + name: "emergency broadcasting", + description: "emit 2 sine waveforms at 853 Hz and 960 Hz
lower your volume", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect: () => { + //setup audio context + function tone(frequency) { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.5; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = frequency; // value in hertz + oscillator1.start(); + return audioCtx + } + // let sound = tone(1050) + + function EBS() { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.3; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = 850; // value in hertz + oscillator1.start(); + + const oscillator2 = audioCtx.createOscillator(); + const gainNode2 = audioCtx.createGain(); + gainNode2.gain.value = 0.3; //controls volume + oscillator2.connect(gainNode2); + gainNode2.connect(audioCtx.destination); + oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator2.frequency.value = 957; // value in hertz + oscillator2.start(); + return audioCtx + } + let sound = EBS() + + delay = 1000 + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() + setTimeout(() => { + sound.suspend() + sound.close() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, delay); + }, + remove() { } + }, + { + name: "automatic", + description: "you can't fire when moving
always fire when at rest", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !tech.isFireMoveLock + }, + requires: "not Higgs mechanism", + effect() { + tech.isAlwaysFire = true; + b.setFireMethod(); + }, + remove() { + if (tech.isAlwaysFire) { + tech.isAlwaysFire = false + b.setFireMethod(); + } + } + }, + { + name: "hidden variable", + descriptionFunction() { + return `spawn ${powerUps.orb.heal(20)}
but hide your health bar` + }, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + for (let i = 0; i < 20; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + }, + remove() { } + }, + { + name: "not a bug", + description: "initiate a totally safe game crash for 10 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const savedfunction = simulation.drawCircle + simulation.drawCircle = () => { + const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop + } + setTimeout(() => { + simulation.drawCircle = savedfunction + canvas.width = canvas.width //clears the canvas // works on chrome at least + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, 10000); + + // for (;;) {} //freezes the tab + }, + remove() { } + }, + { + name: "spinor", + description: "the direction you aim is determined by your position", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.look = function () { + //always on mouse look + m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI + //smoothed mouse look translations + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "p-zombie", + description: "set your health to 1
all mobs, not bosses, die and resurrect as zombies", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.health = 0.01 //set health to 1 + m.displayHealth(); + for (let i = mob.length - 1; i > -1; i--) { //replace mobs with zombies + if (mob[i].isDropPowerUp && !mob[i].isBoss && mob[i].alive) { + mob[i].isSoonZombie = true + mob[i].death() + } + } + }, + remove() { } + }, + { + name: "decomposers", + description: "after they die mobs leave behind spawns", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return tech.deathSpawns === 0 + }, + requires: "", + effect() { + tech.deathSpawns = 0.2 + }, + remove() { + tech.deathSpawns = 0 + } + }, + { + name: "panopticon", + description: "mobs can always see you", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + for (let i = 0; i < mob.length; i++) { + if (!mob[i].shield && mob[i].isDropPowerUp) { + mob[i].locatePlayer() + mob[i].seePlayer.yes = true; + } + } + }, 1000); //every 1 seconds + }, + remove() { } + }, + // { + // name: "inverted mouse", + // description: "your mouse is scrambled
it's fine, just rotate it 90 degrees", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isExperimentHide: true, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return !m.isShipMode + // }, + // requires: "not ship", + // effect() { + // document.body.addEventListener("mousemove", (e) => { + // const ratio = window.innerWidth / window.innerHeight + // simulation.mouse.x = e.clientY * ratio + // simulation.mouse.y = e.clientX / ratio; + // }); + // }, + // remove() { + // // m.look = m.lookDefault + // } + // }, + { + name: "Fourier analysis", + description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "not ship", + effect() { + m.look = () => { + m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + const scale = 0.8; + simulation.mouse.y + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "disintegrated armament", + description: "spawn a gun
remove your active gun", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return b.inventory.length > 0 + }, + requires: "at least 1 gun", + effect() { + if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name) + simulation.makeGunHUD() + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + }, + remove() { } + }, + { + name: "probability", + description: "increase the frequency
of one random tech by 100", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + let options = []; //find what tech I could get + for (let i = 0, len = tech.tech.length; i < len; i++) { + if ( + tech.tech[i].count < tech.tech[i].maxCount && + tech.tech[i].allowed() && + !tech.tech[i].isJunk && + !tech.tech.isLore + ) { + options.push(i); + } + } + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + tech.tech[index].frequency = 100 + } + }, + remove() { } + }, + { + name: "encryption", + description: "secure tech information", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + String.prototype.shuffle = function () { + var a = this.split(""), + n = a.length; + + for (var i = n - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var tmp = a[i]; + a[i] = a[j]; + a[j] = tmp; + } + return a.join(""); + } + + for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() + }, + remove() { } + }, + { + name: "quantum leap", + description: "become an alternate version of yourself
every 20 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.switchWorlds() + simulation.trails() + }, 20000); //every 30 seconds + }, + remove() { } + }, + { + name: "score", + description: "Add a score to n-gon!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) + simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); + }, 10000); //every 10 seconds + }, + remove() { } + }, + { + name: "pop-ups", + description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`); + }, 30000); //every 30 seconds + }, + remove() { } + }, + { + name: "music", + description: "add music to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + window.open('https://www.youtube.com/watch?v=lEbHeSdmS-k&list=PL9Z5wjoBiPKEDhwCW2RN-VZoCpmhIojdn', '_blank') + }, + remove() { } + }, + { + name: "performance", + description: "display performance stats to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + (function () { + var script = document.createElement('script'); + script.onload = function () { + var stats = new Stats(); + document.body.appendChild(stats.dom); + requestAnimationFrame(function loop() { + stats.update(); + requestAnimationFrame(loop) + }); + }; + script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js'; + document.head.appendChild(script); + })() + //move health to the right + document.getElementById("health").style.left = "86px" + document.getElementById("health-bg").style.left = "86px" + document.getElementById("defense-bar").style.left = "86px" + document.getElementById("damage-bar").style.left = "86px" + }, + remove() { } + }, + { + name: "repartitioning", + description: "set the frequency of finding normal tech to 0
spawn 5 tech", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isJunk) { + tech.tech[i].frequency = 2 + } else { + tech.tech[i].frequency = 0 + } + } + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, + remove() { } + }, + { + name: "defragment", + description: "set the frequency of finding JUNKtech to zero", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 + } + }, + remove() { } + }, + // { + // name: "lubrication", + // description: "reduce block density and friction for this level", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // for (let i = 0; i < body.length; i++) { + // Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal + // body[i].friction = 0.01 + // } + // }, + // remove() {} + // }, + { + name: "pitch", + description: "oscillate the pitch of your world", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) + }, 16); + }, + remove() { } + }, + { + name: "flatland", + description: "map blocks line of sight", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + simulation.ephemera.push({ + name: "LoS", count: 0, do() { + const pos = m.pos + const radius = 3000 + if (!simulation.isTimeSkipping) { + const vertices = simulation.sight.circleLoS(pos, radius); + if (vertices.length) { + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + for (var i = 1; i < vertices.length; i++) { + var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2); + var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2); + if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x); + const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x); + ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[i].x, vertices[i].y) + } + } + newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2); + currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2); + if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x); + const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x); + ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + } else { + ctx.lineTo(vertices[0].x, vertices[0].y) + } + + //stroke the map, so it looks different form the line of sight + ctx.strokeStyle = "#234"; + ctx.lineWidth = 9; + ctx.stroke(simulation.draw.mapPath); //this has a pretty large impact on performance, maybe 5% worse performance + + ctx.globalCompositeOperation = "destination-in"; + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + // also see the map + // ctx.fill(simulation.draw.mapPath); + // ctx.fillStyle = "#000"; + ctx.clip(); + } + } + }, + }) + }, + remove() { } + }, + { + name: "umbra", + description: "produce a blue glow around everything
and probably some simulation lag", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + ctx.shadowColor = '#06f'; + ctx.shadowBlur = 25; + }, + remove() { } + }, + { + name: "lighter", + description: `ctx.globalCompositeOperation = "lighter"`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + ctx.globalCompositeOperation = "lighter"; + }, + remove() { } + }, + { + name: "rewind", + description: "every 10 seconds rewind 2 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.rewind(120) + m.energy += 0.4 + }, 10000); + // for (let i = 0; i < 24; i++) { + // setTimeout(() => { m.rewind(120) }, i * 5000); + // } + }, + remove() { } + }, + { + name: "undo", + description: "every 4 seconds rewind 1/2 a second", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.rewind(30) + m.energy += 0.2 + }, 4000); + }, + remove() { } + }, + { + name: "energy to mass conversion", + description: "convert your energy into blocks", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = 40; i < len; i++) { + setTimeout(() => { + m.energy -= 1 / len + where = Vector.add(m.pos, { x: 400 * (Math.random() - 0.5), y: 400 * (Math.random() - 0.5) }) + spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); + }, i * 100); + } + + }, + remove() { } + }, + { + name: "level.nextLevel()", + description: "advance to the next level", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.nextLevel(); + }, + remove() { } + }, + { + name: "reincarnation", + description: "kill all mobs and spawn new ones
(also spawn a few extra mobs for fun)", + maxCount: 3, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + spawn.setSpawnList(); + spawn.setSpawnList(); + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && !mob[i].shield && !mob[i].isBadTarget) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](mob[i].position.x, mob[i].position.y); + if (Math.random() < 0.5) spawn[pick](mob[i].position.x, mob[i].position.y); + mob[i].death(); + } + } + }, + remove() { } + }, + { + name: "expert system", + description: "spawn a tech power up
+64% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + tech.addJunkTechToPool(0.64) + }, + remove() { } + }, + { + name: "energy investment", + description: "every 10 seconds drain your energy
return it doubled 5 seconds later", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused) { + const energy = m.energy + m.energy = 0 + setTimeout(() => { //return energy + m.energy += 2 * energy + }, 5000); + } + }, 10000); + }, + remove() { } + }, + { + name: "missile launching system", + description: "fire missiles for the next 120 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 120; i++) { + setTimeout(() => { + const where = { + x: m.pos.x, + y: m.pos.y - 40 + } + b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2) + }, i * 1000); + } + }, + remove() { } + }, + { + name: "grenade production", + description: "drop a grenade every 2 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused && document.visibilityState !== "hidden") { + b.grenade(Vector.add(m.pos, { + x: 10 * (Math.random() - 0.5), + y: 10 * (Math.random() - 0.5) + }), -Math.PI / 2) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.1, + y: who.velocity.y * 0.1 + }); + } + }, 2000); + }, + remove() { } + }, + { + name: "stubs", + description: "no knees or toes are drawn on the player
you can wall climb though", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.stubs() + Matter.Body.scale(player.parts[3], 2, 2); + }, + remove() { + // if (this.count) m.resetSkin(); + } + }, + { + name: "Sleipnir", + description: "grow more legs", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.Sleipnir() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "diegesis", + description: "indicate fire cooldown

through a rotation of your head", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.diegesis() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "🐱", + description: "🐈", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.cat(); + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "n-gone", + description: "become invisible to yourself
mobs can still see you", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.draw = () => { } + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "pareidolia", + description: "don't", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.pareidolia() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "posture", + description: "stand a bit taller", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.yOffWhen.stand = 70 + }, + remove() { + m.yOffWhen.stand = 49 + } + }, + { + name: "rhythm", + description: "you oscillate up and down", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + setInterval(() => { + m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) + if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand + }, 100); + }, + remove() { } + }, + { + name: "prism", + description: "you cycle through different colors", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.color = { + hue: 0, + sat: 100, + light: 50 + } + setInterval(function () { + m.color.hue++ + m.setFillColors() + }, 10); + }, + remove() { } + }, + // { + // name: "microtransactions", + // description: `when you choose a tech you can
use ${powerUps.orb.research(1)} to buy a free in game skin`, + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // tech.isMicroTransactions = true + // }, + // remove() { + // tech.isMicroTransactions = false + // } + // }, + { + name: "ship", + description: "fly around with no legs
reduce combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode && !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + m.isAltSkin = true + m.shipMode() + level.difficultyDecrease(simulation.difficultyMode) + //unlock relativistic rotation + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === "relativistic rotation") tech.tech[i].frequency = 10 + } + }, + remove() { } + }, + { + name: "circular symmetry", + description: "turning the ship rotates the universe instead
+200% damage", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return m.isShipMode + }, + requires: "", + effect() { + tech.damage *= 3 + + m.look = () => { + // const scale = 0; + m.transSmoothX = canvas.width2 - m.pos.x // - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y // - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + ctx.restore(); + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + ctx.rotate(-m.angle) + ctx.translate(-canvas.width2, -canvas.height2); //center + } + }, + remove() { } + }, + { + name: "assimilation", + description: "all your bots are converted to the same random model", + maxCount: 1, + count: 0, + frequency: 0, + isBotTech: true, + isNonRefundable: true, + isJunk: true, + allowed() { + return b.totalBots() > 2 + }, + requires: "at least 3 bots", + effect() { + const total = b.totalBots(); + tech.dynamoBotCount = 0; + tech.nailBotCount = 0; + tech.laserBotCount = 0; + tech.orbitBotCount = 0; + tech.foamBotCount = 0; + tech.soundBotCount = 0; + tech.boomBotCount = 0; + tech.plasmaBotCount = 0; + tech.missileBotCount = 0; + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) bullet[i].endCycle = 0 + } + + const bots = [ + () => { + b.nailBot(); + tech.nailBotCount++; + }, + () => { + b.foamBot(); + tech.foamBotCount++; + }, + () => { + b.soundBot(); + tech.soundBotCount++; + }, + () => { + b.boomBot(); + tech.boomBotCount++; + }, + () => { + b.laserBot(); + tech.laserBotCount++; + }, + () => { + b.orbitBot(); + tech.orbitBotCount++ + }, + () => { + b.dynamoBot(); + tech.dynamoBotCount++ + } + ] + const index = Math.floor(Math.random() * bots.length) + for (let i = 0; i < total; i++) bots[index]() + }, + remove() { } + }, + { + name: "growth hacking", + description: "increase combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.difficultyIncrease(simulation.difficultyMode) + }, + remove() { } + }, + { + name: "stun", + description: "stun all mobs for up to 8 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) + }, + remove() { } + }, + { + name: "translucent", + description: "spawn 3 gun power ups
your bullets and bots are transparent", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + + // //removes guns and ammo + // b.inventory = []; + // b.activeGun = null; + // b.inventoryGun = 0; + // for (let i = 0, len = b.guns.length; i < len; ++i) { + // b.guns[i].have = false; + // if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; + // } + // simulation.makeGunHUD(); //update gun HUD + b.bulletDraw = () => { }; //make bullets invisible + }, + remove() { } + }, + { + name: "re-research", + description: `eject all your ${powerUps.orb.research(1)}`, + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + powerUps.spawnDelay("research", powerUps.research.count); + powerUps.research.count = 0 + }, + remove() { } + }, + { + name: "black hole", + description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + m.energy = 0 + spawn.suckerBoss(m.pos.x, m.pos.y - 700) + powerUps.research.changeRerolls(-4) + simulation.makeTextLog(`m.research --
${powerUps.research.count}`) + }, + remove() { } + }, + { + name: "black hole cluster", + description: `spawn 30 nearby black holes`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const unit = { + x: 1, + y: 0 + } + for (let i = 0; i < 30; i++) { + const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 2000 + 1200 * Math.random())) + spawn.sucker(where.x, where.y, 140) + const who = mob[mob.length - 1] + who.locatePlayer() + // who.damageReduction = 0.2 + } + }, + remove() { } + }, + { + name: "rule 30", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { }, + remove() { }, + state: [ + [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false] + ], + rule(state, a, b, c) { + //30 + if (state[a] && state[b] && state[c]) return false; // TTT => F + if (state[a] && state[b] && !state[c]) return false; // TTF => F + if (state[a] && !state[b] && state[c]) return false; //TFT => F + if (state[a] && !state[b] && !state[c]) return true; //TFF => T + if (!state[a] && state[b] && state[c]) return true; //FTT => T + if (!state[a] && state[b] && !state[c]) return true; //FTF => T + if (!state[a] && !state[b] && state[c]) return true; //FFT => T + if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + }, + id: 0, + researchSpawned: 0, + descriptionFunction() { + const loop = () => { + if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + let b = []; //produce next row + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + } + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + this.state.push(b) + if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { + this.researchSpawned++ + powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + } + setTimeout(() => { + loop() + }, 300 + 5 * this.state.length); + } + } + setTimeout(() => { + loop() + }, 300); + this.id++ + return `${this.outputText()}` + }, + outputText() { + let text = "
"
+            for (let j = 0; j < this.state.length; j++) {
+                // text += "

" + text += "

" + for (let i = 0; i < this.state[j].length; i++) { + if (this.state[j][i]) { + text += "■" //"☻" //"⬛" //"█" //"■" + } else { + text += " " //"□" //"☺" //"⬜" //"    " //"□" + } + } + text += "

" + } + text += "
" + return text + }, + }, + { + name: "rule 90", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { }, + remove() { }, + state: [ + [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false] + ], + rule(state, a, b, c) { //90 + if (state[a] && state[b] && state[c]) return false; // TTT => F + if (state[a] && state[b] && !state[c]) return true; // TTF => T + if (state[a] && !state[b] && state[c]) return false; //TFT => F + if (state[a] && !state[b] && !state[c]) return true; //TFF => T + if (!state[a] && state[b] && state[c]) return true; //FTT => T + if (!state[a] && state[b] && !state[c]) return false; //FTF => F + if (!state[a] && !state[b] && state[c]) return true; //FFT => T + if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + }, + id: 90, + researchSpawned: 0, + descriptionFunction() { + const loop = () => { + if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + let b = []; //produce next row + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + } + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + this.state.push(b) + if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { + this.researchSpawned++ + powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + } + setTimeout(() => { + loop() + }, 300 + 5 * this.state.length); + } + } + setTimeout(() => { + loop() + }, 300); + this.id++ + return `${this.outputText()}` + }, + outputText() { + let text = "
"
+            for (let j = 0; j < this.state.length; j++) {
+                // text += "

" + text += "

" + for (let i = 0; i < this.state[j].length; i++) { + if (this.state[j][i]) { + text += "■" //"☻" //"⬛" //"█" //"■" + } else { + text += " " //"□" //"☺" //"⬜" //"    " //"□" + } + } + text += "

" + } + text += "
" + return text + }, + }, + { + name: "cosmogonic myth", + description: `open a portal to a primordial version of reality
in 5 minutes close the portal, spawn 1 of each power up
`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const urls = ["https://scratch.mit.edu/projects/14005697/fullscreen/", "https://scratch.mit.edu/projects/22573757/fullscreen/", "https://scratch.mit.edu/projects/41429974/fullscreen/", "https://scratch.mit.edu/projects/43690666/fullscreen/", "https://codepen.io/lilgreenland/full/ozXNWZ", "https://codepen.io/lilgreenland/full/wzARJY", "classic/7-1-2017/", "classic/4-15-2018/", "classic/7-11-2019/", "classic/9-8-2019/", "classic/7-15-2020/", "classic/6-1-2021/"] + const choose = urls[Math.floor(Math.random() * urls.length)] + console.log(`opening new tab" ${choose}`) + let tab = window.open(choose, "_blank"); + setTimeout(() => { + tab.close(); + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + setTimeout(() => { + powerUps.spawn(m.pos.x, m.pos.y - 50, "ammo") + }, 250); + setTimeout(() => { + powerUps.spawn(m.pos.x + 50, m.pos.y, "field"); + }, 500); + setTimeout(() => { + powerUps.spawn(m.pos.x + 50, m.pos.y - 50, "heal"); + }, 750); + setTimeout(() => { + powerUps.spawn(m.pos.x - 50, m.pos.y, "tech"); + }, 1000); + setTimeout(() => { + powerUps.spawn(m.pos.x - 50, m.pos.y - 50, "research"); + }, 1250); + }, 1000 * 5 * 60); + }, + remove() { } + }, + { + name: "planetesimals", + description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + window.open('../../planetesimals/index.html', '_blank') + // powerUps.spawn(m.pos.x, m.pos.y, "tech"); + + // for communicating to other tabs, like planetesimals + // Connection to a broadcast channel + const bc = new BroadcastChannel('planetesimals'); + bc.activated = false + + bc.onmessage = function (ev) { + if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + if (ev.data === 'death') { + m.death() + bc.close(); //end session + } + if (ev.data === 'ready' && !bc.activated) { + bc.activated = true //prevents n-gon from activating multiple copies of planetesimals + bc.postMessage("activate"); + } + } + }, + remove() { } + }, + { + name: "tinker", + description: "permanently unlock JUNKtech in experiment mode
this effect is stored for future visits", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !localSettings.isJunkExperiment + }, + requires: "", + effect() { + localSettings.isJunkExperiment = true + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + remove() { } + }, + { + name: "NFT", + descriptionFunction() { + return `buy your current game seed: ${Math.initialSeed}
no one is allowed to use your seeds
if they use them they are gonna get in trouble

your seeds: ${localSettings.personalSeeds.join(", ")}` + }, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + localSettings.personalSeeds.push(Math.initialSeed) + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + remove() { } + }, + // { + // name: "rule 90", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() {}, + // remove() {}, + // state: [ + // [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false] + // ], + // rule(state, a, b, c) { + // if (state[a] && state[b] && state[c]) return false; // TTT => F + // if (state[a] && state[b] && !state[c]) return true; // TTF => T + // if (state[a] && !state[b] && state[c]) return false; //TFT => F + // if (state[a] && !state[b] && !state[c]) return true; //TFF => T + // if (!state[a] && state[b] && state[c]) return true; //FTT => T + // if (!state[a] && state[b] && !state[c]) return false; //FTF => F + // if (!state[a] && !state[b] && state[c]) return true; //FFT => T + // if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + // }, + // id: 0, + // descriptionFunction() { + // const loop = () => { + // if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + // let b = []; //produce next row + // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + // for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + // b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + // } + // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + // this.state.push(b) + // if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + // if (this.count && this.state.length < 120 && !(this.state.length % 10)) powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + // setTimeout(() => { loop() }, 400); + // } + // } + // setTimeout(() => { loop() }, 400); + // // if (this.id === 0) { + // // for (let i = 0; i < 29; i++) this.state[0][i] = Math.random() < 0.5 //randomize seed + // // } + // this.id++ + // return `${this.outputText()}` + // }, + // outputText() { + // let text = "" + // for (let j = 0; j < this.state.length; j++) { + // text += "

" + // for (let i = 0; i < this.state[j].length; i++) { + // if (this.state[j][i]) { + // text += "⬛" //"█" //"■" + // } else { + // text += "⬜" //"    " //"□" + // } + // } + // text += "

" + // } + // return text + // }, + // }, + + //************************************************** + //************************************************** undefined / lore + //************************************************** tech + //************************************************** + { + name: `undefined`, + description: `this
 `, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isLore: true, + // isExperimentHide: true, + allowed() { return !build.isExperimentSelection }, + requires: "NOT EXPERIMENT MODE", + effect() { + if (localSettings.loreCount > lore.conversation.length - 1) { //reward for people done with lore chapters (or on the final chapter) + for (let i = mob.length - 1; i > -1; i--) { //replace mobs with starters + if (!mob[i].isBoss && mob[i].isDropPowerUp && mob[i].alive) { + spawn.starter(mob[i].position.x, mob[i].position.y) + mob[i].leaveBody = false + mob[i].isDropPowerUp = false + mob[i].death() + + //spawn a random power up + // if (Math.random() < 1 / 5) { + // powerUps.spawn(mob[i].position.x, mob[i].position.y, "research") + // } else + if (Math.random() < 1 / 4) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "ammo") + } else if (Math.random() < 1 / 3) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "heal") + } else if (Math.random() < 1 / 2) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "boost") + } else { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "coupling") + } + } + } + } + + setTimeout(() => { //a short delay, I can't remember why + lore.techCount++ + if (lore.techCount === lore.techGoal) { + // tech.removeLoreTechFromPool(); + this.frequency = 0; + this.description = `null is open at level.final()
 ` + } else { + this.frequency += lore.techGoal * 2 + // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` + // } + // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null` + } + }, 1); + }, + remove() { + lore.techCount = 0; + this.maxCount = lore.techGoal; + this.description = `this
 ` + } + } + ], + // addLoreTechToPool() { //adds lore tech to tech pool + // if (!simulation.isCheating) { + // tech.tech.push({ + // name: `undefined`, + // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, + // maxCount: 1, + // count: 0, + // frequency: 2, + // isLore: true, + // isNonRefundable: true, + // isExperimentHide: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // setTimeout(() => { //a short delay, I can't remember why + // lore.techCount++ + // if (lore.techCount > lore.techGoal - 1) { + // // tech.removeLoreTechFromPool(); + // for (let i = tech.tech.length - 1; i > 0; i--) { + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) + // } + // } else { + // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` + // } + // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + // } + // }, 1); + // }, + // remove() {} + // }) + // } + // }, + // junk: [ + + // ], + //variables use for gun tech upgrades + fireRate: null, + bulletSize: null, + energySiphon: null, + healthDrain: null, + crouchAmmoCount: null, + bulletsLastLonger: null, + isImmortal: null, + sporesOnDeath: null, + isImmuneExplosion: null, + isExplodeMob: null, + isDroneOnDamage: null, + isAcidDmg: null, + isAnnihilation: null, + largerHeals: null, + squirrelFx: null, + isCrit: null, + isLowHealthDmg: null, + isLowHealthDefense: null, + isLowHealthFireRate: null, + isFarAwayDmg: null, + isFirstDer: null, + isMassEnergy: null, + extraChoices: null, + laserBotCount: null, + dynamoBotCount: null, + nailBotCount: null, + foamBotCount: null, + soundBotCount: null, + boomBotCount: null, + plasmaBotCount: null, + missileBotCount: null, + orbitBotCount: null, + blockDmg: null, + isBlockRadiation: null, + isPiezo: null, + isFastDrones: null, + oneSuperBall: null, + laserReflections: null, + laserDamage: null, + isAmmoFromHealth: null, + mobSpawnWithHealth: null, + isEnergyRecovery: null, + isHealthRecovery: null, + isEnergyLoss: null, + isDeathAvoid: null, + isDeathAvoidedThisLevel: null, + isPlasmaRange: null, + isFreezeMobs: null, + isIceCrystals: null, + blockDamage: null, + isBlockStun: null, + isStunField: null, + isHarmDamage: null, + energyRegen: null, + isVacuumBomb: null, + renormalization: null, + fragments: null, + energyDamage: null, + botSpawner: null, + isBotSpawnerReset: null, + isSporeFollow: null, + isNailRadiation: null, + isEnergyHealth: null, + isStun: null, + restDamage: null, + isRPG: null, + missileCount: null, + isDeterminism: null, + isSuperDeterminism: null, + isHarmReduce: null, + nailsDeathMob: null, + isSlowFPS: null, + isNeutronStun: null, + isAnsatz: null, + isDamageFromBulletCount: null, + laserDrain: null, + isNailShot: null, + slowFire: null, + fastTime: null, + squirrelJump: null, + isFastRadiation: null, + isAmmoForGun: null, + isRapidPulse: null, + isSporeFreeze: null, + isShotgunRecoil: null, + isHealLowHealth: null, + isAoESlow: null, + isHarmArmor: null, + isTurret: null, + isRerollDamage: null, + isHarmFreeze: null, + isBotArmor: null, + isRerollHaste: null, + researchHaste: null, + isMineDrop: null, + isRerollBots: null, + isNailBotUpgrade: null, + isFoamBotUpgrade: null, + isSoundBotUpgrade: null, + isLaserBotUpgrade: null, + isBoomBotUpgrade: null, + isOrbitBotUpgrade: null, + isDroneGrab: null, + isOneGun: null, + isDamageForGuns: null, + isGunCycle: null, + isFastFoam: null, + isSporeGrowth: null, + isStimulatedEmission: null, + nailGun: null, + nailInstantFireRate: null, + isCapacitor: null, + isEnergyNoAmmo: null, + // isFreezeHarmImmune: null, + isSmallExplosion: null, + isExplosionHarm: null, + extraMaxHealth: null, + // bonusHealth: null, + isIntangible: null, + isCloakStun: null, + bonusEnergy: null, + // healGiveMaxEnergy: null, + healMaxEnergyBonus: 0, //not null + aimDamage: null, + isNoFireDefense: null, + isNoFireDamage: null, + duplicateChance: null, + beamSplitter: null, + iceEnergy: null, + isPerfectBrake: null, + explosiveRadius: null, + // isWormholeEnergy: null, + isWormholeDamage: null, + isNailCrit: null, + isFlechetteExplode: null, + isWormholeWorms: null, + isWormHoleBullets: null, + isWideLaser: null, + wideLaser: null, + isPulseLaser: null, + isRadioactive: null, + radioactiveDamage: null, + isRailEnergy: null, + isMineSentry: null, + isIncendiary: null, + overfillDrain: null, + isNeutronSlow: null, + // isRailAreaDamage: null, + historyLaser: null, + isSpeedHarm: null, + isSpeedDamage: null, + isTimeSkip: null, + isCancelDuplication: null, + duplication: null, + isCancelRerolls: null, + isCancelTech: null, + isBotDamage: null, + isBanish: null, + isMaxEnergyTech: null, + isLowEnergyDamage: null, + isRewindBot: null, + isRewindGrenade: null, + isExtruder: null, + isEndLevelPowerUp: null, + isMissileBig: null, + isMissileBiggest: null, + isLaserMine: null, + isFoamMine: null, + isAmmoFoamSize: null, + isIceIX: null, + isDupDamage: null, + isFireRateForGuns: null, + cyclicImmunity: null, + isTechDamage: null, + isRestHarm: null, + isFireMoveLock: null, + isRivets: null, + isNeedles: null, + isExplodeRadio: null, + isPauseSwitchField: null, + isPauseEjectTech: null, + isShieldPierce: null, + isDuplicateMobs: null, + is100Duplicate: null, + isDynamoBotUpgrade: null, + isBlockPowerUps: null, + isDamageAfterKillNoRegen: null, + isHarmReduceNoKill: null, + isSwitchReality: null, + isResearchReality: null, + isAnthropicDamage: null, + isFlipFlop: null, + isFlipFlopHarm: null, + isFlipFlopOn: null, + // isFlipFlopLevelReset: null, + isFlipFlopDamage: null, + isFlipFlopEnergy: null, + isFlipFlopChoices: null, + isRelay: null, + relayIce: null, + isMetaAnalysis: null, + isFoamAttract: null, + droneCycleReduction: null, + droneEnergyReduction: null, + isHalfHeals: null, + isAlwaysFire: null, + isDroneRespawn: null, + deathSpawns: null, + isMobBlockFling: null, + // blockingIce: null, + isPhaseVelocity: null, + waveBeamSpeed: null, + wavePacketAmplitude: null, + isCollisionRealitySwitch: null, + iceIXOnDeath: null, + wimpCount: null, + isAddBlockMass: null, + isMACHO: null, + isHarmMACHO: null, + isSneakAttack: null, + isFallingDamage: null, + harmonics: null, + isStandingWaveExpand: null, + isTokamak: null, + deflectEnergy: null, + superBallDelay: null, + isBlockExplode: null, + isOverHeal: null, + isDroneRadioactive: null, + droneRadioDamage: null, + isDroneTeleport: null, + isDroneFastLook: null, + isBulletTeleport: null, + isResearchBoss: null, + isJunkResearch: null, + junkResearchNumber: null, + laserColor: null, + laserColorAlpha: null, + isLongitudinal: null, + is360Longitudinal: null, + isShotgunReversed: null, + fieldDuplicate: null, + isCloakingDamage: null, + harmonicEnergy: null, + isFieldHarmReduction: null, + isFastTime: null, + isAnthropicTech: null, + isSporeWorm: null, + isSporeFlea: null, + isFoamShot: null, + isIceShot: null, + isBlockRestitution: null, + isZeno: null, + isFieldFree: null, + isExtraGunField: null, + isBigField: null, + isSmartRadius: null, + isFilament: null, + isLargeHarpoon: null, + extraHarpoons: null, + ammoCap: null, + isHarpoonPowerUp: null, + harpoonDensity: null, + isAddRemoveMaxHealth: null, + cloakDuplication: null, + extruderRange: null, + isForeverDrones: null, + nailRecoil: null, + baseJumpForce: null, + baseFx: null, + isNeutronium: null, + isFreeWormHole: null, + isRewindField: null, + isCrouchRegen: null, + isAxion: null, + isDarkStar: null, + isWormholeMapIgnore: null, + isLessDamageReduction: null, + needleTunnel: null, + isBrainstorm: null, + isBrainstormActive: null, + brainStormDelay: null, + wormSize: null, + extraSuperBalls: null, + isTimeCrystals: null, + isGroundState: null, + isRailGun: null, + isGrapple: null, + isImmuneGrapple: null, + isDronesTravel: null, + isTechDebt: null, + isPlasmaBall: null, + plasmaDischarge: null, + isFlipFlopHealth: null, + isRelayEnergy: null, + coyoteTime: null, + missileFireCD: null, + isBotField: null, + isFoamBall: null, + isNoDraftPause: null, + isFoamPressure: null, + foamDamage: null, + isClusterExplode: null, + isCircleExplode: null, + isPetalsExplode: null, + deathSkipTime: null, + isIceMaxHealthLoss: null, + isIceKill: null, + isCritKill: null, + isQuantumEraser: null, + isPhononBlock: null, + isPhononWave: null, + isLaserLens: null, + laserCrit: null, + isSporeColony: null, + isExtraBotOption: null, + isLastHitDamage: null, + isCloakHealLastHit: null, + isRicochet: null, + isCancelCouple: null, + isCouplingPowerUps: null, + isBoostPowerUps: null, + isBoostReplaceAmmo: null, + isInfiniteWaveAmmo: null, + isJunkDNA: null, + buffedGun: 0, + isGunChoice: null, + railChargeRate: null, + isSuperHarm: null, + isZombieMobs: null, + isSuperMine: null, + sentryAmmo: null, + collidePowerUps: null, + isDilate: null, + isDiaphragm: null, + hardLanding: null, + isNoGroundDamage: null, + isSuperBounce: null, + isDivisor: null, + isFoamCavitation: null, + isHealAttract: null, + isLaserField: null, + isHealBrake: null, + isMassProduction: null, + isPrinter: null, +} \ No newline at end of file diff --git a/ngon/js/visibility.js b/ngon/js/visibility.js new file mode 100644 index 00000000..d1686b76 --- /dev/null +++ b/ngon/js/visibility.js @@ -0,0 +1,44 @@ +// https://ncase.me/sight-and-light/ +// redblobgames.com/articles/visibility +// https://github.com/Silverwolf90/2d-visibility/tree/master/src +// could apply to explosions, neutron bomb, player LOS + + +const v = { + points: [], + populate() { + v.points = [{ + x: -150, + y: -950 + }, { + x: 710, + y: -950 + }, { + x: 710, + y: -940 + }, { + x: 710, + y: -710 + }, { + x: 710, + y: -700 + }, { + x: -150, + y: -700 + }] + }, + draw() { + ctx.beginPath(); + ctx.moveTo(v.points[0].x, v.points[0].y) + for (let i = 0, len = v.points.length; i < len; i++) { + ctx.lineTo(v.points[i].x, v.points[i].y) + } + // ctx.fillStyle = "#333" + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + } +} +v.populate(); +// console.log(v.points) \ No newline at end of file diff --git a/ngon/lib/matter.min.js b/ngon/lib/matter.min.js new file mode 100644 index 00000000..d93e50fe --- /dev/null +++ b/ngon/lib/matter.min.js @@ -0,0 +1,714 @@ +/*! + * matter-js 0.18.0-alpha+0eeceb5 by @liabru + * Experimental pre-release build. + * http://brm.io/matter-js/ + * License MIT + */ +! function(e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define("Matter", [], t) : "object" == typeof exports ? exports.Matter = t() : e.Matter = t() }(this, (function() { return function(e) { var t = {}; + + function n(i) { if (t[i]) return t[i].exports; var o = t[i] = { i: i, l: !1, exports: {} }; return e[i].call(o.exports, o, o.exports, n), o.l = !0, o.exports } return n.m = e, n.c = t, n.d = function(e, t, i) { n.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: i }) }, n.r = function(e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, n.t = function(e, t) { if (1 & t && (e = n(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var i = Object.create(null); if (n.r(i), Object.defineProperty(i, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) + for (var o in e) n.d(i, o, function(t) { return e[t] }.bind(null, o)); return i }, n.n = function(e) { var t = e && e.__esModule ? function() { return e.default } : function() { return e }; return n.d(t, "a", t), t }, n.o = function(e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, n.p = "", n(n.s = 21) }([function(e, t) { var n = {}; + e.exports = n, + function() { n._nextId = 0, n._seed = 0, n._nowStartTime = +new Date, n._warnedOnce = {}, n._decomp = null, n.extend = function(e, t) { var i, o; "boolean" == typeof t ? (i = 2, o = t) : (i = 1, o = !0); for (var r = i; r < arguments.length; r++) { var a = arguments[r]; if (a) + for (var s in a) o && a[s] && a[s].constructor === Object ? e[s] && e[s].constructor !== Object ? e[s] = a[s] : (e[s] = e[s] || {}, n.extend(e[s], o, a[s])) : e[s] = a[s] } return e }, n.clone = function(e, t) { return n.extend({}, t, e) }, n.keys = function(e) { if (Object.keys) return Object.keys(e); var t = []; for (var n in e) t.push(n); return t }, n.values = function(e) { var t = []; if (Object.keys) { for (var n = Object.keys(e), i = 0; i < n.length; i++) t.push(e[n[i]]); return t } for (var o in e) t.push(e[o]); return t }, n.get = function(e, t, n, i) { t = t.split(".").slice(n, i); for (var o = 0; o < t.length; o += 1) e = e[t[o]]; return e }, n.set = function(e, t, i, o, r) { var a = t.split(".").slice(o, r); return n.get(e, t, 0, -1)[a[a.length - 1]] = i, i }, n.shuffle = function(e) { for (var t = e.length - 1; t > 0; t--) { var i = Math.floor(n.random() * (t + 1)), + o = e[t]; + e[t] = e[i], e[i] = o } return e }, n.choose = function(e) { return e[Math.floor(n.random() * e.length)] }, n.isElement = function(e) { return "undefined" != typeof HTMLElement ? e instanceof HTMLElement : !!(e && e.nodeType && e.nodeName) }, n.isArray = function(e) { return "[object Array]" === Object.prototype.toString.call(e) }, n.isFunction = function(e) { return "function" == typeof e }, n.isPlainObject = function(e) { return "object" == typeof e && e.constructor === Object }, n.isString = function(e) { return "[object String]" === toString.call(e) }, n.clamp = function(e, t, n) { return e < t ? t : e > n ? n : e }, n.sign = function(e) { return e < 0 ? -1 : 1 }, n.now = function() { if ("undefined" != typeof window && window.performance) { if (window.performance.now) return window.performance.now(); if (window.performance.webkitNow) return window.performance.webkitNow() } return Date.now ? Date.now() : new Date - n._nowStartTime }, n.random = function(t, n) { return n = void 0 !== n ? n : 1, (t = void 0 !== t ? t : 0) + e() * (n - t) }; var e = function() { return n._seed = (9301 * n._seed + 49297) % 233280, n._seed / 233280 }; + n.colorToNumber = function(e) { return 3 == (e = e.replace("#", "")).length && (e = e.charAt(0) + e.charAt(0) + e.charAt(1) + e.charAt(1) + e.charAt(2) + e.charAt(2)), parseInt(e, 16) }, n.logLevel = 1, n.log = function() { console && n.logLevel > 0 && n.logLevel <= 3 && console.log.apply(console, ["matter-js:"].concat(Array.prototype.slice.call(arguments))) }, n.info = function() { console && n.logLevel > 0 && n.logLevel <= 2 && console.info.apply(console, ["matter-js:"].concat(Array.prototype.slice.call(arguments))) }, n.warn = function() { console && n.logLevel > 0 && n.logLevel <= 3 && console.warn.apply(console, ["matter-js:"].concat(Array.prototype.slice.call(arguments))) }, n.warnOnce = function() { var e = Array.prototype.slice.call(arguments).join(" "); + n._warnedOnce[e] || (n.warn(e), n._warnedOnce[e] = !0) }, n.deprecated = function(e, t, i) { e[t] = n.chain((function() { n.warnOnce("🔅 deprecated 🔅", i) }), e[t]) }, n.nextId = function() { return n._nextId++ }, n.indexOf = function(e, t) { if (e.indexOf) return e.indexOf(t); for (var n = 0; n < e.length; n++) + if (e[n] === t) return n; return -1 }, n.map = function(e, t) { if (e.map) return e.map(t); for (var n = [], i = 0; i < e.length; i += 1) n.push(t(e[i])); return n }, n.topologicalSort = function(e) { var t = [], + i = [], + o = []; for (var r in e) i[r] || o[r] || n._topologicalSort(r, i, o, e, t); return t }, n._topologicalSort = function(e, t, i, o, r) { var a = o[e] || []; + i[e] = !0; for (var s = 0; s < a.length; s += 1) { var l = a[s]; + i[l] || (t[l] || n._topologicalSort(l, t, i, o, r)) } i[e] = !1, t[e] = !0, r.push(e) }, n.chain = function() { for (var e = [], t = 0; t < arguments.length; t += 1) { var n = arguments[t]; + n._chained ? e.push.apply(e, n._chained) : e.push(n) } var i = function() { for (var t, n = new Array(arguments.length), i = 0, o = arguments.length; i < o; i++) n[i] = arguments[i]; for (i = 0; i < e.length; i += 1) { var r = e[i].apply(t, n); + void 0 !== r && (t = r) } return t }; return i._chained = e, i }, n.chainPathBefore = function(e, t, i) { return n.set(e, t, n.chain(i, n.get(e, t))) }, n.chainPathAfter = function(e, t, i) { return n.set(e, t, n.chain(n.get(e, t), i)) }, n.setDecomp = function(e) { n._decomp = e }, n.getDecomp = function() { var e = n._decomp; try { e || "undefined" == typeof window || (e = window.decomp), e || "undefined" == typeof global || (e = global.decomp) } catch (t) { e = null } return e } }() }, function(e, t) { var n = {}; + e.exports = n, n.create = function(e) { var t = { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } }; return e && n.update(t, e), t }, n.update = function(e, t, n) { e.min.x = 1 / 0, e.max.x = -1 / 0, e.min.y = 1 / 0, e.max.y = -1 / 0; for (var i = 0; i < t.length; i++) { var o = t[i]; + o.x > e.max.x && (e.max.x = o.x), o.x < e.min.x && (e.min.x = o.x), o.y > e.max.y && (e.max.y = o.y), o.y < e.min.y && (e.min.y = o.y) } n && (n.x > 0 ? e.max.x += n.x : e.min.x += n.x, n.y > 0 ? e.max.y += n.y : e.min.y += n.y) }, n.contains = function(e, t) { return t.x >= e.min.x && t.x <= e.max.x && t.y >= e.min.y && t.y <= e.max.y }, n.overlaps = function(e, t) { return e.min.x <= t.max.x && e.max.x >= t.min.x && e.max.y >= t.min.y && e.min.y <= t.max.y }, n.translate = function(e, t) { e.min.x += t.x, e.max.x += t.x, e.min.y += t.y, e.max.y += t.y }, n.shift = function(e, t) { var n = e.max.x - e.min.x, + i = e.max.y - e.min.y; + e.min.x = t.x, e.max.x = t.x + n, e.min.y = t.y, e.max.y = t.y + i } }, function(e, t) { var n = {}; + e.exports = n, n.create = function(e, t) { return { x: e || 0, y: t || 0 } }, n.clone = function(e) { return { x: e.x, y: e.y } }, n.magnitude = function(e) { return Math.sqrt(e.x * e.x + e.y * e.y) }, n.magnitudeSquared = function(e) { return e.x * e.x + e.y * e.y }, n.rotate = function(e, t, n) { var i = Math.cos(t), + o = Math.sin(t); + n || (n = {}); var r = e.x * i - e.y * o; return n.y = e.x * o + e.y * i, n.x = r, n }, n.rotateAbout = function(e, t, n, i) { var o = Math.cos(t), + r = Math.sin(t); + i || (i = {}); var a = n.x + ((e.x - n.x) * o - (e.y - n.y) * r); return i.y = n.y + ((e.x - n.x) * r + (e.y - n.y) * o), i.x = a, i }, n.normalise = function(e) { var t = n.magnitude(e); return 0 === t ? { x: 0, y: 0 } : { x: e.x / t, y: e.y / t } }, n.dot = function(e, t) { return e.x * t.x + e.y * t.y }, n.cross = function(e, t) { return e.x * t.y - e.y * t.x }, n.cross3 = function(e, t, n) { return (t.x - e.x) * (n.y - e.y) - (t.y - e.y) * (n.x - e.x) }, n.add = function(e, t, n) { return n || (n = {}), n.x = e.x + t.x, n.y = e.y + t.y, n }, n.sub = function(e, t, n) { return n || (n = {}), n.x = e.x - t.x, n.y = e.y - t.y, n }, n.mult = function(e, t) { return { x: e.x * t, y: e.y * t } }, n.div = function(e, t) { return { x: e.x / t, y: e.y / t } }, n.perp = function(e, t) { return { x: (t = !0 === t ? -1 : 1) * -e.y, y: t * e.x } }, n.neg = function(e) { return { x: -e.x, y: -e.y } }, n.angle = function(e, t) { return Math.atan2(t.y - e.y, t.x - e.x) }, n._temp = [n.create(), n.create(), n.create(), n.create(), n.create(), n.create()] }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(2), + r = n(0); + i.create = function(e, t) { for (var n = [], i = 0; i < e.length; i++) { var o = e[i], + r = { x: o.x, y: o.y, index: i, body: t, isInternal: !1 }; + n.push(r) } return n }, i.fromPath = function(e, t) { var n = []; return e.replace(/L?\s*([-\d.e]+)[\s,]*([-\d.e]+)*/gi, (function(e, t, i) { n.push({ x: parseFloat(t), y: parseFloat(i) }) })), i.create(n, t) }, i.centre = function(e) { for (var t, n, r, a = i.area(e, !0), s = { x: 0, y: 0 }, l = 0; l < e.length; l++) r = (l + 1) % e.length, t = o.cross(e[l], e[r]), n = o.mult(o.add(e[l], e[r]), t), s = o.add(s, n); return o.div(s, 6 * a) }, i.mean = function(e) { for (var t = { x: 0, y: 0 }, n = 0; n < e.length; n++) t.x += e[n].x, t.y += e[n].y; return o.div(t, e.length) }, i.area = function(e, t) { for (var n = 0, i = e.length - 1, o = 0; o < e.length; o++) n += (e[i].x - e[o].x) * (e[i].y + e[o].y), i = o; return t ? n / 2 : Math.abs(n) / 2 }, i.inertia = function(e, t) { for (var n, i, r = 0, a = 0, s = e, l = 0; l < s.length; l++) i = (l + 1) % s.length, r += (n = Math.abs(o.cross(s[i], s[l]))) * (o.dot(s[i], s[i]) + o.dot(s[i], s[l]) + o.dot(s[l], s[l])), a += n; return t / 6 * (r / a) }, i.translate = function(e, t, n) { n = void 0 !== n ? n : 1; var i, o = e.length, + r = t.x * n, + a = t.y * n; for (i = 0; i < o; i++) e[i].x += r, e[i].y += a; return e }, i.rotate = function(e, t, n) { if (0 !== t) { var i, o, r, a, s = Math.cos(t), + l = Math.sin(t), + c = n.x, + u = n.y, + d = e.length; for (a = 0; a < d; a++) o = (i = e[a]).x - c, r = i.y - u, i.x = c + (o * s - r * l), i.y = u + (o * l + r * s); return e } }, i.contains = function(e, t) { for (var n, i = t.x, o = t.y, r = e.length, a = e[r - 1], s = 0; s < r; s++) { if (n = e[s], (i - a.x) * (n.y - a.y) + (o - a.y) * (a.x - n.x) > 0) return !1; + a = n } return !0 }, i.scale = function(e, t, n, r) { if (1 === t && 1 === n) return e; var a, s; + r = r || i.centre(e); for (var l = 0; l < e.length; l++) a = e[l], s = o.sub(a, r), e[l].x = r.x + s.x * t, e[l].y = r.y + s.y * n; return e }, i.chamfer = function(e, t, n, i, a) { t = "number" == typeof t ? [t] : t || [8], n = void 0 !== n ? n : -1, i = i || 2, a = a || 14; for (var s = [], l = 0; l < e.length; l++) { var c = e[l - 1 >= 0 ? l - 1 : e.length - 1], + u = e[l], + d = e[(l + 1) % e.length], + p = t[l < t.length ? l : t.length - 1]; if (0 !== p) { var f = o.normalise({ x: u.y - c.y, y: c.x - u.x }), + v = o.normalise({ x: d.y - u.y, y: u.x - d.x }), + y = Math.sqrt(2 * Math.pow(p, 2)), + m = o.mult(r.clone(f), p), + g = o.normalise(o.mult(o.add(f, v), .5)), + x = o.sub(u, o.mult(g, y)), + h = n; - 1 === n && (h = 1.75 * Math.pow(p, .32)), (h = r.clamp(h, i, a)) % 2 == 1 && (h += 1); for (var b = Math.acos(o.dot(f, v)) / h, S = 0; S < h; S++) s.push(o.add(o.rotate(m, b * S), x)) } else s.push(u) } return s }, i.clockwiseSort = function(e) { var t = i.mean(e); return e.sort((function(e, n) { return o.angle(t, e) - o.angle(t, n) })), e }, i.isConvex = function(e) { var t, n, i, o, r = 0, + a = e.length; if (a < 3) return null; for (t = 0; t < a; t++) + if (i = (t + 2) % a, o = (e[n = (t + 1) % a].x - e[t].x) * (e[i].y - e[n].y), (o -= (e[n].y - e[t].y) * (e[i].x - e[n].x)) < 0 ? r |= 1 : o > 0 && (r |= 2), 3 === r) return !1; return 0 !== r || null }, i.hull = function(e) { var t, n, i = [], + r = []; for ((e = e.slice(0)).sort((function(e, t) { var n = e.x - t.x; return 0 !== n ? n : e.y - t.y })), n = 0; n < e.length; n += 1) { for (t = e[n]; r.length >= 2 && o.cross3(r[r.length - 2], r[r.length - 1], t) <= 0;) r.pop(); + r.push(t) } for (n = e.length - 1; n >= 0; n -= 1) { for (t = e[n]; i.length >= 2 && o.cross3(i[i.length - 2], i[i.length - 1], t) <= 0;) i.pop(); + i.push(t) } return i.pop(), r.pop(), i.concat(r) } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(0); + i.on = function(e, t, n) { for (var i, o = t.split(" "), r = 0; r < o.length; r++) i = o[r], e.events = e.events || {}, e.events[i] = e.events[i] || [], e.events[i].push(n); return n }, i.off = function(e, t, n) { if (t) { "function" == typeof t && (n = t, t = o.keys(e.events).join(" ")); for (var i = t.split(" "), r = 0; r < i.length; r++) { var a = e.events[i[r]], + s = []; if (n && a) + for (var l = 0; l < a.length; l++) a[l] !== n && s.push(a[l]); + e.events[i[r]] = s } } else e.events = {} }, i.trigger = function(e, t, n) { var i, r, a, s, l = e.events; if (l && o.keys(l).length > 0) { n || (n = {}), i = t.split(" "); for (var c = 0; c < i.length; c++) + if (a = l[r = i[c]]) { + (s = o.clone(n, !1)).name = r, s.source = e; for (var u = 0; u < a.length; u++) a[u].apply(e, [s]) } } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(4), + r = n(0), + a = n(1), + s = n(6); + i.create = function(e) { return r.extend({ id: r.nextId(), type: "composite", parent: null, isModified: !1, bodies: [], constraints: [], composites: [], label: "Composite", plugin: {}, cache: { allBodies: null, allConstraints: null, allComposites: null } }, e) }, i.setModified = function(e, t, n, o) { if (e.isModified = t, t && e.cache && (e.cache.allBodies = null, e.cache.allConstraints = null, e.cache.allComposites = null), n && e.parent && i.setModified(e.parent, t, n, o), o) + for (var r = 0; r < e.composites.length; r++) { var a = e.composites[r]; + i.setModified(a, t, n, o) } }, i.add = function(e, t) { var n = [].concat(t); + o.trigger(e, "beforeAdd", { object: t }); for (var a = 0; a < n.length; a++) { var s = n[a]; switch (s.type) { + case "body": + if (s.parent !== s) { r.warn("Composite.add: skipped adding a compound body part (you must add its parent instead)"); break } i.addBody(e, s); break; + case "constraint": + i.addConstraint(e, s); break; + case "composite": + i.addComposite(e, s); break; + case "mouseConstraint": + i.addConstraint(e, s.constraint) } } return o.trigger(e, "afterAdd", { object: t }), e }, i.remove = function(e, t, n) { var r = [].concat(t); + o.trigger(e, "beforeRemove", { object: t }); for (var a = 0; a < r.length; a++) { var s = r[a]; switch (s.type) { + case "body": + i.removeBody(e, s, n); break; + case "constraint": + i.removeConstraint(e, s, n); break; + case "composite": + i.removeComposite(e, s, n); break; + case "mouseConstraint": + i.removeConstraint(e, s.constraint) } } return o.trigger(e, "afterRemove", { object: t }), e }, i.addComposite = function(e, t) { return e.composites.push(t), t.parent = e, i.setModified(e, !0, !0, !1), e }, i.removeComposite = function(e, t, n) { var o = r.indexOf(e.composites, t); if (-1 !== o && i.removeCompositeAt(e, o), n) + for (var a = 0; a < e.composites.length; a++) i.removeComposite(e.composites[a], t, !0); return e }, i.removeCompositeAt = function(e, t) { return e.composites.splice(t, 1), i.setModified(e, !0, !0, !1), e }, i.addBody = function(e, t) { return e.bodies.push(t), i.setModified(e, !0, !0, !1), e }, i.removeBody = function(e, t, n) { var o = r.indexOf(e.bodies, t); if (-1 !== o && (i.removeBodyAt(e, o), t.isSleeping = !1, t.sleepCounter = 0), n) + for (var a = 0; a < e.composites.length; a++) i.removeBody(e.composites[a], t, !0); return e }, i.removeBodyAt = function(e, t) { return e.bodies.splice(t, 1), i.setModified(e, !0, !0, !1), e }, i.addConstraint = function(e, t) { return e.constraints.push(t), i.setModified(e, !0, !0, !1), e }, i.removeConstraint = function(e, t, n) { var o = r.indexOf(e.constraints, t); if (-1 !== o && i.removeConstraintAt(e, o), n) + for (var a = 0; a < e.composites.length; a++) i.removeConstraint(e.composites[a], t, !0); return e }, i.removeConstraintAt = function(e, t) { return e.constraints.splice(t, 1), i.setModified(e, !0, !0, !1), e }, i.clear = function(e, t, n) { if (n) + for (var o = 0; o < e.composites.length; o++) i.clear(e.composites[o], t, !0); return t ? e.bodies = e.bodies.filter((function(e) { return e.isStatic })) : e.bodies.length = 0, e.constraints.length = 0, e.composites.length = 0, i.setModified(e, !0, !0, !1), e }, i.allBodies = function(e) { if (e.cache && e.cache.allBodies) return e.cache.allBodies; for (var t = [].concat(e.bodies), n = 0; n < e.composites.length; n++) t = t.concat(i.allBodies(e.composites[n])); return e.cache && (e.cache.allBodies = t), t }, i.allConstraints = function(e) { if (e.cache && e.cache.allConstraints) return e.cache.allConstraints; for (var t = [].concat(e.constraints), n = 0; n < e.composites.length; n++) t = t.concat(i.allConstraints(e.composites[n])); return e.cache && (e.cache.allConstraints = t), t }, i.allComposites = function(e) { if (e.cache && e.cache.allComposites) return e.cache.allComposites; for (var t = [].concat(e.composites), n = 0; n < e.composites.length; n++) t = t.concat(i.allComposites(e.composites[n])); return e.cache && (e.cache.allComposites = t), t }, i.get = function(e, t, n) { var o, r; switch (n) { + case "body": + o = i.allBodies(e); break; + case "constraint": + o = i.allConstraints(e); break; + case "composite": + o = i.allComposites(e).concat(e) } return o ? 0 === (r = o.filter((function(e) { return e.id.toString() === t.toString() }))).length ? null : r[0] : null }, i.move = function(e, t, n) { return i.remove(e, t), i.add(n, t), e }, i.rebase = function(e) { for (var t = i.allBodies(e).concat(i.allConstraints(e)).concat(i.allComposites(e)), n = 0; n < t.length; n++) t[n].id = r.nextId(); return e }, i.translate = function(e, t, n) { for (var o = n ? i.allBodies(e) : e.bodies, r = 0; r < o.length; r++) s.translate(o[r], t); return e }, i.rotate = function(e, t, n, o) { for (var r = Math.cos(t), a = Math.sin(t), l = o ? i.allBodies(e) : e.bodies, c = 0; c < l.length; c++) { var u = l[c], + d = u.position.x - n.x, + p = u.position.y - n.y; + s.setPosition(u, { x: n.x + (d * r - p * a), y: n.y + (d * a + p * r) }), s.rotate(u, t) } return e }, i.scale = function(e, t, n, o, r) { for (var a = r ? i.allBodies(e) : e.bodies, l = 0; l < a.length; l++) { var c = a[l], + u = c.position.x - o.x, + d = c.position.y - o.y; + s.setPosition(c, { x: o.x + u * t, y: o.y + d * n }), s.scale(c, t, n) } return e }, i.bounds = function(e) { for (var t = i.allBodies(e), n = [], o = 0; o < t.length; o += 1) { var r = t[o]; + n.push(r.bounds.min, r.bounds.max) } return a.create(n) } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(3), + r = n(2), + a = n(7), + s = (n(16), n(0)), + l = n(1), + c = n(11);! function() { i._inertiaScale = 4, i._nextCollidingGroupId = 1, i._nextNonCollidingGroupId = -1, i._nextCategory = 1, i.create = function(t) { var n = { id: s.nextId(), type: "body", label: "Body", parts: [], plugin: {}, angle: 0, vertices: o.fromPath("L 0 0 L 40 0 L 40 40 L 0 40"), position: { x: 0, y: 0 }, force: { x: 0, y: 0 }, torque: 0, positionImpulse: { x: 0, y: 0 }, constraintImpulse: { x: 0, y: 0, angle: 0 }, totalContacts: 0, speed: 0, angularSpeed: 0, velocity: { x: 0, y: 0 }, angularVelocity: 0, isSensor: !1, isStatic: !1, isSleeping: !1, motion: 0, sleepThreshold: 60, density: .001, restitution: 0, friction: .1, frictionStatic: .5, frictionAir: .01, collisionFilter: { category: 1, mask: 4294967295, group: 0 }, slop: .05, timeScale: 1, render: { visible: !0, opacity: 1, strokeStyle: null, fillStyle: null, lineWidth: null, sprite: { xScale: 1, yScale: 1, xOffset: 0, yOffset: 0 } }, events: null, bounds: null, chamfer: null, circleRadius: 0, positionPrev: null, anglePrev: 0, parent: null, axes: null, area: 0, mass: 0, inertia: 0, _original: null }, + i = s.extend(n, t); return e(i, t), i }, i.nextGroup = function(e) { return e ? i._nextNonCollidingGroupId-- : i._nextCollidingGroupId++ }, i.nextCategory = function() { return i._nextCategory = i._nextCategory << 1, i._nextCategory }; var e = function(e, t) { t = t || {}, i.set(e, { bounds: e.bounds || l.create(e.vertices), positionPrev: e.positionPrev || r.clone(e.position), anglePrev: e.anglePrev || e.angle, vertices: e.vertices, parts: e.parts || [e], isStatic: e.isStatic, isSleeping: e.isSleeping, parent: e.parent || e }), o.rotate(e.vertices, e.angle, e.position), c.rotate(e.axes, e.angle), l.update(e.bounds, e.vertices, e.velocity), i.set(e, { axes: t.axes || e.axes, area: t.area || e.area, mass: t.mass || e.mass, inertia: t.inertia || e.inertia }); var n = e.isStatic ? "#14151f" : s.choose(["#f19648", "#f5d259", "#f55a3c", "#063e7b", "#ececd1"]), + a = e.isStatic ? "#555" : "#ccc", + u = e.isStatic && null === e.render.fillStyle ? 1 : 0; + e.render.fillStyle = e.render.fillStyle || n, e.render.strokeStyle = e.render.strokeStyle || a, e.render.lineWidth = e.render.lineWidth || u, e.render.sprite.xOffset += -(e.bounds.min.x - e.position.x) / (e.bounds.max.x - e.bounds.min.x), e.render.sprite.yOffset += -(e.bounds.min.y - e.position.y) / (e.bounds.max.y - e.bounds.min.y) }; + i.set = function(e, t, n) { var o; for (o in "string" == typeof t && (o = t, (t = {})[o] = n), t) + if (Object.prototype.hasOwnProperty.call(t, o)) switch (n = t[o], o) { + case "isStatic": + i.setStatic(e, n); break; + case "isSleeping": + a.set(e, n); break; + case "mass": + i.setMass(e, n); break; + case "density": + i.setDensity(e, n); break; + case "inertia": + i.setInertia(e, n); break; + case "vertices": + i.setVertices(e, n); break; + case "position": + i.setPosition(e, n); break; + case "angle": + i.setAngle(e, n); break; + case "velocity": + i.setVelocity(e, n); break; + case "angularVelocity": + i.setAngularVelocity(e, n); break; + case "parts": + i.setParts(e, n); break; + case "centre": + i.setCentre(e, n); break; + default: + e[o] = n } }, i.setStatic = function(e, t) { for (var n = 0; n < e.parts.length; n++) { var i = e.parts[n]; + i.isStatic = t, t ? (i._original = { restitution: i.restitution, friction: i.friction, mass: i.mass, inertia: i.inertia, density: i.density, inverseMass: i.inverseMass, inverseInertia: i.inverseInertia }, i.restitution = 0, i.friction = 1, i.mass = i.inertia = i.density = 1 / 0, i.inverseMass = i.inverseInertia = 0, i.positionPrev.x = i.position.x, i.positionPrev.y = i.position.y, i.anglePrev = i.angle, i.angularVelocity = 0, i.speed = 0, i.angularSpeed = 0, i.motion = 0) : i._original && (i.restitution = i._original.restitution, i.friction = i._original.friction, i.mass = i._original.mass, i.inertia = i._original.inertia, i.density = i._original.density, i.inverseMass = i._original.inverseMass, i.inverseInertia = i._original.inverseInertia, i._original = null) } }, i.setMass = function(e, t) { var n = e.inertia / (e.mass / 6); + e.inertia = n * (t / 6), e.inverseInertia = 1 / e.inertia, e.mass = t, e.inverseMass = 1 / e.mass, e.density = e.mass / e.area }, i.setDensity = function(e, t) { i.setMass(e, t * e.area), e.density = t }, i.setInertia = function(e, t) { e.inertia = t, e.inverseInertia = 1 / e.inertia }, i.setVertices = function(e, t) { t[0].body === e ? e.vertices = t : e.vertices = o.create(t, e), e.axes = c.fromVertices(e.vertices), e.area = o.area(e.vertices), i.setMass(e, e.density * e.area); var n = o.centre(e.vertices); + o.translate(e.vertices, n, -1), i.setInertia(e, i._inertiaScale * o.inertia(e.vertices, e.mass)), o.translate(e.vertices, e.position), l.update(e.bounds, e.vertices, e.velocity) }, i.setParts = function(e, t, n) { var r; for (t = t.slice(0), e.parts.length = 0, e.parts.push(e), e.parent = e, r = 0; r < t.length; r++) { var a = t[r]; + a !== e && (a.parent = e, e.parts.push(a)) } if (1 !== e.parts.length) { if (n = void 0 === n || n) { var s = []; for (r = 0; r < t.length; r++) s = s.concat(t[r].vertices); + o.clockwiseSort(s); var l = o.hull(s), + c = o.centre(l); + i.setVertices(e, l), o.translate(e.vertices, c) } var u = i._totalProperties(e); + e.area = u.area, e.parent = e, e.position.x = u.centre.x, e.position.y = u.centre.y, e.positionPrev.x = u.centre.x, e.positionPrev.y = u.centre.y, i.setMass(e, u.mass), i.setInertia(e, u.inertia), i.setPosition(e, u.centre) } }, i.setCentre = function(e, t, n) { n ? (e.positionPrev.x += t.x, e.positionPrev.y += t.y, e.position.x += t.x, e.position.y += t.y) : (e.positionPrev.x = t.x - (e.position.x - e.positionPrev.x), e.positionPrev.y = t.y - (e.position.y - e.positionPrev.y), e.position.x = t.x, e.position.y = t.y) }, i.setPosition = function(e, t) { var n = r.sub(t, e.position); + e.positionPrev.x += n.x, e.positionPrev.y += n.y; for (var i = 0; i < e.parts.length; i++) { var a = e.parts[i]; + a.position.x += n.x, a.position.y += n.y, o.translate(a.vertices, n), l.update(a.bounds, a.vertices, e.velocity) } }, i.setAngle = function(e, t) { var n = t - e.angle; + e.anglePrev += n; for (var i = 0; i < e.parts.length; i++) { var a = e.parts[i]; + a.angle += n, o.rotate(a.vertices, n, e.position), c.rotate(a.axes, n), l.update(a.bounds, a.vertices, e.velocity), i > 0 && r.rotateAbout(a.position, n, e.position, a.position) } }, i.setVelocity = function(e, t) { e.positionPrev.x = e.position.x - t.x, e.positionPrev.y = e.position.y - t.y, e.velocity.x = t.x, e.velocity.y = t.y, e.speed = r.magnitude(e.velocity) }, i.setAngularVelocity = function(e, t) { e.anglePrev = e.angle - t, e.angularVelocity = t, e.angularSpeed = Math.abs(e.angularVelocity) }, i.translate = function(e, t) { i.setPosition(e, r.add(e.position, t)) }, i.rotate = function(e, t, n) { if (n) { var o = Math.cos(t), + r = Math.sin(t), + a = e.position.x - n.x, + s = e.position.y - n.y; + i.setPosition(e, { x: n.x + (a * o - s * r), y: n.y + (a * r + s * o) }), i.setAngle(e, e.angle + t) } else i.setAngle(e, e.angle + t) }, i.scale = function(e, t, n, r) { var a = 0, + s = 0; + r = r || e.position; for (var u = 0; u < e.parts.length; u++) { var d = e.parts[u]; + o.scale(d.vertices, t, n, r), d.axes = c.fromVertices(d.vertices), d.area = o.area(d.vertices), i.setMass(d, e.density * d.area), o.translate(d.vertices, { x: -d.position.x, y: -d.position.y }), i.setInertia(d, i._inertiaScale * o.inertia(d.vertices, d.mass)), o.translate(d.vertices, { x: d.position.x, y: d.position.y }), u > 0 && (a += d.area, s += d.inertia), d.position.x = r.x + (d.position.x - r.x) * t, d.position.y = r.y + (d.position.y - r.y) * n, l.update(d.bounds, d.vertices, e.velocity) } e.parts.length > 1 && (e.area = a, e.isStatic || (i.setMass(e, e.density * a), i.setInertia(e, s))), e.circleRadius && (t === n ? e.circleRadius *= t : e.circleRadius = null) }, i.update = function(e, t, n, i) { var a = Math.pow(t * n * e.timeScale, 2), + s = 1 - e.frictionAir * n * e.timeScale, + u = e.position.x - e.positionPrev.x, + d = e.position.y - e.positionPrev.y; + e.velocity.x = u * s * i + e.force.x / e.mass * a, e.velocity.y = d * s * i + e.force.y / e.mass * a, e.positionPrev.x = e.position.x, e.positionPrev.y = e.position.y, e.position.x += e.velocity.x, e.position.y += e.velocity.y, e.angularVelocity = (e.angle - e.anglePrev) * s * i + e.torque / e.inertia * a, e.anglePrev = e.angle, e.angle += e.angularVelocity, e.speed = r.magnitude(e.velocity), e.angularSpeed = Math.abs(e.angularVelocity); for (var p = 0; p < e.parts.length; p++) { var f = e.parts[p]; + o.translate(f.vertices, e.velocity), p > 0 && (f.position.x += e.velocity.x, f.position.y += e.velocity.y), 0 !== e.angularVelocity && (o.rotate(f.vertices, e.angularVelocity, e.position), c.rotate(f.axes, e.angularVelocity), p > 0 && r.rotateAbout(f.position, e.angularVelocity, e.position, f.position)), l.update(f.bounds, f.vertices, e.velocity) } }, i.applyForce = function(e, t, n) { e.force.x += n.x, e.force.y += n.y; var i = t.x - e.position.x, + o = t.y - e.position.y; + e.torque += i * n.y - o * n.x }, i._totalProperties = function(e) { for (var t = { mass: 0, area: 0, inertia: 0, centre: { x: 0, y: 0 } }, n = 1 === e.parts.length ? 0 : 1; n < e.parts.length; n++) { var i = e.parts[n], + o = i.mass !== 1 / 0 ? i.mass : 1; + t.mass += o, t.area += i.area, t.inertia += i.inertia, t.centre = r.add(t.centre, r.mult(i.position, o)) } return t.centre = r.div(t.centre, t.mass), t } }() }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(4); + i._motionWakeThreshold = .18, i._motionSleepThreshold = .08, i._minBias = .9, i.update = function(e, t) { for (var n = t * t * t, o = 0; o < e.length; o++) { var r = e[o], + a = r.speed * r.speed + r.angularSpeed * r.angularSpeed; if (0 === r.force.x && 0 === r.force.y) { var s = Math.min(r.motion, a), + l = Math.max(r.motion, a); + r.motion = i._minBias * s + (1 - i._minBias) * l, r.sleepThreshold > 0 && r.motion < i._motionSleepThreshold * n ? (r.sleepCounter += 1, r.sleepCounter >= r.sleepThreshold && i.set(r, !0)) : r.sleepCounter > 0 && (r.sleepCounter -= 1) } else i.set(r, !1) } }, i.afterCollisions = function(e, t) { for (var n = t * t * t, o = 0; o < e.length; o++) { var r = e[o]; if (r.isActive) { var a = r.collision, + s = a.bodyA.parent, + l = a.bodyB.parent; if (!(s.isSleeping && l.isSleeping || s.isStatic || l.isStatic) && (s.isSleeping || l.isSleeping)) { var c = s.isSleeping && !s.isStatic ? s : l, + u = c === s ? l : s;!c.isStatic && u.motion > i._motionWakeThreshold * n && i.set(c, !1) } } } }, i.set = function(e, t) { var n = e.isSleeping; + t ? (e.isSleeping = !0, e.sleepCounter = e.sleepThreshold, e.positionImpulse.x = 0, e.positionImpulse.y = 0, e.positionPrev.x = e.position.x, e.positionPrev.y = e.position.y, e.anglePrev = e.angle, e.speed = 0, e.angularSpeed = 0, e.motion = 0, n || o.trigger(e, "sleepStart")) : (e.isSleeping = !1, e.sleepCounter = 0, n && o.trigger(e, "sleepEnd")) } }, function(e, t, n) { var i = {}; + e.exports = i; var o, r, a, s = n(3), + l = n(9); + o = [], r = { overlap: 0, axis: null }, a = { overlap: 0, axis: null }, i.create = function(e, t) { return { pair: null, collided: !1, bodyA: e, bodyB: t, parentA: e.parent, parentB: t.parent, depth: 0, normal: { x: 0, y: 0 }, tangent: { x: 0, y: 0 }, penetration: { x: 0, y: 0 }, supports: [] } }, i.collides = function(e, t, n) { if (i._overlapAxes(r, e.vertices, t.vertices, e.axes), r.overlap <= 0) return null; if (i._overlapAxes(a, t.vertices, e.vertices, t.axes), a.overlap <= 0) return null; var o, c, u = n && n.table[l.id(e, t)]; + u ? o = u.collision : ((o = i.create(e, t)).collided = !0, o.bodyA = e.id < t.id ? e : t, o.bodyB = e.id < t.id ? t : e, o.parentA = o.bodyA.parent, o.parentB = o.bodyB.parent), e = o.bodyA, t = o.bodyB, c = r.overlap < a.overlap ? r : a; var d = o.normal, + p = o.supports, + f = c.axis, + v = f.x, + y = f.y; + v * (t.position.x - e.position.x) + y * (t.position.y - e.position.y) < 0 ? (d.x = v, d.y = y) : (d.x = -v, d.y = -y), o.tangent.x = -d.y, o.tangent.y = d.x, o.depth = c.overlap, o.penetration.x = d.x * o.depth, o.penetration.y = d.y * o.depth; var m = i._findSupports(e, t, d, 1), + g = 0; if (s.contains(e.vertices, m[0]) && (p[g++] = m[0]), s.contains(e.vertices, m[1]) && (p[g++] = m[1]), g < 2) { var x = i._findSupports(t, e, d, -1); + s.contains(t.vertices, x[0]) && (p[g++] = x[0]), g < 2 && s.contains(t.vertices, x[1]) && (p[g++] = x[1]) } return 0 === g && (p[g++] = m[0]), p.length = g, o }, i._overlapAxes = function(e, t, n, i) { var o, r, a, s, l, c, u = t.length, + d = n.length, + p = t[0].x, + f = t[0].y, + v = n[0].x, + y = n[0].y, + m = i.length, + g = Number.MAX_VALUE, + x = 0; for (l = 0; l < m; l++) { var h = i[l], + b = h.x, + S = h.y, + w = p * b + f * S, + A = v * b + y * S, + P = w, + C = A; for (c = 1; c < u; c += 1)(s = t[c].x * b + t[c].y * S) > P ? P = s : s < w && (w = s); for (c = 1; c < d; c += 1)(s = n[c].x * b + n[c].y * S) > C ? C = s : s < A && (A = s); if ((o = (r = P - A) < (a = C - w) ? r : a) < g && (g = o, x = l, o <= 0)) break } e.axis = i[x], e.overlap = g }, i._projectToAxis = function(e, t, n) { for (var i = t[0].x * n.x + t[0].y * n.y, o = i, r = 1; r < t.length; r += 1) { var a = t[r].x * n.x + t[r].y * n.y; + a > o ? o = a : a < i && (i = a) } e.min = i, e.max = o }, i._findSupports = function(e, t, n, i) { var r, a, s, l, c, u = t.vertices, + d = u.length, + p = e.position.x, + f = e.position.y, + v = n.x * i, + y = n.y * i, + m = Number.MAX_VALUE; for (c = 0; c < d; c += 1)(l = v * (p - (a = u[c]).x) + y * (f - a.y)) < m && (m = l, r = a); return m = v * (p - (s = u[(d + r.index - 1) % d]).x) + y * (f - s.y), v * (p - (a = u[(r.index + 1) % d]).x) + y * (f - a.y) < m ? (o[0] = r, o[1] = a, o) : (o[0] = r, o[1] = s, o) } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(17); + i.create = function(e, t) { var n = e.bodyA, + o = e.bodyB, + r = { id: i.id(n, o), bodyA: n, bodyB: o, collision: e, contacts: [], activeContacts: [], separation: 0, isActive: !0, confirmedActive: !0, isSensor: n.isSensor || o.isSensor, timeCreated: t, timeUpdated: t, inverseMass: 0, friction: 0, frictionStatic: 0, restitution: 0, slop: 0 }; return i.update(r, e, t), r }, i.update = function(e, t, n) { var i = e.contacts, + r = t.supports, + a = e.activeContacts, + s = t.parentA, + l = t.parentB, + c = s.vertices.length; + e.isActive = !0, e.timeUpdated = n, e.collision = t, e.separation = t.depth, e.inverseMass = s.inverseMass + l.inverseMass, e.friction = s.friction < l.friction ? s.friction : l.friction, e.frictionStatic = s.frictionStatic > l.frictionStatic ? s.frictionStatic : l.frictionStatic, e.restitution = s.restitution > l.restitution ? s.restitution : l.restitution, e.slop = s.slop > l.slop ? s.slop : l.slop, t.pair = e, a.length = 0; for (var u = 0; u < r.length; u++) { var d = r[u], + p = d.body === s ? d.index : c + d.index, + f = i[p]; + f ? a.push(f) : a.push(i[p] = o.create(d)) } }, i.setActive = function(e, t, n) { t ? (e.isActive = !0, e.timeUpdated = n) : (e.isActive = !1, e.activeContacts.length = 0) }, i.id = function(e, t) { return e.id < t.id ? "A" + e.id + "B" + t.id : "A" + t.id + "B" + e.id } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(3), + r = n(2), + a = n(7), + s = n(1), + l = n(11), + c = n(0); + i._warming = .4, i._torqueDampen = 1, i._minLength = 1e-6, i.create = function(e) { var t = e; + t.bodyA && !t.pointA && (t.pointA = { x: 0, y: 0 }), t.bodyB && !t.pointB && (t.pointB = { x: 0, y: 0 }); var n = t.bodyA ? r.add(t.bodyA.position, t.pointA) : t.pointA, + i = t.bodyB ? r.add(t.bodyB.position, t.pointB) : t.pointB, + o = r.magnitude(r.sub(n, i)); + t.length = void 0 !== t.length ? t.length : o, t.id = t.id || c.nextId(), t.label = t.label || "Constraint", t.type = "constraint", t.stiffness = t.stiffness || (t.length > 0 ? 1 : .7), t.damping = t.damping || 0, t.angularStiffness = t.angularStiffness || 0, t.angleA = t.bodyA ? t.bodyA.angle : t.angleA, t.angleB = t.bodyB ? t.bodyB.angle : t.angleB, t.plugin = {}; var a = { visible: !0, lineWidth: 2, strokeStyle: "#ffffff", type: "line", anchors: !0 }; return 0 === t.length && t.stiffness > .1 ? (a.type = "pin", a.anchors = !1) : t.stiffness < .9 && (a.type = "spring"), t.render = c.extend(a, t.render), t }, i.preSolveAll = function(e) { for (var t = 0; t < e.length; t += 1) { var n = e[t], + i = n.constraintImpulse; + n.isStatic || 0 === i.x && 0 === i.y && 0 === i.angle || (n.position.x += i.x, n.position.y += i.y, n.angle += i.angle) } }, i.solveAll = function(e, t) { for (var n = 0; n < e.length; n += 1) { var o = e[n], + r = !o.bodyA || o.bodyA && o.bodyA.isStatic, + a = !o.bodyB || o.bodyB && o.bodyB.isStatic; + (r || a) && i.solve(e[n], t) } for (n = 0; n < e.length; n += 1) r = !(o = e[n]).bodyA || o.bodyA && o.bodyA.isStatic, a = !o.bodyB || o.bodyB && o.bodyB.isStatic, r || a || i.solve(e[n], t) }, i.solve = function(e, t) { var n = e.bodyA, + o = e.bodyB, + a = e.pointA, + s = e.pointB; if (n || o) { n && !n.isStatic && (r.rotate(a, n.angle - e.angleA, a), e.angleA = n.angle), o && !o.isStatic && (r.rotate(s, o.angle - e.angleB, s), e.angleB = o.angle); var l = a, + c = s; if (n && (l = r.add(n.position, a)), o && (c = r.add(o.position, s)), l && c) { var u = r.sub(l, c), + d = r.magnitude(u); + d < i._minLength && (d = i._minLength); var p, f, v, y, m, g = (d - e.length) / d, + x = e.stiffness < 1 ? e.stiffness * t : e.stiffness, + h = r.mult(u, g * x), + b = (n ? n.inverseMass : 0) + (o ? o.inverseMass : 0), + S = b + ((n ? n.inverseInertia : 0) + (o ? o.inverseInertia : 0)); if (e.damping) { var w = r.create(); + v = r.div(u, d), m = r.sub(o && r.sub(o.position, o.positionPrev) || w, n && r.sub(n.position, n.positionPrev) || w), y = r.dot(v, m) } n && !n.isStatic && (f = n.inverseMass / b, n.constraintImpulse.x -= h.x * f, n.constraintImpulse.y -= h.y * f, n.position.x -= h.x * f, n.position.y -= h.y * f, e.damping && (n.positionPrev.x -= e.damping * v.x * y * f, n.positionPrev.y -= e.damping * v.y * y * f), p = r.cross(a, h) / S * i._torqueDampen * n.inverseInertia * (1 - e.angularStiffness), n.constraintImpulse.angle -= p, n.angle -= p), o && !o.isStatic && (f = o.inverseMass / b, o.constraintImpulse.x += h.x * f, o.constraintImpulse.y += h.y * f, o.position.x += h.x * f, o.position.y += h.y * f, e.damping && (o.positionPrev.x += e.damping * v.x * y * f, o.positionPrev.y += e.damping * v.y * y * f), p = r.cross(s, h) / S * i._torqueDampen * o.inverseInertia * (1 - e.angularStiffness), o.constraintImpulse.angle += p, o.angle += p) } } }, i.postSolveAll = function(e) { for (var t = 0; t < e.length; t++) { var n = e[t], + c = n.constraintImpulse; if (!(n.isStatic || 0 === c.x && 0 === c.y && 0 === c.angle)) { a.set(n, !1); for (var u = 0; u < n.parts.length; u++) { var d = n.parts[u]; + o.translate(d.vertices, c), u > 0 && (d.position.x += c.x, d.position.y += c.y), 0 !== c.angle && (o.rotate(d.vertices, c.angle, n.position), l.rotate(d.axes, c.angle), u > 0 && r.rotateAbout(d.position, c.angle, n.position, d.position)), s.update(d.bounds, d.vertices, n.velocity) } c.angle *= i._warming, c.x *= i._warming, c.y *= i._warming } } }, i.pointAWorld = function(e) { return { x: (e.bodyA ? e.bodyA.position.x : 0) + (e.pointA ? e.pointA.x : 0), y: (e.bodyA ? e.bodyA.position.y : 0) + (e.pointA ? e.pointA.y : 0) } }, i.pointBWorld = function(e) { return { x: (e.bodyB ? e.bodyB.position.x : 0) + (e.pointB ? e.pointB.x : 0), y: (e.bodyB ? e.bodyB.position.y : 0) + (e.pointB ? e.pointB.y : 0) } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(2), + r = n(0); + i.fromVertices = function(e) { for (var t = {}, n = 0; n < e.length; n++) { var i = (n + 1) % e.length, + a = o.normalise({ x: e[i].y - e[n].y, y: e[n].x - e[i].x }), + s = 0 === a.y ? 1 / 0 : a.x / a.y; + t[s = s.toFixed(3).toString()] = a } return r.values(t) }, i.rotate = function(e, t) { if (0 !== t) + for (var n = Math.cos(t), i = Math.sin(t), o = 0; o < e.length; o++) { var r, a = e[o]; + r = a.x * n - a.y * i, a.y = a.x * i + a.y * n, a.x = r } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(3), + r = n(0), + a = n(6), + s = n(1), + l = n(2); + i.rectangle = function(e, t, n, i, s) { s = s || {}; var l = { label: "Rectangle Body", position: { x: e, y: t }, vertices: o.fromPath("L 0 0 L " + n + " 0 L " + n + " " + i + " L 0 " + i) }; if (s.chamfer) { var c = s.chamfer; + l.vertices = o.chamfer(l.vertices, c.radius, c.quality, c.qualityMin, c.qualityMax), delete s.chamfer } return a.create(r.extend({}, l, s)) }, i.trapezoid = function(e, t, n, i, s, l) { l = l || {}; var c, u = n * (s *= .5), + d = u + (1 - 2 * s) * n, + p = d + u; + c = s < .5 ? "L 0 0 L " + u + " " + -i + " L " + d + " " + -i + " L " + p + " 0" : "L 0 0 L " + d + " " + -i + " L " + p + " 0"; var f = { label: "Trapezoid Body", position: { x: e, y: t }, vertices: o.fromPath(c) }; if (l.chamfer) { var v = l.chamfer; + f.vertices = o.chamfer(f.vertices, v.radius, v.quality, v.qualityMin, v.qualityMax), delete l.chamfer } return a.create(r.extend({}, f, l)) }, i.circle = function(e, t, n, o, a) { o = o || {}; var s = { label: "Circle Body", circleRadius: n }; + a = a || 25; var l = Math.ceil(Math.max(10, Math.min(a, n))); return l % 2 == 1 && (l += 1), i.polygon(e, t, l, n, r.extend({}, s, o)) }, i.polygon = function(e, t, n, s, l) { if (l = l || {}, n < 3) return i.circle(e, t, s, l); for (var c = 2 * Math.PI / n, u = "", d = .5 * c, p = 0; p < n; p += 1) { var f = d + p * c, + v = Math.cos(f) * s, + y = Math.sin(f) * s; + u += "L " + v.toFixed(3) + " " + y.toFixed(3) + " " } var m = { label: "Polygon Body", position: { x: e, y: t }, vertices: o.fromPath(u) }; if (l.chamfer) { var g = l.chamfer; + m.vertices = o.chamfer(m.vertices, g.radius, g.quality, g.qualityMin, g.qualityMax), delete l.chamfer } return a.create(r.extend({}, m, l)) }, i.fromVertices = function(e, t, n, i, c, u, d, p) { var f, v, y, m, g, x, h, b, S, w, A = r.getDecomp(); for (f = Boolean(A && A.quickDecomp), i = i || {}, y = [], c = void 0 !== c && c, u = void 0 !== u ? u : .01, d = void 0 !== d ? d : 10, p = void 0 !== p ? p : .01, r.isArray(n[0]) || (n = [n]), S = 0; S < n.length; S += 1) + if (g = n[S], !(m = o.isConvex(g)) && !f && r.warnOnce("Bodies.fromVertices: Install the 'poly-decomp' library and use Common.setDecomp or provide 'decomp' as a global to decompose concave vertices."), m || !f) g = m ? o.clockwiseSort(g) : o.hull(g), y.push({ position: { x: e, y: t }, vertices: g }); + else { var P = g.map((function(e) { return [e.x, e.y] })); + A.makeCCW(P), !1 !== u && A.removeCollinearPoints(P, u), !1 !== p && A.removeDuplicatePoints && A.removeDuplicatePoints(P, p); var C = A.quickDecomp(P); for (x = 0; x < C.length; x++) { var B = C[x].map((function(e) { return { x: e[0], y: e[1] } })); + d > 0 && o.area(B) < d || y.push({ position: o.centre(B), vertices: B }) } } for (x = 0; x < y.length; x++) y[x] = a.create(r.extend(y[x], i)); if (c) + for (x = 0; x < y.length; x++) { var M = y[x]; for (h = x + 1; h < y.length; h++) { var k = y[h]; if (s.overlaps(M.bounds, k.bounds)) { var _ = M.vertices, + I = k.vertices; for (b = 0; b < M.vertices.length; b++) + for (w = 0; w < k.vertices.length; w++) { var T = l.magnitudeSquared(l.sub(_[(b + 1) % _.length], I[w])), + R = l.magnitudeSquared(l.sub(_[b], I[(w + 1) % I.length])); + T < 5 && R < 5 && (_[b].isInternal = !0, I[w].isInternal = !0) } } } } + return y.length > 1 ? (v = a.create(r.extend({ parts: y.slice(0) }, i)), a.setPosition(v, { x: e, y: t }), v) : y[0] } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(0); + i.create = function(e) { var t = {}; return e || o.log("Mouse.create: element was undefined, defaulting to document.body", "warn"), t.element = e || document.body, t.absolute = { x: 0, y: 0 }, t.position = { x: 0, y: 0 }, t.mousedownPosition = { x: 0, y: 0 }, t.mouseupPosition = { x: 0, y: 0 }, t.offset = { x: 0, y: 0 }, t.scale = { x: 1, y: 1 }, t.wheelDelta = 0, t.button = -1, t.pixelRatio = parseInt(t.element.getAttribute("data-pixel-ratio"), 10) || 1, t.sourceEvents = { mousemove: null, mousedown: null, mouseup: null, mousewheel: null }, t.mousemove = function(e) { var n = i._getRelativeMousePosition(e, t.element, t.pixelRatio); + e.changedTouches && (t.button = 0, e.preventDefault()), t.absolute.x = n.x, t.absolute.y = n.y, t.position.x = t.absolute.x * t.scale.x + t.offset.x, t.position.y = t.absolute.y * t.scale.y + t.offset.y, t.sourceEvents.mousemove = e }, t.mousedown = function(e) { var n = i._getRelativeMousePosition(e, t.element, t.pixelRatio); + e.changedTouches ? (t.button = 0, e.preventDefault()) : t.button = e.button, t.absolute.x = n.x, t.absolute.y = n.y, t.position.x = t.absolute.x * t.scale.x + t.offset.x, t.position.y = t.absolute.y * t.scale.y + t.offset.y, t.mousedownPosition.x = t.position.x, t.mousedownPosition.y = t.position.y, t.sourceEvents.mousedown = e }, t.mouseup = function(e) { var n = i._getRelativeMousePosition(e, t.element, t.pixelRatio); + e.changedTouches && e.preventDefault(), t.button = -1, t.absolute.x = n.x, t.absolute.y = n.y, t.position.x = t.absolute.x * t.scale.x + t.offset.x, t.position.y = t.absolute.y * t.scale.y + t.offset.y, t.mouseupPosition.x = t.position.x, t.mouseupPosition.y = t.position.y, t.sourceEvents.mouseup = e }, t.mousewheel = function(e) { t.wheelDelta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail)), e.preventDefault() }, i.setElement(t, t.element), t }, i.setElement = function(e, t) { e.element = t, t.addEventListener("mousemove", e.mousemove), t.addEventListener("mousedown", e.mousedown), t.addEventListener("mouseup", e.mouseup), t.addEventListener("mousewheel", e.mousewheel), t.addEventListener("DOMMouseScroll", e.mousewheel), t.addEventListener("touchmove", e.mousemove), t.addEventListener("touchstart", e.mousedown), t.addEventListener("touchend", e.mouseup) }, i.clearSourceEvents = function(e) { e.sourceEvents.mousemove = null, e.sourceEvents.mousedown = null, e.sourceEvents.mouseup = null, e.sourceEvents.mousewheel = null, e.wheelDelta = 0 }, i.setOffset = function(e, t) { e.offset.x = t.x, e.offset.y = t.y, e.position.x = e.absolute.x * e.scale.x + e.offset.x, e.position.y = e.absolute.y * e.scale.y + e.offset.y }, i.setScale = function(e, t) { e.scale.x = t.x, e.scale.y = t.y, e.position.x = e.absolute.x * e.scale.x + e.offset.x, e.position.y = e.absolute.y * e.scale.y + e.offset.y }, i._getRelativeMousePosition = function(e, t, n) { var i, o, r = t.getBoundingClientRect(), + a = document.documentElement || document.body.parentNode || document.body, + s = void 0 !== window.pageXOffset ? window.pageXOffset : a.scrollLeft, + l = void 0 !== window.pageYOffset ? window.pageYOffset : a.scrollTop, + c = e.changedTouches; return c ? (i = c[0].pageX - r.left - s, o = c[0].pageY - r.top - l) : (i = e.pageX - r.left - s, o = e.pageY - r.top - l), { x: i / (t.clientWidth / (t.width || t.clientWidth) * n), y: o / (t.clientHeight / (t.height || t.clientHeight) * n) } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(0), + r = n(8); + i.create = function(e) { return o.extend({ bodies: [], pairs: null }, e) }, i.setBodies = function(e, t) { e.bodies = t.slice(0) }, i.clear = function(e) { e.bodies = [] }, i.collisions = function(e) { var t, n, o = [], + a = e.pairs, + s = e.bodies, + l = s.length, + c = i.canCollide, + u = r.collides; for (s.sort(i._compareBoundsX), t = 0; t < l; t++) { var d = s[t], + p = d.bounds, + f = d.bounds.max.x, + v = d.bounds.max.y, + y = d.bounds.min.y, + m = d.isStatic || d.isSleeping, + g = d.parts.length, + x = 1 === g; for (n = t + 1; n < l; n++) { var h = s[n]; if ((M = h.bounds).min.x > f) break; if (!(v < M.min.y || y > M.max.y) && (!m || !h.isStatic && !h.isSleeping) && c(d.collisionFilter, h.collisionFilter)) { var b = h.parts.length; if (x && 1 === b)(C = u(d, h, a)) && o.push(C); + else + for (var S = b > 1 ? 1 : 0, w = g > 1 ? 1 : 0; w < g; w++) + for (var A = d.parts[w], P = (p = A.bounds, S); P < b; P++) { var C, B = h.parts[P], + M = B.bounds; + p.min.x > M.max.x || p.max.x < M.min.x || p.max.y < M.min.y || p.min.y > M.max.y || (C = u(A, B, a)) && o.push(C) } } } } return o }, i.canCollide = function(e, t) { return e.group === t.group && 0 !== e.group ? e.group > 0 : 0 != (e.mask & t.category) && 0 != (t.mask & e.category) }, i._compareBoundsX = function(e, t) { return e.bounds.min.x - t.bounds.min.x } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(0); + i._registry = {}, i.register = function(e) { if (i.isPlugin(e) || o.warn("Plugin.register:", i.toString(e), "does not implement all required fields."), e.name in i._registry) { var t = i._registry[e.name], + n = i.versionParse(e.version).number, + r = i.versionParse(t.version).number; + n > r ? (o.warn("Plugin.register:", i.toString(t), "was upgraded to", i.toString(e)), i._registry[e.name] = e) : n < r ? o.warn("Plugin.register:", i.toString(t), "can not be downgraded to", i.toString(e)) : e !== t && o.warn("Plugin.register:", i.toString(e), "is already registered to different plugin object") } else i._registry[e.name] = e; return e }, i.resolve = function(e) { return i._registry[i.dependencyParse(e).name] }, i.toString = function(e) { return "string" == typeof e ? e : (e.name || "anonymous") + "@" + (e.version || e.range || "0.0.0") }, i.isPlugin = function(e) { return e && e.name && e.version && e.install }, i.isUsed = function(e, t) { return e.used.indexOf(t) > -1 }, i.isFor = function(e, t) { var n = e.for && i.dependencyParse(e.for); return !e.for || t.name === n.name && i.versionSatisfies(t.version, n.range) }, i.use = function(e, t) { if (e.uses = (e.uses || []).concat(t || []), 0 !== e.uses.length) { for (var n = i.dependencies(e), r = o.topologicalSort(n), a = [], s = 0; s < r.length; s += 1) + if (r[s] !== e.name) { var l = i.resolve(r[s]); + l ? i.isUsed(e, l.name) || (i.isFor(l, e) || (o.warn("Plugin.use:", i.toString(l), "is for", l.for, "but installed on", i.toString(e) + "."), l._warned = !0), l.install ? l.install(e) : (o.warn("Plugin.use:", i.toString(l), "does not specify an install function."), l._warned = !0), l._warned ? (a.push("🔶 " + i.toString(l)), delete l._warned) : a.push("✅ " + i.toString(l)), e.used.push(l.name)) : a.push("❌ " + r[s]) } a.length > 0 && o.info(a.join(" ")) } else o.warn("Plugin.use:", i.toString(e), "does not specify any dependencies to install.") }, i.dependencies = function(e, t) { var n = i.dependencyParse(e), + r = n.name; if (!(r in (t = t || {}))) { e = i.resolve(e) || e, t[r] = o.map(e.uses || [], (function(t) { i.isPlugin(t) && i.register(t); var r = i.dependencyParse(t), + a = i.resolve(t); return a && !i.versionSatisfies(a.version, r.range) ? (o.warn("Plugin.dependencies:", i.toString(a), "does not satisfy", i.toString(r), "used by", i.toString(n) + "."), a._warned = !0, e._warned = !0) : a || (o.warn("Plugin.dependencies:", i.toString(t), "used by", i.toString(n), "could not be resolved."), e._warned = !0), r.name })); for (var a = 0; a < t[r].length; a += 1) i.dependencies(t[r][a], t); return t } }, i.dependencyParse = function(e) { return o.isString(e) ? (/^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-+]+)?))?$/.test(e) || o.warn("Plugin.dependencyParse:", e, "is not a valid dependency string."), { name: e.split("@")[0], range: e.split("@")[1] || "*" }) : { name: e.name, range: e.range || e.version } }, i.versionParse = function(e) { var t = /^(\*)|(\^|~|>=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-+]+)?$/; + t.test(e) || o.warn("Plugin.versionParse:", e, "is not a valid version or range."); var n = t.exec(e), + i = Number(n[4]), + r = Number(n[5]), + a = Number(n[6]); return { isRange: Boolean(n[1] || n[2]), version: n[3], range: e, operator: n[1] || n[2] || "", major: i, minor: r, patch: a, parts: [i, r, a], prerelease: n[7], number: 1e8 * i + 1e4 * r + a } }, i.versionSatisfies = function(e, t) { t = t || "*"; var n = i.versionParse(t), + o = i.versionParse(e); if (n.isRange) { if ("*" === n.operator || "*" === e) return !0; if (">" === n.operator) return o.number > n.number; if (">=" === n.operator) return o.number >= n.number; if ("~" === n.operator) return o.major === n.major && o.minor === n.minor && o.patch >= n.patch; if ("^" === n.operator) return n.major > 0 ? o.major === n.major && o.number >= n.number : n.minor > 0 ? o.minor === n.minor && o.patch >= n.patch : o.patch === n.patch } return e === t || "*" === e } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(0), + r = n(5), + a = n(1), + s = n(4), + l = n(2), + c = n(13);! function() { var e, t; "undefined" != typeof window && (e = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function(e) { window.setTimeout((function() { e(o.now()) }), 1e3 / 60) }, t = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame), i._goodFps = 30, i._goodDelta = 1e3 / 60, i.create = function(e) { var t = { engine: null, element: null, canvas: null, mouse: null, frameRequestId: null, timing: { historySize: 60, delta: 0, deltaHistory: [], lastTime: 0, lastTimestamp: 0, lastElapsed: 0, timestampElapsed: 0, timestampElapsedHistory: [], engineDeltaHistory: [], engineElapsedHistory: [], elapsedHistory: [] }, options: { width: 800, height: 600, pixelRatio: 1, background: "#14151f", wireframeBackground: "#14151f", hasBounds: !!e.bounds, enabled: !0, wireframes: !0, showSleeping: !0, showDebug: !1, showStats: !1, showPerformance: !1, showBounds: !1, showVelocity: !1, showCollisions: !1, showSeparations: !1, showAxes: !1, showPositions: !1, showAngleIndicator: !1, showIds: !1, showVertexNumbers: !1, showConvexHulls: !1, showInternalEdges: !1, showMousePosition: !1 } }, + n = o.extend(t, e); return n.canvas && (n.canvas.width = n.options.width || n.canvas.width, n.canvas.height = n.options.height || n.canvas.height), n.mouse = e.mouse, n.engine = e.engine, n.canvas = n.canvas || d(n.options.width, n.options.height), n.context = n.canvas.getContext("2d"), n.textures = {}, n.bounds = n.bounds || { min: { x: 0, y: 0 }, max: { x: n.canvas.width, y: n.canvas.height } }, n.controller = i, n.options.showBroadphase = !1, 1 !== n.options.pixelRatio && i.setPixelRatio(n, n.options.pixelRatio), o.isElement(n.element) && n.element.appendChild(n.canvas), n }, i.run = function(t) {! function o(r) { t.frameRequestId = e(o), n(t, r), i.world(t, r), (t.options.showStats || t.options.showDebug) && i.stats(t, t.context, r), (t.options.showPerformance || t.options.showDebug) && i.performance(t, t.context, r) }() }, i.stop = function(e) { t(e.frameRequestId) }, i.setPixelRatio = function(e, t) { var n = e.options, + i = e.canvas; "auto" === t && (t = p(i)), n.pixelRatio = t, i.setAttribute("data-pixel-ratio", t), i.width = n.width * t, i.height = n.height * t, i.style.width = n.width + "px", i.style.height = n.height + "px" }, i.lookAt = function(e, t, n, i) { i = void 0 === i || i, t = o.isArray(t) ? t : [t], n = n || { x: 0, y: 0 }; for (var r = { min: { x: 1 / 0, y: 1 / 0 }, max: { x: -1 / 0, y: -1 / 0 } }, a = 0; a < t.length; a += 1) { var s = t[a], + l = s.bounds ? s.bounds.min : s.min || s.position || s, + u = s.bounds ? s.bounds.max : s.max || s.position || s; + l && u && (l.x < r.min.x && (r.min.x = l.x), u.x > r.max.x && (r.max.x = u.x), l.y < r.min.y && (r.min.y = l.y), u.y > r.max.y && (r.max.y = u.y)) } var d = r.max.x - r.min.x + 2 * n.x, + p = r.max.y - r.min.y + 2 * n.y, + f = e.canvas.height, + v = e.canvas.width / f, + y = d / p, + m = 1, + g = 1; + y > v ? g = y / v : m = v / y, e.options.hasBounds = !0, e.bounds.min.x = r.min.x, e.bounds.max.x = r.min.x + d * m, e.bounds.min.y = r.min.y, e.bounds.max.y = r.min.y + p * g, i && (e.bounds.min.x += .5 * d - d * m * .5, e.bounds.max.x += .5 * d - d * m * .5, e.bounds.min.y += .5 * p - p * g * .5, e.bounds.max.y += .5 * p - p * g * .5), e.bounds.min.x -= n.x, e.bounds.max.x -= n.x, e.bounds.min.y -= n.y, e.bounds.max.y -= n.y, e.mouse && (c.setScale(e.mouse, { x: (e.bounds.max.x - e.bounds.min.x) / e.canvas.width, y: (e.bounds.max.y - e.bounds.min.y) / e.canvas.height }), c.setOffset(e.mouse, e.bounds.min)) }, i.startViewTransform = function(e) { var t = e.bounds.max.x - e.bounds.min.x, + n = e.bounds.max.y - e.bounds.min.y, + i = t / e.options.width, + o = n / e.options.height; + e.context.setTransform(e.options.pixelRatio / i, 0, 0, e.options.pixelRatio / o, 0, 0), e.context.translate(-e.bounds.min.x, -e.bounds.min.y) }, i.endViewTransform = function(e) { e.context.setTransform(e.options.pixelRatio, 0, 0, e.options.pixelRatio, 0, 0) }, i.world = function(e, t) { var n, u = o.now(), + d = e.engine, + p = d.world, + f = e.canvas, + y = e.context, + m = e.options, + g = e.timing, + x = r.allBodies(p), + h = r.allConstraints(p), + b = m.wireframes ? m.wireframeBackground : m.background, + S = [], + w = [], + A = { timestamp: d.timing.timestamp }; if (s.trigger(e, "beforeRender", A), e.currentBackground !== b && v(e, b), y.globalCompositeOperation = "source-in", y.fillStyle = "transparent", y.fillRect(0, 0, f.width, f.height), y.globalCompositeOperation = "source-over", m.hasBounds) { for (n = 0; n < x.length; n++) { var P = x[n]; + a.overlaps(P.bounds, e.bounds) && S.push(P) } for (n = 0; n < h.length; n++) { var C = h[n], + B = C.bodyA, + M = C.bodyB, + k = C.pointA, + _ = C.pointB; + B && (k = l.add(B.position, C.pointA)), M && (_ = l.add(M.position, C.pointB)), k && _ && ((a.contains(e.bounds, k) || a.contains(e.bounds, _)) && w.push(C)) } i.startViewTransform(e), e.mouse && (c.setScale(e.mouse, { x: (e.bounds.max.x - e.bounds.min.x) / e.options.width, y: (e.bounds.max.y - e.bounds.min.y) / e.options.height }), c.setOffset(e.mouse, e.bounds.min)) } else w = h, S = x, 1 !== e.options.pixelRatio && e.context.setTransform(e.options.pixelRatio, 0, 0, e.options.pixelRatio, 0, 0);!m.wireframes || d.enableSleeping && m.showSleeping ? i.bodies(e, S, y) : (m.showConvexHulls && i.bodyConvexHulls(e, S, y), i.bodyWireframes(e, S, y)), m.showBounds && i.bodyBounds(e, S, y), (m.showAxes || m.showAngleIndicator) && i.bodyAxes(e, S, y), m.showPositions && i.bodyPositions(e, S, y), m.showVelocity && i.bodyVelocity(e, S, y), m.showIds && i.bodyIds(e, S, y), m.showSeparations && i.separations(e, d.pairs.list, y), m.showCollisions && i.collisions(e, d.pairs.list, y), m.showVertexNumbers && i.vertexNumbers(e, S, y), m.showMousePosition && i.mousePosition(e, e.mouse, y), i.constraints(w, y), m.hasBounds && i.endViewTransform(e), s.trigger(e, "afterRender", A), g.lastElapsed = o.now() - u }, i.stats = function(e, t, n) { for (var i = e.engine, o = i.world, a = r.allBodies(o), s = 0, l = 0, c = 0; c < a.length; c += 1) s += a[c].parts.length; var u = { Part: s, Body: a.length, Cons: r.allConstraints(o).length, Comp: r.allComposites(o).length, Pair: i.pairs.list.length }; for (var d in t.fillStyle = "#0e0f19", t.fillRect(l, 0, 302.5, 44), t.font = "12px Arial", t.textBaseline = "top", t.textAlign = "right", u) { var p = u[d]; + t.fillStyle = "#aaa", t.fillText(d, l + 55, 8), t.fillStyle = "#eee", t.fillText(p, l + 55, 26), l += 55 } }, i.performance = function(e, t) { var n = e.engine, + o = e.timing, + r = o.deltaHistory, + a = o.elapsedHistory, + s = o.timestampElapsedHistory, + l = o.engineDeltaHistory, + c = o.engineElapsedHistory, + d = n.timing.lastDelta, + p = u(r), + f = u(a), + v = u(l), + y = u(c), + m = u(s) / p || 0, + g = 1e3 / p || 0; + t.fillStyle = "#0e0f19", t.fillRect(0, 50, 370, 34), i.status(t, 10, 69, 60, 4, r.length, Math.round(g) + " fps", g / i._goodFps, (function(e) { return r[e] / p - 1 })), i.status(t, 82, 69, 60, 4, l.length, d.toFixed(2) + " dt", i._goodDelta / d, (function(e) { return l[e] / v - 1 })), i.status(t, 154, 69, 60, 4, c.length, y.toFixed(2) + " ut", 1 - y / i._goodFps, (function(e) { return c[e] / y - 1 })), i.status(t, 226, 69, 60, 4, a.length, f.toFixed(2) + " rt", 1 - f / i._goodFps, (function(e) { return a[e] / f - 1 })), i.status(t, 298, 69, 60, 4, s.length, m.toFixed(2) + " x", m * m * m, (function(e) { return (s[e] / r[e] / m || 0) - 1 })) }, i.status = function(e, t, n, i, r, a, s, l, c) { e.strokeStyle = "#888", e.fillStyle = "#444", e.lineWidth = 1, e.fillRect(t, n + 7, i, 1), e.beginPath(), e.moveTo(t, n + 7 - r * o.clamp(.4 * c(0), -2, 2)); for (var u = 0; u < i; u += 1) e.lineTo(t + u, n + 7 - (u < a ? r * o.clamp(.4 * c(u), -2, 2) : 0)); + e.stroke(), e.fillStyle = "hsl(" + o.clamp(25 + 95 * l, 0, 120) + ",100%,60%)", e.fillRect(t, n - 7, 4, 4), e.font = "12px Arial", e.textBaseline = "middle", e.textAlign = "right", e.fillStyle = "#eee", e.fillText(s, t + i, n - 5) }, i.constraints = function(e, t) { for (var n = t, i = 0; i < e.length; i++) { var r = e[i]; if (r.render.visible && r.pointA && r.pointB) { var a, s, c = r.bodyA, + u = r.bodyB; if (a = c ? l.add(c.position, r.pointA) : r.pointA, "pin" === r.render.type) n.beginPath(), n.arc(a.x, a.y, 3, 0, 2 * Math.PI), n.closePath(); + else { if (s = u ? l.add(u.position, r.pointB) : r.pointB, n.beginPath(), n.moveTo(a.x, a.y), "spring" === r.render.type) + for (var d, p = l.sub(s, a), f = l.perp(l.normalise(p)), v = Math.ceil(o.clamp(r.length / 5, 12, 20)), y = 1; y < v; y += 1) d = y % 2 == 0 ? 1 : -1, n.lineTo(a.x + p.x * (y / v) + f.x * d * 4, a.y + p.y * (y / v) + f.y * d * 4); + n.lineTo(s.x, s.y) } r.render.lineWidth && (n.lineWidth = r.render.lineWidth, n.strokeStyle = r.render.strokeStyle, n.stroke()), r.render.anchors && (n.fillStyle = r.render.strokeStyle, n.beginPath(), n.arc(a.x, a.y, 3, 0, 2 * Math.PI), n.arc(s.x, s.y, 3, 0, 2 * Math.PI), n.closePath(), n.fill()) } } }, i.bodies = function(e, t, n) { var i, o, r, a, s = n, + l = (e.engine, e.options), + c = l.showInternalEdges || !l.wireframes; for (r = 0; r < t.length; r++) + if ((i = t[r]).render.visible) + for (a = i.parts.length > 1 ? 1 : 0; a < i.parts.length; a++) + if ((o = i.parts[a]).render.visible) { if (l.showSleeping && i.isSleeping ? s.globalAlpha = .5 * o.render.opacity : 1 !== o.render.opacity && (s.globalAlpha = o.render.opacity), o.render.sprite && o.render.sprite.texture && !l.wireframes) { var u = o.render.sprite, + d = f(e, u.texture); + s.translate(o.position.x, o.position.y), s.rotate(o.angle), s.drawImage(d, d.width * -u.xOffset * u.xScale, d.height * -u.yOffset * u.yScale, d.width * u.xScale, d.height * u.yScale), s.rotate(-o.angle), s.translate(-o.position.x, -o.position.y) } else { if (o.circleRadius) s.beginPath(), s.arc(o.position.x, o.position.y, o.circleRadius, 0, 2 * Math.PI); + else { s.beginPath(), s.moveTo(o.vertices[0].x, o.vertices[0].y); for (var p = 1; p < o.vertices.length; p++) !o.vertices[p - 1].isInternal || c ? s.lineTo(o.vertices[p].x, o.vertices[p].y) : s.moveTo(o.vertices[p].x, o.vertices[p].y), o.vertices[p].isInternal && !c && s.moveTo(o.vertices[(p + 1) % o.vertices.length].x, o.vertices[(p + 1) % o.vertices.length].y); + s.lineTo(o.vertices[0].x, o.vertices[0].y), s.closePath() } l.wireframes ? (s.lineWidth = 1, s.strokeStyle = "#bbb", s.stroke()) : (s.fillStyle = o.render.fillStyle, o.render.lineWidth && (s.lineWidth = o.render.lineWidth, s.strokeStyle = o.render.strokeStyle, s.stroke()), s.fill()) } s.globalAlpha = 1 } }, i.bodyWireframes = function(e, t, n) { var i, o, r, a, s, l = n, + c = e.options.showInternalEdges; for (l.beginPath(), r = 0; r < t.length; r++) + if ((i = t[r]).render.visible) + for (s = i.parts.length > 1 ? 1 : 0; s < i.parts.length; s++) { for (o = i.parts[s], l.moveTo(o.vertices[0].x, o.vertices[0].y), a = 1; a < o.vertices.length; a++) !o.vertices[a - 1].isInternal || c ? l.lineTo(o.vertices[a].x, o.vertices[a].y) : l.moveTo(o.vertices[a].x, o.vertices[a].y), o.vertices[a].isInternal && !c && l.moveTo(o.vertices[(a + 1) % o.vertices.length].x, o.vertices[(a + 1) % o.vertices.length].y); + l.lineTo(o.vertices[0].x, o.vertices[0].y) } l.lineWidth = 1, l.strokeStyle = "#bbb", l.stroke() }, i.bodyConvexHulls = function(e, t, n) { var i, o, r, a = n; for (a.beginPath(), o = 0; o < t.length; o++) + if ((i = t[o]).render.visible && 1 !== i.parts.length) { for (a.moveTo(i.vertices[0].x, i.vertices[0].y), r = 1; r < i.vertices.length; r++) a.lineTo(i.vertices[r].x, i.vertices[r].y); + a.lineTo(i.vertices[0].x, i.vertices[0].y) } a.lineWidth = 1, a.strokeStyle = "rgba(255,255,255,0.2)", a.stroke() }, i.vertexNumbers = function(e, t, n) { var i, o, r, a = n; for (i = 0; i < t.length; i++) { var s = t[i].parts; for (r = s.length > 1 ? 1 : 0; r < s.length; r++) { var l = s[r]; for (o = 0; o < l.vertices.length; o++) a.fillStyle = "rgba(255,255,255,0.2)", a.fillText(i + "_" + o, l.position.x + .8 * (l.vertices[o].x - l.position.x), l.position.y + .8 * (l.vertices[o].y - l.position.y)) } } }, i.mousePosition = function(e, t, n) { var i = n; + i.fillStyle = "rgba(255,255,255,0.8)", i.fillText(t.position.x + " " + t.position.y, t.position.x + 5, t.position.y - 5) }, i.bodyBounds = function(e, t, n) { var i = n, + o = (e.engine, e.options); + i.beginPath(); for (var r = 0; r < t.length; r++) { if (t[r].render.visible) + for (var a = t[r].parts, s = a.length > 1 ? 1 : 0; s < a.length; s++) { var l = a[s]; + i.rect(l.bounds.min.x, l.bounds.min.y, l.bounds.max.x - l.bounds.min.x, l.bounds.max.y - l.bounds.min.y) } } o.wireframes ? i.strokeStyle = "rgba(255,255,255,0.08)" : i.strokeStyle = "rgba(0,0,0,0.1)", i.lineWidth = 1, i.stroke() }, i.bodyAxes = function(e, t, n) { var i, o, r, a, s = n, + l = (e.engine, e.options); for (s.beginPath(), o = 0; o < t.length; o++) { var c = t[o], + u = c.parts; if (c.render.visible) + if (l.showAxes) + for (r = u.length > 1 ? 1 : 0; r < u.length; r++) + for (i = u[r], a = 0; a < i.axes.length; a++) { var d = i.axes[a]; + s.moveTo(i.position.x, i.position.y), s.lineTo(i.position.x + 20 * d.x, i.position.y + 20 * d.y) } else + for (r = u.length > 1 ? 1 : 0; r < u.length; r++) + for (i = u[r], a = 0; a < i.axes.length; a++) s.moveTo(i.position.x, i.position.y), s.lineTo((i.vertices[0].x + i.vertices[i.vertices.length - 1].x) / 2, (i.vertices[0].y + i.vertices[i.vertices.length - 1].y) / 2) } l.wireframes ? (s.strokeStyle = "indianred", s.lineWidth = 1) : (s.strokeStyle = "rgba(255, 255, 255, 0.4)", s.globalCompositeOperation = "overlay", s.lineWidth = 2), s.stroke(), s.globalCompositeOperation = "source-over" }, i.bodyPositions = function(e, t, n) { var i, o, r, a, s = n, + l = (e.engine, e.options); for (s.beginPath(), r = 0; r < t.length; r++) + if ((i = t[r]).render.visible) + for (a = 0; a < i.parts.length; a++) o = i.parts[a], s.arc(o.position.x, o.position.y, 3, 0, 2 * Math.PI, !1), s.closePath(); for (l.wireframes ? s.fillStyle = "indianred" : s.fillStyle = "rgba(0,0,0,0.5)", s.fill(), s.beginPath(), r = 0; r < t.length; r++)(i = t[r]).render.visible && (s.arc(i.positionPrev.x, i.positionPrev.y, 2, 0, 2 * Math.PI, !1), s.closePath()); + s.fillStyle = "rgba(255,165,0,0.8)", s.fill() }, i.bodyVelocity = function(e, t, n) { var i = n; + i.beginPath(); for (var o = 0; o < t.length; o++) { var r = t[o]; + r.render.visible && (i.moveTo(r.position.x, r.position.y), i.lineTo(r.position.x + 2 * (r.position.x - r.positionPrev.x), r.position.y + 2 * (r.position.y - r.positionPrev.y))) } i.lineWidth = 3, i.strokeStyle = "cornflowerblue", i.stroke() }, i.bodyIds = function(e, t, n) { var i, o, r = n; for (i = 0; i < t.length; i++) + if (t[i].render.visible) { var a = t[i].parts; for (o = a.length > 1 ? 1 : 0; o < a.length; o++) { var s = a[o]; + r.font = "12px Arial", r.fillStyle = "rgba(255,255,255,0.5)", r.fillText(s.id, s.position.x + 10, s.position.y - 10) } } }, i.collisions = function(e, t, n) { var i, o, r, a, s = n, + l = e.options; for (s.beginPath(), r = 0; r < t.length; r++) + if ((i = t[r]).isActive) + for (o = i.collision, a = 0; a < i.activeContacts.length; a++) { var c = i.activeContacts[a].vertex; + s.rect(c.x - 1.5, c.y - 1.5, 3.5, 3.5) } + for (l.wireframes ? s.fillStyle = "rgba(255,255,255,0.7)" : s.fillStyle = "orange", s.fill(), s.beginPath(), r = 0; r < t.length; r++) + if ((i = t[r]).isActive && (o = i.collision, i.activeContacts.length > 0)) { var u = i.activeContacts[0].vertex.x, + d = i.activeContacts[0].vertex.y; + 2 === i.activeContacts.length && (u = (i.activeContacts[0].vertex.x + i.activeContacts[1].vertex.x) / 2, d = (i.activeContacts[0].vertex.y + i.activeContacts[1].vertex.y) / 2), o.bodyB === o.supports[0].body || !0 === o.bodyA.isStatic ? s.moveTo(u - 8 * o.normal.x, d - 8 * o.normal.y) : s.moveTo(u + 8 * o.normal.x, d + 8 * o.normal.y), s.lineTo(u, d) } l.wireframes ? s.strokeStyle = "rgba(255,165,0,0.7)" : s.strokeStyle = "orange", s.lineWidth = 1, s.stroke() }, i.separations = function(e, t, n) { var i, o, r, a, s, l = n, + c = e.options; for (l.beginPath(), s = 0; s < t.length; s++) + if ((i = t[s]).isActive) { r = (o = i.collision).bodyA; var u = 1; + (a = o.bodyB).isStatic || r.isStatic || (u = .5), a.isStatic && (u = 0), l.moveTo(a.position.x, a.position.y), l.lineTo(a.position.x - o.penetration.x * u, a.position.y - o.penetration.y * u), u = 1, a.isStatic || r.isStatic || (u = .5), r.isStatic && (u = 0), l.moveTo(r.position.x, r.position.y), l.lineTo(r.position.x + o.penetration.x * u, r.position.y + o.penetration.y * u) } c.wireframes ? l.strokeStyle = "rgba(255,165,0,0.5)" : l.strokeStyle = "orange", l.stroke() }, i.inspector = function(e, t) { e.engine; var n, i = e.selected, + o = e.render, + r = o.options; if (r.hasBounds) { var a = o.bounds.max.x - o.bounds.min.x, + s = o.bounds.max.y - o.bounds.min.y, + l = a / o.options.width, + c = s / o.options.height; + t.scale(1 / l, 1 / c), t.translate(-o.bounds.min.x, -o.bounds.min.y) } for (var u = 0; u < i.length; u++) { var d = i[u].data; switch (t.translate(.5, .5), t.lineWidth = 1, t.strokeStyle = "rgba(255,165,0,0.9)", t.setLineDash([1, 2]), d.type) { + case "body": + n = d.bounds, t.beginPath(), t.rect(Math.floor(n.min.x - 3), Math.floor(n.min.y - 3), Math.floor(n.max.x - n.min.x + 6), Math.floor(n.max.y - n.min.y + 6)), t.closePath(), t.stroke(); break; + case "constraint": + var p = d.pointA; + d.bodyA && (p = d.pointB), t.beginPath(), t.arc(p.x, p.y, 10, 0, 2 * Math.PI), t.closePath(), t.stroke() } t.setLineDash([]), t.translate(-.5, -.5) } null !== e.selectStart && (t.translate(.5, .5), t.lineWidth = 1, t.strokeStyle = "rgba(255,165,0,0.6)", t.fillStyle = "rgba(255,165,0,0.1)", n = e.selectBounds, t.beginPath(), t.rect(Math.floor(n.min.x), Math.floor(n.min.y), Math.floor(n.max.x - n.min.x), Math.floor(n.max.y - n.min.y)), t.closePath(), t.stroke(), t.fill(), t.translate(-.5, -.5)), r.hasBounds && t.setTransform(1, 0, 0, 1, 0, 0) }; var n = function(e, t) { var n = e.engine, + o = e.timing, + r = o.historySize, + a = n.timing.timestamp; + o.delta = t - o.lastTime || i._goodDelta, o.lastTime = t, o.timestampElapsed = a - o.lastTimestamp || 0, o.lastTimestamp = a, o.deltaHistory.unshift(o.delta), o.deltaHistory.length = Math.min(o.deltaHistory.length, r), o.engineDeltaHistory.unshift(n.timing.lastDelta), o.engineDeltaHistory.length = Math.min(o.engineDeltaHistory.length, r), o.timestampElapsedHistory.unshift(o.timestampElapsed), o.timestampElapsedHistory.length = Math.min(o.timestampElapsedHistory.length, r), o.engineElapsedHistory.unshift(n.timing.lastElapsed), o.engineElapsedHistory.length = Math.min(o.engineElapsedHistory.length, r), o.elapsedHistory.unshift(o.lastElapsed), o.elapsedHistory.length = Math.min(o.elapsedHistory.length, r) }, + u = function(e) { for (var t = 0, n = 0; n < e.length; n += 1) t += e[n]; return t / e.length || 0 }, + d = function(e, t) { var n = document.createElement("canvas"); return n.width = e, n.height = t, n.oncontextmenu = function() { return !1 }, n.onselectstart = function() { return !1 }, n }, + p = function(e) { var t = e.getContext("2d"); return (window.devicePixelRatio || 1) / (t.webkitBackingStorePixelRatio || t.mozBackingStorePixelRatio || t.msBackingStorePixelRatio || t.oBackingStorePixelRatio || t.backingStorePixelRatio || 1) }, + f = function(e, t) { var n = e.textures[t]; return n || ((n = e.textures[t] = new Image).src = t, n) }, + v = function(e, t) { var n = t; /(jpg|gif|png)$/.test(t) && (n = "url(" + t + ")"), e.canvas.style.background = n, e.canvas.style.backgroundSize = "contain", e.currentBackground = t } }() }, function(e, t) { var n = {}; + e.exports = n, n.create = function(e) { return { vertex: e, normalImpulse: 0, tangentImpulse: 0 } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(7), + r = n(19), + a = n(14), + s = n(20), + l = n(4), + c = n(5), + u = n(10), + d = n(0), + p = n(6); + i.create = function(e) { e = e || {}; var t = d.extend({ positionIterations: 6, velocityIterations: 4, constraintIterations: 2, enableSleeping: !1, events: [], plugin: {}, gravity: { x: 0, y: 1, scale: .001 }, timing: { timestamp: 0, timeScale: 1, lastDelta: 0, lastElapsed: 0 } }, e); return t.world = e.world || c.create({ label: "World" }), t.pairs = e.pairs || s.create(), t.detector = e.detector || a.create(), t.grid = { buckets: [] }, t.world.gravity = t.gravity, t.broadphase = t.grid, t.metrics = {}, t }, i.update = function(e, t, n) { var p = d.now(); + t = t || 1e3 / 60, n = n || 1; var f, v = e.world, + y = e.detector, + m = e.pairs, + g = e.timing, + x = g.timestamp; + g.timestamp += t * g.timeScale, g.lastDelta = t * g.timeScale; var h = { timestamp: g.timestamp }; + l.trigger(e, "beforeUpdate", h); var b = c.allBodies(v), + S = c.allConstraints(v); for (v.isModified && a.setBodies(y, b), v.isModified && c.setModified(v, !1, !1, !0), e.enableSleeping && o.update(b, g.timeScale), i._bodiesApplyGravity(b, e.gravity), i._bodiesUpdate(b, t, g.timeScale, n, v.bounds), u.preSolveAll(b), f = 0; f < e.constraintIterations; f++) u.solveAll(S, g.timeScale); + u.postSolveAll(b), y.pairs = e.pairs; var w = a.collisions(y); for (s.update(m, w, x), e.enableSleeping && o.afterCollisions(m.list, g.timeScale), m.collisionStart.length > 0 && l.trigger(e, "collisionStart", { pairs: m.collisionStart }), r.preSolvePosition(m.list), f = 0; f < e.positionIterations; f++) r.solvePosition(m.list, g.timeScale); for (r.postSolvePosition(b), u.preSolveAll(b), f = 0; f < e.constraintIterations; f++) u.solveAll(S, g.timeScale); for (u.postSolveAll(b), r.preSolveVelocity(m.list), f = 0; f < e.velocityIterations; f++) r.solveVelocity(m.list, g.timeScale); return m.collisionActive.length > 0 && l.trigger(e, "collisionActive", { pairs: m.collisionActive }), m.collisionEnd.length > 0 && l.trigger(e, "collisionEnd", { pairs: m.collisionEnd }), i._bodiesClearForces(b), l.trigger(e, "afterUpdate", h), e.timing.lastElapsed = d.now() - p, e }, i.merge = function(e, t) { if (d.extend(e, t), t.world) { e.world = t.world, i.clear(e); for (var n = c.allBodies(e.world), r = 0; r < n.length; r++) { var a = n[r]; + o.set(a, !1), a.id = d.nextId() } } }, i.clear = function(e) { s.clear(e.pairs), a.clear(e.detector) }, i._bodiesClearForces = function(e) { for (var t = 0; t < e.length; t++) { var n = e[t]; + n.force.x = 0, n.force.y = 0, n.torque = 0 } }, i._bodiesApplyGravity = function(e, t) { var n = void 0 !== t.scale ? t.scale : .001; if ((0 !== t.x || 0 !== t.y) && 0 !== n) + for (var i = 0; i < e.length; i++) { var o = e[i]; + o.isStatic || o.isSleeping || (o.force.y += o.mass * t.y * n, o.force.x += o.mass * t.x * n) } }, i._bodiesUpdate = function(e, t, n, i, o) { for (var r = 0; r < e.length; r++) { var a = e[r]; + a.isStatic || a.isSleeping || p.update(a, t, n, i) } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(3), + r = n(1); + i._restingThresh = 4, i._restingThreshTangent = 6, i._positionDampen = .9, i._positionWarming = .8, i._frictionNormalMultiplier = 5, i.preSolvePosition = function(e) { var t, n, i, o = e.length; for (t = 0; t < o; t++)(n = e[t]).isActive && (i = n.activeContacts.length, n.collision.parentA.totalContacts += i, n.collision.parentB.totalContacts += i) }, i.solvePosition = function(e, t) { var n, o, r, a, s, l, c, u, d = i._positionDampen, + p = e.length; for (n = 0; n < p; n++)(o = e[n]).isActive && !o.isSensor && (a = (r = o.collision).parentA, s = r.parentB, l = r.normal, o.separation = l.x * (s.positionImpulse.x + r.penetration.x - a.positionImpulse.x) + l.y * (s.positionImpulse.y + r.penetration.y - a.positionImpulse.y)); for (n = 0; n < p; n++)(o = e[n]).isActive && !o.isSensor && (a = (r = o.collision).parentA, s = r.parentB, l = r.normal, u = (o.separation - o.slop) * t, (a.isStatic || s.isStatic) && (u *= 2), a.isStatic || a.isSleeping || (c = d / a.totalContacts, a.positionImpulse.x += l.x * u * c, a.positionImpulse.y += l.y * u * c), s.isStatic || s.isSleeping || (c = d / s.totalContacts, s.positionImpulse.x -= l.x * u * c, s.positionImpulse.y -= l.y * u * c)) }, i.postSolvePosition = function(e) { for (var t = i._positionWarming, n = e.length, a = o.translate, s = r.update, l = 0; l < n; l++) { var c = e[l], + u = c.positionImpulse, + d = u.x, + p = u.y, + f = c.velocity; if (c.totalContacts = 0, 0 !== d || 0 !== p) { for (var v = 0; v < c.parts.length; v++) { var y = c.parts[v]; + a(y.vertices, u), s(y.bounds, y.vertices, f), y.position.x += d, y.position.y += p } c.positionPrev.x += d, c.positionPrev.y += p, d * f.x + p * f.y < 0 ? (u.x = 0, u.y = 0) : (u.x *= t, u.y *= t) } } }, i.preSolveVelocity = function(e) { var t, n, i = e.length; for (t = 0; t < i; t++) { var o = e[t]; if (o.isActive && !o.isSensor) { var r = o.activeContacts, + a = r.length, + s = o.collision, + l = s.parentA, + c = s.parentB, + u = s.normal, + d = s.tangent; for (n = 0; n < a; n++) { var p = r[n], + f = p.vertex, + v = p.normalImpulse, + y = p.tangentImpulse; if (0 !== v || 0 !== y) { var m = u.x * v + d.x * y, + g = u.y * v + d.y * y; + l.isStatic || l.isSleeping || (l.positionPrev.x += m * l.inverseMass, l.positionPrev.y += g * l.inverseMass, l.anglePrev += l.inverseInertia * ((f.x - l.position.x) * g - (f.y - l.position.y) * m)), c.isStatic || c.isSleeping || (c.positionPrev.x -= m * c.inverseMass, c.positionPrev.y -= g * c.inverseMass, c.anglePrev -= c.inverseInertia * ((f.x - c.position.x) * g - (f.y - c.position.y) * m)) } } } } }, i.solveVelocity = function(e, t) { var n, o, r, a, s = t * t, + l = i._restingThresh * s, + c = i._frictionNormalMultiplier, + u = i._restingThreshTangent * s, + d = Number.MAX_VALUE, + p = e.length; for (r = 0; r < p; r++) { var f = e[r]; if (f.isActive && !f.isSensor) { var v = f.collision, + y = v.parentA, + m = v.parentB, + g = y.velocity, + x = m.velocity, + h = v.normal.x, + b = v.normal.y, + S = v.tangent.x, + w = v.tangent.y, + A = f.activeContacts, + P = A.length, + C = 1 / P, + B = y.inverseMass + m.inverseMass, + M = f.friction * f.frictionStatic * c * s; for (g.x = y.position.x - y.positionPrev.x, g.y = y.position.y - y.positionPrev.y, x.x = m.position.x - m.positionPrev.x, x.y = m.position.y - m.positionPrev.y, y.angularVelocity = y.angle - y.anglePrev, m.angularVelocity = m.angle - m.anglePrev, a = 0; a < P; a++) { var k = A[a], + _ = k.vertex, + I = _.x - y.position.x, + T = _.y - y.position.y, + R = _.x - m.position.x, + E = _.y - m.position.y, + V = g.x - T * y.angularVelocity, + L = g.y + I * y.angularVelocity, + O = V - (x.x - E * m.angularVelocity), + D = L - (x.y + R * m.angularVelocity), + F = h * O + b * D, + H = S * O + w * D, + j = f.separation + F, + q = Math.min(j, 1), + W = (q = j < 0 ? 0 : q) * M; + H > W || -H > W ? (o = H > 0 ? H : -H, (n = f.friction * (H > 0 ? 1 : -1) * s) < -o ? n = -o : n > o && (n = o)) : (n = H, o = d); var G = I * b - T * h, + N = R * b - E * h, + U = C / (B + y.inverseInertia * G * G + m.inverseInertia * N * N), + z = (1 + f.restitution) * F * U; if (n *= U, F * F > l && F < 0) k.normalImpulse = 0; + else { var X = k.normalImpulse; + k.normalImpulse += z, k.normalImpulse = Math.min(k.normalImpulse, 0), z = k.normalImpulse - X } if (H * H > u) k.tangentImpulse = 0; + else { var Q = k.tangentImpulse; + k.tangentImpulse += n, k.tangentImpulse < -o && (k.tangentImpulse = -o), k.tangentImpulse > o && (k.tangentImpulse = o), n = k.tangentImpulse - Q } var Y = h * z + S * n, + Z = b * z + w * n; + y.isStatic || y.isSleeping || (y.positionPrev.x += Y * y.inverseMass, y.positionPrev.y += Z * y.inverseMass, y.anglePrev += (I * Z - T * Y) * y.inverseInertia), m.isStatic || m.isSleeping || (m.positionPrev.x -= Y * m.inverseMass, m.positionPrev.y -= Z * m.inverseMass, m.anglePrev -= (R * Z - E * Y) * m.inverseInertia) } } } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(9), + r = n(0); + i.create = function(e) { return r.extend({ table: {}, list: [], collisionStart: [], collisionActive: [], collisionEnd: [] }, e) }, i.update = function(e, t, n) { var i, r, a, s, l, c, u = e.list, + d = u.length, + p = e.table, + f = t.length, + v = e.collisionStart, + y = e.collisionEnd, + m = e.collisionActive; for (v.length = 0, y.length = 0, m.length = 0, c = 0; c < d; c++) u[c].confirmedActive = !1; for (c = 0; c < f; c++)(l = (i = t[c]).pair) ? (l.isActive ? m.push(l) : v.push(l), o.update(l, i, n), l.confirmedActive = !0) : (p[(l = o.create(i, n)).id] = l, v.push(l), u.push(l)); var g = []; for (d = u.length, c = 0; c < d; c++)(l = u[c]).confirmedActive || (r = l.collision.bodyA, a = l.collision.bodyB, (!r.isSleeping && !r.isStatic || !a.isSleeping && !a.isStatic || r.isStatic && a.isStatic) && (o.setActive(l, !1, n), y.push(l), g.push(c))); for (c = 0; c < g.length; c++) l = u[s = g[c] - c], u.splice(s, 1), delete p[l.id] }, i.clear = function(e) { return e.table = {}, e.list.length = 0, e.collisionStart.length = 0, e.collisionActive.length = 0, e.collisionEnd.length = 0, e } }, function(e, t, n) { var i = e.exports = n(22); + i.Axes = n(11), i.Bodies = n(12), i.Body = n(6), i.Bounds = n(1), i.Collision = n(8), i.Common = n(0), i.Composite = n(5), i.Composites = n(23), i.Constraint = n(10), i.Contact = n(17), i.Detector = n(14), i.Engine = n(18), i.Events = n(4), i.Grid = n(24), i.Mouse = n(13), i.MouseConstraint = n(25), i.Pair = n(9), i.Pairs = n(20), i.Plugin = n(15), i.Query = n(26), i.Render = n(16), i.Resolver = n(19), i.Runner = n(27), i.SAT = n(28), i.Sleeping = n(7), i.Svg = n(29), i.Vector = n(2), i.Vertices = n(3), i.World = n(30), i.Engine.run = i.Runner.run, i.Common.deprecated(i.Engine, "run", "Engine.run ➤ use Matter.Runner.run(engine) instead") }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(15), + r = n(0); + i.name = "matter-js", i.version = "0.18.0-alpha+0eeceb5", i.uses = [], i.used = [], i.use = function() { o.use(i, Array.prototype.slice.call(arguments)) }, i.before = function(e, t) { return e = e.replace(/^Matter./, ""), r.chainPathBefore(i, e, t) }, i.after = function(e, t) { return e = e.replace(/^Matter./, ""), r.chainPathAfter(i, e, t) } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(5), + r = n(10), + a = n(0), + s = n(6), + l = n(12), + c = a.deprecated; + i.stack = function(e, t, n, i, r, a, l) { for (var c, u = o.create({ label: "Stack" }), d = e, p = t, f = 0, v = 0; v < i; v++) { for (var y = 0, m = 0; m < n; m++) { var g = l(d, p, m, v, c, f); if (g) { var x = g.bounds.max.y - g.bounds.min.y, + h = g.bounds.max.x - g.bounds.min.x; + x > y && (y = x), s.translate(g, { x: .5 * h, y: .5 * x }), d = g.bounds.max.x + r, o.addBody(u, g), c = g, f += 1 } else d += r } p += y + a, d = e } return u }, i.chain = function(e, t, n, i, s, l) { for (var c = e.bodies, u = 1; u < c.length; u++) { var d = c[u - 1], + p = c[u], + f = d.bounds.max.y - d.bounds.min.y, + v = d.bounds.max.x - d.bounds.min.x, + y = p.bounds.max.y - p.bounds.min.y, + m = { bodyA: d, pointA: { x: v * t, y: f * n }, bodyB: p, pointB: { x: (p.bounds.max.x - p.bounds.min.x) * i, y: y * s } }, + g = a.extend(m, l); + o.addConstraint(e, r.create(g)) } return e.label += " Chain", e }, i.mesh = function(e, t, n, i, s) { var l, c, u, d, p, f = e.bodies; for (l = 0; l < n; l++) { for (c = 1; c < t; c++) u = f[c - 1 + l * t], d = f[c + l * t], o.addConstraint(e, r.create(a.extend({ bodyA: u, bodyB: d }, s))); if (l > 0) + for (c = 0; c < t; c++) u = f[c + (l - 1) * t], d = f[c + l * t], o.addConstraint(e, r.create(a.extend({ bodyA: u, bodyB: d }, s))), i && c > 0 && (p = f[c - 1 + (l - 1) * t], o.addConstraint(e, r.create(a.extend({ bodyA: p, bodyB: d }, s)))), i && c < t - 1 && (p = f[c + 1 + (l - 1) * t], o.addConstraint(e, r.create(a.extend({ bodyA: p, bodyB: d }, s)))) } return e.label += " Mesh", e }, i.pyramid = function(e, t, n, o, r, a, l) { return i.stack(e, t, n, o, r, a, (function(t, i, a, c, u, d) { var p = Math.min(o, Math.ceil(n / 2)), + f = u ? u.bounds.max.x - u.bounds.min.x : 0; if (!(c > p || a < (c = p - c) || a > n - 1 - c)) return 1 === d && s.translate(u, { x: (a + (n % 2 == 1 ? 1 : -1)) * f, y: 0 }), l(e + (u ? a * f : 0) + a * r, i, a, c, u, d) })) }, i.newtonsCradle = function(e, t, n, i, a) { for (var s = o.create({ label: "Newtons Cradle" }), c = 0; c < n; c++) { var u = l.circle(e + c * (1.9 * i), t + a, i, { inertia: 1 / 0, restitution: 1, friction: 0, frictionAir: 1e-4, slop: 1 }), + d = r.create({ pointA: { x: e + c * (1.9 * i), y: t }, bodyB: u }); + o.addBody(s, u), o.addConstraint(s, d) } return s }, c(i, "newtonsCradle", "Composites.newtonsCradle ➤ moved to newtonsCradle example"), i.car = function(e, t, n, i, a) { var c = s.nextGroup(!0), + u = .5 * -n + 20, + d = .5 * n - 20, + p = o.create({ label: "Car" }), + f = l.rectangle(e, t, n, i, { collisionFilter: { group: c }, chamfer: { radius: .5 * i }, density: 2e-4 }), + v = l.circle(e + u, t + 0, a, { collisionFilter: { group: c }, friction: .8 }), + y = l.circle(e + d, t + 0, a, { collisionFilter: { group: c }, friction: .8 }), + m = r.create({ bodyB: f, pointB: { x: u, y: 0 }, bodyA: v, stiffness: 1, length: 0 }), + g = r.create({ bodyB: f, pointB: { x: d, y: 0 }, bodyA: y, stiffness: 1, length: 0 }); return o.addBody(p, f), o.addBody(p, v), o.addBody(p, y), o.addConstraint(p, m), o.addConstraint(p, g), p }, c(i, "car", "Composites.car ➤ moved to car example"), i.softBody = function(e, t, n, o, r, s, c, u, d, p) { d = a.extend({ inertia: 1 / 0 }, d), p = a.extend({ stiffness: .2, render: { type: "line", anchors: !1 } }, p); var f = i.stack(e, t, n, o, r, s, (function(e, t) { return l.circle(e, t, u, d) })); return i.mesh(f, n, o, c, p), f.label = "Soft Body", f }, c(i, "softBody", "Composites.softBody ➤ moved to softBody and cloth examples") }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(9), + r = n(0), + a = r.deprecated; + i.create = function(e) { return r.extend({ buckets: {}, pairs: {}, pairsList: [], bucketWidth: 48, bucketHeight: 48 }, e) }, i.update = function(e, t, n, o) { var r, a, s, l, c, u = n.world, + d = e.buckets, + p = !1; for (r = 0; r < t.length; r++) { var f = t[r]; if ((!f.isSleeping || o) && (!u.bounds || !(f.bounds.max.x < u.bounds.min.x || f.bounds.min.x > u.bounds.max.x || f.bounds.max.y < u.bounds.min.y || f.bounds.min.y > u.bounds.max.y))) { var v = i._getRegion(e, f); if (!f.region || v.id !== f.region.id || o) { f.region && !o || (f.region = v); var y = i._regionUnion(v, f.region); for (a = y.startCol; a <= y.endCol; a++) + for (s = y.startRow; s <= y.endRow; s++) { l = d[c = i._getBucketId(a, s)]; var m = a >= v.startCol && a <= v.endCol && s >= v.startRow && s <= v.endRow, + g = a >= f.region.startCol && a <= f.region.endCol && s >= f.region.startRow && s <= f.region.endRow;!m && g && g && l && i._bucketRemoveBody(e, l, f), (f.region === v || m && !g || o) && (l || (l = i._createBucket(d, c)), i._bucketAddBody(e, l, f)) } f.region = v, p = !0 } } } p && (e.pairsList = i._createActivePairsList(e)) }, a(i, "update", "Grid.update ➤ replaced by Matter.Detector"), i.clear = function(e) { e.buckets = {}, e.pairs = {}, e.pairsList = [] }, a(i, "clear", "Grid.clear ➤ replaced by Matter.Detector"), i._regionUnion = function(e, t) { var n = Math.min(e.startCol, t.startCol), + o = Math.max(e.endCol, t.endCol), + r = Math.min(e.startRow, t.startRow), + a = Math.max(e.endRow, t.endRow); return i._createRegion(n, o, r, a) }, i._getRegion = function(e, t) { var n = t.bounds, + o = Math.floor(n.min.x / e.bucketWidth), + r = Math.floor(n.max.x / e.bucketWidth), + a = Math.floor(n.min.y / e.bucketHeight), + s = Math.floor(n.max.y / e.bucketHeight); return i._createRegion(o, r, a, s) }, i._createRegion = function(e, t, n, i) { return { id: e + "," + t + "," + n + "," + i, startCol: e, endCol: t, startRow: n, endRow: i } }, i._getBucketId = function(e, t) { return "C" + e + "R" + t }, i._createBucket = function(e, t) { return e[t] = [] }, i._bucketAddBody = function(e, t, n) { var i, r = e.pairs, + a = o.id, + s = t.length; for (i = 0; i < s; i++) { var l = t[i]; if (!(n.id === l.id || n.isStatic && l.isStatic)) { var c = a(n, l), + u = r[c]; + u ? u[2] += 1 : r[c] = [n, l, 1] } } t.push(n) }, i._bucketRemoveBody = function(e, t, n) { var i, a = e.pairs, + s = o.id; + t.splice(r.indexOf(t, n), 1); var l = t.length; for (i = 0; i < l; i++) { var c = a[s(n, t[i])]; + c && (c[2] -= 1) } }, i._createActivePairsList = function(e) { var t, n, i = e.pairs, + o = r.keys(i), + a = o.length, + s = []; for (n = 0; n < a; n++)(t = i[o[n]])[2] > 0 ? s.push(t) : delete i[o[n]]; return s } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(3), + r = n(7), + a = n(13), + s = n(4), + l = n(14), + c = n(10), + u = n(5), + d = n(0), + p = n(1); + i.create = function(e, t) { var n = (e ? e.mouse : null) || (t ? t.mouse : null); + n || (e && e.render && e.render.canvas ? n = a.create(e.render.canvas) : t && t.element ? n = a.create(t.element) : (n = a.create(), d.warn("MouseConstraint.create: options.mouse was undefined, options.element was undefined, may not function as expected"))); var o = { type: "mouseConstraint", mouse: n, element: null, body: null, constraint: c.create({ label: "Mouse Constraint", pointA: n.position, pointB: { x: 0, y: 0 }, length: .01, stiffness: .1, angularStiffness: 1, render: { strokeStyle: "#90EE90", lineWidth: 3 } }), collisionFilter: { category: 1, mask: 4294967295, group: 0 } }, + r = d.extend(o, t); return s.on(e, "beforeUpdate", (function() { var t = u.allBodies(e.world); + i.update(r, t), i._triggerEvents(r) })), r }, i.update = function(e, t) { var n = e.mouse, + i = e.constraint, + a = e.body; if (0 === n.button) { if (i.bodyB) r.set(i.bodyB, !1), i.pointA = n.position; + else + for (var c = 0; c < t.length; c++) + if (a = t[c], p.contains(a.bounds, n.position) && l.canCollide(a.collisionFilter, e.collisionFilter)) + for (var u = a.parts.length > 1 ? 1 : 0; u < a.parts.length; u++) { var d = a.parts[u]; if (o.contains(d.vertices, n.position)) { i.pointA = n.position, i.bodyB = e.body = a, i.pointB = { x: n.position.x - a.position.x, y: n.position.y - a.position.y }, i.angleB = a.angle, r.set(a, !1), s.trigger(e, "startdrag", { mouse: n, body: a }); break } } } else i.bodyB = e.body = null, i.pointB = null, a && s.trigger(e, "enddrag", { mouse: n, body: a }) }, i._triggerEvents = function(e) { var t = e.mouse, + n = t.sourceEvents; + n.mousemove && s.trigger(e, "mousemove", { mouse: t }), n.mousedown && s.trigger(e, "mousedown", { mouse: t }), n.mouseup && s.trigger(e, "mouseup", { mouse: t }), a.clearSourceEvents(t) } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(2), + r = n(8), + a = n(1), + s = n(12), + l = n(3); + i.collides = function(e, t) { for (var n = [], i = t.length, o = e.bounds, s = r.collides, l = a.overlaps, c = 0; c < i; c++) { var u = t[c], + d = u.parts.length, + p = 1 === d ? 0 : 1; if (l(u.bounds, o)) + for (var f = p; f < d; f++) { var v = u.parts[f]; if (l(v.bounds, o)) { var y = s(v, e); if (y) { n.push(y); break } } } } return n }, i.ray = function(e, t, n, r) { r = r || 1e-100; for (var a = o.angle(t, n), l = o.magnitude(o.sub(t, n)), c = .5 * (n.x + t.x), u = .5 * (n.y + t.y), d = s.rectangle(c, u, l, r, { angle: a }), p = i.collides(d, e), f = 0; f < p.length; f += 1) { var v = p[f]; + v.body = v.bodyB = v.bodyA } return p }, i.region = function(e, t, n) { for (var i = [], o = 0; o < e.length; o++) { var r = e[o], + s = a.overlaps(r.bounds, t); + (s && !n || !s && n) && i.push(r) } return i }, i.point = function(e, t) { for (var n = [], i = 0; i < e.length; i++) { var o = e[i]; if (a.contains(o.bounds, t)) + for (var r = 1 === o.parts.length ? 0 : 1; r < o.parts.length; r++) { var s = o.parts[r]; if (a.contains(s.bounds, t) && l.contains(s.vertices, t)) { n.push(o); break } } } return n } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(4), + r = n(18), + a = n(0);! function() { var e, t, n; + ("undefined" != typeof window && (e = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame, t = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame), e) || (e = function(e) { n = setTimeout((function() { e(a.now()) }), 1e3 / 60) }, t = function() { clearTimeout(n) }); + i.create = function(e) { var t = a.extend({ fps: 60, correction: 1, deltaSampleSize: 60, counterTimestamp: 0, frameCounter: 0, deltaHistory: [], timePrev: null, timeScalePrev: 1, frameRequestId: null, isFixed: !1, enabled: !0 }, e); return t.delta = t.delta || 1e3 / t.fps, t.deltaMin = t.deltaMin || 1e3 / t.fps, t.deltaMax = t.deltaMax || 1e3 / (.5 * t.fps), t.fps = 1e3 / t.delta, t }, i.run = function(t, n) { return void 0 !== t.positionIterations && (n = t, t = i.create()), + function o(r) { t.frameRequestId = e(o), r && t.enabled && i.tick(t, n, r) }(), t }, i.tick = function(e, t, n) { var i, a = t.timing, + s = 1, + l = { timestamp: a.timestamp }; + o.trigger(e, "beforeTick", l), e.isFixed ? i = e.delta : (i = n - e.timePrev || e.delta, e.timePrev = n, e.deltaHistory.push(i), e.deltaHistory = e.deltaHistory.slice(-e.deltaSampleSize), s = (i = (i = (i = Math.min.apply(null, e.deltaHistory)) < e.deltaMin ? e.deltaMin : i) > e.deltaMax ? e.deltaMax : i) / e.delta, e.delta = i), 0 !== e.timeScalePrev && (s *= a.timeScale / e.timeScalePrev), 0 === a.timeScale && (s = 0), e.timeScalePrev = a.timeScale, e.correction = s, e.frameCounter += 1, n - e.counterTimestamp >= 1e3 && (e.fps = e.frameCounter * ((n - e.counterTimestamp) / 1e3), e.counterTimestamp = n, e.frameCounter = 0), o.trigger(e, "tick", l), o.trigger(e, "beforeUpdate", l), r.update(t, i, s), o.trigger(e, "afterUpdate", l), o.trigger(e, "afterTick", l) }, i.stop = function(e) { t(e.frameRequestId) }, i.start = function(e, t) { i.run(e, t) } }() }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(8), + r = n(0).deprecated; + i.collides = function(e, t) { return o.collides(e, t) }, r(i, "collides", "SAT.collides ➤ replaced by Collision.collides") }, function(e, t, n) { var i = {}; + e.exports = i; + n(1); var o = n(0); + i.pathToVertices = function(e, t) { "undefined" == typeof window || "SVGPathSeg" in window || o.warn("Svg.pathToVertices: SVGPathSeg not defined, a polyfill is required."); var n, r, a, s, l, c, u, d, p, f, v, y = [], + m = 0, + g = 0, + x = 0; + t = t || 15; var h = function(e, t, n) { var i = n % 2 == 1 && n > 1; if (!p || e != p.x || t != p.y) { p && i ? (f = p.x, v = p.y) : (f = 0, v = 0); var o = { x: f + e, y: v + t };!i && p || (p = o), y.push(o), g = f + e, x = v + t } }, + b = function(e) { var t = e.pathSegTypeAsLetter.toUpperCase(); if ("Z" !== t) { switch (t) { + case "M": + case "L": + case "T": + case "C": + case "S": + case "Q": + g = e.x, x = e.y; break; + case "H": + g = e.x; break; + case "V": + x = e.y } h(g, x, e.pathSegType) } }; for (i._svgPathToAbsolute(e), a = e.getTotalLength(), c = [], n = 0; n < e.pathSegList.numberOfItems; n += 1) c.push(e.pathSegList.getItem(n)); for (u = c.concat(); m < a;) { if ((l = c[e.getPathSegAtLength(m)]) != d) { for (; u.length && u[0] != l;) b(u.shift()); + d = l } switch (l.pathSegTypeAsLetter.toUpperCase()) { + case "C": + case "T": + case "S": + case "Q": + case "A": + s = e.getPointAtLength(m), h(s.x, s.y, 0) } m += t } for (n = 0, r = u.length; n < r; ++n) b(u[n]); return y }, i._svgPathToAbsolute = function(e) { for (var t, n, i, o, r, a, s = e.pathSegList, l = 0, c = 0, u = s.numberOfItems, d = 0; d < u; ++d) { var p = s.getItem(d), + f = p.pathSegTypeAsLetter; if (/[MLHVCSQTA]/.test(f)) "x" in p && (l = p.x), "y" in p && (c = p.y); + else switch ("x1" in p && (i = l + p.x1), "x2" in p && (r = l + p.x2), "y1" in p && (o = c + p.y1), "y2" in p && (a = c + p.y2), "x" in p && (l += p.x), "y" in p && (c += p.y), f) { + case "m": + s.replaceItem(e.createSVGPathSegMovetoAbs(l, c), d); break; + case "l": + s.replaceItem(e.createSVGPathSegLinetoAbs(l, c), d); break; + case "h": + s.replaceItem(e.createSVGPathSegLinetoHorizontalAbs(l), d); break; + case "v": + s.replaceItem(e.createSVGPathSegLinetoVerticalAbs(c), d); break; + case "c": + s.replaceItem(e.createSVGPathSegCurvetoCubicAbs(l, c, i, o, r, a), d); break; + case "s": + s.replaceItem(e.createSVGPathSegCurvetoCubicSmoothAbs(l, c, r, a), d); break; + case "q": + s.replaceItem(e.createSVGPathSegCurvetoQuadraticAbs(l, c, i, o), d); break; + case "t": + s.replaceItem(e.createSVGPathSegCurvetoQuadraticSmoothAbs(l, c), d); break; + case "a": + s.replaceItem(e.createSVGPathSegArcAbs(l, c, p.r1, p.r2, p.angle, p.largeArcFlag, p.sweepFlag), d); break; + case "z": + case "Z": + l = t, c = n } + "M" != f && "m" != f || (t = l, n = c) } } }, function(e, t, n) { var i = {}; + e.exports = i; var o = n(5); + n(0); + i.create = o.create, i.add = o.add, i.remove = o.remove, i.clear = o.clear, i.addComposite = o.addComposite, i.addBody = o.addBody, i.addConstraint = o.addConstraint }]) })); \ No newline at end of file diff --git a/ngon/style.css b/ngon/style.css new file mode 100644 index 00000000..3971c413 --- /dev/null +++ b/ngon/style.css @@ -0,0 +1,1379 @@ +:root { + --build-bg-color: #aeb6c2; + --card-color: #fafcfd; + --hover-card-color: #e5e5ed; +} + +body { + font-family: "Helvetica", "Arial", sans-serif; + margin: 0; + overflow: hidden; + cursor: auto; + /* transition: background-color 0.2s ease-in-out; */ +} + +canvas { + position: absolute; + top: 0; + left: 0; + z-index: 0; + user-select: none; +} + +select { + font-size: 0.8em; + border: 1px #333 solid; + border-radius: 6px; +} + +select option { + background-color: #fff; +} + +input { + padding: 0px 4px; + font-size: 0.8em; + border: 1px #333 solid; + border-radius: 4px; +} + +a { + text-decoration: none; + color: #08c; +} + +em { + opacity: 0.7; +} + +#splash { + user-select: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 2; + transition: opacity 5s ease-in; +} + +*:focus { + outline: none; +} + +table { + border-collapse: collapse; + width: 360px; +} + +tr { + padding: 10px; + text-align: left; +} + +td { + padding-left: 10px; +} + +.key-input-train { + padding: 0px 5px; + border: 2px solid #444; + color: #444; + border-radius: 4px; + background-color: #d0d0d0; + text-align: center; + font-weight: 400; +} + +.key-input { + padding: 3px 8px; + border: 2px solid #333; + background-color: #fff; + text-align: center; +} + +.pause-table { + width: auto; +} + +.key-input-pause { + width: 15px; + padding: 0px 2px; + border: 2px solid #333; + background-color: #fff; + text-align: center; +} + +.key-used { + color: #777; + font-size: 80%; +} + +summary { + font-size: 1.2em; +} + +.SVG-button { + border: 1.5px #333 solid; + border-radius: 8px; + background-color: #fff; +} + +.SVG-button:hover { + background-color: #eee; +} + +#experiment-button { + position: absolute; + bottom: 4px; + right: 4px; + z-index: 12; + transition: opacity 5s ease-in; +} + +#training-button { + position: absolute; + top: 4px; + right: 4px; + z-index: 12; + transition: opacity 5s ease-in; +} + +#construct { + display: none; + position: absolute; + bottom: 0%; + right: 0%; + z-index: 1; + width: 250px; + height: 200px; + background-color: #fff; + color: #000; + font-size: 0.9em; + white-space: pre; + padding: 3px; + border: 1px #333 solid; +} + +#flex-center { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +.choose-grid { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 10px 1px; + margin: 0px; + border: 0px; + z-index: 12; + max-height: 99vh; + font-size: 1.3em; + display: grid; + grid-template-columns: repeat(3, 384px); + align-items: stretch; + visibility: hidden; + opacity: 0; + transition: opacity 0.25s linear; + overflow: auto; + -ms-overflow-style: none; + scrollbar-width: none; +} + + + + +.choose-grid-no-images { + border-radius: 8px; + border: 10px solid #444; + gap: 10px; + background-color: #444; + /* padding: 10px 1px; */ + + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + margin: 0px; + z-index: 12; + max-height: 99vh; + font-size: 1.3em; + display: grid; + grid-template-columns: 384px; + align-items: stretch; + visibility: hidden; + opacity: 0; + transition: opacity 0.25s linear; + overflow: auto; + -ms-overflow-style: none; + scrollbar-width: none; +} + + +#choose-grid::-webkit-scrollbar { + display: none; +} + +.choose-grid-module { + line-height: 160%; + background-color: var(--card-color); + font-size: 0.75em; + /* transform-style: preserve-3d; + transition: transform 2s; + transform: rotateX(180deg); */ +} + +.choose-grid-module:hover { + background-color: var(--hover-card-color); +} + +.choose-grid-module:hover .card-text { + background-color: var(--hover-card-color); +} + +.research-select { + float: right; + margin-right: 10px; +} + +.pause-grid { + padding: 2px; + margin: 0px; + display: none; + grid-template-columns: 384px; + grid-auto-rows: minmax(auto, auto); + align-content: start; + align-items: start; + z-index: 2; + font-size: 1.3em; +} + +#pause-grid-right { + position: absolute; + top: -2px; + right: -4px; + overflow: auto; + max-height: 100vh; + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ + padding: 1px; +} + +#pause-grid-right::-webkit-scrollbar { + display: none; +} + +#pause-grid-left { + position: absolute; + top: -2px; + left: -4px; + overflow: auto; + max-height: 100vh; + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ +} + +.pause-console { + padding: 10px; + margin: 5px; + border-radius: 10px; +} + +#pause-grid-left::-webkit-scrollbar { + display: none; +} + +.pause-grid-module { + line-height: 160%; + background-color: var(--card-color); + font-size: 0.75em; +} + +.sort { + padding: 0.1em 0.3em; + border: 1px solid #444; + /* min-height: 88px; */ + line-height: 100%; + background-color: #fff; +} + +.sort-button { + border: 1px #333 solid; + border-radius: 0.5em; + background-color: #fff; + font-size: 0.5em; + /* padding: 0.3em; */ + +} + +.sort-button:hover { + background-color: #eee; +} + +.pause-eject .card-text { + animation: techColorCycle 1s linear infinite alternate; +} + +#experiment-grid { + display: flex; + justify-content: center; + padding: 0px; + margin: 0px; + border: 0px; + background-color: var(--build-bg-color); + display: none; + grid-template-columns: repeat(auto-fit, 384px); + grid-auto-flow: row; + /* grid-auto-rows: minmax(auto, auto); */ + position: relative; + /* bottom: 0px; */ + z-index: 10; + font-size: 1.3em; + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ +} + +#experiment-grid::-webkit-scrollbar { + display: none; +} + +.experiment-grid-module { + line-height: 160%; + background-color: var(--card-color); + font-size: 0.75em; +} + +.experiment-grid-hide { + display: none; +} + +.grid-title { + padding-bottom: 6px; + padding-top: 4px; + font-size: 1.4em; + font-weight: 600; +} + +.experiment-grid-module:hover:not(.build-tech-selected, .build-field-selected, .build-gun-selected) { + background-color: var(--card-color); +} + +.experiment-start-box { + background-color: var(--card-color); + /* font-size: 1em; */ + position: sticky; + top: 0; + z-index: 10; + align-self: start; + width: 276px; + line-height: 200%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + border: 2px #333 solid; + border-top: 0px; + border-radius: 10px; + border-top-left-radius: 0px; + /* border-bottom-left-radius: 0px; */ + border-top-right-radius: 0px; + padding: 0.2em 0px; + /* height: 210px; */ + box-shadow: 8px 8px 7px rgba(0, 0, 50, 0.15); +} + +.card-background { + height: 340px; + background-size: contain; + background-repeat: no-repeat; + display: flex; + justify-content: flex-end; + flex-direction: column; +} + +.card-text { + background-color: rgba(255, 255, 255, 1); + padding: 0.5em 1em; + border: 1px solid #444; + margin-top: -1px; + margin-right: -1px; + font-size: 0.92em; + min-height: 88px; + /* border-radius: 5px; */ +} + +.cancel-card { + text-align: center; + font-size: 1.1em; + font-weight: 100; + justify-self: end; + border: 1px solid #444; + margin-top: -1px; + margin-right: -1px; + padding: 1px; + line-height: 160%; + background-color: var(--card-color); +} + +.cancel-card:hover { + background-color: var(--hover-card-color); +} + +.research-card { + font-size: 1.1em; + font-weight: 100; + border: 1px solid #444; + margin-top: -1px; + margin-right: -1px; + padding: 1px 0px 1px 10px; + line-height: 160%; + background-color: var(--card-color); +} + +.research-card:hover { + background-color: var(--hover-card-color); +} + +.research-cancel { + display: flex; + gap: 10px; + line-height: 160%; + /* background-color: var(--card-color); */ + font-size: 1em; +} + +/* keeps 5 columns at 1440px */ +@media (min-width: 1710px) and (max-width: 1950px) { + + .experiment-grid-module, + .choose-grid-module, + .pause-grid-module, + .research-cancel { + line-height: 143%; + font-size: 0.68em; + } + + .research-cancel { + font-size: 0.9em; + } + + #experiment-grid, + .choose-grid, + .pause-grid { + grid-template-columns: repeat(auto-fit, 340px); + } + + .card-background { + height: 290px; + } + + .card-text { + min-height: 75px; + } +} + +/* (min-width: 30em) and (max-width: 80em) */ +@media (max-width: 1709px) { + + .experiment-grid-module, + .choose-grid-module, + .pause-grid-module { + line-height: 139%; + font-size: 0.58em; + } + + .research-cancel { + font-size: 0.8em; + } + + #experiment-grid, + .choose-grid, + .pause-grid { + grid-template-columns: repeat(auto-fit, 285px); + } + + .card-background { + height: 240px; + } + + .card-text { + min-height: 64px; + } +} + +/* .card-text */ +.choose-grid-no-images .card-text, +.choose-grid-no-images .research-card, +.choose-grid-no-images .cancel-card { + border-radius: 5px; +} + +/* keeps 4 columns at 1440px */ +/* @media (1500px <= width < 1950px) { + .experiment-grid-module, .choose-grid-module, .pause-grid-module { + line-height: 150%; + font-size: 0.72em; + } + #experiment-grid, #choose-grid, .pause-grid{ + grid-template-columns: repeat(auto-fit, 360px); + } + .card-background{ + height:315px; + } + .card-text { + min-height: 82px; + } +} +@media (width < 1500px) { + .experiment-grid-module, .choose-grid-module, .pause-grid-module { + line-height: 143%; + font-size: 0.68em; + } + #experiment-grid, #choose-grid, .pause-grid{ + grid-template-columns: repeat(auto-fit, 340px); + } + .card-background{ + height:290px; + } + .card-text { + min-height: 75px; + } +} */ + +.experiment-grid-module:hover .card-text { + background-color: var(--hover-card-color); +} + +.build-tech-selected .card-text { + background-color: hsl(253, 100%, 84%); +} + +.build-tech-selected.experiment-grid-module:hover .card-text { + background-color: hsl(253, 100%, 81%); +} + +.build-gun-selected .card-text { + background-color: hsl(218, 100%, 81%); +} + +.build-gun-selected.experiment-grid-module:hover .card-text { + background-color: hsl(218, 100%, 76%); +} + +.build-field-selected .card-text { + background-color: hsl(193, 100%, 75%); +} + +.build-field-selected.experiment-grid-module:hover .card-text { + background-color: hsl(193, 100%, 68%); +} + +.experiment-grid-disabled { + /* background-color: var(--build-bg-color); */ + /* color: #000; */ + opacity: 0.35; +} + +.experiment-grid-disabled[data-descr] { + position: relative; +} + +.experiment-grid-disabled[data-descr]:hover::after { + content: '\a \00a0 \00a0 \00a0 REQUIRES:\a \00a0 \00a0 \00a0 ' attr(data-descr); + white-space: pre-wrap; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: var(--build-bg-color); + z-index: 1; + opacity: 1; + transition: opacity 0.5s ease; +} + +#info { + position: absolute; + bottom: 3px; + left: 3px; + z-index: 12; + font-size: 1.5em; + transition: opacity 5s ease-in; +} + +.details-div { + padding: 10px; + border-radius: 8px; + border: 2px #333 solid; + background-color: #fff; +} + +#dmg { + position: absolute; + z-index: 2; + width: 100%; + height: 100%; + display: none; + /* background-color also set in mass-energy tech */ + background-color: #f67; + opacity: 0; + transition: opacity 1s; +} + +#damage-bar { + position: absolute; + /* top: 1px; + left: 15px; + height: 7px; + width: 0px; + transition: width 0.3s linear; */ + top: 16px; + right: 4px; + height: 0px; + width: 7px; + transition: height 0.25s linear; + opacity: 1; + z-index: 2; + pointer-events: none; + background-color: rgb(255, 55, 95); + /* border-bottom: 1.5px solid #701; */ + /* border-right: 1.5px solid #701; */ + display: none; +} + +#defense-bar { + position: absolute; + top: 8px; + left: 15px; + height: 7px; + width: 0px; + transition: width 0.25s linear; + opacity: 1; + z-index: 2; + pointer-events: none; + background-color: #fff; + border-right: 1.5px solid #777; + display: none; +} + +#health { + position: absolute; + top: 15px; + left: 15px; + height: 20px; + width: 0px; + transition: width 1s ease-out; + z-index: 2; + pointer-events: none; + background-color: rgb(9, 245, 166); + border-right: 2px solid rgb(51, 162, 125); + display: none; +} + +#health-bg { + position: absolute; + top: 15px; + left: 15px; + height: 20px; + width: 0px; + transition: width 1s ease-out; + background-color: #032; + opacity: 0.1; + z-index: 1; + pointer-events: none; + display: none; +} + +/* #damage-bar { + position: absolute; + top: 0px; + left: 15px; + height: 6px; + width: 0px; + transition: width 0.3s linear; + opacity: 1; + z-index: 2; + pointer-events: none; + background-color: #f03; + border: 1px solid #701; + display: none; +} + +#defense-bar { + position: absolute; + top: 9px; + left: 15px; + height: 5px; + width: 0px; + transition: width 0.3s linear; + opacity: 1; + z-index: 2; + pointer-events: none; + background-color: #fff; + border: 1px solid #333; + display: none; +} +#health { + position: absolute; + top: 16px; + left: 15px; + height: 20px; + width: 0px; + transition: width 1s ease-out; z-index: 2; + pointer-events: none; + background-color: #0fa; + border: 2px solid rgb(51, 162, 125); + border-radius: 8px; + display: none; +} +#health-bg { + position: absolute; + top: 18px; + left: 15px; + height: 20px; + width: 0px; + transition: width 1s ease-out; + background-color: #000; + opacity: 0.1; + z-index: 1; + pointer-events: none; + border-radius: 8px; + display: none; +} */ + +/* .low-health { + animation: blink 250ms infinite alternate; +} + +@keyframes blink { + from { + opacity: 1; + } + + to { + opacity: 0.6; + } +} */ + +/* background on title page */ +#fade-out { + position: absolute; + z-index: 2; + width: 100%; + height: 100%; + background-color: #e2e9ec; + opacity: 1; + transition: opacity 3s; + pointer-events: none; +} + +#guns { + position: absolute; + top: 82px; + left: 15px; + z-index: 2; + font-size: 23px; + color: #222; + /* background-color: rgba(255, 255, 255, 0.4); */ + background-color: rgba(255, 255, 255, 0.2); + line-height: 120%; + user-select: none; + pointer-events: none; + padding: 0px 5px 0px 5px; + border-radius: 5px; + /*border: 2px solid rgba(0, 0, 0, 0.4);*/ +} + +#field { + position: absolute; + top: 45px; + left: 15px; + z-index: 2; + font-size: 23px; + color: #000; + text-align: right; + opacity: 0.7; + /* line-height: 140%; */ + background-color: rgba(190, 210, 245, 0.25); + user-select: none; + pointer-events: none; + padding: 0px 5px 0px 5px; + border-radius: 5px; + /*border: 2px solid rgba(0, 0, 0, 0.4);*/ +} + +#tech { + position: absolute; + top: 15px; + right: 15px; + z-index: 2; + font-size: 20px; + color: #222; + text-align: right; + opacity: 0.35; + line-height: 120%; + background-color: rgba(255, 255, 255, 0.4); + user-select: none; + pointer-events: none; + padding: 0px 5px 0px 5px; + border-radius: 5px; + /*border: 2px solid rgba(0, 0, 0, 0.4);*/ +} + +#text-log { + z-index: 2; + position: absolute; + bottom: 10px; + left: 10px; + padding: 10px; + border-radius: 4px; + line-height: 140%; + font-size: 1.15em; + color: #555; + background-color: rgba(255, 255, 255, 0.5); + /* transition: opacity 0.15s; */ + pointer-events: none; + user-select: none; +} + +/* color for in game console output */ +/* .ammo-flash { + color: #f33; + transition: color 2s; +} */ + + +.color-text { + color: #000; +} + +.color-var { + color: hsl(253, 100%, 58%); +} + +.color-symbol { + color: hsl(290, 100%, 40%); +} + +.color-gun { + color: hsl(218, 100%, 58%); +} + +.highlight { + border-radius: 6px; + background-color: #ff0; + padding: 3px; +} + +.color-f { + color: #0ad; + letter-spacing: 1px; +} + +.color-s { + color: #04f; + letter-spacing: 1px; +} + +.color-d { + color: #f03; + letter-spacing: 1px; +} + +.color-p { + color: #067; + letter-spacing: 1px; +} + +.color-h { + color: #0b7; + letter-spacing: 1px; +} + +.color-e { + color: #d60; + letter-spacing: 1px; +} + +.color-m { + color: hsl(253, 100%, 50%); + letter-spacing: 1px; +} + +.color-g { + color: hsl(218, 80%, 40%); + letter-spacing: 1px; +} + +.color-ammo { + color: #356; +} + +.color-dup { + font-variant: small-caps; + letter-spacing: 1px; + text-shadow: 1.5px -1.5px hsla(243, 100%, 38%, 0.2); +} + +.color-cloaked { + letter-spacing: 2px; + animation: cloak 6s linear infinite alternate; + color: rgba(0, 0, 0, 0); +} + +@keyframes cloak { + 0% { + text-shadow: 0px 0px 0px #000; + opacity: 1; + } + + 50% { + text-shadow: 0px 0px 0px #000; + opacity: 1; + } + + 100% { + text-shadow: 0px 0px 15px #000; + opacity: 0; + } +} + +.color-laser { + color: #f02; + font-weight: 100; + letter-spacing: -0.8px; +} + +.color-plasma { + color: #c0e; + letter-spacing: 1px; + background-color: rgba(132, 0, 255, 0.06); + padding: 2px; + border-radius: 9px; + letter-spacing: 1px; +} + +.color-worm { + color: #fff; + text-shadow: 1px 0px 2px #234; +} + +.color-defense { + background-color: hsla(227, 9%, 71%, 0.279); + padding: 2px; + border-radius: 4px; + letter-spacing: 1px; + font-weight: 100; +} + +.color-block { + background-color: rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.5); + padding: 0.5px; + font-weight: 100; +} + +.color-cancel { + background-color: var(--card-color); + border: 0.15em #444 solid; + padding: 0.2em; + border-radius: 0.2em; + font-weight: 800; + font-size: 1em; +} + +.color-junk { + letter-spacing: 1px; + font-family: Lucida Console, Courier, monospace; +} + +.color-r { + color: #f7b; + letter-spacing: 1px; +} + +.color-flop { + text-decoration: underline; + font-weight: 100; + letter-spacing: -1px; +} + +.color-alt { + text-decoration: underline; + font-weight: 100; + letter-spacing: -1px; +} + +.faded { + opacity: 0.7; + font-size: 90%; +} + +.circle-grid { + width: 1.35em; + height: 1.35em; + border-radius: 50%; + display: inline-block; + margin-bottom: -0.3em; +} + +.circle-grid-skin { + width: 1.25em; + height: 1.25em; + border-radius: 50%; + display: inline-block; + background-color: #fff; + opacity: 0.8; + border: 0.08em solid #222; + position: absolute; + top: -0.05em; + left: 0em; + margin-bottom: -0.3em; +} + +.circle-grid-skin-eye { + width: 0.18em; + height: 0.18em; + border-radius: 50%; + display: inline-block; + background-color: #fff; + border: 0.08em solid #222; + position: absolute; + top: 0.45em; + left: 0.9em; + margin-bottom: -0.3em; +} + +.junk { + background-color: hsl(254, 44%, 75%); + border-radius: 25%; +} + +.research-circle { + width: 0.9em; + height: 0.9em; + border-radius: 50%; + display: inline-block; + background-color: #f7b; + border: 0.065em #fff solid; + opacity: 0.85; + margin-bottom: -0.1em; +} + +.ammo-circle { + width: 0.75em; + height: 0.75em; + border-radius: 50%; + display: inline-block; + background-color: #467; + border: 0.05em #fff solid; + opacity: 0.95; + margin-bottom: -1.5px; +} + +.heal-circle { + width: 0.95em; + height: 0.95em; + border-radius: 50%; + display: inline-block; + background-color: #0d9; + border: 0.05em #fff solid; + opacity: 0.85; + margin-bottom: -3px; +} + +.heal-circle-energy { + width: 0.95em; + height: 0.95em; + border-radius: 50%; + display: inline-block; + background-color: #ff0; + border: 0.05em #000 solid; + opacity: 0.85; + margin-bottom: -3px; +} + +.coupling-circle { + width: 0.7em; + height: 0.7em; + border-radius: 50%; + display: inline-block; + background-color: #0ae; + border: 0.05em #fff solid; + margin-bottom: -0.5px; +} + +.boost-circle { + width: 0.7em; + height: 0.7em; + border-radius: 50%; + display: inline-block; + background-color: #f03; + border: 0.05em #fff solid; + opacity: 0.9; + margin-bottom: -0.5px; +} + +@keyframes pulse { + 0% { + border-radius: 0%; + } + + 100% { + border-radius: 50%; + } +} + +.field { + background-color: #0cf; +} + +.tech { + background-color: hsl(255, 100%, 71%); +} + +.gun { + background-color: rgb(0, 80, 218); +} + +.heal { + background-color: #0d9; +} + +.research { + background-color: #f7b; +} + +.alt { + animation: alt 8s linear infinite alternate; + font-weight: 400; + letter-spacing: 1px; +} + +@keyframes alt { + 0% { + text-shadow: 3px 0px 0px; + } + + 100% { + text-shadow: -3px 0px 0px; + } + +} + +.flicker { + animation: flicker 4s linear infinite; +} + +@keyframes flicker { + 0% { + opacity: 1; + } + + 80% { + opacity: 1; + } + + 90% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +.color-coupling { + text-shadow: 0px 0px 1.5px #0cf; + letter-spacing: 1px; + font-weight: 100; +} + +.box { + padding: 3px 8px 3px 8px; + border: 2px solid #444; + border-radius: 4px; + background-color: rgba(255, 255, 255, 0.5); +} + +.wrapper { + display: grid; + grid-template-columns: 360px 10px; + align-self: center; + justify-content: center; +} + +.grid-box { + align-self: center; + justify-self: center; +} + +.right { + text-align: right; +} + +.lore { + animation: bgColor 3.5s linear infinite; +} + +.lore-text { + animation: textColor 3s linear infinite; +} + +.entanglement { + font-size: 1.1em; + font-weight: 100; + text-align: center; + letter-spacing: 10px; + border: none; + background-color: var(--card-color); +} + +.flipX { + animation: 14s anim-flipX linear infinite; +} + +@keyframes anim-flipX { + 0% { + transform: rotateX(0.5turn); + } + + 50% { + transform: rotateX(1turn); + } + + 100% { + transform: rotateX(0.5turn); + } +} + +@keyframes fieldColorCycle { + 0% { + background-color: rgb(255, 255, 255) + } + + 100% { + background-color: rgb(200, 255, 255) + } +} + +@keyframes techColorCycle { + 0% { + background-color: hsl(253, 100%, 100%) + } + + 100% { + background-color: hsl(253, 100%, 90%) + } +} + +@keyframes bgColor { + 0% { + background-color: rgb(63, 218, 216) + } + + 10% { + background-color: rgb(47, 201, 226) + } + + 20% { + background-color: rgb(28, 127, 238) + } + + 30% { + background-color: rgb(95, 21, 242) + } + + 40% { + background-color: rgb(186, 12, 248) + } + + 50% { + background-color: rgb(251, 7, 217) + } + + 60% { + background-color: rgba(255, 0, 0) + } + + 70% { + background-color: rgb(255, 0, 0) + } + + 80% { + background-color: rgb(255, 154, 0) + } + + 90% { + background-color: rgb(208, 222, 33) + } + + 100% { + background-color: rgb(79, 220, 74) + } +} + +@keyframes textColor { + 0% { + color: rgb(63, 218, 216) + } + + 10% { + color: rgb(47, 201, 226) + } + + 20% { + color: rgb(28, 127, 238) + } + + 30% { + color: rgb(95, 21, 242) + } + + 40% { + color: rgb(186, 12, 248) + } + + 50% { + color: rgb(251, 7, 217) + } + + 60% { + color: rgba(255, 0, 0) + } + + 70% { + color: rgb(255, 0, 0) + } + + 80% { + color: rgb(255, 154, 0) + } + + 90% { + color: rgb(208, 222, 33) + } + + 100% { + color: rgb(79, 220, 74) + } +} + +.link { + color: #000; +} + +.link:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/ngon/todo.txt b/ngon/todo.txt new file mode 100644 index 00000000..19f18e7f --- /dev/null +++ b/ngon/todo.txt @@ -0,0 +1,1183 @@ +******************************************************** NEXT PATCH ************************************************** + +on later levels spawn 2 bosses, but 1 power up each + starts at simulation.difficulty > 23 //on hard mode level 6, level 12 on easy, level 4 on why? + makes combat harder, but also means that you will always get at least 2 drops per level + you used to have a chance to only get 1 near the end of the game + I'm guessing this will lead to about 3 more total tech by the final boss + abiogenesis - removed + parthenogenesis - doesn't duplicate bosses anymore, +8% duplication + + +*********************************************************** TODO ***************************************************** + +more (all) bosses need to be made of parts + good examples: spiderBoss, dragonFlyBoss, beetleBoss + methods: + spawn adds in phases, like beetle and tether + constraints, like spider + be nice if each constrained part does something different + constrained mobs that regen after phases + flock, like cellBoss, blockBoss + Bosses that could be converted to more parts + "orbitalBoss", "shooterBoss", "bomberBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "pulsarBoss", "grenadierBoss", "blinkBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "timeSkipBoss", "sneakBoss" + "sneakBoss" could get sneaker adds after each hide phase + "laserTargetingBoss" could have close range stabbers constrained, a few long range shooters, and a few laser shooters + +defense power up - a short term defense boost, like the ones for damage. + Or maybe it would last until you take one hit. + Or last until you lost a total of 20 health. + +use cross product rotation for other mobs? + snipers, shooters? + //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise + const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) + const playerVector = Vector.sub(this.position, m.pos) + const cross = Matter.Vector.cross(laserStartVector, playerVector) + this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) + +super-bot: fires super balls + +tech - only allow 1,2 turrets at time. spawning a new mine removes the oldest mine + turrets never run out of ammo + or turrets automatically use one of your mine ammos when they run out? + good with multi gun builds + conflict with booby trap? + +tech: after a needle hits a mobs + the needle splits into 3 needles? + reset your fire CD? + 2x damage for each consecutive mob hit? + +mob non-combat behaviors, like Rain World + gathering + blocks + eating blocks to heal? + power ups + eating power ups and ejecting them on death + flocking + grouping near mobs + keeping distance from mobs + sharing velocity with other mobs: boids + wander + random walk with a bias towards the player + play + fight other mobs + +consider increasing the base player horizontal movement + maybe only increase ground movement, air control seems fine + would this unbalance any maps? + +tech stubs should be a tech unlocked by skins + nitinol, tungsten? + maybe give another benefit? + defense? + make a shared variable for skin defense, since you can only have one skin + +make a lemming that walks until it hits a wall and then turns around robotically + body or mob? + can't be killed? + +use ephemera to replace some bad code + JUNK? + request animation stuff + simulation checks + damage and defense bars + disable and enable ephemera with settings + +perfect diamagnatism - invulnerable while field is active? + also drain energy while field is active? + +mobs attack mines + mines periodically set all mobs to have player location to be the mine + is this going to work with all mob vision types? + +tech circular polarization - wave gun bullets move in a circle + +Tech: relativity + Simulation speed scales with movement speed. When still, time moves at 0.4 speed, at full walking speed it’s 1. (So if you’re falling or something and you move faster the simulation will be faster than usual) + Also a damage and/or defense boost to make it worth using + +wormhole tech - teleport away mobs with mass below 3 when they get too near the player + short CD, small energy cost, only mobs below a mass + +extend brainstorming animation timers to fps cap? + will it be smoother or choppier? + anything else needs to hit limited fps on a high fps monitor? + +perfect diamagnatism could bounce on mobs, or even map elements? + could work like a rocket jump? + +tech: Bose Einstein condensate - freezes enemies in pilot wave, and drains some energy? + +make super balls with Zectron deflectable with field + but is there a simple way to do this? + +set mob health bar colors based on status effects? + make mob damage immunity a mob status effect? + +tech: rail gun area damage effect, but for all harpoon mode + +mob status effect - vulnerability + mobs take 4x damage for __ time + afterwards mobs go back to normal damage taken + graphical effect while they take extra damage + needs to come from a "rare"ish effect + merge with the decloaking damage effect + decloaking gives all mobs (nearby??) a 3x damage taken status? + this can be merged with the stun crit damage tech + also the increased damage every hit tech? + bad idea because the graphical effect will be too annoying + +tech: sporangium that grow little trees + the trees have an area of effect damage for about 6-10 seconds + maybe something similar to radioactive drones, but maybe a few smaller shapes + +hookBoss fires a hook that pulls player towards it + hook does a bit of damage + player targeted unless cloaking + also add effect to finalBoss + +finalBoss + add synergies between modes: + new modes: + something that needs to be killed quickly + if you don't kill it boss gets a shield + rotating quadrant immunity shield, can't take damage from that quadrant + maybe also attack player near that quadrant + but how to tell the angle of incoming damage + maybe a physics body like the shield but it only covers 1/3 of mob? + falling object warps to ceiling after hitting floor + doesn't end, player needs to kill it + slowly grows? + slow effect zones + random placement or place over player or both! + draw white dot and an outline of area of effect + expanding circle stroke, freeze effect triggers when stroke circle hits fill circle + after 1-2 seconds freeze player if in the zone + also freeze mobs + effect that makes player have to be close to boss + hook that tries to yank the player into hitting finalBoss + does damage + pulls player into center + counter with wormhole, negative mass + player targeted unless cloaking + + +mob status effect - emit - mobs fire lasers for a few seconds + tech: phosphorescence - mobs emit after being hit with laser beams + +Tech: "Solid rocket motor": Missiles would start at 300% speed and 200% missile damage upon explosion, but the speed and damage would decrease to 40% speed and damage as time goes on +tech: every time shotgun fires it's a different shotgun mode: nails, ice, needles, worms, fleas, rivets, ... but you get more ammo + +for tech power ups no tech options are displayed until you research once + or display only JUNK until you research once + increase the number of options after each research + +When receiving damage, in addition to becoming invulnerable to attacks, also become intangible for the set period of time + +tech increase max energy and energy to 5000, but you can no longer regen energy through any process + +it would be nice if there was incentive to go slow when choosing tech so n-gon is more relaxing + add some css based visual effects for opening up a tech,gun,field + +make a new coupling effect for perfect diamagnetism or standing wave + +make a faster smaller version of cell boss that also has map collisions + +laserMines need a copy of laser-bot method + this is a very rare bug, so not a priority + +JUNK tech description that changes similar to cards in inscription + that changes based on mouse position + can you tell if mouse is over card? + +tech - buff MACHO range, effect, move speed? +while you are inside MACHO it will damage mobs? + +PWA? + https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps + https://codeburst.io/how-to-easily-turn-your-static-website-to-a-progressive-web-app-pwa-b0af08da9693 + https://github.com/landgreen/n-gon/pull/32/files + +bug blocks and power ups falling through map + always foam gun (4-5 times) + might be about tech pressure vessel + happens rarely, doesn't repeat + only occurs for 3 people so far + normally after level 6 + occurred once on the first level, didn't fire gun and was able to walk through a body + +Tech:when relay switch/flip flop is on, turn ammo powerups into boosts, when relay swicth/flip flop is off, ammo powerups remain ammo powerups + or toggle other power ups + health/ammo + +JUNK: what the golf? + trying to throw a block throws you instead + +tech for lens - you can only fire through the lens + and some buff? damage or energy? + +complete blowSuckBoss... or don't + +tech: laser reflections increase damage + +JUNK tech different effects based on night or day + use system time + +Boss that shoots out a ring of bullets, then after a few seconds it gravitates the bullets back + +coupling + put coupling description as 4th line on field description + raw text no function call + no need for coupling description in power ups, pause + negative coupling? + wouldn't work for iceIX +coupling tech + names: strongly coupled, Vibronic coupling, NMR coupling + tech: +x% field coupling, your field changes randomly every y seconds + tech: coupling starts at 200%, but decays when the field is in use, coupling recharges when the field is not in use + some fields aren't used much (that's ok?) + +tech give laser mines more lasers (3->4? 5?) + +rewindBoss: after hitting 1/5 damage thresholds the boss rewinds back in time to where it was a few seconds ago + track it's data like player history + +make orbitals increase orbital rotation speed after Orbital boss takes damage + +worms can target player, buff their damage + can't target player in first few seconds? + +plasma field tech - similar to regression, but for plasma ticks + +greatly increase walking speed + not mid air control? + for time dilation field + make jumping normal + +junk tech: charged shot +immediately fire all of your ammo + +after taking damage explode while invulnerable + scale explosion radius with damage + +quantum immortality: send you to a new tab after you die with a random load out + basically everything is the same as it is now, but you switch tabs + +Tech: Tech/guns/fields can no longer be duplicated. Duplication applies twice + +tech: get sent to a new tab that closes in 3 minutes + in the new tab you play reactor + if you die in reactor you die in game, if you win you get 2-3 tech in the original game? + give player equipment like many-worlds + count guns, field, tech and give random stuff on new tab + i-frame instead of tab? + +reduce the amount of research and nerf anti randomization tech + increase possible synergies that go nuts + +tech expansion: field coupling also expands each fields in different ways + how to make the description work + change description based on your current field? + perfect diamagnetism moves forward when you hold down the shield + it's great, but maybe annoying? + maybe only with crouch? + perfect diamagnetism just replace or increase Messier effect + time dilation drains 1/2 as much energy when paused + grow plasma torch as you hold it down + negative mass effects much more space + needs more benefit? + reduces the cloaking vision effect? + needs more benefit? + +tech: missiles explode a 2nd time after 1/2 a second (with a slightly different position determined by original velocity) + +The tech that makes blocks that fall into a wormhole give energy should scale with block size, with the same formula as tokomak + +junk suggestion: useless machine - ejects itself and removes itself from the item pool + + + +seed isn't working right from shared URL + +mob mechanics + bullets hit player and stay attached for 4-5 seconds, slowing player + hopperBullets? + black hole sucker effect on tail + vines + attached to map and grows, a series of spheres connected by vines + if node dies it's removed from tree + vine do + keeps track of connection tree + nested object with nesting to show connection + spawns new nodes + draws connections as quad lines + +tech: You can place an extra perfect diamagnatism field on the map + +standing wave no longer pushes mobs away, but it can do damage to mobs caught in area effect + Standing wave harmonics no longer deflects, but instead discharges excess energy as lightning toward nearby enemies +negative mass field does damage to mobs inside field + combine with standing wave effect? pilot wave? + +store value of last hit health lost + tech: killing the mob that caused the last hit spawns a heal power up equal to 1/2 of last hit + time limit for effect? + need to also store who hit player + +add small SVG pics to in-game console + when? + one for each field? + use stuff on physics notes: +simulation.makeTextLog(` + + + + + + + + + + +`); + +path-finding + build a path-finding map on level load + how to convert vertices into block grid? + check points for map collisions and build grid + is there a path-finding algorithm for vertices, instead of block grid + use for: + mobs, + drones? + bots that can go far from player and return + +make plasma ball power up and block pick up still work when you have no no energy + make a unique CD var for plasma ball? + +tech: entropic gravity - gain defense for each research + requires wormhole or pilot wave or negative mass field? + +mitosis: after needle,bullets or rivets hit a surface split off into two smaller less damaging versions that have their velocity reset -firerate + +after killing a mob go invulnerable for 1 second + critical hit with laser, harpoon, nails, needle, rivet + +spawn 2 bots after exiting 1 level + or spawn 3 after 2 levels? + (randomize 2-5 bots) + replace all bot tech with this? + +cloaking field + for 6 seconds after de-cloaking tell all active mobs that the player is in the wrong position? + how to code? + just delay setting the m.isCloak for a couple seconds + and also set all active bots to remember player in the de-cloaked stop + +scrap bots can't move? + only works for nail, foam, laser + might be tricky code? + make a new bot type called scrap bot? + +JUNK tech: Pacifism + You cannot attack mobs, mobs cannot attack you + over write the mob.damage and player.damage methods + +mob mechanic: beacon + periodically add locations to an array + teleport back to a location in the array + at random + if velocity not facing player? + +super balls do more damage after bouncing? + how to check for bounce? + maybe just increases damage after hitting a mob + +super short range foam that acts like flame thrower + high fire rate + short life spawn + start large? + extra ammo? + or only 1/2 of ammo is used? + +laser tech where bots move around and follow you while firing lasers in the direction you are looking + they fire when you fire? + they aim at player history aim location, with 1s delay + bots position spread out perpendicular to the direction you are looking + can they get stuck? + maybe absolution position, no collide + do they need to be physics objects then? + make a special bot type for this + beam is similar to diffuse beam + + + + +double research + +map ban option in settings + dropdown list with checkboxes by each map + +bullets that can target the player + occurs if no mobs targets around + worms? drones? missiles? spores? + all of the above? + +Currently, the mob just deals higher damage on impact, which is annoying although not hard to compete with nor unique +By "redesign" I mean replacing instances of the regular mob, since the same code is used for the tiny red projectiles (I think) just add a new mob and remove the old one from the rotation +The new mob should be as such, a "real" exploding mob: +Deals regular damage on impact, but breaks apart on touch into several red dots (like the ones thrown out by the going through walls boss) and a chance for also throwing a bomb or two (increases with difficulty) +If the mob is close to the player or heading into the player fast it will shatter as well (and the projectiles will inherit its speed) +By a formula such as +ShouldShatter(distToPlayer, speed) = distToPlayer * speed > threshold +Optionally (and a part I can do as I'm good at it and it doesn't revolve around a lot of functional code which you don't like other people doing): +Color changing based on the mob explosion status +Regular state: red +About to explode: animation to dark red +Exploding: several shockwaves from the explosion points and tiny trails given to the shrapnel for a second or two until they deaccelerate + +pause time like invariant for other things... + throwing blocks + charging railgun + charging anything? + +guntech fire a bullet that fires nail fragments after 1s in the same direction as the original bullet + like overwatch roadhog + +bring back: + the old phase decoherence field + make cloak only active on input.field down + could be a tech + would need some other buff + how to make it good enough + combine with not killing tech? + stun mobs that touch you while phased + firing doesn't exit you from cloak + but it does drain some energy + tech pilot wave: Bose Einstein condensate - freeze mobs in superposition with pilot wave + +tech: plasma drip + +plasma ball + graphics should look more like a real plasma ball + gently scale damage with circleRadius + balance corona discharge + delay on returning to player is annoying + scale float effect with ball size + tech upgrades + greatly improve floating effects while holding + black hole: gives the plasma ball gravity + stun on expansion + plasma orb increases in size and power as it eats enemies + while attached? + +flashlight effect + make level.do() graphic that only shows direction player is facing + pattern it after cloaking field, or timeSkipBoss + will it work with cloaking field at the same time + +double jump? + +tech: coyote time + you can still jump for 2 seconds after falling off a ledge + make it a JUNK tech? so you can keep the name + some other benefit + reduce your gravity also? + increase jump? + +tech smoke grenades - mobs inside a cloud can't see player + draw on the region so it's hard for player to see as well + you'd have to make something similar to MACHO that exists after an explosion goes off + maybe just keep it simple: + stun mobs for a long time, and draw a 99& alpha grey circle for the same time + don't worry about mobs seeing you inside the circle, the circle is kinda small so it doesn't matter + + +tech mines: mines fire _____ instead of nails + needles + super balls? + foam? + +tech: frozen mobs die at 10% life + +tech: harpoons stick into enemies + detonate after a short delay + attaches mob to wall if possible + firing while harpoon is stuck into an enemy rips it out of them, inflicting damage and stun and pulling them towards you + + + +enemies stuck with foam receive upward force over time + only form aerogel tech? + +Tech: superglue +Requires: foam + another gun or plasma torch or molecular assembler + Foam bubbles decay 50% slower + Enemies stuck with foam cannot move + Foam does 0 damage + Enemies stuck with foam take 25% more damage + should foam bots gets this also or is that too strong + +const ctx = canvas.getContext('2d', {‘willReadFrequently': true}); + +//deal with game crashes? +canvas.addEventListener("contextlost", onContextLost); +canvas.addEventListener("contextrestored", redraw); +ctx.reset(); + +a mine tech that makes mines you can stand on + works similar to the field block throwing system + but you don't need to find a block to throw + blocks explode after mobs touch them + or 3 seconds after you touch them + benefits from all block tech + +go non-collide with mobs when immune to damage? + +mobs that are invulnerable from the front + +vertical reversed version of reservoir level, start at top and press buttons to lower slime + mechanic: push a very large block into slime in order to stand on it and avoid slime + +add anticipation to more mob attacks + stabber + striker + +can mob bullets damage other mob? + maybe if they switch collisions and classType === "body" or obj.classType === "bullet" + +figure out how to get friction effects on map/body to apply to player +also horizontal moving platform? + +growBoss and cellBoss are too similar + +variant of Occam's razor - remove 50% of your tech for each removed get: + 2 bots? + 50 energy max + +tech immune to harm after mob kill + require no other mob kill tech? + cloaking field tech? + +final boss invulnerability + until mobs are cleared? + in between phases + for all of one phase + +JUNK tech - do something fun to the mob display health method + +new platform element, spring + toggle to on when player touches platform + platform extends in any direction + +boss that gives nearby mobs invulnerability + invulnerability needs to toggle off and on + boss is only mildly aggressive + repulsed by player up to a point + attracted to mobs + +training + save training level progress as local variable + reset progress to zero if you clear all training levels + maybe only save progress if you made if past the trainingHeal level + make the training button more obvious if the account has only played 1-2 times + larger? + position? + animated text? + uses the lore voice/text code? + replace all mob clear triggers with button triggers? + tutorial rooms: + look around with your mouse? + easier deflecting level, with 1-2 attacking mobs + gun rooms: (different mobs type in each room) + different mobs in each room + how to introduce mob shields? + "hopper" "slasher" "shooter "grenadier" "striker" "laser" "stabber" "springer" "pulsar" "launcher" "launcherOne" "exploder" "sneaker" "sucker" "sniper" "spinner" "grower" "beamer" "focuser" "spawner" "ghoster" + spores - use 1 ammo to take out several mobs at once, you have to block with your shield until the mobs die + drones - use mouse to bring drones around a couple corners + foam - slow boss mob, and run away + laser - reflect off walls to hit mobs + field rooms: + standing wave - bullets come from every direction + perfect diamagnetism - drop field to rapid stream of bullets and fire gun at them + negative mass - fly over a bunch of ground based mobs , hoppers + molecular assembler - guide drones around the corner + plasma torch - nothing fancy, just kill mobs + time dilation - get past some mobs + cloaking - sneak past mobs to collect some heals + pilot wave - toss blocks at mobs + worm hole - teleport past lasers + puzzle/platforming rooms: + use the double constrained platforms + stealth room + probably should make 2+ + combat rooms: + boss gauntlet, spawn with nothing but a few power ups and fight 10 bosses + use no gun, just bots to kil stuff + +balance time dilation with bose einstein (you can freeze everything and take no damage) + code is still there, need to balance + balance with energy drain? + +make a line of constrained mobs move like a snake + apply forces with directions determined by time and position on the snake + +tech: basic research - heal power ups spawn as research power ups instead, and using research heals you (needs to be pretty low, like 3% health) +tech: maintenance - heals no longer spawn, but using research heals you 100% + +foam tech - make it move slower, last much longer, and push away other foam bullets + not sure about bouncing off walls, but that might be fun too + +tech extend fracture analysis to give bonus damage to frozen also, but reduce the 400%->300%? + +pulsar mobs retarget too easily +also they drift around too much + +convert blocked mobs into bullets + only for the very small bullets that move fast after being blocked + delete bulletmob and spawn a nail-like bullet with the same properties as the bulletmob + +electric motors: increases movement speed and jump height, but jumping and moving costs energy + overwrite the key event listeners? + JUNK tech? + +mob that fires bullets in 4,5,6,7 different directions at once, no aiming + grow a bit before it fires to indicate state + +quasarBoss: inverted pulsar boss that hits everything except where its aiming + +intro map: diegeticly draw a mouse with field highlighted + also indicate space? + dynamically adjust drawing after picking up a gun + +increase mass and movement speed at the same time + increase jump differently because it scales extra with mass + m.defaultMass = 4.5 + m.definePlayerMass() + +give history boss legs? + +field tech - disable blocking, but does high damage to mobs inside field + and maybe slows mobs it damages + +mob/boss that fires a laser at player, but give player time to avoid + laser isn't always on + they target where player was 1 second ago + they turn to face player? + +tech rocket jump - jumping produces an explosion at your feet that lets you jump extra high, but does some damage + require electric reactive armor? + +Plasma Burner: upgrade for plasma torch, basically just a jet engine. does high damage, but short range, mostly for player movement. + maybe reduce gravity to really low then apply a vector away from mouse direction + +auto-gon - auto battler with n-gon mob AI and tech + you build a group of mobs and bosses from n-gon + they fight other mobs and bosses + similar research and tech system to n-gon + some mobs can fire player weapons + +tech: relativistic jets: + small particles that shot out from front and back poles and end up in a wide variety of spirals + slow trickle when charging and several more when firing + +Tech: Make player smol + +adapt the cloaking graphics to make a flashlight cone visual effect + put code in level.do? + +be nice if block throwing had a projected path + +Pilot wave tech + Energy use is increased, but you can now shape blocks using pressure + Grouping blocks will merge them into a massive ball + Size, density is determined by total mass + +aoe effect pushes mobs away, then rapidly pulls them in + for mines? + +mob: spawning seekers on death + +drones can combine with other drones to get bigger? +drones that grab powers ups can grab more then one and get even bigger each time + +it would be helpful if there was a mechanism to recover mobs that fly off the map + add a ceiling system and a left/right walls system similar to the floor checks but only for mobs +make non moving bosses not move after getting hit + shooter, shielding, + +scrolling console history in pause menu? +pause should at least show the last in game console message + +in testing mode console log the body you click on + +tech: Standing Wave: Shockwave. Use FIELD button to shrink your shield and charge up, release to unleash a Shockwave. + +tech: quantized shields - harmonic standing wave field can only lose 33 energy per hit + draw 1,2,3 levels of the field based on energy? + the blocked value only scales up to 2x or 4x (33 energy) blocked + doesn't stack with spherical tech + +make a tech that improves all charge guns + for: pulse, foam, railgun + effect: + faster charge rate? + fire speed already does that... + harm reduction while charging + less ammo/energy used while charging? + +tech plasma : plasma length increases then decreases as you hold down the field button (like stabbing with a spear) + grows to 1.5 longer after 0.3 seconds, then returns to normal length over 1 second, until field is pressed again + extra energy is drained when field is longer + +energy conservation 6% damage recovered as energy + there is space for a negative effect in the text... + +tech: use the ability for power ups to have custom code + (note: this code is half way done, it just needs to be completed) + attracted to player + attracted to other power ups + explode if they touch? + +apply the new gun.do functions to other guns + railgun + crouching missile? + works similar to foam + performance issues? + +look into improving mouse lag with pointer lock? +https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API +https://www.vsynctester.com/game.html +https://news.ycombinator.com/item?id=26530272 + +tech: time dilation - when you exit time dilation rewind to the state you entered + position, velocity, and health + no energy cost + +be able to open up custom mode in the normal game + might need to be rebuilt from scratch + while in through testing mode? + have a way to make limited changes as allowed by tech you pick up in game + disable the in custom setting flag + +make different move methods + tech crouch charge jump + tech double jump + +tech when mobs are at full health you do 40% to them + +tech- move super fast, go intangible, drain energy very fast + this is like a dodge roll + tech for standing wave?, cloaking? + +tech pilot wave: mini black hole - pull mobs and blocks in with more force + also from farther away + also do damage? +tech pilot wave: antigravity - blocks have no gravity for a few seconds after exiting the field + maybe they bounce too? + maybe they explode? + +new power up - increase damage and fire speed, for 15 seconds + named boost? + enabled by a tech + power up color: ? + how to indicate effect duration + or just give the effect after picking up a reroll + +tech- do 50% more damage in close, but 50% less at a distance + code it like techisFarAwayDmg + have these tech disable each other + +new status effect: weakness, mobs do 75% les damage + graphic indication? + +new status effect: fear - push mob away from player for a time + +new status effect - apply status effect to mobs that makes blocks attracted to them + only lasts a few cycles + or zero cycles and it doesn't need to be a status + +have some mobs spawn in later in the level (in hard and why modes) + where + at defined points in array levelSpawns = [{x:0,y:0},{x:0,y:0}] + store the locations of mobs when the level starts to use as respawn points + remove the locations that are close to player + when? + after some mobs are dead + after the boss is killed + +look for tech that could update description text with count and tech is information + can only use variables that change in effect() and remove() + this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${techduplicateChance}` + +use mac automator to speed up your n-gon -> git sync + +movement fluidity + let legs jump on mobs, but player will still take damage + like: ori and the blind forest, celeste + many of the movement abilities in these games require levels to be built around the ability + general feeling of responsiveness and control + coyote time: can still jump a few cycles after leaving ground + tech double jump + tech air dash + tech wall jump + wall grab? + maybe remove falling damage and block damage? + +have a mob apply a positive status effect on other mobs, + heal? + make it yellow + damage bonus, but how? + possible balance issues + +css transition for pause menu + +animate new level spawn by having the map aspects randomly fly into place + +n-gon outreach ideas + blips - errant signal on youtube + reddit - r/IndieGaming + hacker news - show hacker news post + twitch - lets play + +******************************************************** BUGS ******************************************************** + +bug: maybe I can put in an event listener to reset inputs to false when you tab out to prevent key sticking + +bug - url sharing still broken sometimes + +tech upgrade to anthropic principle to make it trigger at 50% life and 0% once per map + +bug? cloaking field doesn't show energy over max + +run more profiles of n-gon to fix performance issues + +bug - death while paused crashes game? + +bug: possibly clearing away all bullets causes a problem + bullet.js 255 (.do() is missing) + I died and quantum immortality triggered (I had needles and ice-IX) + game crashed but recovered + +vanish element bug, crashes on touching element, happens for 1 person maybe with junk tech? + +safari issues + once: can't pick up blocks + fixed on new map + cloaking field + once: after damage, locked into slow time mode + fixed on damage + 3 times player head graphics not rotating + left/right leg flip broke + walk leg direction, legs are walking backwards + happened maybe after power up selection menu?? + cloaking field(at least once) + aiming still works + fixed on new map, although flip still broken (is flip a separate issue?) + flip fixed on new game + +sharing builds as html doesn't work for long lists... + it shouldn't be sharing undefined at all + probably some other problems too + (this might be fixed...) + +blocks on buttons teleport into the button endlessly if they are being slowly floated away + maybe add a cooldown? + can't reproduce + +ants marching outline doesn't sync right on safari anymore. + +door to exit in level: vats does nothing + did I do that? + +death while in power up selection menu doesn't reset properly + of course it's not possible to die in this menu unless you use testing and shift+X + +player can become crouched while not touching the ground if they exit the ground while crouched + +a couple times people have reported the final boss dropping extra bodies on death + +blue triangle boss can move backwards and aim away from you if set up properly + issues with dot product probably, but might not be worth fixing + +mouse event e.which is deprecated + +fix door.isClosing actually meaning isClosed? + +make it so that when you are immune to harm you can either jump on mobs or you pass through them + +is there a way to check if the player is stuck inside the map or block + trigger a short term non-collide if that occurs + +(intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause + +******************************************************** LEVELS ******************************************************** + +map: observatory + button controls rotation of telescope + laser beam shoots out of telescope + button opens the dome + +level with mobs that follow a genetic algorithm + mobs have genes + the last mob that did damage saves it's genes to local storage + new mobs have the saved genes, but with some random mutations + mutations need to be balanced to prevent a gene from moving towards infinity + total genome must equal 1 (100%) + binary genes have a flat cost + example: phasing through walls might cost 0.2 + spectrum genes have a rate + example: acceleration cost 0.01 per 0.001 + possible genes + genes should only effect it's ability to touch the player + so not damage? + genome: spectrum + acceleration + top speed / air friction + damageReduction + duration? + health decreases naturally? + or they just go away like bullets? + spawn rate + look frequency / memory? + genome: binary + go through walls + blink/teleport (like striker) + grow when near target + split into two + shielded + occurs in a specialized level + named: gene lab, gene factory, genetic lab, genome facility + in the level sequence after lab and before gauntlet? + level ends after a period of time + exit is hidden until time is up and it appears + the level tests player durability/evasion + this is a nice contrast to the final level that tests damage output, and the gauntlet which tests AoE damage + +rename intro level to something lore related + +buttons can now on/off boosts + +repeat map in vertical when you fall teleport to above the mab, as if the map repeats + camera looks strange when you teleport player with a high velocity + +map element - player rotates a rotor that makes a platform go up or down + +level element: a zone with wind, anti-gravity, extra gravity + control with button + +******************************************************** MOBS ******************************************************** + +mob that charges up and then fires many bullets at once in a connect + +mob that draws a lin from it to the player, and past. then it charges across that line + +mob that spawns eggs after they die + eggs don't attack but grow back into a mob after about 10s + +mob mechanics + use the force at a location effect, like the plasma field + Matter.Body.applyForce(who, path[1], force) + +mob - after taking damage + release seekers + teleports + +hop boss: + AoE damage when landing + pull in player? and blocks? + extra gravity on falling? + immune to damage while falling? + +mob: molecule shapes - 2 separate mobs joined by a bond + use constraints: just spawn 2x or 3x groupings + low friction so they can spin around + spin when attacking player? + increase constraint length when attacking + +Mob: "Tentacle": Sits on wall. Is a black blob. When you get near it, reaches out and grabs you, similar to wires. Does not deal damage. + maybe it could be immune to damage? but it is spawned by an actual mob + +level Boss: fractal Sierpiński triangle + https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle + spawns a 1/2 size version of the boss, this version can also spawn a smaller version, but it is capped at some size level + they spawn once at the start of the level + if a version dies, one can be replaced every ten seconds by the largest version + +mob: wall mounted guns / lasers + not part of randomized mob pool, customized to each level + +level boss: fires a line intersection in a random direction every few seconds. + the last two intersections have a destructive laser between them. + +******************************************************** SOUND ******************************************************** + +add sounds + https://developer.mozilla.org/en-US/docs/Web/API/OscillatorNode + https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Advanced_techniques + style + interesting chords, like music, mixolyd + can fade into the background + chords gradual rise and fall in volume + shouldn't be traditional effects + adjust volume with distance + mute option to settings + end all sounds with each new level + write some functions to produce sounds + will this effect performance? + https://padenot.github.io/web-audio-perf/ + "Web Audio API implementation use two threads", so it probably isn't much of an issue + when to make sounds? + if near some strange thing in the level + after grabbing a power up, in the selection menu + from mobs, damage,inside field + when activating fields: time, negative mass, + + //setup audio context + function tone(frequency) { + const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); + const oscillator = audioCtx.createOscillator(); + const gainNode = audioCtx.createGain(); + gainNode.gain.value = 0.25; //controls volume + oscillator.connect(gainNode); + gainNode.connect(audioCtx.destination); + oscillator.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator.frequency.value = frequency; // value in hertz + oscillator.start(); + return { oscillator: oscillator, audioCtx: audioCtx } + } + // https://www.fresnostate.edu/folklore/Olson/JUSTINT.HTM + // mixolyd. C D E F+ G A+ B + // key=G 264 297 330 356.40 396* 445.50 495 + // const t264 = tone(264) + // t264.oscillator.stop(t264.audioCtx.currentTime + 2) //in seconds, not milliseconds for some reason + // tone(297) + // tone(330) + // tone(356.40) + // tone(396) + // tone(445.50) + // tone(495) + + +******************************************************** LORE ******************************************************** + +possible names for tech + strange loop + holonomy - parallel transport of a vector leads to movement (applies to curved space) + hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other. + swarm intelligence - for a drone tech + genetic algorithm + metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity + stochastic optimization + electrostatic discharge + Gödel's incompleteness + quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields) + counterfactual - something false + regression to the mean + phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston + Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics. + evolutionary cosmology + eternal inflation + hypergraph + SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions. + Josephson junction - superconducting junction used in SQUIDS and quantum computers + nuclear pasta - hard matter in neutron star + nonlocal, nonlocality: maybe use for pilot wave + fine-tuned universe + nonperturbative + D-branes + instanton or pseudoparticle + soliton (wave packet) + hall effect thrusters + spaghettification + particle accelerator + superluminal signalling + NP-complete + lenticular lens: is an array of lenses, designed so that when viewed from slightly different angles, different parts of the image underneath are shown. + p-hacking JUNK tech + https://en.wikipedia.org/wiki/High-entropy_alloys high yield strength and low ductility, high temp resistance + https://en.wikipedia.org/wiki/Refractory_metals hard, high temp resistance + https://en.wikipedia.org/wiki/Upper-atmospheric_lightning#Elves + prion quine - self replicating protein + Unitarity - https://en.wikipedia.org/wiki/Unitarity_(physics) - all probabilities add up to 1, calculations work the same forward and backwards in time + this is violated by expansion of the universe + https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak + Alcubierre warp drive (FTL with negative mass) + Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi + negative entropy + memetics + magnetorquers - produce spin by pushing on earth's magnetic field + Josephson junction - superconducting junction + Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity + dark star - upgrade to WIMPs + +******************************************************** CARS IMAGES ******************************************************** + +process: discord midjourney prompts -> "pixelmator pro" adjust color, repair, scale to 384x256, export PNG -> webP? -> place in /img folder +make n-gon a progressive web app to manage image downloads, cache +wave function collapse opens the pause menu after it triggers alternate reality + this is actually good, maybe reuse this code to get pause menu to open at any time +if pause is pressed while selecting power ups, display pause menu on top of selection menu +***styles*** + try --- Pastel drawing, Psychedelic art, Arabesque (cool patterns), knolling (everything spread out and placed on a flat mat) + try taking screen shots of fields graphics and feeding them into midJourney V4 + technology stuff --- Dan Matutina (cute complex technology), + Katsuhiro Otomo (intricate space technology), Tsutomu Nihei (black and white detailed future tech) + infographics of all know multiverses. 1980s Japanese graphic design, dimensional astrolabe, + Japanese poster graphics, Ralph McQuarrie (looks like star wars), Simon Stålenhag (retro-futuristic), Yoshiyuki Tomino (detailed anime future technology) + isometric: low-poly, box cutout, made in blender, Materials: matte clay + subtractive sculpture + kinetic sculpture + quantum stuff -- Hypertorus, Glowing Opal Pearlescent, Physics, Hydro-Dipping Hydrodipped, Vija Celmins, Matt Molloy (photo of golden waves in the sky) +***maybe redo*** + laser + supercritical fission +***past style themes*** + base prompt for player on 5.2: clean white robot spherical turret on bird legs test chamber + standing wave - a 3-D cyan transparent nested concentric aligned centered sphere with rings + by Philippe Starck + perfect diamagnetism - physics magnetic field chalk diagram + time dilation - graphic of a hyperbolic equation Luminogram + negative mass - Blacklight painting by Moebius + plasma torch - by Dan Mumford + + metamaterial cloaking - Scientific photography by Miki Asai, by Bruce Munro + molecular assembler - by Laurie Greasley 16-bit Isometric + wormhole - by Tim White + pilot wave - none + + nail gun - Screenprint + shotgun - blueprint by Dan McPharlin + grenades, missiles, explosions - vibrant fireball explosion sonic shockwave ring art by Victo Ngai --ar 3:2 --v 5 --s 750 + spores - turquoise black spores on a white background full color scientific anatomy by Ernst Haeckel + drones - insect quadcopter tilt-shift photography + super balls - By Akari Toriyama + wave - sound wave oscilloscope by Paul Catherall, concentric circles by Paul Catherall + Barbara Takenaga's painting depicting a clean sound wave on aoscilloscope device --ar 3:2 --v 5 + foam - black blobs Ink doodle + harpoon - iron harpoon on a rope weapon art white background by Eiichiro Oda --no fish --ar 3:2 --v 5 --s 750 + mine - by Dan McPharlin + laser - complex optical scientific equipment + knolling photography + + guns, ammo - isometric clean pixel art image cutaway of , style of tekkonkinkreet + defensive - Paper cutout + bots - hovering drone by Laurie Greasley 16-bit Isometric + generic energy tech - by Laurie Greasley + duplication, cancel - by Kazumasa Nagai + anti-shear topology, fracture analysis, shear stress - Chemigram + ON/OFF - ASCII art + block throwing - Bauhaus style + tech that adds JUNK - by Choi Jeong-hwa + ice IX - microscope images of ice crystals + tech that spawns health - glowing green balls by Enki Bilal + invulnerable - by Nick Veasey (photos that look like x-rays) + alternate reality - Fractal art + tech choice - mandala tile Mosaic + time, CPT, pause - by Lee Bontecou + boost, coupling power ups tech - cyan electron orbiting a black nucleus electric field as bas-relief //(by Kazumasa Nagai) + radioactive - volumetric atomic nucleus diagram by Paul Catherall + + + diff --git a/resent-client/assets.epk b/online-mc/assets.epk similarity index 100% rename from resent-client/assets.epk rename to online-mc/assets.epk diff --git a/resent-client/classes.js b/online-mc/classes.js similarity index 100% rename from resent-client/classes.js rename to online-mc/classes.js diff --git a/resent-client/favicon.png b/online-mc/favicon.png similarity index 100% rename from resent-client/favicon.png rename to online-mc/favicon.png diff --git a/resent-client/fix-webm-duration.js b/online-mc/fix-webm-duration.js similarity index 100% rename from resent-client/fix-webm-duration.js rename to online-mc/fix-webm-duration.js diff --git a/resent-client/index.html b/online-mc/index.html similarity index 99% rename from resent-client/index.html rename to online-mc/index.html index 20510920..b280885d 100644 --- a/resent-client/index.html +++ b/online-mc/index.html @@ -307,8 +307,8 @@ assetsURI: "assets.epk", localesURI: "lang/", servers: [ - { addr: "wss://asianf4rmer.minecraft.pe", name: "AsianF4rmer SMP" }, - { addr: "wss://legendsmp.minecraft.pe", name: "LegendSMP" } + { addr: "wss://asian.minecraft.pe", name: "AsianF4rmer SMP" }, + { addr: "wss://legendsmp.minecraft.pe", name: "blocked :(" } ], }; diff --git a/resent-client/logo.png b/online-mc/logo.png similarity index 100% rename from resent-client/logo.png rename to online-mc/logo.png diff --git a/projects.html b/projects.html new file mode 100644 index 00000000..64df3618 --- /dev/null +++ b/projects.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + Projects | Selenite + + + + + + + + + + + + + + +
+ Home + Bookmarklets + Games + Settings + Support + +
+ +
+ + + + +
+ + +
+
+ + +
+ +

Pinned Games

+

pin some games for things to show up here!

+
+ + +

All Games

+
+

games loading..

+
+ +
+ + + + diff --git a/quotes.json b/quotes.json new file mode 100644 index 00000000..de352d79 --- /dev/null +++ b/quotes.json @@ -0,0 +1,34 @@ +[ + "pay attention during school", + "yall playing games while im making the website ur using", + "\"p-p-p-p-pwettier code ><\" - sky 2023", + "\"im jumping off a FUCKING BRIDGE\" - also sky 2023", + "shout out to whoever spent 2 hours on snake", + "selenite >>> byvolp", + "Lorem ipsum dolor sit amet, adipiscing consequat adipiscing sit amet. In tortor dolor, sollicitudin quis urna vitae, rutrum selecrelico dolor. Ut facilisis ornare lacus, ut dictum felis aliquam ut. Omnino interdum fit cum cura. Non est aequum, non solum, sed nec refert quis sit terra. Sed vestibulum a tellus non semper. Aliquam a neque euismod dolor dapibus aliquet ultricies ut nibh. Sed eget auctor ante. Aliquam dictum volutpat vestibulum. Putasti ovum paschale futurum. Nullo modo.", + "FAGGOT", + "ive currently lost $5 on this website", + "join the discord NOW", + "have any of you played on the og padlet?", + "linux is so much better than windows", + "chromebooks suck", + "technoblade never dies", + "Selenite has shut down.", + "theres at least 2, maybe 3 games on this website", + "yall failing ur classes while im a straight A student in all honors", + "just maybe stole a bunch ideas from 3kh0", + "why tf is cookie clicker the most popular game", + "https://discord.gg/7jyufnwJNf", + "buy smth from the patreon so i dont have to be in debt anymore", + "https://skysthelimit.dev", + "https://github.com/skysthelimitt", + "selenite.pages.dev", + "the square root of OGNUM1 is SQRTNUM1", + "ZG8geW91ciBzY2hvb2wgd29yaw", + "use the transfer tool to keep ur data from other websites", + "best way to bypass the filter is on my patreon!", + "best proxy ever only on my patreon", + "🥺👉👈", + "what website is this?", + "btw, hcps leaked 30k+ private emails and are hiding it :)" +] \ No newline at end of file diff --git a/settings.html b/settings.html index 17a05725..01f762e2 100644 --- a/settings.html +++ b/settings.html @@ -1,118 +1,101 @@ - - + - - - - - + - - + + + + - Selenite + Settings | Selenite - + - - + + - + + - -
- - + +
+ Home + Bookmarklets + Games + Settings + Support + +
-
- -

selenite

-
- -
+
+

Settings

-

Website Name

-
+

Website Name


-

Website Icon

-
-

+

Website Icon

+
+

Or select a preset:

+

Themes

- - - +
+ + + + + +

Website URL

-

- -
+ + + + + diff --git a/snake/assets/logos/fnbx/snake_arcade/v3/apple_00.png b/snake/assets/apple_00.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v3/apple_00.png rename to snake/assets/apple_00.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_01.png b/snake/assets/apple_01.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_01.png rename to snake/assets/apple_01.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_02.png b/snake/assets/apple_02.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_02.png rename to snake/assets/apple_02.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_03.png b/snake/assets/apple_03.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_03.png rename to snake/assets/apple_03.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_04.png b/snake/assets/apple_04.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_04.png rename to snake/assets/apple_04.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_05.png b/snake/assets/apple_05.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_05.png rename to snake/assets/apple_05.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_06.png b/snake/assets/apple_06.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_06.png rename to snake/assets/apple_06.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_07.png b/snake/assets/apple_07.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_07.png rename to snake/assets/apple_07.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_08.png b/snake/assets/apple_08.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_08.png rename to snake/assets/apple_08.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_09.png b/snake/assets/apple_09.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_09.png rename to snake/assets/apple_09.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_10.png b/snake/assets/apple_10.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_10.png rename to snake/assets/apple_10.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_11.png b/snake/assets/apple_11.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_11.png rename to snake/assets/apple_11.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_12.png b/snake/assets/apple_12.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_12.png rename to snake/assets/apple_12.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_13.png b/snake/assets/apple_13.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_13.png rename to snake/assets/apple_13.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_14.png b/snake/assets/apple_14.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_14.png rename to snake/assets/apple_14.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_15.png b/snake/assets/apple_15.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_15.png rename to snake/assets/apple_15.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_16.png b/snake/assets/apple_16.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_16.png rename to snake/assets/apple_16.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_17.png b/snake/assets/apple_17.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_17.png rename to snake/assets/apple_17.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_18.png b/snake/assets/apple_18.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_18.png rename to snake/assets/apple_18.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_19.png b/snake/assets/apple_19.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_19.png rename to snake/assets/apple_19.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_20.png b/snake/assets/apple_20.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_20.png rename to snake/assets/apple_20.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_21.png b/snake/assets/apple_21.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/apple_21.png rename to snake/assets/apple_21.png diff --git a/snake/assets/logos/fnbx/snake_arcade/blink.png b/snake/assets/blink.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/blink.png rename to snake/assets/blink.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/box.png b/snake/assets/box.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v4/box.png rename to snake/assets/box.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_00.png b/snake/assets/color_00.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_00.png rename to snake/assets/color_00.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_01.png b/snake/assets/color_01.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_01.png rename to snake/assets/color_01.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_02.png b/snake/assets/color_02.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_02.png rename to snake/assets/color_02.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_03.png b/snake/assets/color_03.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_03.png rename to snake/assets/color_03.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_04.png b/snake/assets/color_04.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_04.png rename to snake/assets/color_04.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_05.png b/snake/assets/color_05.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_05.png rename to snake/assets/color_05.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_06.png b/snake/assets/color_06.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_06.png rename to snake/assets/color_06.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_07.png b/snake/assets/color_07.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_07.png rename to snake/assets/color_07.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_08.png b/snake/assets/color_08.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_08.png rename to snake/assets/color_08.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_09.png b/snake/assets/color_09.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_09.png rename to snake/assets/color_09.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_10.png b/snake/assets/color_10.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_10.png rename to snake/assets/color_10.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_11.png b/snake/assets/color_11.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_11.png rename to snake/assets/color_11.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_12.png b/snake/assets/color_12.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_12.png rename to snake/assets/color_12.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_13.png b/snake/assets/color_13.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_13.png rename to snake/assets/color_13.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_14.png b/snake/assets/color_14.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_14.png rename to snake/assets/color_14.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_15.png b/snake/assets/color_15.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_15.png rename to snake/assets/color_15.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_16.png b/snake/assets/color_16.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_16.png rename to snake/assets/color_16.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_17.png b/snake/assets/color_17.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_17.png rename to snake/assets/color_17.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v5/color_18.png b/snake/assets/color_18.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v5/color_18.png rename to snake/assets/color_18.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v3/count_00.png b/snake/assets/count_00.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v3/count_00.png rename to snake/assets/count_00.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v3/count_01.png b/snake/assets/count_01.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v3/count_01.png rename to snake/assets/count_01.png diff --git a/snake/assets/logos/fnbx/snake_arcade/v3/count_02.png b/snake/assets/count_02.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/v3/count_02.png rename to snake/assets/count_02.png diff --git a/snake/assets/css.css b/snake/assets/css.css index 6b683c26..abca5352 100644 --- a/snake/assets/css.css +++ b/snake/assets/css.css @@ -53,4 +53,4 @@ font-weight: 500; src: url(https://fonts.gstatic.com/s/roboto/v29/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} +} \ No newline at end of file diff --git a/snake/assets/logos/fnbx/snake_arcade/default_end.png b/snake/assets/default_end.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/default_end.png rename to snake/assets/default_end.png diff --git a/snake/assets/logos/fnbx/snake_arcade/die.png b/snake/assets/die.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/die.png rename to snake/assets/die.png diff --git a/snake/assets/logos/fnbx/snake_arcade/eat.png b/snake/assets/eat.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/eat.png rename to snake/assets/eat.png diff --git a/snake/assets/logos/fnbx/snake_arcade/effect.png b/snake/assets/effect.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/effect.png rename to snake/assets/effect.png diff --git a/snake/assets/logos/fnbx/snake_arcade/end_empty.png b/snake/assets/end_empty.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/end_empty.png rename to snake/assets/end_empty.png diff --git a/snake/assets/logos/fnbx/snake_arcade/gradient_end.png b/snake/assets/gradient_end.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/gradient_end.png rename to snake/assets/gradient_end.png diff --git a/snake/assets/logos/fnbx/snake_arcade/key_types.png b/snake/assets/key_types.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/key_types.png rename to snake/assets/key_types.png diff --git a/snake/assets/logos/fnbx/snake_arcade/key_types_dark.png b/snake/assets/key_types_dark.png similarity index 100% rename from snake/assets/logos/fnbx/snake_arcade/key_types_dark.png rename to snake/assets/key_types_dark.png diff --git a/snake/assets/keys.svg b/snake/assets/keys.svg new file mode 100644 index 00000000..54f7356b --- /dev/null +++ b/snake/assets/keys.svg @@ -0,0 +1 @@ +NEWSVG1 \ No newline at end of file diff --git a/snake/assets/logos/fnbx/snake_arcade/keys.svg b/snake/assets/logos/fnbx/snake_arcade/keys.svg deleted file mode 100644 index 44a72984..00000000 --- a/snake/assets/logos/fnbx/snake_arcade/keys.svg +++ /dev/null @@ -1 +0,0 @@ -NEWSVG1 \ No newline at end of file diff --git a/snake/assets/logos/fnbx/snake_arcade/poison_audio.bin b/snake/assets/logos/fnbx/snake_arcade/poison_audio.bin deleted file mode 100644 index 256b3fb9..00000000 Binary files a/snake/assets/logos/fnbx/snake_arcade/poison_audio.bin and /dev/null differ diff --git a/snake/assets/logos/fnbx/snake_arcade/v3/trophy_00.png b/snake/assets/logos/fnbx/snake_arcade/v3/trophy_00.png deleted file mode 100644 index 6ba4527b..00000000 Binary files a/snake/assets/logos/fnbx/snake_arcade/v3/trophy_00.png and /dev/null differ diff --git a/snake/assets/logos/fnbx/snake_arcade/v4/apple_00.png b/snake/assets/logos/fnbx/snake_arcade/v4/apple_00.png deleted file mode 100644 index 9a5156d0..00000000 Binary files a/snake/assets/logos/fnbx/snake_arcade/v4/apple_00.png and /dev/null differ diff --git a/snake/assets/m_aLUfP.js b/snake/assets/m_aLUfP.js index 75778296..97ce0cce 100644 --- a/snake/assets/m_aLUfP.js +++ b/snake/assets/m_aLUfP.js @@ -365,11 +365,11 @@ b();a.Lc({dispose:function(){},isDisposed:function(){return d}});return function try{ s_a("pKhWu"); -var s_Fwd={sUb:new s_jD("/logos/fnbx/snake_arcade/","end_audio",{DEATH:0,WIN:49},418)},s_Gwd={pUb:new s_iD(s_Fwd.sUb,"DEATH",626.939),WIN:new s_iD(s_Fwd.sUb,"WIN",914.286)},s_Hwd=function(){s_hD.call(this,s_Fwd,s_Gwd)};s_m(s_Hwd,s_hD); -var s_AD={Hw:new s_jD("/logos/fnbx/snake_arcade/","game_audio.4",{BOX:0,DOWN:59,EAT:78,KEY:101,LEFT:132,PORTAL:151,REVERSE:212,RIGHT:273,UP:292},418)},s_BD={mGc:new s_iD(s_AD.Hw,"BOX",757.551),AFa:new s_iD(s_AD.Hw,"DOWN",235.102),qHc:new s_iD(s_AD.Hw,"EAT",287.347),EQc:new s_iD(s_AD.Hw,"KEY",391.837),LEFT:new s_iD(s_AD.Hw,"LEFT",235.102),wSc:new s_iD(s_AD.Hw,"PORTAL",783.673),TSc:new s_iD(s_AD.Hw,"REVERSE",783.673),RIGHT:new s_iD(s_AD.Hw,"RIGHT",235.102),UP:new s_iD(s_AD.Hw,"UP",235.102)},s_Iwd=function(){s_hD.call(this, +var s_Fwd={sUb:new s_jD("/stack/","end_audio",{DEATH:0,WIN:49},418)},s_Gwd={pUb:new s_iD(s_Fwd.sUb,"DEATH",626.939),WIN:new s_iD(s_Fwd.sUb,"WIN",914.286)},s_Hwd=function(){s_hD.call(this,s_Fwd,s_Gwd)};s_m(s_Hwd,s_hD); +var s_AD={Hw:new s_jD("/stack/","game_audio.4",{BOX:0,DOWN:59,EAT:78,KEY:101,LEFT:132,PORTAL:151,REVERSE:212,RIGHT:273,UP:292},418)},s_BD={mGc:new s_iD(s_AD.Hw,"BOX",757.551),AFa:new s_iD(s_AD.Hw,"DOWN",235.102),qHc:new s_iD(s_AD.Hw,"EAT",287.347),EQc:new s_iD(s_AD.Hw,"KEY",391.837),LEFT:new s_iD(s_AD.Hw,"LEFT",235.102),wSc:new s_iD(s_AD.Hw,"PORTAL",783.673),TSc:new s_iD(s_AD.Hw,"REVERSE",783.673),RIGHT:new s_iD(s_AD.Hw,"RIGHT",235.102),UP:new s_iD(s_AD.Hw,"UP",235.102)},s_Iwd=function(){s_hD.call(this, s_AD,s_BD)};s_m(s_Iwd,s_hD); -var s_Jwd={cva:new s_jD("/logos/fnbx/snake_arcade/","poison_audio",{BURP:0,DIZZY_1:27,DIZZY_2:42,DIZZY_3:57,DIZZY_4:72,SQUISH:87},418)},s_CD={nGc:new s_iD(s_Jwd.cva,"BURP",339.592),gHc:new s_iD(s_Jwd.cva,"DIZZY_1",182.857),hHc:new s_iD(s_Jwd.cva,"DIZZY_2",182.857),iHc:new s_iD(s_Jwd.cva,"DIZZY_3",182.857),jHc:new s_iD(s_Jwd.cva,"DIZZY_4",182.857),Ujb:new s_iD(s_Jwd.cva,"SQUISH",470.204)},s_Kwd=function(){s_hD.call(this,s_Jwd,s_CD)};s_m(s_Kwd,s_hD); -var s_DD=function(a,b,c){this.path=a;this.Aa=b;this.context=c;this.loaded=!1;this.Eo=new Image;this.Eo.crossOrigin="Anyonymous";this.wa=document.createElement("canvas").getContext("2d");this.oa=document.createElement("canvas").getContext("2d");this.Ca=this.Ba="";this.Ea=-1;s_Lwd(this)},s_Lwd=function(a){a.Eo.src="./assets/logos/fnbx/"+a.path;s_Ii(a.Eo,"load",function(){a.loaded=!0;s_Mwd(a);""!==a.Ba&&""!==a.Ca&&(s_ED(a,a.Ba,a.Ca,a.Ea),a.Ba="",a.Ca="",a.Ea=0)})},s_Mwd=function(a){a.wa.canvas.width= +var s_Jwd={cva:new s_jD("/stack/","poison_audio",{BURP:0,DIZZY_1:27,DIZZY_2:42,DIZZY_3:57,DIZZY_4:72,SQUISH:87},418)},s_CD={nGc:new s_iD(s_Jwd.cva,"BURP",339.592),gHc:new s_iD(s_Jwd.cva,"DIZZY_1",182.857),hHc:new s_iD(s_Jwd.cva,"DIZZY_2",182.857),iHc:new s_iD(s_Jwd.cva,"DIZZY_3",182.857),jHc:new s_iD(s_Jwd.cva,"DIZZY_4",182.857),Ujb:new s_iD(s_Jwd.cva,"SQUISH",470.204)},s_Kwd=function(){s_hD.call(this,s_Jwd,s_CD)};s_m(s_Kwd,s_hD); +var s_DD=function(a,b,c){this.path=a;this.Aa=b;this.context=c;this.loaded=!1;this.Eo=new Image;this.Eo.crossOrigin="Anyonymous";this.wa=document.createElement("canvas").getContext("2d");this.oa=document.createElement("canvas").getContext("2d");this.Ca=this.Ba="";this.Ea=-1;s_Lwd(this)},s_Lwd=function(a){a.Eo.src="./assets/"+a.path;s_Ii(a.Eo,"load",function(){a.loaded=!0;s_Mwd(a);""!==a.Ba&&""!==a.Ca&&(s_ED(a,a.Ba,a.Ca,a.Ea),a.Ba="",a.Ca="",a.Ea=0)})},s_Mwd=function(a){a.wa.canvas.width= a.Eo.width;a.wa.canvas.height=a.Eo.height;a.wa.clearRect(0,0,a.wa.canvas.width,a.wa.canvas.height);a.wa.drawImage(a.Eo,0,0);a.oa.canvas.width=a.Eo.width;a.oa.canvas.height=a.Eo.height;a.oa.clearRect(0,0,a.oa.canvas.width,a.oa.canvas.height);a.oa.drawImage(a.Eo,0,0)},s_ED=function(a,b,c,d){d=void 0===d?-1:d;if(a.loaded){s_Mwd(a);b=s_HSc(b);c=s_HSc(c);for(var e=0===b[2]?1:c[2]/b[2],f=a.wa.getImageData(0,0,a.wa.canvas.width,a.wa.canvas.height),g=f.data,h=0;h=d;1>k[2]&&!l&&(k[0]=c[0],k[1]=c[1],k[2]*=e);k=s_ESc(k[0],k[1],k[2]);g[h]=k[0];g[h+1]=k[1];g[h+2]=k[2]}a.oa.putImageData(f,0,0)}else a.Ba=b,a.Ca=c,a.Ea=d};s_DD.prototype.render=function(a,b,c,d,e){this.loaded&&(this.context.translate(b.x,b.y),this.context.rotate(d),this.context.drawImage(this.oa.canvas,0,this.ld()*a,this.Dd(),this.ld(),c.x*e,c.y*e,this.Dd()*e,this.ld()*e),this.context.rotate(-d),this.context.translate(-b.x,-b.y))}; s_DD.prototype.Dd=function(){return this.Eo.width};s_DD.prototype.ld=function(){return this.Eo.height/this.Aa};var s_FD=function(a){return a.wa.canvas}; @@ -380,9 +380,9 @@ threshold:10},{base:"#fc2d00",target:"#808080",threshold:10}],s_Swd="#4E7CF6 #54 [];this.kva=0;this.yb=[];this.hj=!1;this.Ya=0;this.HRb=this.$k=!1;this.Ye=this.Vb=0;this.muted=!1;this.closed=!0;this.Yda=this.Ve=0;this.fW="";this.qR=this.xu=0;this.gW="";this.Wda=this.ticks=this.oR=this.Mf=this.Bx=this.qB=this.lR=this.Oc=0;this.aW=[];this.iHa=1;this.jHa=4;this.cW=10;this.zD=0;this.kkb=8;this.tgb=7;this.IK=0;this.lzb=6;this.yu=0;this.Ga={YL:0,YR:0,QJ:0,Hx:0,jt:!1,WQ:0,tV:0,zY:!1};this.Ja={YL:0,YR:0,QJ:0,Hx:0,jt:!1,WQ:0,tV:0,zY:!1};this.Uc=this.uc=this.pR=this.Rda=0;this.DTb=this.lva= !1;this.TYb=this.J_b=null;this.Ds=!1;this.wL=-1;this.yUb=this.getData("si").Cb(!1);this.hHa=new s_Iwd;this.gCb=new s_Hwd;this.rTb=new s_Kwd;this.Yc=a.service.Yc;this.Ek=new s_Sg(0,0);this.oa=new s_Sg(0,0);this.FUb=function(){};this.canvas=this.Da("UzWXSb").Vd();this.context=this.canvas.getContext("2d");this.zr=this.Da("kAVrAc").Vd();this.ii=this.zr.getContext("2d");this.Ca=s_Uwd();this.Cc=s_Uwd();this.Oa=s_Uwd();this.Oa.fillStyle="#000000";this.Iv=s_Uwd();this.Mn=s_Uwd();s_BA(this);this.dVc=new s_Is(document); s_i(this.dVc,"key",function(g){return b.PB(g)});s_i(this.getRoot().el(),"touchstart",function(g){return b.kE(g)});s_i(this.getRoot().el(),"touchmove",function(g){return b.nK(g)});s_i(this.getRoot().el(),"touchend",function(g){return b.yT(g)});this.Yc.addListener(function(){s_Vwd(b)});this.direction="NONE";this.Ra="RIGHT";this.Ab=this.wy="NONE";this.Sda=new s_Og(0,0);this.Ax=new s_Og(0,0);this.kb=new Set;this.Ib=new Set;this.Ze=new s_Og(0,0);this.X3=new s_Og(0,0);this.lW=new s_Og(0,0);this.vo=new Map; -this.bW=new Set;this.yg=new Map;this.ECb=new Map;s_Wwd(this);this.Vi=s_Owd*s_Xwd(this);for(a=0;21>a;a++){var c=new s_DD("snake_arcade/v4/apple_"+((10>a?"0":"")+a)+".png",1,this.Ca),d=s_Rwd[a];s_ED(c,d.base,d.target,d.threshold);this.aW.push(c)}this.Em=new s_DD("snake_arcade/blink.png",9,this.Ca);this.ED=new s_DD("snake_arcade/eat.png",15,this.Ca);this.hz=new s_DD("snake_arcade/die.png",37,this.Ca);this.Es=new s_DD("snake_arcade/tongue.png",21,this.Ca);this.uJ=new s_DD("snake_arcade/effect.png",21, -this.Ca);this.vf=new s_Og;this.cZb=new s_DD("snake_arcade/key_types.png",5,this.Ca);this.YZb=new s_DD("snake_arcade/key_types_dark.png",5,this.Ca);this.Zd=new s_DD("snake_arcade/v4/box.png",8,this.Ca);this.TJb=new s_DD("snake_arcade/blink.png",9,this.Ca);this.QZb=new s_DD("snake_arcade/eat.png",15,this.Ca);this.HZb=new s_DD("snake_arcade/die.png",37,this.Ca);this.TZb=new s_DD("snake_arcade/tongue.png",21,this.Ca);this.CTb=new s_DD("snake_arcade/blink.png",9,this.Ca);this.D_b=new s_DD("snake_arcade/eat.png", -15,this.Ca);this.k_b=new s_DD("snake_arcade/die.png",37,this.Ca);s_ED(this.CTb,"#5282F2","#909090");s_ED(this.D_b,"#5282F2","#909090");s_ED(this.k_b,"#5282F2","#909090");this.Be=new s_DD("snake_arcade/end_empty.png",1,this.ii);this.Uda=new s_DD("snake_arcade/default_end.png",1,this.ii);this.iVc=new s_DD("snake_arcade/rainbow_end.png",1,this.ii);this.XUb=new s_DD("snake_arcade/gradient_end.png",1,this.ii);this.lCb=new s_DD("snake_arcade/v3/speed_01.png",1,this.ii);this.GTb=new s_DD("snake_arcade/v3/speed_02.png", +this.bW=new Set;this.yg=new Map;this.ECb=new Map;s_Wwd(this);this.Vi=s_Owd*s_Xwd(this);for(a=0;21>a;a++){var c=new s_DD("apple_"+((10>a?"0":"")+a)+".png",1,this.Ca),d=s_Rwd[a];s_ED(c,d.base,d.target,d.threshold);this.aW.push(c)}this.Em=new s_DD("blink.png",9,this.Ca);this.ED=new s_DD("eat.png",15,this.Ca);this.hz=new s_DD("die.png",37,this.Ca);this.Es=new s_DD("tongue.png",21,this.Ca);this.uJ=new s_DD("effect.png",21, +this.Ca);this.vf=new s_Og;this.cZb=new s_DD("key_types.png",5,this.Ca);this.YZb=new s_DD("key_types_dark.png",5,this.Ca);this.Zd=new s_DD("box.png",8,this.Ca);this.TJb=new s_DD("blink.png",9,this.Ca);this.QZb=new s_DD("eat.png",15,this.Ca);this.HZb=new s_DD("die.png",37,this.Ca);this.TZb=new s_DD("tongue.png",21,this.Ca);this.CTb=new s_DD("blink.png",9,this.Ca);this.D_b=new s_DD("eat.png", +15,this.Ca);this.k_b=new s_DD("die.png",37,this.Ca);s_ED(this.CTb,"#5282F2","#909090");s_ED(this.D_b,"#5282F2","#909090");s_ED(this.k_b,"#5282F2","#909090");this.Be=new s_DD("end_empty.png",1,this.ii);this.Uda=new s_DD("default_end.png",1,this.ii);this.iVc=new s_DD("rainbow_end.png",1,this.ii);this.XUb=new s_DD("gradient_end.png",1,this.ii);this.lCb=new s_DD("speed_01.png",1,this.ii);this.GTb=new s_DD("speed_02.png", 1,this.ii);this.$Uc="./assets/volume_up_white_24dp.png";this.RUc="./assets/volume_off_white_24dp.png";this.vy=s_ID[0][0];this.ugb=s_ID[0][1];this.T8=new s_Og(0,0);s_C(this.Da("y7GBZ").el(),"visibility","hidden");this.Gc="score";a=this.Da("wXSCdb").el();c=[];for(d=0;d + - - - Snake - - - - + + + + Google Snake | 3kh0 + + + + + + + + + + + +
+ +
+
+
+
0
+
+ +
+
+
00:00:000
+
+
25
+
--:--:---
+
+
+
+ End game + Mute +
+
+
+
+
+
+ +
+
+
+ + + + +
+ +
+
0
+
+
+
+
0
+
+
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + +
+
+
+
+ + + + + + + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +

Play

+
+
Settings
+
Shuffle
+
+
+
+
+ +
+ + + + diff --git a/snake/inframe.html b/snake/inframe.html deleted file mode 100644 index 7516a83a..00000000 --- a/snake/inframe.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - -Snake
0
00:00:000
25
--:--:---
End gameMute
0
0

Play

Settings
Shuffle
\ No newline at end of file diff --git a/style.css b/style.css index 79adb9eb..09b3894c 100644 --- a/style.css +++ b/style.css @@ -1,249 +1,251 @@ -html { - text-align: center; - align-content: center; - font-family: "Share Tech Mono", monospace; - padding: 0; - margin: 0; -} -.content { - animation: fadeIn 5s; - width: 90%; - margin: 0 auto; -} -* { - z-index: 1; -} -button { - background-color: var(--input-bg-color); - color: var(--main-text-color); - border: 2px solid var(--border-color); - border-radius: 6px; - padding: 5px 13px; - margin-bottom: 6px; - margin: 10px; - transition-duration: 0.45s; - font-size: 20px; - color: var(--main-text-color); - font-family: "Share Tech Mono", monospace; - text-underline-position: under; - opacity: 70%; -} -button:hover { - cursor: pointer; - opacity: 90%; -} - -h2 { - font-size: 42px; - color: var(--main-text-color); - margin-top: 3%; -} - -.game { - width: 13%; - display: inline-block; - background-color: var(--game-color); - padding: 1%; - border-radius: 10%; - margin: 2%; - transition-duration: 0.5s; - box-shadow: 0px 0px 5px 5px var(--game-color); - height: 100%; -} - -.game:hover { - transform: scale(1.05); - filter: brightness(120%); -} - -.game h1 { - float: right; - font-size: 0.85em; - color: var(--main-text-color); -} -.game img { - width: 100%; - height: 100%; - border-radius: 10%; - aspect-ratio: 1/1; -} - -a { - font-size: 20px; - color: var(--main-text-color); - margin: 10px; - transition-duration: 0.5s; -} - -a:hover { - filter: brightness(150%); -} - -.bookmarklet { - font-size: 25px; -} - -.title { - display: flex; - align-items: center; - justify-content: center; -} - -@keyframes fadeIn { +@import url('https://fonts.googleapis.com/css2?family=Prompt:wght@300&display=swap'); +@keyframes loadInAnimation { 0% { opacity: 0; } - 20% { - opacity: 40; - } - 80% { - opacity: 90; - } - 90% { - opacity: 92; - } 100% { opacity: 100; } } -@keyframes fadeInPadding { - 0% { - padding-left: 0%; - padding-right: 0%; - } - 100% { - padding-left: 5%; - padding-right: 5%; - } -} - -body { - background: repeating-linear-gradient( - 45deg, - var(--bg-1) 0%, - var(--bg-2) 33%, - var(--bg-1) 66%, - var(--bg-2) 100% - ); - background-size: 400% 400%; - background-attachment: fixed; - animation: gradient 30s linear infinite; - height: 100vh; -} - -@keyframes gradient { - 0% { - background-position: 100% 0%; - } - 100% { - background-position: 15% 100%; - } -} - -input[type="text"] { - width: 50%; - box-sizing: border-box; - font-size: 20px; - background-color: var(--input-bg-color); - padding: 0.8% 0.8%; - text-align: center; - color: var(--main-text-color); - border: 2px solid var(--border-color); - border-radius: 5px; +* { + transition-duration: 0.5s; outline: none; - opacity: 70%; - transition: opacity 0.25s ease-in-out; - font-family: "Share Tech Mono", monospace; + border: none; + box-sizing: border-box; + align-content: center; + text-align: center; + font-family: 'Prompt', sans-serif; + color: var(--textcolor); } -input[type="text"]:hover { - opacity: 80%; +body { + margin: 0; + padding: 0; + background-color: var(--bg); + color: var(--textcolor); } -input[type="text"]:focus { - opacity: 100%; +header, +footer { + background-color: var(--uibg); + display: flex; + padding: 0.7rem; + width: 100%; + z-index: 1; + justify-content: center; +} +header { + top: 0; + position: fixed; + box-shadow: 0 5px 10px var(--uibg); +} +footer.noscroll { + position: fixed; + overflow: hidden; +} +footer { + bottom: 0; + box-shadow: 0 -5px 10px var(--uibg); +} +footer a, +footer a:visited, +header a, +header a:visited { + text-align: center; + font-weight: 700; + text-decoration: none; + margin-right: 2rem; + margin-left: 1rem; } -::placeholder { - /* Chrome, Firefox, Opera, Safari 10.1+ */ - color: var(--main-text-color); - opacity: 0.6; /* Firefox */ +footer a:hover, +header a:hover { + text-shadow: 2px 2px 6px var(--textcolor); +} +main { + animation: 1.25s ease-in-out 0s 1 loadInAnimation; + align-items: center; + flex-direction: column; + display: flex; + min-height: calc(100vh - (2 * 0.7rem)); + gap: 0; + flex-wrap: wrap; + margin: auto; + margin-top: 4rem; + width: 90%; +} +main#main.noscroll { + justify-content: center; } -ul { - list-style-type: none; +h1, +h2, +h3, +p, +a { + margin: 0; padding: 0; } -li { - display: inline; - padding: 8px; - top: 10px; +.samerow { + display: flex; + justify-content: center; + gap: 0; + flex-wrap: wrap; +} +h1 { + font-size: 60px; } -#footer { - font-size: 10px; +h2 { + font-size: 40px; } - -p { - font-size: 20px; - color: var(--p-text-color); -} - -#webname #webicon { - width: 20%; - box-sizing: border-box; - font-size: 20px; - background-color: var(--input-bg-color); - padding: 0.8% 0.8%; - text-align: center; - color: var(--main-text-color); - border: 2px solid var(--border-color); - border-radius: 5px; - outline: none; - opacity: 70%; - transition: opacity 0.25s ease-in-out; - font-family: "Share Tech Mono", monospace; -} - h3 { font-size: 20px; - color: var(--main-text-color); +} +p, a { + font-size: 16px; } -.submit { - background-color: var(--input-bg-color); - color: var(--main-text-color); - border: 2px solid var(--border-color); - border-radius: 6px; - padding: 5px 13px; - margin-bottom: 6px; - margin: 10px; - transition-duration: 0.45s; - font-size: 20px; - color: var(--main-text-color); - font-family: "Share Tech Mono", monospace; - text-underline-position: under; - opacity: 70%; -} -.submit:hover { +button, input#websubmit.submit, input[type=submit]#panic { cursor: pointer; - opacity: 90%; + background-color: var(--inputbg); + border-color: var(--inputborder); + border-width: 5px; + border-radius: 10px; + border-style: solid; + padding: 7px; + margin-left: 20px; + margin-right: 20px; + margin: 10px; +} + +button:hover, input#websubmit.submit:hover, input[type=submit]#panic:hover { + filter: brightness(80%); + transform: scale(1.07); +} + +img { + width: 100%; + aspect-ratio: auto; + user-select: none; +} + +.img-container a { + width: 100%; +} + +.img-container { + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + gap: 0; + flex-wrap: wrap; + width: 6%; + aspect-ratio: 1 / 1; + margin: 2%; +} + +.game { + width: 15%; + border-radius: 20px; + display: inline-block; + background-color: var(--uibg); + margin: 15px; + cursor: pointer; + box-shadow: 0px 0px 5px 5px var(--uibg); +} + +.game:hover { + transform: scale(1.1); + filter: brightness(85%); + box-shadow: 0px 0px 5px 5px var(--uibg); +} + +.game h1 { + font-size: 13px; + float: right; + margin-right: 10px; + margin-top: 3px; + margin-bottom: 3px; +} + +.game img { + width: 100%; + border-radius: 20px; + aspect-ratio: 1 / 1; } img.star { float: left; - filter: invert(var(--invert-logo, 0)); - width: 20%; - transition-duration: 0.75s; + width: 10%; + margin-top: 3px; + margin-bottom: 3px; + margin-left: 10px; } -#random { - padding: 0.8% 0.8%; +#noscroll { + overflow-y: hidden; + justify-content: center; } -#particles-js { - position: fixed; + +#games, +#pinned { width: 100%; - height: 100%; - background-position: 50% 50%; - z-index: -127321; + justify-content: center; } + +a { + font-size: 16px; +} + +input[type="text"] { + width: 50%; + background-color: var(--inputbg); + box-sizing: border-box; + font-size: 20px; + padding: 0.8% 0.8%; + text-align: center; + border-radius: 5px; + outline: none; + margin: 10px; + border: 2px solid var(--inputborder); + border-radius: 5px; + transition: opacity 0.25s ease-in-out; + opacity: 100%; +} +input[type=text]:hover { + opacity: 80%; +} + +.bookmarkletdiv { + width: 30%; + background-color: var(--uibg); + box-shadow: 0px 0px 5px 5px var(--uibg); + padding: 5px; + margin: 15px; + border-radius: 10px; + display: inline-block; +} + +.bookmarkletdiv a { + font-size: 30px; +} + +form { + width: 100%; +} +.samerow.themebtns { + width: 70%; +} + +select { + border-radius: 5px; + background-color: var(--inputbg); + border-color: var(--inputborder); + border-width: 5px; + border-radius: 10px; + border-style: solid; + padding: 0.5% 0.5%; + margin: 0.25%; +} \ No newline at end of file diff --git a/subway-surfers-ny/NewYorkIcon.png b/subway-surfers-ny/NewYorkIcon.png new file mode 100644 index 00000000..185297c3 Binary files /dev/null and b/subway-surfers-ny/NewYorkIcon.png differ diff --git a/subway-surfers-ny/assets/audio/guard_catch.ogg b/subway-surfers-ny/assets/audio/guard_catch.ogg new file mode 100644 index 00000000..7ba35b52 Binary files /dev/null and b/subway-surfers-ny/assets/audio/guard_catch.ogg differ diff --git a/subway-surfers-ny/assets/audio/guard_proximity.ogg b/subway-surfers-ny/assets/audio/guard_proximity.ogg new file mode 100644 index 00000000..e032642c Binary files /dev/null and b/subway-surfers-ny/assets/audio/guard_proximity.ogg differ diff --git a/subway-surfers-ny/assets/audio/guard_start.ogg b/subway-surfers-ny/assets/audio/guard_start.ogg new file mode 100644 index 00000000..dd59ce1a Binary files /dev/null and b/subway-surfers-ny/assets/audio/guard_start.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_death.ogg b/subway-surfers-ny/assets/audio/hero_death.ogg new file mode 100644 index 00000000..84622137 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_death.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_death_hitcam.ogg b/subway-surfers-ny/assets/audio/hero_death_hitcam.ogg new file mode 100644 index 00000000..beffaf70 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_death_hitcam.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_dodge.ogg b/subway-surfers-ny/assets/audio/hero_dodge.ogg new file mode 100644 index 00000000..2cbd368e Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_dodge.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_foot_l.ogg b/subway-surfers-ny/assets/audio/hero_foot_l.ogg new file mode 100644 index 00000000..789457fa Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_foot_l.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_foot_r.ogg b/subway-surfers-ny/assets/audio/hero_foot_r.ogg new file mode 100644 index 00000000..6a708003 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_foot_r.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_hoverboard_crash.ogg b/subway-surfers-ny/assets/audio/hero_hoverboard_crash.ogg new file mode 100644 index 00000000..8c55f94a Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_hoverboard_crash.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_jump.ogg b/subway-surfers-ny/assets/audio/hero_jump.ogg new file mode 100644 index 00000000..a266e570 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_jump.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_revive.ogg b/subway-surfers-ny/assets/audio/hero_revive.ogg new file mode 100644 index 00000000..448309b3 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_revive.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_roll.ogg b/subway-surfers-ny/assets/audio/hero_roll.ogg new file mode 100644 index 00000000..efe7e394 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_roll.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_sneakers_foot_l.ogg b/subway-surfers-ny/assets/audio/hero_sneakers_foot_l.ogg new file mode 100644 index 00000000..5b17d906 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_sneakers_foot_l.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_sneakers_foot_r.ogg b/subway-surfers-ny/assets/audio/hero_sneakers_foot_r.ogg new file mode 100644 index 00000000..856e875b Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_sneakers_foot_r.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_sneakers_jump.ogg b/subway-surfers-ny/assets/audio/hero_sneakers_jump.ogg new file mode 100644 index 00000000..41f91da3 Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_sneakers_jump.ogg differ diff --git a/subway-surfers-ny/assets/audio/hero_stumble.ogg b/subway-surfers-ny/assets/audio/hero_stumble.ogg new file mode 100644 index 00000000..2b9ea27b Binary files /dev/null and b/subway-surfers-ny/assets/audio/hero_stumble.ogg differ diff --git a/subway-surfers-ny/assets/audio/pickup_coin.ogg b/subway-surfers-ny/assets/audio/pickup_coin.ogg new file mode 100644 index 00000000..09048fdb Binary files /dev/null and b/subway-surfers-ny/assets/audio/pickup_coin.ogg differ diff --git a/subway-surfers-ny/assets/audio/pickup_powerup.ogg b/subway-surfers-ny/assets/audio/pickup_powerup.ogg new file mode 100644 index 00000000..227ea648 Binary files /dev/null and b/subway-surfers-ny/assets/audio/pickup_powerup.ogg differ diff --git a/subway-surfers-ny/assets/audio/special_jetpack.ogg b/subway-surfers-ny/assets/audio/special_jetpack.ogg new file mode 100644 index 00000000..8aeaefcc Binary files /dev/null and b/subway-surfers-ny/assets/audio/special_jetpack.ogg differ diff --git a/subway-surfers-ny/assets/audio/special_jetpack_start.ogg b/subway-surfers-ny/assets/audio/special_jetpack_start.ogg new file mode 100644 index 00000000..b74fdd58 Binary files /dev/null and b/subway-surfers-ny/assets/audio/special_jetpack_start.ogg differ diff --git a/subway-surfers-ny/assets/audio/special_magnet.ogg b/subway-surfers-ny/assets/audio/special_magnet.ogg new file mode 100644 index 00000000..139f3dfc Binary files /dev/null and b/subway-surfers-ny/assets/audio/special_magnet.ogg differ diff --git a/subway-surfers-ny/assets/audio/theme.ogg b/subway-surfers-ny/assets/audio/theme.ogg new file mode 100644 index 00000000..0c1e01b4 Binary files /dev/null and b/subway-surfers-ny/assets/audio/theme.ogg differ diff --git a/subway-surfers-ny/assets/audio/ui_button.ogg b/subway-surfers-ny/assets/audio/ui_button.ogg new file mode 100644 index 00000000..2d3fb893 Binary files /dev/null and b/subway-surfers-ny/assets/audio/ui_button.ogg differ diff --git a/subway-surfers-ny/assets/data/chunks_game.json b/subway-surfers-ny/assets/data/chunks_game.json new file mode 100644 index 00000000..4c9c533a --- /dev/null +++ b/subway-surfers-ny/assets/data/chunks_game.json @@ -0,0 +1 @@ +{"routeChunk_default_b-s-b":{"name":"routeChunk_default_b-s-b","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_b-s-b","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_end","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":630},"$type":"TransformData"}},"children":[]},{"name":"ramp_and_egg","components":{"Transform":{"position":{"x":0,"y":0,"z":690},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":690},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":50.5,"z":720},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":50.5,"z":720},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"random_path","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":360},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":360},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":540},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"161"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"161"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"173"},"customOut":{"$id":"174"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"173"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"174"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"185"},"customOut":{"$id":"186"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"185"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"186"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"199"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"199"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"211"},"customOut":{"$id":"212"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"211"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"212"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"223"},"customOut":{"$id":"224"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"223"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"224"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"237"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurveParent":{"$ref":"237"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"249"},"customOut":{"$id":"250"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"249"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"250"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"261"},"customOut":{"$id":"262"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"261"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"262"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"275"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"275"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"287"},"customOut":{"$id":"288"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"287"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"288"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"299"},"customOut":{"$id":"300"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"299"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"300"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"313"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurveParent":{"$ref":"313"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"325"},"customOut":{"$id":"326"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"325"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"326"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"337"},"customOut":{"$id":"338"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"337"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"338"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"351"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"351"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"363"},"customOut":{"$id":"364"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"363"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"364"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"375"},"customOut":{"$id":"376"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"375"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"376"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":20,"y":7,"z":390},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":390},"$type":"TransformData"}},"children":[]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"403"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurveParent":{"$ref":"403"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"415"},"customOut":{"$id":"416"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"415"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"416"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"427"},"customOut":{"$id":"428"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"427"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"428"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"441"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurveParent":{"$ref":"441"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"453"},"customOut":{"$id":"454"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"453"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"454"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"465"},"customOut":{"$id":"466"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"465"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"466"}},"children":[]}]}]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"487"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurveParent":{"$ref":"487"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"499"},"customOut":{"$id":"500"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"499"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"500"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"511"},"customOut":{"$id":"512"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"511"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"512"}},"children":[]}]}]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"}},"children":[]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},"routeChunk_default_choice":{"name":"routeChunk_default_choice","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"18"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_choice","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"random_path","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"14"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":19.24548,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20.75452,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":19.24548,"y":0,"z":315},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"68"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"68"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"80"},"customOut":{"$id":"81"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"80"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"81"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":416},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"92"},"customOut":{"$id":"93"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":416},"$type":"TransformData"},"CurvePointTangent":{"$ref":"92"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20.75452,"y":29,"z":416},"$type":"TransformData"},"CurvePointTangent":{"$ref":"93"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"106"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"106"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"118"},"customOut":{"$id":"119"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"118"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"119"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":686},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"130"},"customOut":{"$id":"131"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":686},"$type":"TransformData"},"CurvePointTangent":{"$ref":"130"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":19.24548,"y":29,"z":686},"$type":"TransformData"},"CurvePointTangent":{"$ref":"131"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":19.24548,"y":37,"z":705},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":19.24548,"y":37,"z":705},"$type":"TransformData"}},"children":[]}]}]},{"name":"1","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"161"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"161"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"173"},"customOut":{"$id":"174"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"173"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"174"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":91},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"185"},"customOut":{"$id":"186"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"185"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"186"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"199"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurveParent":{"$ref":"199"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"211"},"customOut":{"$id":"212"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"211"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"212"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":121},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"223"},"customOut":{"$id":"224"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"223"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"224"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"237"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurveParent":{"$ref":"237"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"249"},"customOut":{"$id":"250"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"249"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"250"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"261"},"customOut":{"$id":"262"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"261"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"262"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"275"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"275"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"287"},"customOut":{"$id":"288"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"287"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"288"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"299"},"customOut":{"$id":"300"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"299"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"300"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":330},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":45},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":45},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":540},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"random_coins","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"18"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":126},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"386"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"386"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"398"},"customOut":{"$id":"399"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"398"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"399"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":291},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"410"},"customOut":{"$id":"411"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"410"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"411"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":126},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"424"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurveParent":{"$ref":"424"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"436"},"customOut":{"$id":"437"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"436"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"437"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":486},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"448"},"customOut":{"$id":"449"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":486},"$type":"TransformData"},"CurvePointTangent":{"$ref":"448"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":486},"$type":"TransformData"},"CurvePointTangent":{"$ref":"449"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":555},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":126},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"462"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":555},"$type":"TransformData"},"CurveParent":{"$ref":"462"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":555},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"474"},"customOut":{"$id":"475"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"474"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"475"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":681},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"486"},"customOut":{"$id":"487"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":681},"$type":"TransformData"},"CurvePointTangent":{"$ref":"486"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":681},"$type":"TransformData"},"CurvePointTangent":{"$ref":"487"}},"children":[]}]}]}]}]},{"name":"1","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"503"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"503"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"515"},"customOut":{"$id":"516"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"515"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"516"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"527"},"customOut":{"$id":"528"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"527"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"528"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"541"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"541"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"553"},"customOut":{"$id":"554"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"553"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"554"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"565"},"customOut":{"$id":"566"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"565"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"566"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"579"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"579"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"591"},"customOut":{"$id":"592"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"591"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"592"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":376},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"603"},"customOut":{"$id":"604"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"603"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"604"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"617"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurveParent":{"$ref":"617"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"629"},"customOut":{"$id":"630"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"629"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"630"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"641"},"customOut":{"$id":"642"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"641"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"642"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":495},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"655"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.33,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":495},"$type":"TransformData"},"CurveParent":{"$ref":"655"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":495},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"667"},"customOut":{"$id":"668"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"667"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":593.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"668"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":593.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"679"},"customOut":{"$id":"680"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"679"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":593.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"680"}},"children":[]}]}]}]}]}]},{"name":"random","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"19"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":540},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":20,"y":37,"z":705},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":37,"z":705},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":7,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":210},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":570},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_s-b-s-b":{"name":"routeChunk_default_s-b-s-b","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_s-b-s-b","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"48"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"48"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"60"},"customOut":{"$id":"61"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"60"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"61"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"72"},"customOut":{"$id":"73"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"72"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"73"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"86"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"86"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"98"},"customOut":{"$id":"99"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"98"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"99"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"110"},"customOut":{"$id":"111"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"110"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"111"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"124"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"124"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"136"},"customOut":{"$id":"137"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"136"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"137"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":211},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"148"},"customOut":{"$id":"149"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"148"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"149"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"162"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"162"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"174"},"customOut":{"$id":"175"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"174"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"175"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"186"},"customOut":{"$id":"187"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"186"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"187"}},"children":[]}]}]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"209"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurveParent":{"$ref":"209"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"221"},"customOut":{"$id":"222"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"222"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"233"},"customOut":{"$id":"234"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"233"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"234"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"247"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"247"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"259"},"customOut":{"$id":"260"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"260"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"271"},"customOut":{"$id":"272"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"271"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"272"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"285"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurveParent":{"$ref":"285"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"297"},"customOut":{"$id":"298"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"298"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"309"},"customOut":{"$id":"310"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"309"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"310"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"323"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"323"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"335"},"customOut":{"$id":"336"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"335"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"336"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"347"},"customOut":{"$id":"348"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"347"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"348"}},"children":[]}]}]}]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":480},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":35.8,"z":630},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":35.8,"z":630},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-30,"y":0,"z":690},"$type":"TransformData"}},"children":[]}]},{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"411"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurveParent":{"$ref":"411"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"423"},"customOut":{"$id":"424"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"423"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"424"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"435"},"customOut":{"$id":"436"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"435"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"436"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"449"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"449"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"461"},"customOut":{"$id":"462"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"461"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"462"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"473"},"customOut":{"$id":"474"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"473"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"474"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"487"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurveParent":{"$ref":"487"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"499"},"customOut":{"$id":"500"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"499"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"500"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"511"},"customOut":{"$id":"512"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"511"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"512"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"525"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurveParent":{"$ref":"525"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"537"},"customOut":{"$id":"538"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"537"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"538"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"549"},"customOut":{"$id":"550"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"549"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"550"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"563"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"CurveParent":{"$ref":"563"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"575"},"customOut":{"$id":"576"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"575"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"576"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":451},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"587"},"customOut":{"$id":"588"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"587"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"588"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"601"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"CurveParent":{"$ref":"601"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"613"},"customOut":{"$id":"614"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"613"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"614"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":481},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"625"},"customOut":{"$id":"626"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"625"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"626"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"639"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"},"CurveParent":{"$ref":"639"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"651"},"customOut":{"$id":"652"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"651"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"652"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":511},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"663"},"customOut":{"$id":"664"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":511},"$type":"TransformData"},"CurvePointTangent":{"$ref":"663"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":511},"$type":"TransformData"},"CurvePointTangent":{"$ref":"664"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"677"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"CurveParent":{"$ref":"677"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"689"},"customOut":{"$id":"690"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"689"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"690"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":541},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"701"},"customOut":{"$id":"702"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"701"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"702"}},"children":[]}]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":675},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":675},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_s-s-s-s":{"name":"routeChunk_default_s-s-s-s","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_s-s-s-s","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"29"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurveParent":{"$ref":"29"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"41"},"customOut":{"$id":"42"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"41"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"42"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"53"},"customOut":{"$id":"54"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"53"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"54"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"67"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"67"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"79"},"customOut":{"$id":"80"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"79"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"80"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"91"},"customOut":{"$id":"92"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"91"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"92"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"105"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurveParent":{"$ref":"105"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"117"},"customOut":{"$id":"118"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"117"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"118"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"129"},"customOut":{"$id":"130"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"129"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"130"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"143"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"143"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"155"},"customOut":{"$id":"156"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"155"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"156"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"167"},"customOut":{"$id":"168"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"167"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"168"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"181"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurveParent":{"$ref":"181"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"193"},"customOut":{"$id":"194"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"193"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"194"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"205"},"customOut":{"$id":"206"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"205"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"206"}},"children":[]}]}]}]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"234"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"234"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"246"},"customOut":{"$id":"247"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"246"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"247"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"258"},"customOut":{"$id":"259"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"258"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"272"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurveParent":{"$ref":"272"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"284"},"customOut":{"$id":"285"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"284"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"285"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"296"},"customOut":{"$id":"297"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"296"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"310"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"310"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"322"},"customOut":{"$id":"323"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"322"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"323"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"334"},"customOut":{"$id":"335"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"334"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"335"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"348"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"348"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"360"},"customOut":{"$id":"361"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"360"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"361"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":211},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"372"},"customOut":{"$id":"373"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"372"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"373"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"386"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"386"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"398"},"customOut":{"$id":"399"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"398"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"399"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"410"},"customOut":{"$id":"411"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"410"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"411"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"424"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"424"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"436"},"customOut":{"$id":"437"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"436"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"437"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"448"},"customOut":{"$id":"449"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"448"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"449"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"462"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"462"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"474"},"customOut":{"$id":"475"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"474"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"475"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"486"},"customOut":{"$id":"487"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"486"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"487"}},"children":[]}]}]}]}]},{"name":"random_path","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":450},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":37,"z":495},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":37,"z":495},"$type":"TransformData"}},"children":[]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":525},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"550"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":525},"$type":"TransformData"},"CurveParent":{"$ref":"550"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":525},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"562"},"customOut":{"$id":"563"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"562"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":623.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"563"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":623.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"574"},"customOut":{"$id":"575"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"574"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":623.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"575"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"}},"children":[]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"600"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurveParent":{"$ref":"600"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"612"},"customOut":{"$id":"613"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"612"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"613"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"624"},"customOut":{"$id":"625"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"624"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"625"}},"children":[]}]}]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"647"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurveParent":{"$ref":"647"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"659"},"customOut":{"$id":"660"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"659"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"660"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"671"},"customOut":{"$id":"672"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"671"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"672"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"685"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurveParent":{"$ref":"685"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"697"},"customOut":{"$id":"698"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"697"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"},"CurvePointTangent":{"$ref":"698"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"709"},"customOut":{"$id":"710"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"709"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":481},"$type":"TransformData"},"CurvePointTangent":{"$ref":"710"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"723"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurveParent":{"$ref":"723"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"735"},"customOut":{"$id":"736"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"735"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"736"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":511},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"747"},"customOut":{"$id":"748"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":511},"$type":"TransformData"},"CurvePointTangent":{"$ref":"747"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":511},"$type":"TransformData"},"CurvePointTangent":{"$ref":"748"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"761"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"},"CurveParent":{"$ref":"761"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"773"},"customOut":{"$id":"774"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"773"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"774"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":541},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"785"},"customOut":{"$id":"786"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"785"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"786"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":570},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"799"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":570},"$type":"TransformData"},"CurveParent":{"$ref":"799"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":570},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"811"},"customOut":{"$id":"812"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"811"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"812"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":571},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"823"},"customOut":{"$id":"824"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":571},"$type":"TransformData"},"CurvePointTangent":{"$ref":"823"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":571},"$type":"TransformData"},"CurvePointTangent":{"$ref":"824"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"837"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"837"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"849"},"customOut":{"$id":"850"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"849"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"850"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"861"},"customOut":{"$id":"862"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"861"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"862"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":600},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"875"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":600},"$type":"TransformData"},"CurveParent":{"$ref":"875"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":600},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"887"},"customOut":{"$id":"888"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":600},"$type":"TransformData"},"CurvePointTangent":{"$ref":"887"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":600},"$type":"TransformData"},"CurvePointTangent":{"$ref":"888"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":601},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"899"},"customOut":{"$id":"900"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":601},"$type":"TransformData"},"CurvePointTangent":{"$ref":"899"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":601},"$type":"TransformData"},"CurvePointTangent":{"$ref":"900"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"913"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"},"CurveParent":{"$ref":"913"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"925"},"customOut":{"$id":"926"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"},"CurvePointTangent":{"$ref":"925"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"},"CurvePointTangent":{"$ref":"926"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":631},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"937"},"customOut":{"$id":"938"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":631},"$type":"TransformData"},"CurvePointTangent":{"$ref":"937"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":631},"$type":"TransformData"},"CurvePointTangent":{"$ref":"938"}},"children":[]}]}]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":660},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":675},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":675},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":7,"z":375},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":375},"$type":"TransformData"}},"children":[]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},"routeChunk_default_s-s":{"name":"routeChunk_default_s-s","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_s-s","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":75},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"25"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":75},"$type":"TransformData"},"CurveParent":{"$ref":"25"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":75},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"37"},"customOut":{"$id":"38"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"37"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"38"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":76},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"49"},"customOut":{"$id":"50"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":76},"$type":"TransformData"},"CurvePointTangent":{"$ref":"49"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":76},"$type":"TransformData"},"CurvePointTangent":{"$ref":"50"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"63"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"63"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"75"},"customOut":{"$id":"76"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"75"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"76"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":91},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"87"},"customOut":{"$id":"88"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"87"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"88"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"101"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"CurveParent":{"$ref":"101"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"113"},"customOut":{"$id":"114"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"113"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"114"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":121},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"125"},"customOut":{"$id":"126"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"125"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":121},"$type":"TransformData"},"CurvePointTangent":{"$ref":"126"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"139"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"139"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"151"},"customOut":{"$id":"152"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"151"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"152"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"163"},"customOut":{"$id":"164"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"163"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"164"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"198"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"198"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"210"},"customOut":{"$id":"211"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"210"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"211"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":541},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"222"},"customOut":{"$id":"223"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"222"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"223"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":26.2,"z":570},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":26.2,"z":570},"$type":"TransformData"}},"children":[]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":660},"$type":"TransformData"}},"children":[]}]},{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"262"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurveParent":{"$ref":"262"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"274"},"customOut":{"$id":"275"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"274"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"CurvePointTangent":{"$ref":"275"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"286"},"customOut":{"$id":"287"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"286"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":61},"$type":"TransformData"},"CurvePointTangent":{"$ref":"287"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"300"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurveParent":{"$ref":"300"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"312"},"customOut":{"$id":"313"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"312"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"313"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"324"},"customOut":{"$id":"325"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"324"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":151},"$type":"TransformData"},"CurvePointTangent":{"$ref":"325"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"338"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"338"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"350"},"customOut":{"$id":"351"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"350"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"351"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"362"},"customOut":{"$id":"363"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"362"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"363"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"376"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"376"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"388"},"customOut":{"$id":"389"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"388"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"389"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"400"},"customOut":{"$id":"401"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"400"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"401"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"414"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"414"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"426"},"customOut":{"$id":"427"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"426"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"427"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"438"},"customOut":{"$id":"439"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"438"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"439"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"452"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurveParent":{"$ref":"452"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"464"},"customOut":{"$id":"465"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"464"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"465"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"476"},"customOut":{"$id":"477"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"476"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"477"}},"children":[]}]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":630},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":true,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":630},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-30,"y":0,"z":690},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_train_tops_1":{"name":"routeChunk_default_train_tops_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":10,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_train_tops_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_end","components":{"Transform":{"position":{"x":0,"y":0,"z":-900},"$type":"TransformData"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":-900},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":75},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"39"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":75},"$type":"TransformData"},"CurveParent":{"$ref":"39"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":75},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"51"},"customOut":{"$id":"52"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"51"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"52"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":76},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"63"},"customOut":{"$id":"64"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":76},"$type":"TransformData"},"CurvePointTangent":{"$ref":"63"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":76},"$type":"TransformData"},"CurvePointTangent":{"$ref":"64"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"77"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"77"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"89"},"customOut":{"$id":"90"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"89"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"90"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":106},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"101"},"customOut":{"$id":"102"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":106},"$type":"TransformData"},"CurvePointTangent":{"$ref":"101"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":106},"$type":"TransformData"},"CurvePointTangent":{"$ref":"102"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"124"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"124"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"136"},"customOut":{"$id":"137"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"136"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"137"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"148"},"customOut":{"$id":"149"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"148"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"149"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"173"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"173"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"185"},"customOut":{"$id":"186"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"185"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"186"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"197"},"customOut":{"$id":"198"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"197"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"198"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"211"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"211"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"223"},"customOut":{"$id":"224"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"223"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"224"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"235"},"customOut":{"$id":"236"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"235"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"236"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"249"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"249"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"261"},"customOut":{"$id":"262"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"261"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"262"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"273"},"customOut":{"$id":"274"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"273"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"274"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"287"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"287"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"299"},"customOut":{"$id":"300"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"299"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"300"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"311"},"customOut":{"$id":"312"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"311"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"312"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"325"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"325"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"337"},"customOut":{"$id":"338"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"337"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"338"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"349"},"customOut":{"$id":"350"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"349"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"350"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"363"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"363"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"375"},"customOut":{"$id":"376"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"375"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"376"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"387"},"customOut":{"$id":"388"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"387"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"388"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"409"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"409"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"421"},"customOut":{"$id":"422"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"421"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"422"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"433"},"customOut":{"$id":"434"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"433"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"434"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"}},"children":[]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":-900},"$type":"TransformData"},"Randomizer":{"$ref":"13"}},"children":[{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":-900},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"489"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.84,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurveParent":{"$ref":"489"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"501"},"customOut":{"$id":"502"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"501"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":638.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"502"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":638.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"513"},"customOut":{"$id":"514"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"513"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":638.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"514"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"527"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"527"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"539"},"customOut":{"$id":"540"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"539"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":683.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"540"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":683.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"551"},"customOut":{"$id":"552"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"551"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":683.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"552"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":660},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":-900},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"598"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.82,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurveParent":{"$ref":"598"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"610"},"customOut":{"$id":"611"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"610"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":638.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"611"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":638.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"622"},"customOut":{"$id":"623"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":540},"$type":"TransformData"},"CurvePointTangent":{"$ref":"622"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":638.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"623"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":780},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"644"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.85,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":780},"$type":"TransformData"},"CurveParent":{"$ref":"644"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":780},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"656"},"customOut":{"$id":"657"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"656"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":878.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"657"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":878.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"668"},"customOut":{"$id":"669"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"668"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":878.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"669"}},"children":[]}]}]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":35.8,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":35.8,"z":210},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":570},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":570},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":630},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":720},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":720},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":47,"z":855},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":47,"z":855},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_train_tops_2":{"name":"routeChunk_default_train_tops_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":10,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_train_tops_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_trains","components":{"Transform":{"position":{"x":0,"y":0,"z":-30},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"trains_mid2left","components":{"Transform":{"position":{"x":0,"y":0,"z":-30},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":29,"z":-30},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"45"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"45"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"57"},"customOut":{"$id":"58"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"57"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"58"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"69"},"customOut":{"$id":"70"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"69"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"70"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":29,"z":765},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"83"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":765},"$type":"TransformData"},"CurveParent":{"$ref":"83"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":765},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"95"},"customOut":{"$id":"96"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"95"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"96"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"107"},"customOut":{"$id":"108"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"107"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"108"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":29,"z":795},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"121"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":795},"$type":"TransformData"},"CurveParent":{"$ref":"121"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":795},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"133"},"customOut":{"$id":"134"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":795},"$type":"TransformData"},"CurvePointTangent":{"$ref":"133"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":795},"$type":"TransformData"},"CurvePointTangent":{"$ref":"134"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":796},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"145"},"customOut":{"$id":"146"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":796},"$type":"TransformData"},"CurvePointTangent":{"$ref":"145"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":796},"$type":"TransformData"},"CurvePointTangent":{"$ref":"146"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":29,"z":825},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"159"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":825},"$type":"TransformData"},"CurveParent":{"$ref":"159"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":825},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"171"},"customOut":{"$id":"172"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":825},"$type":"TransformData"},"CurvePointTangent":{"$ref":"171"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":825},"$type":"TransformData"},"CurvePointTangent":{"$ref":"172"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":826},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"183"},"customOut":{"$id":"184"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":826},"$type":"TransformData"},"CurvePointTangent":{"$ref":"183"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":826},"$type":"TransformData"},"CurvePointTangent":{"$ref":"184"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":29,"z":855},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"197"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":855},"$type":"TransformData"},"CurveParent":{"$ref":"197"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":855},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"209"},"customOut":{"$id":"210"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"210"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":856},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"221"},"customOut":{"$id":"222"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"222"}},"children":[]}]}]}]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":-30},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":420},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":570},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"259"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":570},"$type":"TransformData"},"CurveParent":{"$ref":"259"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":570},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"271"},"customOut":{"$id":"272"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"271"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":668.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"272"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":668.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"283"},"customOut":{"$id":"284"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"283"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":668.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"284"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":780},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":37,"z":165},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":37,"z":165},"$type":"TransformData"}},"children":[]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":405},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"357"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":405},"$type":"TransformData"},"CurveParent":{"$ref":"357"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":405},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"369"},"customOut":{"$id":"370"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"369"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":503.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"370"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":503.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"381"},"customOut":{"$id":"382"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"381"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":503.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"382"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":690},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":855},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":855},"$type":"TransformData"}},"children":[]}]}]},{"name":"trains_sides2mid","components":{"Transform":{"position":{"x":0,"y":0,"z":-30},"$type":"TransformData"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":-30},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":150},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"462"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"462"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"474"},"customOut":{"$id":"475"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"474"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"475"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"486"},"customOut":{"$id":"487"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"486"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"487"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"500"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"500"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"512"},"customOut":{"$id":"513"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"512"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"513"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"524"},"customOut":{"$id":"525"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"524"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"525"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"538"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"538"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"550"},"customOut":{"$id":"551"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"550"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"551"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"562"},"customOut":{"$id":"563"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"562"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"563"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"576"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"576"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"588"},"customOut":{"$id":"589"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"588"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"589"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"600"},"customOut":{"$id":"601"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"600"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"601"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"614"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"614"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"626"},"customOut":{"$id":"627"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"626"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"627"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"638"},"customOut":{"$id":"639"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"638"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"639"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"652"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"652"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"664"},"customOut":{"$id":"665"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"664"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"665"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"676"},"customOut":{"$id":"677"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"676"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"677"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":420},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":420},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":37,"z":450},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":37,"z":450},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":600},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":660},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"748"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":660},"$type":"TransformData"},"CurveParent":{"$ref":"748"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":660},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"760"},"customOut":{"$id":"761"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"760"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":758.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"761"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":758.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"772"},"customOut":{"$id":"773"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"772"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":758.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"773"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":690},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":780},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":55,"z":870},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":55,"z":870},"$type":"TransformData"}},"children":[]}]}]}]}]},"routeChunk_default_tunnel_notrain":{"name":"routeChunk_default_tunnel_notrain","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Gates,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"7"},{"$id":"8"},{"$id":"9"},{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_tunnel_notrain","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_tunnel","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"7"}},"children":[{"name":"mid","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_mid_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]}]},{"name":"left","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_left_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":64.60001,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":64.60001,"z":210},"$type":"TransformData"}},"children":[]}]}]},{"name":"sides","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_sides_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":75},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"86"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":75},"$type":"TransformData"},"CurveParent":{"$ref":"86"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":75},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"98"},"customOut":{"$id":"99"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"98"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"99"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"110"},"customOut":{"$id":"111"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"110"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"111"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":195},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":195},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"right","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_right_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":30},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":64.60001,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":64.60001,"z":210},"$type":"TransformData"}},"children":[]}]}]}]}]},"Short2Tracks":{"name":"Short2Tracks","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"routeChunk_default_short_2_tracks_end","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_2_tracks_end","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":135},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":135},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[]}]},{"name":"routeChunk_default_short_2_tracks_mid_var_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":10,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"45"},{"$id":"46"},{"$id":"47"},{"$id":"48"},{"$id":"49"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_2_tracks_mid_var_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"pillars_short","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":38},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"70"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"70"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"82"},"customOut":{"$id":"83"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"82"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"83"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"94"},"customOut":{"$id":"95"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"94"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"95"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"108"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"108"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"120"},"customOut":{"$id":"121"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"120"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"121"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"132"},"customOut":{"$id":"133"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"132"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"133"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"146"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"146"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"158"},"customOut":{"$id":"159"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"158"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"159"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"170"},"customOut":{"$id":"171"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"170"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"171"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"184"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"184"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"196"},"customOut":{"$id":"197"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"196"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"197"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"208"},"customOut":{"$id":"209"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"208"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"222"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"222"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"234"},"customOut":{"$id":"235"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"234"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"235"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"246"},"customOut":{"$id":"247"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"246"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"247"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":397},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":410},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"279"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"279"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"291"},"customOut":{"$id":"292"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"291"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"292"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"303"},"customOut":{"$id":"304"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"303"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"304"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"317"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurveParent":{"$ref":"317"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"329"},"customOut":{"$id":"330"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"329"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"330"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"341"},"customOut":{"$id":"342"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePointTangent":{"$ref":"341"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePointTangent":{"$ref":"342"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"355"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurveParent":{"$ref":"355"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"367"},"customOut":{"$id":"368"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"367"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"368"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"379"},"customOut":{"$id":"380"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePointTangent":{"$ref":"379"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePointTangent":{"$ref":"380"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"393"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurveParent":{"$ref":"393"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"405"},"customOut":{"$id":"406"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"405"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"406"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"417"},"customOut":{"$id":"418"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"417"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"418"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"431"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurveParent":{"$ref":"431"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"443"},"customOut":{"$id":"444"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"443"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"444"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"455"},"customOut":{"$id":"456"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"455"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"456"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"469"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"469"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"481"},"customOut":{"$id":"482"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"481"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"482"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"493"},"customOut":{"$id":"494"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"493"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"494"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":756},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":770},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"525"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"525"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"537"},"customOut":{"$id":"538"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"537"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"538"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"549"},"customOut":{"$id":"550"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"549"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"550"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"563"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"563"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"575"},"customOut":{"$id":"576"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"575"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"576"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"587"},"customOut":{"$id":"588"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"587"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"588"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"601"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"601"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"613"},"customOut":{"$id":"614"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"613"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"614"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"625"},"customOut":{"$id":"626"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"625"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"626"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"639"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurveParent":{"$ref":"639"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"651"},"customOut":{"$id":"652"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePointTangent":{"$ref":"651"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePointTangent":{"$ref":"652"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"663"},"customOut":{"$id":"664"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePointTangent":{"$ref":"663"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePointTangent":{"$ref":"664"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"677"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurveParent":{"$ref":"677"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"689"},"customOut":{"$id":"690"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePointTangent":{"$ref":"689"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePointTangent":{"$ref":"690"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"701"},"customOut":{"$id":"702"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePointTangent":{"$ref":"701"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePointTangent":{"$ref":"702"}},"children":[]}]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"pillars_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"pillars_environment (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"pillars_environment (2)","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":10,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]}]},{"name":"routeChunk_default_short_2_tracks_mid_var_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":10,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"741"},{"$id":"742"},{"$id":"743"},{"$id":"744"},{"$id":"745"},{"$id":"746"},{"$id":"747"},{"$id":"748"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_2_tracks_mid_var_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"pillars_long","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"roof_setup","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"subway","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"roof","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"772"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"772"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"784"},"customOut":{"$id":"785"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"784"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"785"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"796"},"customOut":{"$id":"797"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"796"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"797"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"810"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"810"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"822"},"customOut":{"$id":"823"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"822"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"823"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"834"},"customOut":{"$id":"835"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"834"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"835"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"848"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurveParent":{"$ref":"848"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"860"},"customOut":{"$id":"861"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"860"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"861"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"872"},"customOut":{"$id":"873"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"872"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"873"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"886"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurveParent":{"$ref":"886"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"898"},"customOut":{"$id":"899"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"898"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"899"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"910"},"customOut":{"$id":"911"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"910"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"911"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":251},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"942"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"942"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"954"},"customOut":{"$id":"955"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"954"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"955"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"966"},"customOut":{"$id":"967"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"966"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"967"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"980"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"980"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"992"},"customOut":{"$id":"993"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"992"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"993"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1004"},"customOut":{"$id":"1005"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1004"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1005"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1018"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"1018"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1030"},"customOut":{"$id":"1031"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1030"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1031"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1042"},"customOut":{"$id":"1043"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1042"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1043"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1056"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurveParent":{"$ref":"1056"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1068"},"customOut":{"$id":"1069"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1068"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1069"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1080"},"customOut":{"$id":"1081"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1080"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1081"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":431},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1112"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurveParent":{"$ref":"1112"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1124"},"customOut":{"$id":"1125"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1124"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1125"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1136"},"customOut":{"$id":"1137"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1136"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1137"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1150"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurveParent":{"$ref":"1150"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1162"},"customOut":{"$id":"1163"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1162"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1163"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1174"},"customOut":{"$id":"1175"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1174"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1175"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1188"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurveParent":{"$ref":"1188"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1200"},"customOut":{"$id":"1201"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1200"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1201"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1212"},"customOut":{"$id":"1213"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1212"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1213"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1226"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"1226"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1238"},"customOut":{"$id":"1239"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1238"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1239"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1250"},"customOut":{"$id":"1251"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1250"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1251"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":611},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1282"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurveParent":{"$ref":"1282"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1294"},"customOut":{"$id":"1295"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1294"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1295"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1306"},"customOut":{"$id":"1307"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1306"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1307"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1320"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurveParent":{"$ref":"1320"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1332"},"customOut":{"$id":"1333"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1332"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1333"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1344"},"customOut":{"$id":"1345"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1344"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1345"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1358"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"1358"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1370"},"customOut":{"$id":"1371"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1370"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1371"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1382"},"customOut":{"$id":"1383"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1382"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1383"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1396"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurveParent":{"$ref":"1396"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1408"},"customOut":{"$id":"1409"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1408"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1409"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1420"},"customOut":{"$id":"1421"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1420"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1421"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":791},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":810},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1452"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurveParent":{"$ref":"1452"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1464"},"customOut":{"$id":"1465"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1464"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1465"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1476"},"customOut":{"$id":"1477"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1476"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1477"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1490"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"1490"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1502"},"customOut":{"$id":"1503"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1502"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1503"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1514"},"customOut":{"$id":"1515"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1514"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1515"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1528"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"1528"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1540"},"customOut":{"$id":"1541"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1540"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1541"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1552"},"customOut":{"$id":"1553"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1552"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1553"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1566"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"1566"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1578"},"customOut":{"$id":"1579"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1578"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1579"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1590"},"customOut":{"$id":"1591"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1590"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1591"}},"children":[]}]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"station_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":8,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":10,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]}]},{"name":"routeChunk_default_short_2_tracks_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"1618"},{"$id":"1619"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_2_tracks_start","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":7,"z":165},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":165},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_ramp_2":{"name":"routeChunk_default_ramp_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_ramp_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":30},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":35.8,"z":120},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":35.8,"z":120},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":36,"z":165},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":36,"z":165},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_short_1_track":{"name":"routeChunk_default_short_1_track","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":14,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"7"},{"$id":"8"},{"$id":"9"},{"$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_1_track","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"random_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"35"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"35"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"47"},"customOut":{"$id":"48"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"47"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"48"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"59"},"customOut":{"$id":"60"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"59"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"60"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"73"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"73"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"85"},"customOut":{"$id":"86"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"85"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"86"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"97"},"customOut":{"$id":"98"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"97"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"98"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"111"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"111"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"123"},"customOut":{"$id":"124"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"123"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"124"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"135"},"customOut":{"$id":"136"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"135"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"136"}},"children":[]}]}]}]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"152"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"152"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"164"},"customOut":{"$id":"165"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"164"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"165"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"176"},"customOut":{"$id":"177"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"176"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"177"}},"children":[]}]}]}]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"193"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"193"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"205"},"customOut":{"$id":"206"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"205"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"206"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"217"},"customOut":{"$id":"218"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"217"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"218"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":275},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"231"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"231"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"243"},"customOut":{"$id":"244"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"243"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":8.8,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"244"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"255"},"customOut":{"$id":"256"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"255"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":8.8,"z":860},"$type":"TransformData"},"CurvePointTangent":{"$ref":"256"}},"children":[]}]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":323},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"station_platforms_extended_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"}},"children":[]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":958},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"end_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":990},"$type":"TransformData"}},"children":[{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"311"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"},"CurveParent":{"$ref":"311"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"323"},"customOut":{"$id":"324"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"323"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":80,"z":1178.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"324"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1178.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"335"},"customOut":{"$id":"336"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":80,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"335"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1178.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"336"}},"children":[]}]}]}]},{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"356"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"},"CurveParent":{"$ref":"356"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"368"},"customOut":{"$id":"369"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"368"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":80,"z":1208.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"369"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":1208.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"380"},"customOut":{"$id":"381"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":80,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"380"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":1208.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"381"}},"children":[]}]}]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"station_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Station,All","_density":""},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"start","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":0,"y":0,"z":-180.6776},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":29.32239},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"410"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":29.32239},"$type":"TransformData"},"CurveParent":{"$ref":"410"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":29.32239},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"422"},"customOut":{"$id":"423"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":29.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"422"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":29.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"423"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":30.32239},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"434"},"customOut":{"$id":"435"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":30.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"434"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":30.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"435"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":59.32239},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"448"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":59.32239},"$type":"TransformData"},"CurveParent":{"$ref":"448"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":59.32239},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"460"},"customOut":{"$id":"461"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":59.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"460"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":59.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"461"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":60.32239},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"472"},"customOut":{"$id":"473"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":60.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"472"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":60.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"473"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":89.32239},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"486"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":89.32239},"$type":"TransformData"},"CurveParent":{"$ref":"486"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":89.32239},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"498"},"customOut":{"$id":"499"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":89.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"498"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":89.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"499"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":90.32239},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"510"},"customOut":{"$id":"511"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":90.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"510"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":90.32239},"$type":"TransformData"},"CurvePointTangent":{"$ref":"511"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":119.3224},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":119.3224},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"533"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":119.3224},"$type":"TransformData"},"CurveParent":{"$ref":"533"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":119.3224},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"545"},"customOut":{"$id":"546"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":119.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"545"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":119.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"546"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":120.3224},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"557"},"customOut":{"$id":"558"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":120.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"557"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":120.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"558"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":149.3224},"$type":"TransformData"}},"children":[]},{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":329.3224},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":0,"z":329.3224},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"586"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":329.3224},"$type":"TransformData"},"CurveParent":{"$ref":"586"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":329.3224},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"598"},"customOut":{"$id":"599"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":329.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"598"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":80,"z":427.7094},"$type":"TransformData"},"CurvePointTangent":{"$ref":"599"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":427.7094},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"610"},"customOut":{"$id":"611"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":80,"z":329.3224},"$type":"TransformData"},"CurvePointTangent":{"$ref":"610"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":427.7094},"$type":"TransformData"},"CurvePointTangent":{"$ref":"611"}},"children":[]}]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":60},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":60},"$type":"TransformData"}},"children":[]}]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":1,"_blockCount":12,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":26.2,"z":495},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":26.2,"z":495},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1260},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_short_train_tops_moving":{"name":"routeChunk_default_short_train_tops_moving","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":12,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Epic,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"7"},{"$id":"8"},{"$id":"9"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"},{"$id":"12"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_train_tops_moving","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":46},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":20,"y":0,"z":75.28589},"$type":"TransformData"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":20,"y":0,"z":-59.71411},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":75.28589},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":135.2859},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"62"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurveParent":{"$ref":"62"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"74"},"customOut":{"$id":"75"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"74"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":278.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"75"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":278.6729},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"86"},"customOut":{"$id":"87"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":180.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"86"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":278.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"87"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":255.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":75.28589},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":135.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":375.2859},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":375.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"132"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":375.2859},"$type":"TransformData"},"CurveParent":{"$ref":"132"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":375.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"144"},"customOut":{"$id":"145"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":375.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"144"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":473.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"145"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":473.6729},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"156"},"customOut":{"$id":"157"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":375.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"156"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":473.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"157"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":75.28589},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":20,"y":29,"z":75.28589},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":150.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"176"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":150.2859},"$type":"TransformData"},"CurveParent":{"$ref":"176"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":150.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"188"},"customOut":{"$id":"189"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":150.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"188"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":150.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"189"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":151.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"200"},"customOut":{"$id":"201"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":151.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"200"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":151.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"201"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"214"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurveParent":{"$ref":"214"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"226"},"customOut":{"$id":"227"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"226"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":180.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"227"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":181.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"238"},"customOut":{"$id":"239"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":181.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"238"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":181.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"239"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":210.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"252"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":210.2859},"$type":"TransformData"},"CurveParent":{"$ref":"252"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":210.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"264"},"customOut":{"$id":"265"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":210.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"264"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":210.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"265"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":211.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"276"},"customOut":{"$id":"277"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":211.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"276"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":211.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"277"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":240.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"290"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":240.2859},"$type":"TransformData"},"CurveParent":{"$ref":"290"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":240.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"302"},"customOut":{"$id":"303"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":240.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"302"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":240.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"303"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":241.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"314"},"customOut":{"$id":"315"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":241.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"314"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":241.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"315"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":270.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"328"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":270.2859},"$type":"TransformData"},"CurveParent":{"$ref":"328"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":270.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"340"},"customOut":{"$id":"341"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":270.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"340"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":270.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"341"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":271.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"352"},"customOut":{"$id":"353"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":271.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"352"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":271.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"353"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":300.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"366"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":300.2859},"$type":"TransformData"},"CurveParent":{"$ref":"366"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":300.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"378"},"customOut":{"$id":"379"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":300.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"378"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":300.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"379"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":301.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"390"},"customOut":{"$id":"391"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":301.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"390"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":301.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"391"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":330.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"404"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":330.2859},"$type":"TransformData"},"CurveParent":{"$ref":"404"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":330.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"416"},"customOut":{"$id":"417"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":330.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"416"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":330.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"417"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":331.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"428"},"customOut":{"$id":"429"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":331.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"428"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":331.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"429"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":360.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"442"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":360.2859},"$type":"TransformData"},"CurveParent":{"$ref":"442"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":360.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"454"},"customOut":{"$id":"455"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":360.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"454"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":360.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"455"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":361.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"466"},"customOut":{"$id":"467"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":361.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"466"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":361.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"467"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":390.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"480"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":390.2859},"$type":"TransformData"},"CurveParent":{"$ref":"480"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":390.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"492"},"customOut":{"$id":"493"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":390.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"492"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":390.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"493"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":391.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"504"},"customOut":{"$id":"505"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":391.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"504"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":391.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"505"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":135.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":105.2859},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":225.2859},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":401},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":-20,"y":0,"z":495.2859},"$type":"TransformData"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":0,"z":360.2859},"$type":"TransformData"},"Randomizer":{"$ref":"13"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":495.2859},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":555.2859},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"574"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurveParent":{"$ref":"574"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"586"},"customOut":{"$id":"587"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"586"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":698.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"587"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":698.6729},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"598"},"customOut":{"$id":"599"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":600.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"598"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":698.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"599"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":675.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":495.2859},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":555.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":795.2859},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":795.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"643"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":795.2859},"$type":"TransformData"},"CurveParent":{"$ref":"643"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":795.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"655"},"customOut":{"$id":"656"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":795.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"655"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":893.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"656"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":893.6729},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"667"},"customOut":{"$id":"668"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":795.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"667"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":893.6729},"$type":"TransformData"},"CurvePointTangent":{"$ref":"668"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":495.2859},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":29,"z":495.2859},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":570.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"687"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":570.2859},"$type":"TransformData"},"CurveParent":{"$ref":"687"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":570.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"699"},"customOut":{"$id":"700"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":570.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"699"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":570.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"700"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":571.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"711"},"customOut":{"$id":"712"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":571.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"711"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":571.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"712"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"725"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurveParent":{"$ref":"725"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"737"},"customOut":{"$id":"738"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"737"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":600.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"738"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":601.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"749"},"customOut":{"$id":"750"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":601.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"749"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":601.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"750"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":630.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"763"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":630.2859},"$type":"TransformData"},"CurveParent":{"$ref":"763"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":630.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"775"},"customOut":{"$id":"776"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":630.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"775"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":630.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"776"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":631.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"787"},"customOut":{"$id":"788"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":631.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"787"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":631.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"788"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"801"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":660.2859},"$type":"TransformData"},"CurveParent":{"$ref":"801"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"813"},"customOut":{"$id":"814"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":660.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"813"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":660.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"814"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":661.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"825"},"customOut":{"$id":"826"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":661.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"825"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":661.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"826"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":690.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"839"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":690.2859},"$type":"TransformData"},"CurveParent":{"$ref":"839"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":690.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"851"},"customOut":{"$id":"852"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":690.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"851"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":690.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"852"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":691.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"863"},"customOut":{"$id":"864"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":691.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"863"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":691.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"864"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":720.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"877"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":720.2859},"$type":"TransformData"},"CurveParent":{"$ref":"877"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":720.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"889"},"customOut":{"$id":"890"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":720.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"889"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":720.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"890"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":721.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"901"},"customOut":{"$id":"902"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":721.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"901"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":721.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"902"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":750.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"915"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":750.2859},"$type":"TransformData"},"CurveParent":{"$ref":"915"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":750.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"927"},"customOut":{"$id":"928"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":750.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"927"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":750.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"928"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":751.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"939"},"customOut":{"$id":"940"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":751.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"939"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":751.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"940"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":780.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"953"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":780.2859},"$type":"TransformData"},"CurveParent":{"$ref":"953"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":780.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"965"},"customOut":{"$id":"966"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":780.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"965"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":780.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"966"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":781.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"977"},"customOut":{"$id":"978"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":781.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"977"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":781.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"978"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":810.2859},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"991"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":810.2859},"$type":"TransformData"},"CurveParent":{"$ref":"991"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":810.2859},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1003"},"customOut":{"$id":"1004"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":810.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1003"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":810.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1004"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":811.2859},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1015"},"customOut":{"$id":"1016"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":811.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1015"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":811.2859},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1016"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":555.2859},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":525.2859},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":615.2859},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":7,"z":570},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":570},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":820},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":20,"y":0,"z":915.2859},"$type":"TransformData"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":975.2859},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_short_train_tops_moving_combined":{"name":"routeChunk_default_short_train_tops_moving_combined","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":12,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$id":"37"},{"$id":"38"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_train_tops_moving_combined","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_start_end","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"101"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"101"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"113"},"customOut":{"$id":"114"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"113"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"114"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1066},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"125"},"customOut":{"$id":"126"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"125"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"126"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":960},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":55,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":55,"z":210},"$type":"TransformData"}},"children":[]}]},{"name":"side","components":{"Transform":{"position":{"x":0,"y":0,"z":395.3984},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"trains_side_ground","components":{"Transform":{"position":{"x":0,"y":0,"z":395.3984},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":515.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":515.3984},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":665.3984},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":695.3984},"$type":"TransformData"}},"children":[]}]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":0,"z":395.3984},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":0,"z":260.3984},"$type":"TransformData"},"Randomizer":{"$ref":"23"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":455.3984},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"225"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurveParent":{"$ref":"225"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"237"},"customOut":{"$id":"238"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"237"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":598.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"238"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":598.7854},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"249"},"customOut":{"$id":"250"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"249"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":598.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"250"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":575.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":455.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":695.3984},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":695.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"294"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":695.3984},"$type":"TransformData"},"CurveParent":{"$ref":"294"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":695.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"306"},"customOut":{"$id":"307"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":695.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"306"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":793.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"307"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":793.7854},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"318"},"customOut":{"$id":"319"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":695.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"318"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":793.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"319"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":29,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":470.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"338"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":470.3984},"$type":"TransformData"},"CurveParent":{"$ref":"338"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":470.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"350"},"customOut":{"$id":"351"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":470.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"350"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":470.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"351"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":471.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"362"},"customOut":{"$id":"363"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":471.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"362"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":471.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"363"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"376"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurveParent":{"$ref":"376"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"388"},"customOut":{"$id":"389"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"388"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"389"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":501.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"400"},"customOut":{"$id":"401"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":501.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"400"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":501.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"401"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":530.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"414"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":530.3984},"$type":"TransformData"},"CurveParent":{"$ref":"414"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":530.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"426"},"customOut":{"$id":"427"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":530.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"426"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":530.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"427"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":531.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"438"},"customOut":{"$id":"439"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":531.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"438"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":531.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"439"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":560.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"452"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":560.3984},"$type":"TransformData"},"CurveParent":{"$ref":"452"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":560.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"464"},"customOut":{"$id":"465"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":560.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"464"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":560.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"465"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":561.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"476"},"customOut":{"$id":"477"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":561.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"476"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":561.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"477"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":590.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"490"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":590.3984},"$type":"TransformData"},"CurveParent":{"$ref":"490"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":590.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"502"},"customOut":{"$id":"503"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":590.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"502"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":590.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"503"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":591.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"514"},"customOut":{"$id":"515"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":591.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"514"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":591.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"515"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":620.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"528"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":620.3984},"$type":"TransformData"},"CurveParent":{"$ref":"528"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":620.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"540"},"customOut":{"$id":"541"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":620.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"540"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":620.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"541"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":621.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"552"},"customOut":{"$id":"553"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":621.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"552"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":621.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"553"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":650.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"566"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":650.3984},"$type":"TransformData"},"CurveParent":{"$ref":"566"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":650.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"578"},"customOut":{"$id":"579"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":650.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"578"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":650.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"579"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":651.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"590"},"customOut":{"$id":"591"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":651.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"590"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":651.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"591"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":680.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"604"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":680.3984},"$type":"TransformData"},"CurveParent":{"$ref":"604"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":680.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"616"},"customOut":{"$id":"617"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":680.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"616"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":680.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"617"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":681.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"628"},"customOut":{"$id":"629"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":681.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"628"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":681.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"629"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":710.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"642"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":710.3984},"$type":"TransformData"},"CurveParent":{"$ref":"642"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":710.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"654"},"customOut":{"$id":"655"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":710.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"654"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":710.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"655"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":711.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"666"},"customOut":{"$id":"667"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":711.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"666"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":711.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"667"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":455.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":425.3984},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":425.3984},"$type":"TransformData"}},"children":[]}]},{"name":"3 moving sides","components":{"Transform":{"position":{"x":0,"y":0,"z":395.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":454.3984},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":662.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"middle","components":{"Transform":{"position":{"x":0,"y":0,"z":448.5102},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"1 moving","components":{"Transform":{"position":{"x":0,"y":0,"z":13.31934},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":283.3193},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"3 moving coins","components":{"Transform":{"position":{"x":0,"y":0,"z":608.8612},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270.8612},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":418.8612},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"middle","components":{"Transform":{"position":{"x":0,"y":0,"z":895.1921},"$type":"TransformData"},"Randomizer":{"$ref":"12"}},"children":[{"name":"1 moving","components":{"Transform":{"position":{"x":0,"y":0,"z":460.0012},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":730.0012},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"3 moving coins","components":{"Transform":{"position":{"x":0,"y":0,"z":1055.543},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":717.543},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":865.543},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]}]},"routeChunk_default_short_train_tops_moving_multiple":{"name":"routeChunk_default_short_train_tops_moving_multiple","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":12,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_short_train_tops_moving_multiple","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":37,"z":165},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":37,"z":165},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":480},"$type":"TransformData"}},"children":[]}]},{"name":"trains_moving_sides","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":717},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":870},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"trains_moving_mid","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":382},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":632},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":829},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":55,"z":690},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":55,"z":690},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_4_units_3_tracks_b-s-b":{"name":"routeChunk_default_4_units_3_tracks_b-s-b","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_4_units_3_tracks_b-s-b","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"40"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"40"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"52"},"customOut":{"$id":"53"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"52"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"53"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"64"},"customOut":{"$id":"65"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"64"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"65"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"78"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"78"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"90"},"customOut":{"$id":"91"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"90"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"91"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":211},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"102"},"customOut":{"$id":"103"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"102"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"103"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"116"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"116"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"128"},"customOut":{"$id":"129"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"128"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"129"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"140"},"customOut":{"$id":"141"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"140"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"141"}},"children":[]}]}]}]}]},{"name":"coins_mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"157"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"157"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"169"},"customOut":{"$id":"170"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"169"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"170"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"181"},"customOut":{"$id":"182"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"181"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"182"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"195"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"195"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"207"},"customOut":{"$id":"208"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"207"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"208"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"219"},"customOut":{"$id":"220"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"219"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"220"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"233"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"233"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"245"},"customOut":{"$id":"246"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"245"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"246"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"257"},"customOut":{"$id":"258"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"257"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"258"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"271"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurveParent":{"$ref":"271"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"283"},"customOut":{"$id":"284"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"283"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"284"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"295"},"customOut":{"$id":"296"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"295"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"296"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"309"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurveParent":{"$ref":"309"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"321"},"customOut":{"$id":"322"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"321"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"322"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"333"},"customOut":{"$id":"334"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"333"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"334"}},"children":[]}]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":55,"z":150},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":55,"z":150},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":495},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":495},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":510},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_4_units_3_tracks_choice":{"name":"routeChunk_default_4_units_3_tracks_choice","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_4_units_3_tracks_choice","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"25"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"CurveParent":{"$ref":"25"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"37"},"customOut":{"$id":"38"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"37"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"38"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":131},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"49"},"customOut":{"$id":"50"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":131},"$type":"TransformData"},"CurvePointTangent":{"$ref":"49"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":131},"$type":"TransformData"},"CurvePointTangent":{"$ref":"50"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":135},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"92"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"92"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"104"},"customOut":{"$id":"105"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"104"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"105"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"116"},"customOut":{"$id":"117"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"116"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"117"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":7,"z":330},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":330},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":420},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":465},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"187"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.9,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":465},"$type":"TransformData"},"CurveParent":{"$ref":"187"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":465},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"199"},"customOut":{"$id":"200"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":465},"$type":"TransformData"},"CurvePointTangent":{"$ref":"199"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":563.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"200"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":563.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"211"},"customOut":{"$id":"212"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":465},"$type":"TransformData"},"CurvePointTangent":{"$ref":"211"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":563.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"212"}},"children":[]}]}]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":510},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":20,"y":57,"z":555},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":57,"z":555},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_4_units_3_tracks_s-b-s-b":{"name":"routeChunk_default_4_units_3_tracks_s-b-s-b","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_4_units_3_tracks_s-b-s-b","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":15},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":76},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"43"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":15},"$type":"TransformData"},"CurveParent":{"$ref":"43"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":15},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"55"},"customOut":{"$id":"56"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":15},"$type":"TransformData"},"CurvePointTangent":{"$ref":"55"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":15},"$type":"TransformData"},"CurvePointTangent":{"$ref":"56"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"67"},"customOut":{"$id":"68"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"67"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":91},"$type":"TransformData"},"CurvePointTangent":{"$ref":"68"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"81"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"81"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"93"},"customOut":{"$id":"94"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"93"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"94"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"105"},"customOut":{"$id":"106"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"105"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"106"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":75},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"139"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"139"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"151"},"customOut":{"$id":"152"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"151"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"152"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"163"},"customOut":{"$id":"164"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"163"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"164"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":345},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":345},"$type":"TransformData"}},"children":[]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":55,"z":540},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":55,"z":540},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_4_units_3_tracks_s-s-s-s":{"name":"routeChunk_default_4_units_3_tracks_s-s-s-s","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_4_units_3_tracks_s-s-s-s","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":30},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"33"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":30},"$type":"TransformData"},"CurveParent":{"$ref":"33"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":30},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"45"},"customOut":{"$id":"46"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"45"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"46"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":131},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"57"},"customOut":{"$id":"58"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":131},"$type":"TransformData"},"CurvePointTangent":{"$ref":"57"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":131},"$type":"TransformData"},"CurvePointTangent":{"$ref":"58"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":29,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"71"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"71"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"83"},"customOut":{"$id":"84"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"83"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"84"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"95"},"customOut":{"$id":"96"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"95"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"96"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":150},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"127"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"127"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"139"},"customOut":{"$id":"140"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"139"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"140"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":371},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"151"},"customOut":{"$id":"152"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":371},"$type":"TransformData"},"CurvePointTangent":{"$ref":"151"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":371},"$type":"TransformData"},"CurvePointTangent":{"$ref":"152"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":420},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":35.8,"z":420},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":35.8,"z":420},"$type":"TransformData"}},"children":[]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":495},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":495},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_4_units_3_tracks_s-s":{"name":"routeChunk_default_4_units_3_tracks_s-s","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_4_units_3_tracks_s-s","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":7,"z":150},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":150},"$type":"TransformData"}},"children":[]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":51},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"62"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"62"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"74"},"customOut":{"$id":"75"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"74"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"75"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":216},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"86"},"customOut":{"$id":"87"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":216},"$type":"TransformData"},"CurvePointTangent":{"$ref":"86"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":216},"$type":"TransformData"},"CurvePointTangent":{"$ref":"87"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":315},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":315},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_epic_various":{"name":"routeChunk_default_epic_various","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":16,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"23"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"24"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"25"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$id":"37"},{"$id":"38"},{"$id":"39"},{"$id":"40"},{"$id":"41"},{"$id":"42"},{"$id":"43"},{"$id":"44"},{"$id":"45"},{"$id":"46"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_epic_various","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"Randomizer","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"roofRun","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":451},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":605},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":832},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":990},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"groundRunDoubleTrains","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"23"}},"children":[{"name":"train double","components":{"Transform":{"position":{"x":0,"y":0,"z":145.1655},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"157"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"CurveParent":{"$ref":"157"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"169"},"customOut":{"$id":"170"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"CurvePointTangent":{"$ref":"169"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.667267},"$type":"TransformData"},"CurvePointTangent":{"$ref":"170"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.667267},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"181"},"customOut":{"$id":"182"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.667267},"$type":"TransformData"},"CurvePointTangent":{"$ref":"181"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.667267},"$type":"TransformData"},"CurvePointTangent":{"$ref":"182"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.33273},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"195"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":25.33273},"$type":"TransformData"},"CurveParent":{"$ref":"195"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.33273},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"207"},"customOut":{"$id":"208"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":25.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"207"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":25.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"208"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":26.33273},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"219"},"customOut":{"$id":"220"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":26.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"219"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":26.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"220"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.33273},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"233"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":55.33273},"$type":"TransformData"},"CurveParent":{"$ref":"233"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.33273},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"245"},"customOut":{"$id":"246"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":55.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"245"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":55.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"246"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":56.33273},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"257"},"customOut":{"$id":"258"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":56.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"257"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":56.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"258"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.33273},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"271"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":85.33273},"$type":"TransformData"},"CurveParent":{"$ref":"271"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.33273},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"283"},"customOut":{"$id":"284"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":85.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"283"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":85.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"284"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":86.33273},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"295"},"customOut":{"$id":"296"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":86.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"295"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":86.33273},"$type":"TransformData"},"CurvePointTangent":{"$ref":"296"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.3327},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"309"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":115.3327},"$type":"TransformData"},"CurveParent":{"$ref":"309"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.3327},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"321"},"customOut":{"$id":"322"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":115.3327},"$type":"TransformData"},"CurvePointTangent":{"$ref":"321"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":115.3327},"$type":"TransformData"},"CurvePointTangent":{"$ref":"322"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":116.3327},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"333"},"customOut":{"$id":"334"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":116.3327},"$type":"TransformData"},"CurvePointTangent":{"$ref":"333"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":116.3327},"$type":"TransformData"},"CurvePointTangent":{"$ref":"334"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":137.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":137.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train double close","components":{"Transform":{"position":{"x":0,"y":0,"z":145.1655},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"371"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurveParent":{"$ref":"371"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"383"},"customOut":{"$id":"384"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"383"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"384"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"395"},"customOut":{"$id":"396"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"395"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"396"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"409"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurveParent":{"$ref":"409"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"421"},"customOut":{"$id":"422"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"421"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"422"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"433"},"customOut":{"$id":"434"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"433"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"434"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"447"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurveParent":{"$ref":"447"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"459"},"customOut":{"$id":"460"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"459"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"460"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"471"},"customOut":{"$id":"472"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"471"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"472"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"485"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurveParent":{"$ref":"485"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"497"},"customOut":{"$id":"498"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"497"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"498"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"509"},"customOut":{"$id":"510"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"509"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"510"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"523"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurveParent":{"$ref":"523"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"535"},"customOut":{"$id":"536"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"535"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"536"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"547"},"customOut":{"$id":"548"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"547"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"548"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":118.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":2,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":137.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":145.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"586"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurveParent":{"$ref":"586"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"598"},"customOut":{"$id":"599"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"598"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"599"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"610"},"customOut":{"$id":"611"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"610"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"611"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"624"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurveParent":{"$ref":"624"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"636"},"customOut":{"$id":"637"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"636"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"637"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"648"},"customOut":{"$id":"649"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"648"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"649"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"662"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurveParent":{"$ref":"662"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"674"},"customOut":{"$id":"675"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"674"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"675"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"686"},"customOut":{"$id":"687"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"686"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"687"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"700"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurveParent":{"$ref":"700"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"712"},"customOut":{"$id":"713"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"712"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"713"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"724"},"customOut":{"$id":"725"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"724"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"725"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"738"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurveParent":{"$ref":"738"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"750"},"customOut":{"$id":"751"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"750"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"751"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"762"},"customOut":{"$id":"763"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"762"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"763"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":137.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":285.5849},"$type":"TransformData"},"Randomizer":{"$ref":"24"}},"children":[{"name":"train double","components":{"Transform":{"position":{"x":0,"y":0,"z":430.7503},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"797"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"CurveParent":{"$ref":"797"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"809"},"customOut":{"$id":"810"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"809"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":280.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"810"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":2819176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"821"},"customOut":{"$id":"822"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":2819176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"821"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":2819176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"822"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.9176},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"835"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":310.9176},"$type":"TransformData"},"CurveParent":{"$ref":"835"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.9176},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"847"},"customOut":{"$id":"848"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":310.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"847"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":310.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"848"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":3119176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"859"},"customOut":{"$id":"860"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":3119176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"859"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":3119176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"860"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.9176},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"873"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":340.9176},"$type":"TransformData"},"CurveParent":{"$ref":"873"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.9176},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"885"},"customOut":{"$id":"886"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":340.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"885"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":340.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"886"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":3419176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"897"},"customOut":{"$id":"898"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":3419176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"897"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":3419176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"898"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.9176},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"911"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":370.9176},"$type":"TransformData"},"CurveParent":{"$ref":"911"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.9176},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"923"},"customOut":{"$id":"924"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":370.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"923"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":370.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"924"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":3719176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"935"},"customOut":{"$id":"936"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":3719176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"935"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":3719176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"936"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.9176},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"949"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":400.9176},"$type":"TransformData"},"CurveParent":{"$ref":"949"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.9176},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"961"},"customOut":{"$id":"962"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":400.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"961"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":400.9176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"962"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":4019176},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"973"},"customOut":{"$id":"974"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":4019176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"973"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":4019176},"$type":"TransformData"},"CurvePointTangent":{"$ref":"974"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":422.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":422.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train double close","components":{"Transform":{"position":{"x":0,"y":0,"z":430.7503},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1010"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1010"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1022"},"customOut":{"$id":"1023"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1022"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1023"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1034"},"customOut":{"$id":"1035"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1034"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1035"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1048"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1048"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1060"},"customOut":{"$id":"1061"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1060"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1061"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1072"},"customOut":{"$id":"1073"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1072"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1073"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1086"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1086"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1098"},"customOut":{"$id":"1099"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1098"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1099"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1110"},"customOut":{"$id":"1111"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1110"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1111"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1124"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1124"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1136"},"customOut":{"$id":"1137"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1136"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1137"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1148"},"customOut":{"$id":"1149"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1148"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1149"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1162"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1162"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1174"},"customOut":{"$id":"1175"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1174"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1175"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1186"},"customOut":{"$id":"1187"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1186"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1187"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":403.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":2,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":422.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":430.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1225"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1225"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1237"},"customOut":{"$id":"1238"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1237"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":280.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1238"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1249"},"customOut":{"$id":"1250"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1249"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":281.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1250"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1263"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1263"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1275"},"customOut":{"$id":"1276"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1275"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":310.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1276"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1287"},"customOut":{"$id":"1288"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1287"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":311.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1288"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1301"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1301"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1313"},"customOut":{"$id":"1314"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1313"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":340.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1314"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1325"},"customOut":{"$id":"1326"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1325"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":341.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1326"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1339"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1339"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1351"},"customOut":{"$id":"1352"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1351"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":370.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1352"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1363"},"customOut":{"$id":"1364"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1363"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":371.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1364"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1377"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurveParent":{"$ref":"1377"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1389"},"customOut":{"$id":"1390"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1389"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":400.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1390"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1401"},"customOut":{"$id":"1402"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1401"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":401.7503},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1402"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":422.7503},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":576.1245},"$type":"TransformData"},"Randomizer":{"$ref":"25"}},"children":[{"name":"train double","components":{"Transform":{"position":{"x":0,"y":0,"z":721.29},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1436"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"CurveParent":{"$ref":"1436"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1448"},"customOut":{"$id":"1449"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1448"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":571.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1449"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":572.4573},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1460"},"customOut":{"$id":"1461"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":572.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1460"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":572.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1461"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.4573},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1474"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":601.4573},"$type":"TransformData"},"CurveParent":{"$ref":"1474"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.4573},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1486"},"customOut":{"$id":"1487"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":601.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1486"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":601.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1487"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":602.4573},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1498"},"customOut":{"$id":"1499"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":602.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1498"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":602.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1499"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.4573},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1512"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":631.4573},"$type":"TransformData"},"CurveParent":{"$ref":"1512"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.4573},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1524"},"customOut":{"$id":"1525"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":631.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1524"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":631.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1525"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":632.4573},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1536"},"customOut":{"$id":"1537"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":632.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1536"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":632.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1537"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.4573},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1550"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":661.4573},"$type":"TransformData"},"CurveParent":{"$ref":"1550"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.4573},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1562"},"customOut":{"$id":"1563"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":661.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1562"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":661.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1563"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":662.4573},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1574"},"customOut":{"$id":"1575"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":662.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1574"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":662.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1575"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.4573},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1588"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":691.4573},"$type":"TransformData"},"CurveParent":{"$ref":"1588"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.4573},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1600"},"customOut":{"$id":"1601"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":691.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1600"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":691.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1601"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":692.4573},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1612"},"customOut":{"$id":"1613"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":692.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1612"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":692.4573},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1613"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":713.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":713.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train double close","components":{"Transform":{"position":{"x":0,"y":0,"z":721.29},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1649"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurveParent":{"$ref":"1649"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1661"},"customOut":{"$id":"1662"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1661"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1662"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1673"},"customOut":{"$id":"1674"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1673"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1674"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1687"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurveParent":{"$ref":"1687"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1699"},"customOut":{"$id":"1700"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1699"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1700"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1711"},"customOut":{"$id":"1712"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1711"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1712"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1725"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurveParent":{"$ref":"1725"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1737"},"customOut":{"$id":"1738"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1737"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1738"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1749"},"customOut":{"$id":"1750"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1749"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1750"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1763"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurveParent":{"$ref":"1763"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1775"},"customOut":{"$id":"1776"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1775"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1776"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1787"},"customOut":{"$id":"1788"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1787"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1788"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1801"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurveParent":{"$ref":"1801"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1813"},"customOut":{"$id":"1814"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1813"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1814"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1825"},"customOut":{"$id":"1826"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1825"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1826"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":694.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":2,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":713.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":721.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1864"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurveParent":{"$ref":"1864"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1876"},"customOut":{"$id":"1877"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1876"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":571.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1877"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1888"},"customOut":{"$id":"1889"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1888"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":572.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1889"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1902"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurveParent":{"$ref":"1902"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1914"},"customOut":{"$id":"1915"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1914"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":601.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1915"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1926"},"customOut":{"$id":"1927"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1926"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":602.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1927"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1940"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurveParent":{"$ref":"1940"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1952"},"customOut":{"$id":"1953"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1952"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":631.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1953"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1964"},"customOut":{"$id":"1965"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1964"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":632.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1965"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1978"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurveParent":{"$ref":"1978"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1990"},"customOut":{"$id":"1991"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1990"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":661.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1991"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2002"},"customOut":{"$id":"2003"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2002"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":662.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2003"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2016"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurveParent":{"$ref":"2016"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2028"},"customOut":{"$id":"2029"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2028"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":691.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2029"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2040"},"customOut":{"$id":"2041"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2040"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":692.29},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2041"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":713.29},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":861.6984},"$type":"TransformData"},"Randomizer":{"$ref":"26"}},"children":[{"name":"train double","components":{"Transform":{"position":{"x":0,"y":0,"z":1006.864},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2075"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"CurveParent":{"$ref":"2075"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2087"},"customOut":{"$id":"2088"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2087"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2088"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0311},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2099"},"customOut":{"$id":"2100"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2099"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2100"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0311},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2113"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0311},"$type":"TransformData"},"CurveParent":{"$ref":"2113"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0311},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2125"},"customOut":{"$id":"2126"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2125"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2126"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0311},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2137"},"customOut":{"$id":"2138"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2137"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2138"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0311},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2151"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0311},"$type":"TransformData"},"CurveParent":{"$ref":"2151"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0311},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2163"},"customOut":{"$id":"2164"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2163"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2164"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0311},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2175"},"customOut":{"$id":"2176"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2175"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2176"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0311},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2189"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0311},"$type":"TransformData"},"CurveParent":{"$ref":"2189"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0311},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2201"},"customOut":{"$id":"2202"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2201"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2202"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0311},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2213"},"customOut":{"$id":"2214"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2213"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2214"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0311},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2227"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0311},"$type":"TransformData"},"CurveParent":{"$ref":"2227"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0311},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2239"},"customOut":{"$id":"2240"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2239"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2240"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0311},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2251"},"customOut":{"$id":"2252"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2251"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0311},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2252"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":998.8638},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":998.8638},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train double close","components":{"Transform":{"position":{"x":0,"y":0,"z":1006.864},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2288"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2288"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2300"},"customOut":{"$id":"2301"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2300"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2301"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2312"},"customOut":{"$id":"2313"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2312"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2313"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2326"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2326"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2338"},"customOut":{"$id":"2339"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2338"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2339"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2350"},"customOut":{"$id":"2351"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2350"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2351"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2364"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2364"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2376"},"customOut":{"$id":"2377"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2376"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2377"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2388"},"customOut":{"$id":"2389"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2388"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2389"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2402"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2402"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2414"},"customOut":{"$id":"2415"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2414"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2415"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2426"},"customOut":{"$id":"2427"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2426"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2427"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2440"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2440"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2452"},"customOut":{"$id":"2453"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2452"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2453"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2464"},"customOut":{"$id":"2465"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2464"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2465"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":979.8638},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":2,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":998.8638},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":1006.864},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2503"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2503"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2515"},"customOut":{"$id":"2516"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2515"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":856.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2516"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2527"},"customOut":{"$id":"2528"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2527"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":857.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2528"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2541"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2541"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2553"},"customOut":{"$id":"2554"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2553"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2554"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2565"},"customOut":{"$id":"2566"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2565"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":887.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2566"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2579"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2579"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2591"},"customOut":{"$id":"2592"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2591"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2592"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2603"},"customOut":{"$id":"2604"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2603"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":917.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2604"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2617"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2617"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2629"},"customOut":{"$id":"2630"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2629"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2630"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2641"},"customOut":{"$id":"2642"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2641"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":947.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2642"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2655"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurveParent":{"$ref":"2655"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2667"},"customOut":{"$id":"2668"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2667"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":976.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2668"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2679"},"customOut":{"$id":"2680"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2679"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":977.8638},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2680"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":998.8638},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]}]}]},{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2709"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurveParent":{"$ref":"2709"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2721"},"customOut":{"$id":"2722"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2721"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2722"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2733"},"customOut":{"$id":"2734"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2733"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2734"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2747"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"2747"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2759"},"customOut":{"$id":"2760"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2759"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2760"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2771"},"customOut":{"$id":"2772"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2771"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2772"}},"children":[]}]}]}]}]},{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2792"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurveParent":{"$ref":"2792"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2804"},"customOut":{"$id":"2805"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2804"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2805"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2816"},"customOut":{"$id":"2817"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2816"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2817"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2830"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurveParent":{"$ref":"2830"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2842"},"customOut":{"$id":"2843"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2842"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2843"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2854"},"customOut":{"$id":"2855"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2854"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2855"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2868"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"2868"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2880"},"customOut":{"$id":"2881"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2880"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2881"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2892"},"customOut":{"$id":"2893"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2892"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2893"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2906"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurveParent":{"$ref":"2906"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2918"},"customOut":{"$id":"2919"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2918"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2919"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2930"},"customOut":{"$id":"2931"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2930"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2931"}},"children":[]}]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2944"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurveParent":{"$ref":"2944"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2956"},"customOut":{"$id":"2957"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2956"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2957"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2968"},"customOut":{"$id":"2969"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2968"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2969"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2982"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurveParent":{"$ref":"2982"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2994"},"customOut":{"$id":"2995"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2994"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2995"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3006"},"customOut":{"$id":"3007"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3006"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3007"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"3020"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurveParent":{"$ref":"3020"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"3032"},"customOut":{"$id":"3033"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3032"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3033"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3044"},"customOut":{"$id":"3045"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3044"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3045"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"3058"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurveParent":{"$ref":"3058"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"3070"},"customOut":{"$id":"3071"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3070"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3071"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3082"},"customOut":{"$id":"3083"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3082"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3083"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"3096"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurveParent":{"$ref":"3096"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"3108"},"customOut":{"$id":"3109"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3108"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3109"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3120"},"customOut":{"$id":"3121"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3120"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3121"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"3134"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurveParent":{"$ref":"3134"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"3146"},"customOut":{"$id":"3147"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3146"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3147"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3158"},"customOut":{"$id":"3159"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3158"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3159"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"3172"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurveParent":{"$ref":"3172"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"3184"},"customOut":{"$id":"3185"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3184"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3185"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"3196"},"customOut":{"$id":"3197"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3196"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePointTangent":{"$ref":"3197"}},"children":[]}]}]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"epic_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Epic,All","_density":""},"_blockCount":12,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":7,"z":45},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":45},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":1260},"$type":"TransformData"}},"children":[]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":1350},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":1410},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":1410},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_fallback":{"name":"routeChunk_default_fallback","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_fallback","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[]},"routeChunk_default_jetpack_landing":{"name":"routeChunk_default_jetpack_landing","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_jetpack_landing","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[]},"routeChunk_default_no_gameplay_1":{"name":"routeChunk_default_no_gameplay_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":8,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"9"},{"$id":"10"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_no_gameplay_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},"routeChunk_default_no_gameplay_2":{"name":"routeChunk_default_no_gameplay_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":12,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"9"},{"$id":"10"},{"$id":"11"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_no_gameplay_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":810},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},"routeChunk_default_no_gameplay_epic":{"name":"routeChunk_default_no_gameplay_epic","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":12,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Epic,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"7"},{"$id":"8"},{"$id":"9"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_no_gameplay_epic","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":390},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":810},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},"routeChunk_default_start1":{"name":"routeChunk_default_start1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"9"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_start1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"random_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"39"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"39"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"51"},"customOut":{"$id":"52"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"51"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"52"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"63"},"customOut":{"$id":"64"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"63"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"64"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"77"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"77"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"89"},"customOut":{"$id":"90"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"89"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"90"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"101"},"customOut":{"$id":"102"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"101"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"102"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"115"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"115"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"127"},"customOut":{"$id":"128"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"127"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"128"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"139"},"customOut":{"$id":"140"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"139"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"140"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"153"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"153"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"165"},"customOut":{"$id":"166"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"165"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"166"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"177"},"customOut":{"$id":"178"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"177"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"178"}},"children":[]}]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"191"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"191"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"203"},"customOut":{"$id":"204"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"203"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"204"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"215"},"customOut":{"$id":"216"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"215"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"216"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"229"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"229"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"241"},"customOut":{"$id":"242"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"241"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"242"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"253"},"customOut":{"$id":"254"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"253"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"254"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"267"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"267"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"279"},"customOut":{"$id":"280"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"279"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"280"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"291"},"customOut":{"$id":"292"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"291"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"292"}},"children":[]}]}]}]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"308"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"308"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"320"},"customOut":{"$id":"321"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"320"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"321"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":326},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"332"},"customOut":{"$id":"333"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":326},"$type":"TransformData"},"CurvePointTangent":{"$ref":"332"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":326},"$type":"TransformData"},"CurvePointTangent":{"$ref":"333"}},"children":[]}]}]}]}]},{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"349"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurveParent":{"$ref":"349"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"361"},"customOut":{"$id":"362"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"361"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"CurvePointTangent":{"$ref":"362"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":191},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"373"},"customOut":{"$id":"374"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":191},"$type":"TransformData"},"CurvePointTangent":{"$ref":"373"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":191},"$type":"TransformData"},"CurvePointTangent":{"$ref":"374"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"387"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"387"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"399"},"customOut":{"$id":"400"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"399"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"400"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":356},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"411"},"customOut":{"$id":"412"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":356},"$type":"TransformData"},"CurvePointTangent":{"$ref":"411"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":356},"$type":"TransformData"},"CurvePointTangent":{"$ref":"412"}},"children":[]}]}]}]}]}]},{"name":"environment_constraints","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Empty,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"},"EnvironmentBase":{"_type":"Empty","_numTracks":3,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"train_sub_2_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":60},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_start2":{"name":"routeChunk_default_start2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"9"},{"$id":"10"},{"$id":"11"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"12"},{"$id":"13"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"14"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_start2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"9"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"12"}},"children":[{"name":"coins_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"42"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"42"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"54"},"customOut":{"$id":"55"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"54"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"55"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"66"},"customOut":{"$id":"67"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"66"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"67"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"80"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"80"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"92"},"customOut":{"$id":"93"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"92"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"93"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"104"},"customOut":{"$id":"105"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"104"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"105"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":180},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"118"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":180},"$type":"TransformData"},"CurveParent":{"$ref":"118"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":180},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"130"},"customOut":{"$id":"131"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"130"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":180},"$type":"TransformData"},"CurvePointTangent":{"$ref":"131"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"142"},"customOut":{"$id":"143"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"142"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"143"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"156"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"156"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"168"},"customOut":{"$id":"169"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"168"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"169"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"180"},"customOut":{"$id":"181"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"180"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"181"}},"children":[]}]}]}]}]},{"name":"coins_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"197"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"197"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"209"},"customOut":{"$id":"210"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"210"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"221"},"customOut":{"$id":"222"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"222"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"235"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"235"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"247"},"customOut":{"$id":"248"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"247"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"248"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"259"},"customOut":{"$id":"260"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"260"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"273"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"273"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"285"},"customOut":{"$id":"286"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"285"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"286"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"297"},"customOut":{"$id":"298"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"298"}},"children":[]}]}]}]}]}]},{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"315"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"315"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"327"},"customOut":{"$id":"328"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"327"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"328"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"339"},"customOut":{"$id":"340"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"339"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"340"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"353"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"353"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"365"},"customOut":{"$id":"366"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"365"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"366"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"377"},"customOut":{"$id":"378"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"377"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"378"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"391"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"391"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"403"},"customOut":{"$id":"404"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"403"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"404"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"415"},"customOut":{"$id":"416"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"415"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"416"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"429"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"429"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"441"},"customOut":{"$id":"442"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"441"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"442"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"453"},"customOut":{"$id":"454"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"453"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"454"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"467"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"467"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"479"},"customOut":{"$id":"480"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"479"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"480"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"491"},"customOut":{"$id":"492"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"491"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"492"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"505"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"505"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"517"},"customOut":{"$id":"518"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"517"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"518"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"529"},"customOut":{"$id":"530"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"529"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"530"}},"children":[]}]}]}]}]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"random","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"14"}},"children":[{"name":"coins_2","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"563"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"563"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"575"},"customOut":{"$id":"576"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"575"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"576"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"587"},"customOut":{"$id":"588"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"587"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20.04517,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"588"}},"children":[]}]}]}]}]},{"name":"coins_1","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"604"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"604"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"616"},"customOut":{"$id":"617"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"616"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"617"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"628"},"customOut":{"$id":"629"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"628"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-0.04516602,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"629"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"642"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"642"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"654"},"customOut":{"$id":"655"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"654"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"655"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"666"},"customOut":{"$id":"667"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"666"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10.04517,"y":0,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"667"}},"children":[]}]}]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"680"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"680"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"692"},"customOut":{"$id":"693"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"692"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"693"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"704"},"customOut":{"$id":"705"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"704"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"705"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"718"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"718"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"730"},"customOut":{"$id":"731"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"730"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"731"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"742"},"customOut":{"$id":"743"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"742"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"743"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-10,"y":0,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"756"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-10,"y":0,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"756"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-10,"y":0,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"768"},"customOut":{"$id":"769"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"768"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"769"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-10,"y":0,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"780"},"customOut":{"$id":"781"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"780"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-10,"y":0,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"781"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"794"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"794"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"806"},"customOut":{"$id":"807"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"806"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"807"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"818"},"customOut":{"$id":"819"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"818"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"819"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"832"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"832"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"844"},"customOut":{"$id":"845"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"844"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"845"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"856"},"customOut":{"$id":"857"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"856"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"857"}},"children":[]}]}]}]}]}]},{"name":"environment_constraints","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Empty,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"},"EnvironmentBase":{"_type":"Empty","_numTracks":3,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"train_sub_2_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":60},"$type":"TransformData"}},"children":[]}]},"Default1Track":{"name":"Default1Track","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"routeChunk_default_1_track_end","components":{"Transform":{"position":{"x":0,"y":0,"z":1620},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_1_track_end","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"end","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"}},"children":[{"name":"endtrain","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1620},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"blocker_jump_standard_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1620},"$type":"TransformData"}},"children":[]},{"name":"blocker_jump_standard_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1620},"$type":"TransformData"}},"children":[]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":1710},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":1725},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":1725},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"routeChunk_default_1_track_mid_var_1","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":14,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"83"},{"$id":"84"},{"$id":"85"},{"$id":"86"},{"$id":"87"},{"$id":"88"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_1_track_mid_var_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":1,"_blockCount":14,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":492},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":719},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"station_platforms_extended_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"}},"children":[]},{"name":"random_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":720},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"138"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":720},"$type":"TransformData"},"CurveParent":{"$ref":"138"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":720},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"150"},"customOut":{"$id":"151"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":720},"$type":"TransformData"},"CurvePointTangent":{"$ref":"150"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":720},"$type":"TransformData"},"CurvePointTangent":{"$ref":"151"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":721},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"162"},"customOut":{"$id":"163"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"162"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"163"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":750},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"176"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":750},"$type":"TransformData"},"CurveParent":{"$ref":"176"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":750},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"188"},"customOut":{"$id":"189"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"188"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"189"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":751},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"200"},"customOut":{"$id":"201"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":751},"$type":"TransformData"},"CurvePointTangent":{"$ref":"200"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":751},"$type":"TransformData"},"CurvePointTangent":{"$ref":"201"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":780},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"214"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":780},"$type":"TransformData"},"CurveParent":{"$ref":"214"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":780},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"226"},"customOut":{"$id":"227"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"226"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"227"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":781},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"238"},"customOut":{"$id":"239"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":781},"$type":"TransformData"},"CurvePointTangent":{"$ref":"238"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":781},"$type":"TransformData"},"CurvePointTangent":{"$ref":"239"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":810},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"252"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":810},"$type":"TransformData"},"CurveParent":{"$ref":"252"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":810},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"264"},"customOut":{"$id":"265"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":810},"$type":"TransformData"},"CurvePointTangent":{"$ref":"264"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":810},"$type":"TransformData"},"CurvePointTangent":{"$ref":"265"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":811},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"276"},"customOut":{"$id":"277"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"276"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"277"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":840},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"290"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":840},"$type":"TransformData"},"CurveParent":{"$ref":"290"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":840},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"302"},"customOut":{"$id":"303"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":840},"$type":"TransformData"},"CurvePointTangent":{"$ref":"302"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":840},"$type":"TransformData"},"CurvePointTangent":{"$ref":"303"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":841},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"314"},"customOut":{"$id":"315"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":841},"$type":"TransformData"},"CurvePointTangent":{"$ref":"314"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":841},"$type":"TransformData"},"CurvePointTangent":{"$ref":"315"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":870},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"328"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":870},"$type":"TransformData"},"CurveParent":{"$ref":"328"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":870},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"340"},"customOut":{"$id":"341"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":870},"$type":"TransformData"},"CurvePointTangent":{"$ref":"340"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":870},"$type":"TransformData"},"CurvePointTangent":{"$ref":"341"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":871},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"352"},"customOut":{"$id":"353"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":871},"$type":"TransformData"},"CurvePointTangent":{"$ref":"352"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":871},"$type":"TransformData"},"CurvePointTangent":{"$ref":"353"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":900},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"366"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":900},"$type":"TransformData"},"CurveParent":{"$ref":"366"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":900},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"378"},"customOut":{"$id":"379"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":900},"$type":"TransformData"},"CurvePointTangent":{"$ref":"378"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":900},"$type":"TransformData"},"CurvePointTangent":{"$ref":"379"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":901},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"390"},"customOut":{"$id":"391"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"390"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"391"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":930},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"404"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":930},"$type":"TransformData"},"CurveParent":{"$ref":"404"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":930},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"416"},"customOut":{"$id":"417"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":930},"$type":"TransformData"},"CurvePointTangent":{"$ref":"416"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":930},"$type":"TransformData"},"CurvePointTangent":{"$ref":"417"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":931},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"428"},"customOut":{"$id":"429"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":931},"$type":"TransformData"},"CurvePointTangent":{"$ref":"428"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":931},"$type":"TransformData"},"CurvePointTangent":{"$ref":"429"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":960},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"442"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":960},"$type":"TransformData"},"CurveParent":{"$ref":"442"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":960},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"454"},"customOut":{"$id":"455"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":960},"$type":"TransformData"},"CurvePointTangent":{"$ref":"454"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":960},"$type":"TransformData"},"CurvePointTangent":{"$ref":"455"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":961},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"466"},"customOut":{"$id":"467"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":961},"$type":"TransformData"},"CurvePointTangent":{"$ref":"466"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":961},"$type":"TransformData"},"CurvePointTangent":{"$ref":"467"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":990},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"480"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":990},"$type":"TransformData"},"CurveParent":{"$ref":"480"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":990},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"492"},"customOut":{"$id":"493"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":990},"$type":"TransformData"},"CurvePointTangent":{"$ref":"492"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":990},"$type":"TransformData"},"CurvePointTangent":{"$ref":"493"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":991},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"504"},"customOut":{"$id":"505"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":991},"$type":"TransformData"},"CurvePointTangent":{"$ref":"504"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":991},"$type":"TransformData"},"CurvePointTangent":{"$ref":"505"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":1020},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"518"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":1020},"$type":"TransformData"},"CurveParent":{"$ref":"518"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":1020},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"530"},"customOut":{"$id":"531"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":1020},"$type":"TransformData"},"CurvePointTangent":{"$ref":"530"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":1020},"$type":"TransformData"},"CurvePointTangent":{"$ref":"531"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":1021},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"542"},"customOut":{"$id":"543"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":1021},"$type":"TransformData"},"CurvePointTangent":{"$ref":"542"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":1021},"$type":"TransformData"},"CurvePointTangent":{"$ref":"543"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":10,"z":1050},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"556"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":10,"z":1050},"$type":"TransformData"},"CurveParent":{"$ref":"556"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":10,"z":1050},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"568"},"customOut":{"$id":"569"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":1050},"$type":"TransformData"},"CurvePointTangent":{"$ref":"568"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":1050},"$type":"TransformData"},"CurvePointTangent":{"$ref":"569"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":10,"z":1051},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"580"},"customOut":{"$id":"581"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":10,"z":1051},"$type":"TransformData"},"CurvePointTangent":{"$ref":"580"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":10,"z":1051},"$type":"TransformData"},"CurvePointTangent":{"$ref":"581"}},"children":[]}]}]}]}]},{"name":"random_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":1175.177},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"obstacle_group_group","components":{"Transform":{"position":{"x":0,"y":0,"z":1265.177},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":0,"z":1265.177},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"607"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1265.177},"$type":"TransformData"},"CurveParent":{"$ref":"607"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1265.177},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"619"},"customOut":{"$id":"620"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1265.177},"$type":"TransformData"},"CurvePointTangent":{"$ref":"619"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":80,"z":1363.564},"$type":"TransformData"},"CurvePointTangent":{"$ref":"620"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1363.564},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"631"},"customOut":{"$id":"632"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":80,"z":1265.177},"$type":"TransformData"},"CurvePointTangent":{"$ref":"631"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1363.564},"$type":"TransformData"},"CurvePointTangent":{"$ref":"632"}},"children":[]}]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1261},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"random_coins","components":{"Transform":{"position":{"x":0,"y":0,"z":1407.424},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1467.424},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":0,"z":1467.424},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"667"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1467.424},"$type":"TransformData"},"CurveParent":{"$ref":"667"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1467.424},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"679"},"customOut":{"$id":"680"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1467.424},"$type":"TransformData"},"CurvePointTangent":{"$ref":"679"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":80,"z":1565.811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"680"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1565.811},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"691"},"customOut":{"$id":"692"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":80,"z":1467.424},"$type":"TransformData"},"CurvePointTangent":{"$ref":"691"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1565.811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"692"}},"children":[]}]}]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"}},"children":[{"name":"high_filler","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Fillers,All","_density":"High,All"},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"station_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Station,All","_density":""},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"high_filler","components":{"Transform":{"position":{"x":0,"y":0,"z":1260},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Fillers,All","_density":"High,All"},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]}]},{"name":"routeChunk_default_1_track_mid_var_2","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":14,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"726"},{"$id":"727"},{"$id":"728"},{"$id":"729"},{"$id":"730"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_1_track_mid_var_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":1,"_blockCount":14,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":461},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"station_platforms_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"763"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"763"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"775"},"customOut":{"$id":"776"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"775"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"776"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"787"},"customOut":{"$id":"788"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"787"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"788"}},"children":[]}]}]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":967},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1080},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"station_platforms_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1260},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"830"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurveParent":{"$ref":"830"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"842"},"customOut":{"$id":"843"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"842"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"843"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1426},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"854"},"customOut":{"$id":"855"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1426},"$type":"TransformData"},"CurvePointTangent":{"$ref":"854"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1426},"$type":"TransformData"},"CurvePointTangent":{"$ref":"855"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":1530},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":1530},"$type":"TransformData"}},"children":[]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"}},"children":[{"name":"high_filler","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Fillers,All","_density":"High,All"},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"station","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Station,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"high_filler","components":{"Transform":{"position":{"x":0,"y":0,"z":900},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Fillers,All","_density":"High,All"},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"station","components":{"Transform":{"position":{"x":0,"y":0,"z":1260},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Station,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]}]},{"name":"routeChunk_default_1_track_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":4,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"912"},{"$id":"913"},{"$id":"914"},{"$id":"915"},{"$id":"916"},{"$id":"917"},{"$id":"918"},{"$id":"919"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_1_track_start","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"912"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"946"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"946"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"958"},"customOut":{"$id":"959"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"958"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"959"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"970"},"customOut":{"$id":"971"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"970"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"971"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"984"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"984"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"996"},"customOut":{"$id":"997"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"996"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"997"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1008"},"customOut":{"$id":"1009"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1008"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1009"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1022"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"1022"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1034"},"customOut":{"$id":"1035"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1034"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1035"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1046"},"customOut":{"$id":"1047"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1046"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1047"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1060"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"1060"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1072"},"customOut":{"$id":"1073"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1072"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1073"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1084"},"customOut":{"$id":"1085"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1084"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1085"}},"children":[]}]}]}]},{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1105"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurveParent":{"$ref":"1105"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1117"},"customOut":{"$id":"1118"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1117"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":80,"z":608.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1118"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":608.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1129"},"customOut":{"$id":"1130"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":80,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1129"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":608.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1130"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":7,"z":510},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":510},"$type":"TransformData"}},"children":[]}]}]},{"name":"group","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1170"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"1170"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1182"},"customOut":{"$id":"1183"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1182"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1183"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1194"},"customOut":{"$id":"1195"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1194"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1195"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1208"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"1208"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1220"},"customOut":{"$id":"1221"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1220"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1221"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1232"},"customOut":{"$id":"1233"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1232"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1233"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1246"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"1246"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1258"},"customOut":{"$id":"1259"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1258"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1259"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1270"},"customOut":{"$id":"1271"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1270"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1271"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1284"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"1284"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1296"},"customOut":{"$id":"1297"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1296"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1297"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1308"},"customOut":{"$id":"1309"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1308"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1309"}},"children":[]}]}]}]},{"name":"obstacle_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1329"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"},"CurveParent":{"$ref":"1329"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1341"},"customOut":{"$id":"1342"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1341"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":80,"z":608.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1342"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":608.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1353"},"customOut":{"$id":"1354"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":80,"z":510},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1353"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":608.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1354"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":510},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":510},"$type":"TransformData"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"blockers","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"}},"children":[]}]}]}]},"Default2Tracks":{"name":"Default2Tracks","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"routeChunk_default_2_tracks_end","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_2_tracks_end","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":105},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":105},"$type":"TransformData"}},"children":[]}]}]},{"name":"routeChunk_default_2_tracks_mid_var_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":16,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"50"},{"$id":"51"},{"$id":"52"},{"$id":"53"},{"$id":"54"},{"$id":"55"},{"$id":"56"},{"$id":"57"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_2_tracks_mid_var_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"pillar_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"pillar_environment (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"pillar_environment (2)","components":{"Transform":{"position":{"x":0,"y":0,"z":720},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]},{"name":"pillar_environment (3)","components":{"Transform":{"position":{"x":0,"y":0,"z":1080},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":16,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"pillars_short","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"98"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-135},"$type":"TransformData"},"CurveParent":{"$ref":"98"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"110"},"customOut":{"$id":"111"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"110"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"111"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-134},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"122"},"customOut":{"$id":"123"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-134},"$type":"TransformData"},"CurvePointTangent":{"$ref":"122"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-134},"$type":"TransformData"},"CurvePointTangent":{"$ref":"123"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"136"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-105},"$type":"TransformData"},"CurveParent":{"$ref":"136"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"148"},"customOut":{"$id":"149"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"148"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"149"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-104},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"160"},"customOut":{"$id":"161"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-104},"$type":"TransformData"},"CurvePointTangent":{"$ref":"160"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-104},"$type":"TransformData"},"CurvePointTangent":{"$ref":"161"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-75},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"174"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-75},"$type":"TransformData"},"CurveParent":{"$ref":"174"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-75},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"186"},"customOut":{"$id":"187"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"186"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-75},"$type":"TransformData"},"CurvePointTangent":{"$ref":"187"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-74},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"198"},"customOut":{"$id":"199"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-74},"$type":"TransformData"},"CurvePointTangent":{"$ref":"198"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-74},"$type":"TransformData"},"CurvePointTangent":{"$ref":"199"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-45},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"212"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-45},"$type":"TransformData"},"CurveParent":{"$ref":"212"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-45},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"224"},"customOut":{"$id":"225"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-45},"$type":"TransformData"},"CurvePointTangent":{"$ref":"224"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-45},"$type":"TransformData"},"CurvePointTangent":{"$ref":"225"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-44},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"236"},"customOut":{"$id":"237"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-44},"$type":"TransformData"},"CurvePointTangent":{"$ref":"236"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-44},"$type":"TransformData"},"CurvePointTangent":{"$ref":"237"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-15},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"250"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-15},"$type":"TransformData"},"CurveParent":{"$ref":"250"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-15},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"262"},"customOut":{"$id":"263"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-15},"$type":"TransformData"},"CurvePointTangent":{"$ref":"262"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-15},"$type":"TransformData"},"CurvePointTangent":{"$ref":"263"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-14},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"274"},"customOut":{"$id":"275"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-14},"$type":"TransformData"},"CurvePointTangent":{"$ref":"274"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-14},"$type":"TransformData"},"CurvePointTangent":{"$ref":"275"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":39},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (3)","components":{"Transform":{"position":{"x":0,"y":0,"z":50},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"308"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"308"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"320"},"customOut":{"$id":"321"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"320"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"321"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"332"},"customOut":{"$id":"333"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"332"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"333"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"346"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"346"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"358"},"customOut":{"$id":"359"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"358"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"359"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"370"},"customOut":{"$id":"371"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"370"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"371"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"384"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"384"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"396"},"customOut":{"$id":"397"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"396"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"397"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"408"},"customOut":{"$id":"409"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"408"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"409"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"422"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"422"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"434"},"customOut":{"$id":"435"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"434"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"435"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"446"},"customOut":{"$id":"447"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"446"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"447"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"460"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"460"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"472"},"customOut":{"$id":"473"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"472"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"473"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"484"},"customOut":{"$id":"485"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"484"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"485"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"498"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"498"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"510"},"customOut":{"$id":"511"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"510"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"511"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"522"},"customOut":{"$id":"523"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"522"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"523"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"536"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"536"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"548"},"customOut":{"$id":"549"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"548"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"549"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"560"},"customOut":{"$id":"561"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"560"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"561"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"574"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"574"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"586"},"customOut":{"$id":"587"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"586"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"587"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"598"},"customOut":{"$id":"599"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"598"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"599"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":398},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":410},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"630"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurveParent":{"$ref":"630"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"642"},"customOut":{"$id":"643"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"642"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"643"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"654"},"customOut":{"$id":"655"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"654"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"655"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"668"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurveParent":{"$ref":"668"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"680"},"customOut":{"$id":"681"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"680"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"681"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"692"},"customOut":{"$id":"693"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"692"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"693"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"706"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurveParent":{"$ref":"706"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"718"},"customOut":{"$id":"719"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"718"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"719"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"730"},"customOut":{"$id":"731"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"730"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"731"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"744"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"744"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"756"},"customOut":{"$id":"757"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"756"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"757"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"768"},"customOut":{"$id":"769"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"768"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"769"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"782"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurveParent":{"$ref":"782"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"794"},"customOut":{"$id":"795"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"794"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"795"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"806"},"customOut":{"$id":"807"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePointTangent":{"$ref":"806"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":616},"$type":"TransformData"},"CurvePointTangent":{"$ref":"807"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"820"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurveParent":{"$ref":"820"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"832"},"customOut":{"$id":"833"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"832"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"833"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"844"},"customOut":{"$id":"845"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePointTangent":{"$ref":"844"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":646},"$type":"TransformData"},"CurvePointTangent":{"$ref":"845"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"858"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurveParent":{"$ref":"858"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"870"},"customOut":{"$id":"871"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"870"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"871"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"882"},"customOut":{"$id":"883"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"882"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"883"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"896"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurveParent":{"$ref":"896"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"908"},"customOut":{"$id":"909"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"908"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"909"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"920"},"customOut":{"$id":"921"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"920"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"921"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":758},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":770},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"952"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"952"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"964"},"customOut":{"$id":"965"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"964"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"965"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"976"},"customOut":{"$id":"977"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"976"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"977"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"990"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"990"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1002"},"customOut":{"$id":"1003"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1002"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1003"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1014"},"customOut":{"$id":"1015"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1014"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1015"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1028"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"1028"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1040"},"customOut":{"$id":"1041"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1040"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1041"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1052"},"customOut":{"$id":"1053"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1052"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1053"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1066"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurveParent":{"$ref":"1066"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1078"},"customOut":{"$id":"1079"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1078"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1079"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1090"},"customOut":{"$id":"1091"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1090"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":976},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1091"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1104"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurveParent":{"$ref":"1104"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1116"},"customOut":{"$id":"1117"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1116"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1005},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1117"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1128"},"customOut":{"$id":"1129"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1128"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1006},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1129"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1142"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurveParent":{"$ref":"1142"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1154"},"customOut":{"$id":"1155"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1154"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1155"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1166"},"customOut":{"$id":"1167"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1166"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1167"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1180"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurveParent":{"$ref":"1180"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1192"},"customOut":{"$id":"1193"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1192"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1193"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1204"},"customOut":{"$id":"1205"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1204"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1205"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1118},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (2)","components":{"Transform":{"position":{"x":0,"y":0,"z":1130},"$type":"TransformData"}},"children":[]}]}]},{"name":"routeChunk_default_2_tracks_mid_var_2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":16,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"1237"},{"$id":"1238"},{"$id":"1239"},{"$id":"1240"},{"$id":"1241"},{"$id":"1242"},{"$id":"1243"},{"$id":"1244"},{"$id":"1245"},{"$id":"1246"},{"$id":"1247"},{"$id":"1248"},{"$id":"1249"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_2_tracks_mid_var_2","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"pillars_long","components":{"Transform":{"position":{"x":0,"y":0,"z":-180},"$type":"TransformData"}},"children":[{"name":"roof_setup","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":30},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1263"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":30},"$type":"TransformData"},"CurveParent":{"$ref":"1263"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":30},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1275"},"customOut":{"$id":"1276"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1275"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":30},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1276"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":181},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1287"},"customOut":{"$id":"1288"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1287"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":181},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1288"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1301"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"1301"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1313"},"customOut":{"$id":"1314"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1313"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1314"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1325"},"customOut":{"$id":"1326"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1325"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1326"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1339"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"1339"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1351"},"customOut":{"$id":"1352"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1351"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1352"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1363"},"customOut":{"$id":"1364"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1363"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":541},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1364"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1377"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurveParent":{"$ref":"1377"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1389"},"customOut":{"$id":"1390"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1389"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":570},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1390"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1401"},"customOut":{"$id":"1402"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1401"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1402"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1415"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurveParent":{"$ref":"1415"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1427"},"customOut":{"$id":"1428"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1427"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1428"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1439"},"customOut":{"$id":"1440"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1439"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":901},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1440"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":930},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1453"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":930},"$type":"TransformData"},"CurveParent":{"$ref":"1453"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":930},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1465"},"customOut":{"$id":"1466"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":930},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1465"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":930},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1466"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":1081},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1477"},"customOut":{"$id":"1478"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":1081},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1477"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":1081},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1478"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":1110},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1491"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":1110},"$type":"TransformData"},"CurveParent":{"$ref":"1491"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":1110},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1503"},"customOut":{"$id":"1504"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1503"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1504"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":1261},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1515"},"customOut":{"$id":"1516"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1515"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1516"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":91,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1529"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":91,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"1529"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":91,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1541"},"customOut":{"$id":"1542"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1541"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1542"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":91,"z":1441},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1553"},"customOut":{"$id":"1554"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":91,"z":1441},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1553"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":91,"z":1441},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1554"}},"children":[]}]}]}]}]},{"name":"pillar_group_place (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1575"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"1575"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1587"},"customOut":{"$id":"1588"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1587"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1588"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1599"},"customOut":{"$id":"1600"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1599"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":136},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1600"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1613"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"1613"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1625"},"customOut":{"$id":"1626"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1625"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1626"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1637"},"customOut":{"$id":"1638"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1637"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":166},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1638"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1651"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"1651"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1663"},"customOut":{"$id":"1664"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1663"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1664"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1675"},"customOut":{"$id":"1676"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1675"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":196},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1676"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1689"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurveParent":{"$ref":"1689"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1701"},"customOut":{"$id":"1702"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1701"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":225},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1702"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1713"},"customOut":{"$id":"1714"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1713"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":226},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1714"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":251},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (4)","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1745"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"1745"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1757"},"customOut":{"$id":"1758"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1757"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1758"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1769"},"customOut":{"$id":"1770"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1769"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1770"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1783"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"1783"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1795"},"customOut":{"$id":"1796"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1795"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1796"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1807"},"customOut":{"$id":"1808"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1807"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":346},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1808"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1821"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"1821"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1833"},"customOut":{"$id":"1834"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1833"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1834"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1845"},"customOut":{"$id":"1846"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1845"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":376},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1846"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1859"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurveParent":{"$ref":"1859"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1871"},"customOut":{"$id":"1872"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1871"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1872"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1883"},"customOut":{"$id":"1884"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1883"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1884"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":431},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":450},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1915"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurveParent":{"$ref":"1915"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1927"},"customOut":{"$id":"1928"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1927"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":495},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1928"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1939"},"customOut":{"$id":"1940"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1939"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1940"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1953"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurveParent":{"$ref":"1953"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1965"},"customOut":{"$id":"1966"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1965"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1966"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1977"},"customOut":{"$id":"1978"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1977"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1978"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1991"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurveParent":{"$ref":"1991"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2003"},"customOut":{"$id":"2004"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2003"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2004"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2015"},"customOut":{"$id":"2016"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2015"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":556},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2016"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2029"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"2029"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2041"},"customOut":{"$id":"2042"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2041"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2042"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2053"},"customOut":{"$id":"2054"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2053"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2054"}},"children":[]}]}]}]},{"name":"pillar_group_place (3)","components":{"Transform":{"position":{"x":0,"y":0,"z":630},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2075"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurveParent":{"$ref":"2075"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2087"},"customOut":{"$id":"2088"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2087"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":675},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2088"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2099"},"customOut":{"$id":"2100"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2099"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":676},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2100"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2113"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurveParent":{"$ref":"2113"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2125"},"customOut":{"$id":"2126"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2125"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":705},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2126"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2137"},"customOut":{"$id":"2138"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2137"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":706},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2138"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2151"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"2151"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2163"},"customOut":{"$id":"2164"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2163"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2164"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2175"},"customOut":{"$id":"2176"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2175"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2176"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2189"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurveParent":{"$ref":"2189"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2201"},"customOut":{"$id":"2202"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2201"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2202"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2213"},"customOut":{"$id":"2214"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2213"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2214"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":791},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (5)","components":{"Transform":{"position":{"x":0,"y":0,"z":810},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2245"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurveParent":{"$ref":"2245"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2257"},"customOut":{"$id":"2258"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2257"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2258"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2269"},"customOut":{"$id":"2270"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2269"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":856},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2270"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2283"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"2283"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2295"},"customOut":{"$id":"2296"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2295"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2296"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2307"},"customOut":{"$id":"2308"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2307"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2308"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2321"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"2321"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2333"},"customOut":{"$id":"2334"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2333"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2334"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2345"},"customOut":{"$id":"2346"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2345"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":916},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2346"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2359"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"2359"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2371"},"customOut":{"$id":"2372"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2371"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2372"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2383"},"customOut":{"$id":"2384"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2383"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":946},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2384"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":971},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (7)","components":{"Transform":{"position":{"x":0,"y":0,"z":990},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2415"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurveParent":{"$ref":"2415"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2427"},"customOut":{"$id":"2428"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2427"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2428"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2439"},"customOut":{"$id":"2440"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2439"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2440"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2453"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurveParent":{"$ref":"2453"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2465"},"customOut":{"$id":"2466"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2465"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2466"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2477"},"customOut":{"$id":"2478"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2477"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2478"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1095},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2491"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1095},"$type":"TransformData"},"CurveParent":{"$ref":"2491"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1095},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2503"},"customOut":{"$id":"2504"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1095},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2503"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1095},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2504"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1096},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2515"},"customOut":{"$id":"2516"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1096},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2515"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1096},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2516"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1125},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2529"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1125},"$type":"TransformData"},"CurveParent":{"$ref":"2529"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1125},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2541"},"customOut":{"$id":"2542"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1125},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2541"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1125},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2542"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1126},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2553"},"customOut":{"$id":"2554"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1126},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2553"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1126},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2554"}},"children":[]}]}]}]},{"name":"pillar_group_place (6)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"}},"children":[]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1215},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2575"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1215},"$type":"TransformData"},"CurveParent":{"$ref":"2575"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1215},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2587"},"customOut":{"$id":"2588"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1215},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2587"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1215},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2588"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1216},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2599"},"customOut":{"$id":"2600"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1216},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2599"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1216},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2600"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1245},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2613"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1245},"$type":"TransformData"},"CurveParent":{"$ref":"2613"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1245},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2625"},"customOut":{"$id":"2626"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2625"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2626"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1246},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2637"},"customOut":{"$id":"2638"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2637"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2638"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2651"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurveParent":{"$ref":"2651"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2663"},"customOut":{"$id":"2664"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2663"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2664"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1276},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2675"},"customOut":{"$id":"2676"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1276},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2675"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1276},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2676"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1305},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2689"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1305},"$type":"TransformData"},"CurveParent":{"$ref":"2689"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1305},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2701"},"customOut":{"$id":"2702"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2701"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2702"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1306},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2713"},"customOut":{"$id":"2714"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2713"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2714"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1331},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"pillar_group_place (2)","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"}},"children":[]}]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":16,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"pillars_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Pillars,All","_density":""},"_blockCount":16,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]}]},{"name":"routeChunk_default_2_tracks_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"2758"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_2_tracks_start","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"environment_base","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":26.2,"z":120},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":26.2,"z":120},"$type":"TransformData"}},"children":[]}]}]}]},"routeChunk_default_epic":{"name":"routeChunk_default_epic","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":false,"_blockCount":16,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"9"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_epic","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"9"}},"children":[{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"37"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurveParent":{"$ref":"37"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"49"},"customOut":{"$id":"50"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"49"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"50"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"61"},"customOut":{"$id":"62"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"61"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"62"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"75"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"75"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"87"},"customOut":{"$id":"88"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"87"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"88"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"99"},"customOut":{"$id":"100"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"99"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"100"}},"children":[]}]}]}]}]},{"name":"endCoins","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"120"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurveParent":{"$ref":"120"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"132"},"customOut":{"$id":"133"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"132"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1245},"$type":"TransformData"},"CurvePointTangent":{"$ref":"133"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"144"},"customOut":{"$id":"145"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"144"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1246},"$type":"TransformData"},"CurvePointTangent":{"$ref":"145"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"158"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurveParent":{"$ref":"158"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"170"},"customOut":{"$id":"171"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"170"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"171"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"182"},"customOut":{"$id":"183"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"182"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"183"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"196"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"196"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"208"},"customOut":{"$id":"209"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"208"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"220"},"customOut":{"$id":"221"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"220"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"234"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurveParent":{"$ref":"234"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"246"},"customOut":{"$id":"247"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"246"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1305},"$type":"TransformData"},"CurvePointTangent":{"$ref":"247"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"258"},"customOut":{"$id":"259"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"258"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":10,"y":0,"z":1306},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]}]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"272"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurveParent":{"$ref":"272"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"284"},"customOut":{"$id":"285"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"284"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"285"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"296"},"customOut":{"$id":"297"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"296"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"310"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurveParent":{"$ref":"310"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"322"},"customOut":{"$id":"323"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"322"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"323"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"334"},"customOut":{"$id":"335"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"334"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"335"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"348"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurveParent":{"$ref":"348"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"360"},"customOut":{"$id":"361"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"360"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"361"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"372"},"customOut":{"$id":"373"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"372"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"373"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"386"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurveParent":{"$ref":"386"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"398"},"customOut":{"$id":"399"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"398"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"399"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"410"},"customOut":{"$id":"411"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"410"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"411"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"424"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurveParent":{"$ref":"424"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"436"},"customOut":{"$id":"437"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePointTangent":{"$ref":"436"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1320},"$type":"TransformData"},"CurvePointTangent":{"$ref":"437"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"448"},"customOut":{"$id":"449"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePointTangent":{"$ref":"448"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1321},"$type":"TransformData"},"CurvePointTangent":{"$ref":"449"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"462"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurveParent":{"$ref":"462"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"474"},"customOut":{"$id":"475"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePointTangent":{"$ref":"474"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1350},"$type":"TransformData"},"CurvePointTangent":{"$ref":"475"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"486"},"customOut":{"$id":"487"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePointTangent":{"$ref":"486"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1351},"$type":"TransformData"},"CurvePointTangent":{"$ref":"487"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"500"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurveParent":{"$ref":"500"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"512"},"customOut":{"$id":"513"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePointTangent":{"$ref":"512"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1380},"$type":"TransformData"},"CurvePointTangent":{"$ref":"513"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"524"},"customOut":{"$id":"525"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePointTangent":{"$ref":"524"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1381},"$type":"TransformData"},"CurvePointTangent":{"$ref":"525"}},"children":[]}]}]}]}]}]},{"name":"Randomizer","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"roofRun","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":240},"$type":"TransformData"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":451},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":605},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":832},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":990},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"groundRun","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":145.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"636"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurveParent":{"$ref":"636"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"648"},"customOut":{"$id":"649"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"648"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-4.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"649"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"660"},"customOut":{"$id":"661"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"660"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":-3.834534},"$type":"TransformData"},"CurvePointTangent":{"$ref":"661"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"674"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurveParent":{"$ref":"674"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"686"},"customOut":{"$id":"687"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"686"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":25.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"687"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"698"},"customOut":{"$id":"699"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"698"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":26.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"699"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"712"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurveParent":{"$ref":"712"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"724"},"customOut":{"$id":"725"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"724"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":55.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"725"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"736"},"customOut":{"$id":"737"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"736"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":56.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"737"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"750"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurveParent":{"$ref":"750"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"762"},"customOut":{"$id":"763"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"762"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":85.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"763"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"774"},"customOut":{"$id":"775"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"774"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":86.16547},"$type":"TransformData"},"CurvePointTangent":{"$ref":"775"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"788"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurveParent":{"$ref":"788"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"800"},"customOut":{"$id":"801"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"800"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":115.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"801"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"812"},"customOut":{"$id":"813"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"812"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":116.1655},"$type":"TransformData"},"CurvePointTangent":{"$ref":"813"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":137.1655},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":432.2206},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":282.2206},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"842"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":282.2206},"$type":"TransformData"},"CurveParent":{"$ref":"842"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":282.2206},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"854"},"customOut":{"$id":"855"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":282.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"854"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":282.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"855"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":283.2206},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"866"},"customOut":{"$id":"867"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":283.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"866"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":283.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"867"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":312.2206},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"880"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":312.2206},"$type":"TransformData"},"CurveParent":{"$ref":"880"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":312.2206},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"892"},"customOut":{"$id":"893"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":312.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"892"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":312.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"893"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":313.2206},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"904"},"customOut":{"$id":"905"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":313.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"904"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":313.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"905"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":342.2206},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"918"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":342.2206},"$type":"TransformData"},"CurveParent":{"$ref":"918"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":342.2206},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"930"},"customOut":{"$id":"931"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":342.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"930"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":342.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"931"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":343.2206},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"942"},"customOut":{"$id":"943"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":343.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"942"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":343.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"943"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":372.2206},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"956"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":372.2206},"$type":"TransformData"},"CurveParent":{"$ref":"956"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":372.2206},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"968"},"customOut":{"$id":"969"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":372.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"968"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":372.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"969"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":373.2206},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"980"},"customOut":{"$id":"981"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":373.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"980"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":373.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"981"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":402.2206},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"994"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":402.2206},"$type":"TransformData"},"CurveParent":{"$ref":"994"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":402.2206},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1006"},"customOut":{"$id":"1007"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":402.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1006"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":402.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1007"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":403.2206},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1018"},"customOut":{"$id":"1019"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":403.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1018"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":403.2206},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1019"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":423.2206},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":719.3317},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":569.3317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1047"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":569.3317},"$type":"TransformData"},"CurveParent":{"$ref":"1047"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":569.3317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1059"},"customOut":{"$id":"1060"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":569.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1059"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":569.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1060"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":570.3317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1071"},"customOut":{"$id":"1072"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":570.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1071"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":570.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1072"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":599.3317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1085"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":599.3317},"$type":"TransformData"},"CurveParent":{"$ref":"1085"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":599.3317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1097"},"customOut":{"$id":"1098"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":599.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1097"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":599.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1098"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":600.3317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1109"},"customOut":{"$id":"1110"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":600.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1109"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":600.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1110"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":629.3317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1123"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":629.3317},"$type":"TransformData"},"CurveParent":{"$ref":"1123"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":629.3317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1135"},"customOut":{"$id":"1136"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":629.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1135"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":629.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1136"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":630.3317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1147"},"customOut":{"$id":"1148"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":630.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1147"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":630.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1148"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":659.3317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1161"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":659.3317},"$type":"TransformData"},"CurveParent":{"$ref":"1161"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":659.3317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1173"},"customOut":{"$id":"1174"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":659.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1173"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":659.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1174"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":660.3317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1185"},"customOut":{"$id":"1186"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":660.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1185"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":660.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1186"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":689.3317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1199"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":689.3317},"$type":"TransformData"},"CurveParent":{"$ref":"1199"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":689.3317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1211"},"customOut":{"$id":"1212"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":689.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1211"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":689.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1212"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":690.3317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1223"},"customOut":{"$id":"1224"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":690.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1223"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":690.3317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1224"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":712.3317},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"train 1","components":{"Transform":{"position":{"x":0,"y":0,"z":1007.032},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1252"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0317},"$type":"TransformData"},"CurveParent":{"$ref":"1252"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1264"},"customOut":{"$id":"1265"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1264"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":857.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1265"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1276"},"customOut":{"$id":"1277"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1276"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":858.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1277"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1290"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0317},"$type":"TransformData"},"CurveParent":{"$ref":"1290"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1302"},"customOut":{"$id":"1303"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1302"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":887.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1303"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1314"},"customOut":{"$id":"1315"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1314"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":888.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1315"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1328"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0317},"$type":"TransformData"},"CurveParent":{"$ref":"1328"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1340"},"customOut":{"$id":"1341"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1340"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":917.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1341"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1352"},"customOut":{"$id":"1353"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1352"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":918.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1353"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1366"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0317},"$type":"TransformData"},"CurveParent":{"$ref":"1366"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1378"},"customOut":{"$id":"1379"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1378"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":947.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1379"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1390"},"customOut":{"$id":"1391"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1390"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":948.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1391"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0317},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1404"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0317},"$type":"TransformData"},"CurveParent":{"$ref":"1404"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0317},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1416"},"customOut":{"$id":"1417"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1416"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":977.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1417"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0317},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1428"},"customOut":{"$id":"1429"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1428"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":978.0317},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1429"}},"children":[]}]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":999.0317},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":2,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"epic_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Epic,All","_density":""},"_blockCount":12,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":30},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":30},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":-10,"y":0,"z":1260},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":20,"y":7,"z":1410},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":1410},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_ramp_1":{"name":"routeChunk_default_ramp_1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_ramp_1","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_trains","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":56,"z":195},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":56,"z":195},"$type":"TransformData"}},"children":[]}]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":30},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":30},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_train_tops_moving":{"name":"routeChunk_default_train_tops_moving","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":20,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"15"},{"$id":"16"},{"$id":"17"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"18"},{"$id":"19"},{"$id":"20"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"21"},{"$id":"22"},{"$id":"23"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$id":"37"},{"$id":"38"},{"$id":"39"},{"$id":"40"},{"$id":"41"},{"$id":"42"},{"$id":"43"},{"$id":"44"},{"$id":"45"},{"$id":"46"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_train_tops_moving","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":106},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":0,"z":135},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"15"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"83"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"83"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"95"},"customOut":{"$id":"96"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"95"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":338.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"96"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":338.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"107"},"customOut":{"$id":"108"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"107"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":338.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"108"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":315},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":435},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"153"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"153"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"165"},"customOut":{"$id":"166"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"165"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":533.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"166"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":533.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"177"},"customOut":{"$id":"178"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"177"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":533.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"178"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":135},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":20,"y":29,"z":135},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":210},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"197"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":210},"$type":"TransformData"},"CurveParent":{"$ref":"197"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":210},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"209"},"customOut":{"$id":"210"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":210},"$type":"TransformData"},"CurvePointTangent":{"$ref":"210"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":211},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"221"},"customOut":{"$id":"222"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":211},"$type":"TransformData"},"CurvePointTangent":{"$ref":"222"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"235"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurveParent":{"$ref":"235"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"247"},"customOut":{"$id":"248"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"247"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":240},"$type":"TransformData"},"CurvePointTangent":{"$ref":"248"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":241},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"259"},"customOut":{"$id":"260"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":241},"$type":"TransformData"},"CurvePointTangent":{"$ref":"260"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":270},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"273"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":270},"$type":"TransformData"},"CurveParent":{"$ref":"273"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":270},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"285"},"customOut":{"$id":"286"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"285"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":270},"$type":"TransformData"},"CurvePointTangent":{"$ref":"286"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":271},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"297"},"customOut":{"$id":"298"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":271},"$type":"TransformData"},"CurvePointTangent":{"$ref":"298"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":300},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"311"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":300},"$type":"TransformData"},"CurveParent":{"$ref":"311"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":300},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"323"},"customOut":{"$id":"324"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"323"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":300},"$type":"TransformData"},"CurvePointTangent":{"$ref":"324"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":301},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"335"},"customOut":{"$id":"336"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"335"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":301},"$type":"TransformData"},"CurvePointTangent":{"$ref":"336"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":330},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"349"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":330},"$type":"TransformData"},"CurveParent":{"$ref":"349"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":330},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"361"},"customOut":{"$id":"362"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"361"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":330},"$type":"TransformData"},"CurvePointTangent":{"$ref":"362"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":331},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"373"},"customOut":{"$id":"374"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"373"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":331},"$type":"TransformData"},"CurvePointTangent":{"$ref":"374"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":360},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"387"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":360},"$type":"TransformData"},"CurveParent":{"$ref":"387"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":360},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"399"},"customOut":{"$id":"400"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"399"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":360},"$type":"TransformData"},"CurvePointTangent":{"$ref":"400"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":361},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"411"},"customOut":{"$id":"412"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"411"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":361},"$type":"TransformData"},"CurvePointTangent":{"$ref":"412"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":390},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"425"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":390},"$type":"TransformData"},"CurveParent":{"$ref":"425"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":390},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"437"},"customOut":{"$id":"438"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"437"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":390},"$type":"TransformData"},"CurvePointTangent":{"$ref":"438"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":391},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"449"},"customOut":{"$id":"450"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"449"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":391},"$type":"TransformData"},"CurvePointTangent":{"$ref":"450"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":420},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"463"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":420},"$type":"TransformData"},"CurveParent":{"$ref":"463"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":420},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"475"},"customOut":{"$id":"476"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"475"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":420},"$type":"TransformData"},"CurvePointTangent":{"$ref":"476"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":421},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"487"},"customOut":{"$id":"488"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"487"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":421},"$type":"TransformData"},"CurvePointTangent":{"$ref":"488"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":450},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"501"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":450},"$type":"TransformData"},"CurveParent":{"$ref":"501"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":450},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"513"},"customOut":{"$id":"514"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"513"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"514"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":451},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"525"},"customOut":{"$id":"526"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"525"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":451},"$type":"TransformData"},"CurvePointTangent":{"$ref":"526"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":195},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":165},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":210},"$type":"TransformData"}},"children":[]}]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":461},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":0,"z":555},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"Randomizer":{"$ref":"18"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":555},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":615},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"610"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurveParent":{"$ref":"610"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"622"},"customOut":{"$id":"623"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"622"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":758.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"623"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":758.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"634"},"customOut":{"$id":"635"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"634"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":758.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"635"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":735},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":555},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":615},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":855},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":855},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"679"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":855},"$type":"TransformData"},"CurveParent":{"$ref":"679"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":855},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"691"},"customOut":{"$id":"692"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"691"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":953.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"692"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":953.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"703"},"customOut":{"$id":"704"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":855},"$type":"TransformData"},"CurvePointTangent":{"$ref":"703"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":953.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"704"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":555},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":29,"z":555},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":630},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"723"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":630},"$type":"TransformData"},"CurveParent":{"$ref":"723"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":630},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"735"},"customOut":{"$id":"736"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":630},"$type":"TransformData"},"CurvePointTangent":{"$ref":"735"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":630},"$type":"TransformData"},"CurvePointTangent":{"$ref":"736"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":631},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"747"},"customOut":{"$id":"748"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":631},"$type":"TransformData"},"CurvePointTangent":{"$ref":"747"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":631},"$type":"TransformData"},"CurvePointTangent":{"$ref":"748"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"761"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurveParent":{"$ref":"761"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"773"},"customOut":{"$id":"774"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"773"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":660},"$type":"TransformData"},"CurvePointTangent":{"$ref":"774"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":661},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"785"},"customOut":{"$id":"786"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":661},"$type":"TransformData"},"CurvePointTangent":{"$ref":"785"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":661},"$type":"TransformData"},"CurvePointTangent":{"$ref":"786"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":690},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"799"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":690},"$type":"TransformData"},"CurveParent":{"$ref":"799"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":690},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"811"},"customOut":{"$id":"812"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":690},"$type":"TransformData"},"CurvePointTangent":{"$ref":"811"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":690},"$type":"TransformData"},"CurvePointTangent":{"$ref":"812"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":691},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"823"},"customOut":{"$id":"824"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":691},"$type":"TransformData"},"CurvePointTangent":{"$ref":"823"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":691},"$type":"TransformData"},"CurvePointTangent":{"$ref":"824"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":720},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"837"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":720},"$type":"TransformData"},"CurveParent":{"$ref":"837"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":720},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"849"},"customOut":{"$id":"850"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":720},"$type":"TransformData"},"CurvePointTangent":{"$ref":"849"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":720},"$type":"TransformData"},"CurvePointTangent":{"$ref":"850"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":721},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"861"},"customOut":{"$id":"862"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"861"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":721},"$type":"TransformData"},"CurvePointTangent":{"$ref":"862"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":750},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"875"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":750},"$type":"TransformData"},"CurveParent":{"$ref":"875"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":750},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"887"},"customOut":{"$id":"888"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"887"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":750},"$type":"TransformData"},"CurvePointTangent":{"$ref":"888"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":751},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"899"},"customOut":{"$id":"900"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":751},"$type":"TransformData"},"CurvePointTangent":{"$ref":"899"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":751},"$type":"TransformData"},"CurvePointTangent":{"$ref":"900"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":780},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"913"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":780},"$type":"TransformData"},"CurveParent":{"$ref":"913"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":780},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"925"},"customOut":{"$id":"926"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"925"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":780},"$type":"TransformData"},"CurvePointTangent":{"$ref":"926"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":781},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"937"},"customOut":{"$id":"938"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":781},"$type":"TransformData"},"CurvePointTangent":{"$ref":"937"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":781},"$type":"TransformData"},"CurvePointTangent":{"$ref":"938"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":810},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"951"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":810},"$type":"TransformData"},"CurveParent":{"$ref":"951"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":810},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"963"},"customOut":{"$id":"964"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":810},"$type":"TransformData"},"CurvePointTangent":{"$ref":"963"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":810},"$type":"TransformData"},"CurvePointTangent":{"$ref":"964"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":811},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"975"},"customOut":{"$id":"976"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"975"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":811},"$type":"TransformData"},"CurvePointTangent":{"$ref":"976"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":840},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"989"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":840},"$type":"TransformData"},"CurveParent":{"$ref":"989"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":840},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1001"},"customOut":{"$id":"1002"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":840},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1001"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":840},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1002"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":841},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1013"},"customOut":{"$id":"1014"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":841},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1013"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":841},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1014"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":870},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1027"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":870},"$type":"TransformData"},"CurveParent":{"$ref":"1027"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":870},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1039"},"customOut":{"$id":"1040"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":870},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1039"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":870},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1040"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":871},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1051"},"customOut":{"$id":"1052"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":871},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1051"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":871},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1052"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":615},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":675},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":879},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":20,"y":0,"z":840},"$type":"TransformData"},"Randomizer":{"$ref":"21"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":975},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1035},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1119"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurveParent":{"$ref":"1119"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1131"},"customOut":{"$id":"1132"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1131"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":1178.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1132"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1178.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1143"},"customOut":{"$id":"1144"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1143"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1178.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1144"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1155},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":975},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1035},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1275},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":1275},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1188"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1275},"$type":"TransformData"},"CurveParent":{"$ref":"1188"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1275},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1200"},"customOut":{"$id":"1201"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1200"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":1373.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1201"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1373.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1212"},"customOut":{"$id":"1213"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":1275},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1212"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1373.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1213"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":20,"y":0,"z":975},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":20,"y":29,"z":975},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1050},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1232"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1050},"$type":"TransformData"},"CurveParent":{"$ref":"1232"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1050},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1244"},"customOut":{"$id":"1245"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1050},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1244"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1050},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1245"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1051},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1256"},"customOut":{"$id":"1257"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1051},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1256"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1051},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1257"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1270"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurveParent":{"$ref":"1270"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1282"},"customOut":{"$id":"1283"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1282"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1080},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1283"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1081},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1294"},"customOut":{"$id":"1295"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1081},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1294"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1081},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1295"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1110},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1308"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1110},"$type":"TransformData"},"CurveParent":{"$ref":"1308"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1110},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1320"},"customOut":{"$id":"1321"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1320"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1110},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1321"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1111},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1332"},"customOut":{"$id":"1333"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1111},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1332"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1111},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1333"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1140},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1346"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1140},"$type":"TransformData"},"CurveParent":{"$ref":"1346"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1140},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1358"},"customOut":{"$id":"1359"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1358"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1140},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1359"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1141},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1370"},"customOut":{"$id":"1371"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1370"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1141},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1371"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1170},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1384"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1170},"$type":"TransformData"},"CurveParent":{"$ref":"1384"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1170},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1396"},"customOut":{"$id":"1397"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1396"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1170},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1397"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1171},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1408"},"customOut":{"$id":"1409"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1408"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1171},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1409"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1200},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1422"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1200},"$type":"TransformData"},"CurveParent":{"$ref":"1422"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1200},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1434"},"customOut":{"$id":"1435"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1434"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1200},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1435"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1201},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1446"},"customOut":{"$id":"1447"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1446"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1201},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1447"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1230},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1460"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1230},"$type":"TransformData"},"CurveParent":{"$ref":"1460"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1230},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1472"},"customOut":{"$id":"1473"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1472"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1230},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1473"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1231},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1484"},"customOut":{"$id":"1485"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1484"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1231},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1485"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1260},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1498"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1260},"$type":"TransformData"},"CurveParent":{"$ref":"1498"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1260},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1510"},"customOut":{"$id":"1511"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1510"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1260},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1511"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1261},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1522"},"customOut":{"$id":"1523"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1522"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1261},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1523"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":20,"y":29,"z":1290},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1536"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1290},"$type":"TransformData"},"CurveParent":{"$ref":"1536"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1290},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1548"},"customOut":{"$id":"1549"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1548"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1290},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1549"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1291},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1560"},"customOut":{"$id":"1561"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1560"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1291},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1561"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1035},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":-20,"y":7,"z":990},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":990},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1005},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1035},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1301},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":0,"z":1395},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":0,"z":1260},"$type":"TransformData"},"Randomizer":{"$ref":"24"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":1395},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1455},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1642"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurveParent":{"$ref":"1642"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1654"},"customOut":{"$id":"1655"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1654"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":1598.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1655"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1598.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1666"},"customOut":{"$id":"1667"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":1500},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1666"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1598.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1667"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1575},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":1395},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1455},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1695},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1695},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1711"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1695},"$type":"TransformData"},"CurveParent":{"$ref":"1711"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1695},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1723"},"customOut":{"$id":"1724"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1695},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1723"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":1793.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1724"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1793.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1735"},"customOut":{"$id":"1736"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":1695},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1735"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1793.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1736"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":0,"z":1395},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":29,"z":1395},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1470},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1755"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1470},"$type":"TransformData"},"CurveParent":{"$ref":"1755"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1470},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1767"},"customOut":{"$id":"1768"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1470},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1767"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1470},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1768"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1471},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1779"},"customOut":{"$id":"1780"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1471},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1779"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1471},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1780"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1793"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurveParent":{"$ref":"1793"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1805"},"customOut":{"$id":"1806"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1805"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1500},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1806"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1501},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1817"},"customOut":{"$id":"1818"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1501},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1817"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1501},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1818"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1530},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1831"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1530},"$type":"TransformData"},"CurveParent":{"$ref":"1831"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1530},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1843"},"customOut":{"$id":"1844"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1530},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1843"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1530},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1844"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1531},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1855"},"customOut":{"$id":"1856"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1531},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1855"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1531},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1856"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1560},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1869"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1560},"$type":"TransformData"},"CurveParent":{"$ref":"1869"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1560},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1881"},"customOut":{"$id":"1882"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1560},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1881"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1560},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1882"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1561},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1893"},"customOut":{"$id":"1894"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1561},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1893"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1561},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1894"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1590},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1907"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1590},"$type":"TransformData"},"CurveParent":{"$ref":"1907"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1590},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1919"},"customOut":{"$id":"1920"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1590},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1919"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1590},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1920"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1591},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1931"},"customOut":{"$id":"1932"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1591},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1931"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1591},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1932"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1620},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1945"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1620},"$type":"TransformData"},"CurveParent":{"$ref":"1945"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1620},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1957"},"customOut":{"$id":"1958"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1620},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1957"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1620},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1958"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1621},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1969"},"customOut":{"$id":"1970"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1621},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1969"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1621},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1970"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1650},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1983"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1650},"$type":"TransformData"},"CurveParent":{"$ref":"1983"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1650},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1995"},"customOut":{"$id":"1996"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1650},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1995"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1650},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1996"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1651},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2007"},"customOut":{"$id":"2008"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1651},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2007"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1651},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2008"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1680},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2021"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1680},"$type":"TransformData"},"CurveParent":{"$ref":"2021"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1680},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2033"},"customOut":{"$id":"2034"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1680},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2033"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1680},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2034"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1681},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2045"},"customOut":{"$id":"2046"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1681},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2045"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1681},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2046"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1710},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2059"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1710},"$type":"TransformData"},"CurveParent":{"$ref":"2059"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1710},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2071"},"customOut":{"$id":"2072"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1710},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2071"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1710},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2072"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1711},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2083"},"customOut":{"$id":"2084"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1711},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2083"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1711},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2084"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1455},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1425},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1425},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":1530},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_train_tops_moving_combined":{"name":"routeChunk_default_train_tops_moving_combined","components":{"Transform":{"position":{"x":0,"y":413.3,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":20,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"12"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"13"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"14"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"37"},{"$id":"38"},{"$id":"39"},{"$id":"40"},{"$id":"41"},{"$id":"42"},{"$id":"43"},{"$id":"44"},{"$id":"45"},{"$id":"46"},{"$id":"47"},{"$id":"48"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"49"},{"$id":"50"},{"$id":"51"},{"$id":"52"},{"$id":"53"},{"$id":"54"},{"$id":"55"},{"$id":"56"},{"$id":"57"},{"$id":"58"},{"$id":"59"},{"$id":"60"},{"$id":"61"},{"$id":"62"},{"$id":"63"},{"$id":"64"},{"$id":"65"},{"$id":"66"},{"$id":"67"},{"$id":"68"},{"$id":"69"},{"$id":"70"},{"$id":"71"},{"$id":"72"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_train_tops_moving_combined","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_start","components":{"Transform":{"position":{"x":0,"y":413.3,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":60},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":439.5,"z":105},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":439.5,"z":105},"$type":"TransformData"}},"children":[]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":120},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":120},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":150},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":442.3,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"146"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":442.3,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"146"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":442.3,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"158"},"customOut":{"$id":"159"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":442.3,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"158"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":442.3,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"159"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":442.3,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"170"},"customOut":{"$id":"171"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":442.3,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"170"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":442.3,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"171"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":195},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"184"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":195},"$type":"TransformData"},"CurveParent":{"$ref":"184"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":195},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"196"},"customOut":{"$id":"197"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"196"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":195},"$type":"TransformData"},"CurvePointTangent":{"$ref":"197"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":296},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"208"},"customOut":{"$id":"209"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":296},"$type":"TransformData"},"CurvePointTangent":{"$ref":"208"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":296},"$type":"TransformData"},"CurvePointTangent":{"$ref":"209"}},"children":[]}]}]}]}]},{"name":"side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":395.3984},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"trains_side_ground","components":{"Transform":{"position":{"x":0,"y":413.3,"z":395.3984},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":515.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":515.3984},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":665.3984},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":695.3984},"$type":"TransformData"}},"children":[]}]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":395.3984},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":260.3984},"$type":"TransformData"},"Randomizer":{"$ref":"25"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":455.3984},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"284"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurveParent":{"$ref":"284"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"296"},"customOut":{"$id":"297"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"296"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":598.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"297"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":598.7854},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"308"},"customOut":{"$id":"309"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"308"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":598.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"309"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":575.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":455.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":695.3984},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":695.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"353"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":695.3984},"$type":"TransformData"},"CurveParent":{"$ref":"353"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":695.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"365"},"customOut":{"$id":"366"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":695.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"365"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":793.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"366"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":793.7854},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"377"},"customOut":{"$id":"378"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":695.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"377"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":793.7854},"$type":"TransformData"},"CurvePointTangent":{"$ref":"378"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":395.3984},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":470.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"397"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":470.3984},"$type":"TransformData"},"CurveParent":{"$ref":"397"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":470.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"409"},"customOut":{"$id":"410"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":470.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"409"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":470.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"410"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":471.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"421"},"customOut":{"$id":"422"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":471.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"421"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":471.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"422"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"435"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurveParent":{"$ref":"435"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"447"},"customOut":{"$id":"448"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"447"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":500.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"448"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":501.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"459"},"customOut":{"$id":"460"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":501.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"459"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":501.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"460"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":530.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"473"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":530.3984},"$type":"TransformData"},"CurveParent":{"$ref":"473"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":530.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"485"},"customOut":{"$id":"486"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":530.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"485"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":530.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"486"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":531.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"497"},"customOut":{"$id":"498"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":531.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"497"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":531.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"498"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":560.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"511"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":560.3984},"$type":"TransformData"},"CurveParent":{"$ref":"511"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":560.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"523"},"customOut":{"$id":"524"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":560.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"523"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":560.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"524"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":561.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"535"},"customOut":{"$id":"536"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":561.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"535"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":561.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"536"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":590.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"549"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":590.3984},"$type":"TransformData"},"CurveParent":{"$ref":"549"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":590.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"561"},"customOut":{"$id":"562"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":590.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"561"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":590.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"562"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":591.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"573"},"customOut":{"$id":"574"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":591.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"573"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":591.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"574"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":620.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"587"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":620.3984},"$type":"TransformData"},"CurveParent":{"$ref":"587"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":620.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"599"},"customOut":{"$id":"600"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":620.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"599"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":620.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"600"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":621.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"611"},"customOut":{"$id":"612"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":621.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"611"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":621.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"612"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":650.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"625"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":650.3984},"$type":"TransformData"},"CurveParent":{"$ref":"625"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":650.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"637"},"customOut":{"$id":"638"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":650.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"637"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":650.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"638"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":651.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"649"},"customOut":{"$id":"650"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":651.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"649"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":651.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"650"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":680.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"663"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":680.3984},"$type":"TransformData"},"CurveParent":{"$ref":"663"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":680.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"675"},"customOut":{"$id":"676"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":680.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"675"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":680.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"676"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":681.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"687"},"customOut":{"$id":"688"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":681.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"687"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":681.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"688"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":710.3984},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"701"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":710.3984},"$type":"TransformData"},"CurveParent":{"$ref":"701"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":710.3984},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"713"},"customOut":{"$id":"714"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":710.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"713"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":710.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"714"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":711.3984},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"725"},"customOut":{"$id":"726"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":711.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"725"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":711.3984},"$type":"TransformData"},"CurvePointTangent":{"$ref":"726"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":455.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":425.3984},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":425.3984},"$type":"TransformData"}},"children":[]}]},{"name":"3 moving sides","components":{"Transform":{"position":{"x":0,"y":413.3,"z":395.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":454.3984},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":662.3984},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"middle","components":{"Transform":{"position":{"x":0,"y":413.3,"z":448.5102},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"1 moving","components":{"Transform":{"position":{"x":0,"y":413.3,"z":13.31934},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":283.3193},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"3 moving coins","components":{"Transform":{"position":{"x":0,"y":413.3,"z":608.8612},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":270.8612},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":418.8612},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":842.0803},"$type":"TransformData"},"Randomizer":{"$ref":"12"}},"children":[{"name":"trains_side_ground","components":{"Transform":{"position":{"x":0,"y":413.3,"z":842.0803},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":962.0803},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":962.0803},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1112.08},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1142.08},"$type":"TransformData"}},"children":[]}]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":842.0803},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":707.0803},"$type":"TransformData"},"Randomizer":{"$ref":"37"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":842.0803},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":902.0803},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"888"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurveParent":{"$ref":"888"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"900"},"customOut":{"$id":"901"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"900"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1045.467},"$type":"TransformData"},"CurvePointTangent":{"$ref":"901"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1045.467},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"912"},"customOut":{"$id":"913"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":947.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"912"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1045.467},"$type":"TransformData"},"CurvePointTangent":{"$ref":"913"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1022.08},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":842.0803},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":902.0803},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1142.08},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1142.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"957"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1142.08},"$type":"TransformData"},"CurveParent":{"$ref":"957"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1142.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"969"},"customOut":{"$id":"970"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1142.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"969"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1240.467},"$type":"TransformData"},"CurvePointTangent":{"$ref":"970"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1240.467},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"981"},"customOut":{"$id":"982"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1142.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"981"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1240.467},"$type":"TransformData"},"CurvePointTangent":{"$ref":"982"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":842.0803},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":842.0803},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":917.0803},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1001"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":917.0803},"$type":"TransformData"},"CurveParent":{"$ref":"1001"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":917.0803},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1013"},"customOut":{"$id":"1014"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":917.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1013"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":917.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1014"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":918.0803},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1025"},"customOut":{"$id":"1026"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":918.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1025"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":918.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1026"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1039"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurveParent":{"$ref":"1039"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1051"},"customOut":{"$id":"1052"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1051"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":947.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1052"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":948.0803},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1063"},"customOut":{"$id":"1064"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":948.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1063"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":948.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1064"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":977.0803},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1077"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":977.0803},"$type":"TransformData"},"CurveParent":{"$ref":"1077"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":977.0803},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1089"},"customOut":{"$id":"1090"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":977.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1089"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":977.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1090"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":978.0803},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1101"},"customOut":{"$id":"1102"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":978.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1101"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":978.0803},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1102"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1007.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1115"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1007.08},"$type":"TransformData"},"CurveParent":{"$ref":"1115"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1007.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1127"},"customOut":{"$id":"1128"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1007.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1127"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1007.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1128"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1008.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1139"},"customOut":{"$id":"1140"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1008.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1139"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1008.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1140"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1037.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1153"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1037.08},"$type":"TransformData"},"CurveParent":{"$ref":"1153"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1037.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1165"},"customOut":{"$id":"1166"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1037.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1165"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1037.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1166"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1038.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1177"},"customOut":{"$id":"1178"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1038.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1177"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1038.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1178"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1067.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1191"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1067.08},"$type":"TransformData"},"CurveParent":{"$ref":"1191"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1067.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1203"},"customOut":{"$id":"1204"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1067.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1203"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1067.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1204"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1068.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1215"},"customOut":{"$id":"1216"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1068.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1215"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1068.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1216"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1097.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1229"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1097.08},"$type":"TransformData"},"CurveParent":{"$ref":"1229"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1097.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1241"},"customOut":{"$id":"1242"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1097.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1241"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1097.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1242"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1098.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1253"},"customOut":{"$id":"1254"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1098.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1253"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1098.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1254"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1127.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1267"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1127.08},"$type":"TransformData"},"CurveParent":{"$ref":"1267"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1127.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1279"},"customOut":{"$id":"1280"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1127.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1279"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1127.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1280"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1128.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1291"},"customOut":{"$id":"1292"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1128.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1291"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1128.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1292"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1157.08},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1305"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1157.08},"$type":"TransformData"},"CurveParent":{"$ref":"1305"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1157.08},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1317"},"customOut":{"$id":"1318"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1157.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1317"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1157.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1318"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1158.08},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1329"},"customOut":{"$id":"1330"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1158.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1329"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1158.08},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1330"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":902.0803},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":872.0803},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":872.0803},"$type":"TransformData"}},"children":[]}]},{"name":"3 moving sides","components":{"Transform":{"position":{"x":0,"y":413.3,"z":842.0803},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":901.0803},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1109.08},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"middle","components":{"Transform":{"position":{"x":0,"y":413.3,"z":895.1921},"$type":"TransformData"},"Randomizer":{"$ref":"13"}},"children":[{"name":"1 moving","components":{"Transform":{"position":{"x":0,"y":413.3,"z":460.0012},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":730.0012},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"3 moving coins","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1055.543},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":717.543},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":865.543},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1290.069},"$type":"TransformData"},"Randomizer":{"$ref":"14"}},"children":[{"name":"trains_side_ground","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1290.069},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1410.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1410.069},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1590.069},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1620.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"trains_side","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1290.069},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"randomizer","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1155.069},"$type":"TransformData"},"Randomizer":{"$ref":"49"}},"children":[{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1290.069},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1350.069},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1490"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurveParent":{"$ref":"1490"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1502"},"customOut":{"$id":"1503"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1502"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1493.456},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1503"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1493.456},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1514"},"customOut":{"$id":"1515"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1395.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1514"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1493.456},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1515"}},"children":[]}]}]}]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1470.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1290.069},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1350.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1590.069},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1590.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1559"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.8,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1590.069},"$type":"TransformData"},"CurveParent":{"$ref":"1559"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1590.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1571"},"customOut":{"$id":"1572"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1590.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1571"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1688.456},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1572"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1688.456},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1583"},"customOut":{"$id":"1584"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":522.3,"z":1590.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1583"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1688.456},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1584"}},"children":[]}]}]}]}]},{"name":"group","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1290.069},"$type":"TransformData"}},"children":[{"name":"coins","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1290.069},"$type":"TransformData"}},"children":[{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1365.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1603"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1365.069},"$type":"TransformData"},"CurveParent":{"$ref":"1603"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1365.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1615"},"customOut":{"$id":"1616"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1365.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1615"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1365.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1616"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1366.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1627"},"customOut":{"$id":"1628"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1366.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1627"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1366.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1628"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1641"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurveParent":{"$ref":"1641"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1653"},"customOut":{"$id":"1654"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1653"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1395.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1654"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1396.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1665"},"customOut":{"$id":"1666"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1396.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1665"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1396.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1666"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1425.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1679"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1425.069},"$type":"TransformData"},"CurveParent":{"$ref":"1679"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1425.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1691"},"customOut":{"$id":"1692"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1425.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1691"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1425.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1692"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1426.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1703"},"customOut":{"$id":"1704"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1426.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1703"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1426.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1704"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1455.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1717"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1455.069},"$type":"TransformData"},"CurveParent":{"$ref":"1717"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1455.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1729"},"customOut":{"$id":"1730"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1455.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1729"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1455.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1730"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1456.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1741"},"customOut":{"$id":"1742"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1456.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1741"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1456.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1742"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1485.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1755"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1485.069},"$type":"TransformData"},"CurveParent":{"$ref":"1755"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1485.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1767"},"customOut":{"$id":"1768"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1485.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1767"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1485.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1768"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1486.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1779"},"customOut":{"$id":"1780"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1486.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1779"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1486.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1780"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1515.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1793"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1515.069},"$type":"TransformData"},"CurveParent":{"$ref":"1793"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1515.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1805"},"customOut":{"$id":"1806"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1515.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1805"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1515.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1806"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1516.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1817"},"customOut":{"$id":"1818"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1516.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1817"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1516.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1818"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1545.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1831"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1545.069},"$type":"TransformData"},"CurveParent":{"$ref":"1831"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1545.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1843"},"customOut":{"$id":"1844"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1545.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1843"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1545.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1844"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1546.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1855"},"customOut":{"$id":"1856"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1546.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1855"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1546.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1856"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1575.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1869"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1575.069},"$type":"TransformData"},"CurveParent":{"$ref":"1869"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1575.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1881"},"customOut":{"$id":"1882"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1575.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1881"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1575.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1882"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1576.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1893"},"customOut":{"$id":"1894"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1576.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1893"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1576.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1894"}},"children":[]}]}]}]},{"name":"Coin (Single)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1605.069},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":1},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1907"},"_scaleMode":"NoScale","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1605.069},"$type":"TransformData"},"CurveParent":{"$ref":"1907"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1605.069},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1919"},"customOut":{"$id":"1920"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1605.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1919"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1605.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1920"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1606.069},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1931"},"customOut":{"$id":"1932"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1606.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1931"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":442.3,"z":1606.069},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1932"}},"children":[]}]}]}]}]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1350.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1320.069},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":20,"y":413.3,"z":1320.069},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":413.3,"z":1740.069},"$type":"TransformData"}},"children":[]}]},{"name":"3 moving sides","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1290.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1349.069},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1557.069},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"middle","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1343.181},"$type":"TransformData"},"Randomizer":{"$ref":"15"}},"children":[{"name":"1 moving","components":{"Transform":{"position":{"x":0,"y":413.3,"z":907.9899},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1177.99},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"3 moving coins","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1503.532},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1165.532},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":413.3,"z":1313.532},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]}]},"routeChunk_default_train_tops_moving_multiple":{"name":"routeChunk_default_train_tops_moving_multiple","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":20,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_train_tops_moving_multiple","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"trains_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":37,"z":165},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":37,"z":165},"$type":"TransformData"}},"children":[]}]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":270},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"trains_moving_sides","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":660},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1060},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1460},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"trains_moving_mid","components":{"Transform":{"position":{"x":0,"y":0,"z":120},"$type":"TransformData"}},"children":[{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":382},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":575},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":772},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":975},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1175},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]},{"name":"trains_3_moving_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1375},"$type":"TransformData"},"MovingTrainPlaceholder":{"_speed":0.5,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":0,"y":55,"z":210},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":55,"z":210},"$type":"TransformData"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":1530},"$type":"TransformData"}},"children":[]}]},"routeChunk_default_tunnel":{"name":"routeChunk_default_tunnel","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"9"},{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_tunnel","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_tunnel","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"9"}},"children":[{"name":"sides","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_sides_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"Mirror","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"46"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurveParent":{"$ref":"46"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"58"},"customOut":{"$id":"59"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"58"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"CurvePointTangent":{"$ref":"59"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":221},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"70"},"customOut":{"$id":"71"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":221},"$type":"TransformData"},"CurvePointTangent":{"$ref":"70"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":221},"$type":"TransformData"},"CurvePointTangent":{"$ref":"71"}},"children":[]}]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":-20,"y":7,"z":150},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":-20,"y":7,"z":150},"$type":"TransformData"}},"children":[]}]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":309},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"right","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_right_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":30},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":64.60001,"z":225},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":64.60001,"z":225},"$type":"TransformData"}},"children":[]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":309},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"mid","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_mid_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":309},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]},{"name":"left","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_left_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":30},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Medium)","components":{"Transform":{"position":{"x":20,"y":64.60001,"z":225},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Medium","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":64.60001,"z":225},"$type":"TransformData"}},"children":[]}]},{"name":"trains_3_moving_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":309},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"},"MovingTrainPlaceholder":{"_speed":1,"$type":"SYBO.Subway.Placeholders.MovingTrainPlaceholder"}},"children":[]}]}]},{"name":"environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates_environment","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Gates,All","_density":""},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":510},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":510},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_bonus_long":{"name":"routeChunk_default_bonus_long","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":14,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"15"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"16"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"17"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"18"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"19"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$id":"37"},{"$id":"38"},{"$id":"39"},{"$id":"40"},{"$id":"41"},{"$id":"42"},{"$id":"43"},{"$id":"44"},{"$id":"45"},{"$id":"46"},{"$id":"47"},{"$id":"48"},{"$id":"49"},{"$id":"50"},{"$id":"51"},{"$id":"52"},{"$id":"53"},{"$id":"54"},{"$id":"55"},{"$id":"56"},{"$id":"57"},{"$id":"58"},{"$id":"59"},{"$id":"60"},{"$id":"61"},{"$id":"62"},{"$id":"63"},{"$id":"64"},{"$id":"65"},{"$id":"66"},{"$id":"67"},{"$id":"68"},{"$id":"69"},{"$id":"70"},{"$id":"71"},{"$id":"72"},{"$id":"73"},{"$id":"74"},{"$id":"75"},{"$id":"76"},{"$id":"77"},{"$id":"78"},{"$id":"79"},{"$id":"80"},{"$id":"81"},{"$id":"82"},{"$id":"83"},{"$id":"84"},{"$id":"85"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_bonus_long","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random_start","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Randomizer":{"$ref":"10"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"}},"children":[]},{"name":"blocker_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"ground_run","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"ground","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Randomizer":{"$ref":"15"}},"children":[{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"138"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"138"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"150"},"customOut":{"$id":"151"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"150"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"151"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"162"},"customOut":{"$id":"163"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"162"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"163"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"176"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"176"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"188"},"customOut":{"$id":"189"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"188"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"189"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"200"},"customOut":{"$id":"201"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"200"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"201"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"214"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"214"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"226"},"customOut":{"$id":"227"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"226"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"227"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"238"},"customOut":{"$id":"239"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"238"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"239"}},"children":[]}]}]}]}]},{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"278"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"278"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"290"},"customOut":{"$id":"291"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"290"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"291"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"302"},"customOut":{"$id":"303"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"302"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"303"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"4","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"329"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurveParent":{"$ref":"329"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"341"},"customOut":{"$id":"342"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"341"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":285},"$type":"TransformData"},"CurvePointTangent":{"$ref":"342"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"353"},"customOut":{"$id":"354"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"353"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":436},"$type":"TransformData"},"CurvePointTangent":{"$ref":"354"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":20,"y":7,"z":240},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":240},"$type":"TransformData"}},"children":[]}]},{"name":"ground","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"Randomizer":{"$ref":"16"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":660},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":660},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"4","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"421"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"421"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"433"},"customOut":{"$id":"434"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"433"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"434"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"445"},"customOut":{"$id":"446"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"445"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"446"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":660},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"472"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"472"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"484"},"customOut":{"$id":"485"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"484"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"485"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"496"},"customOut":{"$id":"497"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"496"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"497"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":660},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"523"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"523"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"535"},"customOut":{"$id":"536"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"535"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"536"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"547"},"customOut":{"$id":"548"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"547"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"548"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"561"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"561"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"573"},"customOut":{"$id":"574"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"573"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"574"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"585"},"customOut":{"$id":"586"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"585"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"586"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"599"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurveParent":{"$ref":"599"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"611"},"customOut":{"$id":"612"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"611"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":585},"$type":"TransformData"},"CurvePointTangent":{"$ref":"612"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"623"},"customOut":{"$id":"624"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"623"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":736},"$type":"TransformData"},"CurvePointTangent":{"$ref":"624"}},"children":[]}]}]}]}]}]},{"name":"ground","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"},"Randomizer":{"$ref":"17"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":960},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":960},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"4","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"667"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"667"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"679"},"customOut":{"$id":"680"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"679"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"680"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"691"},"customOut":{"$id":"692"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"691"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"692"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":960},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"718"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"718"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"730"},"customOut":{"$id":"731"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"730"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"731"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"742"},"customOut":{"$id":"743"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"742"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"743"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":960},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"769"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"769"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"781"},"customOut":{"$id":"782"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"781"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"782"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"793"},"customOut":{"$id":"794"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"793"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"794"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"807"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"807"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"819"},"customOut":{"$id":"820"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"819"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"820"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"831"},"customOut":{"$id":"832"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"831"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"832"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"845"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurveParent":{"$ref":"845"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"857"},"customOut":{"$id":"858"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"857"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":885},"$type":"TransformData"},"CurvePointTangent":{"$ref":"858"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"869"},"customOut":{"$id":"870"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"869"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":1036},"$type":"TransformData"},"CurvePointTangent":{"$ref":"870"}},"children":[]}]}]}]}]}]}]},{"name":"roof_run","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"start","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":240},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":37,"z":285},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":37,"z":285},"$type":"TransformData"}},"children":[]}]}]},{"name":"roof","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Randomizer":{"$ref":"18"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"959"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"959"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"971"},"customOut":{"$id":"972"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"971"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"972"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"983"},"customOut":{"$id":"984"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"983"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"984"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"997"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"997"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1009"},"customOut":{"$id":"1010"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1009"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":443.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1010"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":443.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1021"},"customOut":{"$id":"1022"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1021"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":443.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1022"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":390},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":510},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":525},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1061"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.7,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":525},"$type":"TransformData"},"CurveParent":{"$ref":"1061"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":525},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1073"},"customOut":{"$id":"1074"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1073"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":623.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1074"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":623.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1085"},"customOut":{"$id":"1086"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":525},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1085"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":623.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1086"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":540},"$type":"TransformData"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1141"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"1141"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1153"},"customOut":{"$id":"1154"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1153"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1154"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1165"},"customOut":{"$id":"1166"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1165"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1166"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1179"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"1179"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1191"},"customOut":{"$id":"1192"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1191"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1192"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1203"},"customOut":{"$id":"1204"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1203"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1204"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":465},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1217"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":465},"$type":"TransformData"},"CurveParent":{"$ref":"1217"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":465},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1229"},"customOut":{"$id":"1230"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":465},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1229"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":563.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1230"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":563.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1241"},"customOut":{"$id":"1242"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":465},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1241"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":563.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1242"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":510},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":540},"$type":"TransformData"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":270},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":300},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1304"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"1304"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1316"},"customOut":{"$id":"1317"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1316"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1317"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":496},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1328"},"customOut":{"$id":"1329"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1328"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1329"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1342"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"1342"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1354"},"customOut":{"$id":"1355"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1354"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":533.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1355"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":533.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1366"},"customOut":{"$id":"1367"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1366"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":533.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1367"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1380"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"1380"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1392"},"customOut":{"$id":"1393"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1392"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1393"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1404"},"customOut":{"$id":"1405"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1404"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":586},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1405"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":510},"$type":"TransformData"}},"children":[]}]}]},{"name":"roof","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"Randomizer":{"$ref":"19"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":570},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1469"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurveParent":{"$ref":"1469"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1481"},"customOut":{"$id":"1482"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1481"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1482"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1493"},"customOut":{"$id":"1494"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1493"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1494"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1507"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurveParent":{"$ref":"1507"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1519"},"customOut":{"$id":"1520"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1519"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":743.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1520"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":743.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1531"},"customOut":{"$id":"1532"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1531"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":743.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1532"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":690},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":720},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":810},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":825},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1571"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.7,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":825},"$type":"TransformData"},"CurveParent":{"$ref":"1571"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":825},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1583"},"customOut":{"$id":"1584"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":825},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1583"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":923.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1584"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":923.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1595"},"customOut":{"$id":"1596"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":825},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1595"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":923.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1596"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":840},"$type":"TransformData"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":570},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1651"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurveParent":{"$ref":"1651"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1663"},"customOut":{"$id":"1664"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1663"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":615},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1664"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1675"},"customOut":{"$id":"1676"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1675"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":766},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1676"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1689"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"1689"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1701"},"customOut":{"$id":"1702"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1701"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1702"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1713"},"customOut":{"$id":"1714"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1713"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1714"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":765},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1727"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":765},"$type":"TransformData"},"CurveParent":{"$ref":"1727"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":765},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1739"},"customOut":{"$id":"1740"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1739"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":863.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1740"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":863.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1751"},"customOut":{"$id":"1752"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":765},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1751"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":863.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1752"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":810},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":840},"$type":"TransformData"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":480},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":570},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":600},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1814"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurveParent":{"$ref":"1814"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1826"},"customOut":{"$id":"1827"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1826"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":645},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1827"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":796},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1838"},"customOut":{"$id":"1839"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":796},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1838"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":796},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1839"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1852"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"1852"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1864"},"customOut":{"$id":"1865"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1864"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":833.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1865"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":833.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1876"},"customOut":{"$id":"1877"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1876"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":833.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1877"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1890"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurveParent":{"$ref":"1890"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1902"},"customOut":{"$id":"1903"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1902"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":735},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1903"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1914"},"customOut":{"$id":"1915"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1914"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":886},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1915"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":810},"$type":"TransformData"}},"children":[]}]}]},{"name":"roof","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"},"Randomizer":{"$ref":"20"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":870},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":900},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1979"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"1979"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1991"},"customOut":{"$id":"1992"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1991"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1992"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2003"},"customOut":{"$id":"2004"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2003"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2004"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2017"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"2017"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2029"},"customOut":{"$id":"2030"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2029"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":1043.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2030"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1043.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2041"},"customOut":{"$id":"2042"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2041"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1043.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2042"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":990},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1020},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1110},"$type":"TransformData"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":1125},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2081"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.7,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1125},"$type":"TransformData"},"CurveParent":{"$ref":"2081"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1125},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2093"},"customOut":{"$id":"2094"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1125},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2093"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":1223.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2094"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1223.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2105"},"customOut":{"$id":"2106"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":1125},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2105"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1223.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2106"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1140},"$type":"TransformData"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":870},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2161"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurveParent":{"$ref":"2161"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2173"},"customOut":{"$id":"2174"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2173"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":915},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2174"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2185"},"customOut":{"$id":"2186"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2185"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":1066},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2186"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2199"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurveParent":{"$ref":"2199"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2211"},"customOut":{"$id":"2212"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2211"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2212"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2223"},"customOut":{"$id":"2224"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2223"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2224"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1065},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2237"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":1065},"$type":"TransformData"},"CurveParent":{"$ref":"2237"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1065},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2249"},"customOut":{"$id":"2250"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2249"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":1163.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2250"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1163.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2261"},"customOut":{"$id":"2262"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":1065},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2261"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1163.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2262"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1110},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1140},"$type":"TransformData"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":780},"$type":"TransformData"}},"children":[{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":870},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":900},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2324"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurveParent":{"$ref":"2324"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2336"},"customOut":{"$id":"2337"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2336"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":945},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2337"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":1096},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2348"},"customOut":{"$id":"2349"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":1096},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2348"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":1096},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2349"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":1035},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2362"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":1035},"$type":"TransformData"},"CurveParent":{"$ref":"2362"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":1035},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2374"},"customOut":{"$id":"2375"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2374"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":1133.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2375"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":1133.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2386"},"customOut":{"$id":"2387"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2386"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":1133.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2387"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"2400"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurveParent":{"$ref":"2400"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"2412"},"customOut":{"$id":"2413"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2412"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1035},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2413"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"2424"},"customOut":{"$id":"2425"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2424"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":1186},"$type":"TransformData"},"CurvePointTangent":{"$ref":"2425"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1080},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1110},"$type":"TransformData"}},"children":[]}]}]}]}]}]},"routeChunk_default_bonus_short":{"name":"routeChunk_default_bonus_short","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":6,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"},{"_type":"Tube,All","_density":""}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$type":"SYBO.Subway.Level.Randomizer","$id":"10"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"11"},{"$type":"SYBO.Subway.Level.Randomizer","$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"},{"$id":"33"},{"$id":"34"},{"$id":"35"},{"$id":"36"},{"$id":"37"},{"$id":"38"},{"$id":"39"},{"$id":"40"},{"$id":"41"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_bonus_short","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"random","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"10"}},"children":[{"name":"ground_run","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"ground","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"11"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"81"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"81"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"93"},"customOut":{"$id":"94"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"93"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"94"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":496},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"105"},"customOut":{"$id":"106"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"105"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":496},"$type":"TransformData"},"CurvePointTangent":{"$ref":"106"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"129"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"129"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"141"},"customOut":{"$id":"142"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"141"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"142"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":526},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"153"},"customOut":{"$id":"154"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"153"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"154"}},"children":[]}]}]}]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"170"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"170"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"182"},"customOut":{"$id":"183"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"182"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"183"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"194"},"customOut":{"$id":"195"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"194"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"195"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"208"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"208"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"220"},"customOut":{"$id":"221"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"220"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"221"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"232"},"customOut":{"$id":"233"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"232"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"233"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"246"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"246"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"258"},"customOut":{"$id":"259"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"258"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"259"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"270"},"customOut":{"$id":"271"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"270"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"271"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":420},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":420},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":420},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"317"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurveParent":{"$ref":"317"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"329"},"customOut":{"$id":"330"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"329"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":105},"$type":"TransformData"},"CurvePointTangent":{"$ref":"330"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"341"},"customOut":{"$id":"342"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"341"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":256},"$type":"TransformData"},"CurvePointTangent":{"$ref":"342"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"365"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"},"CurveParent":{"$ref":"365"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"377"},"customOut":{"$id":"378"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"377"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":315},"$type":"TransformData"},"CurvePointTangent":{"$ref":"378"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":0,"z":466},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"389"},"customOut":{"$id":"390"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":0,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"389"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":0,"z":466},"$type":"TransformData"},"CurvePointTangent":{"$ref":"390"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":0,"z":375},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"403"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":0,"z":375},"$type":"TransformData"},"CurveParent":{"$ref":"403"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":0,"z":375},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"415"},"customOut":{"$id":"416"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"415"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":375},"$type":"TransformData"},"CurvePointTangent":{"$ref":"416"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":0,"z":526},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"427"},"customOut":{"$id":"428"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"427"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":0,"z":526},"$type":"TransformData"},"CurvePointTangent":{"$ref":"428"}},"children":[]}]}]}]},{"name":"blocker_w_coins_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":420},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]}]}]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":7,"z":60},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":7,"z":60},"$type":"TransformData"}},"children":[]}]}]},{"name":"roof_run","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"end","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"471"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurveParent":{"$ref":"471"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"483"},"customOut":{"$id":"484"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"483"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":503.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"484"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":503.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"495"},"customOut":{"$id":"496"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":405},"$type":"TransformData"},"CurvePointTangent":{"$ref":"495"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":503.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"496"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":0,"z":435},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":101},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"509"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":0,"z":435},"$type":"TransformData"},"CurveParent":{"$ref":"509"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":0,"z":435},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"521"},"customOut":{"$id":"522"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"521"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":435},"$type":"TransformData"},"CurvePointTangent":{"$ref":"522"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":0,"z":536},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"533"},"customOut":{"$id":"534"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":0,"z":536},"$type":"TransformData"},"CurvePointTangent":{"$ref":"533"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":0,"z":536},"$type":"TransformData"},"CurvePointTangent":{"$ref":"534"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":450},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"547"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.5,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":450},"$type":"TransformData"},"CurveParent":{"$ref":"547"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":450},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"559"},"customOut":{"$id":"560"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"559"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":548.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"560"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":548.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"571"},"customOut":{"$id":"572"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":450},"$type":"TransformData"},"CurvePointTangent":{"$ref":"571"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":548.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"572"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":480},"$type":"TransformData"}},"children":[]}]},{"name":"start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":60},"$type":"TransformData"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":false,"right":true},"$type":"RandomizeOffset"}},"children":[]},{"name":"PickupSpawnPoint(Easy)","components":{"Transform":{"position":{"x":0,"y":37,"z":105},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"Normal","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"OnGround","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":0,"y":37,"z":105},"$type":"TransformData"}},"children":[]}]}]},{"name":"roof","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Randomizer":{"$ref":"12"}},"children":[{"name":"3","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"675"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"675"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"687"},"customOut":{"$id":"688"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"687"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"688"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"699"},"customOut":{"$id":"700"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"699"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"700"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"713"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"713"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"725"},"customOut":{"$id":"726"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"725"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":263.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"726"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":263.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"737"},"customOut":{"$id":"738"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"737"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":263.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"738"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":330},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"781"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.7,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurveParent":{"$ref":"781"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"793"},"customOut":{"$id":"794"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"793"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":109,"z":443.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"794"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":443.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"805"},"customOut":{"$id":"806"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":109,"z":345},"$type":"TransformData"},"CurvePointTangent":{"$ref":"805"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":443.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"806"}},"children":[]}]}]}]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"}},"children":[]}]},{"name":"2","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Mirror":{"$type":"Mirror"}},"children":[{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"859"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurveParent":{"$ref":"859"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"871"},"customOut":{"$id":"872"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"871"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":135},"$type":"TransformData"},"CurvePointTangent":{"$ref":"872"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"883"},"customOut":{"$id":"884"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"883"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":286},"$type":"TransformData"},"CurvePointTangent":{"$ref":"884"}},"children":[]}]}]}]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"897"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"897"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"909"},"customOut":{"$id":"910"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"909"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":109,"z":263.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"910"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":263.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"921"},"customOut":{"$id":"922"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":109,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"921"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":263.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"922"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":240},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"955"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"955"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"967"},"customOut":{"$id":"968"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"967"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"968"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"979"},"customOut":{"$id":"980"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"979"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"980"}},"children":[]}]}]}]}]},{"name":"1","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Jump Curve)","components":{"Transform":{"position":{"x":0,"y":29,"z":150},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":true,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":98.38699},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1024"},"_scaleMode":"PreserveSpacing","_spacingMode":"NonStraight","_curveOffset":0.1,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":0,"y":29,"z":150},"$type":"TransformData"},"CurveParent":{"$ref":"1024"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":0,"y":29,"z":150},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1036"},"customOut":{"$id":"1037"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":29,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1036"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":109,"z":248.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1037"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":0,"y":29,"z":248.387},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1048"},"customOut":{"$id":"1049"},"weight":1,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":0,"y":109,"z":150},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1048"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":0,"y":29,"z":248.387},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1049"}},"children":[]}]}]}]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1062"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurveParent":{"$ref":"1062"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1074"},"customOut":{"$id":"1075"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1074"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":165},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1075"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":-20,"y":29,"z":316},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1086"},"customOut":{"$id":"1087"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":-20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1086"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":-20,"y":29,"z":316},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1087"}},"children":[]}]}]}]},{"name":"train_ramp_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":180},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":210},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"Coins (Line)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CoinCurve":{"_curveParent":{"_disableGizmos":false,"color":{"r":1,"g":0,"b":0,"a":1},"_cachedCurve":{"MinCoords":{"x":0,"y":0,"z":0},"MaxCoords":{"x":0,"y":0,"z":151},"MinTime":0,"MaxTime":1},"_forceLinearZ":true,"$id":"1120"},"_scaleMode":"NoScale","_spacingMode":"Straight","_curveOffset":0,"_coinIndexToSkip":-1,"_redistributeAlongCurveOnSpeedChange":true,"_debugIsMovingTrain":false,"$type":"SYBO.Subway.Coins.CoinCurve"}},"children":[{"name":"CurveParent","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurveParent":{"$ref":"1120"}},"children":[{"name":"CurvePoint (0)","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePoint":{"t":0,"smoothTangents":false,"customIn":{"$id":"1132"},"customOut":{"$id":"1133"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1132"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":255},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1133"}},"children":[]}]},{"name":"CurvePoint (1)","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePoint":{"t":1,"smoothTangents":false,"customIn":{"$id":"1144"},"customOut":{"$id":"1145"},"weight":20,"$type":"CurvePoint"}},"children":[{"name":"In","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1144"}},"children":[]},{"name":"Out","components":{"Transform":{"position":{"x":20,"y":29,"z":406},"$type":"TransformData"},"CurvePointTangent":{"$ref":"1145"}},"children":[]}]}]}]}]}]}]}]}]},"routeChunk_default_pogostick_start":{"name":"routeChunk_default_pogostick_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":true,"_blockCount":2,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"Low,All"},{"_type":"Fillers,All","_density":"Medium,All"},{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_pogostick_start","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"PickupSpawnPoint(DynamicPogoStick)","components":{"Transform":{"position":{"x":20,"y":7,"z":90},"$type":"TransformData"},"PickupSpawnPoint":{"ContainingRoute":null,"_pickupParent":{},"__spawnPointMode":"WillForcePickupType","__forceSpawnPickupType":"PogoStick","_spawnPointDifficulty":"Easy","_spawnPointHeight":"","$type":"SYBO.Subway.Pickups.PickupSpawnPoint"},"RandomizeOffset":{"randomOffsets":{"left":true,"mid":true,"right":true},"$type":"RandomizeOffset"}},"children":[{"name":"PickupParent","components":{"Transform":{"position":{"x":20,"y":7,"z":90},"$type":"TransformData"}},"children":[]}]}]},"routeChunk_default_tutorial":{"name":"routeChunk_default_tutorial","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"RouteChunk":{"_isRepeatableInSections":false,"_blockCount":34,"_generateEnvironment":true,"_generateEnvironmentBase":true,"_limitedAllowedEnvironmentKinds":[{"_type":"Fillers,All","_density":"High,All"}],"_trackObjects":[],"_forcedEnvironmentBases":[],"_forcedEnvironments":[],"_selectors":[{"$id":"7"},{"$id":"8"},{"$id":"9"},{"$id":"10"},{"$id":"11"},{"$id":"12"},{"$id":"13"},{"$id":"14"},{"$id":"15"},{"$id":"16"},{"$id":"17"},{"$id":"18"},{"$id":"19"},{"$id":"20"},{"$id":"21"},{"$id":"22"},{"$id":"23"},{"$id":"24"},{"$id":"25"},{"$id":"26"},{"$id":"27"},{"$id":"28"},{"$id":"29"},{"$id":"30"},{"$id":"31"},{"$id":"32"}],"_routeTransitionTrigger":null,"Previous":null,"Next":null,"_reportedName":"default_tutorial","PreviewEnvironmentIndex":0,"$type":"SYBO.Subway.Routes.RouteChunk"}},"children":[{"name":"obstacles","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train_sub_1_gen_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":90},"$type":"TransformData"}},"children":[]},{"name":"blocker_jump_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"}},"children":[]},{"name":"blocker_jump_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":540},"$type":"TransformData"}},"children":[]},{"name":"blocker_roll_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":900},"$type":"TransformData"}},"children":[]},{"name":"blocker_roll_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1080},"$type":"TransformData"}},"children":[]},{"name":"train_sub_1_gen_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1290},"$type":"TransformData"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1320},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1500},"$type":"TransformData"}},"children":[]},{"name":"trains_5_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1680},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"trains_1_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":1800},"$type":"TransformData"}},"children":[]},{"name":"trains_3_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1980},"$type":"TransformData"},"RandomizeOffset":{"randomOffsets":{"left":false,"mid":false,"right":false},"$type":"RandomizeOffset"}},"children":[]},{"name":"gates_mid_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":2520},"$type":"TransformData"}},"children":[]}]},{"name":"Ground","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"Trigger_up","components":{"Transform":{"position":{"x":0,"y":0,"z":203.0626},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":0,"time":1,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_up","components":{"Transform":{"position":{"x":0,"y":0,"z":413.0626},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":0,"time":0.75,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_down","components":{"Transform":{"position":{"x":0,"y":0,"z":763.0626},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":180,"time":0.75,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_down","components":{"Transform":{"position":{"x":0,"y":0,"z":963.0626},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":180,"time":0.65,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_good","components":{"Transform":{"position":{"x":0,"y":0,"z":1091.433},"$type":"TransformData"},"TutorialEvent":{"displayText":true,"tutorialText":"FreshMoves","displayMesh":false,"direction":0,"time":1,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_left","components":{"Transform":{"position":{"x":0,"y":0,"z":1343.063},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":90,"time":1,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_right","components":{"Transform":{"position":{"x":0,"y":0,"z":1523.063},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":-90,"time":0.5,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_right","components":{"Transform":{"position":{"x":0,"y":0,"z":1643.063},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":-90,"time":0.5,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_left","components":{"Transform":{"position":{"x":0,"y":0,"z":1823.063},"$type":"TransformData"},"TutorialEvent":{"displayText":false,"tutorialText":"None","displayMesh":true,"direction":90,"time":0.6,"endTutorial":false,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_hoverboard","components":{"Transform":{"position":{"x":0,"y":0,"z":2195},"$type":"TransformData"},"TutorialEvent":{"displayText":true,"tutorialText":"DoubleTap","displayMesh":false,"direction":0,"time":1,"endTutorial":false,"allowHoverboard":true,"$type":"TutorialEvent"}},"children":[]},{"name":"Trigger_finished","components":{"Transform":{"position":{"x":0,"y":0,"z":2574.015},"$type":"TransformData"},"TutorialEvent":{"displayText":true,"tutorialText":"YouRock","displayMesh":false,"direction":0,"time":1,"endTutorial":true,"allowHoverboard":false,"$type":"TutorialEvent"}},"children":[]}]},{"name":"obstacles_trains_start","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"collider stumble","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"collider stumble","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":120},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":180},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":300},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":360},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":480},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":540},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":660},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":720},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":840},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":900},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1020},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":20,"y":0,"z":1080},"$type":"TransformData"}},"children":[]},{"name":"trains_3_gen_group_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":1200},"$type":"TransformData"}},"children":[]}]},{"name":"base_environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"2_tracks","components":{"Transform":{"position":{"x":0,"y":0,"z":360},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]},{"name":"2_tracks","components":{"Transform":{"position":{"x":0,"y":0,"z":900},"$type":"TransformData"},"EnvironmentBase":{"_type":"Tracks","_numTracks":2,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]}]},{"name":"environments","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"gates","components":{"Transform":{"position":{"x":0,"y":0,"z":2520},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Gates,All","_density":""},"_blockCount":4,"$type":"SYBO.Subway.Level.Environment"}},"children":[]}]},{"name":"checkpoints","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"checkpoint_1","components":{"Transform":{"position":{"x":0,"y":0,"z":41},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_2","components":{"Transform":{"position":{"x":0,"y":0,"z":380},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_3","components":{"Transform":{"position":{"x":0,"y":0,"z":560},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_4","components":{"Transform":{"position":{"x":0,"y":0,"z":918},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_5","components":{"Transform":{"position":{"x":0,"y":0,"z":1104},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_6","components":{"Transform":{"position":{"x":0,"y":0,"z":1576},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]},{"name":"checkpoint_7","components":{"Transform":{"position":{"x":0,"y":0,"z":2182},"$type":"TransformData"},"CharacterCollisionTrigger":{"$type":"SYBO.Subway.Level.CharacterCollisionTrigger"},"CheckpointTrigger":{"$type":"SYBO.Subway.Level.CheckpointTrigger"}},"children":[]}]},{"name":"environment_constraints","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"},"Environment":{"_environmentKind":{"_type":"Empty,All","_density":""},"_blockCount":2,"$type":"SYBO.Subway.Level.Environment"},"EnvironmentBase":{"_type":"Empty","_numTracks":3,"_blockCount":2,"$type":"SYBO.Subway.Level.EnvironmentBase"}},"children":[]}]}} \ No newline at end of file diff --git a/subway-surfers-ny/assets/data/chunks_idle.json b/subway-surfers-ny/assets/data/chunks_idle.json new file mode 100644 index 00000000..60d697fa --- /dev/null +++ b/subway-surfers-ny/assets/data/chunks_idle.json @@ -0,0 +1 @@ +{"intro":{"name":"intro","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"high_tracks3_high_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":-180},"$type":"TransformData"}},"children":[]},{"name":"bag_place","components":{"Transform":{"position":{"x":0.141,"y":0.604,"z":-18.326},"$type":"TransformData"}},"children":[]},{"name":"Ground","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"trains","components":{"Transform":{"position":{"x":-20,"y":0,"z":0},"$type":"TransformData"}},"children":[{"name":"train_sub_1_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":-30},"$type":"TransformData"}},"children":[]},{"name":"train_start_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":-30},"$type":"TransformData"}},"children":[]},{"name":"train_sub_1_place","components":{"Transform":{"position":{"x":-20,"y":0,"z":30},"$type":"TransformData"}},"children":[]}]},{"name":"high_tracks3_high_group_place","components":{"Transform":{"position":{"x":0,"y":0,"z":0},"$type":"TransformData"}},"children":[]},{"name":"lightSignal_group_place","components":{"Transform":{"position":{"x":10,"y":0,"z":0},"$type":"TransformData"}},"children":[]}]}} \ No newline at end of file diff --git a/subway-surfers-ny/assets/data/config.json b/subway-surfers-ny/assets/data/config.json new file mode 100644 index 00000000..5beca2d0 --- /dev/null +++ b/subway-surfers-ny/assets/data/config.json @@ -0,0 +1,21 @@ +{ + "lang": "en", + "bundlepath": " ", + "env": "1103_seoul", + "env_builtin": "1103_seoul", + "char": "jake", + "loading_en": "loading", + "loading_pt-br": "carregando", + "revivals": 3, + "reviveTimeout": 6, + "pokiSdkDebug": false, + "commercials": false, + "rewardedBreaks": false, + "rewardedBreaksMobile": false, + "autoload": false, + "leaderboards": false, + "useDefaultPrompt": false, + "vconsole": false, + "monitor": false, + "maxNicknameChars": 15 +} \ No newline at end of file diff --git a/subway-surfers-ny/assets/data/strings_en.json b/subway-surfers-ny/assets/data/strings_en.json new file mode 100644 index 00000000..388fc6e9 --- /dev/null +++ b/subway-surfers-ny/assets/data/strings_en.json @@ -0,0 +1,53 @@ +{ + "STR_L_HTML5": "This game uses HTML5. If it loads but does not run, please update your web browser", + "menu": "Menu", + "toprun": "Top Run", + "highscores": "High Scores", + "collect": "Collect", + "characters": "Characters", + "boards": "Boards", + "awards": "Awards", + "boosts": "Boosts", + "free_stuff": "Free Stuff", + "store": "Store", + "resume": "Resume", + "play": "Play", + "pause": "Pause", + "gameover": "Game Over", + "wip": "(WIP)", + "free": "Free!", + "save_me": "Save me!", + + "leaderboards": "Leaderboards", + "score": "Score", + "coins": "Coins", + "high_score": "High Score", + "new_high_score": "New High Score!", + "settings": "Settings", + "tap_to_play": "Tap to play", + "tap_to_play_desktop": "Press space to play", + "nickname_prompt": "Set your nickname", + "ok": "OK", + "cancel": "Cancel", + + "sound": "Sound", + "on": "On", + "off": "Off", + "nickname": "Nickname", + + "tutorial_good": "Fresh Moves!", + "tutorial_hoverboard": "Double Tap for\nHoverboard", + "tutorial_hoverboard_desktop": "Press Space for\nHoverboard", + "tutorial_finished": "You rock! Now GO!", + "tutorial_up": "Swipe Up", + "tutorial_up_desktop": "Press Arrow Key Up", + "tutorial_down": "Swipe Down", + "tutorial_down_desktop": "Press Arrow Key Down", + "tutorial_left": "Swipe Left", + "tutorial_left_desktop": "Press Arrow Key Left", + "tutorial_right": "Swipe Right", + "tutorial_right_desktop": "Press Arrow Key Right", + + "privacy_policy": "More games", + "privacy_policy_link": "https://zazgames.com/" +} diff --git a/subway-surfers-ny/assets/font/lilita-one.css b/subway-surfers-ny/assets/font/lilita-one.css new file mode 100644 index 00000000..a2bca916 --- /dev/null +++ b/subway-surfers-ny/assets/font/lilita-one.css @@ -0,0 +1,6 @@ +@font-face { + font-family: 'Lilita One'; + src: url('lilita-one.woff2') format('woff2'), url('lilita-one.woff') format('woff'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/subway-surfers-ny/assets/font/lilita-one.woff2 b/subway-surfers-ny/assets/font/lilita-one.woff2 new file mode 100644 index 00000000..22253895 Binary files /dev/null and b/subway-surfers-ny/assets/font/lilita-one.woff2 differ diff --git a/subway-surfers-ny/assets/font/titan-one.css b/subway-surfers-ny/assets/font/titan-one.css new file mode 100644 index 00000000..4ab96a7f --- /dev/null +++ b/subway-surfers-ny/assets/font/titan-one.css @@ -0,0 +1,6 @@ +@font-face { + font-family: 'Titan One'; + src: url('titan-one.woff2') format('woff2'), url('titan-one.woff') format('woff'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/subway-surfers-ny/assets/font/titan-one.woff2 b/subway-surfers-ny/assets/font/titan-one.woff2 new file mode 100644 index 00000000..78ce40f6 Binary files /dev/null and b/subway-surfers-ny/assets/font/titan-one.woff2 differ diff --git a/subway-surfers-ny/assets/game/board_new_york.fbx b/subway-surfers-ny/assets/game/board_new_york.fbx new file mode 100644 index 00000000..6e0b3e04 Binary files /dev/null and b/subway-surfers-ny/assets/game/board_new_york.fbx differ diff --git a/subway-surfers-ny/assets/game/board_new_york01.fbx b/subway-surfers-ny/assets/game/board_new_york01.fbx new file mode 100644 index 00000000..ca6631db Binary files /dev/null and b/subway-surfers-ny/assets/game/board_new_york01.fbx differ diff --git a/subway-surfers-ny/assets/game/board_new_york_tex_mip.png b/subway-surfers-ny/assets/game/board_new_york_tex_mip.png new file mode 100644 index 00000000..e05fe027 Binary files /dev/null and b/subway-surfers-ny/assets/game/board_new_york_tex_mip.png differ diff --git a/subway-surfers-ny/assets/game/effects_tex_mip.png b/subway-surfers-ny/assets/game/effects_tex_mip.png new file mode 100644 index 00000000..617bc61a Binary files /dev/null and b/subway-surfers-ny/assets/game/effects_tex_mip.png differ diff --git a/subway-surfers-ny/assets/game/enemies_mip.png b/subway-surfers-ny/assets/game/enemies_mip.png new file mode 100644 index 00000000..8a766291 Binary files /dev/null and b/subway-surfers-ny/assets/game/enemies_mip.png differ diff --git a/subway-surfers-ny/assets/game/environment.fbx b/subway-surfers-ny/assets/game/environment.fbx new file mode 100644 index 00000000..5d2fe780 Binary files /dev/null and b/subway-surfers-ny/assets/game/environment.fbx differ diff --git a/subway-surfers-ny/assets/game/halo_mip.png b/subway-surfers-ny/assets/game/halo_mip.png new file mode 100644 index 00000000..1ebdc334 Binary files /dev/null and b/subway-surfers-ny/assets/game/halo_mip.png differ diff --git a/subway-surfers-ny/assets/game/jetpackSmoke_mip.png b/subway-surfers-ny/assets/game/jetpackSmoke_mip.png new file mode 100644 index 00000000..c6d04603 Binary files /dev/null and b/subway-surfers-ny/assets/game/jetpackSmoke_mip.png differ diff --git a/subway-surfers-ny/assets/game/model_avatar_catch.fbx b/subway-surfers-ny/assets/game/model_avatar_catch.fbx new file mode 100644 index 00000000..d09dde88 Binary files /dev/null and b/subway-surfers-ny/assets/game/model_avatar_catch.fbx differ diff --git a/subway-surfers-ny/assets/game/model_avatar_movement.fbx b/subway-surfers-ny/assets/game/model_avatar_movement.fbx new file mode 100644 index 00000000..a7f8134a Binary files /dev/null and b/subway-surfers-ny/assets/game/model_avatar_movement.fbx differ diff --git a/subway-surfers-ny/assets/game/model_avatar_powerup_jet_pack.fbx b/subway-surfers-ny/assets/game/model_avatar_powerup_jet_pack.fbx new file mode 100644 index 00000000..ce4f3062 Binary files /dev/null and b/subway-surfers-ny/assets/game/model_avatar_powerup_jet_pack.fbx differ diff --git a/subway-surfers-ny/assets/game/model_avatar_powerup_pogostick.fbx b/subway-surfers-ny/assets/game/model_avatar_powerup_pogostick.fbx new file mode 100644 index 00000000..f3a82388 Binary files /dev/null and b/subway-surfers-ny/assets/game/model_avatar_powerup_pogostick.fbx differ diff --git a/subway-surfers-ny/assets/game/model_avatar_start.fbx b/subway-surfers-ny/assets/game/model_avatar_start.fbx new file mode 100644 index 00000000..20cff1fb Binary files /dev/null and b/subway-surfers-ny/assets/game/model_avatar_start.fbx differ diff --git a/subway-surfers-ny/assets/game/model_dog_catch.fbx b/subway-surfers-ny/assets/game/model_dog_catch.fbx new file mode 100644 index 00000000..6f8d490d Binary files /dev/null and b/subway-surfers-ny/assets/game/model_dog_catch.fbx differ diff --git a/subway-surfers-ny/assets/game/model_dog_movement.fbx b/subway-surfers-ny/assets/game/model_dog_movement.fbx new file mode 100644 index 00000000..a4581224 Binary files /dev/null and b/subway-surfers-ny/assets/game/model_dog_movement.fbx differ diff --git a/subway-surfers-ny/assets/game/model_guard_catch.fbx b/subway-surfers-ny/assets/game/model_guard_catch.fbx new file mode 100644 index 00000000..dbfe3142 Binary files /dev/null and b/subway-surfers-ny/assets/game/model_guard_catch.fbx differ diff --git a/subway-surfers-ny/assets/game/model_guard_movement.fbx b/subway-surfers-ny/assets/game/model_guard_movement.fbx new file mode 100644 index 00000000..c7f538cd Binary files /dev/null and b/subway-surfers-ny/assets/game/model_guard_movement.fbx differ diff --git a/subway-surfers-ny/assets/game/ocean_mip.png b/subway-surfers-ny/assets/game/ocean_mip.png new file mode 100644 index 00000000..f6920ea6 Binary files /dev/null and b/subway-surfers-ny/assets/game/ocean_mip.png differ diff --git a/subway-surfers-ny/assets/game/props.fbx b/subway-surfers-ny/assets/game/props.fbx new file mode 100644 index 00000000..a4e9ac80 Binary files /dev/null and b/subway-surfers-ny/assets/game/props.fbx differ diff --git a/subway-surfers-ny/assets/game/shadow_mip.png b/subway-surfers-ny/assets/game/shadow_mip.png new file mode 100644 index 00000000..ee689efb Binary files /dev/null and b/subway-surfers-ny/assets/game/shadow_mip.png differ diff --git a/subway-surfers-ny/assets/game/spraySplash_mip.png b/subway-surfers-ny/assets/game/spraySplash_mip.png new file mode 100644 index 00000000..fd2e66a9 Binary files /dev/null and b/subway-surfers-ny/assets/game/spraySplash_mip.png differ diff --git a/subway-surfers-ny/assets/game/trains.fbx b/subway-surfers-ny/assets/game/trains.fbx new file mode 100644 index 00000000..2e295965 Binary files /dev/null and b/subway-surfers-ny/assets/game/trains.fbx differ diff --git a/subway-surfers-ny/assets/idle/environment_idle.fbx b/subway-surfers-ny/assets/idle/environment_idle.fbx new file mode 100644 index 00000000..be83da77 Binary files /dev/null and b/subway-surfers-ny/assets/idle/environment_idle.fbx differ diff --git a/subway-surfers-ny/assets/idle/environment_tex_mip.png b/subway-surfers-ny/assets/idle/environment_tex_mip.png new file mode 100644 index 00000000..96d97998 Binary files /dev/null and b/subway-surfers-ny/assets/idle/environment_tex_mip.png differ diff --git a/subway-surfers-ny/assets/idle/jake_tex_mip.png b/subway-surfers-ny/assets/idle/jake_tex_mip.png new file mode 100644 index 00000000..38f382f4 Binary files /dev/null and b/subway-surfers-ny/assets/idle/jake_tex_mip.png differ diff --git a/subway-surfers-ny/assets/idle/model_avatar_idle_paint.fbx b/subway-surfers-ny/assets/idle/model_avatar_idle_paint.fbx new file mode 100644 index 00000000..974c1842 Binary files /dev/null and b/subway-surfers-ny/assets/idle/model_avatar_idle_paint.fbx differ diff --git a/subway-surfers-ny/assets/idle/props_start.fbx b/subway-surfers-ny/assets/idle/props_start.fbx new file mode 100644 index 00000000..80b84100 Binary files /dev/null and b/subway-surfers-ny/assets/idle/props_start.fbx differ diff --git a/subway-surfers-ny/assets/idle/props_tex_mip.png b/subway-surfers-ny/assets/idle/props_tex_mip.png new file mode 100644 index 00000000..0c743a69 Binary files /dev/null and b/subway-surfers-ny/assets/idle/props_tex_mip.png differ diff --git a/subway-surfers-ny/assets/idle/train_start_mip.png b/subway-surfers-ny/assets/idle/train_start_mip.png new file mode 100644 index 00000000..09547b14 Binary files /dev/null and b/subway-surfers-ny/assets/idle/train_start_mip.png differ diff --git a/subway-surfers-ny/assets/idle/trains_start.fbx b/subway-surfers-ny/assets/idle/trains_start.fbx new file mode 100644 index 00000000..87216076 Binary files /dev/null and b/subway-surfers-ny/assets/idle/trains_start.fbx differ diff --git a/subway-surfers-ny/assets/idle/trains_tex_mip.png b/subway-surfers-ny/assets/idle/trains_tex_mip.png new file mode 100644 index 00000000..7c3f9208 Binary files /dev/null and b/subway-surfers-ny/assets/idle/trains_tex_mip.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_00_brody.png b/subway-surfers-ny/assets/placeholder/icon_00_brody.png new file mode 100644 index 00000000..950f7283 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_00_brody.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_01_tagbot.png b/subway-surfers-ny/assets/placeholder/icon_01_tagbot.png new file mode 100644 index 00000000..2fd4c253 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_01_tagbot.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_02_tasha.png b/subway-surfers-ny/assets/placeholder/icon_02_tasha.png new file mode 100644 index 00000000..d8191fe5 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_02_tasha.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_03_ninja.png b/subway-surfers-ny/assets/placeholder/icon_03_ninja.png new file mode 100644 index 00000000..60fa536f Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_03_ninja.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_04_lucy.png b/subway-surfers-ny/assets/placeholder/icon_04_lucy.png new file mode 100644 index 00000000..c918f7c9 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_04_lucy.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_05_king.png b/subway-surfers-ny/assets/placeholder/icon_05_king.png new file mode 100644 index 00000000..8b2f135a Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_05_king.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_07_yutani.png b/subway-surfers-ny/assets/placeholder/icon_07_yutani.png new file mode 100644 index 00000000..ceaa4b46 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_07_yutani.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_08_spike.png b/subway-surfers-ny/assets/placeholder/icon_08_spike.png new file mode 100644 index 00000000..5191a287 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_08_spike.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_09_fresh.png b/subway-surfers-ny/assets/placeholder/icon_09_fresh.png new file mode 100644 index 00000000..5c5f616d Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_09_fresh.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_11_tricky.png b/subway-surfers-ny/assets/placeholder/icon_11_tricky.png new file mode 100644 index 00000000..4ff05146 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_11_tricky.png differ diff --git a/subway-surfers-ny/assets/placeholder/icon_friend.png b/subway-surfers-ny/assets/placeholder/icon_friend.png new file mode 100644 index 00000000..87929c87 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/icon_friend.png differ diff --git a/subway-surfers-ny/assets/placeholder/jake.png b/subway-surfers-ny/assets/placeholder/jake.png new file mode 100644 index 00000000..5d963eb0 Binary files /dev/null and b/subway-surfers-ny/assets/placeholder/jake.png differ diff --git a/subway-surfers-ny/assets/preload/splash_mip.png b/subway-surfers-ny/assets/preload/splash_mip.png new file mode 100644 index 00000000..00ed83ff Binary files /dev/null and b/subway-surfers-ny/assets/preload/splash_mip.png differ diff --git a/subway-surfers-ny/assets/ui/ui.json b/subway-surfers-ny/assets/ui/ui.json new file mode 100644 index 00000000..263a2688 --- /dev/null +++ b/subway-surfers-ny/assets/ui/ui.json @@ -0,0 +1,1191 @@ +{ + "frames": { + "base_blurry.png": { + "frame": { + "x": 2, + "y": 2, + "w": 512, + "h": 90 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 512, + "h": 90 + }, + "sourceSize": { + "w": 512, + "h": 90 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "scoreboard.png": { + "frame": { + "x": 2, + "y": 96, + "w": 319, + "h": 180 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 319, + "h": 180 + }, + "sourceSize": { + "w": 320, + "h": 185 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "base_long.png": { + "frame": { + "x": 2, + "y": 280, + "w": 300, + "h": 80 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 300, + "h": 80 + }, + "sourceSize": { + "w": 302, + "h": 82 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "tutorial_arrow.png": { + "frame": { + "x": 2, + "y": 364, + "w": 256, + "h": 264 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 256, + "h": 264 + }, + "sourceSize": { + "w": 256, + "h": 264 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "front_icon_top_run.png": { + "frame": { + "x": 262, + "y": 364, + "w": 260, + "h": 171 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 2, + "w": 260, + "h": 171 + }, + "sourceSize": { + "w": 260, + "h": 174 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "front_icon_mytour.png": { + "frame": { + "x": 518, + "y": 2, + "w": 227, + "h": 125 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 227, + "h": 125 + }, + "sourceSize": { + "w": 228, + "h": 126 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "front_icon_shop.png": { + "frame": { + "x": 325, + "y": 131, + "w": 190, + "h": 151 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 190, + "h": 151 + }, + "sourceSize": { + "w": 194, + "h": 154 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "front_icon_me.png": { + "frame": { + "x": 519, + "y": 131, + "w": 133, + "h": 168 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 133, + "h": 168 + }, + "sourceSize": { + "w": 136, + "h": 172 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "clock_base.png": { + "frame": { + "x": 656, + "y": 131, + "w": 111, + "h": 108 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 111, + "h": 108 + }, + "sourceSize": { + "w": 112, + "h": 109 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_tv.png": { + "frame": { + "x": 656, + "y": 243, + "w": 83, + "h": 102 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 83, + "h": 102 + }, + "sourceSize": { + "w": 86, + "h": 106 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "btn_base.png": { + "frame": { + "x": 526, + "y": 303, + "w": 100, + "h": 100 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 100, + "h": 100 + }, + "sourceSize": { + "w": 102, + "h": 102 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "base_short.png": { + "frame": { + "x": 630, + "y": 349, + "w": 100, + "h": 80 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 100, + "h": 80 + }, + "sourceSize": { + "w": 102, + "h": 82 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "box_fill_green.png": { + "frame": { + "x": 526, + "y": 407, + "w": 55, + "h": 85 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 55, + "h": 85 + }, + "sourceSize": { + "w": 55, + "h": 86 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "btn_close.png": { + "frame": { + "x": 526, + "y": 496, + "w": 78, + "h": 82 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 78, + "h": 82 + }, + "sourceSize": { + "w": 83, + "h": 86 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "clock_fill.png": { + "frame": { + "x": 262, + "y": 539, + "w": 82, + "h": 82 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 82, + "h": 82 + }, + "sourceSize": { + "w": 82, + "h": 84 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_piggy.png": { + "frame": { + "x": 348, + "y": 539, + "w": 81, + "h": 62 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 2, + "w": 81, + "h": 62 + }, + "sourceSize": { + "w": 84, + "h": 64 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "box_border_grey.png": { + "frame": { + "x": 433, + "y": 539, + "w": 76, + "h": 77 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 76, + "h": 77 + }, + "sourceSize": { + "w": 78, + "h": 78 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_high_scores.png": { + "frame": { + "x": 585, + "y": 433, + "w": 77, + "h": 59 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 77, + "h": 59 + }, + "sourceSize": { + "w": 80, + "h": 64 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "diagonal.png": { + "frame": { + "x": 306, + "y": 286, + "w": 74, + "h": 74 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 74, + "h": 74 + }, + "sourceSize": { + "w": 77, + "h": 78 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_daily_challenge.png": { + "frame": { + "x": 384, + "y": 286, + "w": 73, + "h": 60 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 73, + "h": 60 + }, + "sourceSize": { + "w": 76, + "h": 62 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "box_fill_orange.png": { + "frame": { + "x": 461, + "y": 286, + "w": 41, + "h": 69 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 41, + "h": 69 + }, + "sourceSize": { + "w": 43, + "h": 71 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_top_run.png": { + "frame": { + "x": 666, + "y": 433, + "w": 48, + "h": 69 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 48, + "h": 69 + }, + "sourceSize": { + "w": 50, + "h": 72 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_back.png": { + "frame": { + "x": 608, + "y": 506, + "w": 69, + "h": 60 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 69, + "h": 60 + }, + "sourceSize": { + "w": 76, + "h": 68 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_awards.png": { + "frame": { + "x": 681, + "y": 506, + "w": 65, + "h": 69 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 65, + "h": 69 + }, + "sourceSize": { + "w": 68, + "h": 72 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "box_border_grey_small.png": { + "frame": { + "x": 348, + "y": 605, + "w": 68, + "h": 68 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 68, + "h": 68 + }, + "sourceSize": { + "w": 71, + "h": 71 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_upgrades.png": { + "frame": { + "x": 262, + "y": 625, + "w": 68, + "h": 62 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 2, + "w": 68, + "h": 62 + }, + "sourceSize": { + "w": 72, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "base_item.png": { + "frame": { + "x": 608, + "y": 570, + "w": 68, + "h": 68 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 68, + "h": 68 + }, + "sourceSize": { + "w": 70, + "h": 70 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_coins.png": { + "frame": { + "x": 513, + "y": 582, + "w": 68, + "h": 66 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 68, + "h": 66 + }, + "sourceSize": { + "w": 72, + "h": 68 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_boards.png": { + "frame": { + "x": 420, + "y": 620, + "w": 67, + "h": 63 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 2, + "w": 67, + "h": 63 + }, + "sourceSize": { + "w": 70, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_item_jetpack.png": { + "frame": { + "x": 680, + "y": 579, + "w": 61, + "h": 66 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 61, + "h": 66 + }, + "sourceSize": { + "w": 62, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_item_multiplier.png": { + "frame": { + "x": 2, + "y": 632, + "w": 65, + "h": 66 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 65, + "h": 66 + }, + "sourceSize": { + "w": 66, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_weekly_hunt.png": { + "frame": { + "x": 71, + "y": 632, + "w": 62, + "h": 65 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 62, + "h": 65 + }, + "sourceSize": { + "w": 66, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_user.png": { + "frame": { + "x": 718, + "y": 433, + "w": 50, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 50, + "h": 64 + }, + "sourceSize": { + "w": 51, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_characters.png": { + "frame": { + "x": 137, + "y": 632, + "w": 47, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 47, + "h": 64 + }, + "sourceSize": { + "w": 50, + "h": 68 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_house.png": { + "frame": { + "x": 188, + "y": 632, + "w": 62, + "h": 63 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 62, + "h": 63 + }, + "sourceSize": { + "w": 72, + "h": 74 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_item_hoverboard.png": { + "frame": { + "x": 585, + "y": 642, + "w": 62, + "h": 37 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 62, + "h": 37 + }, + "sourceSize": { + "w": 69, + "h": 40 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_collect_coins.png": { + "frame": { + "x": 334, + "y": 677, + "w": 52, + "h": 62 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 52, + "h": 62 + }, + "sourceSize": { + "w": 56, + "h": 66 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_white_missions.png": { + "frame": { + "x": 254, + "y": 691, + "w": 62, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 62, + "h": 58 + }, + "sourceSize": { + "w": 66, + "h": 59 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_info.png": { + "frame": { + "x": 734, + "y": 349, + "w": 29, + "h": 60 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 29, + "h": 60 + }, + "sourceSize": { + "w": 37, + "h": 71 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_settings.png": { + "frame": { + "x": 188, + "y": 699, + "w": 57, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 57, + "h": 58 + }, + "sourceSize": { + "w": 59, + "h": 60 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_coin_large.png": { + "frame": { + "x": 71, + "y": 701, + "w": 58, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 3, + "y": 4, + "w": 58, + "h": 58 + }, + "sourceSize": { + "w": 64, + "h": 64 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_item_sneakers.png": { + "frame": { + "x": 2, + "y": 702, + "w": 57, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 57, + "h": 58 + }, + "sourceSize": { + "w": 58, + "h": 58 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_item_magnet.png": { + "frame": { + "x": 137, + "y": 700, + "w": 47, + "h": 57 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 47, + "h": 57 + }, + "sourceSize": { + "w": 49, + "h": 58 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_sound.png": { + "frame": { + "x": 491, + "y": 652, + "w": 57, + "h": 45 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 57, + "h": 45 + }, + "sourceSize": { + "w": 59, + "h": 48 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_key.png": { + "frame": { + "x": 390, + "y": 687, + "w": 36, + "h": 52 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 36, + "h": 52 + }, + "sourceSize": { + "w": 36, + "h": 52 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_coin.png": { + "frame": { + "x": 430, + "y": 687, + "w": 44, + "h": 43 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 44, + "h": 43 + }, + "sourceSize": { + "w": 52, + "h": 50 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_pause.png": { + "frame": { + "x": 743, + "y": 243, + "w": 32, + "h": 38 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 38 + }, + "sourceSize": { + "w": 32, + "h": 38 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "item_duration_bar.png": { + "frame": { + "x": 630, + "y": 303, + "w": 11, + "h": 34 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 11, + "h": 34 + }, + "sourceSize": { + "w": 11, + "h": 34 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "icon_star.png": { + "frame": { + "x": 743, + "y": 285, + "w": 29, + "h": 29 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 29, + "h": 29 + }, + "sourceSize": { + "w": 40, + "h": 38 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + } + }, + "meta": { + "app": "http://github.com/odrick/free-tex-packer-core", + "version": "0.2.0", + "image": "ui.png", + "format": "RGBA8888", + "size": { + "w": 777, + "h": 762 + }, + "scale": 1 + } +} diff --git a/subway-surfers-ny/assets/ui/ui.png b/subway-surfers-ny/assets/ui/ui.png new file mode 100644 index 00000000..ac680ac4 Binary files /dev/null and b/subway-surfers-ny/assets/ui/ui.png differ diff --git a/subway-surfers-ny/index.html b/subway-surfers-ny/index.html new file mode 100644 index 00000000..35866c40 --- /dev/null +++ b/subway-surfers-ny/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + Subway Surfers New York | 3kh0 + + + + +
+ + + + diff --git a/subway-surfers-ny/js/boot.js b/subway-surfers-ny/js/boot.js new file mode 100644 index 00000000..e24689b5 --- /dev/null +++ b/subway-surfers-ny/js/boot.js @@ -0,0 +1,122 @@ +! function(t) { + var a = {}; + + function __webpack_require__(e) { + if (a[e]) return a[e].exports; + var n = a[e] = { + i: e, + l: !1, + exports: {} + }; + return t[e].call(n.exports, n, n.exports, __webpack_require__), n.l = !0, n.exports + } + __webpack_require__.m = t, __webpack_require__.c = a, __webpack_require__.d = function(e, n, t) { + __webpack_require__.o(e, n) || Object.defineProperty(e, n, { + enumerable: !0, + get: t + }) + }, __webpack_require__.r = function(e) { + "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { + value: "Module" + }), Object.defineProperty(e, "__esModule", { + value: !0 + }) + }, __webpack_require__.t = function(n, e) { + if (1 & e && (n = __webpack_require__(n)), 8 & e) return n; + if (4 & e && "object" == typeof n && n && n.__esModule) return n; + var t = Object.create(null); + if (__webpack_require__.r(t), Object.defineProperty(t, "default", { + enumerable: !0, + value: n + }), 2 & e && "string" != typeof n) + for (var a in n) __webpack_require__.d(t, a, function(e) { + return n[e] + }.bind(null, a)); + return t + }, __webpack_require__.n = function(e) { + var n = e && e.__esModule ? function() { + return e.default + } : function() { + return e + }; + return __webpack_require__.d(n, "a", n), n + }, __webpack_require__.o = function(e, n) { + return Object.prototype.hasOwnProperty.call(e, n) + }, __webpack_require__.p = "", __webpack_require__(__webpack_require__.s = 486) +}({ + 486: function(e, n, t) { + function pathJoin() { + for (var e = arguments.length, n = new Array(e), t = 0; t < e; t++) n[t] = arguments[t]; + return n.join("/") + } + + function loadJson(e, n, t) { + var a = new XMLHttpRequest; + a.addEventListener("load", function() { + var e = JSON.parse(this.responseText); + n && n(e) + }), a.open("GET", e + "?v=1gefx1jwdk0gj12hb"), a.send() + } + + function loadScript(e, n) { + try { + var t = document.createElement("script"); + n && (t.onload = n), t.src = e + "?v=1gefx1jwdk0gj12hb", document.head.appendChild(t) + } catch (e) {} + } + + function loadFont(e, n) { + var t = document.createElement("link"); + t.rel = "stylesheet", t.type = "text/css", t.href = e + "?v=1gefx1jwdk0gj12hb", t.media = "all", document.head.appendChild(t); + var a = document.createElement("div"); + a.className = "fontcache", a.style = 'font-family: "' + n + '";', a.textContent = ".", document.body.appendChild(a) + } + + function initApp(e) { + var n, t, a; + n = e.meta, t = pathJoin(n.path, n.splash), (a = document.querySelector("#og-game-holder")).style.backgroundColor = n.color, a.style.backgroundImage = 'url("' + t + '?v=1gefx1jwdk0gj12hb"")', loadFont("assets/font/lilita-one.css", "Lilita One"), loadFont("assets/font/titan-one.css", "Titan One"), window.sharedAppData.config.leaderboards && loadScript("js/leaderboard.js"), window.sharedAppData.config.vconsole && loadScript("https://cdnjs.cloudflare.com/ajax/libs/vConsole/3.3.0/vconsole.min.js"), loadScript("js/inflate.min.js"), loadScript("js/vendor.js"), loadScript("js/main.js") + } + console.log = function() { + return null + }, console.warn = function() { + return null + }, window.GAME_CONFIG || (window.GAME_CONFIG = { + fastplay: !1 + }), loadJson("assets/data/config.json", function(t) { + var e, n, a, o = function() { + var e = location.search.slice(location.search.indexOf("?") + 1).split("&"), + n = {}; + for (var t in e) { + var a = e[t].split("="), + o = a[0], + r = a[1]; + void 0 !== r && ("true" !== r && "false" !== r || (r = "true" === r), n[o] = "string" == typeof r && r.match(/^[-.0-9]+$/) ? parseFloat(r) : r) + } + return n + }(); + for (var r in t.pokiSdkDebug = !1, window.GAME_CONFIG) t[r] = window.GAME_CONFIG[r]; + for (var r in o) t[r] = o[r]; + window.sharedAppData = { + config: t, + bundle: { + meta: { + path: "./", + color: "#ffa4cd", + splash: "/assets/preload/splash_mip.png" + } + } + }, e = t.pokiSdkDebug, n = function(e) { + if (void 0 === t.adBlocking && (t.adBlocking = e), t.bundlepath && t.env !== t.env_builtin) { + var n = pathJoin(t.bundlepath, t.env); + loadJson(pathJoin(n, "manifest.json"), function(e) { + e.meta.path = n, window.sharedAppData.bundle = e, initApp(window.sharedAppData.bundle) + }) + } else initApp(window.sharedAppData.bundle) + }, (a = window.Tabouzigtsdk) ? (a.init().then(function() { + a.gameLoadingStart(), n(!1) + }).catch(function() { + a.gameLoadingStart(), n(!0) + }), a.setDebug(e)) : n(!0) + }) + } +}); \ No newline at end of file diff --git a/subway-surfers-ny/js/games_lib/ludiAdapter.js b/subway-surfers-ny/js/games_lib/ludiAdapter.js new file mode 100644 index 00000000..3a8eae59 --- /dev/null +++ b/subway-surfers-ny/js/games_lib/ludiAdapter.js @@ -0,0 +1,57 @@ +var myDataLayer = function() { + var arr = []; + + + return arr; +}; + +var dataLayer = new myDataLayer; +var enableADS = parent.enableADS; +var productKey = parent.productKey; +var productTitle = parent.productTitle; +if (parent.AdsState) { + var AdsState = parent.AdsState; +} +if (parent.adsContainer) { + var adsContainer = parent.adsContainer; +} + +if (parent.adsCurrentState) { + var adsCurrentState = parent.adsCurrentState; +} + +var playAds = function() { + //parent.playAds(); + sdk.showBanner(); +} +var getReferrer = function() { + return parent.getReferrer(); +} + +var show_freeFrame = function() { + //return parent.playAds(); + sdk.showBanner(); +} +var show_freeFrame_Loc = function(l) { + //return parent.playAds(); + sdk.showBanner(); +} +var o_setDisplayRewardAds = function(v) { + //return parent.o_setDisplayRewardAds(v); + sdk.showBanner(); +} +var ora_isDisplayingReward = function(v) { + //return parent.ora_isDisplayingReward(v); + sdk.showBanner(); +} +var ora_getPayloadAmount = function() { + return parent.ora_getPayloadAmount(); +} +var isAdsComplete = function() { + return parent.isAdsComplete(); +} +var myConsoleLog = function() { + return parent.myConsoleLog(); +} + +var isShowAdScreen = false; \ No newline at end of file diff --git a/subway-surfers-ny/js/inflate.min.js b/subway-surfers-ny/js/inflate.min.js new file mode 100644 index 00000000..33067ae9 --- /dev/null +++ b/subway-surfers-ny/js/inflate.min.js @@ -0,0 +1,358 @@ +/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */ +(function() { + 'use strict'; + var l = void 0, + aa = this; + + function r(c, d) { + var a = c.split("."), + b = aa; + !(a[0] in b) && b.execScript && b.execScript("var " + a[0]); + for (var e; a.length && (e = a.shift());) !a.length && d !== l ? b[e] = d : b = b[e] ? b[e] : b[e] = {} + }; + var t = "undefined" !== typeof Uint8Array && "undefined" !== typeof Uint16Array && "undefined" !== typeof Uint32Array && "undefined" !== typeof DataView; + + function v(c) { + var d = c.length, + a = 0, + b = Number.POSITIVE_INFINITY, + e, f, g, h, k, m, n, p, s, x; + for (p = 0; p < d; ++p) c[p] > a && (a = c[p]), c[p] < b && (b = c[p]); + e = 1 << a; + f = new(t ? Uint32Array : Array)(e); + g = 1; + h = 0; + for (k = 2; g <= a;) { + for (p = 0; p < d; ++p) + if (c[p] === g) { + m = 0; + n = h; + for (s = 0; s < g; ++s) m = m << 1 | n & 1, n >>= 1; + x = g << 16 | p; + for (s = m; s < e; s += k) f[s] = x; + ++h + }++g; + h <<= 1; + k <<= 1 + } + return [f, a, b] + }; + + function w(c, d) { + this.g = []; + this.h = 32768; + this.d = this.f = this.a = this.l = 0; + this.input = t ? new Uint8Array(c) : c; + this.m = !1; + this.i = y; + this.r = !1; + if (d || !(d = {})) d.index && (this.a = d.index), d.bufferSize && (this.h = d.bufferSize), d.bufferType && (this.i = d.bufferType), d.resize && (this.r = d.resize); + switch (this.i) { + case A: + this.b = 32768; + this.c = new(t ? Uint8Array : Array)(32768 + this.h + 258); + break; + case y: + this.b = 0; + this.c = new(t ? Uint8Array : Array)(this.h); + this.e = this.z; + this.n = this.v; + this.j = this.w; + break; + default: + throw Error("invalid inflate mode"); + } + } + var A = 0, + y = 1, + B = { + t: A, + s: y + }; + w.prototype.k = function() { + for (; !this.m;) { + var c = C(this, 3); + c & 1 && (this.m = !0); + c >>>= 1; + switch (c) { + case 0: + var d = this.input, + a = this.a, + b = this.c, + e = this.b, + f = d.length, + g = l, + h = l, + k = b.length, + m = l; + this.d = this.f = 0; + if (a + 1 >= f) throw Error("invalid uncompressed block header: LEN"); + g = d[a++] | d[a++] << 8; + if (a + 1 >= f) throw Error("invalid uncompressed block header: NLEN"); + h = d[a++] | d[a++] << 8; + if (g === ~h) throw Error("invalid uncompressed block header: length verify"); + if (a + g > d.length) throw Error("input buffer is broken"); + switch (this.i) { + case A: + for (; e + + g > b.length;) { + m = k - e; + g -= m; + if (t) b.set(d.subarray(a, a + m), e), e += m, a += m; + else + for (; m--;) b[e++] = d[a++]; + this.b = e; + b = this.e(); + e = this.b + } + break; + case y: + for (; e + g > b.length;) b = this.e({ + p: 2 + }); + break; + default: + throw Error("invalid inflate mode"); + } + if (t) b.set(d.subarray(a, a + g), e), e += g, a += g; + else + for (; g--;) b[e++] = d[a++]; + this.a = a; + this.b = e; + this.c = b; + break; + case 1: + this.j(ba, ca); + break; + case 2: + for (var n = C(this, 5) + 257, p = C(this, 5) + 1, s = C(this, 4) + 4, x = new(t ? Uint8Array : Array)(D.length), S = l, T = l, U = l, u = l, M = l, F = l, z = l, q = l, V = l, q = 0; q < s; ++q) x[D[q]] = + C(this, 3); + if (!t) { + q = s; + for (s = x.length; q < s; ++q) x[D[q]] = 0 + } + S = v(x); + u = new(t ? Uint8Array : Array)(n + p); + q = 0; + for (V = n + p; q < V;) switch (M = E(this, S), M) { + case 16: + for (z = 3 + C(this, 2); z--;) u[q++] = F; + break; + case 17: + for (z = 3 + C(this, 3); z--;) u[q++] = 0; + F = 0; + break; + case 18: + for (z = 11 + C(this, 7); z--;) u[q++] = 0; + F = 0; + break; + default: + F = u[q++] = M + } + T = t ? v(u.subarray(0, n)) : v(u.slice(0, n)); + U = t ? v(u.subarray(n)) : v(u.slice(n)); + this.j(T, U); + break; + default: + throw Error("unknown BTYPE: " + c); + } + } + return this.n() + }; + var G = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], + D = t ? new Uint16Array(G) : G, + H = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 258, 258], + I = t ? new Uint16Array(H) : H, + J = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0], + K = t ? new Uint8Array(J) : J, + L = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577], + da = t ? new Uint16Array(L) : L, + ea = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, + 13, 13 + ], + N = t ? new Uint8Array(ea) : ea, + O = new(t ? Uint8Array : Array)(288), + P, fa; + P = 0; + for (fa = O.length; P < fa; ++P) O[P] = 143 >= P ? 8 : 255 >= P ? 9 : 279 >= P ? 7 : 8; + var ba = v(O), + Q = new(t ? Uint8Array : Array)(30), + R, ga; + R = 0; + for (ga = Q.length; R < ga; ++R) Q[R] = 5; + var ca = v(Q); + + function C(c, d) { + for (var a = c.f, b = c.d, e = c.input, f = c.a, g = e.length, h; b < d;) { + if (f >= g) throw Error("input buffer is broken"); + a |= e[f++] << b; + b += 8 + } + h = a & (1 << d) - 1; + c.f = a >>> d; + c.d = b - d; + c.a = f; + return h + } + + function E(c, d) { + for (var a = c.f, b = c.d, e = c.input, f = c.a, g = e.length, h = d[0], k = d[1], m, n; b < k && !(f >= g);) a |= e[f++] << b, b += 8; + m = h[a & (1 << k) - 1]; + n = m >>> 16; + if (n > b) throw Error("invalid code length: " + n); + c.f = a >> n; + c.d = b - n; + c.a = f; + return m & 65535 + } + w.prototype.j = function(c, d) { + var a = this.c, + b = this.b; + this.o = c; + for (var e = a.length - 258, f, g, h, k; 256 !== (f = E(this, c));) + if (256 > f) b >= e && (this.b = b, a = this.e(), b = this.b), a[b++] = f; + else { + g = f - 257; + k = I[g]; + 0 < K[g] && (k += C(this, K[g])); + f = E(this, d); + h = da[f]; + 0 < N[f] && (h += C(this, N[f])); + b >= e && (this.b = b, a = this.e(), b = this.b); + for (; k--;) a[b] = a[b++ - h] + } + for (; 8 <= this.d;) this.d -= 8, this.a--; + this.b = b + }; + w.prototype.w = function(c, d) { + var a = this.c, + b = this.b; + this.o = c; + for (var e = a.length, f, g, h, k; 256 !== (f = E(this, c));) + if (256 > f) b >= e && (a = this.e(), e = a.length), a[b++] = f; + else { + g = f - 257; + k = I[g]; + 0 < K[g] && (k += C(this, K[g])); + f = E(this, d); + h = da[f]; + 0 < N[f] && (h += C(this, N[f])); + b + k > e && (a = this.e(), e = a.length); + for (; k--;) a[b] = a[b++ - h] + } + for (; 8 <= this.d;) this.d -= 8, this.a--; + this.b = b + }; + w.prototype.e = function() { + var c = new(t ? Uint8Array : Array)(this.b - 32768), + d = this.b - 32768, + a, b, e = this.c; + if (t) c.set(e.subarray(32768, c.length)); + else { + a = 0; + for (b = c.length; a < b; ++a) c[a] = e[a + 32768] + } + this.g.push(c); + this.l += c.length; + if (t) e.set(e.subarray(d, d + 32768)); + else + for (a = 0; 32768 > a; ++a) e[a] = e[d + a]; + this.b = 32768; + return e + }; + w.prototype.z = function(c) { + var d, a = this.input.length / this.a + 1 | 0, + b, e, f, g = this.input, + h = this.c; + c && ("number" === typeof c.p && (a = c.p), "number" === typeof c.u && (a += c.u)); + 2 > a ? (b = (g.length - this.a) / this.o[2], f = 258 * (b / 2) | 0, e = f < h.length ? h.length + f : h.length << 1) : e = h.length * a; + t ? (d = new Uint8Array(e), d.set(h)) : d = h; + return this.c = d + }; + w.prototype.n = function() { + var c = 0, + d = this.c, + a = this.g, + b, e = new(t ? Uint8Array : Array)(this.l + (this.b - 32768)), + f, g, h, k; + if (0 === a.length) return t ? this.c.subarray(32768, this.b) : this.c.slice(32768, this.b); + f = 0; + for (g = a.length; f < g; ++f) { + b = a[f]; + h = 0; + for (k = b.length; h < k; ++h) e[c++] = b[h] + } + f = 32768; + for (g = this.b; f < g; ++f) e[c++] = d[f]; + this.g = []; + return this.buffer = e + }; + w.prototype.v = function() { + var c, d = this.b; + t ? this.r ? (c = new Uint8Array(d), c.set(this.c.subarray(0, d))) : c = this.c.subarray(0, d) : (this.c.length > d && (this.c.length = d), c = this.c); + return this.buffer = c + }; + + function W(c, d) { + var a, b; + this.input = c; + this.a = 0; + if (d || !(d = {})) d.index && (this.a = d.index), d.verify && (this.A = d.verify); + a = c[this.a++]; + b = c[this.a++]; + switch (a & 15) { + case ha: + this.method = ha; + break; + default: + throw Error("unsupported compression method"); + } + if (0 !== ((a << 8) + b) % 31) throw Error("invalid fcheck flag:" + ((a << 8) + b) % 31); + if (b & 32) throw Error("fdict flag is not supported"); + this.q = new w(c, { + index: this.a, + bufferSize: d.bufferSize, + bufferType: d.bufferType, + resize: d.resize + }) + } + W.prototype.k = function() { + var c = this.input, + d, a; + d = this.q.k(); + this.a = this.q.a; + if (this.A) { + a = (c[this.a++] << 24 | c[this.a++] << 16 | c[this.a++] << 8 | c[this.a++]) >>> 0; + var b = d; + if ("string" === typeof b) { + var e = b.split(""), + f, g; + f = 0; + for (g = e.length; f < g; f++) e[f] = (e[f].charCodeAt(0) & 255) >>> 0; + b = e + } + for (var h = 1, k = 0, m = b.length, n, p = 0; 0 < m;) { + n = 1024 < m ? 1024 : m; + m -= n; + do h += b[p++], k += h; while (--n); + h %= 65521; + k %= 65521 + } + if (a !== (k << 16 | h) >>> 0) throw Error("invalid adler-32 checksum"); + } + return d + }; + var ha = 8; + r("Zlib.Inflate", W); + r("Zlib.Inflate.prototype.decompress", W.prototype.k); + var X = { + ADAPTIVE: B.s, + BLOCK: B.t + }, + Y, Z, $, ia; + if (Object.keys) Y = Object.keys(X); + else + for (Z in Y = [], $ = 0, X) Y[$++] = Z; + $ = 0; + for (ia = Y.length; $ < ia; ++$) Z = Y[$], r("Zlib.Inflate.BufferType." + Z, X[Z]); +}).call(this); \ No newline at end of file diff --git a/subway-surfers-ny/js/main.js b/subway-surfers-ny/js/main.js new file mode 100644 index 00000000..4cd9ff3f --- /dev/null +++ b/subway-surfers-ny/js/main.js @@ -0,0 +1,13254 @@ +!(function (h) { + function webpackJsonpCallback(t) { + for (var e, i, n = t[0], o = t[1], s = t[2], a = 0, r = []; a < n.length; a++) (i = n[a]), d[i] && r.push(d[i][0]), (d[i] = 0); + for (e in o) Object.prototype.hasOwnProperty.call(o, e) && (h[e] = o[e]); + for (c && c(t); r.length; ) r.shift()(); + return l.push.apply(l, s || []), checkDeferredModules(); + } + + function checkDeferredModules() { + for (var t, e = 0; e < l.length; e++) { + for (var i = l[e], n = !0, o = 1; o < i.length; o++) { + var s = i[o]; + 0 !== d[s] && (n = !1); + } + n && (l.splice(e--, 1), (t = __webpack_require__((__webpack_require__.s = i[0])))); + } + return t; + } + var i = {}, + d = { + 1: 0, + }, + l = []; + + function __webpack_require__(t) { + if (i[t]) return i[t].exports; + var e = (i[t] = { + i: t, + l: !1, + exports: {}, + }); + return h[t].call(e.exports, e, e.exports, __webpack_require__), (e.l = !0), e.exports; + } + (__webpack_require__.m = h), + (__webpack_require__.c = i), + (__webpack_require__.d = function (t, e, i) { + __webpack_require__.o(t, e) || + Object.defineProperty(t, e, { + enumerable: !0, + get: i, + }); + }), + (__webpack_require__.r = function (t) { + "undefined" != typeof Symbol && + Symbol.toStringTag && + Object.defineProperty(t, Symbol.toStringTag, { + value: "Module", + }), + Object.defineProperty(t, "__esModule", { + value: !0, + }); + }), + (__webpack_require__.t = function (e, t) { + if ((1 & t && (e = __webpack_require__(e)), 8 & t)) return e; + if (4 & t && "object" == typeof e && e && e.__esModule) return e; + var i = Object.create(null); + if ( + (__webpack_require__.r(i), + Object.defineProperty(i, "default", { + enumerable: !0, + value: e, + }), + 2 & t && "string" != typeof e) + ) + for (var n in e) + __webpack_require__.d( + i, + n, + function (t) { + return e[t]; + }.bind(null, n) + ); + return i; + }), + (__webpack_require__.n = function (t) { + var e = + t && t.__esModule + ? function () { + return t["default"]; + } + : function () { + return t; + }; + return __webpack_require__.d(e, "a", e), e; + }), + (__webpack_require__.o = function (t, e) { + return Object.prototype.hasOwnProperty.call(t, e); + }), + (__webpack_require__.p = ""); + var t = (this.webpackJsonp = this.webpackJsonp || []), + e = t.push.bind(t); + (t.push = webpackJsonpCallback), (t = t.slice()); + for (var n = 0; n < t.length; n++) webpackJsonpCallback(t[n]); + var c = e; + l.push([199, 2]), checkDeferredModules(); + })({ + 127: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + easy: i(430), + expert: i(439), + normal: i(446), + hard: i(455), + main: i(462), + special: i(468), + }); + }, + 129: function (t, e) { + t.exports = + "//precision highp float;\nvarying vec2 vUvs;\n\nuniform sampler2D diffuse;\nvarying vec3 vNormal;\nvarying float vLight;\nvarying vec3 vPosition;\nvarying vec3 vView;\n\nvoid main()\n{\n\t// vec3 p = vec3(vView * vPosition);\n\t// vec3 v = normalize(-p);\n\t// float vdn = 1.0 - max(dot(v, vNormal), 0.0); \n\n // // gl_FragColor = texture2D(diffuse, vec2(vUvs.x, 1.-vUvs.y));//directionalLightWeighting;\n // // gl_FragColor.rgb = mix(gl_FragColor.rgb*vLight, gl_FragColor.rgb, 0.7);\n\n // if (vdn <= 1.0) {\n // \tgl_FragColor.rgb = vec3(0, 0, 0);\n\t// } else {\n\t// \tgl_FragColor.rgb = vec3(1, 1, 1);\t\n\t// }\n \n vec3 nn = normalize(vNormal);\n vec3 light_dir = normalize(vPosition);\n vec3 eye_dir = normalize(-vPosition);\n vec3 reflect_dir = normalize(reflect(light_dir, nn));\n\t\n float spec = max(dot(reflect_dir, eye_dir), 0.0);\n\tfloat diffuse = max(dot(-light_dir, nn), 0.0);\n\n float intensity = 0.6 * diffuse + 0.4 * spec;\n\n \tif (intensity > 0.9) {\n \t\tintensity = 1.1;\n \t}\n \telse if (intensity > 0.5) {\n \t\tintensity = 0.7;\n \t}\n \telse {\n \t\tintensity = 0.0;\n \t}\n\n \t// gl_FragColor = texture2D(diffuse, vec2(vUvs.x, 1.-vUvs.y));\n \t// vec3 col = mix(gl_FragColor.rgb*vLight, gl_FragColor.rgb, 0.7);\n \tgl_FragColor.rgb = vec3(1, 1, 1) * intensity;\n \n}\n"; + }, + 130: function (t, e) { + t.exports = + "\nattribute vec3 position;\nattribute vec3 normals;\nattribute vec4 bone_indices;\nattribute vec2 uvs;\n\nattribute vec4 weights;\n\nvarying vec2 vUvs;\nvarying vec3 vNormal;\nvarying float vLight;\nvarying vec3 vPosition;\nvarying vec3 vView;\n\nuniform mat4 view;\nuniform mat4 model;\nuniform mat3 normal;\nuniform mat4 projection;\n\nuniform mat4 bones[26];\n\nvoid main() {\n\n\tvUvs = uvs;\n\n\tvec4 outPosition = vec4(0.0);\n\tvec4 originalPosition = vec4(position, 1.);\n\n\tmat4 boneMatrix = mat4(0.0);\n\n\tboneMatrix += bones[int(bone_indices.x)] * weights.x;\n\tboneMatrix += bones[int(bone_indices.y)] * weights.y;\n\tboneMatrix += bones[int(bone_indices.z)] * weights.z;\n\tboneMatrix += bones[int(bone_indices.w)] * weights.w;\n\t// vNormal = normalize( normal * (vec4(boneMatrix * vec4(normals,0.0)).xyz) );\n\n\tvec3 transformedNormal = normalize( normal * (vec4(boneMatrix * vec4(normals,0.0)).xyz) );\n\tvNormal = transformedNormal;\n\tvPosition = position;\n\n\tvec3 p;\n\tvView = vec3(view * vec4(p, 0.0));\n\n\t// no light for now\n\t// vLight = max(dot(transformedNormal, vec3(0.0, 0.0,1.0)), 0.0);\n\tvLight = 1.0;\n\n gl_Position = projection * view * model * boneMatrix * originalPosition;\n}\n"; + }, + 132: function (t) { + t.exports = { + default: { + image: {}, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: {}, + }, + audio: { + image: {}, + fonts: {}, + audio: { + "audio/guard_catch": { + mp3: "audio/guard_catch.mp3", + ogg: "audio/guard_catch.ogg", + }, + "audio/guard_proximity": { + mp3: "audio/guard_proximity.mp3", + ogg: "audio/guard_proximity.ogg", + }, + "audio/guard_start": { + mp3: "audio/guard_start.mp3", + ogg: "audio/guard_start.ogg", + }, + "audio/hero_death": { + mp3: "audio/hero_death.mp3", + ogg: "audio/hero_death.ogg", + }, + "audio/hero_death_hitcam": { + mp3: "audio/hero_death_hitcam.mp3", + ogg: "audio/hero_death_hitcam.ogg", + }, + "audio/hero_dodge": { + mp3: "audio/hero_dodge.mp3", + ogg: "audio/hero_dodge.ogg", + }, + "audio/hero_foot_l": { + mp3: "audio/hero_foot_l.mp3", + ogg: "audio/hero_foot_l.ogg", + }, + "audio/hero_foot_r": { + mp3: "audio/hero_foot_r.mp3", + ogg: "audio/hero_foot_r.ogg", + }, + "audio/hero_hoverboard_crash": { + mp3: "audio/hero_hoverboard_crash.mp3", + ogg: "audio/hero_hoverboard_crash.ogg", + }, + "audio/hero_jump": { + mp3: "audio/hero_jump.mp3", + ogg: "audio/hero_jump.ogg", + }, + "audio/hero_revive": { + mp3: "audio/hero_revive.mp3", + ogg: "audio/hero_revive.ogg", + }, + "audio/hero_roll": { + mp3: "audio/hero_roll.mp3", + ogg: "audio/hero_roll.ogg", + }, + "audio/hero_sneakers_foot_l": { + mp3: "audio/hero_sneakers_foot_l.mp3", + ogg: "audio/hero_sneakers_foot_l.ogg", + }, + "audio/hero_sneakers_foot_r": { + mp3: "audio/hero_sneakers_foot_r.mp3", + ogg: "audio/hero_sneakers_foot_r.ogg", + }, + "audio/hero_sneakers_jump": { + mp3: "audio/hero_sneakers_jump.mp3", + ogg: "audio/hero_sneakers_jump.ogg", + }, + "audio/hero_stumble": { + mp3: "audio/hero_stumble.mp3", + ogg: "audio/hero_stumble.ogg", + }, + "audio/pickup_coin": { + mp3: "audio/pickup_coin.mp3", + ogg: "audio/pickup_coin.ogg", + }, + "audio/pickup_powerup": { + mp3: "audio/pickup_powerup.mp3", + ogg: "audio/pickup_powerup.ogg", + }, + "audio/special_jetpack": { + mp3: "audio/special_jetpack.mp3", + ogg: "audio/special_jetpack.ogg", + }, + "audio/special_jetpack_start": { + mp3: "audio/special_jetpack_start.mp3", + ogg: "audio/special_jetpack_start.ogg", + }, + "audio/special_magnet": { + mp3: "audio/special_magnet.mp3", + ogg: "audio/special_magnet.ogg", + }, + "audio/theme": { + mp3: "audio/theme.mp3", + ogg: "audio/theme.ogg", + }, + "audio/ui_button": { + mp3: "audio/ui_button.mp3", + ogg: "audio/ui_button.ogg", + }, + }, + model: {}, + json: {}, + animate: {}, + misc: {}, + }, + data: { + image: {}, + fonts: {}, + audio: {}, + model: {}, + json: { + "data/config": { + default: "data/config.json", + }, + "data/strings_en": { + default: "data/strings_en.json", + }, + "data/strings_pt-br": { + default: "data/strings_pt-br.json", + }, + }, + animate: {}, + misc: {}, + }, + chunks_game: { + image: {}, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: { + "data/chunks_game": { + tags: { + manifest: !0, + }, + default: "data/chunks_game.json", + }, + }, + }, + chunks_idle: { + image: {}, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: { + "data/chunks_idle": { + tags: { + manifest: !0, + }, + default: "data/chunks_idle.json", + }, + }, + }, + font: { + image: {}, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: { + "font/lilita-one": { + default: "font/lilita-one.css", + }, + "font/lilita-one-1": { + default: "font/lilita-one.woff", + }, + "font/lilita-one-2": { + default: "font/lilita-one.woff2", + }, + "font/titan-one": { + default: "font/titan-one.css", + }, + "font/titan-one-1": { + default: "font/titan-one.woff", + }, + "font/titan-one-2": { + default: "font/titan-one.woff2", + }, + }, + }, + game: { + image: { + "game/board_new_york_tex": { + default: "game/board_new_york_tex.png", + low: "game/board_new_york_tex_mip.png", + }, + "game/effects_tex": { + default: "game/effects_tex.png", + low: "game/effects_tex_mip.png", + }, + "game/enemies": { + default: "game/enemies.png", + low: "game/enemies_mip.png", + }, + "game/halo": { + default: "game/halo.png", + low: "game/halo_mip.png", + }, + "game/jetpackSmoke": { + default: "game/jetpackSmoke.png", + low: "game/jetpackSmoke_mip.png", + }, + "game/ocean": { + default: "game/ocean.png", + low: "game/ocean_mip.png", + }, + "game/shadow": { + default: "game/shadow.png", + low: "game/shadow_mip.png", + }, + "game/spraySplash": { + default: "game/spraySplash.png", + low: "game/spraySplash_mip.png", + }, + }, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: { + "game/board_new_york": { + default: "game/board_new_york.fbx", + }, + "game/board_new_york01": { + default: "game/board_new_york01.fbx", + }, + "game/environment": { + default: "game/environment.fbx", + }, + "game/model_avatar_catch": { + default: "game/model_avatar_catch.fbx", + }, + "game/model_avatar_movement": { + default: "game/model_avatar_movement.fbx", + }, + "game/model_avatar_powerup_jet_pack": { + default: "game/model_avatar_powerup_jet_pack.fbx", + }, + "game/model_avatar_powerup_pogostick": { + default: "game/model_avatar_powerup_pogostick.fbx", + }, + "game/model_avatar_start": { + default: "game/model_avatar_start.fbx", + }, + "game/model_dog_catch": { + default: "game/model_dog_catch.fbx", + }, + "game/model_dog_movement": { + default: "game/model_dog_movement.fbx", + }, + "game/model_guard_catch": { + default: "game/model_guard_catch.fbx", + }, + "game/model_guard_movement": { + default: "game/model_guard_movement.fbx", + }, + "game/props": { + default: "game/props.fbx", + }, + "game/trains": { + default: "game/trains.fbx", + }, + }, + }, + idle: { + image: { + "idle/environment_tex": { + default: "idle/environment_tex.png", + low: "idle/environment_tex_mip.png", + }, + "idle/jake_tex": { + default: "idle/jake_tex.png", + low: "idle/jake_tex_mip.png", + }, + "idle/props_tex": { + default: "idle/props_tex.png", + low: "idle/props_tex_mip.png", + }, + "idle/train_start": { + default: "idle/train_start.png", + low: "idle/train_start_mip.png", + }, + "idle/trains_tex": { + default: "idle/trains_tex.png", + low: "idle/trains_tex_mip.png", + }, + }, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: { + "idle/environment_idle": { + default: "idle/environment_idle.fbx", + }, + "idle/model_avatar_idle_paint": { + default: "idle/model_avatar_idle_paint.fbx", + }, + "idle/props_start": { + default: "idle/props_start.fbx", + }, + "idle/trains_start": { + default: "idle/trains_start.fbx", + }, + }, + }, + placeholder: { + image: { + "placeholder/box_base": { + default: "placeholder/box_base.png", + }, + "placeholder/icon_00_brody": { + default: "placeholder/icon_00_brody.png", + }, + "placeholder/icon_01_tagbot": { + default: "placeholder/icon_01_tagbot.png", + }, + "placeholder/icon_02_tasha": { + default: "placeholder/icon_02_tasha.png", + }, + "placeholder/icon_03_ninja": { + default: "placeholder/icon_03_ninja.png", + }, + "placeholder/icon_04_lucy": { + default: "placeholder/icon_04_lucy.png", + }, + "placeholder/icon_05_king": { + default: "placeholder/icon_05_king.png", + }, + "placeholder/icon_06_frizzy": { + default: "placeholder/icon_06_frizzy.png", + }, + "placeholder/icon_07_yutani": { + default: "placeholder/icon_07_yutani.png", + }, + "placeholder/icon_08_spike": { + default: "placeholder/icon_08_spike.png", + }, + "placeholder/icon_09_fresh": { + default: "placeholder/icon_09_fresh.png", + }, + "placeholder/icon_10_jake": { + default: "placeholder/icon_10_jake.png", + }, + "placeholder/icon_11_tricky": { + default: "placeholder/icon_11_tricky.png", + }, + "placeholder/icon_friend": { + default: "placeholder/icon_friend.png", + }, + "placeholder/jake": { + default: "placeholder/jake.png", + }, + }, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: {}, + }, + preload: { + image: { + "preload/splash": { + tags: { + quant: !0, + }, + default: "preload/splash.png", + low: "preload/splash_mip.png", + }, + }, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: {}, + }, + ui: { + image: { + "ui/ui": { + tags: { + fix: !0, + tps: !0, + manifest: !0, + }, + default: "ui/ui.json", + }, + }, + fonts: {}, + audio: {}, + model: {}, + json: {}, + animate: {}, + misc: {}, + }, + }; + }, + 186: function (t, e) { + t.exports = + "\nattribute vec3 position;\nattribute vec3 normals;\nattribute vec2 uvs;\n\nuniform mat4 projection;\nuniform mat4 view;\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec4 vScreenPosition;\n\n#ifdef INSTANCING\n\tattribute vec3 i_mat1;\n\tattribute vec3 i_mat2;\n\tattribute vec3 i_mat3;\n\tattribute vec3 i_mat4;\n attribute vec3 i_norm1;\n\tattribute vec3 i_norm2;\n\tattribute vec3 i_norm3;\n#else\n uniform mat4 model;\n uniform mat3 normal;\n#endif\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\n\tvarying vec2 vUv;\n uniform vec4 uMapFrame;\n#endif\n\n#ifdef FOG\n uniform float uFogDistance;\n uniform float uFogDensity;\n varying float vFogFactor;\n uniform vec3 uFogColor;\n varying vec3 vFogColor;\n#endif\n\n\n#ifdef BEND\n\nuniform vec2 uBend;\n\nvec4 bend(vec4 pos) {\n float dx = uBend.x;\n float dy = uBend.y;\n float z_sqr = pos.w * pos.w;\n\tpos.x = pos.x + z_sqr * dx;\n\tpos.y = pos.y + z_sqr * dy;\n\treturn pos;\n}\n\n#endif\n\n#HOOK_VERTEX_START\n\nvoid main() {\n\t\n#if defined( MAP ) || defined( EMISSIVE_MAP )\n\tvUv = (uvs * uMapFrame.zw ) + uMapFrame.xy;\n#endif\n\n#ifdef INSTANCING\n mat4 model = mat4(vec4(i_mat1, 0.), \n vec4(i_mat2, 0.), \n vec4(i_mat3, 0.), \n vec4(i_mat4, 1.));\n\n mat3 normal = mat3(i_norm1, \n i_norm2, \n i_norm3);\n#endif\n\n vec3 transformed = position;\n\n vNormal = normalize( normal * normals);\n \n #HOOK_VERTEX_MAIN\n\n\tvec4 worldPosition = model * vec4(transformed, 1.0);\n\tvec4 worldViewPosition = projection * view * worldPosition;\n // clamp z position?\n // worldViewPosition.z = mod(worldViewPosition.z, 3000.0);\n vPosition = worldPosition.xyz;\n vScreenPosition = worldViewPosition;\n \n \n#ifdef BEND\n gl_Position = bend(worldViewPosition);\n#else\n gl_Position = worldViewPosition;\n#endif \n\n#ifdef FOG\n float depth = worldViewPosition.z - uFogDistance;\n vFogFactor = clamp(depth / uFogDistance, 0.0, 1.0);\n vFogColor = uFogColor;\n#endif\n\n\t#HOOK_VERTEX_END\n\n}"; + }, + 187: function (t, e) { + t.exports = + "uniform vec3 uGlobalAmbient;\nuniform float uOpacity;\nuniform vec3 uAmbiantLight;\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec4 vScreenPosition;\n\n#ifdef COLOR\nuniform vec3 uColor;\n#endif\n\n#ifdef OCCLUSION_MAP\nuniform sampler2D uOcclusionMap;\n#endif\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\nvarying vec2 vUv;\n#endif\n\n#ifdef MAP\nuniform sampler2D uMap;\n#endif\n\n#ifdef FOG\nvarying float vFogFactor;\nvarying vec3 vFogColor;\n#endif\n\n#HOOK_LIGHT_UNIFROMS\n\nvec3 gammaCorrectInput(vec3 color) {\n return pow(color, vec3(2.2));\n}\n\nfloat gammaCorrectInput(float color) {\n return pow(color, 2.2);\n}\n\nvec4 gammaCorrectInput(vec4 color) {\n return vec4(pow(color.rgb, vec3(2.2)), color.a);\n}\n\nvec3 gammaCorrectOutput(vec3 color) {\n color += vec3(0.0000001);\n return pow(color, vec3(0.45));\n}\n\nvec4 texture2DSRGB(sampler2D tex, vec2 uv) {\n vec4 rgba = texture2D(tex, uv);\n rgba.rgb = gammaCorrectInput(rgba.rgb);\n return rgba;\n}\n\n\n#HOOK_FRAGMENT_START\n\nvoid main() {\n vec4 finalColor = vec4(0.0, 0.0, 0.0, uOpacity);\n vec4 diffuseColor = vec4(1.0, 1.0, 1.0, uOpacity);\n\n#ifdef COLOR\n diffuseColor.rgb *= uColor;\n#endif\n\n#ifdef MAP\n // diffuseColor *= texture2DSRGB(uMap, vUv);\n diffuseColor *= texture2D(uMap, vUv);\n#endif\n\n #HOOK_LIGHT;\n\n finalColor.rgb = diffuseColor.rgb;\n finalColor *= diffuseColor.a;\n \n #HOOK_FRAGMENT_MAIN\n\n \n gl_FragColor = vec4(finalColor.rgb * finalColor.a, finalColor.a);\n \n #HOOK_FRAGMENT_END\n\n#ifdef FOG\n // FORCING FOG COLOR TO PREVENT A BUG WHERE SOME RED FOG OBJECT WHERE SHOWING UP \n gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.388, 0.698, 1.0), vFogFactor);\n#endif\n\n // gl_FragColor.rgb = gammaCorrectOutput(gl_FragColor.rgb);\n\n\n#ifdef RAILS\n if (vPosition.y < 1.2) return;\n\n float reflWidth = 1.5;\n float railDist = 7.0;\n float laneWidth = 20.0;\n vec3 reflColor = vec3(1.0, 1.0, 1.0);\n float reflFactor = abs(vScreenPosition.z - 100.0) * 0.02;\n if (reflFactor < 0.0) reflFactor = 0.0;\n if (reflFactor > 1.0) return;\n\n\n // Middle lane\n if (vPosition.x > -4.0 && vPosition.x < -2.5) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 && vPosition.x < 4.0) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n\n // Left lane\n if (vPosition.x > -4.0 - laneWidth && vPosition.x < -2.5 - laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 - laneWidth && vPosition.x < 4.0 - laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n\n // Right lane\n if (vPosition.x > -4.0 + laneWidth && vPosition.x < -2.5 + laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 + laneWidth && vPosition.x < 4.0 + laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n#endif\n}\n"; + }, + 188: function (t, e) { + t.exports = + "attribute vec3 position;\nattribute vec3 normals;\nattribute vec2 uvs;\n\nuniform mat4 projection;\nuniform mat4 view;\nuniform mat4 model;\nuniform mat3 normal;\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\nvarying vec2 vUv;\nuniform vec4 uMapFrame;\n#endif\n\n\n#HOOK_VERTEX_START\n\nvoid main() {\n\t\n#if defined( MAP ) || defined( EMISSIVE_MAP )\n\t//vUv = uvs;\n\tvUv = (uvs * uMapFrame.zw ) + uMapFrame.xy;\n#endif\n \n #HOOK_VERTEX_MAIN\n\t\n vec4 worldPosition = model * vec4(position, 1.0);\n\tvec4 worldViewPosition = projection * view * worldPosition;\n\tgl_Position = worldViewPosition;\n\t\n #HOOK_VERTEX_END\n}"; + }, + 189: function (t, e) { + t.exports = + "#ifdef COLOR\nuniform vec3 uColor;\n#endif\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\nvarying vec2 vUv;\n#endif\n\n#ifdef MAP\nuniform sampler2D uMap;\n#endif\n\nvoid main() {\n gl_FragColor = texture2D(uMap, vUv);\n gl_FragColor.rgb *= uColor.rgb;\n}\n"; + }, + 190: function (t, e) { + t.exports = + "\nattribute vec3 position;\nattribute vec3 normals;\nattribute vec2 uvs;\n\nuniform mat4 projection;\nuniform mat4 view;\n\n\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec4 vScreenPosition;\n\nuniform float uTime;\nvarying float vTime;\n\n\n#ifdef INSTANCING\n\tattribute vec3 i_mat1;\n\tattribute vec3 i_mat2;\n\tattribute vec3 i_mat3;\n\tattribute vec3 i_mat4;\n\n attribute vec3 i_norm1;\n\tattribute vec3 i_norm2;\n\tattribute vec3 i_norm3;\n\n#else\n uniform mat4 model;\n uniform mat3 normal;\n#endif\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\n\tvarying vec2 vUv;\n uniform vec4 uMapFrame;\n#endif\n\n#define FOG\n\n#ifdef FOG\n uniform float uFogDistance;\n uniform float uFogDensity;\n varying float vFogFactor;\n uniform vec3 uFogColor;\n varying vec3 vFogColor;\n#endif\n\n\n\n#HOOK_VERTEX_START\n\nuniform vec2 uBend;\nvec4 bend(vec4 pos) {\n // hardcoding values just for testing\n // float dx = -0.001;\n // float dy = -0.0003;\n float dx = uBend.x;\n float dy = uBend.y;\n float z_sqr = pos.w * pos.w;\n\tpos.x = pos.x + z_sqr * dx;\n\tpos.y = pos.y + z_sqr * dy;\n\treturn pos;\n}\n\nvec4 wave(vec4 pos, float t) {\n pos.y += sin(pos.z*0.05) * cos(pos.x*0.05) * 5.0 + 5.0;\n\treturn pos;\n}\n\nvoid main() {\n #if defined( MAP ) || defined( EMISSIVE_MAP )\n\t//vUv = uvs;\n\tvUv = (uvs * uMapFrame.zw ) + uMapFrame.xy;\n \n\t#endif\n\n #ifdef INSTANCING\n\n mat4 model = mat4(vec4(i_mat1, 0.), \n vec4(i_mat2, 0.), \n vec4(i_mat3, 0.), \n vec4(i_mat4, 1.));\n\n mat3 normal = mat3(i_norm1, \n i_norm2, \n i_norm3);\n\n #endif\n\n vec3 transformed = position;\n\n vNormal = normalize( normal * normals);\n \n #HOOK_VERTEX_MAIN\n\n\tvec4 worldPosition = model * vec4(transformed, 1.0);\n\tvec4 worldViewPosition = projection * view * worldPosition;\n vPosition = worldPosition.xyz;\n vTime = worldPosition.z * 0.1;\n \n // clamp z position?\n // worldViewPosition.z = mod(worldViewPosition.z, 3000.0);\n\n vScreenPosition = worldViewPosition;\n\n\t// gl_Position = worldViewPosition;\n worldViewPosition = wave(worldViewPosition, uTime);\n gl_Position = bend(worldViewPosition);\n\n\t#ifdef FOG\n // float depth = worldViewPosition.z - uFogDistance;\n // vFogFactor = exp(-depth * uFogDensity);\n // vFogFactor = clamp(vFogFactor, 0.0, 1.0);\n // vFogColor = uFogColor;\n float depth = worldViewPosition.z - uFogDistance;\n vFogFactor = clamp(depth / uFogDistance, 0.0, 1.0);\n vFogColor = uFogColor;\n #endif\n\n\t#HOOK_VERTEX_END\n\n}"; + }, + 191: function (t, e) { + t.exports = + "\n\nuniform vec3 uGlobalAmbient;\n\nuniform float uOpacity;\n\nuniform vec3 uAmbiantLight;\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec4 vScreenPosition;\n\n#ifdef COLOR\nuniform vec3 uColor;\n#endif\n\n#ifdef OCCLUSION_MAP\nuniform sampler2D uOcclusionMap;\n#endif\n\n#if defined( MAP ) || defined( EMISSIVE_MAP ) || defined( OCCLUSION_MAP )\nvarying vec2 vUv;\n#endif\n\n#ifdef MAP\nuniform sampler2D uMap;\n#endif\n\n// #define FOG\n\n#ifdef FOG\n varying float vFogFactor;\n varying vec3 vFogColor;\n#endif\n\nvarying float vTime;\n\n#HOOK_LIGHT_UNIFROMS\n\n/** \n gamma stuff!\n*/\nvec3 gammaCorrectInput(vec3 color) {\n return pow(color, vec3(2.2));\n}\n\nfloat gammaCorrectInput(float color) {\n return pow(color, 2.2);\n}\n\nvec4 gammaCorrectInput(vec4 color) {\n return vec4(pow(color.rgb, vec3(2.2)), color.a);\n}\n\nvec3 gammaCorrectOutput(vec3 color) {\n color += vec3(0.0000001);\n return pow(color, vec3(0.45));\n}\n\nvec4 texture2DSRGB(sampler2D tex, vec2 uv) {\n vec4 rgba = texture2D(tex, uv);\n rgba.rgb = gammaCorrectInput(rgba.rgb);\n return rgba;\n}\n\n\nfloat diffuse(vec3 N, vec3 L) {\n\treturn max(dot(N, L), 0.0);\n}\n\nvec3 diffuseLighting(vec3 N, vec3 L, vec3 C) {\n\treturn diffuse(N, L) * C;\n}\n\n/// speculer lights...\nuniform vec3 uSpecular;\n\n#ifdef SHININESS_FLOAT\n uniform float uShininess;\n#endif\n\n#ifdef SHININESS_MAP\n uniform sampler2D uShininessMap;\n#endif\n\n// we write the specular to here..\nfloat dShininess;\n\n// glosyness..\nvoid getShininess() {\n dShininess = 1.0;\n #ifdef SHININESS_FLOAT\n dShininess *= uShininess;\n #endif\n #ifdef SHININESS_MAP\n dShininess *= texture2D(uShininessMap, vUv).r;\n #endif\n \n //IOS hack.. \n dShininess += 0.0000001;\n}\n\n/// emissive lights...\n#ifdef EMISSIVE_FLOAT\n uniform float uEmissive;\n#endif\n\n#ifdef EMISSIVE_COLOR\n uniform vec3 uEmissiveColor;\n#endif\n\n#ifdef EMISSIVE_MAP\n uniform sampler2D uEmissiveMap;\n#endif\n\n// we write the specular to here..\nvec3 dEmissive;\n\n// glosyness..\nvec3 getEmission() {\n vec3 emission = vec3(1.);\n\n #ifdef EMISSIVE_FLOAT\n emission *= uEmissive;\n #endif\n\n #ifdef EMISSIVE_COLOR\n emission *= uEmissiveColor;\n #endif\n \n #ifdef EMISSIVE_MAP\n emission *= texture2D(uEmissiveMap, vUv).rgb;\n #endif\n \n return emission;\n}\n\nvec3 specularLighting(vec3 N, vec3 L, vec3 V) \n{\n vec3 R = reflect(-L, N); // Reflected light vector\n\n float specular = pow(max(dot(R, -V), 0.0), dShininess + 0.0001);\n \n return specular * uSpecular;\n}\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n\n\n#HOOK_FRAGMENT_START\n\nvoid main() {\n vec4 finalColor = vec4(0.0,0.0,0.0, uOpacity);\n vec4 diffuseColor = vec4(1., 1., 1., uOpacity);\n\n\n #ifdef COLOR\n diffuseColor.rgb *= uColor;\n #endif\n\n #ifdef MAP\n diffuseColor *= texture2DSRGB(uMap, vec2(vUv.x, vUv.y + sin(vScreenPosition.z* 0.0005) * 0.5));\n \n #endif\n\n #ifdef OCCLUSION_MAP\n diffuseColor *= texture2DSRGB(uOcclusionMap, vUv);\n #endif\n\n vec3 lighting = vec3(0.0);\n vec3 specular = vec3(0.0);\n\n lighting += uGlobalAmbient;\n\n vec3 N = normalize(vNormal);\n vec3 V = normalize(-vPosition);\n \n // some temp variables \n vec3 L;\n vec3 lightDirection;\n float attenuation;\n float dist;\n\n getShininess();\n // do lighting..\n #HOOK_LIGHT;\n\n finalColor.rgb = diffuseColor.rgb * lighting;// + uEmissive;\n\n /// finalColor.rgb += specular;//mix(finalColor.rgb, specularLighting, )\n finalColor *= diffuseColor.a;\n \n #HOOK_FRAGMENT_MAIN\n\n \n #ifdef EMISSIVE\n\n finalColor.rgb += getEmission();\n\n #endif\n\n gl_FragColor = vec4(finalColor.rgb * finalColor.a, finalColor.a);\n \n #HOOK_FRAGMENT_END\n\n #ifdef FOG\n // gl_FragColor.rgb = mix(vec3(0.388, 0.698, 1.0), gl_FragColor.rgb, vFogFactor);\n // gl_FragColor.rgb = mix(gl_FragColor.rgb, vFogColor, vFogFactor);\n // FORCING FOG COLOR TO PREVENT A BUG WHERE SOME RED FOG OBJECT WHERE SHOWING UP \n gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.388, 0.698, 1.0), vFogFactor);\n #endif\n\n gl_FragColor.rgb = gammaCorrectOutput(gl_FragColor.rgb);\n\n\n #ifdef RAILS\n if (vPosition.y < 1.2) return;\n\n float reflWidth = 1.5;\n float railDist = 7.0;\n float laneWidth = 20.0;\n vec3 reflColor = vec3(1.0, 1.0, 1.0);\n float reflFactor = abs(vScreenPosition.z - 100.0) * 0.02;\n if (reflFactor < 0.0) reflFactor = 0.0;\n if (reflFactor > 1.0) return;\n\n\n // Middle lane\n if (vPosition.x > -4.0 && vPosition.x < -2.5) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 && vPosition.x < 4.0) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n\n // Left lane\n if (vPosition.x > -4.0 - laneWidth && vPosition.x < -2.5 - laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 - laneWidth && vPosition.x < 4.0 - laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n\n // Right lane\n if (vPosition.x > -4.0 + laneWidth && vPosition.x < -2.5 + laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n } else if (vPosition.x > 2.5 + laneWidth && vPosition.x < 4.0 + laneWidth) {\n gl_FragColor.rgb = mix(reflColor, gl_FragColor.rgb, reflFactor);\n }\n #endif\n}\n"; + }, + 199: function (t, e, i) { + i(200), (t.exports = i(487)); + }, + 430: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + default_train_tops_2: i(431), + "route_section_default_s-s": i(432), + "default_b-s-b": i(433), + "default_s-b-s-b": i(434), + "default_s-s-s-s": i(435), + default_tunnel_notrain: i(436), + default_choice: i(437), + default_train_tops_1: i(438), + }); + }, + 431: function (t) { + t.exports = { + name: "route_section_default_train_tops_2", + start: [], + mid: ["routeChunk_default_train_tops_2"], + end: [], + }; + }, + 432: function (t) { + t.exports = { + name: "route_section_default_s-s", + start: [], + mid: ["routeChunk_default_s-s"], + end: [], + }; + }, + 433: function (t) { + t.exports = { + name: "route_section_default_b-s-b", + start: [], + mid: ["routeChunk_default_b-s-b"], + end: [], + }; + }, + 434: function (t) { + t.exports = { + name: "route_section_default_s-b-s-b", + start: [], + mid: ["routeChunk_default_s-b-s-b"], + end: [], + }; + }, + 435: function (t) { + t.exports = { + name: "route_section_default_s-s-s-s", + start: [], + mid: ["routeChunk_default_s-s-s-s"], + end: [], + }; + }, + 436: function (t) { + t.exports = { + name: "route_section_default_tunnel_notrain", + start: [], + mid: ["routeChunk_default_tunnel_notrain"], + end: [], + }; + }, + 437: function (t) { + t.exports = { + name: "route_section_default_choice", + start: [], + mid: ["routeChunk_default_choice"], + end: [], + }; + }, + 438: function (t) { + t.exports = { + name: "route_section_default_train_tops_1", + start: [], + mid: ["routeChunk_default_train_tops_1"], + end: [], + }; + }, + 439: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + default_short_train_tops_moving_multiple: i(440), + default_short_2_tracks: i(441), + default_short_train_tops_moving: i(442), + default_ramp_2: i(443), + default_short_train_tops_moving_combined: i(444), + default_short_1_track: i(445), + }); + }, + 440: function (t) { + t.exports = { + name: "route_section_default_short_train_tops_moving_multiple", + start: [], + mid: ["routeChunk_short_train_tops_moving_multiple"], + end: [], + }; + }, + 441: function (t) { + t.exports = { + name: "route_section_default_short_2_tracks", + start: ["routeChunk_short_2_tracks_start"], + mid: ["routeChunk_short_2_tracks_mid_var_1", "routeChunk_short_2_tracks_mid_var_2"], + end: ["routeChunk_short_2_tracks_end"], + }; + }, + 442: function (t) { + t.exports = { + name: "route_section_default_short_train_tops_moving", + start: [], + mid: ["routeChunk_short_train_tops_moving"], + end: [], + }; + }, + 443: function (t) { + t.exports = { + name: "route_section_default_ramp_2", + start: [], + mid: ["routeChunk_ramp_2"], + end: [], + }; + }, + 444: function (t) { + t.exports = { + name: "route_section_default_short_train_tops_moving_combined", + start: [], + mid: ["routeChunk_short_train_tops_moving_combined"], + end: [], + }; + }, + 445: function (t) { + t.exports = { + name: "route_section_default_short_1_track", + start: [], + mid: ["routeChunk_short_1_track"], + end: [], + }; + }, + 446: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + default_train_tops_moving: i(447), + default_epic: i(448), + default_train_tops_moving_multiple: i(449), + default_2_tracks: i(450), + default_ramp_1: i(451), + default_tunnel: i(452), + default_train_tops_moving_combined: i(453), + default_1_track: i(454), + }); + }, + 447: function (t) { + t.exports = { + name: "route_section_default_train_tops_moving", + start: [], + mid: ["routeChunk_default_train_tops_moving"], + end: [], + }; + }, + 448: function (t) { + t.exports = { + name: "route_section_default_epic", + start: [], + mid: ["routeChunk_default_epic"], + end: [], + }; + }, + 449: function (t) { + t.exports = { + name: "route_section_default_train_tops_moving_multiple", + start: [], + mid: ["routeChunk_default_train_tops_moving_multiple"], + end: [], + }; + }, + 450: function (t) { + t.exports = { + name: "route_section_default_2_tracks", + start: ["routeChunk_default_2_tracks_start"], + mid: ["routeChunk_default_2_tracks_mid_var_1", "routeChunk_default_2_tracks_mid_var_2"], + end: ["routeChunk_default_2_tracks_end"], + }; + }, + 451: function (t) { + t.exports = { + name: "route_section_default_ramp_1", + start: [], + mid: ["routeChunk_default_ramp_1"], + end: [], + }; + }, + 452: function (t) { + t.exports = { + name: "route_section_default_tunnel", + start: [], + mid: ["routeChunk_default_tunnel"], + end: [], + }; + }, + 453: function (t) { + t.exports = { + name: "route_section_default_train_tops_moving_combined", + start: [], + mid: ["routeChunk_default_train_tops_moving_combined"], + end: [], + }; + }, + 454: function (t) { + t.exports = { + name: "route_section_default_1_track", + start: [], + mid: ["routeChunk_short_1_track"], + end: [], + ignored_start: ["routeChunk_default_1_track_start"], + ignored_mid: ["routeChunk_default_1_track_mid_var_1", "routeChunk_default_1_track_mid_var_2"], + ignored_end: ["routeChunk_default_1_track_end"], + }; + }, + 455: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + default_4_units_3_tracks_choice: i(456), + "default_4_units_3_tracks_s-b-s-b": i(457), + "default_4_units_3_tracks_b-s-b": i(458), + default_epic_various: i(459), + "default_4_units_3_tracks_s-s": i(460), + "default_4_units_3_tracks_s-s-s-s": i(461), + }); + }, + 456: function (t) { + t.exports = { + name: "route_section_default_4_units_3_tracks_choice", + start: [], + mid: ["routeChunk_4_units_3_tracks_choice"], + end: [], + }; + }, + 457: function (t) { + t.exports = { + name: "route_section_default_4_units_3_tracks_s-b-s-b", + start: [], + mid: ["routeChunk_4_units_3_tracks_s-b-s-b"], + end: [], + }; + }, + 458: function (t) { + t.exports = { + name: "route_section_default_4_units_3_tracks_b-s-b", + start: [], + mid: ["routeChunk_4_units_3_tracks_b-s-b"], + end: [], + }; + }, + 459: function (t) { + t.exports = { + name: "route_section_default_epic_various", + start: [], + mid: ["routeChunk_epic_various"], + end: [], + }; + }, + 460: function (t) { + t.exports = { + name: "route_section_default_4_units_3_tracks_s-s", + start: [], + mid: ["routeChunk_4_units_3_tracks_s-s"], + end: [], + }; + }, + 461: function (t) { + t.exports = { + name: "route_section_default_4_units_3_tracks_s-s-s-s", + start: [], + mid: ["routeChunk_4_units_3_tracks_s-s-s-s"], + end: [], + }; + }, + 462: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + default_start: i(463), + default_fallback: i(464), + default_start_short: i(465), + default_jetpack_landing: i(466), + default_no_gameplay: i(467), + }); + }, + 463: function (t) { + t.exports = { + name: "routeSection_default_start", + start: [], + mid: ["routeChunk_default_start1", "routeChunk_default_start2"], + end: [], + }; + }, + 464: function (t) { + t.exports = { + name: "routeSection_default_fallback", + start: [], + mid: ["routeChunk_default_fallback"], + end: [], + }; + }, + 465: function (t) { + t.exports = { + name: "routeSection_default_start_short", + start: [], + mid: ["routeChunk_default_fallback"], + end: [], + }; + }, + 466: function (t) { + t.exports = { + name: "routeSection_default_jetpack_landing", + start: [], + mid: ["routeChunk_default_jetpack_landing"], + end: [], + }; + }, + 467: function (t) { + t.exports = { + name: "routeSection_default_no_gameplay", + start: [], + mid: ["routeChunk_default_no_gameplay_1", "routeChunk_default_no_gameplay_2", "routeChunk_default_no_gameplay_epic"], + end: [], + }; + }, + 468: function (t, e, i) { + "use strict"; + i.r(e), + (e["default"] = { + tutorial: i(469), + default_bonus_short: i(470), + default_bonus_long: i(471), + default_pogostick_start: i(472), + }); + }, + 469: function (t) { + t.exports = { + name: "routeSection_tutorial", + start: [], + mid: ["routeChunk_tutorial"], + end: [], + }; + }, + 470: function (t) { + t.exports = { + name: "routeSection_default_bonus_short", + start: [], + mid: ["routeChunk_bonus_short"], + end: [], + }; + }, + 471: function (t) { + t.exports = { + name: "routeSection_default_bonus_long", + start: [], + mid: ["routeChunk_bonus_long"], + end: [], + }; + }, + 472: function (t) { + t.exports = { + name: "routeSection_default_pogostick_start", + start: [], + mid: ["routeChunk_default_pogostick_start"], + end: [], + }; + }, + 475: function (t) { + t.exports = { + file: "model_avatar_idle_paint.fbx", + texture: "jake_tex", + fps: 24, + clips: { + idle: [1, 220], + }, + }; + }, + 476: function (t) { + t.exports = { + file: "model_avatar_powerup_jet_pack.fbx", + texture: "jake_tex", + fps: 25, + clips: { + Jetpack_forward: [1, 12], + Jetpack_BarrelRoll_right: [20, 30], + Jetpack_BarrelRoll_left: [40, 50], + Jetpack_changeLane_left: [60, 70], + Jetpack_changeLane_right: [80, 90], + Jetpack_forward_2: [100, 123], + Double_Headstart: [130, 172], + Triple_Headstart: [202, 242], + }, + }; + }, + 477: function (t) { + t.exports = { + file: "model_avatar_movement.fbx", + texture: "jake_tex", + fps: 24, + clips: { + run: [-53, -6], + dodgeLeft: [200, 212], + dodgeRight: [230, 242], + jump: [87, 103], + hangtime: [106, 118], + landing: [120, 124], + roll: [151, 164], + death_bounce: [400, 416], + hold_magnet: [280, 281], + h_run: [514, 538, 1], + h_jump: [551, 553], + h_hangtime: [556, 568], + h_landing: [570, 578], + h_roll: [581, 594], + introRun: [-50, -21], + stumbleCornerLeft: [311, 330], + stumbleCornerRight: [291, 311], + stumble: [335, 355], + stumbleSideRight: [362, 372], + stumbleSideLeft: [382, 392], + death_upper: [420, 428], + death_lower: [440, 452], + death_movingTrain: [460, 495], + h_left: [610, 623], + h_right: [640, 653], + superRun: [740, 760], + h_Grind1: [670, 684], + h_Grind2: [700, 720], + h_skate_on: [770, 781], + h_Grind1_land: [661, 670], + h_Grind2_land: [691, 700], + h_jump2_kickflip_flip: [800, 820], + h_jump3_bs360grab: [830, 850], + h_jump4_360_flip: [866, 889], + h_Grind3: [923, 943], + h_Grind3_land: [904, 923], + run2: [960, 970], + run3: [1e3, 1010], + h_jump5_Impossible_flip: [1400, 1423], + h_jump6_nollie: [1430, 1457], + h_jump7_heelflip_flip: [1500, 1524], + h_jump8_pop_shuvit_flip: [1560, 1583], + h_jump9_fs360grab: [1600, 1628], + h_jump10_heel360_flip: [1650, 1674], + h_jump11_fs_salto: [1690, 1718], + jump_salto: [1300, 1324], + run4_long: [1070, 1180], + jump2: [60, 78], + hangtime2: [65, 78], + landing2: [78, 84], + jump3: [1720, 1738], + hangtime3: [1725, 1738], + landing3: [1739, 1744], + stumble_low: [1200, 1215], + IconJump: [63, 63.1], + IconRoll: [154, 154.1], + _run_06_old: [1, 11], + DoubleJump_Jump: [1750, 1754], + DoubleJump_Hangtime_01_star: [1756, 1778], + DoubleJump_Hangtime_02_BackFlip: [1780, 1802], + DoubleJump_Hangtime_03_360: [1805, 1831], + run_HighScore_main: [1950, 1989], + run_HighScore_hatadjust: [2005, 2044], + run_HighScore_xianHat: [2060, 2099], + run_HighScore_Yutani: [2110, 2149], + run_HighScore_Ninja: [2160, 2199], + run_HighScore_Tagbot: [2210, 2249], + run_HighScore_VeniceMain: [2260, 2299], + run_HighScore_HolidayMainandOutfit1: [2310, 2349], + run_HighScore_MexicoOutfit1: [2360, 2399], + }, + }; + }, + 478: function (t) { + t.exports = { + file: "model_avatar_catch.fbx", + texture: "jake_tex", + fps: 25, + offset: 0, + clips: { + Avatar_Catch_Shoulder: [300, 400], + Avatar_Catch_Right_Pickup: [150, 248], + Avatar_Catch_Left_Pickup: [2, 100], + }, + }; + }, + 479: function (t) { + t.exports = { + file: "model_avatar_powerup_pogostick.fbx", + texture: "jake_tex", + fps: 25, + offset: 0, + clips: { + pogostick_SpinMove: [1, 50], + pogostick_kicking: [60, 76], + pogostick_Hangtime_kick: [82, 102], + pogostick_ChangeLane_right: [120, 126], + pogostick_ChangeLane_left: [132, 138], + pogostick_Hangtime_flying: [150, 183], + pogostick_Hangtime_front_flip1: [200, 233], + }, + }; + }, + 480: function (t) { + t.exports = { + file: "model_avatar_start.fbx", + texture: "jake_tex", + fps: 24, + clips: { + start_run: [7, 53], + }, + }; + }, + 481: function (t) { + t.exports = { + file: "model_guard_movement.fbx", + texture: "enemies", + fps: 25, + offset: 51, + clips: { + Guard_run: [1, 13], + Guard_grap_after: [24, 48], + Guard_jump: [52, 75], + Guard_hangtime: [59, 65], + Guard_landing: [71, 77], + Guard_dodgeRight: [80, 93], + Guard_dodgeLeft: [100, 113], + Guard_whistle: [125, 149], + Guard_death_movingTrain: [173, 210], + Guard_playIntro: [-50, -21], + Guard_roll: [220, 228], + }, + }; + }, + 482: function (t) { + t.exports = { + file: "model_guard_catch.fbx", + texture: "enemies", + fps: 25, + offset: 0, + clips: { + Guard_Catch_Shoulder: [300, 400], + Guard_Catch_Right_Pickup: [150, 248], + Guard_Catch_Left_Pickup: [2, 100], + }, + }; + }, + 483: function (t) { + t.exports = { + file: "model_dog_movement.fbx", + texture: "enemies", + fps: 25, + offset: 50, + clips: { + Dog_run: [1, 14], + "Dog_Fast Run": [20, 40], + "Dog_Run offset": [-6, 6], + Dog_jump: [52, 75], + Dog_hangtime: [37, 46], + Dog_landing: [46, 51], + Dog_dodgeRight: [80, 93], + Dog_dodgeLeft: [100, 113], + Dog_whistle: [125, 149], + Dog_death_movingTrain: [173, 210], + Dog_playIntro: [-50, -21], + Dog_Bite_After: [220, 228], + }, + }; + }, + 484: function (t) { + t.exports = { + file: "model_dog_catch.fbx", + texture: "enemies", + fps: 25, + offset: 0, + clips: { + Dog_Catch_Shoulder: [300, 400], + Dog_Catch_Right: [150, 248], + Dog_Catch_Left: [2, 100], + }, + }; + }, + 487: function (t, e, i) { + "use strict"; + i.r(e); + var c = i(0), + a = i(133), + r = i(21), + h = i(184), + d = i(14), + n = i(12), + l = i.n(n), + o = i(185), + s = i.n(o), + u = i(126), + p = i.n(u), + m = i(13), + f = i.n(m), + g = (function () { + function Platform() {} + return ( + (Platform.getUrlParams = function () { + var t = location.search, + e = {}, + i = t.slice(t.indexOf("?") + 1).split("&"), + n = Array.isArray(i), + o = 0; + for (i = n ? i : i[Symbol.iterator](); ; ) { + var s; + if (n) { + if (o >= i.length) break; + s = i[o++]; + } else { + if ((o = i.next()).done) break; + s = o.value; + } + var a = s.split("="), + r = a[0], + h = a[1]; + void 0 !== h && (("true" !== h && "false" !== h) || (h = "true" === h), (e[r] = "string" == typeof h && h.match(/^[-.0-9]+$/) ? parseFloat(h) : h)); + } + return e; + }), + (Platform.getUrlParam = function (t, e) { + var i = this.getUrlParams(); + return void 0 !== i[t] ? i[t] : e; + }), + Platform + ); + })(), + B = new ((function () { + function Config() { + (this.fps = 60), + (this.frameSkip = 0), + (this.ticker = !1), + (this.smoothDelta = !0), + (this.deltaCap = 5), + (this.frameByFrame = 0), + (this.culling = !0), + (this.fixedPhysicsSteps = 0), + (this.vertp = ""), + (this.fragp = ""), + (this.maxViewportScale = 1.75), + (this.debug = !1), + (this.monitor = !1), + (this.blocks = !1), + (this.models = !0), + (this.routeSection = ""), + (this.timeScale = 1), + (this.forceTube = !1), + (this.optm = !1), + (this.god = !1), + (this.freejump = !1), + (this.shortcuts = !1), + (this.laneWidth = 20), + (this.blockSize = 90), + (this.visibleMaxDistance = 1e3), + (this.visibleMinDistance = -500), + (this.fog = !0), + (this.bend = !0), + (this.bendX = -65e-5), + (this.bendY = -3e-4), + (this.fillers = !0), + (this.ground = !0), + (this.speed = 0), + (this.speedProgr = 0.25), + (this.speedProgrInterval = 500), + (this.maxSpeed = 5), + (this.gravity = 0.055), + (this.cameraFov = 68), + (this.cameraPosX = 0), + (this.cameraPosY = 33.8), + (this.cameraPosZ = 33), + (this.cameraModX = 0.75), + (this.cameraRotX = -0.375), + (this.cameraRotY = 0), + (this.cameraRotZ = 0), + (this.mobile = !1), + (this.env = "191_new_york"), + (this.volume = 0.5), + (this.theme = !0), + (this.fastplay = !1), + (this.autoload = !1), + (this.workers = 1), + (this.section = "title"), + (this.tutorial = !1), + (this.intro = !0), + (this.loadAll = !0), + this.override(window.GAME_CONFIG), + this.override(g.getUrlParams()); + } + return ( + (Config.prototype.override = function (t) { + if (t) for (var e in this) void 0 !== t[e] && ((this[e] = t[e]), "debug" === e && ((this.monitor = this.debug), (this.shortcuts = this.debug))); + }), + Config + ); + })())(), + y = i(3), + v = i.n(y), + _ = i(92), + b = i.n(_), + w = i(93), + k = i.n(w), + x = i(45), + C = i.n(x), + S = i(37), + z = i.n(S), + T = i(186), + P = i.n(T), + M = i(187), + E = i.n(M); + var O = (function (n) { + var t, e; + + function PhongBendMaterial(t, e) { + void 0 === t && (t = {}), void 0 === e && (e = {}); + var i = [e.fog ? "#define FOG" : "", e.rails ? "#define RAILS" : "", "#define BEND"]; + return (t.uniforms = PhongBendMaterial.unformGroup()), n.call(this, t, P.a, E.a, i, e.rails ? "phong-rails" : "phong") || this; + } + return ( + (e = n), + ((t = PhongBendMaterial).prototype = Object.create(e.prototype)), + ((t.prototype.constructor = t).__proto__ = e), + (PhongBendMaterial.unformGroup = function () { + return ( + this._uniformGroup || + (this._uniformGroup = new c.UniformGroup( + { + uTime: 0, + uBend: new Float32Array([0, 0]), + uFogDensity: 0.008, + uFogDistance: 450, + uFogColor: new Float32Array([0.388, 0.698, 1]), + }, + !1 + )), + this._uniformGroup.uniforms + ); + }), + PhongBendMaterial + ); + })(z.a); + var R = (function (i) { + var t, e; + + function PhongBendRailsMaterial(t, e) { + return ( + void 0 === t && (t = {}), + void 0 === e && (e = {}), + Object.assign(e, { + rails: !0, + }), + i.call(this, t, e) || this + ); + } + return (e = i), ((t = PhongBendRailsMaterial).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), PhongBendRailsMaterial; + })(O); + var I = (function (r) { + var t, e; + + function RampGeometry(t, e, i) { + var n; + void 0 === t && (t = 1), void 0 === e && (e = 1), void 0 === i && (i = 1), (n = r.call(this) || this); + var o = new Float32Array([ + -0.5, + -0.5, + 0.5, + 0.5, + -0.5, + 0.5, + 0.5, + 0.5, + 0.5, + -0.5, + 0.5, + 0.5, + -0.5, + -0.5, + -0.5, + -0.5, + 0.5, + -0.5, + 0.5, + 0.5, + -0.5, + 0.5, + -0.5, + -0.5, + -0.5, + 0.5, + -0.5, + -0.5, + -0.5, + 0.5, + 0.5, + -0.5, + 0.5, + 0.5, + 0.5, + -0.5, + -0.5, + -0.5, + -0.5, + 0.5, + -0.5, + -0.5, + 0.5, + -0.5, + 0.5, + -0.5, + -0.5, + 0.5, + 0.5, + -0.5, + -0.5, + 0.5, + 0.5, + -0.5, + 0.5, + 0.5, + 0.5, + 0.5, + -0.5, + 0.5, + -0.5, + -0.5, + -0.5, + -0.5, + -0.5, + 0.5, + -0.5, + 0.5, + 0.5, + -0.5, + 0.5, + -0.5, + ]), + s = new Float32Array([ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + ]), + a = new Uint16Array([0, 0, 0, 0, 0, 0, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 19, 0, 0, 0, 20, 21, 23, 0, 0, 0]); + return n.addAttribute("position", o, 3), n.addAttribute("normals", s, 3), n.addIndex(a), n; + } + return (e = r), ((t = RampGeometry).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), RampGeometry; + })(c.Geometry), + L = (function () { + function Random() {} + return ( + (Random.color = function () { + return Math.floor(16777215 * Math.random()); + }), + (Random.range = function (t, e, i) { + void 0 === i && (i = !1); + var n = t + (e - t) * Math.random(); + return i ? Math.floor(n) : n; + }), + (Random.pick = function () { + for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; + return e[Math.floor(Math.random() * e.length)]; + }), + (Random.item = function (t) { + if (Array.isArray(t)) return t[Math.floor(Math.random() * t.length)]; + var e = Object.keys(t); + return t[e[Math.floor(Math.random() * e.length)]]; + }), + Random + ); + })(), + j = i(188), + F = i.n(j), + A = i(189), + D = i.n(A); + var G = (function (i) { + var t, e; + + function ParticleMaterial(t, e) { + void 0 === t && (t = {}), void 0 === e && (e = {}); + return (t.uniforms = {}), i.call(this, t, F.a, D.a, [], "particle") || this; + } + return (e = i), ((t = ParticleMaterial).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), ParticleMaterial; + })(z.a), + N = O, + X = null, + Y = { + "environment.fbx": "environment_tex", + "environment_start.fbx": "environment_tex", + "environment_idle.fbx": "environment_tex", + "trains.fbx": "trains_tex", + "trains_start.fbx": "trains_tex", + "model_dog.fbx": "enemies", + "model_guard.fbx": "enemies", + "board_new_york.fbx": "board_new_york_tex", + "avatar_jake.fbx": "jake_tex", + "model_avatar-movement.fbx": "jake_tex", + "props.fbx": "props_tex", + "props_start.fbx": "props_tex", + }, + H = (function () { + function Model() {} + return ( + (Model.box = function (t, e, i) { + void 0 === e && (e = L.color()), void 0 === i && (i = 1); + var n = new c.State(); + (n.blend = i < 1), (n.depthTest = !0); + var o = new b.a(t.width, t.height, t.depth, 1, 1, 64), + s = new N( + { + color: e, + opacity: i, + }, + { + fog: B.fog, + } + ); + return new v.a({ + geometry: o, + material: s, + state: n, + }); + }), + (Model.ramp = function (t, e, i) { + void 0 === e && (e = L.color()), void 0 === i && (i = 1); + var n = new c.State(); + (n.blend = i < 1), (n.depthTest = !0); + var o = new I(1, 1, 1), + s = new N( + { + color: e, + opacity: i, + }, + { + fog: B.fog, + } + ); + return new v.a({ + geometry: o, + material: s, + state: n, + }); + }), + (Model.plane = function (t, e, i, n, o) { + void 0 === i && (i = 1); + var s = new c.State(); + (s.blend = i < 1), (s.depthTest = !0), o && (s.blendMode = o); + var a = this.getTexture(n); + if (!a) throw new Error("Map not found for " + mapFile); + var r = new k.a(t, e, 4, 4), + h = new N( + { + map: a, + opacity: i, + }, + { + fog: B.fog, + } + ); + return new v.a({ + geometry: r, + material: h, + state: s, + }); + }), + (Model.particle = function (t, e, i, n, o) { + void 0 === i && (i = 1); + var s = new c.State(); + (s.blend = !0), (s.depthTest = !1), o && (s.blendMode = o); + var a = this.getTexture(n); + if (!a) throw new Error("Map not found for " + mapFile); + var r = new k.a(t, e, 4, 4), + h = new G({ + map: a, + opacity: i, + }); + return new v.a({ + geometry: r, + material: h, + state: s, + }); + }), + (Model.particleMesh = function (t, e, i, n) { + void 0 === i && (i = 1), void 0 === n && (n = 0); + var o = new c.State(); + (o.blend = !0), (o.depthTest = !1), (o.blendMode = n); + var s = this.getTexture(e); + if (!s) throw new Error("Map not found for " + mapFile); + var a = new G({ + map: s, + opacity: i, + }), + r = this.getEntity(t).view3d.geometry; + return new v.a({ + geometry: r, + material: a, + state: o, + }); + }), + (Model.tint = function (t, e) { + t.view3d && t.view3d.material && (t.view3d.material.color = e); + for (var i = this.entityChildren(t), n = i.length; n--; ) { + var o = i[n]; + this.tint(o, e); + } + }), + (Model.normalizeName = function (t) { + return t.replace("\0", " "); + }), + (Model.cloneEntity = function (t) { + if (!t) throw new Error("Invalid entity to clone"); + var e = t.view3d.geometry, + i = t.view3d.material, + n = t.view3d.state; + return new v.a({ + geometry: e, + material: i, + state: n, + }); + }), + (Model.entityChildren = function (t) { + return (t.container && t.container.children ? t.container.children : []).slice(0); + }), + (Model.findFileName = function (t) { + var e = t.split("/"); + for (var i in e) if (e[i].endsWith(".fbx")) return e[i]; + return ""; + }), + (Model.findScene = function (t) { + if (!t) throw new Error("Scene name is empty"); + if (c.sceneCache[t]) return c.sceneCache[t]; + for (var e in c.sceneCache) if (e.endsWith(t)) return c.sceneCache[e]; + return null; + }), + (Model.getScene = function (t) { + var e = this.findScene(t); + if (!e) throw new Error("Scene not found: " + t); + return e; + }), + (Model.hasScene = function (t) { + for (var e in c.sceneCache) if (e.match(t)) return !0; + return !1; + }), + (Model.whichScene = function () { + for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; + for (var n in e) if (this.findEntity(e[n])) return e[n]; + return null; + }), + (Model.forEach = function (t, e) { + e(t); + for (var i = this.entityChildren(t), n = i.length; n--; ) this.forEach(i[n], e); + }), + (Model.applyTexture = function (t, e, i) { + if ((void 0 === i && (i = !0), t.view3d && t.view3d.material)) + if (i) { + var n = !!t.__path.match("track"), + o = this.getTexture(e), + s = new N( + { + map: o, + }, + { + fog: B.fog, + rails: n, + } + ); + t.view3d.material = s; + } else t.view3d.material.map = this.getTexture(e); + for (var a = this.entityChildren(t), r = a.length; r--; ) this.applyTexture(a[r], e, i); + }), + (Model.getTexture = function (t) { + var e = c.utils.TextureCache[t]; + if (!e) { + var i = c.utils.TextureCache; + for (var n in i) + if (n.match(t)) { + e = i[n]; + break; + } + } + if (!e) throw new Error("Texture not found for " + t); + return "ocean" === t && (e.baseTexture.wrapMode = c.WRAP_MODES.REPEAT), e; + }), + (Model.entityMap = function (t, e, i, n) { + void 0 === t && (t = null), void 0 === e && (e = {}), void 0 === i && (i = ""), void 0 === n && (n = 4), t && t.pixiTree && (t = t.pixiTree); + var o = t ? this.entityChildren(t) : c.sceneCache, + s = o === c.sceneCache; + for (var a in ((n -= 1), o)) { + var r = o[a].pixiTree || o[a]; + if (s) { + var h = a.match(/\?v=/) ? a.replace(/(\?v\=.+)/, "") : a; + (o[h] = o[a]), (a = h); + } + var d = this.normalizeName(t && r.__name ? r.__name : a), + l = i + "/" + d; + (r.__path = l), (r.__file = this.findFileName(l)), (r.__name = d), (r.__scene = r.__file ? this.findScene(r.__file) : null), (e[l] = r), n && this.entityMap(r, e, l, n); + } + return e; + }), + (Model.cachedEntityMap = function () { + return X || (X = this.refreshCache()), X; + }), + (Model.refreshCache = function () { + X = this.entityMap(null, {}, "", 5); + }), + (Model.findEntity = function (t, e, i) { + if ((void 0 === i && (i = 4), !t)) throw new Error("A path must be provided"); + var n = e ? this.entityMap(e, {}, "", i) : this.cachedEntityMap(); + if (n[t]) return n[t]; + for (var o in n) if (o.endsWith(t) && !o.match("_old")) return n[o]; + return null; + }), + (Model.getEntity = function (t, e) { + if ((void 0 === e && (e = ""), !t)) throw new Error("A path must be provided"); + var i = this.findEntity(t); + if (!i) throw new Error("Entity not found: " + t); + if (e) this.applyTexture(i, e); + else { + var n = Y[i.__file]; + if (!n) throw new Error("Texture not found for entity: " + i.name + " file: " + i.__file); + n && this.applyTexture(i, n); + } + return i; + }), + (Model.getEntityClone = function (t, e, i, n) { + if ((void 0 === e && (e = ""), void 0 === i && (i = !0), void 0 === n && (n = null), !i)) { + var o = n || C.a, + s = "string" == typeof t ? this.getEntity(t, e) : t, + a = s.view3d.geometry, + r = new o({ + map: s.view3d.material.map, + }), + h = s.view3d.state; + return new v.a({ + geometry: a, + material: r, + state: h, + }); + } + var d = this.getEntity(t, e); + if (!d) throw new Error("Entity not found by name: " + t); + return B.culling || (d.view3d.state.culling = !1), this.cloneEntity(d); + }), + (Model.getEntityCloneBlend = function (t, e, i, n) { + void 0 === e && (e = ""), void 0 === i && (i = 1), void 0 === n && (n = 0); + var o = this.getEntity(t, e), + s = o.view3d.geometry, + a = o.view3d.material; + a.opacity = i; + var r = o.view3d.state; + return ( + (r.blend = i < 1), + (r.depthTest = !0), + n && (r.blendMode = n), + new v.a({ + geometry: s, + material: a, + state: r, + }) + ); + }), + (Model.getEntityCloneColor = function (t, e, i) { + void 0 === i && (i = !0); + var n = i ? N : C.a, + o = this.getEntity(t); + if (!o) throw new Error("Entity not found: " + t); + var s = o.view3d.geometry, + a = new n( + { + color: e, + }, + { + fog: B.fog, + } + ), + r = o.view3d.state; + return new v.a({ + geometry: s, + material: a, + state: r, + }); + }), + (Model.getEntityCloneOpaque = function (t, e, i) { + void 0 === e && (e = ""), void 0 === i && (i = !0); + var n = i ? N : C.a, + o = "string" == typeof t ? this.getEntity(t) : t; + e || (e = Y[o.__file]); + var s = this.getTexture(e); + if (!o) throw new Error("Entity not found: " + t); + var a = o.view3d.geometry, + r = new n( + { + map: s, + }, + { + fog: B.fog, + } + ), + h = o.view3d.state; + return new v.a({ + geometry: a, + material: r, + state: h, + }); + }), + Model + ); + })(), + U = i(4), + V = (function () { + function Math2() {} + return ( + (Math2.oldLerp = function (t, e, i, n, o) { + return void 0 === n && (n = 0), void 0 === o && (o = 0), n && e - n <= t && t <= e + n ? e : t * (1 - i) + e * i; + }), + (Math2.lerp = function (t, e, i, n) { + return void 0 === n && (n = 0), n && e - n <= t && t <= e + n ? e : t + (e - t) * (i < 0 ? 0 : 1 < i ? 1 : i); + }), + (Math2.lerpCap = function (t, e, i, n, o) { + if ((void 0 === n && (n = 0), void 0 === o && (o = 0), n && e - n <= t && t <= e + n)) return e; + var s = (i < 0 ? 0 : 1 < i ? 1 : i) * (e - t); + return o && (s = this.clamp(s, -o, o)), t + s; + }), + (Math2.clamp = function (t, e, i) { + return void 0 === e && (e = 0), void 0 === i && (i = 1), t < e ? e : i < t ? i : t; + }), + (Math2.sign = function (t, e) { + return void 0 === e && (e = !1), e ? (t < 0 ? -1 : 0 < t ? 1 : 0) : t < 0 ? -1 : 1; + }), + (Math2.ease = function (t, e, i, n) { + var o = -(t - e) * i; + return n ? this.clamp(o, -n, n) : o; + }), + (Math2.lerpVec3 = function (t, e, i, n, o) { + return void 0 === o && (o = 0), (t.x = this.lerp(e.x, i.x, n, o)), (t.y = this.lerp(e.y, i.y, n, o)), (t.z = this.lerp(e.z, i.z, n, o)), t; + }), + (Math2.smoothStep = function (t, e, i) { + return (i = this.clamp((i - t) / (e - t), 0, 1)) * i * (3 - 2 * i); + }), + (Math2.smoothDamp = function (t, e, i, n, o, s) { + var a = 2 / (n = Math.max(1e-4, n)), + r = a * s, + h = 1 / (1 + r + 0.479999989271164 * r * r + 0.234999999403954 * r * r * r), + d = t - e, + l = e, + c = o * n, + u = this.clamp(d, -c, c), + p = (i + a * u) * s; + i = (i - a * p) * h; + var m = (e = t - u) + (u + p) * h; + return 0 < l - t == l < m && (i = ((m = l) - l) / s), m; + }), + Math2 + ); + })(); + + function _defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + (V.PI_HALF = 0.5 * Math.PI), (V.PI_QUARTER = 0.25 * Math.PI), (V.PI_DOUBLE = 2 * Math.PI), (V.DEG_TO_RAD = 0.0174533); + var q = (function () { + function Vector3(t, e, i) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === i && (i = 0), (this.vec = U.vec3.create()), (this.x = t), (this.y = e), (this.z = i); + } + var t, + e, + i, + n = Vector3.prototype; + return ( + (n.reset = function (t, e, i) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === i && (i = 0), (this.vec[0] = t), (this.vec[1] = e), (this.vec[2] = i); + }), + (n.copy = function (t) { + (this.vec[0] = t.vec[0]), (this.vec[1] = t.vec[1]), (this.vec[2] = t.vec[2]); + }), + (n.clone = function () { + return new Vector3(this.vec[0], this.vec[1], this.vec[2]); + }), + (n.distance = function (t) { + return U.vec3.distance(this.vec, t.vec); + }), + (n.magnitude = function () { + return U.vec3.length(this.vec); + }), + (n.add = function (t) { + U.vec3.add(this.vec, this.vec, t.vec); + }), + (n.subtract = function (t) { + U.vec3.add(this.vec, this.vec, t.vec); + }), + (n.lerp = function (t, e, i, n) { + void 0 === i && (i = 0), void 0 === n && (n = 0), -1 !== e ? V.lerpVec3(this, this, t, e, i, n) : this.copy(t); + }), + (t = Vector3), + (e = [ + { + key: "x", + get: function () { + return this.vec[0]; + }, + set: function (t) { + this.vec[0] = t; + }, + }, + { + key: "y", + get: function () { + return this.vec[1]; + }, + set: function (t) { + this.vec[1] = t; + }, + }, + { + key: "z", + get: function () { + return this.vec[2]; + }, + set: function (t) { + this.vec[2] = t; + }, + }, + ]) && _defineProperties(t.prototype, e), + i && _defineProperties(t, i), + Vector3 + ); + })(); + + function CameraSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function CameraSystem_createClass(t, e, i) { + return e && CameraSystem_defineProperties(t.prototype, e), i && CameraSystem_defineProperties(t, i), t; + } + + function _assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + + function CameraSystem_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + (q.UP = new q(0, 1, 0)), (q.RIGHT = new q(1, 0, 0)), (q.FORWARD = new q(0, 0, -1)), (q.DOWN = new q(0, -1, 0)), (q.LEFT = new q(-1, 0, 0)), (q.BACK = new q(0, 0, 1)), (q.ZERO = new q(0, 0, 0)), (q.ONE = new q(1, 1, 1)); + var W = (function (n) { + function CameraSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).game = t.game), + i.game.onIdle.add(_assertThisInitialized(i)), + i.game.onRun.add(_assertThisInitialized(i)), + i.game.onEnterTunnel.add(_assertThisInitialized(i)), + i.game.onExitTunnel.add(_assertThisInitialized(i)), + i + ); + } + CameraSystem_inheritsLoose(CameraSystem, n); + var t = CameraSystem.prototype; + return ( + (t.idle = function () { + this.rig && this.rig.reset(), (this.running = !1), (this._shakePower = 0), (this.tunnel = !1); + }), + (t.run = function () { + this.rig && this.rig.resetLevel("_tunnel"), (this.running = !0), (this.tunnel = !1); + }), + (t.enterTunnel = function () { + this.tunnel = !0; + }), + (t.exitTunnel = function () { + this.tunnel = !1; + }), + (t.setup = function () { + this.rig || + ((this.rig = new Z(this.game.view3d.camera)), + this.game.addChild(this.rig), + (this.game.view3d.camera.x = 0), + (this.game.view3d.camera.y = 0), + (this.game.view3d.camera.z = 0), + (this.game.view3d.camera.rx = 0), + (this.game.view3d.camera.ry = 0), + (this.game.view3d.camera.rz = 0), + (this.game.view3d.camera.camera.near = 3), + (this.game.view3d.camera.camera.far = 1e3)); + }), + (t.update = function (t) { + this.rig || this.setup(), this.running ? this.updateRunning(t) : this.updateIdle(t); + }), + (t.updateIdle = function (t) { + this._controlled || + ((this.rig.idleX = -21.94232), + (this.rig.idleY = -13.06444), + (this.rig.idleZ = 11.95625), + (this.rig.idleRotX = 16.37991 * V.DEG_TO_RAD), + (this.rig.idleRotY = 59.09998 * V.DEG_TO_RAD), + (this.rig.mainX = 0), + (this.rig.mainY = B.cameraPosY), + (this.rig.mainZ = B.cameraPosZ), + (this.rig.mainRotX = B.cameraRotX), + (this.rig.mainRotY = 0), + (this.rig.fov = 71.99513)); + }), + (t.updateRunning = function (t) { + if ((this._animating && this.updateAnimation(t), !this._controlled)) { + (this.rig.idleX = 0), (this.rig.idleY = 0), (this.rig.idleZ = 0), (this.rig.idleRotX = 0), (this.rig.idleRotY = 0); + var e = this.game.stats.x * B.cameraModX; + this.rig.mainX = V.lerp(this.rig.mainX, e, 0.3 * t); + var i = this.game.hero.player.cameraY + B.cameraPosY; + B.cameraRotX; + (this.rig.mainY = V.lerp(this.rig.mainY, i, 0.3 * t)), + (this.rig.mainZ = this.game.stats.z + B.cameraPosZ), + (this.rig.mainRotX = V.lerp(this.rig.mainRotX, B.cameraRotX, 0.1 * t)), + (this.rig.mainRotY = 0), + (this.rig.fov = B.cameraFov); + } + this.updateTunnel(t), this.updateShake(t); + }), + (t.updateTunnel = function (t) { + var e = this.tunnel, + i = this.rig._tunnel; + if (e || 0 !== i.rx || 0 !== this.rig.y) { + var n = -4.621953 * V.DEG_TO_RAD, + o = e ? B.cameraPosY - 18.30177 : 0, + s = e ? B.cameraRotX - n : 0, + a = 0.25 * this.game.stats.speed + 0.5; + (i.y = V.smoothDamp(i.y, -o, 0, 0.2, 1.2 * a, t)), (i.rx = V.smoothDamp(i.rx, -s, 0, 0.05, 0.1 * a, t)); + } + }), + (t.updateShake = function (t) { + var e = this.rig; + (this._shakePower || 0 !== e.x || 0 !== this.rig.y) && + ((e.y = L.range(-this._shakePower, this._shakePower)), (e.x = L.range(-this._shakePower, this._shakePower)), (this._shakePower -= 0.5 * t), this._shakePower < 0 && (this._shakePower = 0)); + }), + (t.takeControl = function () { + return (this._controlled = !0), this.rig; + }), + (t.releaseControl = function () { + this._controlled = !1; + }), + (t.shake = function (t) { + this._shakePower = t; + }), + (t.animate = function (t, e, i, n) { + for (var o in ((this._controlled = !0), (this._animating = !0), this._animStart || (this._animStart = {}), this._animEnd || (this._animEnd = {}), (this._animCurve = i), (this._animTime = e), t)) + (this._animStart[o] = this.rig[o]), (this._animEnd[o] = t[o]); + }), + (t.updateAnimation = function (t) {}), + (t.animateEnd = function () { + (this._controlled = !1), (this._animating = !1); + }), + (t.rigProps = function (t) { + return Object.assign( + { + idleX: this.rig.idleX, + idleY: this.rig.idleY, + idleZ: this.rig.idleZ, + idleRotX: this.rig.idleRotX, + idleRotY: this.rig.idleRotY, + mainX: this.rig.mainX, + mainY: this.rig.mainY, + mainZ: this.rig.mainZ, + mainRotX: this.rig.mainRotX, + mainRotY: this.rig.mainRotY, + }, + t + ); + }), + CameraSystem_createClass(CameraSystem, [ + { + key: "profile", + get: function () { + return ( + this._profile || (this._profile = {}), + this.rig && + ((this._profile.idleX = this.rig.idleX), + (this._profile.idleY = this.rig.idleY), + (this._profile.idleZ = this.rig.idleZ), + (this._profile.idleRotX = this.rig.idleRotX), + (this._profile.idleRotY = this.rig.idleRotY), + (this._profile.mainX = this.rig.mainX), + (this._profile.mainY = this.rig.mainY), + (this._profile.mainZ = this.rig.mainZ), + (this._profile.mainRotX = this.rig.mainRotX), + (this._profile.mainRotY = this.rig.mainRotY), + (this._profile.fov = this.game.view3d.camera.camera.fov), + (this._profile.near = this.game.view3d.camera.camera.near), + (this._profile.far = this.game.view3d.camera.camera.far)), + this._profile + ); + }, + }, + ]), + CameraSystem + ); + })(f.a), + Z = (function (s) { + function CameraRig(t) { + var e; + void 0 === t && (t = null), ((e = s.call(this) || this).camera = t), (e.levels = ["_idle", "_idleRotY", "_idleRotX", "_main", "_mainRotY", "_mainRotX", "_tunnel"]); + var i = _assertThisInitialized(e); + for (var n in e.levels) { + var o = new v.a(); + (e[e.levels[n]] = o), i.addChild(o), (i = o), t && o.addChild(t); + } + return e; + } + CameraSystem_inheritsLoose(CameraRig, s); + var t = CameraRig.prototype; + return ( + (t.reset = function () { + for (var t in this.levels) this.resetLevel(this.levels[t]); + }), + (t.resetLevel = function (t) { + var e = this[t]; + (e.x = 0), (e.y = 0), (e.z = 0), (e.rx = 0), (e.ry = 0); + }), + (t.resetIdle = function () { + this.resetLevel("_idle"); + }), + CameraSystem_createClass(CameraRig, [ + { + key: "fov", + get: function () { + return this.camera.camera.fov; + }, + set: function (t) { + this.camera.camera.fov = t; + }, + }, + { + key: "idleX", + get: function () { + return this._idle.x; + }, + set: function (t) { + this._idle.x = t; + }, + }, + { + key: "idleY", + get: function () { + return this._idle.y; + }, + set: function (t) { + this._idle.y = t; + }, + }, + { + key: "idleZ", + get: function () { + return this._idle.z; + }, + set: function (t) { + this._idle.z = t; + }, + }, + { + key: "idleRotX", + get: function () { + return this._idleRotX.rx; + }, + set: function (t) { + this._idleRotX.rx = t; + }, + }, + { + key: "idleRotY", + get: function () { + return this._idleRotY.ry; + }, + set: function (t) { + this._idleRotY.ry = t; + }, + }, + { + key: "mainX", + get: function () { + return this._main.x; + }, + set: function (t) { + this._main.x = t; + }, + }, + { + key: "mainY", + get: function () { + return this._main.y; + }, + set: function (t) { + this._main.y = t; + }, + }, + { + key: "mainZ", + get: function () { + return this._main.z; + }, + set: function (t) { + this._main.z = t; + }, + }, + { + key: "mainRotX", + get: function () { + return this._mainRotX.rx; + }, + set: function (t) { + this._mainRotX.rx = t; + }, + }, + { + key: "mainRotY", + get: function () { + return this._mainRotY.ry; + }, + set: function (t) { + this._mainRotY.ry = t; + }, + }, + ]), + CameraRig + ); + })(v.a); + var K = i(5), + J = i.n(K); + + function Box_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Q = (function () { + function Box(t, e, i) { + void 0 === t && (t = 1), void 0 === e && (e = 1), void 0 === i && (i = 1), (this.size = new q(t, e, i)), (this.center = new q(0, 0, 0)); + } + var t, + e, + i, + n = Box.prototype; + return ( + (n.copy = function (t) { + this.size.copy(t.size), this.center.copy(t.center); + }), + (n.resize = function (t, e, i) { + void 0 === t && (t = 1), void 0 === e && (e = 1), void 0 === i && (i = 1), (this.size.x = t), (this.size.y = e), (this.size.z = i); + }), + (n.reposition = function (t, e, i) { + void 0 === t && (t = 1), void 0 === e && (e = 1), void 0 === i && (i = 1), (this.center.x = t), (this.center.y = e), (this.center.z = i); + }), + (n.hitTest = function (t, e) { + void 0 === e && (e = null); + var i = this.left <= t.right && this.right >= t.left, + n = this.bottom <= t.top && this.top >= t.bottom, + o = this.front <= t.back && this.back >= t.front; + if (!(i && n && o)) return null; + var s = e; + s || (this._intersection || (this._intersection = new Box()), (s = this._intersection)); + var a = Math.max(this.left, t.left), + r = Math.min(this.right, t.right), + h = Math.max(this.bottom, t.bottom), + d = Math.min(this.top, t.top), + l = Math.max(this.front, t.front), + c = Math.min(this.back, t.back); + return (s.size.x = r - a), (s.size.y = d - h), (s.size.z = c - l), (s.center.x = a + 0.5 * s.size.x), (s.center.y = h + 0.5 * s.size.y), (s.center.z = c - 0.5 * s.size.z), s; + }), + (t = Box), + (e = [ + { + key: "x", + get: function () { + return this.center.x; + }, + set: function (t) { + this.center.x = t; + }, + }, + { + key: "y", + get: function () { + return this.center.y; + }, + set: function (t) { + this.center.y = t; + }, + }, + { + key: "z", + get: function () { + return this.center.z; + }, + set: function (t) { + this.center.z = t; + }, + }, + { + key: "left", + get: function () { + return this.center.x - 0.5 * this.size.x; + }, + set: function (t) { + this.center.x = t + 0.5 * this.size.x; + }, + }, + { + key: "right", + get: function () { + return this.center.x + 0.5 * this.size.x; + }, + set: function (t) { + this.center.x = t - 0.5 * this.size.x; + }, + }, + { + key: "top", + get: function () { + return this.center.y + 0.5 * this.size.y; + }, + set: function (t) { + this.center.y = t - 0.5 * this.size.y; + }, + }, + { + key: "bottom", + get: function () { + return this.center.y - 0.5 * this.size.y; + }, + set: function (t) { + this.center.y = t + 0.5 * this.size.y; + }, + }, + { + key: "front", + get: function () { + return this.center.z - 0.5 * this.size.z; + }, + set: function (t) { + this.center.z = t + 0.5 * this.size.z; + }, + }, + { + key: "back", + get: function () { + return 0.5 * this.size.z + this.center.z; + }, + set: function (t) { + this.center.z = t - 0.5 * this.size.z; + }, + }, + { + key: "width", + get: function () { + return this.size.x; + }, + set: function (t) { + this.size.x = t; + }, + }, + { + key: "height", + get: function () { + return this.size.y; + }, + set: function (t) { + this.size.y = t; + }, + }, + { + key: "depth", + get: function () { + return this.size.z; + }, + set: function (t) { + this.size.z = t; + }, + }, + ]) && Box_defineProperties(t.prototype, e), + i && Box_defineProperties(t, i), + Box + ); + })(), + $ = (function () { + function Collision() { + (this.act = null), (this.pas = null), (this.hit = new Q()), (this.flags = Collision.NONE); + } + return ( + (Collision.prototype.reset = function () { + (this.act = null), (this.pas = null), (this.flags = Collision.NONE); + }), + Collision + ); + })(); + ($.NONE = 0), ($.LEFT = 4), ($.TOP = 8), ($.RIGHT = 16), ($.BOTTOM = 32), ($.FRONT = 64), ($.BACK = 128), ($.INSIDE = 256), ($.SLOPE = 512); + var tt = i(8), + et = i.n(tt); + + function Body_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var it = (function (n) { + var t, e; + + function Body(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).trigger = e.trigger || !1), + (i.ghost = e.ghost || !1), + (i.deco = e.deco || !1), + (i.movable = e.movable || !1), + (i.box = new Q(1, 1, 1)), + (i.origin = new Q(1, 1, 1)), + (i.velocity = new q()), + (i.sensor = e.sensor ? new Q(0.2, 100, 1) : null), + (i.ground = 0), + (i.groundBefore = 0), + (i.groundChangeTolerance = 0), + (i.colliding = []), + (i.triggering = []), + B.blocks && !e.noView && i.drawView(), + (i.onCollisionEnter = new et.a("onCollisionEnter", 1)), + (i.onCollisionExit = new et.a("onCollisionExit", 1)), + (i.onTriggerEnter = new et.a("onTriggerEnter", 1)), + (i.onTriggerExit = new et.a("onTriggerExit", 1)), + (t.z = 9999), + i + ); + } + (e = n), ((t = Body).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Body.prototype; + return ( + (a.reset = function () { + (this.ground = 0), (this.colliding = []), (this.triggering = []), this.origin.copy(this.box), this.velocity.reset(), this.resetGroundChangeTolerance(); + }), + (a.drawView = function () { + this.view || + (this.entity.ramp ? (this.view = H.ramp(this.box, this.data.boxColor, 0.75)) : (this.view = H.box(this.box, this.data.boxColor, 0.75)), + this.entity.addChild(this.view), + this.sensor && ((this.sensorView = H.box(this.sensor, this.data.boxColor, 0.5)), (this.sensorView.y = -50), this.entity.addChild(this.sensorView))); + }), + (a.render = function () { + (this.entity.transform.position.x = this.box.center.x), + (this.entity.transform.position.y = this.box.center.y), + (this.entity.transform.position.z = this.box.center.z), + this.view && ((this.view.scale.x = this.box.size.x), (this.view.scale.y = this.box.size.y), (this.view.scale.z = this.box.size.z)), + !B.models && this.entity.model && (this.entity.model.active = !1); + }), + (a.matchEntityPosition = function () { + (this.entity.x = this.box.center.x), (this.entity.y = this.box.center.y), (this.entity.z = this.box.center.z); + }), + (a.move = function (t) { + (this.origin.center.x = this.box.center.x), + (this.origin.center.y = this.box.center.y), + (this.origin.center.z = this.box.center.z), + (this.origin.size.x = this.box.size.x), + (this.origin.size.y = this.box.size.y), + (this.origin.size.z = this.box.size.z), + (this.box.center.z += this.velocity.z * t), + (this.box.center.x += this.velocity.x * t), + (this.box.center.y += this.velocity.y * t), + this.box.bottom <= this.ground && !this.ghost && ((this.box.bottom = this.ground), (this.velocity.y = 0)), + this.sensor && ((this.sensor.x = this.box.x), (this.sensor.y = this.box.y - 50), (this.sensor.z = this.box.z)), + this.groundChangeTolerance && ((this.groundChangeTolerance -= t), this.groundChangeTolerance < 0 && (this.groundChangeTolerance = 0)); + }), + (a.onCollision = function (t) { + t.flags & $.RIGHT && ((this.velocity.x = 0), (this.lane -= 1)), + t.flags & $.LEFT && ((this.velocity.x = 0), (this.lane += 1)), + t.flags & $.FRONT && (this.velocity.z = 0), + t.flags & $.BOTTOM && (this.velocity.y = 0), + t.flags & $.TOP && (this.velocity.y = 0); + }), + (a.triggerEnter = function (t, e) { + this.onTriggerEnter.dispatch(t); + }), + (a.triggerExit = function (t, e) { + this.onTriggerExit.dispatch(t); + }), + (a.collisionEnter = function (t, e) { + (e.flags & $.LEFT || e.flags & $.RIGHT) && (this.velocity.x = 0), + (e.flags & $.TOP || e.flags & $.BOTTOM) && (this.velocity.y = 0), + (e.flags & $.FRONT || e.flags & $.BACK) && (this.velocity.z = 0), + this.onCollisionEnter.dispatch(e); + }), + (a.collisionExit = function (t, e) { + this.onCollisionExit.dispatch(e); + }), + (a.isColliding = function (t) { + return 0 <= this.colliding.indexOf(t); + }), + (a.isTrigering = function (t) { + return 0 <= this.colliding.indexOf(t); + }), + (a.resetGroundChangeTolerance = function () { + this.groundChangeTolerance = 0; + }), + (i = Body), + (o = [ + { + key: "ascending", + get: function () { + return this.origin.y < this.box.y; + }, + }, + { + key: "descending", + get: function () { + return this.origin.y > this.box.y; + }, + }, + { + key: "hangtime", + get: function () { + var t = -0.2 < this.velocity.y && this.velocity.y < 0.2; + return !this.landed && t; + }, + }, + { + key: "airborne", + get: function () { + return !this.landed; + }, + }, + { + key: "stable", + get: function () { + return this.origin.y === this.box.y; + }, + }, + { + key: "landed", + get: function () { + return this.bottom <= this.ground + 1 && !this.ghost; + }, + }, + { + key: "moving", + get: function () { + return 0 !== this.velocity.x || 0 !== this.velocity.y || 0 !== this.velocity.z; + }, + }, + { + key: "dodging", + get: function () { + var t = this.velocity.x; + return t < -0.25 ? -1 : 0.25 < t ? 1 : 0; + }, + }, + { + key: "canJump", + get: function () { + return !!this.groundChangeTolerance || this.landed; + }, + }, + { + key: "x", + get: function () { + return this.box.center.x; + }, + set: function (t) { + this.box.center.x = t; + }, + }, + { + key: "y", + get: function () { + return this.box.center.y; + }, + set: function (t) { + this.box.center.y = t; + }, + }, + { + key: "z", + get: function () { + return this.box.center.z; + }, + set: function (t) { + this.box.center.z = t; + }, + }, + { + key: "left", + get: function () { + return this.box.left; + }, + set: function (t) { + this.box.left = t; + }, + }, + { + key: "right", + get: function () { + return this.box.right; + }, + set: function (t) { + this.box.right = t; + }, + }, + { + key: "top", + get: function () { + return this.box.top; + }, + set: function (t) { + this.box.top = t; + }, + }, + { + key: "bottom", + get: function () { + return this.box.bottom; + }, + set: function (t) { + this.box.bottom = t; + }, + }, + { + key: "front", + get: function () { + return this.box.front; + }, + set: function (t) { + this.box.front = t; + }, + }, + { + key: "back", + get: function () { + return this.box.back; + }, + set: function (t) { + this.box.back = t; + }, + }, + { + key: "width", + get: function () { + return this.box.size.x; + }, + set: function (t) { + this.box.size.x = t; + }, + }, + { + key: "height", + get: function () { + return this.box.size.y; + }, + set: function (t) { + this.box.size.y = t; + }, + }, + { + key: "depth", + get: function () { + return this.box.size.z; + }, + set: function (t) { + this.box.size.z = t; + }, + }, + { + key: "center", + get: function () { + return this.box.center; + }, + }, + { + key: "size", + get: function () { + return this.box.size; + }, + }, + { + key: "phx", + get: function () { + return this.box.center.x; + }, + set: function (t) { + var e = t - this.box.center.x; + this.velocity.x = e; + }, + }, + { + key: "phy", + get: function () { + return this.center.y; + }, + set: function (t) { + var e = t - this.box.center.y; + this.velocity.y = e; + }, + }, + { + key: "phz", + get: function () { + return this.center.z; + }, + set: function (t) { + var e = t - this.box.center.z; + this.velocity.z = e; + }, + }, + ]) && Body_defineProperties(i.prototype, o), + s && Body_defineProperties(i, s), + Body + ); + })(J.a); + var nt = { + speed: 0, + }, + ot = (function (n) { + var t, e; + + function Movable(t, e) { + var i; + return void 0 === e && (e = {}), (e = Object.assign({}, nt, e)), ((i = n.call(this, t, e) || this).speed = e.speed), (i.lastDest = null), i; + } + (e = n), ((t = Movable).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Movable.prototype; + return ( + (i.reset = function () { + (this.speed = 0), (this.entity.body.movable = !1), this.entity.body.velocity.reset(); + }), + (i.update = function (t) { + if (!this.entity.game.hero.player.dead && this.speed && this.entity.active) { + null !== this.lastDest && ((this.entity.body.back = this.lastDest), (this.entity.body.origin.back = this.lastDest)); + var e = this.target - this.entity.game.stats.z, + i = this.origin + e * this.speed, + n = i - this.entity.body.back; + (this.entity.body.velocity.z = n), (this.lastDest = i); + } + }), + (i.run = function (t, e) { + (this.speed = t), (this.origin = this.entity.body.back), (this.target = e), (this.lastDest = this.origin), (this.entity.body.movable = 0 < this.speed); + }), + Movable + ); + })(J.a); + var st = (function (n) { + var t, e; + + function Collectible(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).entity.body.solid = !1), i; + } + (e = n), ((t = Collectible).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Collectible.prototype; + return ( + (i.reset = function () { + (this.entity.active = !0), (this.collected = !1); + }), + (i.collect = function (t) { + this.collected || + ((this.collected = !0), + (this.entity.active = !1), + this.entity.body && ((this.entity.body.movable = 0), this.entity.body.velocity.reset()), + this.entity.attractable && (this.entity.attractable.attracted = !1), + this.entity.onCollect && this.entity.onCollect(t)); + }), + Collectible + ); + })(J.a); + var at = (function (n) { + var t, e; + + function Attractable(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).startPosition = new q()), (i.endPosition = new q()), i.reset(), i; + } + (e = n), ((t = Attractable).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Attractable.prototype; + return ( + (i.reset = function () { + (this.speedX = L.range(0.001, 0.005)), (this.speedY = L.range(0.001, 0.005)), (this.speedZ = L.range(0.001, 0.005)), (this.attracted = !1), (this.entity.body.movable = !1); + }), + (i.update = function (t) { + if (this.entity.game && this.entity.active) { + var e = this.entity.game.hero; + if ((this.attracted && this.entity.game.state !== pn.RUNNING && ((this.attracted = !1), (this.entity.active = !1)), this.attracted && this.attractionUpdate(t), !this.attracted && this.entity.game.state === pn.RUNNING)) + if (e.magnet.isOn()) this.entity.body.center.distance(e.body.center) < 110 && this.attractionStart(); + else if (e.sneakers.isOn()) { + var i = Math.abs(this.entity.body.x - e.body.x), + n = e.body.y - this.entity.body.y, + o = Math.abs(e.body.z - this.entity.body.z); + e.body.airborne && i < 10 && 0 < n && n < 50 && o < 50 && this.attractionStart(); + } + } + }), + (i.attractionStart = function () { + (this.attracted = !0), this.entity.movable && this.entity.movable.reset(); + var t = this.entity.game.hero; + (this.entity.body.movable = !0), + (this.entity.body.ghost = !0), + (this.startPosition.x = this.entity.body.x), + (this.startPosition.y = this.entity.body.y), + (this.startPosition.z = this.entity.body.z), + (this.endPosition.x = t.body.x), + (this.endPosition.y = t.body.y), + (this.endPosition.z = t.body.z + t.body.velocity.z); + var e = this.startPosition.distance(this.endPosition); + (this.duration = 0.2 * e), this.duration < 4 && (this.duration = 4), (this.time = 0); + }), + (i.attractionUpdate = function (t) { + var e = this.entity.game.hero, + i = this.time / this.duration; + (this.endPosition.x = e.body.x), (this.endPosition.y = e.body.y), (this.endPosition.z = e.body.z + 2 * e.body.velocity.z), V.lerpVec3(this.entity.body, this.startPosition, this.endPosition, i * i), (this.time += t); + }), + (i.updateAttraction2 = function (t) { + var e = -hero.body.velocity.z, + i = (hero.body.velocity.y, this.entity.body.x - hero.body.x), + n = this.entity.body.y - hero.body.y, + o = this.entity.body.z - hero.body.z; + (this.entity.body.velocity.x = -i * this.speedX), + (this.entity.body.velocity.y = -n * this.speedY), + (this.entity.body.velocity.z = -o * this.speedZ), + (this.speedX = V.clamp(this.speedX + 0.003 * e, 0, 2)), + (this.speedY = V.clamp(this.speedY + 0.003 * e, 0, 2)), + (this.speedZ = V.clamp(this.speedZ + 0.003, 0, 2)), + this.entity.body.z >= hero.body.z - 1 && + ((this.entity.body.x = hero.body.x), + (this.entity.body.y = hero.body.y), + (this.entity.body.z = hero.body.z), + (this.entity.body.velocity.x = 0), + (this.entity.body.velocity.y = 0), + (this.entity.body.velocity.z = 0), + (this.attracted = !1), + (this.entity.body.movable = !1), + (this.entity.body.ghost = !1)); + }), + Attractable + ); + })(J.a); + var rt = { + rotationSpeed: 0.1, + }, + ht = 1, + dt = (function (n) { + var t, e; + + function Floating(t, e) { + var i; + return void 0 === e && (e = {}), (e = Object.assign({}, rt, e)), ((i = n.call(this, t, e) || this).startingRot = null), (i.index = null), i; + } + (e = n), ((t = Floating).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Floating.prototype; + return ( + (i.reset = function () { + (this.startingRot = null), (this.index = ht++); + }), + (i.update = function (t) { + this.entity.model && (null === this.startingRot && ((this.startingRot = 0.4 * ht++), (this.entity.model.ry = this.startingRot)), (this.entity.model.ry -= t * this.data.rotationSpeed)); + }), + Floating + ); + })(J.a); + var lt = (function (n) { + var t, e; + + function Shine(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).view = H.plane(15, 15, 0.9, "halo_mip.png", 1)), + (i.view.view3d.state.blend = !0), + (i.view.view3d.state.depthTest = !0), + (i.view.view3d.orderBias = 999), + (i.view.z = -1.2), + t.addChild(i.view), + i + ); + } + return ( + (e = n), + ((t = Shine).prototype = Object.create(e.prototype)), + ((t.prototype.constructor = t).__proto__ = e), + (Shine.prototype.update = function (t) { + var e = this.entity.game.hero.body, + i = this.entity.body.z - e.z, + n = 1 - V.clamp(-i / 500); + this.view.scale.set(1.1 * n); + }), + Shine + ); + })(J.a); + var ct = { + type: "coin", + }, + ut = (function (n) { + var t, e; + + function Pop(t, e) { + var i; + return void 0 === e && (e = {}), (e = Object.assign({}, ct, e)), ((i = n.call(this, t, e) || this).view = H.getEntityCloneBlend("/star7", "effects_tex", 0.9, 3)), (i.view.rotation.y = Math.PI), (i.duration = 8), i.reset(), i; + } + (e = n), ((t = Pop).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Pop.prototype; + return ( + (i.reset = function () { + this.view.parent && this.entity.removeChild(this.view), (this.view.active = !1), this.view.scale.set(0), (this.count = 0); + }), + (i.update = function (t) { + if (this.count) { + this.count -= t; + var e = 0.5 + 0.75 * (1 - this.count / this.duration); + this.view.scale.set(e), this.count <= 0 && this.reset(); + } + }), + (i.play = function () { + (this.view.rotation.z = V.PI_DOUBLE * Math.random()), (this.count = this.duration), (this.view.active = !0), this.view.scale.set(0.5), this.entity.addChild(this.view); + }), + Pop + ); + })(J.a); + var pt = (function (e) { + var t, i; + + function Coin() { + var t; + return ( + (t = e.call(this) || this).add(it, { + ghost: !0, + boxColor: 16776960, + }), + t.add(at), + t.add(ot), + t.add(st), + t.add(dt, { + rotationSpeed: 0.06, + }), + B.optm || t.add(lt), + (t.body.width = 10), + (t.body.height = 10), + (t.body.depth = 10), + (t.trigger = 0), + (t.speed = 0), + t + ); + } + (i = e), ((t = Coin).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i); + var n = Coin.prototype; + return ( + (n.createView = function () { + this.model || ((this.model = H.getEntityCloneOpaque("/currency/currency_coin", "props_tex")), this.addChild(this.model)); + }), + (n.reset = function (t) { + this.movable.reset(), this.body.velocity.reset(), this.attractable.reset(), (this.body.movable = !1), (this.active = !0); + }), + (n.update = function (t) { + this.body.movable && this.game && (heroBody.z <= this.trigger ? (this.body.velocity.z = this.speed) : (this.body.velocity.z = 0)); + }), + (n.init = function () { + this.createView(), this.movable.reset(), this.attractable.reset(), this.body.velocity.reset(), (this.body.movable = !1), (this.active = !0); + }), + (n.onCollect = function (t) { + (this.game.stats.coins += 1), + this.game.sfx.play("pickup_coin", { + volume: 0.5, + }), + this.game.hero.pop.play(); + }), + Coin + ); + })(v.a); + + function Train_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + (pt.cache = function (t, e) { + void 0 === e && (e = 50), t.pool.prepopulate(pt, amout); + }), + (pt.match = function (t) { + return !!t.name.match(/Coin \(/) || !!t.name.match(/Coins \(/); + }), + (pt.factory = function (t, e, i) { + if (e.name.match(/Line/)) { + if (50 < e.components.Transform.position.y) return; + for (var n = e.components.CoinCurve._curveOffset, o = e.components.CoinCurve._curveParent._cachedCurve.MaxCoords.z, s = o * n, a = o / 5, r = 0; r < 5; r++) { + var h = pt.spawnCoin(t, e, i), + d = r * a - s; + (h.body.z -= d), (h.arc = !1); + } + } else e.name.match(/Jump Curve/) ? pt.spawnJumpCurve(t, e, i) : pt.spawnCoin(t, e, i); + }), + (pt.spawnCoin = function (t, e, i) { + var n = t.game.pool.get(pt), + o = e.components.Transform.position.x, + s = e.components.Transform.position.y, + a = e.components.Transform.position.z; + return ( + (n.body.x = o), + (n.body.bottom = s), + (n.body.z = t.z - a), + i.px && (n.body.x = i.px), + i.offsetX && (n.body.x = i.offsetX), + i.flip && (n.body.x *= -1), + 150 < n.body.bottom && (n.body.bottom = 29), + n.init(e), + i.py && (n.body.bottom = i.py), + i.bottom && (n.body.bottom = i.bottom), + (n.arc = !1), + t.game.addChild(n), + n + ); + }), + (pt.spawnLine = function (t, e, i, n, o) { + void 0 === o && (o = 5); + for (var s = 29 * o * 0.5 - 15, a = 0; a < o; a++) { + var r = t.game.pool.get(pt); + (r.body.z = n - 30 * a + s), (r.body.bottom = i), (r.body.x = e), r.init(), (r.arc = !1), t.game.addChild(r); + } + }), + (pt.spawnCurve = function (t, e, i, n) { + for (var o = 50 * t.game.stats.speed, s = Math.floor(o / 13), a = o / s, r = (a - 1) * s * 0.5 - 0.5 * a, h = Math.PI / (s - 1), d = 0; d < s; d++) { + var l = t.game.pool.get(pt); + (l.body.z = n - d * a + r), (l.body.bottom = i + 22 * Math.sin(h * d)), (l.body.x = e), l.init(), (l.arc = !0), t.game.addChild(l); + } + }), + (pt.spawnJumpCurve = function (t, e, i) { + for (var n = 50 * t.game.stats.speed, o = Math.floor(n / 14), s = e.components.CoinCurve ? e.components.CoinCurve._curveOffset : 0.5, a = n / o, r = Math.PI / o, h = n * s, d = 0; d < o; d++) { + var l = pt.spawnCoin(t, e, i), + c = d * a - h, + u = 22 * Math.sin(r * d); + (l.body.bottom += u), (l.body.z -= c), (l.arc = !0); + } + }); + var mt = (function (e) { + function Train() { + var t; + return ( + (t = e.call(this) || this).add(it, { + boxColor: 16711680, + }), + t.add(ot, { + speed: 0, + }), + (t.body.width = 18), + (t.body.height = 29), + (t.body.depth = 58), + t + ); + } + Train_inheritsLoose(Train, e); + var t = Train.prototype; + return ( + (t.reset = function (t) { + this.body.velocity.reset(); + }), + (t.clean = function (t) { + for (var e = this.wagons.length; e--; ) { + var i = this.wagons[e]; + t.game.pool["return"](i), this.removeChild(i); + } + }), + Train + ); + })(v.a), + ft = (function (e) { + function TrainStandard() { + var t; + return ((t = e.call(this) || this).model = H.getEntityClone("train_standard")), (t.model.y = 0.5 * -t.body.height), (t.model.ry = Math.PI), t.addChild(t.model), t; + } + return Train_inheritsLoose(TrainStandard, e), TrainStandard; + })(mt), + gt = (function (e) { + function TrainCargo() { + var t; + return ((t = e.call(this) || this).model = H.getEntityClone("train_cargo")), (t.model.y = 0.5 * -t.body.height), (t.model.ry = Math.PI), t.addChild(t.model), t; + } + return Train_inheritsLoose(TrainCargo, e), TrainCargo; + })(mt), + yt = (function (e) { + function TrainSub() { + var t; + return ((t = e.call(this) || this).model = H.getEntityClone("train_sub")), (t.model.y = 0.5 * -t.body.height), (t.model.ry = Math.PI), t.addChild(t.model), t; + } + return Train_inheritsLoose(TrainSub, e), TrainSub; + })(mt), + vt = [ft, gt, yt]; + + function Blocker_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + (mt.factory = function (t, e, i) { + var n = null; + n = e.name.match("sub") ? yt : e.name.match("cargo") ? gt : e.name.match("standard") ? ft : L.pick.apply(L, vt); + for (var o = t.name.match("intro") ? "z" : "back", s = parseInt(e.name.match(/_(\d)_/)[1]), a = e.name.match(/coins/), r = 60 * s, h = s, d = 0, l = Number.NEGATIVE_INFINITY, c = 0, u = null; h--; ) { + ((u = t.game.pool.get(n)).chunk = t), (c = ue.get(e, "components.MovingTrainPlaceholder._speed", 0)); + var p = e.components.Transform.position.x, + m = e.components.Transform.position.z, + f = 60 * h; + (u.body.x = null !== i.offsetX ? i.offsetX : p), (u.body.bottom = 0); + var g = t.z - m - f; + (u.body[o] = g), d || (d = u.body[o] + 0.6 * r + 30), i.flip && (u.body.x *= -1), u.movable.run(c, d), t.game.addChild(u), u.body.back > l && (l = u.body.back); + } + if (a) + for (var y = Math.ceil(1.1 * s), v = l - 50, _ = 0; _ < y; _++) { + var b = pt.spawnCoin(t, e, i); + (b.body.x = u.body.x), (b.body.bottom = 29), (b.body.z = v - 30 * _), b.movable.run(c, d); + } + }), + (mt.cache = function (t, e) { + void 0 === e && (e = 10), t.pool.prepopulate(ft, amout), t.pool.prepopulate(gt, amout), t.pool.prepopulate(yt, amout); + }); + var _t = (function (e) { + function Blocker() { + var t; + return ( + (t = e.call(this) || this).add(it, { + ghost: !0, + }), + (t.body.width = 16), + (t.body.height = 26), + (t.body.depth = 1), + (t.model = null), + t + ); + } + return ( + Blocker_inheritsLoose(Blocker, e), + (Blocker.prototype.init = function (t, e) { + (this.model.ry = Math.PI), (this.model.z = -5), this.addChild(this.model); + }), + Blocker + ); + })(v.a), + bt = (function (i) { + function BlockerJump() { + var t; + return ((t = i.call(this) || this).model = H.getEntityCloneOpaque("/blocker_jump", "environment_tex")), t; + } + return ( + Blocker_inheritsLoose(BlockerJump, i), + (BlockerJump.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), (this.body.height = 26); + }), + BlockerJump + ); + })(_t), + wt = { + jump: bt, + roll: (function (i) { + function BlockerRoll() { + var t; + return ((t = i.call(this) || this).model = H.getEntityCloneOpaque("/blocker_roll", "environment_tex")), t; + } + return ( + Blocker_inheritsLoose(BlockerRoll, i), + (BlockerRoll.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), (this.body.height = 19), (this.body.bottom = 10), (this.model.y = -this.body.height / 2 - this.body.bottom); + }), + BlockerRoll + ); + })(_t), + standard: (function (i) { + function BlockerStandard() { + var t; + return ((t = i.call(this) || this).model = H.getEntityCloneOpaque("/blocker_standard", "environment_tex")), t; + } + return ( + Blocker_inheritsLoose(BlockerStandard, i), + (BlockerStandard.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), (this.body.height = 4), (this.body.bottom = 10), (this.model.y = -this.body.height / 2 - this.body.bottom); + }), + BlockerStandard + ); + })(_t), + }; + + function Ramp_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + _t.factory = function (t, e, i) { + var n = L.pick.apply(L, Object.keys(wt)); + e.name.match("jump") && (n = "jump"), e.name.match("roll") && (n = "roll"); + var o = wt[n], + s = t.game.pool.get(o), + a = e.components.Transform.position.x, + r = e.components.Transform.position.z; + (s.body.front = t.z - r), + (s.body.x = a), + i.offsetX && (s.body.x = i.offsetX), + i.flip && (s.body.x *= -1), + s.init(t, e), + t.game.addChild(s), + e.name.match("w_coins") && (o === bt ? pt.spawnCurve(t, s.body.x, 0, s.body.z) : pt.spawnLine(t, s.body.x, 0, s.body.z)); + }; + var kt = (function (e) { + function Ramp() { + var t; + return ( + ((t = e.call(this) || this).ramp = !0), + t.add(it), + (t.body.width = 18), + (t.body.height = 29), + (t.body.depth = 70), + (t.model = H.getEntityClone("/train_ramp")), + (t.model.position.y = 0.5 * -t.body.height), + (t.model.rotation.y = Math.PI), + (t.model.z = -8), + t.addChild(t.model), + t + ); + } + return ( + Ramp_inheritsLoose(Ramp, e), + (Ramp.prototype.init = function (t, e, i) { + var n = e.components.Transform.position.x, + o = e.components.Transform.position.z; + (this.body.x = n), (this.body.bottom = 0), (this.body.z = t.z - o + 6), i.offsetX && (this.body.x = i.offsetX), i.flip && (this.body.x *= -1); + var s = t.game.pool.get(xt); + s.reset(), (s.body.width = 0.2), (s.body.height = this.body.height), (s.body.depth = 0.7 * this.body.depth), (s.body.x = this.body.left), (s.body.y = this.body.y), (s.body.z = this.body.z), t.game.addChild(s); + var a = t.game.pool.get(xt); + a.reset(), (a.body.width = 0.2), (a.body.height = this.body.height), (a.body.depth = 0.7 * this.body.depth), (a.body.x = this.body.right), (a.body.y = this.body.y), (a.body.z = this.body.z), t.game.addChild(a); + }), + Ramp + ); + })(v.a), + xt = (function (e) { + function Side() { + var t; + return (t = e.call(this) || this).add(it), t; + } + return ( + Ramp_inheritsLoose(Side, e), + (Side.prototype.reset = function () { + (this.body.deco = !1), this.body.box.size.reset(), this.body.box.center.reset(); + }), + Side + ); + })(v.a); + var Ct = (function (e) { + var t, i; + + function LightSignal() { + var t; + return ( + (t = e.call(this) || this).add(it, { + ghost: !0, + soft: !0, + }), + (t.body.width = 4), + (t.body.height = 42), + (t.body.depth = 4), + (t.model = H.getEntityClone("/lightSignal")), + (t.model.ry = Math.PI), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return ( + (i = e), + ((t = LightSignal).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (LightSignal.prototype.init = function (t, e, i) { + (this.body.x = e.components.Transform.position.x), i.flip && (this.body.x *= -1), (this.body.bottom = 0), (this.body.z = t.z - e.components.Transform.position.z); + }), + LightSignal + ); + })(v.a), + St = (function () { + function Curve() {} + return ( + (Curve.calculateJumpVerticalSpeed = function (t, e) { + if (t < 0) throw new Error("Jump height cannot be negative"); + return Math.sqrt(2 * t * e); + }), + (Curve.calculateJumpLength = function (t, e, i) { + return (2 * t * this.calculateJumpVerticalSpeed(e)) / i; + }), + (Curve.linear = function (t) { + return t; + }), + (Curve.expoIn = function (t) { + return 0 === t ? t : Math.pow(2, 10 * (t - 1)); + }), + (Curve.expoOut = function (t) { + return 1 === t ? t : 1 - Math.pow(2, -10 * t); + }), + (Curve.expoInOut = function (t) { + return 0 === t || 1 === t ? t : t < 0.5 ? 0.5 * Math.pow(2, 20 * t - 10) : -0.5 * Math.pow(2, 10 - 20 * t) + 1; + }), + (Curve.sineIn = function (t) { + var e = Math.cos(t * Math.PI * 0.5); + return Math.abs(e) < 1e-14 ? 1 : 1 - e; + }), + (Curve.sineOut = function (t) { + return Math.sin((t * Math.PI) / 2); + }), + (Curve.sineInOut = function (t) { + return -0.5 * (Math.cos(Math.PI * t) - 1); + }), + (Curve.quadIn = function (t) { + return t * t; + }), + (Curve.quadOut = function (t) { + return -t * (t - 2); + }), + (Curve.quadInOut = function (t) { + return (t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1); + }), + (Curve.quartIn = function (t) { + return Math.pow(t, 4); + }), + (Curve.quartOut = function (t) { + return Math.pow(t - 1, 3) * (1 - t) + 1; + }), + (Curve.quartInOut = function (t) { + return t < 0.5 ? 8 * Math.pow(t, 4) : -8 * Math.pow(t - 1, 4) + 1; + }), + (Curve.quintIn = function (t) { + return t * t * t * t * t; + }), + (Curve.quintOut = function (t) { + return --t * t * t * t * t + 1; + }), + (Curve.quintInOut = function (t) { + return (t *= 2) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2); + }), + (Curve.backInOut = function (t) { + var e = 2.5949095; + return (t *= 2) < 1 ? t * t * ((e + 1) * t - e) * 0.5 : 0.5 * ((t -= 2) * t * ((e + 1) * t + e) + 2); + }), + (Curve.backIn = function (t) { + return t * t * (2.70158 * t - 1.70158); + }), + (Curve.backOut = function (t) { + return --t * t * (2.70158 * t + 1.70158) + 1; + }), + (Curve.circInOut = function (t) { + return (t *= 2) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }), + (Curve.circIn = function (t) { + return 1 - Math.sqrt(1 - t * t); + }), + (Curve.circOut = function (t) { + return Math.sqrt(1 - --t * t); + }), + (Curve.elasticOut = function (t) { + return Math.sin((-13 * (t + 1) * Math.PI) / 2) * Math.pow(2, -10 * t) + 1; + }), + (Curve.elasticIn = function (t) { + return Math.sin((13 * t * Math.PI) / 2) * Math.pow(2, 10 * (t - 1)); + }), + (Curve.elasticInOut = function (t) { + return t < 0.5 ? 0.5 * Math.sin(((13 * Math.PI) / 2) * 2 * t) * Math.pow(2, 10 * (2 * t - 1)) : 0.5 * Math.sin(((-13 * Math.PI) / 2) * (2 * t - 1 + 1)) * Math.pow(2, -10 * (2 * t - 1)) + 1; + }), + Curve + ); + })(); + var zt = (function (n) { + var t, e; + + function Halo(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).view = new v.a()), + (i.halo = H.getEntityCloneBlend("/powBoost", "effects_tex", 0.95, 1)), + i.view.addChild(i.halo), + (i.view.z = -3), + (i.view.ry = Math.PI), + t.addChild(i.view), + i.reset(), + i + ); + } + (e = n), ((t = Halo).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Halo.prototype; + return ( + (i.reset = function () { + (this.scaleStart = 0), (this.scaleEnd = 1), (this.maxDistance = 500), (this.rotationSpeed = -0.03); + }), + (i.update = function (t) { + this.updateHaloScale(), this.updateViewScale(), this.rotationSpeed && (this.halo.rotation.z += t * this.rotationSpeed); + }), + (i.updateHaloScale = function (t) { + var e = this.entity.game.stats.z + 20 - this.entity.body.z, + i = 1.5 + 0.5 * Math.sin(0.03 * e); + this.halo.scale.set(i); + }), + (i.updateViewScale = function () { + var t = this.entity.game.stats.z - 10 - this.entity.body.z, + e = 1 - V.clamp(t / this.maxDistance), + i = St.backOut(e), + n = this.scaleStart + (this.scaleEnd - this.scaleStart) * i; + this.view.scale.set(n); + }), + Halo + ); + })(J.a); + + function Pickup_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Tt = (function (e) { + function Pickup() { + var t; + return ( + (t = e.call(this) || this).add(it, { + ghost: !0, + }), + t.add(st), + t.add(dt, { + rotationSpeed: -0.03, + }), + t.add(zt, { + type: "rays", + }), + (t.body.width = 12), + (t.body.height = 12), + (t.body.depth = 12), + t + ); + } + Pickup_inheritsLoose(Pickup, e); + var t = Pickup.prototype; + return ( + (t.init = function (t, e) { + this.addChild(this.model); + }), + (t.onCollect = function (t) { + !!this.game.hero[this.type] && this.game.hero[this.type].turnOn(), + "key" === this.type && (this.game.stats.keys += 1), + "jetpack" !== this.type && this.game.sfx.play("pickup_powerup"), + this.game.hero.popPickup && this.game.hero.popPickup.play(); + }), + Pickup + ); + })(v.a), + Pt = (function (e) { + function PickupJetpack() { + var t; + return ((t = e.call(this) || this).type = "jetpack"), (t.model = H.getEntityCloneOpaque("/powerups_jetpack", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupJetpack, e), PickupJetpack; + })(Tt), + Mt = (function (e) { + function PickupPogo() { + var t; + return ((t = e.call(this) || this).type = "pogo"), (t.model = H.getEntityCloneOpaque("/powerups_rocketPogo", "props_tex")), t.model.scale.set(1.75), t; + } + return Pickup_inheritsLoose(PickupPogo, e), PickupPogo; + })(Tt), + Et = (function (e) { + function PickupMagnet() { + var t; + return ((t = e.call(this) || this).type = "magnet"), (t.model = H.getEntityCloneOpaque("/powerups_coinMagnet", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupMagnet, e), PickupMagnet; + })(Tt), + Ot = (function (e) { + function PickupSneakers() { + var t; + return ((t = e.call(this) || this).type = "sneakers"), (t.model = H.getEntityCloneOpaque("/powerups_superSneakers", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupSneakers, e), PickupSneakers; + })(Tt), + Rt = (function (e) { + function PickupMultiplier() { + var t; + return ((t = e.call(this) || this).type = "multiplier"), (t.model = H.getEntityCloneOpaque("/powerups_2xMultiplier", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupMultiplier, e), PickupMultiplier; + })(Tt), + It = (function (e) { + function PickupLetter() { + var t; + return ((t = e.call(this) || this).type = "letter"), (t.model = H.getEntityCloneOpaque("/letters/A", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupLetter, e), PickupLetter; + })(Tt), + Lt = (function (n) { + function PickupMysteryBox() { + var t; + ((t = n.call(this) || this).type = "mysteryBox"), (t.model = new v.a()), (t.base = H.getEntityCloneOpaque("/mysteryBox_base/mysteryBox_base_default", "props_tex")), t.model.addChild(t.base); + var e = H.getEntity("/containers/mysteryBox_base/mysteryBox_lid"), + i = H.findEntity("mysteryBox_lid_default", e); + return (t.lid = H.getEntityCloneOpaque(i, "props_tex")), (t.lid.x = 0.8), (t.lid.z = -0.8), t.model.addChild(t.lid), t; + } + return Pickup_inheritsLoose(PickupMysteryBox, n), PickupMysteryBox; + })(Tt), + jt = (function (e) { + function PickupKey() { + var t; + return ((t = e.call(this) || this).type = "key"), (t.model = H.getEntityCloneOpaque("/currency_key", "props_tex")), t.model.scale.set(1.5), t; + } + return Pickup_inheritsLoose(PickupKey, e), PickupKey; + })(Tt), + Ft = { + jetpack: Pt, + pogo: Mt, + magnet: Et, + sneakers: Ot, + multiplier: Rt, + letter: It, + key: jt, + mysteryBox: Lt, + }, + At = [Rt, jt, Lt], + Dt = [Rt, Pt, Et, Ot], + Gt = { + Jetpack: Pt, + PogoStick: Mt, + CoinMagnet: Et, + SuperSneakers: Ot, + CoinMultiplier: Rt, + }; + (Tt.match = function (t) { + return !!t.name.match(/PickupSpawn/); + }), + (Tt.factory = function (t, e, i) { + if (!t.envTube) { + var n = t.z - e.components.Transform.position.z, + o = null, + s = ue.get(e, "components.PickupSpawnPoint.__spawnPointMode", ""), + a = ue.get(e, "components.PickupSpawnPoint.__forceSpawnPickupType", ""); + if ("WillForcePickupType" === s) o = Gt[a]; + else { + if (!t.game.route.canSpawn("pickup", n)) return; + var r = ue.get(e, "components.PickupSpawnPoint._spawnPointDifficulty", "Hard"); + if ( + L.range(0, 1) >= + { + Hard: 0.1, + Medium: 0.5, + Easy: 0.8, + }[r] + ) + return; + o = "Easy" === r ? L.item(At) : L.item(Dt); + } + if (o && o !== Lt && o !== jt && (o !== Pt || t.game.route.canSpawn("jetpack", n))) { + var h = t.game.pool.get(o), + d = e.components.Transform.position.x, + l = e.components.Transform.position.y; + (h.body.z = n), + (h.body.x = null !== i.offsetX ? i.offsetX : d), + (h.body.y = l), + i.flip && (h.body.x *= -1), + h.init(t, e), + t.game.addChild(h), + t.game.route.setSpawn("pickup", n - 2700), + o === Pt && t.game.route.setSpawn("jetpack", n - 4e3); + } + } + }), + (Tt.spawn = function (t, e) { + var i = e || Object.keys(Ft), + n = L.pick.apply(L, i), + o = Ft[n], + s = t.pool.get(o); + return s.init(), t.addChild(s), s; + }); + var Bt = {}, + Nt = Bt; + B.ground + ? ((Bt.newTrack = function () { + return H.getEntityClone("/track"); + }), + (Bt.newTrackShadowStart = function () { + return H.getEntityClone("/track_shadow_start"); + }), + (Bt.newTrackShadowMid = function () { + return H.getEntityClone("/track_shadow_mid"); + }), + (Bt.newTrackShadowEnd = function () { + return H.getEntityClone("/track_shadow_end"); + }), + (Bt.newTrackShadowShortStart = function () { + return H.getEntityClone("/track_shadow_short_start"); + }), + (Bt.newTrackShadowShortEnd = function () { + return H.getEntityClone("/track_shadow_short_end"); + }), + (Bt.newGround = function () { + return H.getEntityClone("/ground"); + }), + (Bt.newGroundShadowStart = function () { + return H.getEntityClone("/ground_shadow_start"); + }), + (Bt.newGroundShadowMid = function () { + return H.getEntityClone("/ground_shadow_mid"); + }), + (Bt.newGroundShadowEnd = function () { + return H.getEntityClone("/ground_shadow_mid"); + }), + (Bt.newGroundShadowShortStart = function () { + return H.getEntityClone("/ground_shadow_short_start"); + }), + (Bt.newGroundShadowShortEnd = function () { + return H.getEntityClone("/ground_shadow_short_end"); + }), + (Bt.newGates = function () { + return H.getEntityClone("track_gates"); + }), + (Bt.newGatesShadow = function () { + return H.getEntityClone("track_gates_shadows"); + })) + : ((Bt.newTrack = function () { + return new v.a(); + }), + (Bt.newTrackShadowStart = function () { + return new v.a(); + }), + (Bt.newTrackShadowMid = function () { + return new v.a(); + }), + (Bt.newTrackShadowEnd = function () { + return new v.a(); + }), + (Bt.newTrackShadowShortStart = function () { + return new v.a(); + }), + (Bt.newTrackShadowShortEnd = function () { + return new v.a(); + }), + (Bt.newGround = function () { + return new v.a(); + }), + (Bt.newGroundShadowStart = function () { + return new v.a(); + }), + (Bt.newGroundShadowMid = function () { + return new v.a(); + }), + (Bt.newGroundShadowEnd = function () { + return new v.a(); + }), + (Bt.newGroundShadowShortStart = function () { + return new v.a(); + }), + (Bt.newGroundShadowShortEnd = function () { + return new v.a(); + }), + (Bt.newGates = function () { + return new v.a(); + }), + (Bt.newGatesShadow = function () { + return new v.a(); + })), + (Bt.spawn = function (t, e) { + void 0 === e && (e = {}), e.z || (e.z = t.z), e.l || (e.l = Bt.newTrack), e.m || (e.m = e.l), e.r || (e.r = e.m); + var i = t.game.pool.get(e.l); + t.game.addChild(i), (i.x = -B.laneWidth), (i.y = 0), (i.z = e.z), (i.ry = Math.PI); + var n = t.game.pool.get(e.m); + t.game.addChild(n), (n.x = 0), (n.y = 0), (n.z = e.z), (n.ry = Math.PI); + var o = t.game.pool.get(e.r); + t.game.addChild(o), (o.x = B.laneWidth), (o.y = 0), (o.z = e.z), (o.ry = Math.PI); + }), + (Bt.spawnGates = function (t, e) { + if ((void 0 === e && (e = {}), !t.name.match("tutorial"))) { + e.z || (e.z = t.z); + var i = t.game.pool.get(Bt.newGates); + t.game.addChild(i), (i.x = 0), (i.y = 0), (i.z = e.z), (i.ry = Math.PI); + } + }), + (Bt.mount = function (t) { + if ("intro" === t.name) { + for (var e = 0; e < 1; e++) + if (!t.floors[e]) { + var i = 2 * B.blockSize, + n = Bt.newTrack, + o = Bt.newTrack, + s = Bt.newTrack; + Bt.spawn(t, { + z: i, + l: n, + m: o, + r: s, + }); + } + } else if (t.name.match("default_short_1_track")) { + for (var a = t.blocks / 2, r = 0; r < a; r++) + if (!t.floors[r]) { + var h = 0 < r, + d = t.z - r * B.blockSize * 2, + l = h ? Bt.newGround : Bt.newTrack, + c = Bt.newTrack, + u = h ? Bt.newGround : Bt.newTrack; + Bt.spawn(t, { + z: d, + l: l, + m: c, + r: u, + }), + (t.floors[r] = !0); + } + } else if (t.name.match("default_1_track")) { + for (var p = t.blocks / 2, m = 0; m < p; m++) + if (!t.floors[m]) { + var f = t.name.match("_mid"), + g = t.z - m * B.blockSize * 2, + y = f ? Bt.newGround : Bt.newTrack, + v = Bt.newTrack, + _ = f ? Bt.newGround : Bt.newTrack; + Bt.spawn(t, { + z: g, + l: y, + m: v, + r: _, + }), + (t.floors[m] = !0); + } + } else if (t.name.match("default_short_2_tracks")) { + for (var b = t.blocks / 2, w = 0; w < b; w++) + if (!t.floors[w]) { + var k = t.name.match("_mid_") || t.name.match("_end"), + x = t.z - w * B.blockSize * 2, + C = Bt.newTrack, + S = k ? Bt.newGround : Bt.newTrack, + z = Bt.newTrack; + Bt.spawn(t, { + z: x, + l: C, + m: S, + r: z, + }), + (t.floors[w] = !0); + } + } else if (t.name.match("default_2_tracks")) { + for (var T = t.blocks / 2, P = 0; P < T; P++) + if (!t.floors[P]) { + var M = !t.name.match("_end"), + E = t.z - P * B.blockSize * 2, + O = Bt.newTrack, + R = M ? Bt.newGround : Bt.newTrack, + I = Bt.newTrack; + Bt.spawn(t, { + z: E, + l: O, + m: R, + r: I, + }), + (t.floors[P] = !0); + } + } else if ("default_tutorial" === t.name); + else if (!t.hasGround) + for (var L = t.blocks / 2, j = 0; j < L; j++) + if (!t.floors[j]) { + var F = t.z - j * B.blockSize * 2, + A = Bt.newTrack, + D = Bt.newTrack, + G = Bt.newTrack; + Bt.spawn(t, { + z: F, + l: A, + m: D, + r: G, + }), + (t.floors[j] = !0); + } + }); + var Xt = {}, + Yt = Xt; + + function Gates_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + B.fillers + ? ((Xt.newLow01Left = function () { + return H.getEntityClone("/low_01_left"); + }), + (Xt.newLow02Left = function () { + return H.getEntityClone("/low_02_left"); + }), + (Xt.newMed01Left = function () { + return H.getEntityClone("/med_01_left"); + }), + (Xt.newMed02Left = function () { + return H.getEntityClone("/med_02_left"); + }), + (Xt.newMed03Left = function () { + return H.getEntityClone("/med_03_left"); + }), + (Xt.newHigh01Left = function () { + return H.getEntityClone("/high_01_left"); + }), + (Xt.newHigh02Left = function () { + return H.getEntityClone("/high_02_left"); + }), + (Xt.newHigh03Left = function () { + return H.getEntityClone("/high_03_left"); + }), + (Xt.newLow01Right = function () { + return H.getEntityClone("/low_01_right", "", !1, R); + }), + (Xt.newLow02Right = function () { + return H.getEntityClone("/low_02_right", "", !1, R); + }), + (Xt.newMed01Right = function () { + return H.getEntityClone("/med_01_right"); + }), + (Xt.newMed02Right = function () { + return H.getEntityClone("/med_02_right"); + }), + (Xt.newMed03Right = function () { + return H.getEntityClone("/med_03_right"); + }), + (Xt.newHigh01Right = function () { + return H.getEntityClone("/high_01_right"); + }), + (Xt.newHigh02Right = function () { + return H.getEntityClone("/high_02_right"); + }), + (Xt.newHigh03Right = function () { + return H.getEntityClone("/high_03_right"); + })) + : ((Xt.newLow01Left = function () { + return new v.a(); + }), + (Xt.newLow02Left = function () { + return new v.a(); + }), + (Xt.newMed01Left = function () { + return new v.a(); + }), + (Xt.newMed02Left = function () { + return new v.a(); + }), + (Xt.newMed03Left = function () { + return new v.a(); + }), + (Xt.newHigh01Left = function () { + return new v.a(); + }), + (Xt.newHigh02Left = function () { + return new v.a(); + }), + (Xt.newHigh03Left = function () { + return new v.a(); + }), + (Xt.newLow01Right = function () { + return new v.a(); + }), + (Xt.newLow02Right = function () { + return new v.a(); + }), + (Xt.newMed01Right = function () { + return new v.a(); + }), + (Xt.newMed02Right = function () { + return new v.a(); + }), + (Xt.newMed03Right = function () { + return new v.a(); + }), + (Xt.newHigh01Right = function () { + return new v.a(); + }), + (Xt.newHigh02Right = function () { + return new v.a(); + }), + (Xt.newHigh03Right = function () { + return new v.a(); + })), + (Xt.spawn = function (t, e) { + void 0 === e && (e = {}), e.z || (e.z = t.z), e.l || (e.l = Xt.newLow01Left), e.r || (e.r = Xt.newLow01Right); + var i = t.game.pool.get(e.l); + t.game.addChild(i), (i.x = 0), (i.y = 0), (i.z = e.z), (i.ry = Math.PI); + var n = t.game.pool.get(e.r); + t.game.addChild(n), (n.x = 0), (n.y = 0), (n.z = e.z), (n.ry = Math.PI); + }), + (Xt.mount = function (t) { + if (t.name.match("default_short_1_track")) { + for (var e = t.blocks / 2, i = 0; i < e; i++) + if (!t.fillers[i]) { + var n = t.z - i * B.blockSize * 2, + o = Xt.newMed02Left, + s = Xt.newMed02Right; + Xt.spawn(t, { + z: n, + l: o, + r: s, + }); + } + } else if (t.name.match("default_short_2_tracks")) { + for (var a = t.blocks / 2, r = 0; r < a; r++) + if (!t.fillers[r]) { + var h = t.z - r * B.blockSize * 2, + d = Xt.newLow02Left, + l = Xt.newHigh02Right; + Xt.spawn(t, { + z: h, + l: d, + r: l, + }); + } + } else if (t.name.match("default_2_tracks")) { + for (var c = t.blocks / 2, u = 0; u < c; u++) + if (!t.fillers[u]) { + var p = t.z - u * B.blockSize * 2, + m = Xt.newLow01Left, + f = Xt.newHigh01Right; + Xt.spawn(t, { + z: p, + l: m, + r: f, + }); + } + } else + for (var g = t.blocks / 2, y = L.item(["Low", "Med", "High"]), v = "Low" == y ? ["01", "02"] : ["01", "02", "03"], _ = 0; _ < g; _++) + if (!t.fillers[_]) { + var b = L.item(v), + w = Xt["new" + y + b + "Left"], + k = Xt["new" + y + b + "Right"], + x = t.z - _ * B.blockSize * 2; + Xt.spawn(t, { + z: x, + l: w, + r: k, + }), + "Low" === y && "1103_seoul" === B.env && (t.floors[_] = !0); + } + }); + var Ht = (function (e) { + function Gates() { + var t; + return ( + (t = e.call(this) || this).add(it, { + noView: !0, + trigger: !0, + }), + (t.body.width = 80), + (t.body.height = 50), + (t.body.depth = 120), + (t.lowCamera = !0), + t + ); + } + return ( + Gates_inheritsLoose(Gates, e), + (Gates.prototype.init = function (t, e) { + (this.body.x = 0), + (this.body.bottom = 0), + (this.body.z = t.z - e.components.Transform.position.z - 2 * B.blockSize), + this.model && ((this.model.ry = Math.PI), (this.model.y = 0.5 * -this.body.height), (this.model.z = 1.45 * this.body.depth), this.addChild(this.model)); + var i = t.game.pool.get(Ut); + (i.body.width = this.body.width), (i.body.height = 16), (i.body.depth = 0.9 * this.body.depth), (i.body.x = 0), (i.body.bottom = 37), (i.body.z = this.body.z), t.game.addChild(i); + var n = t.game.pool.get(Ut); + (n.body.width = 20), (n.body.height = i.body.bottom), (n.body.depth = 0.9 * this.body.depth), (n.body.bottom = 0), (n.body.right = 1.5 * -B.laneWidth), (n.body.z = this.body.z), t.game.addChild(n); + var o = t.game.pool.get(Ut); + (o.body.width = 20), (o.body.height = i.body.bottom), (o.body.depth = 0.9 * this.body.depth), (o.body.bottom = 0), (o.body.x = 0), (o.body.z = this.body.z), t.game.addChild(o); + var s = t.game.pool.get(Ut); + (s.body.width = 20), + (s.body.height = i.body.bottom), + (s.body.depth = 0.9 * this.body.depth), + (s.body.bottom = 0), + (s.body.left = 1.5 * B.laneWidth), + (s.body.z = this.body.z), + t.game.addChild(s), + (this.colL = n), + (this.colR = s), + (this.colM = o), + (this.ceiling = i); + var a = t.game.pool.get(Gates.newEnvironment); + (a.ry = Math.PI), + (a.z = t.z), + t.game.addChild(a), + t.setFillersByPosition(t.z, t.z - 4 * B.blockSize), + Nt.spawnGates(t), + 4 < t.blocks && + (Nt.spawn(t, { + z: t.z - 4 * B.blockSize, + }), + Yt.spawn(t, { + z: t.z - 4 * B.blockSize, + l: Yt.newHigh01Left, + r: Yt.newHigh01Right, + })); + }), + Gates + ); + })(v.a); + Ht.newEnvironment = function () { + return H.getEntityClone("/gates_base"); + }; + var Ut = (function (e) { + function GatePart() { + var t; + return (t = e.call(this) || this).add(it), t; + } + return Gates_inheritsLoose(GatePart, e), (GatePart.prototype.reset = function () {}), GatePart; + })(v.a), + Vt = (function (i) { + function GatesSides() { + var t; + return ((t = i.call(this) || this).model = H.getEntityClone("/gates_sides")), (t.model.ry = Math.PI), (t.model.y = 0.5 * -t.body.height), (t.model.sx = 0.9), (t.model.x = -1), t; + } + return ( + Gates_inheritsLoose(GatesSides, i), + (GatesSides.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), + (this.colM.body.width = 20), + (this.colM.body.x = 0), + (this.colL.body.width = 20), + (this.colL.body.right = 1.5 * -B.laneWidth), + (this.colR.body.width = 20), + (this.colR.body.left = 1.5 * B.laneWidth); + }), + GatesSides + ); + })(Ht), + qt = { + gates_mid_group_place: (function (i) { + function GatesMid() { + var t; + return ((t = i.call(this) || this).model = H.getEntityClone("/gates_mid")), (t.model.ry = Math.PI), (t.model.y = 0.5 * -t.body.height), t.addChild(t.model), t; + } + return ( + Gates_inheritsLoose(GatesMid, i), + (GatesMid.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), + (this.colM.body.width = 0), + (this.colM.body.x = 999), + (this.colL.body.width = 60), + (this.colL.body.right = 0.5 * -B.laneWidth), + (this.colR.body.width = 60), + (this.colR.body.left = 0.5 * B.laneWidth); + }), + GatesMid + ); + })(Ht), + gates_left_group_place: (function (i) { + function GatesLeft() { + var t; + return ((t = i.call(this) || this).model = H.getEntityClone("/gates_left")), (t.model.ry = Math.PI), (t.model.y = 0.5 * -t.body.height), t.addChild(t.model), t; + } + return ( + Gates_inheritsLoose(GatesLeft, i), + (GatesLeft.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), + (this.colM.body.width = 0), + (this.colM.body.x = 999), + (this.colL.body.width = 60), + (this.colL.body.right = 1.5 * -B.laneWidth), + (this.colR.body.width = 60), + (this.colR.body.left = 0.5 * -B.laneWidth); + }), + GatesLeft + ); + })(Ht), + gates_right_group_place: (function (i) { + function GatesRight() { + var t; + return ((t = i.call(this) || this).model = H.getEntityClone("/gates_right")), (t.model.ry = Math.PI), (t.model.y = 0.5 * -t.body.height), t.addChild(t.model), t; + } + return ( + Gates_inheritsLoose(GatesRight, i), + (GatesRight.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), + (this.colM.body.width = 0), + (this.colM.body.x = 999), + (this.colL.body.width = 60), + (this.colL.body.right = 0.5 * B.laneWidth), + (this.colR.body.width = 60), + (this.colR.body.left = 1.5 * B.laneWidth); + }), + GatesRight + ); + })(Ht), + gates_sides_group_place: Vt, + }; + + function StationPlatform_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + Ht.factory = function (t, e) { + var i = qt[e.name], + n = t.game.pool.get(i); + n.init(t, e), t.game.addChild(n); + }; + var Wt = (function (e) { + function StationPlatform() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 2), + (t.body.depth = 2 * B.blockSize), + (t.model = H.getEntityClone("station_platforms")), + (t.model.ry = Math.PI), + (t.model.z = B.blockSize), + t.addChild(t.model), + t + ); + } + return ( + StationPlatform_inheritsLoose(StationPlatform, e), + (StationPlatform.prototype.init = function (t, e, i) { + (this.body.back = i), (this.body.x = 0); + var n = t.game.pool.get(Zt); + n.reset(), (n.body.width = 20), (n.body.height = 9), (n.body.depth = this.body.depth), (n.body.x = -B.laneWidth), (n.body.bottom = 0), (n.body.z = this.body.z), t.game.addChild(n); + var o = t.game.pool.get(Zt); + o.reset(), (o.body.width = 20), (o.body.height = 9), (o.body.depth = this.body.depth), (o.body.x = B.laneWidth), (o.body.bottom = 0), (o.body.z = this.body.z), t.game.addChild(o); + }), + StationPlatform + ); + })(v.a), + Zt = (function (e) { + function StationPart() { + var t; + return (t = e.call(this) || this).add(it), t; + } + return ( + StationPlatform_inheritsLoose(StationPart, e), + (StationPart.prototype.reset = function () { + this.body.box.size.reset(), this.body.box.center.reset(); + }), + StationPart + ); + })(v.a); + + function StationEnvironment_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + Wt.factory = function (t, e, i) { + var n = e.components.Transform.position.z, + o = t.game.pool.get(Wt); + o.init(t, e, t.z - n), t.game.addChild(o); + var s = t.game.pool.get(Wt); + s.init(t, e, t.z - n - o.body.depth), t.game.addChild(s); + }; + var Kt = (function (e) { + function StationEnvironment() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !1, + }), + (t.body.width = 80), + (t.body.height = 4), + (t.body.depth = 4 * B.blockSize), + t + ); + } + return ( + StationEnvironment_inheritsLoose(StationEnvironment, e), + (StationEnvironment.prototype.init = function (t, e, i) { + void 0 === i && (i = !0); + var n = e.components.Environment, + o = n ? n._blockCount : 4, + s = e.components.Transform.position.z, + a = t.z - s; + t.name.match("short") || (a += 4 * B.blockSize), (this.body.depth = B.blockSize * o), (this.body.x = 0), (this.body.top = 86), (this.body.back = a); + var r = t.game.pool.get(Jt); + (r.body.z = a), t.game.addChild(r); + for (var h = 0.5 * (o - 2), d = 0; d < h; d++) { + var l = t.game.pool.get(Qt); + (l.body.z = a - B.blockSize - l.body.depth * d), t.game.addChild(l); + } + var c = t.game.pool.get($t); + if (((c.body.z = a - (o - 1) * B.blockSize), t.game.addChild(c), t.setFillersByPosition(this.body.back, this.body.front), i)) + for (var u = 0.5 * o, p = 0; p < u; p++) { + var m = t.game.pool.get(Wt); + m.init(t, e, a - m.body.depth * p), t.game.addChild(m); + } + for (var f = 0.5 * o, g = 0; g < f; g++) { + var y = a - g * (2 * B.blockSize); + 0 === g + ? Nt.spawn(t, { + z: y, + l: Nt.newGroundShadowStart, + m: Nt.newTrackShadowStart, + r: Nt.newGroundShadowStart, + }) + : g < f - 1 + ? Nt.spawn(t, { + z: y, + l: Nt.newGroundShadowMid, + m: Nt.newTrackShadowMid, + r: Nt.newGroundShadowMid, + }) + : Nt.spawn(t, { + z: y, + l: Nt.newGroundShadowEnd, + m: Nt.newTrackShadowEnd, + r: Nt.newGroundShadowEnd, + }); + } + t.setFillersByPosition(this.body.back, this.body.front), t.setFloorsByPosition(this.body.back, this.body.front); + }), + StationEnvironment + ); + })(v.a), + Jt = (function (e) { + function StationStart() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 0), + (t.body.depth = B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("/station_start")), + (t.model.ry = Math.PI), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return StationEnvironment_inheritsLoose(StationStart, e), StationStart; + })(v.a), + Qt = (function (e) { + function StationMid() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 0), + (t.body.depth = 2 * B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("/station_mid")), + (t.model.ry = Math.PI), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return StationEnvironment_inheritsLoose(StationMid, e), StationMid; + })(v.a), + $t = (function (e) { + function StationEnd() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 0), + (t.body.depth = B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("/station_end")), + (t.model.ry = Math.PI), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return StationEnvironment_inheritsLoose(StationEnd, e), StationEnd; + })(v.a); + Kt.match = function (t) { + return !!t.components.Environment && 0 <= t.components.Environment._environmentKind._type.split(",").indexOf("Station"); + }; + var te = (function (e) { + var t, i; + + function StartBag() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + }), + (t.body.width = 4), + (t.body.height = 4), + (t.body.depth = 4), + (t.model = H.getEntityCloneOpaque("/startScreen_bag/startScreen_bag_base", "props_tex")), + (t.model.ry = Math.PI), + (t.model.y = -1.3), + t.addChild(t.model), + t + ); + } + return ( + (i = e), + ((t = StartBag).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (StartBag.prototype.init = function (t, e) { + (this.body.x = e.components.Transform.position.x), (this.body.bottom = 0), (this.body.z = t.z - e.components.Transform.position.z); + }), + StartBag + ); + })(v.a); + + function Obstacle_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var ee = (function (e) { + function Obstacle() { + var t; + return ( + (t = e.call(this) || this).add(it, { + ghost: !0, + }), + (t.body.width = 18), + (t.body.height = 14), + (t.body.depth = 1), + (t.model = null), + t + ); + } + return ( + Obstacle_inheritsLoose(Obstacle, e), + (Obstacle.prototype.init = function (t, e) { + (this.model.ry = Math.PI), (this.model.z = -4), this.addChild(this.model); + }), + Obstacle + ); + })(v.a), + ie = [ + (function (i) { + function ObstacleDumpster() { + var t; + return ((t = i.call(this) || this).body.soft = !1), (t.model = H.getEntityClone("dumpster")), t; + } + return ( + Obstacle_inheritsLoose(ObstacleDumpster, i), + (ObstacleDumpster.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), (this.body.height = 14), (this.model.y = 0.5 * -this.body.height); + }), + ObstacleDumpster + ); + })(ee), + (function (i) { + function ObstacleBush() { + var t; + return ((t = i.call(this) || this).body.soft = !0), (t.model = H.getEntityClone("bush_1")), t; + } + return ( + Obstacle_inheritsLoose(ObstacleBush, i), + (ObstacleBush.prototype.init = function (t, e) { + i.prototype.init.call(this, t, e), (this.body.height = 12), (this.model.y = 0.5 * -this.body.height); + }), + ObstacleBush + ); + })(ee), + ]; + (ee.match = function (t) { + return t.name.match(/obstacle_group/); + }), + (ee.factory = function (t, e, i) { + var n = L.pick.apply(L, ie), + o = t.game.pool.get(n), + s = e.components.Transform.position.x, + a = e.components.Transform.position.z; + (o.body.z = t.z - a), (o.body.x = s), (o.body.bottom = 0), i.offsetX && (o.body.x = i.offsetX), i.flip && (o.body.x *= -1), o.init(t, e), t.game.addChild(o); + }); + var ne = (function (e) { + var t, i; + + function Pillar() { + var t; + return (t = e.call(this) || this).add(it), (t.body.width = 9), (t.body.height = 80), (t.body.depth = 9), (t.model = H.getEntityClone("pillar")), (t.model.ry = Math.PI), (t.model.y = 0.5 * -t.body.height), t.addChild(t.model), t; + } + return ( + (i = e), + ((t = Pillar).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (Pillar.prototype.init = function (t, e) { + this.addChild(this.model); + }), + Pillar + ); + })(v.a); + + function PillarsEnvironment_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + (ne.factory = function (t, e, i) { + var n = t.game.pool.get(ne), + o = e.components.Transform.position.x, + s = e.components.Transform.position.z; + (n.body.z = t.z - s), (n.body.x = o), (n.body.bottom = 0), i.offsetX && (n.body.x = i.offsetX), i.flip && (n.body.x *= -1), n.init(t, e), t.game.addChild(n); + }), + (ne.match = function (t) { + return t.name.match(/pillar_group_place/); + }); + var oe = (function (e) { + function PillarsEnvironment() { + var t; + return (t = e.call(this) || this).add(it), (t.body.width = 80), (t.body.height = 20), (t.body.depth = B.blockSize), (t.body.top = 90), t; + } + return ( + PillarsEnvironment_inheritsLoose(PillarsEnvironment, e), + (PillarsEnvironment.prototype.init = function (t, e) { + var i = e.components.Transform.position.z, + n = e.components.Environment._blockCount; + (this.body.x = 0), (this.body.depth = B.blockSize * n), (this.body.back = t.z - i); + var o = t.game.pool.get(se); + (o.body.back = this.body.back), + t.game.addChild(o), + Nt.spawn(t, { + z: o.body.back, + l: Nt.newTrackShadowShortStart, + m: Nt.newGroundShadowShortStart, + r: Nt.newTrackShadowShortStart, + }); + for (var s = 0; s < n - 2; s++) { + var a = t.game.pool.get(ae); + (a.body.back = o.body.back - B.blockSize * (s + 1)), + t.game.addChild(a), + Nt.spawn(t, { + z: a.body.back, + l: Nt.newTrackShadowMid, + m: Nt.newGroundShadowMid, + r: Nt.newTrackShadowMid, + }); + } + var r = t.game.pool.get(re); + (r.body.back = o.body.back - B.blockSize * (n - 1)), + t.game.addChild(r), + Nt.spawn(t, { + z: r.body.back, + l: Nt.newTrackShadowShortEnd, + m: Nt.newGroundShadowShortEnd, + r: Nt.newTrackShadowShortEnd, + }); + }), + PillarsEnvironment + ); + })(v.a), + se = (function (e) { + function PillarsEnvironmentStart() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 1), + (t.body.depth = B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("pillars_start")), + (t.model.ry = Math.PI), + (t.model.z = 0.5 * t.body.depth), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return PillarsEnvironment_inheritsLoose(PillarsEnvironmentStart, e), PillarsEnvironmentStart; + })(v.a), + ae = (function (e) { + function PillarsEnvironmentMid() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 1), + (t.body.depth = B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("pillars_mid")), + (t.model.ry = Math.PI), + (t.model.z = 0.5 * t.body.depth), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return PillarsEnvironment_inheritsLoose(PillarsEnvironmentMid, e), PillarsEnvironmentMid; + })(v.a), + re = (function (e) { + function PillarsEnvironmentEnd() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 1), + (t.body.depth = B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("pillars_end")), + (t.model.ry = Math.PI), + (t.model.z = 0.5 * t.body.depth), + (t.model.y = 0.5 * -t.body.height), + t.addChild(t.model), + t + ); + } + return PillarsEnvironment_inheritsLoose(PillarsEnvironmentEnd, e), PillarsEnvironmentEnd; + })(v.a); + + function TubeEnvironment_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + oe.match = function (t) { + return !!t.components.Environment && 0 <= t.components.Environment._environmentKind._type.split(",").indexOf("Pillars"); + }; + var he = (function (e) { + function TubeEnvironment() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !1, + }), + (t.body.width = 80), + (t.body.height = 16), + (t.body.depth = B.blockSize), + (t.body.top = 88), + t + ); + } + return ( + TubeEnvironment_inheritsLoose(TubeEnvironment, e), + (TubeEnvironment.prototype.init = function (t, e) { + var i = e.components.Transform.position.z; + t.game.route.hasTube = !0; + var n = t.blocks; + e.components.Environment && (n = e.components.Environment._blockCount), (this.body.x = 0), (this.body.depth = B.blockSize * n), (this.body.back = t.z - i); + for (var o = 0.5 * n, s = 0; s < o; s++) { + var a = t.game.pool.get(de); + (a.body.back = this.body.back - 2 * B.blockSize * s), t.game.addChild(a); + var r = a.body.back; + 0 === s + ? Nt.spawn(t, { + z: r, + l: Nt.newTrackShadowStart, + }) + : s < o - 1 + ? Nt.spawn(t, { + z: r, + l: Nt.newTrackShadowMid, + }) + : Nt.spawn(t, { + z: r, + l: Nt.newTrackShadowEnd, + }); + } + }), + TubeEnvironment + ); + })(v.a), + de = (function (e) { + function TubeEnvironmentBlock() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + noView: !0, + }), + (t.body.width = 80), + (t.body.height = 0), + (t.body.depth = 2 * B.blockSize), + (t.body.bottom = 0), + (t.model = H.getEntityClone("tube")), + (t.model.ry = Math.PI), + (t.model.z = 0.5 * t.body.depth), + (t.model.y = 0.5 * -t.body.height), + (t.model.view3d.state.culling = !1), + t.addChild(t.model), + "1103_seoul" === B.env && + ((t.model001 = H.getEntityClone("tube001")), + (t.model001.ry = t.model.ry), + (t.model001.z = t.model.z), + (t.model001.y = t.model.y), + (t.model001.view3d.state.blend = !0), + (t.model001.view3d.material.opacity = 0.7), + (t.model001.view3d.state.culling = !1), + t.addChild(t.model001), + (t.model002 = H.getEntityClone("tube002")), + (t.model002.ry = t.model.ry), + (t.model002.z = t.model.z), + (t.model002.y = t.model.y), + (t.model002.view3d.state.culling = !1), + t.addChild(t.model002)), + t + ); + } + return TubeEnvironment_inheritsLoose(TubeEnvironmentBlock, e), TubeEnvironmentBlock; + })(v.a); + (he.match = function (t) { + return !!t.components.Environment && 0 <= t.components.Environment._environmentKind._type.split(",").indexOf("Tube"); + }), + (he.factory = function (t, e, i) { + t.game.route.setSpawnDistance("tube", 1e3); + var n = t.game.pool.get(he); + return n.init(t, e, i), t.game.addChild(n), n; + }); + var le = (function (e) { + var t, i; + + function Trigger() { + var t; + return ((t = e.call(this) || this).tutorialTrigger = !0), t.add(it), (t.body.width = 60), (t.body.height = 30), (t.body.depth = 1), (t.body.trigger = !0), (t.view = H.box(t.body.box)), t; + } + (i = e), ((t = Trigger).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i); + var n = Trigger.prototype; + return ( + (n.reset = function () {}), + (n.init = function (t, e) { + this.type = e.name.split("_").pop(); + }), + Trigger + ); + })(v.a); + (le.match = function (t) { + return t.name.match(/Trigger_/); + }), + (le.factory = function (t, e, i) { + var n = t.game.pool.get(le), + o = e.components.Transform.position.x, + s = e.components.Transform.position.z; + (n.body.z = t.z - s), (n.body.x = o), (n.body.bottom = 0), i.offsetX && (n.body.x = i.offsetX), i.flip && (n.body.x *= -1), n.init(t, e), t.game.addChild(n); + }); + var ce = (function (e) { + var t, i; + + function Checkpoint() { + var t; + return ((t = e.call(this) || this).checkpoint = !0), t.add(it), (t.body.width = 2), (t.body.height = 20), (t.body.depth = 2), (t.body.deco = !0), (t.view = H.box(t.body.box)), t; + } + (i = e), ((t = Checkpoint).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i); + var n = Checkpoint.prototype; + return (n.reset = function () {}), (n.init = function (t, e) {}), Checkpoint; + })(v.a); + (ce.match = function (t) { + return t.name.match(/checkpoint_/); + }), + (ce.factory = function (t, e, i) { + var n = t.game.pool.get(ce), + o = e.components.Transform.position.x, + s = e.components.Transform.position.z; + return (n.body.z = t.z - s), (n.body.x = o), (n.body.bottom = 0), i.offsetX && (n.body.x = i.offsetX), i.flip && (n.body.x *= -1), n.init(t, e), t.game.addChild(n), n; + }); + var ue = (function () { + function Node() {} + return ( + (Node.getEntityClass = function (t) { + return t.name.match(/train_ramp/) + ? kt + : t.name.match(/trains_(\d)_/) + ? mt + : t.name.match(/train_sub_(\d)_/) + ? mt + : t.name.match(/lightSignal/) + ? Ct + : t.name.match(/blocker/) + ? _t + : pt.match(t) + ? pt + : Tt.match(t) + ? Tt + : t.name.match(/gates_(.*?)_group_place/) + ? Ht + : t.name.match(/bag_place/) + ? te + : ee.match(t) + ? ee + : ne.match(t) + ? ne + : Kt.match(t) + ? Kt + : oe.match(t) + ? oe + : he.match(t) + ? he + : le.match(t) + ? le + : ce.match(t) + ? ce + : null; + }), + (Node.comp = function (t, e) { + return t.components[e]; + }), + (Node.get = function (t, e, i) { + if ((void 0 === i && (i = null), !t)) return i; + "string" == typeof e && (e = e.split(".")); + var n = e.shift(); + return e.length ? this.get(t[n], e, i) : void 0 !== t[n] ? t[n] : i; + }), + (Node.environment = function (t) { + if (t.components.Environment) return t; + for (var e = t.children.length; e--; ) { + var i = this.environment(t.children[e]); + if (i) return i; + } + return null; + }), + (Node.environmentType = function (t) { + return t.components.Environment ? t.components.Environment._environmentKind._type.split(",") : null; + }), + (Node.environmentKinds = function (t) { + var e = t.components.RouteChunk._limitedAllowedEnvironmentKinds, + i = Random.item(e)._type.split(","); + this.environment = i || []; + }), + Node + ); + })(); + var pe = (function (e) { + var t, i; + + function Cube() { + var t; + return (t = e.call(this) || this).add(it), t; + } + return ( + (i = e), + ((t = Cube).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (Cube.prototype.reset = function () { + (this.body.deco = !1), this.body.box.size.reset(), this.body.box.center.reset(); + }), + Cube + ); + })(v.a); + var me = (function (e) { + var t, i; + + function Logo() { + var t; + return ((t = e.call(this) || this).model = H.getEntityCloneBlend("/train_start", "train_start", 0.999)), (t.model.ry = Math.PI), t.addChild(t.model), t; + } + return (i = e), ((t = Logo).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i), Logo; + })(v.a), + fe = null, + ge = (function () { + function Chunk() { + this.reset(); + } + var t = Chunk.prototype; + return ( + (t.reset = function () { + (this.node = null), (this.section = ""); + }), + (t.init = function (t, e, i, n) { + (this.game = t), + (this.node = i), + (this.name = this.node.name), + (this.entities = []), + (this.blocks = ue.get(i, "components.RouteChunk._blockCount", 0)), + (this.length = this.blocks * B.blockSize), + (this.z = -e), + (this.start = e), + (this.middle = this.start + 0.5 * this.length), + (this.end = this.start + this.length), + (this.offset = 0), + (this.index = n), + (this.fillers = {}), + (this.floors = {}), + this.game.environment.setup(this), + this.mount(this.node, this, {}), + this.game.environment.mount(this), + B.blocks && this.addChunkDebugMarks(), + "intro" === this.name && this.mountIntro(); + }), + (t.mountIntro = function () { + fe || (fe = new me()), (fe.x = -20), (fe.y = 0), (fe.z = 30), this.game.addChild(fe), Nt.mount(this); + }), + (t.defineEnvironment = function () { + if (((this.envNode = ue.environment(this.node)), (this.environment = this.envNode ? ue.environmentType(this.envNode) : null), !this.environment)) + if (this.node.name.match(/tunnel/)) this.environment = ["Gates", "All"]; + else if (this.node.name.match(/epic/)) this.environment = ["Epic", "All"]; + else if (this.node.components.RouteChunk) { + var t = this.node.components.RouteChunk._limitedAllowedEnvironmentKinds, + e = t.length ? L.item(t)._type.split(",") : ["Fillers", "All"]; + this.environment = e || []; + } else this.environment = []; + (this.envTube = !1), (this.envStation = !1), (this.envEpic = !1), (this.envGates = !1), (this.envEmpty = !1); + var i = this.lastTube; + B.forceTube || (0 <= this.environment.indexOf("Tube") && this.z < i) + ? ((this.envTube = !0), (this.lastTube = this.z)) + : 0 <= this.environment.indexOf("Station") + ? (this.envStation = !0) + : 0 <= this.environment.indexOf("Epic") + ? (this.envEpic = !0) + : 0 <= this.environment.indexOf("Gates") + ? (this.envGates = !0) + : 0 <= this.environment.indexOf("Empty") && (this.envEmpty = !0); + }), + (t.mount = function (t, e) { + if ("intro" !== this.name || !t.name.match("lightSignal") || B.loadAll) + if ((void 0 === (e = Object.assign({}, e || {})).flip && (e.flip = 0), void 0 === e.offsetX && (e.offsetX = null), ue.comp(t, "Randomizer"))) { + var i = L.pick.apply(L, t.children); + this.mount(i, e); + } else { + if (ue.comp(t, "RandomizeOffset")) { + var n = t.components.RandomizeOffset.randomOffsets.left, + o = t.components.RandomizeOffset.randomOffsets.mid, + s = t.components.RandomizeOffset.randomOffsets.right, + a = []; + n && a.push(-20), o && a.push(0), s && a.push(20), a.length && (e.offsetX = L.pick.apply(L, a)); + } + ue.comp(t, "Mirror") && (e.flip = L.pick(0, 1)); + var r = ue.getEntityClass(t); + if (r) { + var h = null; + if ( + (r.factory ? (h = r.factory(this, t, e)) : ((h = this.game.pool.get(r)).init(this, t, e), this.game.addChild(h)), + h && h.checkpoint && (this.checkpoints || (this.checkpoints = []), this.checkpoints.push(h)), + h && ue.comp(t, "Environment")) + ) { + for (var d = -h.body.back - this.start, l = -h.body.front - this.start, c = 2 * B.blockSize, u = Math.round(d / c), p = Math.round(l / c), m = u; m < p; m++) this.fillers[m] = !0; + for (var f = u; f < p; f++) this.floors[f] = !0; + } + } + if (t.children) for (var g = t.children.length; g--; ) this.mount(t.children[g], e); + } + }), + (t.setFillersByPosition = function (t, e, i) { + void 0 === i && (i = !0); + for (var n = -t - this.start, o = -e - this.start, s = 2 * B.blockSize, a = Math.round(n / s), r = Math.round(o / s), h = a; h < r; h++) this.fillers[h] = !0; + }), + (t.setFloorsByPosition = function (t, e, i) { + void 0 === i && (i = !0); + for (var n = -t - this.start, o = -e - this.start, s = 2 * B.blockSize, a = Math.round(n / s), r = Math.round(o / s), h = a; h < r; h++) this.floors[h] = !0; + }), + (t.addChunkDebugMarks = function () { + for (var t = this.blocks; t--; ) { + var e = this.game.pool.get(pe); + (e.body.deco = !0), (e.body.width = 80), (e.body.height = t ? 2 : 20), (e.body.depth = 0.1), (e.body.x = 0), (e.body.bottom = 0), (e.body.z = -this.start - t * B.blockSize), this.game.addChild(e); + } + }), + (t.clean = function () { + for (var t = this.entities.length; t--; ) this.game.removeChild(this.entities[t]); + this.entities.length = 0; + }), + (t.triggerEnter = function () { + for (var t = this.entities.length; t--; ) { + var e = this.entities[t]; + e.triggerEnter && e.triggerEnter(); + } + }), + (t.triggerExit = function () {}), + (t.getLastCheckpointByPosition = function (t) { + if (!this.checkpoints) return null; + for (var e = null, i = this.checkpoints.length; i--; ) { + var n = this.checkpoints[i]; + n.body.z > t && (e = n); + } + return e || this.checkpoints[0]; + }), + Chunk + ); + })(), + ye = i(127), + ve = null, + _e = null, + be = null, + we = (function () { + function Data() {} + return ( + (Data.init = function (t) { + be = t; + }), + (Data.sectionMap = function () { + if (_e) return _e; + return ( + (_e = {}), + (function extract(t) { + if (void 0 !== t.name) _e[t.name] = t; + else for (var e in t) extract(t[e]); + })(ye), + _e + ); + }), + (Data.section = function (t) { + t = (t = t.replace("routeSection_", "")).replace("route_section_", ""); + var e = this.sectionMap(), + i = e[t] || e["route_section_" + t] || e["routeSection_" + t]; + if (!i) throw "Section data not found: " + t; + return i; + }), + (Data.sectionClone = function (t) { + var e = this.sectionMap(); + if (!e[t]) throw "Section data not found: " + t; + var i = e[t]; + return { + name: i.name, + start: i.start.slice(0), + mid: i.start.slice(0), + end: i.start.slice(0), + }; + }), + (Data.chunkMap = function (t) { + console.log("chunkMap " + t); + if (ve && !t) return ve; + ve = {}; + var e = {}; + for (var i in be) i.match("chunks_") && (e[i] = be[i].data); + return ( + (function extract(t, e) { + console.log(be); + if (!t || !e--) return; + if ("intro" === t.name) ve[t.name] = t; + else if (t.components && t.components.RouteChunk) { + var i = t.components.RouteChunk._reportedName || t.name; + ve[i] = t; + } else if (t.children) for (var n in t.children) extract(t.children[n], e); + else for (var o in t) extract(t[o], e); + })(e, 5), + ve + ); + }), + (Data.chunk = function (t) { + t = (t = t.replace("routeChunk_", "")).replace("route_chunk_", ""); + var e = this.chunkMap(), + i = e[t] || e["routeChunk_" + t] || e["route_chunk_" + t] || e["default_" + t]; + if (!i) throw "Chunk data not found: " + t; + return (i.__name = t), i; + }), + (Data.refreshCache = function () { + ve = this.chunkMap(!0); + }), + Data + ); + })(); + var ke = (function (n) { + var t, e; + + function LevelSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).entities = []), + (i.chunks = []), + (i.currentChunk = null), + (i.game = t.game), + i.game.onReset.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + (i.sequence = []), + (i.queued = []), + (i.onEnterChunk = new et.a("onEnterChunk", 1)), + (i.onExitChunk = new et.a("onExitChunk", 1)), + (i.onEnterTutorial = new et.a("onEnterTutorial")), + (i.onExitTutorial = new et.a("onExitTutorial")), + (i.countRemoveObsolete = 2), + (i.countUpdate = 0), + i + ); + } + (e = n), ((t = LevelSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = LevelSystem.prototype; + return ( + (i.reset = function () { + (this.chunks = []), (this.sequence = []), (this.queued = []), (this.nextPosition = 0); + var t = this.entities.slice(0); + (this.currentChunk = this.placeNextChunk("intro")), this.game.forceUpdate(), this.removeAllEntities(t); + }), + (i.entityAddedToGame = function (t) { + !t._CLASS_ID || t.player || t.follower || 0 <= this.entities.indexOf(t) || this._addEntity(t); + }), + (i.entityRemovedFromGame = function (t) { + !t._CLASS_ID || t.player || t.follower || t.particle || this.entities.indexOf(t) < 0 || this._removeEntity(t); + }), + (i._addEntity = function (t) { + t.player || t.follower || this.entities.push(t); + }), + (i._removeEntity = function (t, e) { + t.player || t.follower || t.particle || (void 0 === e && (e = this.entities.indexOf(t)), (t.z = 99999), t.body && (t.body.z = t.z), this.entities.splice(e, 1), this.game.removeChild(t), this.game.pool["return"](t)); + }), + (i.postupdate = function () { + this.game.state === pn.RUNNING && (0 < this.countRemoveObsolete-- || (this.removeObsoleteEntities(), (this.countRemoveObsolete = 5))); + }), + (i.preupdate = function () { + if ((this.game.state === pn.RUNNING || this.queued.length) && !(0 < this.countUpdate--)) { + this.countRemoveObsolete = 5; + var t = this.chunks[this.game.stats.chunkIndex]; + t || this.game.crash("No chunk found at current index:", this.game.stats.chunkIndex), + t !== this.currentChunk && + (this.currentChunk && (this.isTutorial() && this.onExitTutorial.dispatch(), this.onExitChunk.dispatch(this.currentChunk.name)), + (this.currentChunk = t), + this.onEnterChunk.dispatch(this.currentChunk.name), + this.isTutorial() && this.onEnterTutorial.dispatch()), + this.game.stats.setCurrentChunk(t), + this.game.stats.distance > t.end && (this.game.stats.chunkIndex += 1), + this.placeChunks(); + } + }), + (i.placeChunks = function () { + console.log("placeChunks"); + if (this.queued && this.queued.length) { + for (var t = this.queued.length; t--; ) this.placeNextChunk(this.queued[t]); + this.queued.length = 0; + } + for (var e = 20, i = this.game.stats.distance + B.visibleMaxDistance; this.nextPosition < i; ) e-- || this.game.crash("Too many chunks placed at same time"), this.placeNextChunk(this.name); + }), + (i.nextInSequence = function () { + this.sequence.length || (this.sequence = this.game.route.getSequence()); + var t = this.sequence.shift(), + e = "string" == typeof t ? t : L.item(e); + return we.chunk(e); + }), + (i.queueNextChunk = function (t) { + var e = t ? we.chunk(t) : this.nextInSequence(); + this.queued.unshift(e.__name); + }), + (i.placeNextChunk = function (t) { + var e = new ge(), + i = t ? we.chunk(t) : this.nextInSequence(); + if (!i) throw new Error("Chunk not found"); + return e.init(this.game, this.nextPosition, i, this.chunks.length), this.chunks.push(e), (this.nextPosition += e.length), e; + }), + (i.removeObsoleteEntities = function () { + if (!this.isTutorial()) + for (var t = this.entities.length; t--; ) { + var e = this.entities[t], + i = e.body ? e.body.front : e.z, + n = this.game.stats.z - B.visibleMinDistance; + (!e.active || n < i) && this._removeEntity(e, t); + } + }), + (i.removeAllEntities = function (t) { + t || (t = this.entities); + for (var e = t.length; e--; ) this._removeEntity(t[e], e); + }), + (i.reshuffle = function (t) { + void 0 === t && (t = !1), + this.game.route.resetSpawns(), + (this.currentChunk = null), + this.game.physics.reset(), + this.removeAllEntities(this.entities), + (this.nextPosition = this.game.stats.distance - 10), + (this.currentChunk = this.placeNextChunk("default_fallback")), + (this.currentChunk = this.placeNextChunk("default_fallback")), + t && this.game.update(1); + }), + (i.isTutorial = function () { + return !(!this.currentChunk || "routeChunk_default_tutorial" !== this.currentChunk.name) && this.game.stats.distance < this.currentChunk.end - 300; + }), + (i.setSafeLanding = function (t) { + for (; this.nextPosition <= t; ) this.placeNextChunk(); + var e = this.nextPosition; + return this.placeNextChunk("jetpack_landing"), e; + }), + (i.removeChunksAhead = function (t) { + for (var e = this.chunks.length; e--; ) { + var i = this.chunks[e]; + i.start > t && (this.chunks.splice(e, 1), (this.nextPosition = i.start)); + } + }), + (i.removeEntitiesAhead = function (t) { + for (var e = this.entities.length; e--; ) { + var i = this.entities[e]; + t < -(i.body ? i.body.back : i.z) && this._removeEntity(i, e); + } + }), + LevelSystem + ); + })(f.a); + var xe = (function (n) { + var t, e; + + function PhysicsSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).collision = new $()), + t.game.onReset.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + i.reset(), + i + ); + } + (e = n), ((t = PhysicsSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = PhysicsSystem.prototype; + return ( + (i.reset = function () { + (this.stats = {}), (this.entities = []), (this._hasReset = !0); + }), + (i.entityAddedToGame = function (t) { + !t.body || t.body.deco || t.player || 0 <= this.entities.indexOf(t) || this.entities.push(t); + }), + (i.entityRemovedFromGame = function (t) { + if (t.body && !t.body.deco && !t.player) { + var e = this.entities.indexOf(t); + e < 0 || this.entities.splice(e, 1); + } + }), + (i.postupdate = function (t) { + var e = this.game.hero; + if (this.game.state === pn.RUNNING || e.player.dead) { + void 0 === t && (t = this.game.delta), e.player.dead || ((e.body.groundBefore = e.body.ground), (e.body.ground = 0)); + var i = Math.ceil(t), + n = Math.ceil(Math.abs(e.body.velocity.z)), + o = B.fixedPhysicsSteps || V.clamp(i + n, 1, 40), + s = t / o; + B.debug && ((this.stats.delta = t), (this.stats.steps = o), (this.stats.stepsDelta = s), (this.stats.bodies = this.entities.length), (this.stats.collidables = 0)); + for (var a = o; a-- && !this._hasReset; ) { + e.body.move(s); + for (var r = this.entities.length; r--; ) { + var h = this.entities[r]; + if (h && h.active && h !== e) { + if ((h.body.movable && h.body.move(s), this._hasReset)) break; + if (!(h.body.back < e.body.z - 10 || h.body.front > e.body.z + 5)) { + if (this.game.state !== pn.RUNNING) break; + if (e.player.dead) break; + if ((B.debug && (this.stats.collidables += 1), e.body.sensor && !h.body.ghost && !h.body.trigger)) { + var d = e.body.sensor.hitTest(h.body.box); + d && this.resolveGroundSensorHit(e, h, d); + } + var l = e.body.box.hitTest(h.body.box); + if ((l && !h.body.trigger && this.resolveHit(e, h, l), h.body.trigger)) { + var c = e.body.colliding.indexOf(h.body); + !l && 0 <= c && (e.body.colliding.splice(c, 1), e.body.triggerExit(h.body)), l && c < 0 && (e.body.colliding.push(h.body), e.body.triggerEnter(h.body)); + } + } + } + } + } + e.body.ground < e.body.groundBefore && (e.body.groundChangeTolerance = 8), (this._hasReset = !1); + } + }), + (i.resolve = function (t) { + if (!this.isHeroDead()) + for (var e = this.entities.length; e--; ) { + var i = this.entities[e]; + if (i && i.active && !(i.body.back < t.body.z - 10 || i.body.front > t.body.z + 5)) { + if ((B.debug && (this.stats.collidables += 1), t.body.sensor && !i.body.ghost && !i.body.trigger)) { + var n = t.body.sensor.hitTest(i.body.box); + n && this.resolveGroundSensorHit(t, i, n); + } + var o = t.body.box.hitTest(i.body.box); + if ((o && !i.body.trigger && this.resolveHit(t, i, o), i.body.trigger)) { + var s = t.body.colliding.indexOf(i.body); + !o && 0 <= s && (t.body.colliding.splice(s, 1), t.body.triggerExit(i.body)), o && s < 0 && (t.body.colliding.push(i.body), t.body.triggerEnter(i.body)); + } + } + } + }), + (i.resolveHit = function (t, e, i) { + if (!this.isHeroDead()) + if (e.collectible) e.collectible.collect(); + else if (!t.body.ghost) { + var n = t.body, + o = e.body; + if ((this.collision.reset(), (this.collision.act = n), (this.collision.pas = o), this.collision.hit.copy(i), e.ramp)) if (n.right >= o.left && n.left <= o.right) return; + var s = o.movable ? o.origin : o.box; + n.y > o.top && i.height <= 6 && -1 < n.velocity.y + ? ((n.bottom = o.top + 0.1), (this.collision.flags = this.collision.flags | $.BOTTOM), 2 < i.height && (this.collision.flags = this.collision.flags | $.SLOPE), n.origin.copy(n.box)) + : n.origin.bottom > s.top + ? ((n.bottom = o.top + 0.1), (this.collision.flags = this.collision.flags | $.BOTTOM), n.origin.copy(n.box)) + : n.origin.left >= s.right + ? ((n.box.left = o.box.right + 0.1), (this.collision.flags = this.collision.flags | $.LEFT), n.origin.copy(n.box)) + : n.origin.right <= s.left + ? ((n.box.right = o.box.left - 0.1), (this.collision.flags = this.collision.flags | $.RIGHT), n.origin.copy(n.box)) + : n.origin.front >= s.back + ? ((n.box.front = o.box.back + 0.1), (this.collision.flags = this.collision.flags | $.FRONT), n.origin.copy(n.box)) + : n.origin.top < s.bottom && ((n.box.top = o.box.bottom - 0.1), (this.collision.flags = this.collision.flags | $.TOP), n.origin.copy(n.box)), + this.collision.flags && n.collisionEnter(o, this.collision); + } + }), + (i.resolveGroundSensorHit = function (t, e, i) { + if (!this.isHeroDead()) + if (t.body.ghost) t.body.ground = 0; + else { + var n = t.body, + o = e.body, + s = n.ground; + if (e.ramp) { + var a = o.box.size.y, + r = o.box.size.z; + s = a * ((o.back - n.front) / r) + 0.11; + } else s = o.top + 0.11; + s >= n.ground && (n.ground = s); + } + }), + (i.canUpdate = function () { + return this.game.state === pn.RUNNING; + }), + (i.isHeroDead = function () { + return this.game.hero.player.dead; + }), + PhysicsSystem + ); + })(f.a), + Ce = (function () { + function Graph() {} + return ( + (Graph.rect = function (t) { + return t.image ? this.rectImg(t) : this.rectColor(t); + }), + (Graph.rectColor = function (t) { + void 0 === t && (t = {}); + var e = Object.assign( + { + name: "rectColor", + w: 120, + h: 120, + x: 0, + y: 0, + round: 0, + alpha: 1, + color: 3246014, + }, + t + ), + i = new c.Graphics(); + i.beginFill(e.color, e.alpha); + var n = -e.w / 2 + e.x, + o = -e.h / 2 + e.y; + return e.round ? i.drawRoundedRect(n, o, e.w, e.h, e.round) : i.drawRect(n, o, e.w, e.h), i.endFill(), i; + }), + (Graph.rectImg = function (t) { + void 0 === t && (t = {}); + var e = Object.assign( + { + name: "rectImg", + w: 120, + h: 120, + x: 0, + y: 0, + l: 15, + t: 15, + r: 25, + b: 25, + image: "box_border_grey.png", + }, + t + ), + i = c.Texture.from(e.image), + n = new c.NineSlicePlane(i, e.l, e.t, e.r, e.b); + return (n.width = e.w), (n.height = e.h), (n.x = 0.5 * -e.w + e.x), (n.y = 0.5 * -e.h + e.y), n; + }), + (Graph.rectComp = function () { + for (var t = new c.Container(), e = arguments.length, i = new Array(e), n = 0; n < e; n++) i[n] = arguments[n]; + for (var o = 0, s = i; o < s.length; o++) { + var a = s[o], + r = this.rect(a); + (t[a.name] = r), t.addChild(r); + } + return t; + }), + (Graph.roundRectBorder = function (t) { + void 0 === t && (t = {}); + var e = Object.assign( + { + w: 120, + h: 120, + round: 5, + color: 3246014, + alpha: 1, + borderWidth: 12, + borderColor: 16777215, + }, + t + ), + i = Object.assign( + { + name: "fill", + }, + e + ), + n = { + name: "border", + w: e.w + e.borderWidth, + h: e.h + e.borderWidth, + round: e.round + 0.5 * e.borderWidth, + color: e.borderColor, + alpha: e.alpha, + }; + return this.rectComp(n, i); + }), + (Graph.rectShadow = function (t) { + void 0 === t && (t = {}); + var e = Object.assign( + { + w: 120, + h: 120, + round: 0, + color: 3246014, + alpha: 1, + shadowDistance: 8, + shadowColor: 0, + shadowAngle: 0.25 * Math.PI, + shadowAlpha: 1, + }, + t + ), + i = Object.assign({}, e, { + name: "fill", + }), + n = Object.assign({}, e, { + name: "shadow", + color: e.shadowColor, + alpha: e.shadowAlpha, + x: Math.sin(e.shadowAngle) * e.shadowDistance, + y: Math.cos(e.shadowAngle) * e.shadowDistance, + }); + return this.rectComp(n, i); + }), + (Graph.rectBorder = function (t) { + void 0 === t && (t = {}); + var e = Object.assign( + { + w: 120, + h: 120, + round: 5, + color: 3246014, + alpha: 1, + borderWidth: 8, + borderColor: 0, + borderAlpha: 1, + }, + t + ), + i = Object.assign({}, e, { + name: "fill", + }), + n = Object.assign({}, e, { + name: "border", + w: e.w + 2 * e.borderWidth, + h: e.h + 2 * e.borderWidth, + color: e.borderColor, + alpha: e.borderAlpha, + round: e.round + 0.5 * e.borderWidth, + }); + return this.rectComp(n, i); + }), + (Graph.clear = function (t) { + if ((t.clear && t.clear(), t.children)) for (var e in t.children) this.clear(t.children[e]); + }), + (Graph.resize = function (t, e, i) { + if ((t.width && ((t.width = e), (t.height = i)), t.children)) for (var n in t.children) this.resize(t.children[n]); + }), + Graph + ); + })(); + + function ItemTimer_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function ItemTimer_createClass(t, e, i) { + return e && ItemTimer_defineProperties(t.prototype, e), i && ItemTimer_defineProperties(t, i), t; + } + + function ItemTimer_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Se = (function (n) { + function ItemTimer(t, e) { + var i; + return ( + ((i = n.call(this) || this).id = e), + (i.game = t), + (i.iconBase = c.Sprite.from("base_item.png")), + i.iconBase.anchor.set(0.5), + i.addChild(i.iconBase), + (i.icon = c.Sprite.from("icon_item_" + e + ".png")), + i.icon.anchor.set(0.5), + i.addChild(i.icon), + (i.bar = new ze(200, 30, 16777215, 10867241)), + i.addChild(i.bar), + (i.bar.x = i.iconBase.width / 2 + i.bar.w / 2 + 6), + (i.base = Ce.roundRectBorder({ + w: i.bar.w + 34, + h: i.bar.h + 16, + round: 5, + color: 16777215, + alpha: 1, + borderWidth: 3, + borderColor: 10066329, + })), + i.addChildAt(i.base, 0), + (i.base.x = i.bar.x - 4), + (i.visible = !1), + i + ); + } + ItemTimer_inheritsLoose(ItemTimer, n); + var t = ItemTimer.prototype; + return ( + (t.show = function () { + this.visible || (this.visible = !0); + }), + (t.hide = function () { + this.visible && ((this.visible = !1), this.parent && this.parent.removeChild(this)); + }), + ItemTimer_createClass(ItemTimer, [ + { + key: "w", + get: function () { + return this.iconBase.width; + }, + }, + { + key: "h", + get: function () { + return this.iconBase.height; + }, + }, + { + key: "ratio", + get: function () { + return this.bar.ratio; + }, + set: function (t) { + this.bar.ratio = t; + }, + }, + ]), + ItemTimer + ); + })(c.Container), + ze = (function (r) { + function ProgressBar(t, e, i, n) { + var o; + void 0 === i && (i = 0), void 0 === n && (n = 16142336); + var s = ((o = r.call(this) || this).w = t), + a = (o.h = e); + return ( + (o.barBg = new c.Graphics()), + o.barBg.beginFill(i, 0.75), + o.barBg.drawRect(0, 0, s, a), + o.barBg.position.set(-s / 2, -a / 2), + o.addChild(o.barBg), + (o.masked = new c.Container()), + o.addChild(o.masked), + (s -= 8), + (a -= 8), + (o.barFill = new Te(s, a)), + (o.barFill.x = -s / 2), + o.masked.addChild(o.barFill), + (o.barRed = new Te(s, a, 16711680)), + (o.barRed.x = -s / 2), + o.masked.addChild(o.barRed), + (o.barMask = new c.Graphics()), + o.barMask.beginFill(255), + o.barMask.drawRect(0, 0, s, a), + o.barMask.position.set(-s / 2, -a / 2), + o.addChild(o.barMask), + (o.masked.mask = o.barMask), + (o._ratio = 1), + o.update(), + o + ); + } + return ( + ItemTimer_inheritsLoose(ProgressBar, r), + (ProgressBar.prototype.update = function () { + (this.barMask.scale.x = this._ratio), (this.barRed.alpha = 1 - this._ratio); + }), + ItemTimer_createClass(ProgressBar, [ + { + key: "ratio", + get: function () { + return this._ratio; + }, + set: function (t) { + (this._ratio = V.clamp(t)), this.update(); + }, + }, + ]), + ProgressBar + ); + })(c.Container), + Te = (function (r) { + function Bar(t, e, i) { + var n; + ((n = r.call(this) || this).w = t), (n.h = e); + for (var o = Math.ceil(n.w / 11), s = 0; s < o; s++) { + var a = c.Sprite.from("item_duration_bar.png"); + n.addChild(a), (a.x = 11 * s), (a.anchor.y = 0.5), (a.width = 8), (a.height = n.h), i && (a.tint = i); + } + return n; + } + return ItemTimer_inheritsLoose(Bar, r), Bar; + })(c.Container); + + function HUDSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function HUDSystem_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + + function HUDSystem_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Pe = (function (n) { + function HudSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).view = new c.Container()), + (i.game = t.game), + i.game.onReset.add(HUDSystem_assertThisInitialized(i)), + i.game.onRun.add(HUDSystem_assertThisInitialized(i)), + i.game.onPause.add(HUDSystem_assertThisInitialized(i)), + i.game.onResume.add(HUDSystem_assertThisInitialized(i)), + i.game.onGameover.add(HUDSystem_assertThisInitialized(i)), + i.game.onRevive.add(HUDSystem_assertThisInitialized(i)), + (i.countUpdate = 1), + i + ); + } + HUDSystem_inheritsLoose(HudSystem, n); + var t = HudSystem.prototype; + return ( + (t.build = function () { + this._built || + ((this._built = !0), + (this.distance = new Me("base_long.png")), + this.view.addChild(this.distance), + (this.coins = new Me("base_long.png", "icon_coin_large.png")), + this.view.addChild(this.coins), + (this.multiplier = new Ee(this.game)), + this.view.addChild(this.multiplier), + (this.countdown = new c.Text("0", { + fill: "white", + align: "center", + fontSize: 80, + fontFamily: "Titan One", + stroke: "black", + strokeThickness: 5, + })), + this.countdown.anchor.set(0.5), + this.view.addChild(this.countdown), + (this.countdown.visible = !1), + (this.timers = new c.Container()), + this.view.addChild(this.timers), + this.resize()); + }), + (t.update = function (t) { + if (this.game.state === pn.RUNNING && this._built && !(0 < this.countUpdate--)) { + this.countUpdate = 2; + var e = this.game.stats; + this.distance.getText() <= e.score && this.distance.setText(e.score, 6), this.coins.setText(e.coins), (this.multiplier.text = "x" + e.multiplier), this.multiplier.update(t); + } + }), + (t.reset = function () { + (this.view.visible = !1), this.message && (this.message.text = ""); + }), + (t.run = function () { + this.build(), this.distance.setText(this.game.stats.score, 6), (this.view.visible = !0), this.game.stage.addChild(this.view), this.message && (this.message.text = ""); + }), + (t.pause = function () { + this.clearCountdown(); + }), + (t.resume = function () { + this.clearCountdown(), (this.view.visible = !0); + }), + (t.gameover = function () { + this.view.visible = !1; + }), + (t.revive = function () { + this.view.visible = !0; + }), + (t.resize = function (t, e, i) { + (this.sw = t || this.sw), + (this.sh = e || this.sh), + (this.sr = i || this.sr), + (this.s = this.sh / (667 * this.sr)), + (this.w = this.sw / this.s), + (this.h = this.sh / this.s), + this.view.scale.set(this.s), + this._built && + ((this.distance.x = this.w - 110), + (this.distance.y = 60), + (this.coins.x = this.w - 170), + (this.coins.y = 160), + (this.countdown.x = this.w / 2), + (this.countdown.y = this.h / 2), + (this.multiplier.x = this.distance.x - 170), + (this.multiplier.y = this.distance.y), + this.message && ((this.message.x = this.w / 2), (this.message.y = 120)), + this.stats && ((this.stats.x = 10), (this.stats.y = 200)), + this.timers && ((this.timers.x = 0), (this.timers.y = this.h))); + }), + (t.addMessage = function (t) { + this.message && (0 <= this.msgs.indexOf(t) || (this.msgs.push(t), (this.message.text = this.msgs.join("\n")))); + }), + (t.clearMessage = function (t) { + if (this.message) + if (void 0 !== t) { + var e = this.msgs.indexOf(t); + e < 0 || (this.msgs.splice(e, 1), (this.message.text = this.msgs.join("\n"))); + } else this.msgs.length = 0; + }), + (t.runCountdown = function (t, i) { + this.clearCountdown(), + (this.view.visible = !0), + this.view.addChild(this.countdown), + (this.countdown.visible = !0), + function _runStep(t) { + if (0 == t) return i(), void (this.countdown.visible = !1); + var e = "Starting in\n" + t; + (this.countdown.text = e), (this.countdown.visible = !0), (this._timeout = setTimeout(_runStep.bind(this), 900, t - 1)); + }.call(this, t); + }), + (t.clearCountdown = function () { + this._timeout && clearTimeout(this._timeout), this.countdown && (this.countdown.visible = !1); + }), + (t.addItemTimer = function (t) { + this.timers[t] || (this.timers[t] = new Se(this.game, t)); + var e = this.timers[t]; + return this.timers.addChild(e), e.show(), this.organizeTimers(), e; + }), + (t.removeItemTimer = function (t) { + var e = this.timers[t]; + e && (e.hide(), this.organizeTimers()); + }), + (t.updateItemTimer = function (t, e) { + var i = this.timers[t]; + i && (i.ratio = e); + }), + (t.organizeTimers = function () { + for (var t = this.timers.children.length; t--; ) { + var e = this.timers.children[t]; + (e.x = e.w / 2 + 20), (e.y = -t * (e.h + 20) - e.h / 2 - 20); + } + (this.timers.x = 0), (this.timers.y = this.h); + }), + HudSystem + ); + })(f.a), + Me = (function (s) { + function Label(t, e) { + var i; + ((i = s.call(this) || this).numChars = 6), + (i.spacing = 30), + (i._txt = -1), + t && ((i.base = c.Sprite.from(t)), i.base.anchor.set(0, 0.5), (i.base.alpha = 0.5), i.addChild(i.base)), + e && ((i.icon = c.Sprite.from(e)), i.icon.anchor.set(0.5), i.addChild(i.icon), (i.icon.x = i.numChars * i.spacing * 0.5 + 35), i.icon.scale.set(0.75)), + (i.text = new c.Container()), + i.addChild(i.text); + for (var n = 0; n < i.numChars; n++) { + var o = new c.Text("0", { + fill: "white", + align: "center", + fontSize: 50, + fontFamily: "Lilita One", + }); + o.anchor.set(0.5), i.text.addChild(o); + } + return i; + } + HUDSystem_inheritsLoose(Label, s); + var t = Label.prototype; + return ( + (t.setText = function (t, e) { + if (t !== this._txt) { + for (var i = (this._txt = t) + "", n = e || i.length; i.length < this.numChars; ) { + i = (i.length < e ? "0" : " ") + i; + } + for (var o = this.numChars; o--; ) { + var s = this.text.children[o], + a = i[o]; + o < i.length && (s.text = a), (s.visible = " " !== a), (s.x = o * this.spacing); + } + (this.text.x = -(this.numChars - 1) * this.spacing * 0.5), (this.base.x = this.numChars * this.spacing * 0.5 - n * this.spacing - 20); + } + }), + (t.getText = function () { + return this._txt; + }), + Label + ); + })(c.Container), + Ee = (function (s) { + function MultiplierLabel(t) { + var e; + (e = s.call(this) || this).game = t; + var i = c.Sprite.from("base_short.png"); + i.anchor.set(0.5), (i.alpha = 0.5), (e.base = i), e.addChild(i); + var n = new c.Text("0", { + align: "center", + fill: 16702212, + fontSize: 50, + fontFamily: "Lilita One", + }); + n.anchor.set(0.5), e.addChild(n), (e.label = n); + var o = new c.Text("0", { + align: "center", + fill: 7829367, + fontSize: 50, + fontFamily: "Lilita One", + }); + return o.anchor.set(0.5), e.addChild(o), (e.labelOverlay = o), e; + } + var t, e, i; + return ( + HUDSystem_inheritsLoose(MultiplierLabel, s), + (MultiplierLabel.prototype.update = function (t) { + var e = this.game.hero.multiplier.isOn(); + this.labelOverlay.alpha = e ? 0.5 + 0.4 * Math.sin(0.1 * this.game.time) : 0; + }), + (t = MultiplierLabel), + (e = [ + { + key: "text", + get: function () { + return this.label.text; + }, + set: function (t) { + this.label.text !== t && ((this.label.text = t), (this.labelOverlay.text = t)); + }, + }, + ]) && HUDSystem_defineProperties(t.prototype, e), + i && HUDSystem_defineProperties(t, i), + MultiplierLabel + ); + })(c.Container); + + function StatsSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Oe = { + x: 0, + y: 0, + z: 0, + distance: 0, + distanceDelta: 0, + score: 0, + coins: 0, + keys: 0, + chunkIndex: 0, + chunkName: "", + chunkStart: 0, + chunkEnd: 0, + chunkLength: 0, + block: 0, + hoverboards: 1, + revivals: 1, + multiplier: 1, + route: "", + time: 0, + delta: 0, + }, + Re = (function (n) { + var t, e; + + function StatsSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + (i = n.call(this, t, e) || this), + t.game.onReset.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + (i.game = t.game), + (i.data = {}), + i.reset(), + i + ); + } + (e = n), ((t = StatsSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = StatsSystem.prototype; + return ( + (a.reset = function () { + Object.assign(this.data, Oe), (this.data.revivals = this.game.app.config.revivals || B.revivals || 0); + }), + (a.preupdate = function () { + (this.x = this.game.hero.transform.position.x), (this.y = this.game.hero.transform.position.y - 5.5), (this.z = this.game.hero.transform.position.z); + }), + (a.setCurrentChunk = function (t) { + (this.data.chunkName = t.name), (this.data.chunkStart = t.start), (this.data.chunkEnd = t.end), (this.data.chunkLength = t.length); + }), + (a.toString = function () { + var t = ["level", "route", "chunk"], + e = ""; + for (var i in t) { + var n = t[i]; + "z" !== n && "distance" !== n && "distanceDelta" !== n && (e += n + ": " + this[n] + "\n"); + } + return e; + }), + (i = StatsSystem), + (o = [ + { + key: "multiplier", + get: function () { + return this.data.multiplier; + }, + set: function (t) { + this.data.multiplier = t; + }, + }, + { + key: "x", + get: function () { + return this.data.x; + }, + set: function (t) { + this.data.x = t; + }, + }, + { + key: "y", + get: function () { + return this.data.y; + }, + set: function (t) { + this.data.y = t; + }, + }, + { + key: "z", + get: function () { + return this.data.z; + }, + set: function (t) { + (this.data.z = t), + (this.data.distanceDelta = -t - this.data.distance), + (this.data.distance = -t), + (this.data.block = (this.data.distance / B.blockSize) | 0), + (this.data.score += this.data.distanceDelta * this.data.multiplier); + }, + }, + { + key: "distance", + get: function () { + return this.data.distance; + }, + set: function (t) { + (this.data.z = -t), + (this.data.distanceDelta = t - this.data.distance), + (this.data.distance = t), + (this.data.block = (this.data.distance / B.blockSize) | 0), + (this.data.score += this.data.distanceDelta * this.data.multiplier); + }, + }, + { + key: "distanceDelta", + get: function () { + return this.data.distanceDelta; + }, + }, + { + key: "score", + get: function () { + return Math.floor(0.1 * this.data.score); + }, + }, + { + key: "coins", + get: function () { + return this.data.coins; + }, + set: function (t) { + this.data.coins = t; + }, + }, + { + key: "keys", + get: function () { + return this.data.keys; + }, + set: function (t) { + this.data.keys = t; + }, + }, + { + key: "chunkIndex", + get: function () { + return this.data.chunkIndex; + }, + set: function (t) { + this.data.chunkIndex = t; + }, + }, + { + key: "hoverboards", + get: function () { + return this.data.hoverboards; + }, + set: function (t) { + this.data.hoverboards = t; + }, + }, + { + key: "speed", + get: function () { + if (B.speed) return B.speed; + var t = this.data.time, + e = 240; + t < 180 && (e = 110 + 130 * (t / 180)); + return e / 60; + }, + }, + { + key: "level", + get: function () { + return Math.floor(this.data.time / 20); + }, + }, + { + key: "levelName", + get: function () { + switch (this.level) { + case 0: + return "easy"; + case 1: + return "normal"; + case 2: + return "hard"; + default: + return "expert"; + } + }, + }, + { + key: "time", + get: function () { + return this.data.time; + }, + set: function (t) { + this.data.time = t; + }, + }, + { + key: "delta", + get: function () { + return this.data.delta; + }, + set: function (t) { + this.data.delta = t; + }, + }, + { + key: "route", + set: function (t) { + this.data.route = t; + }, + get: function () { + return this.data.route; + }, + }, + { + key: "chunk", + get: function () { + return this.data.chunkName; + }, + }, + { + key: "revivals", + get: function () { + return this.data.revivals; + }, + set: function (t) { + this.data.revivals = t; + }, + }, + { + key: "profile", + get: function () { + return this._profile || (this._profile = {}), Object.assign(this._profile, this.data), (this._profile.speed = this.speed), (this._profile.level = this.level), (this._profile.levelName = this.levelName), this._profile; + }, + }, + ]) && StatsSystem_defineProperties(i.prototype, o), + s && StatsSystem_defineProperties(i, s), + StatsSystem + ); + })(f.a); + + function PoolSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Ie = (function (n) { + var t, e; + + function PoolSystem(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).toRemove = []), i; + } + (e = n), ((t = PoolSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = PoolSystem.prototype; + return ( + (i.prepopulate = function (t, e) { + Le.getPool(t).prepopulate(e); + }), + (i.get = function (t, e) { + var i = Le.get(t); + return i.onEntityDestroyed.add(this), i._init.run(e || {}), i; + }), + (i.factory = function (t, e) { + var i = Le.get(_class); + return i.onEntityDestroyed.add(this), i._init.run(data || {}), i; + }), + (i.entityDestroyed = function (t) { + var e = this; + t.onEntityDestroyed.remove(this), + setTimeout(function () { + e["return"](t); + }, 200); + }), + (i["return"] = function (t) { + t._reset.run(), t.resetSignals(), this.toRemove.push(t); + }), + (i.postupdate = function () { + for (var t = 0; t < this.toRemove.length; t++) { + var e = this.toRemove[t]; + Le["return"](e); + } + this.toRemove.length = 0; + }), + (i.empty = function () { + this.postupdate(); + }), + PoolSystem + ); + })(f.a), + Le = (function () { + function Pool(t) { + t instanceof Object + ? (this._create = function () { + return new t(); + }) + : (this._create = function () { + return t(); + }), + (this.classType = t), + (this.pool = []), + (this.totalCreated = 0), + (this.debug = !1); + } + var t, + e, + i, + n = Pool.prototype; + return ( + (n.prepopulate = function (t) { + for (var e = 0; e < t; e++) this.pool.push(this._create()); + }), + (n.get = function () { + var t = this.pool.pop(); + return t || (this.totalCreated++, this.debug, (t = this._create())), t; + }), + (n["return"] = function (t) { + -1 === this.pool.indexOf(t) && this.pool.push(t), this.debug; + }), + (Pool.getPool = function (t) { + var e = this.getClassId(t); + return Pool.pools[e] || (Pool.pools[e] = new Pool(t)), Pool.pools[e]; + }), + (Pool.get = function (t) { + var e = this.getPool(t).get(); + return (e._CLASS_ID = this.getClassId(t)), e; + }), + (Pool["return"] = function (t) { + Pool.pools[t._CLASS_ID]["return"](t); + }), + (Pool.getClassId = function (t) { + return t._CLASS_ID || (t._CLASS_ID = Pool.idGenerator++), t._CLASS_ID; + }), + (t = Pool), + (e = [ + { + key: "total", + get: function () { + return this.pool.length; + }, + }, + ]) && PoolSystem_defineProperties(t.prototype, e), + i && PoolSystem_defineProperties(t, i), + Pool + ); + })(); + (Le.pools = []), (Le.idGenerator = 1); + var je = i(94); + + function ControllerSystem_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + var Fe = (function (n) { + var t, e; + + function ControllerSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).keyboard = new je.a()), + i.keyboard.onKeyPress("left", i.pressLeft.bind(ControllerSystem_assertThisInitialized(i))), + i.keyboard.onKeyPress("up", i.pressUp.bind(ControllerSystem_assertThisInitialized(i))), + i.keyboard.onKeyPress("right", i.pressRight.bind(ControllerSystem_assertThisInitialized(i))), + i.keyboard.onKeyPress("down", i.pressDown.bind(ControllerSystem_assertThisInitialized(i))), + i.keyboard.onKeyPress("space", i.pressAction.bind(ControllerSystem_assertThisInitialized(i))), + (i.view = new c.Graphics()), + (i.view.alpha = 0), + i.view.beginFill(16763904), + i.view.drawRect(0, 0, 4, 4), + i.view.endFill(), + i.view.addListener("pointerdown", i.onPointerDown, ControllerSystem_assertThisInitialized(i)), + i.view.addListener("pointermove", i.onPointerMove, ControllerSystem_assertThisInitialized(i)), + i.view.addListener("pointerup", i.onPointerUp, ControllerSystem_assertThisInitialized(i)), + t.game.onReset.add(ControllerSystem_assertThisInitialized(i)), + t.game.onRun.add(ControllerSystem_assertThisInitialized(i)), + t.game.onPause.add(ControllerSystem_assertThisInitialized(i)), + t.game.onResume.add(ControllerSystem_assertThisInitialized(i)), + t.game.onGameover.add(ControllerSystem_assertThisInitialized(i)), + t.game.onRevive.add(ControllerSystem_assertThisInitialized(i)), + (i.vertical = 0), + (i.horizontal = 0), + (i.action = 0), + (i.pressing = !1), + (i.pressStartX = 0), + (i.pressStartY = 0), + (i.pressCount = 0), + (i.onSwipeHorizontal = new et.a("onSwipeHorizontal", 1)), + (i.onSwipeVertical = new et.a("onSwipeVertical", 1)), + (i.onDoubleTap = new et.a("onDoubleTap")), + i + ); + } + (e = n), ((t = ControllerSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = ControllerSystem.prototype; + return ( + (i.onPointerDown = function (t) { + var e = t.data.global; + 0 < this.pressCount && (this.action = 1), (this.pressCount = 50), (this.pressStartX = e.x), (this.pressStartY = e.y), (this.pressing = !0); + }), + (i.onPointerMove = function (t) { + if (this.pressing) { + var e = t.data.global, + i = e.x - this.pressStartX; + if (60 < Math.abs(i)) return (this.pressing = !1), (this.pressCount = 0), void (this.horizontal = V.sign(i)); + var n = e.y - this.pressStartY; + return 60 < Math.abs(n) ? ((this.pressing = !1), (this.pressCount = 0), void (this.vertical = -V.sign(n))) : void 0; + } + }), + (i.onPointerUp = function (t) { + this.pressing = !1; + }), + (i.onPointerCancel = function (t) { + this.pressing = !1; + }), + (i.pressUp = function () { + this.vertical = 1; + }), + (i.pressDown = function () { + this.vertical = -1; + }), + (i.pressLeft = function () { + this.horizontal = -1; + }), + (i.pressRight = function () { + this.horizontal = 1; + }), + (i.pressAction = function () { + this.action = 1; + }), + (i.update = function () { + 0 < this.pressCount && (this.pressCount -= 1), + this.game.state === pn.RUNNING && + (1 === this.vertical + ? this.onSwipeVertical.dispatch(this.vertical) + : -1 === this.vertical + ? this.onSwipeVertical.dispatch(this.vertical) + : this.horizontal + ? this.onSwipeHorizontal.dispatch(this.horizontal) + : this.action && this.onDoubleTap.dispatch(), + (this.vertical = 0), + (this.horizontal = 0), + (this.action = 0)); + }), + (i.reset = function () { + this.hide(); + }), + (i.show = function () { + (this.view.visible = !0), (this.view.interactive = !0), this.view.visible && this.game.stage.addChild(this.view), this.keyboard.start(); + }), + (i.hide = function () { + (this.view.visible = !1), (this.view.interactive = !1), this.keyboard.stop(); + }), + (i.run = function () { + this.show(); + }), + (i.revive = function () { + this.show(); + }), + (i.pause = function () { + this.hide(); + }), + (i.resume = function () { + this.show(); + }), + (i.gameover = function () { + this.hide(); + }), + (i.resize = function (t, e) { + (this.view.x = 0), (this.view.y = 0), (this.view.width = t), (this.view.height = e); + }), + ControllerSystem + ); + })(f.a); + + function FreeCamera_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + var Ae = (function (n) { + var t, e; + + function FreeCamera(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).axis = new q()), + (i.keyboard = new je.a()), + (i.keyboard._keyCodes[81] = { + label: "q", + state: 0, + preventBubble: !0, + }), + (i.keyboard._keyCodes[69] = { + label: "e", + state: 0, + preventBubble: !0, + }), + i.keyboard.onKeyPress("a", i.pressLeft.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyPress("d", i.pressRight.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyPress("w", i.pressFront.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyPress("s", i.pressBack.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyPress("q", i.pressUp.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyPress("e", i.pressDown.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("a", i.releaseLeft.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("d", i.releaseRight.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("w", i.releaseFront.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("s", i.releaseBack.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("q", i.releaseUp.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.onKeyRelease("e", i.releaseDown.bind(FreeCamera_assertThisInitialized(i))), + i.keyboard.stop(), + (i.onMouseMoveBind = i.onMouseMove.bind(FreeCamera_assertThisInitialized(i))), + (i.useDelta = !0), + i + ); + } + (e = n), ((t = FreeCamera).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = FreeCamera.prototype; + return ( + (i.added = function () { + (this._oldParent = this.entity.parent), + this.entity.parent && this.entity.parent.removeChild(this.entity), + (this._rig = this.entity.game.view3d.camera.camera), + (this._rig.x = this.entity.game.camera.rig.mainX), + (this._rig.y = this.entity.game.camera.rig.mainY), + (this._rig.z = this.entity.game.camera.rig.mainZ), + (this._rig.rx = this.entity.game.camera.rig.mainRotX), + (this._rig.ry = this.entity.game.camera.rig.mainRotY), + this.keyboard.start(), + (this.mouseX = 0), + (this.mouseY = 0), + (this.lastMouseX = null), + (this.lastMouseY = null), + (this.deltaX = 0), + (this.deltaY = 0), + (this.speed = 0), + this.axis.reset(), + (this._updateProjection = this.entity.camera.updateProjection), + (this.entity.camera.updateProjection = FreeCamera_updateProjection), + window.addEventListener("mousemove", this.onMouseMoveBind); + }), + (i.removed = function () { + this._oldParent && this._oldParent.addChild(this.entity), (this.entity.camera.updateProjection = this._updateProjection), this.keyboard.stop(), window.removeEventListener("mousemove", this.onMouseMoveBind), (this._rig = null); + }), + (i.onMouseMove = function (t) { + (this.mouseX = t.clientX - window.innerWidth / 2), (this.mouseY = t.clientY - window.innerHeight / 2), null === this.lastMouseX && (this.lastMouseX = this.mouseX), null === this.lastMouseY && (this.lastMouseY = this.mouseY); + }), + (i.update = function () { + if (this._rig) { + null !== this.lastMouseX && ((this.deltaX = this.mouseX - this.lastMouseX), (this.deltaY = this.mouseY - this.lastMouseY), (this.lastMouseX = this.mouseX), (this.lastMouseY = this.mouseY)), + 0 !== this.axis.z || 0 !== this.axis.x ? ((this.speed += 0.02), 5 < this.speed && (this.speed = 5)) : (this.speed = 0.5); + this.useDelta ? ((this._rig.ry -= 0.01 * this.deltaX), (this._rig.rx -= 0.01 * this.deltaY)) : ((this._rig.ry = 0.01 * -this.mouseX), (this._rig.rx = 0.01 * -this.mouseY)), + (this._rig.z += Math.cos(this._rig.ry) * (this.axis.z * this.speed)), + (this._rig.x += Math.sin(this._rig.ry) * (this.axis.z * this.speed)), + (this._rig.z += Math.cos(this._rig.ry + V.PI_HALF) * (this.axis.x * this.speed)), + (this._rig.x += Math.sin(this._rig.ry + V.PI_HALF) * (this.axis.x * this.speed)), + (this._rig.y -= Math.sin(this._rig.rx) * (this.axis.z * this.speed)), + (this._rig.y += Math.sin(this._rig.rx + V.PI_HALF) * (this.axis.y * this.speed)); + } + }), + (i.pressFront = function () { + this.axis.z = -1; + }), + (i.pressBack = function () { + this.axis.z = 1; + }), + (i.pressLeft = function () { + this.axis.x = -1; + }), + (i.pressRight = function () { + this.axis.x = 1; + }), + (i.pressUp = function () { + this.axis.y = 1; + }), + (i.pressDown = function () { + this.axis.y = -1; + }), + (i.releaseFront = function () { + -1 === this.axis.z && (this.axis.z = 0); + }), + (i.releaseBack = function () { + 1 === this.axis.z && (this.axis.z = 0); + }), + (i.releaseLeft = function () { + -1 === this.axis.x && (this.axis.x = 0); + }), + (i.releaseRight = function () { + 1 === this.axis.x && (this.axis.x = 0); + }), + (i.releaseUp = function () { + 1 === this.axis.y && (this.axis.y = 0); + }), + (i.releaseDown = function () { + -1 === this.axis.y && (this.axis.y = 0); + }), + FreeCamera + ); + })(J.a), + De = U.quat.create(), + Ge = U.vec3.create(); + + function FreeCamera_updateProjection(t, e) { + void 0 === this.x && (this.x = 0), + void 0 === this.y && (this.y = 0), + void 0 === this.z && (this.z = 0), + void 0 === this.rx && (this.rx = 0), + void 0 === this.ry && (this.ry = 0), + void 0 === this.rz && (this.rz = 0), + U.quat.identity(De), + U.quat.rotateZ(De, De, this.rz), + U.quat.rotateY(De, De, this.ry), + U.quat.rotateX(De, De, this.rx), + (Ge[0] = this.x), + (Ge[1] = this.y), + (Ge[2] = this.z), + U.mat4.fromRotationTranslation(this.view, De, Ge), + U.mat4.perspective(this.projection, this.fov * (Math.PI / 180), t / e, this.near, this.far), + U.mat4.invert(this.view, this.view), + this.dirty++; + } + + function DebugSystem_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + var Be = (function (n) { + var t, e; + + function DebugSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).game = t.game), + i.game.onReset.add(DebugSystem_assertThisInitialized(i)), + (i.running = !1), + window.addEventListener("keydown", i.onKeyDown.bind(DebugSystem_assertThisInitialized(i))), + (i.view = new c.Container()), + (i.title = new c.Text("DEBUG MODE", { + fill: "red", + align: "center", + fontSize: 18, + fontFamily: "Arial Black", + })), + i.view.addChild(i.title), + i.title.anchor.set(0.5), + (i.title.y = 12), + (i.stats = new c.Text("", { + fill: "white", + fontSize: 18, + fontFamily: "Arial", + stroke: !0, + strokeThickness: 4, + lineJoin: "round", + })), + i.view.addChild(i.stats), + (i.stats.x = 10), + (i.stats.y = 200), + (i.view.visible = B.debug), + setInterval(function () { + return i._update(0); + }, 100), + i + ); + } + (e = n), ((t = DebugSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = DebugSystem.prototype; + return ( + (i.reset = function () { + this.game.stage.addChild(this.view); + }), + (i._update = function (t) { + if (this.game && !this.game.app.crashed) { + var e = [ + DebugSystem_str("ENGINE", this.game.profile), + DebugSystem_str("STATS", this.game.stats.profile), + DebugSystem_str("CAMERA", this.game.camera.profile), + DebugSystem_str("PHYSICS", this.game.physics.stats), + DebugSystem_str("ROUTE", this.game.route.profile), + ]; + (this.stats.text = e.join("\n")), (this.title.x = this.game.w / 2); + } + }), + (i.onKeyDown = function (t) { + "z" === t.key && this.toggle(), + this.running || + ("t" === t.key && this.game.level.reshuffle(), + "m" === t.key && this.game.hero.magnet.turnOn(), + "j" === t.key && this.game.hero.jetpack.turnOn(), + "q" === t.key && this.game.hero.pogo.turnOn(), + "s" === t.key && this.game.hero.sneakers.turnOn(), + "e" === t.key && this.game.hero.multiplier.turnOn()); + }), + (i.toggle = function () { + this.running ? this.finish() : this.begin(); + }), + (i.begin = function () { + this.running || ((this.running = !0), (this.game.timeScale = 0), this.game.view3d.camera.add(Ae)); + }), + (i.finish = function () { + this.running && ((this.running = !1), this.game.view3d.camera.remove(Ae), (this.game.timeScale = this.game.config.timeScale)); + }), + (i.resize = function (t, e) {}), + DebugSystem + ); + })(f.a); + + function DebugSystem_str(t, e, i) { + i || (i = Object.keys(e)); + var n = ""; + for (var o in i) { + var s = i[o], + a = e[s]; + "number" == typeof a && a % 1 != 0 && (a = a.toFixed(3)), (n += s + ": " + a + "\n"); + } + return t.toUpperCase() + "\n" + n; + } + var Ne = (function (n) { + var t, e; + + function FramebufferSystem(t) { + var e; + (e = n.call(this, t) || this).renderer = t; + var i = c.utils.hex2rgb(9753343); + return (e.r = i[0]), (e.g = i[1]), (e.b = i[2]), (e.a = 1), e; + } + return ( + (e = n), + ((t = FramebufferSystem).prototype = Object.create(e.prototype)), + ((t.prototype.constructor = t).__proto__ = e), + (FramebufferSystem.prototype.prerender = function () { + var t = this.renderer.gl; + t.clearColor(this.r, this.g, this.b, this.a), t.clear(t.COLOR_BUFFER_BIT | t.DEPTH_BUFFER_BIT); + }), + FramebufferSystem + ); + })(c.System); + var Xe = (function (n) { + var t, e; + + function Smoke(t, e) { + var i; + return ( + void 0 === t && (t = 5), + void 0 === e && (e = !0), + ((i = n.call(this) || this).speed = t), + (i.view = i.getView(e)), + (i.view2 = i.getView(e)), + (i.view2.y = -73), + (i.view2.scale.x = 1), + (i.view2.scale.y = 2), + (i.view2.scale.z = 2), + i + ); + } + (e = n), ((t = Smoke).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Smoke.prototype; + return ( + (i.getView = function (t) { + var e = H.getEntityClone("jetpackCloud", "jetpackSmoke", !1, G); + return ( + (e.view3d.state.blend = !0), + (e.view3d.state.culling = !1), + (e.view3d.material.map.baseTexture.wrapMode = c.WRAP_MODES.REPEAT), + this.addChild(e), + (e.ry = 0.5 * -Math.PI), + (e.rx = 0.5 * Math.PI), + e.scale.set(0.25), + (e.y = -12), + (e.active = !1), + (e.view3d.state.depthTest = t), + e + ); + }), + (i.update = function (t) { + this.view.active && ((this.view.view3d.material.map.orig.x -= t * this.speed), (this.view2.view3d.material.map.orig.x -= t * this.speed)); + }), + (i.turnOn = function () { + (this.view.active = !0), (this.view2.active = !0); + }), + (i.turnOff = function () { + var t = this; + (this.view.active = !1), + (this.view2.active = !1), + this.parent && this.parent.removeChild(this), + setTimeout(function () { + (t.view.active = !1), (t.view2.active = !1), t.parent && t.parent.removeChild(t); + }, 100); + }), + Smoke + ); + })(v.a); + + function Jetpack_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Ye = (function (n) { + var t, e; + + function Jetpack(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).distance = 0), (i.distanceTotal = 1), (i.speed = 0), (i.ceiling = 100), (i.coinDistance = 30), i; + } + (e = n), ((t = Jetpack).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Jetpack.prototype; + return ( + (a.show = function () { + this.view || (this.view = H.getEntityCloneOpaque("/powerups_jetpack", "props_tex", !1)), (this.view.ry = Math.PI), (this.view.y = 1), (this.view.z = 0.5), this.view.scale.set(0.6); + var t = this.entity.anim.scenes[2]; + H.findEntity("LowerSpine_jnt", t.pixiTree, 10).addChild(this.view), + this.entity.game.sfx.play("special_jetpack_start"), + this.entity.game.sfx.play("special_jetpack", { + loop: !0, + }), + this.entity.game.hud.addItemTimer("jetpack"), + this.jetpackSmokeOn(); + }), + (a.hide = function () { + this.entity.game.hud.removeItemTimer("jetpack"), this.jetpackSmokeOff(), this.entity.game.sfx.stop("special_jetpack"), this.view.parent && this.view.parent.removeChild(this.view); + }), + (a.update = function (t) { + if (this.distance) { + this.timer && (this.timer.ratio = this.ratio); + var e = V.lerp(this.entity.body.velocity.z, this.speed, 0.1 * t); + (this.entity.body.velocity.z = e), (this.distance -= this.entity.game.stats.distanceDelta), this.entity.game.hud.updateItemTimer("jetpack", this.ratio); + var i = this.entity.game.stats.y + B.cameraPosY, + n = this.entity.game.stats.z + B.cameraPosZ; + if (((this.entity.game.hero.player.cameraY = i), this.takeOffTime < this.takeOffDuration)) { + (this.takeOffTime += this.entity.game.deltaSecs), this.takeOffTime > this.takeOffDuration && (this.takeOffTime = this.takeOffDuration); + var o = this.takeOffTime / this.takeOffDuration; + St.quartInOut(o); + (this.entity.body.y = V.lerp(this.takeOffStartY, this.takeOffEndY, o)), (this.rig.mainY = V.lerp(this.rigStartY, i, St.sineOut(o))), (this.rig.mainZ = V.lerp(this.rigStartZ, n, St.expoOut(o))); + } else (this.rig.mainY = i), (this.rig.mainZ = n); + (this.rig.mainX = this.entity.game.stats.x * B.cameraModX), + (this.rig.mainRotX = B.cameraRotX + this.entity.game.hero.player.cameraRotX), + (this.entity.player.cameraTargetY = this.entity.body.bottom), + this.smokeLeft && this.smokeLeft.update(t), + this.smokeRight && this.smokeRight.update(t), + this.distance <= 0 && this.turnOff(); + } + }), + (a.turnOn = function () { + this.entity.player.dizzyEnd(), + this.entity.sneakers.turnOff(), + this.entity.pogo.turnOff(), + this.entity.hoverboard.pause(), + this.entity.jump.lock(), + this.entity.roll.lock(), + this.entity.state.set("empty"), + (this.entity.body.velocity.y = 0), + (this.entity.body.ghost = !0), + (this.speed = 2 * -this.entity.game.stats.speed - 1); + var t = -this.entity.body.z, + e = t + 1e3 + 200 * Math.abs(this.speed), + i = this.entity.game.level.setSafeLanding(e); + (this.distanceTotal = i - t), + (this.distance = this.distanceTotal), + (this.takeOffStartY = this.entity.body.y), + (this.takeOffEndY = 100), + (this.takeOffTime = 0), + (this.takeOffDuration = 2), + (this.rig = this.entity.game.camera.takeControl()), + (this.rigStartY = this.rig.mainY), + (this.rigStartZ = this.rig.mainZ), + this.spawnCoins(this.entity.game, this.takeOffEndY, this.distance), + this.show(); + }), + (a.turnOff = function () { + this.distance && + (this.hide(), + (this.entity.body.ghost = !1), + (this.entity.body.ceiling = 999), + (this.entity.body.velocity.y = 0), + this.entity.jump.unlock(), + this.entity.roll.unlock(), + this.entity.game.camera.releaseControl(), + (this.distance = 0), + this.entity.restoreSize(), + this.entity.hoverboard.resume()); + }), + (a.isOn = function () { + return !!this.distance; + }), + (a.spawnCoins = function (t, e, i) { + for (var n = 350 * t.stats.speed, o = i - n, s = o / this.coinDistance, a = o / s, r = 0, h = 0, d = 5, l = 0; l < s; l++) { + var c = t.pool.get(pt); + d ? (d -= 1) : ((r = r ? L.pick(0, r) : L.pick(-1, 0, 1)), (d = 5)), h < r ? (h += 0.5) : r < h && (h -= 0.5), (c.body.x = B.laneWidth * h), (c.body.y = e), (c.body.z = t.stats.z - a * l - n), c.init(), t.addChild(c); + } + }), + (a.jetpackSmokeOn = function () { + this.smokeLeft || + ((this.smokeLeft = new Xe(3, !0)), + (this.smokeLeft.x = 0.9 + this.entity.model.x), + (this.smokeLeft.y = 1.7), + (this.smokeLeft.z = 0.5), + (this.smokeLeft.scale.x = 0.5), + (this.smokeLeft.scale.y = 2), + (this.smokeLeft.rotation.x = -V.PI_HALF), + (this.smokeRight = new Xe(3, !0)), + (this.smokeRight.x = -0.9 + this.entity.model.x), + (this.smokeRight.y = this.smokeLeft.y), + (this.smokeRight.z = this.smokeLeft.z), + (this.smokeRight.scale.x = -this.smokeLeft.scale.x), + (this.smokeRight.scale.y = this.smokeLeft.scale.y), + (this.smokeRight.rotation.x = this.smokeLeft.rotation.x)), + this.entity.addChild(this.smokeLeft), + this.entity.addChild(this.smokeRight), + this.smokeLeft.turnOn(), + this.smokeRight.turnOn(); + }), + (a.jetpackSmokeOff = function () { + this.smokeLeft && (this.smokeLeft.turnOff(), this.smokeRight.turnOff()); + }), + (i = Jetpack), + (o = [ + { + key: "ratio", + get: function () { + return this.distance / this.distanceTotal; + }, + }, + ]) && Jetpack_defineProperties(i.prototype, o), + s && Jetpack_defineProperties(i, s), + Jetpack + ); + })(J.a); + var He = (function (n) { + var t, e; + + function Pogo(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).count = 0), + (i.onTurnOn = new et.a("onPogoTurnOn")), + (i.onTurnOff = new et.a("onPogoTurnOff")), + (i.onHangtime = new et.a("onPogoHangtime")), + (i.settings = { + jumpHeight: 150, + jumpDistance: 300, + characterChangeTrackLength: 60, + finalJumpSpeed: 0, + rows: 14, + startRowPosition: 1, + endRowPosition: 1, + fadeInPosition: 0.1, + hangtimePosition: 0.6, + smoothCameraXDuration: 0.05, + }), + i + ); + } + (e = n), ((t = Pogo).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Pogo.prototype; + return ( + (i.show = function () { + this.view || (this.view = H.getEntityCloneOpaque("/powerups_rocketPogo", "props_tex", !1)); + var t = this.entity.anim.scenes[4]; + H.findEntity("attachPoint1", t.pixiTree, 10).addChild(this.view), this.pogoSmokeOn(); + }), + (i.hide = function () { + this.pogoSmokeOff(), this.view.parent && this.view.parent.removeChild(this.view); + }), + (i.turnOn = function () { + this.show(), + (this.hangtime = !1), + this.entity.sneakers.turnOff(), + this.entity.jetpack.turnOff(), + this.entity.player.dizzyEnd(), + this.entity.hoverboard.pause(), + this.entity.roll.cancel(), + this.onTurnOn.dispatch(), + this.entity.jump.lock(), + (this.camera = this.entity.game.camera.takeControl()), + (this.entity.body.ghost = !0), + this.entity.anim.play(["pogostick_kicking"], { + loop: !1, + }), + (this.position = this.entity.body.center.clone()), + (this.positionEnd = this.position.z - this.settings.jumpDistance), + (this.count = 1), + this.spawnCoins(this.entity.game, this.entity.game.hero.body), + (this.cameraStartY = this.camera.mainY), + this.entity.roll.onStart.add(this); + }), + (i.update = function (t) { + if (this.count) { + var e = this.entity.body.z, + i = this.entity.game.stats.speed; + if (e > this.positionEnd) { + var n = e - i * t, + o = -(n - this.position.z) / this.settings.jumpDistance, + s = (this.jumpCurveEvaluate(o), this.settings.jumpHeight, this.position.y + 1 * this.jumpCurveEvaluate(o) * this.settings.jumpHeight); + (s = V.lerp(this.position.y, s, o / this.settings.fadeInPosition)), + (this.entity.body.bottom = s), + o > this.settings.hangtimePosition && + !this.hangtime && + ((this.hangtime = !0), + this.onHangtime.dispatch(), + this.pogoSmokeOff(), + this.entity.anim.play(["pogostick_Hangtime_flying", "pogostick_Hangtime_kick", "pogostick_Hangtime_front_flip1"], { + loop: !1, + })); + var a = 0.8 * St.sineIn(o), + r = this.entity.game.stats.x * B.cameraModX, + h = this.entity.game.stats.y + B.cameraPosY * a, + d = this.entity.game.stats.z + B.cameraPosZ, + l = St.expoOut(o); + (this.entity.player.cameraY = h), (this.camera.mainX = r), (this.camera.mainY = V.lerp(this.cameraStartY, h, l)), (this.camera.mainZ = d); + var c = this.camera.mainY - this.entity.game.stats.y, + u = this.camera.mainZ - this.entity.game.stats.z + 50; + (this.camera.mainRotX = Math.atan2(u, c) - 0.5 * Math.PI), (this.entity.player.cameraTargetY = s), (this.entity.player.cameraY = s), (this.entity.body.velocity.y = 0); + } else this.turnOff(); + this.smoke && this.smoke.update(t); + } + }), + (i.turnOff = function () { + this.count && + (this.entity.roll.onStart.remove(this), this.onTurnOff.dispatch(), (this.entity.body.ghost = !1), this.entity.jump.unlock(), this.entity.game.camera.releaseControl(), this.entity.hoverboard.resume(), (this.count = 0)); + }), + (i.onRollStart = function () { + this.entity.game.sfx.stop("special_jetpack"), this.turnOff(); + }), + (i.jumpCurveEvaluate = function (t) { + return St.quartOut(t); + }), + (i.cameraCurveEvaluate = function (t) { + return St.expoOut(t); + }), + (i.cameraAimFollowCurveEvaluate = function (t) { + return St.quadOut(t); + }), + (i.isOn = function () { + return !!this.count; + }), + (i.spawnCoins = function (t, e) { + for (var i = this.entity.body.y, n = i + this.settings.jumpHeight, o = this.entity.body.z, s = o - this.settings.jumpDistance, a = i, r = o, h = this.settings.rows + 1, d = 0; d <= h; d++) + if (!(d < 1)) { + var l = d / h, + c = this.jumpCurveEvaluate(l); + (a = V.lerp(i, n, c)), (r = V.lerp(o, s, l)), d < h ? this.spawnCoinsRow(a, r) : this.spawnPowerup(a, r); + } + }), + (i.spawnCoinsRow = function (t, e) { + for (var i = 3; i--; ) { + var n = this.entity.game.pool.get(pt); + (n.body.x = (i - 1) * B.laneWidth), (n.body.bottom = t), (n.body.z = e), n.init(), this.entity.game.addChild(n); + } + }), + (i.spawnPowerup = function (t, e) { + var i = Tt.spawn(this.entity.game, ["sneakers", "jetpack", "magnet", "multiplier"]); + (i.body.x = L.pick(-1, 0, 1) * B.laneWidth), (i.body.bottom = t), (i.body.z = e); + }), + (i.pogoSmokeOn = function () { + this.smoke || ((this.smoke = new Xe(5)), (this.smoke.scale.x = 0.5), (this.smoke.scale.y = 1.1), (this.smoke.y = -3)), + this.view.addChild(this.smoke), + this.smoke.turnOn(), + this.entity.game.sfx.play("special_jetpack", { + loop: !0, + }); + }), + (i.pogoSmokeOff = function () { + this.smoke && (this.smoke.turnOff(), this.entity.game.sfx.stop("special_jetpack")); + }), + Pogo + ); + })(J.a); + + function Sneakers_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Ue = (function (n) { + var t, e; + + function Sneakers(t, e) { + var i; + return ( + void 0 === e && (e = {}), + (i = n.call(this, t, e) || this), + t.body.onCollisionEnter.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + (i.mod = 0), + i + ); + } + (e = n), ((t = Sneakers).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Sneakers.prototype; + return ( + (a.show = function () { + this.left || + ((this.left = H.getEntityCloneOpaque("/powerups_superSneakers", "props_tex", !1)), + (this.left.rx = 0.5 * Math.PI), + (this.left.ry = 0.5 * Math.PI), + (this.left.y -= 0.5), + (this.left.z += 0.5), + this.left.scale.set(0.5), + (this.right = H.getEntityCloneOpaque("/powerups_superSneakers", "props_tex", !1)), + (this.right.rx = 0.5 * -Math.PI), + (this.right.ry = 0.5 * Math.PI), + (this.right.y += 0.5), + (this.right.z -= 0.5), + this.right.scale.set(0.5)); + var t = this.entity.anim.scenes[1], + e = H.findEntity("L_Toes_jnt", t.pixiTree, 10), + i = H.findEntity("R_Toes_jnt", t.pixiTree, 10); + e.addChild(this.left), i.addChild(this.right), this.entity.game.hud.addItemTimer("sneakers"); + }), + (a.hide = function () { + this.left.parent && this.left.parent.removeChild(this.left), this.right.parent && this.right.parent.removeChild(this.right), this.entity.game.hud.removeItemTimer("sneakers"); + }), + (a.update = function (t) { + if (this.time && (!this._activated && this.entity.body.landed && this._activate(), this._activated)) { + (this.time -= this.entity.game.deltaSecs), this.jumpUpdate(t), this.entity.game.hud.updateItemTimer("sneakers", this.ratio); + var e = 0.5 * (this.entity.body.bottom - this.entity.player.cameraY); + this.hitTop && (e *= 0.5), + (this.mod = e), + (this.rig.mainX = this.entity.game.stats.x * B.cameraModX), + (this.rig.mainY = this.entity.game.hero.player.cameraY + B.cameraPosY + this.mod), + (this.rig.mainZ = this.entity.game.stats.z + B.cameraPosZ), + (this.rig.mainRotX = B.cameraRotX), + (this.rig.mainRotY = 0); + var i = this.entity.game.level.currentChunk, + n = 80 < this.entity.body.y; + if (!n && i && i.envTube) { + 70 < this.rig.mainY && (this.rig.mainY = 70); + } + if (!n && i && i.envPillars) { + 60 < this.rig.mainY && (this.rig.mainY = 60); + } + this.time <= 0 && (this.entity.body.landed && !this.ascending ? this.turnOff() : (this.time = 0.01)); + } + }), + (a.turnOn = function () { + (this.mod = 0), + this.entity.pogo.turnOff(), + this.entity.jetpack.turnOff(), + this.entity.hoverboard.cancel(), + (this.hitTop = 0), + (this.duration = 10), + (this.time = this.duration), + (this.gravity = B.gravity), + this.show(), + this.entity.game.controller.onSwipeVertical.add(this); + }), + (a.turnOff = function () { + this.time && + (this.entity.jump.unlock(), + (this.time = 0), + (this.mod = 0), + this.entity.game.controller.onSwipeVertical.remove(this), + this.entity.game.camera.releaseControl(), + this.hide(), + this.entity.state.set("empty"), + (this._activated = !1)); + }), + (a._activate = function () { + this.entity.jump.lock(), (this._activated = !0), this.entity.state.set("empty"), (this.rig = this.entity.game.camera.takeControl()); + }), + (a.isOn = function () { + return !!this.time; + }), + (a.onSwipeVertical = function (t) { + 1 == t && this.jump(); + }), + (a.jump = function (t, e) { + void 0 === t && (t = 40), + void 0 === e && (e = !1), + this.time && + ((!e && this.locked) || + (!e && this.isJumping) || + ((e || this.entity.body.canJump) && + (this.entity.roll && this.entity.roll.cancel(), + this.entity.body.resetGroundChangeTolerance(), + (this.isJumping = !0), + (this.entity.body.y += 1), + (this.entity.body.velocity.y = 0), + (this.jumpHeight = 40), + (this.jumpLength = 150), + (this.startPosY = this.entity.body.y), + (this.startPosZ = this.entity.body.z), + (this.ascending = !0), + this.entity.game.sfx.play("hero_sneakers_jump")))); + }), + (a.jumpUpdate = function (t) { + if (this.isJumping && this.ascending) { + var e = (-(this.entity.body.z - this.startPosZ) / this.jumpLength) * 2, + i = e <= 1 ? e : 1; + 1 <= i && (this.ascending = !1); + var n = this.startPosY + this.jumpHeight * St.expoOut(i) - this.entity.body.y; + (this.entity.body.velocity.y = t ? n / t : 0), (this.ascending && !this.hitTop) || (this.entity.body.velocity.y = 0); + } else this.entity.body.velocity.y -= this.gravity * t; + this.entity.body.bottom <= this.entity.body.ground + 0.01 && this.entity.body.velocity.y <= 0 && ((this.entity.body.bottom = this.entity.body.ground), this.isJumping && this.jumpEnd()); + }), + (a.jumpEnd = function () { + (this.hitTop = 0), (this.isJumping = !1), (this.ascending = !1), (this.entity.body.velocity.y = 0); + }), + (a.jumpCancel = function () { + this.jumpEnd(); + }), + (a.onCollisionEnter = function (t) { + t.flags & $.TOP && ((this.entity.body.velocity.y = 0), (this.hitTop = t.hit.bottom - 10)); + }), + (i = Sneakers), + (o = [ + { + key: "ratio", + get: function () { + return this.time / this.duration; + }, + }, + ]) && Sneakers_defineProperties(i.prototype, o), + s && Sneakers_defineProperties(i, s), + Sneakers + ); + })(J.a); + + function Magnet_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Ve = (function (n) { + var t, e; + + function Magnet(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).count = 0), (i.duration = 10), i; + } + (e = n), ((t = Magnet).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Magnet.prototype; + return ( + (a.createView = function () { + this.view || ((this.view = H.getEntityCloneOpaque("/powerups_coinMagnet", "props_tex", !1)), this.view.scale.set(0.5), (this.view.x = -2), (this.view.rz = 0.5 * Math.PI)); + }), + (a.update = function (t) { + this.count && ((this.count -= this.entity.game.stats.delta), this.entity.game.hud.updateItemTimer("magnet", this.ratio), this.count <= 0 && this.turnOff()); + }), + (a.turnOn = function () { + this.createView(), this.entity.game.hud.addMessage("POWERUP: COIN MAGNET"), this.entity.game.hud.addItemTimer("magnet"), (this.count = this.duration); + var t = this.entity.anim.scenes[1]; + H.findEntity("R_Hand_jnt", t.pixiTree, 10).addChild(this.view), + (this.timer = this.entity.game.sfx.play("special_magnet", { + loop: !0, + })); + }), + (a.turnOff = function () { + this.count && + (this.entity.game.hud.clearMessage("POWERUP: COIN MAGNET"), + this.entity.game.hud.removeItemTimer("magnet"), + (this.count = 0), + this.view.parent && this.view.parent.removeChild(this.view), + this.entity.game.sfx.stop("special_magnet")); + }), + (a.isOn = function () { + return !!this.count; + }), + (i = Magnet), + (o = [ + { + key: "ratio", + get: function () { + return this.count / this.duration; + }, + }, + ]) && Magnet_defineProperties(i.prototype, o), + s && Magnet_defineProperties(i, s), + Magnet + ); + })(J.a); + + function Particles_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function Particles_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var qe = { + EntityClass: null, + container: null, + rate: 5, + spawns: 1, + life: 30, + color: null, + x: 0, + y: 0, + z: 0, + xMod: [0, 0], + yMod: [0, 0], + zMod: [0, 0], + velocityX: 0, + velocityY: 0, + velocityZ: 0, + velocityXMod: [0, 0], + velocityYMod: [0, 0], + velocityZMod: [0, 0], + forceX: 0, + forceY: 0, + forceZ: 0, + forceXMod: [0, 0], + forceYMod: [0, 0], + forceZMod: [0, 0], + scaleX: 1, + scaleY: 1, + scaleZ: 1, + scaleXMod: [0, 0], + scaleYMod: [0, 0], + scaleZMod: [0, 0], + growX: 0, + growY: 0, + growZ: 0, + growXMod: [0, 0], + growYMod: [0, 0], + growZMod: [0, 0], + }, + We = (function (n) { + function Particles(t, e) { + var i; + return void 0 === e && (e = {}), (e = Object.assign({}, qe, e)), (i = n.call(this, t, e) || this).setup(e), (i._time = 0), (i.rateCount = 0), (i.list = []), (i.idle = !0), (i.delta = 0), i; + } + Particles_inheritsLoose(Particles, n); + var t, + e, + i, + o = Particles.prototype; + return ( + (o.setup = function (t) { + (this.data = Object.assign({}, qe, t)), this.reset(); + }), + (o.reset = function () { + this.list = []; + }), + (o._update = function (t) { + this.delta = t; + }), + (o.update = function (t) { + if (!this.idle) { + t || (t = this.delta), (this.delta = t); + for (var e = this.list.length; e--; ) { + var i = this.list[e].particle; + i.updateParticle(t), i.data.life <= 0 && this.removeParticle(e); + } + (this.idle = !this.list.length && !this._time), + this._time <= 0 || ((this._time -= t), this._time < 0 && (this._time = 0), (this.rateCount -= t), this.rateCount <= 0 && (this.spawn(this.data.spawns), (this.rateCount = this.data.rate))); + } + }), + (o.run = function (t) { + void 0 === t && (t = 9999999), (this._time = t), (this.rateCount = 0), (this.idle = !1); + }), + (o.stop = function () { + this._time = 0; + }), + (o.clear = function () { + this.stop(); + for (var t = this.list.length; t--; ) this.removeParticle(t); + }), + (o.removeParticle = function (t) { + var e = this.list[t]; + e && (this.list.splice(t, 1), e.parent && (e.parent.removeChild(e), this.entity.game.pool["return"](e))); + }), + (o.spawn = function (t, e) { + void 0 === t && (t = 1), e || (e = this.data), (this.idle = !1); + var i = e.EntityClass, + n = this.entity.game.pool.get(i); + n.particle || n.add(Ze), n.particle.spawn(e), (e.container || this.entity.game).addChild(n), this.list.push(n), (n.particle.active = !0), 1 < t && this.spawn(t - 1, e); + }), + (t = Particles), + (e = [ + { + key: "time", + get: function () { + return this._time; + }, + set: function (t) { + (this.idle = t <= 0), (this._time = t); + }, + }, + ]) && Particles_defineProperties(t.prototype, e), + i && Particles_defineProperties(t, i), + Particles + ); + })(J.a), + Ze = (function (n) { + function Particle(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).data = Object.assign({}, qe)), i; + } + Particles_inheritsLoose(Particle, n); + var t = Particle.prototype; + return ( + (t.spawn = function (t) { + Object.assign(this.data, qe, t), + (this.entity.x = this.data.x + L.range(this.data.xMod[0], this.data.xMod[1])), + (this.entity.y = this.data.y + L.range(this.data.yMod[0], this.data.yMod[1])), + (this.entity.z = this.data.z + L.range(this.data.zMod[0], this.data.zMod[1])), + (this.entity.scale.x = this.data.scaleX + L.range(this.data.scaleXMod[0], this.data.scaleXMod[1])), + (this.entity.scale.y = this.data.scaleY + L.range(this.data.scaleYMod[0], this.data.scaleYMod[1])), + (this.entity.scale.z = this.data.scaleZ + L.range(this.data.scaleZMod[0], this.data.scaleZMod[1])), + (this.data.growX = this.data.growX + L.range(this.data.growXMod[0], this.data.growXMod[1])), + (this.data.growY = this.data.growY + L.range(this.data.growYMod[0], this.data.growYMod[1])), + (this.data.growZ = this.data.growZ + L.range(this.data.growZMod[0], this.data.growZMod[1])), + (this.data.velocityX += L.range(this.data.velocityXMod[0], this.data.velocityXMod[1])), + (this.data.velocityY += L.range(this.data.velocityYMod[0], this.data.velocityYMod[1])), + (this.data.velocityZ += L.range(this.data.velocityZMod[0], this.data.velocityZMod[1])), + (this.data.forceX += L.range(this.data.forceXMod[0], this.data.forceXMod[1])), + (this.data.forceY += L.range(this.data.forceYMod[0], this.data.forceYMod[1])), + (this.data.forceZ += L.range(this.data.forceZMod[0], this.data.forceZMod[1])); + }), + (t.updateParticle = function (t) { + (this.data.life -= t), + this.data.life < 0 && (this.data.life = 0), + (this.entity.x += this.data.velocityX * t), + (this.entity.y += this.data.velocityY * t), + (this.entity.z += this.data.velocityZ * t), + (this.data.velocityX += this.data.forceX * t), + (this.data.velocityY += this.data.forceY * t), + (this.data.velocityZ += this.data.forceZ * t), + (this.entity.scale.x += this.data.growX * t), + (this.entity.scale.y += this.data.growY * t), + (this.entity.scale.z += this.data.growZ * t); + }), + Particle + ); + })(J.a); + var Ke = (function (i) { + var t, e; + + function ParticleHoverCollision() { + var t; + ((t = i.call(this) || this).view = H.particle(16, 16, 1, "spraySplash")), t.addChild(t.view); + var e = L.pick(16256769, 10287617, 12040194); + return H.tint(t.view, e), t; + } + return (e = i), ((t = ParticleHoverCollision).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), ParticleHoverCollision; + })(v.a); + var Je = (function (e) { + var t, i; + + function ParticleSpark() { + var t; + return ( + ((t = e.call(this) || this).view = H.getEntityCloneBlend("/grindSpark", "effects_tex", 0.8, 1)), + (t.view.ry = 0.5 * Math.PI), + (t.view.view3d.state.blend = !0), + (t.view.view3d.state.depthTest = !0), + (t.view.view3d.orderBias = 999), + t.addChild(t.view), + t.view.scale.set(0.3), + H.tint(t.view, 16776960), + t + ); + } + return (i = e), ((t = ParticleSpark).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i), ParticleSpark; + })(v.a); + + function Hoverboard_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var Qe = (function (n) { + var t, e; + + function Hoverboard(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).count = 0), (i.duration = 30), i; + } + (e = n), ((t = Hoverboard).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Hoverboard.prototype; + return ( + (a.show = function () { + this.view || (this.view = H.getEntityClone("board_newYork_base")), this.entity.game.hud.addItemTimer("hoverboard"), this.entity.anim.scenes[1].attachPoint1.addChild(this.view), this.entity.state.set("empty"); + }), + (a.hide = function () { + this.view.parent && this.view.parent.removeChild(this.view); + }), + (a.update = function (t) { + this.count && + !this.paused && + ((this.view.y = 0.2), (this.view.x = 14), (this.view.z = -7), (this.count -= this.entity.game.stats.delta), this.entity.game.hud.updateItemTimer("hoverboard", this.ratio), this.count <= 0 && this.end()); + }), + (a.updateGrinding = function () { + this.entity.grindingSparks || this.createGrindingSparks(), + (this.entity.grindingSparks.time = 10), + (this.entity.grindingSparks.data.x = this.entity.x - 1), + (this.entity.grindingSparks.data.y = this.entity.y - 4.5), + (this.entity.grindingSparks.data.z = this.entity.z - 0.5); + }), + (a.enable = function () { + this.entity.game.controller.onDoubleTap.add(this); + }), + (a.disable = function () { + this.entity.game.controller.onDoubleTap.remove(this); + }), + (a.pause = function () { + this.count && (this.paused = !0); + }), + (a.resume = function () { + this.count && + this.paused && + ((this.paused = !1), + this.entity.state.set("empty"), + this.show(), + this.entity.anim.play("h_jump3_bs360grab", { + loop: !1, + })); + }), + (a.isOn = function () { + return !!this.count && !this.paused; + }), + (a.explode = function () { + this.end(), + this.entity.collisionSmoke || + this.entity.add( + We, + { + EntityClass: Ke, + container: this.entity.game, + rate: 1, + life: 60, + velocityXMod: [-1.5, 1.5], + velocityYMod: [1, 3], + velocityZMod: [-1, -2], + scaleXMod: [0.8, 1.5], + scaleYMod: [0.8, 1.5], + scaleZMod: [0.8, 1.5], + growXMod: [0.01, 0.03], + growYMod: [0.01, 0.03], + growZMod: [0.01, 0.03], + }, + "collisionSmoke" + ), + (this.entity.collisionSmoke.data.x = this.entity.x), + (this.entity.collisionSmoke.data.y = this.entity.y), + (this.entity.collisionSmoke.data.z = this.entity.z), + this.entity.collisionSmoke.spawn(10), + this.entity.jump.perform(15, !0), + this.entity.player.dizzyEnd(); + }), + (a.onDoubleTap = function () { + this.start(); + }), + (a.start = function () { + var t = this; + this.entity.game.state === pn.RUNNING && + this.entity.game.stats.hoverboards && + (this.entity.pogo.isOn() || + this.entity.jetpack.isOn() || + this.locked || + (this.entity.anim.play("h_skate_on", { + loop: !1, + enforce: 0.3, + sudden: !0, + }), + setTimeout(function () { + t.entity.anim.play("h_landing", { + loop: !1, + enforce: 0.3, + }), + t.entity.anim.play("h_run", { + loop: !0, + }); + }, 500), + this.show(), + (this.count = this.duration), + (this.paused = !1), + (this.entity.game.stats.hoverboards -= 1), + this.entity.player.dizzyEnd(), + this.entity.state.set("empty"), + this.entity.popPickup && this.entity.popPickup.play())); + }), + (a.cancel = function () { + this.count && (this.end(), (this.entity.game.stats.hoverboards += 1)); + }), + (a.end = function () { + this.count && (this.hide(), this.entity.game.hud.removeItemTimer("hoverboard"), (this.count = 0), this.entity.state.set("empty")); + }), + (a.lock = function () { + this.locked = !0; + }), + (a.unlock = function () { + this.locked = !1; + }), + (a.createGrindingSparks = function () { + this.entity.grindingSparks || + this.entity.add( + We, + { + EntityClass: Je, + container: this.entity.game, + rate: 0.5, + spawns: 1, + life: 20, + xMod: [-0.4, 0.4], + velocityXMod: [-0.2, 0.2], + velocityYMod: [0.03, 0.08], + velocityZMod: [-0.1, -0.2], + growXMod: [0.1, 0.3], + growZMod: [0.3, 0.6], + }, + "grindingSparks" + ); + }), + (i = Hoverboard), + (o = [ + { + key: "ratio", + get: function () { + return this.count / this.duration; + }, + }, + { + key: "grinding", + get: function () { + return !!this.count && !this.paused && this.entity.body.landed && 29 < this.entity.body.ground && this.entity.body.ground < 29.2; + }, + }, + ]) && Hoverboard_defineProperties(i.prototype, o), + s && Hoverboard_defineProperties(i, s), + Hoverboard + ); + })(J.a); + + function Multiplier_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var $e = (function (n) { + var t, e; + + function Multiplier(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).count = 0), (i.duration = 10), i; + } + (e = n), ((t = Multiplier).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Multiplier.prototype; + return ( + (a.update = function (t) { + this.count && ((this.count -= this.entity.game.stats.delta), this.entity.game.hud.updateItemTimer("multiplier", this.ratio), this.count <= 0 && this.turnOff()); + }), + (a.turnOn = function () { + this.count ? (this.count = this.duration) : ((this.count = this.duration), this.entity.game.hud.addMessage("POWERUP: MULTIPLIER"), this.entity.game.hud.addItemTimer("multiplier"), (this.entity.game.stats.multiplier *= 2)); + }), + (a.turnOff = function () { + this.count && (this.entity.game.hud.clearMessage("POWERUP: MULTIPLIER"), this.entity.game.hud.removeItemTimer("multiplier"), (this.entity.game.stats.multiplier /= 2), (this.count = 0)); + }), + (a.isOn = function () { + return !!this.count; + }), + (i = Multiplier), + (o = [ + { + key: "ratio", + get: function () { + return this.count / this.duration; + }, + }, + ]) && Multiplier_defineProperties(i.prototype, o), + s && Multiplier_defineProperties(i, s), + Multiplier + ); + })(J.a), + ti = i(2); + + function Player_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + var ei = (function (n) { + var t, e; + + function Player(t, e) { + var i; + return ( + void 0 === e && (e = {}), + (i = n.call(this, t, e) || this), + t.lane.onBumpSideways.add(Player_assertThisInitialized(i)), + t.body.onCollisionEnter.add(Player_assertThisInitialized(i)), + t.body.onCollisionExit.add(Player_assertThisInitialized(i)), + t.body.onTriggerEnter.add(Player_assertThisInitialized(i)), + t.body.onTriggerExit.add(Player_assertThisInitialized(i)), + i.reset(), + i + ); + } + (e = n), ((t = Player).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Player.prototype; + return ( + (i.reset = function (t, e, i) { + void 0 === t && (t = 0), + void 0 === e && (e = 0), + void 0 === i && (i = null), + this.entity.body.reset(), + (this.entity.body.lane = 0), + (this.entity.body.x = e), + (this.entity.body.z = t), + (this.entity.body.lane = i || 0), + (this.entity.body.bottom = 0), + (this.entity.body.movable = !0), + (this.entity.body.glide = 0), + (this.entity.body.laning = 0), + (this.entity.body.ghost = !1), + (this.entity.x = this.entity.body.x), + (this.entity.y = this.entity.body.y), + (this.entity.z = this.entity.body.z), + this.entity.resetModel(), + (this.dizzy = 0), + (this.jumpLocked = !1), + (this.rollLocked = !1), + (this.hoverboardLocked = !1), + (this.running = !1), + (this.cameraY = 0), + (this.cameraTargetY = 0), + (this.cameraRotX = 0), + (this.cameraLow = 0), + (this.tunnel = !1), + (this.dead = !1), + (this.deathCause = ""), + (this.catchMode = ""), + (this.rewindStartPoint = new ti.P()), + (this.rewindEndPoint = null); + }), + (i.run = function (t) { + void 0 === t && (t = 200), + this.entity.game.level.onEnterTutorial.contains(this) || (this.entity.game.level.onEnterTutorial.add(this), this.entity.game.level.onExitTutorial.add(this)), + (this.entity.body.gravity = this.entity.game.config.gravity), + (this.entity.body.velocity.z = -B.speed), + (this.entity.body.height = 11), + (this.entity.body.bottom = 0), + (this.entity.body.movable = !0), + (this.running = !0), + (this.jumpForce = this.entity.game.config.jump), + (this.dizzy = t), + this.entity.shadow.turnOn(), + this.entity.lane.turnOn(), + this.entity.jump.turnOn(), + this.entity.roll.turnOn(), + this.entity.hoverboard.enable(), + this.entity.hoverboard.isOn() + ? this.entity.anim.play("h_run", { + loop: !0, + sudden: !0, + }) + : this.entity.anim.play("run2", { + loop: !0, + sudden: !0, + }), + this.entity.game.sfx.stop("special_jetpack"); + }), + (i.stop = function () { + (this.running = !1), (this.entity.body.velocity.z = 0); + }), + (i.update = function (t) { + if ( + (this.rewindEndPoint && + ((this.rewinding = !0), + this.entity.body.velocity.reset(), + (this.entity.body.ghost = !0), + (this.entity.body.x = V.lerp(this.entity.body.x, 0, 0.1 * t)), + (this.entity.body.bottom = V.lerp(this.entity.body.bottom, 0, 0.3 * t)), + (this.entity.body.z += 4 * t), + this.entity.body.z >= this.rewindEndPoint.z - 0.1 && + (this.entity.lane.reset(), (this.entity.body.z = this.rewindEndPoint.z), (this.entity.body.velocity.z = 0), (this.entity.body.ghost = !1), (this.rewinding = !1), this.reset(this.rewindEndPoint.z), this.run())), + this.running && !this.dead) + ) { + if (!this.entity.jetpack.isOn()) { + var e = this.entity.game.stats.speed, + i = V.lerp(this.entity.body.velocity.z, -e, 0.1 * t); + this.entity.body.velocity.z = i; + } + var n = this.entity.body.ground, + o = this.entity.body.bottom; + n >= this.cameraTargetY && this.entity.body.landed ? (this.cameraTargetY = n) : o < this.cameraTargetY && (this.cameraTargetY = o); + } else this.entity.body.velocity.z = 0; + (this.cameraY = V.lerp(this.cameraY, this.cameraTargetY, 0.2 * t)), + this.cameraY > this.entity.body.bottom + 3 && (this.cameraY = this.entity.body.bottom + 3), + this.dizzy && ((this.dizzy -= t), this.dizzy <= 0 && this.dizzyEnd()), + this.bumpCount && ((this.bumpCount -= t), this.bumpCount <= 0 && (this.bumpCount = 0)); + }), + (i.getMode = function () { + return this.entity.hoverboard.isOn() ? "hoverboard" : this.entity.sneakers.isOn() ? "sneakers" : "normal"; + }), + (i.dizzyStart = function () { + (this.dizzy = 200), this.entity.dizzy.turnOn(), this.entity.game.sfx.play("hero_stumble"); + }), + (i.dizzyEnd = function () { + (this.dizzy = 0), this.entity.dizzy.turnOff(); + }), + (i.lockHoverboard = function (t) { + this.hoverboardLocked = t; + }), + (i.goBackToLastCheckPoint = function () { + this.entity.anim.play("run3", { + loop: !0, + }), + (this.rewindStartTime = this.entity.game.time), + (this.rewindDuration = 600), + this.rewindStartPoint.copy(this.entity.body.center); + var t = this.entity.game.level.currentChunk.getLastCheckpointByPosition(this.entity.body.z); + t && (this.rewindEndPoint = t.body.center); + }), + (i.stumble = function (t, e) { + void 0 === t && (t = "lower"), void 0 === e && (e = !1), (this.bumpCount && e) || (this.entity.game.camera.shake(3), this.dizzy ? this.die(t) : (this.dizzyStart(), (this.bumpCount = 20))); + }), + (i.crash = function (t) { + void 0 === t && (t = "train"), (this.cameraLow = 0), (this.tunnel = !1), this.entity.game.camera.shake(5), this.die(t); + }), + (i.die = function (t) { + var e = this; + if (this.entity.hoverboard.isOn()) + return ( + this.entity.game.level.reshuffle(), + this.entity.hoverboard.explode(), + void setTimeout(function () { + e.entity.game.exitTunnel(), e.entity.game.sfx.play("hero_hoverboard_crash"); + }, 1) + ); + (this.entity.body.z += 5), + (this.dead = !0), + (this.deathCause = t), + this.entity.game.sfx.play("hero_death"), + this.entity.dizzy.turnOff(), + this.entity.jetpack.turnOff(), + this.entity.magnet.turnOff(), + this.entity.pogo.turnOff(), + this.entity.shadow.turnOff(), + this.entity.lane.turnOff(), + this.entity.jump.turnOff(), + this.entity.roll.turnOff(), + this.entity.sneakers.turnOff(), + this.entity.hoverboard.disable(), + this.entity.game.sfx.stop("special_jetpack"), + this.entity.game.level.isTutorial() + ? setTimeout(function () { + e.goBackToLastCheckPoint(); + }, 700) + : setTimeout(function () { + e.entity.game.gameover(); + }, 200); + }), + (i.onBumpSideways = function (t) { + this.stumble("lower"); + }), + (i.onCollisionEnter = function (t) { + B.god || + (t.flags & $.FRONT + ? ((this.entity.body.velocity.z = 0), + this.entity.lane.changing || t.hit.height < 1 + ? this.stumble("bounce", !0) + : t.pas.movable + ? this.crash("train") + : 6 < t.hit.height + ? this.crash("bounce") + : t.hit.y > this.entity.body.y + ? this.crash("upper") + : this.crash("lower")) + : t.flags & $.LEFT || t.flags & $.RIGHT + ? this.entity.lane.bump(-this.entity.lane.absStep) + : t.flags & $.SLOPE && (this.stumble("bounce", !0), (this.entity.body.velocity.z *= 0.5))); + }), + (i.onCollisionExit = function (t) {}), + (i.onTriggerEnter = function (t) { + t.entity.lowCamera && !this.entity.jetpack.isOn() ? ((this.cameraLow = -15), (this.tunnel = !0), this.entity.game.enterTunnel()) : t.entity.tutorialTrigger && this.entity.game.tutorial.enterTrigger(t.entity.type); + }), + (i.onTriggerExit = function (t) { + t.entity.lowCamera && ((this.cameraLow = 0), (this.tunnel = !1), this.entity.game.exitTunnel()), t.entity.tutorialTrigger && this.entity.game.tutorial.exitTrigger(t.entity.type); + }), + (i.onEnterTutorial = function () {}), + (i.onExitTutorial = function () {}), + Player + ); + })(J.a); + var ii = (function (i) { + var t, e; + + function Shadow(t, e) { + return void 0 === e && (e = {}), i.call(this, t, e) || this; + } + (e = i), ((t = Shadow).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Shadow.prototype; + return ( + (n.createView = function () { + this.view || ((this.view = H.plane(8, 8, 0.8, "shadow_mip.png")), (this.view.rx = V.PI_HALF), (this.view.z = 1), this.entity.addChild(this.view)); + }), + (n.update = function (t) { + if (this.view && this.view.active) { + var e = this.entity.body ? this.entity.body.ground : 0; + this.view.y = -this.entity.y + e + 1; + } + }), + (n.turnOn = function () { + this.createView(), (this.view.active = !0); + }), + (n.turnOff = function () { + this.view.active = !1; + }), + (n.isOn = function () { + return this.view.active; + }), + Shadow + ); + })(J.a), + ni = i(18), + oi = i(129), + si = i.n(oi), + ai = i(130), + ri = i.n(ai); + c.Shader; + var hi = { + character_idle: i(475), + character_jetpack: i(476), + character_movement: i(477), + character_catch: i(478), + character_pogo: i(479), + character_start: i(480), + guard_movement: i(481), + guard_catch: i(482), + dog_movement: i(483), + dog_catch: i(484), + }; + + function Anim_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var di = (function (n) { + var t, e; + + function Anim(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).scene = null), (i.fpsGame = 60), (i.currentAction = null), (i.scenes = {}), (i.clips = {}), (i.actions = {}), i; + } + (e = n), ((t = Anim).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Anim.prototype; + return ( + (a.reset = function () { + (this.currentAction = null), this.stop(); + }), + (a.addAnim = function (t, e) { + if (!this.scenes[t]) { + var i = e.offset || 0, + n = H.getScene(e.file); + if (!n) throw new Error("Scene not found: " + e.file); + H.applyTexture(n.pixiTree, e.texture, !1); + var o = H.findEntity("Mess", n.pixiTree); + for (var s in (o && o.parent && o.parent.removeChild(o), + n.initMixer(), + ((this.scenes[t] = n).attachPoint1 = H.findEntity("attachPoint1", n.pixiTree)), + (n.attachPoint2 = H.findEntity("attachPoint2", n.pixiTree)), + (n.attachPoint3 = H.findEntity("attachPoint3", n.pixiTree)), + (n.pixiTree.active = !1), + e.clips)) { + var a = e.fps, + r = n.sceneGraph.animations[0], + h = e.clips[s], + d = h[0] + i, + l = h[1] + i, + c = ni.g(r, s, d, l, a); + (c.__scene = n), (c.__name = s), (this.clips[s] = c); + } + this.updateActions(); + } + }), + (a.updateActions = function () { + for (var t in this.clips) + if (!this.actions[t]) { + var e = this.clips[t], + i = e.__scene.mixer.clipAction(e); + i.setLoop(ti.s), (i.clampWhenFinished = !0), (i.__scene = e.__scene), (i.__name = t), (this.actions[t] = i); + } + }), + (a.play = function (t, e) { + var i = this; + if ((void 0 === e && (e = {}), Array.isArray(t) && (t = L.pick.apply(L, t)), !this.actions[t])) throw new Error("Animation not found: " + t); + if ((void 0 === e.loop && (e.loop = !1), void 0 === e.sudden && (e.sudden = !1), void 0 === e.crossfade && (e.crossfade = 0.2), void 0 === e.enforce && (e.enforce = 0), this._enforce)) + this._queued = { + name: t, + opts: e, + }; + else { + e.enforce && + ((this._enforce = e.enforce), + setTimeout(function () { + (i._enforce = 0), i._queued && (i.play(i._queued.name, i._queued.opts), (i._queued = null)); + }, 1e3 * this._enforce)); + var n = this.actions[t]; + if (n !== this.currentAction) { + var o = n.__scene, + s = this.currentAction && this.currentAction.__scene === o; + return ( + o.mixer.stopAllAction(), + this.currentAction && (this.currentAction.stop(), this.currentAction.reset()), + n.stop(), + n.reset(), + n.setLoop(e.loop ? ti.s : ti.r), + this.currentAction && !e.sudden && s && n.crossFadeFrom(this.currentAction, e.crossfade, !0), + n.play(), + o.mixer.update(0.01), + o.syncToPixi(), + this.showScene(o), + (this.currentAction = n) + ); + } + } + }), + (a.showScene = function (e) { + var i = this; + (e.pixiTree.active = !0), + (this.entity.model || this.entity).addChild(e.pixiTree), + setTimeout(function () { + for (var t in i.scenes) i.scenes[t].pixiTree.active = !1; + e.pixiTree.active = !0; + }, 20); + }), + (a.stop = function () { + this.currentAction && (this.currentAction.stop(), this.currentAction.reset(), (this.currentAction = null)); + }), + (a.update = function (t) { + if (this.scenes && this.currentAction) { + var e = this.currentAction.__scene; + e.mixer.update(t / this.fpsGame), e.syncToPixi(); + } + }), + (a.sum = function (t) { + this.extra = t; + var e = this.actions[this.extra]; + e.setEffectiveWeight(0.75), e.setLoop(!0), e.play(); + }), + (a.subtract = function (t) { + this.actions[this.extra].stop(), (this.extra = null); + }), + (i = Anim), + (o = [ + { + key: "currentName", + get: function () { + return this.currentAction ? this.currentAction.__name : null; + }, + }, + ]) && Anim_defineProperties(i.prototype, o), + s && Anim_defineProperties(i, s), + Anim + ); + })(J.a); + + function State_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var li = function () {}, + ci = (function (n) { + var t, e; + + function State(t, e) { + var i; + return ( + ((i = n.call(this, t, e) || this).states = {}), + (i.currentStateId = "empty"), + (i.transitionMap = {}), + i.add("empty", { + end: li, + begin: li, + update: li, + render: li, + }), + (i.params = {}), + i + ); + } + (e = n), ((t = State).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = State.prototype; + return ( + (a.addTransition = function (t, e, i) { + return this.transitionMap[t] || (this.transitionMap[t] = {}), (this.transitionMap[t][e] = !0), i && (this.transitionMap[e][t] = !0), this; + }), + (a.add = function (t, e) { + return ( + ((this.states[t] = e).id = t), + (e.entity = this.entity), + e.empty || (e.empty = li), + e.begin || (e.begin = li), + e.update || (e.update = li), + e.render || (e.render = li), + e.end || (e.end = li), + this.transitionMap[t] || (this.transitionMap[t] = {}), + this.addTransition("empty", t, !0), + this + ); + }), + (a.set = function (t) { + this.can(t) && (this.states[this.currentStateId].end(), (this.currentStateId = t), this.states[this.currentStateId].begin()); + }), + (a.can = function (t) { + if (this.currentStateId === t) return !1; + var e = this.transitionMap.all[t], + i = this.transitionMap[this.currentStateId].all, + n = this.transitionMap[this.currentStateId][t]; + return e || i || n; + }), + (a.update = function (t) { + if (this.entity.onStateUpdate) { + this.entity.onStateUpdate(); + var e = this.params, + i = this.states; + for (var n in i) + if ("empty" !== n) { + var o = i[n], + s = !0; + for (var a in e) + if (void 0 !== o[a] && o[a] !== e[a]) { + s = !1; + break; + } + if (s && this.currentStateId !== n) { + var r = this.transitionMap.all[n], + h = this.transitionMap[this.currentStateId].all, + d = this.transitionMap[this.currentStateId][n]; + if (r || h || d) { + this.set(n); + break; + } + } + } + } + this.states[this.currentStateId].update(t); + }), + (a.render = function (t) { + this.states[this.currentStateId].render(t); + }), + (i = State), + (o = [ + { + key: "id", + get: function () { + return this.currentStateId; + }, + }, + ]) && State_defineProperties(i.prototype, o), + s && State_defineProperties(i, s), + State + ); + })(J.a); + var ui = (function (i) { + var t, e; + + function Dizzy(t, e) { + return void 0 === e && (e = {}), i.call(this, t, e) || this; + } + (e = i), ((t = Dizzy).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Dizzy.prototype; + return ( + (n.createView = function () { + this.view || + ((this.view = new v.a()), + (this.view.y = 4), + (this.view.x = 0.5), + (this.trail1 = H.getEntityCloneBlend("effects/Dizzytrail", "effects_tex", 0.9, 1)), + this.view.addChild(this.trail1), + (this.trail2 = H.getEntityCloneBlend("effects/Dizzytrail", "effects_tex", 0.9, 1)), + (this.trail2.ry = Math.PI), + this.view.addChild(this.trail2), + (this.star1 = H.getEntityCloneBlend("effects/Dizzystar", "effects_tex", 0.9, 1)), + (this.star1.z = -1.5), + this.view.addChild(this.star1), + (this.star2 = H.getEntityCloneBlend("effects/Dizzystar", "effects_tex", 0.9, 1)), + (this.star2.z = 1.5), + this.view.addChild(this.star2), + (this.view.active = !1), + (this.view.rx = -0.5), + this.view.scale.set(1.25)); + }), + (n.reset = function () { + this.view && (this.view.active = !1); + }), + (n.update = function (t) { + this.view && this.view.active && (this.view.ry += 0.05 * t); + }), + (n.turnOn = function () { + if ((this.createView(), !this.view.parent)) { + var t = this.entity.anim.scenes[1]; + H.findEntity("Head_jnt", t.pixiTree, 10).addChild(this.view); + } + this.view.active = !0; + }), + (n.turnOff = function () { + this.view && (this.view.parent && this.view.parent.removeChild(this.view), (this.view.active = !1)); + }), + Dizzy + ); + })(J.a); + var pi = (function (n) { + var t, e; + + function PopPickup(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).view = H.getEntityCloneBlend("/powBlue", "effects_tex", 0.95, 1)), (i.view.rotation.y = Math.PI), (i.duration = 13), i.reset(), i; + } + (e = n), ((t = PopPickup).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = PopPickup.prototype; + return ( + (i.reset = function () { + this.view.parent && this.entity.removeChild(this.view), (this.view.active = !1), this.view.scale.set(0), (this.count = 0); + }), + (i.update = function (t) { + if (this.count) { + this.count -= t; + var e = 1 + 20 * (1 - this.count / this.duration); + this.view.scale.set(e), this.count <= 0 && this.reset(); + } + }), + (i.play = function () { + (this.view.rotation.z = V.PI_DOUBLE * Math.random()), (this.count = this.duration), (this.view.active = !0), this.view.scale.set(0.5), this.entity.addChild(this.view); + }), + PopPickup + ); + })(J.a); + var mi = (function (i) { + var t, e; + + function ParticleRevive() { + var t; + ((t = i.call(this) || this).view = H.particle(16, 16, 1, "spraySplash")), t.addChild(t.view); + var e = L.pick(1407438, 4308976, 2726108); + return H.tint(t.view, e), t; + } + return (e = i), ((t = ParticleRevive).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), ParticleRevive; + })(v.a); + var fi = (function (n) { + var t, e; + + function ReviveHalo(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).view = H.getEntityCloneBlend("powRevive", "effects_tex", 0.7, 3)), + (i.view.view3d.state.depthTest = !1), + (i.view.rotation.y = Math.PI), + (i.view.rotation.x = -0.3), + t.addChild(i.view), + (i.scale = 2), + i.view.scale.set(i.scale), + (i.view.y = 2), + (i.view.active = !1), + (i.time = 0), + (i.duration = 120), + i + ); + } + (e = n), ((t = ReviveHalo).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = ReviveHalo.prototype; + return ( + (i.update = function (t) { + if (this.time) { + (this.time -= t), (this.scale = 0.2 * Math.sin(0.1 * this.time) + 2), this.view.scale.set(this.scale); + var e = 0.1 * this.duration, + i = 1 - (this.time - e) / (this.duration - e), + n = this.time / e, + o = this.time > e ? i : n; + (this.view.view3d.material.opacity = 0.6 * o), (this.view.rotation.z += 0.01 * t), this.time < 0 && this.stop(); + } + }), + (i.play = function () { + (this.view.active = !0), (this.time = this.duration); + }), + (i.stop = function () { + (this.time = 0), (this.view.active = !1); + }), + ReviveHalo + ); + })(J.a), + gi = (function () { + function Time() { + (this.scale = 1), (this.current = 0), (this.delta = 0), (this._schedule = []); + } + var t = Time.prototype; + return ( + (t.reset = function () { + (this.scale = 1), (this.current = 0), (this.delta = 0), (this._schedule = []); + }), + (t.update = function (t) { + (this.delta = t * this.scale), (this.current += this.delta); + for (var e = this._schedule.length; e--; ) { + var i = this._schedule[e]; + i.time >= this.current && (i.fn.call(i.context, i.params), this._schedule.splice(e, 1)); + } + }), + (t.schedule = function (t, e, i, n) { + void 0 === i && (i = null), void 0 === n && (n = null); + var o = { + fn: t, + time: this.current + e, + context: i, + params: n, + }; + this._schedule.push(o); + }), + (Time.secs = function () { + return new Date().getTime() / 1e3; + }), + Time + ); + })(); + + function Lane_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var yi = (function (i) { + var t, e; + + function Lane(t) { + var e; + return ((e = i.call(this, t) || this).entity = t), (e.onBumpSideways = new et.a("onBumpSideways", 1)), e.reset(), e; + } + (e = i), ((t = Lane).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n, + o, + s, + a = Lane.prototype; + return ( + (a.reset = function () { + (this.lane = 0), (this.last = 0), (this.absStep = 0), (this.changing = !1), (this.changeStartX = 0), (this.changeEndX = 0), (this.changeDuration = 0), (this.changeTime = 0), (this.queuedStep = 0), (this.queuedDuration = 0); + }), + (a.turnOn = function () { + this._isOn || ((this._isOn = !0), this.entity.game.controller.onSwipeHorizontal.add(this)); + }), + (a.turnOff = function () { + this._isOn && ((this._isOn = !1), this.entity.game.controller.onSwipeHorizontal.remove(this)); + }), + (a.onSwipeHorizontal = function (t) { + this.change(t); + }), + (a.change = function (t) { + if (!this.entity.jetpack.isOn() && !this.entity.pogo.isOn()) { + if (1 === this.lane && 0 < t) return void (this.entity.body.x > B.laneWidth - 1 && this.onBumpSideways.dispatch(t)); + if (-1 === this.lane && t < 0) return void (this.entity.body.x < 1 - B.laneWidth && this.onBumpSideways.dispatch(t)); + } + var e = 0.3 * (1 - this.entity.game.stats.speed / B.maxSpeed) + 0.1, + i = Math.round(this.lane + t), + n = Math.clamp(i, -1, 1), + o = n * B.laneWidth, + s = Math.abs(o - this.entity.body.x); + if (s > B.laneWidth) return (this.queuedStep = t), void (this.queuedDuration = e); + this.entity.state.set("empty"), + (this.absStep = t < 0 ? -1 : 1), + (this.last = this.lane), + (this.lane = n), + (this.queuedStep = 0), + (this.queuedDuration = 0), + (this.changing = !0), + (this.changeStartX = this.entity.body.x), + (this.changeEndX = o), + (this.changeDuration = Math.max((e * s) / B.laneWidth, 0.1)), + this.entity.jetpack.isOn() && (this.changeDuration *= 0.7), + (this.changeTime = 0), + (this._secs = gi.secs()), + this.entity.game.sfx.play("hero_dodge"); + }), + (a.update = function (t) { + if (this.changing) { + (this.changeTime += this.entity.game.deltaSecs), this.changeTime > this.changeDuration && (this.changeTime = this.changeDuration); + var e = V.clamp(this.changeTime / this.changeDuration), + i = V.lerp(this.changeStartX, this.changeEndX, e); + this.entity.body.velocity.x = t ? (i - this.entity.body.x) / t : 0; + var n = 0.05 * -(this.changeEndX - this.entity.body.x), + o = this.entity.jetpack.isOn() || this.entity.pogo.isOn() || this.entity.hoverboard.isOn(); + (this.entity.ry = o ? 0 : n), this.changeTime >= this.changeDuration && this.changeEnd(); + } + }), + (a.changeEnd = function () { + (this.entity.body.x = this.lane * B.laneWidth), + (this.changing = !1), + (this.entity.ry = 0), + (this.entity.body.velocity.x = 0), + this.queuedDuration && (this.change(this.queuedStep, this.queuedDuration), (this.queuedStep = 0), (this.queuedDuration = 0)); + }), + (a.changeCancel = function () { + (this.changing = !1), (this.queuedStep = 0), (this.queuedDuration = 0), (this.entity.ry = 0), (this.entity.body.velocity.x = 0); + }), + (a.bump = function (t) { + this.changeCancel(), this.change(t, 0.1), this.onBumpSideways.dispatch(t), (this.entity.body.velocity.x = 0); + }), + (n = Lane), + (o = [ + { + key: "lanePos", + get: function () { + return this.entity.body.x / B.laneWidth; + }, + set: function (t) { + (t = Mathf.clamp(t, -1, 1)), (this.entity.body.x = B.laneWidth * t); + }, + }, + ]) && Lane_defineProperties(n.prototype, o), + s && Lane_defineProperties(n, s), + Lane + ); + })(J.a); + var vi = (function (i) { + var t, e; + + function Jump(t) { + var e; + return (e = i.call(this, t) || this).reset(), e; + } + (e = i), ((t = Jump).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Jump.prototype; + return ( + (n.reset = function () { + (this.locked = !1), (this.isJumping = !1), (this.goingUpwards = !1), (this.gravity = B.gravity); + }), + (n.turnOn = function () { + this._isOn || ((this._isOn = !0), this.entity.game.controller.onSwipeVertical.add(this)); + }), + (n.turnOff = function () { + this._isOn && ((this._isOn = !1), this.entity.game.controller.onSwipeVertical.remove(this)); + }), + (n.onSwipeVertical = function (t) { + 1 == t && this.perform(); + }), + (n.update = function (t) { + if (!this.locked) { + if (this.isJumping && this.goingUpwards) { + (this.time += this.entity.game.deltaSecs), this.time > this.duration && (this.time = this.duration); + var e = this.time / this.duration, + i = St.sineOut(e), + n = V.lerp(this.startY, this.endY, i); + (this.entity.body.velocity.y = t ? (n - this.entity.body.y) / t : 0), 1 <= e && ((this.entity.body.velocity.y = 0), (this.goingUpwards = !1)); + } else this.entity.body.velocity.y -= this.gravity * t; + this.entity.body.bottom <= this.entity.body.ground + 0.01 && this.entity.body.velocity.y <= 0 && ((this.entity.body.velocity.y = 0), (this.entity.body.bottom = this.entity.body.ground), this.isJumping && this.end()); + } + }), + (n.perform = function (t, e) { + void 0 === t && (t = 20), + void 0 === e && (e = !1), + B.freejump && (e = !0), + (!e && this.locked) || + (!e && this.isJumping) || + ((e || this.entity.body.canJump) && + (this.entity.roll && this.entity.roll.cancel(), + this.entity.body.resetGroundChangeTolerance(), + (this.isJumping = !0), + (this.entity.body.velocity.y = 0), + (this.entity.body.y += 1), + (this.startY = this.entity.body.y), + (this.endY = this.startY + t - 1), + (this.time = 0), + (this.duration = 0.41), + (this.goingUpwards = !0), + this.entity.game.sfx.play("hero_jump"))); + }), + (n.end = function () { + (this.isJumping = !1), (this.goingUpwards = !1); + }), + (n.cancel = function () { + this.end(); + }), + (n.lock = function () { + this.end(), (this.locked = !0); + }), + (n.unlock = function () { + (this.locked = !1), (this.gravity = B.gravity); + }), + Jump + ); + })(J.a); + var _i = (function (i) { + var t, e; + + function Roll(t) { + var e; + return ((e = i.call(this, t) || this).onStart = new et.a("onRollStart")), (e.onEnd = new et.a("onRollEnd")), e.reset(), e; + } + (e = i), ((t = Roll).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Roll.prototype; + return ( + (n.reset = function () { + (this.locked = !1), (this.isRolling = !1), (this.gravity = B.gravity), (this.duration = 30), (this.time = 0), (this.rollingHeight = 5), (this.regularHeight = 11), (this.entity.body.height = this.regularHeight); + }), + (n.turnOn = function () { + this._isOn || ((this._isOn = !0), this.entity.game.controller.onSwipeVertical.add(this)); + }), + (n.turnOff = function () { + this._isOn && ((this._isOn = !1), this.entity.game.controller.onSwipeVertical.remove(this)); + }), + (n.onSwipeVertical = function (t) { + -1 == t && this.perform(); + }), + (n.update = function (t) { + this.isRolling && ((this.time += t), this.time > this.duration && (this.time = this.duration), this.time === this.duration && this.end()); + }), + (n.perform = function () { + if (!this.locked && !this.isRolling) { + this.onStart.dispatch(), + this.entity.jump && this.entity.jump.cancel(), + this.entity.sneakers.isOn() && this.entity.sneakers.jumpCancel(), + (this.isRolling = !0), + (this.time = 0), + this.entity.body.landed || (this.entity.body.velocity.y = -2); + var t = this.entity.body.bottom; + (this.entity.body.height = this.rollingHeight), (this.entity.body.bottom = t), this.entity.game.sfx.play("hero_roll"); + } + }), + (n.end = function () { + (this.isRolling = !1), (this.entity.body.height = this.regularHeight), this.entity.body.bottom < this.entity.body.ground && (this.entity.body.bottom = this.entity.body.ground), (this.time = 0), this.onEnd.dispatch(); + }), + (n.cancel = function () { + this.end(); + }), + (n.lock = function () { + this.end(), (this.locked = !0); + }), + (n.unlock = function () { + this.locked = !1; + }), + (n.isOn = function () { + return this._isOn; + }), + Roll + ); + })(J.a); + var bi = (function (n) { + var t, e; + + function Notifier(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).ignore = ["transform", "container", "notifier"]), (i.entity = t), i; + } + (e = n), ((t = Notifier).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Notifier.prototype; + return ( + (i.cacheComponents = function () { + for (var t in ((this.comps = {}), this.entity)) + if (!(0 <= this.ignore.indexOf(t))) { + var e = this.entity[t]; + e instanceof J.a && (this.comps[t] = e); + } + }), + (i.notify = function (t) { + this.comps || this.cacheComponents(); + for (var e = arguments.length, i = new Array(1 < e ? e - 1 : 0), n = 1; n < e; n++) i[n - 1] = arguments[n]; + for (var o in this.comps) { + var s = this.comps[o]; + if (s) { + var a = s[t]; + a && a.apply(s, i); + } + } + }), + Notifier + ); + })(J.a); + var wi = (function (i) { + var t, e; + + function Character() { + var t; + (t = i.call(this) || this).add(it, { + boxColor: 5570560, + sensor: !0, + }), + (t.model = new v.a()), + t.addChild(t.model), + t.add(ci), + t.add(Ye), + t.add(di), + t.add(He), + t.add(Ue), + t.add(Ve), + t.add(Qe), + t.add($e), + t.add(ii), + t.add(ui), + t.add(yi), + t.add(vi), + t.add(_i), + t.add(ei), + t.add(bi), + (t.body.width = 4), + (t.body.height = 11), + (t.body.depth = 4), + t.anim.addAnim("0", hi.character_idle), + t.updateModel(), + (t.sprayCan = H.getEntityCloneOpaque("sprayCan", "props_tex", !1)), + (t.sprayCan.x = 0.2), + (t.sprayCan.z = -0.5), + (t.sprayCan.y = -0.5), + (t.sprayCan.rz = 0.5 * Math.PI), + (t.sprayCan.ry = 0.15 * Math.PI); + var e = t.anim.scenes[0]; + return H.findEntity("R_Hand_jnt", e.pixiTree, 10).addChild(t.sprayCan), t; + } + (e = i), ((t = Character).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Character.prototype; + return ( + (n.resetModel = function () { + (this.model.ry = Math.PI), (this.model.rx = 0), (this.model.rz = 0), (this.model.x = -1), (this.model.y = 0.5 * -this.body.height + 1), (this.model.z = 0); + }), + (n.init = function () { + this._initialized || + ((this._initialized = !0), + this.add(ut), + this.add(pi), + (this.pop.view.y = 0), + (this.pop.view.z = -3), + this.anim.addAnim("1", hi.character_movement), + this.anim.addAnim("2", hi.character_jetpack), + this.anim.addAnim("3", hi.character_catch), + this.anim.addAnim("4", hi.character_pogo), + this.anim.addAnim("5", hi.character_start), + (this.anim.debug = !0)); + }), + (n.playIntro = function () { + this.init(), + this.lane.reset(), + this.resetModel(), + (this.z = 0), + (this.x = -1), + (this.body.z = 0), + (this.body.x = 0), + (this.body.bottom = 0), + (this.y = this.body.bottom + 0.5 * this.body.height), + this.anim.play("start_run", { + sudden: !0, + crossfade: 0, + }); + }), + (n.run = function () { + (this.body.velocity.z = -this.game.stats.speed), this.player.run(); + }), + (n.reset = function () { + (this.model.x = 0), + (this.z = 0), + (this.x = 1), + (this.body.z = 0), + (this.body.x = 1), + (this.body.bottom = 0), + this.pogo.turnOff(), + this.magnet.turnOff(), + this.jetpack.turnOff(), + this.sneakers.turnOff(), + this.dizzy.turnOff(), + this.multiplier.turnOff(), + this.hoverboard.end(), + this.notifier.notify("reset"), + this.reviveHalo && this.reviveHalo.stop(); + }), + (n.restoreSize = function () { + (this.body.width = 4), (this.body.height = 11), (this.body.depth = 4), (this.model.position.y = 0.5 * -this.body.height + 1); + }), + (n.updateModel = function () { + (this.model.position.y = 0.5 * -this.body.height + 1), (this.model.rotation.y = Math.PI); + }), + (n.onStateUpdate = function () { + var t; + this.statesReady || + (((t = this).state.params = { + playing: !1, + landed: !1, + ascending: !1, + descending: !1, + dodging: !1, + rolling: !1, + hoverboard: !1, + dizzy: !1, + special: !1, + pogo: !1, + dead: !1, + catch: !1, + }), + t.state.add("idle", new ki()), + t.state.add("dead", new Ii()), + t.state.add("catch", new Li()), + t.state.add("running", new xi()), + t.state.add("grinding", new Ci()), + t.state.add("dodging", new Si()), + t.state.add("ascending", new zi()), + t.state.add("hangtime", new Ti()), + t.state.add("descending", new Pi()), + t.state.add("rolling", new Mi()), + t.state.add("jetpack", new Ei()), + t.state.add("jetpackDodging", new Oi()), + t.state.add("pogo", new Ri()), + t.state.addTransition("all", "idle"), + t.state.addTransition("idle", "running"), + t.state.addTransition("running", "dodging"), + t.state.addTransition("running", "rolling"), + t.state.addTransition("running", "airborne"), + t.state.addTransition("running", "jetpack"), + t.state.addTransition("running", "pogo"), + t.state.addTransition("running", "grinding", !0), + t.state.addTransition("dodging", "rolling"), + t.state.addTransition("dodging", "running"), + t.state.addTransition("dodging", "airborne"), + t.state.addTransition("dodging", "jetpack"), + t.state.addTransition("dodging", "pogo"), + t.state.addTransition("airborne", "rolling"), + t.state.addTransition("airborne", "running"), + t.state.addTransition("airborne", "dodging"), + t.state.addTransition("airborne", "jetpack"), + t.state.addTransition("airborne", "pogo"), + t.state.addTransition("ascending", "rolling", !0), + t.state.addTransition("ascending", "running", !0), + t.state.addTransition("ascending", "dodging", !0), + t.state.addTransition("ascending", "jetpack", !0), + t.state.addTransition("ascending", "pogo", !0), + t.state.addTransition("hangtime", "ascending", !0), + t.state.addTransition("hangtime", "rolling", !0), + t.state.addTransition("hangtime", "running", !0), + t.state.addTransition("hangtime", "dodging", !0), + t.state.addTransition("hangtime", "jetpack", !0), + t.state.addTransition("hangtime", "pogo", !0), + t.state.addTransition("descending", "ascending", !0), + t.state.addTransition("descending", "hangtime", !0), + t.state.addTransition("descending", "rolling", !0), + t.state.addTransition("descending", "running", !0), + t.state.addTransition("descending", "dodging", !0), + t.state.addTransition("descending", "jetpack", !0), + t.state.addTransition("descending", "pogo", !0), + t.state.addTransition("rolling", "running"), + t.state.addTransition("rolling", "airborne"), + t.state.addTransition("rolling", "dodging"), + t.state.addTransition("rolling", "jetpack"), + t.state.addTransition("rolling", "pogo"), + t.state.addTransition("jetpack", "ascending"), + t.state.addTransition("jetpack", "hangtime"), + t.state.addTransition("jetpack", "descending"), + t.state.addTransition("jetpack", "airborne"), + t.state.addTransition("jetpack", "jetpackDodging"), + t.state.addTransition("jetpackDodging", "jetpack"), + t.state.addTransition("jetpackDodging", "airborne"), + t.state.addTransition("jetpackDodging", "ascending"), + t.state.addTransition("jetpackDodging", "hangtime"), + t.state.addTransition("jetpackDodging", "descending"), + t.state.addTransition("pogo", "airborne"), + t.state.addTransition("pogo", "jetpack"), + t.state.addTransition("pogo", "rolling"), + t.state.addTransition("dead", "catch"), + t.state.addTransition("catch", "all"), + t.state.addTransition("all", "dead"), + t.state.addTransition("dead", "idle"), + t.state.addTransition("dead", "running"), + t.state.addTransition("grinding", "dodging", !0), + t.state.addTransition("grinding", "rolling", !0), + t.state.addTransition("grinding", "airborne", !0), + t.state.addTransition("grinding", "jetpack", !0), + t.state.addTransition("grinding", "pogo", !0), + t.state.addTransition("grinding", "ascending", !0), + t.state.addTransition("grinding", "descending", !0)), + (this.statesReady = !0); + var e = this.state.params; + (e.landed = this.body.landed), + (e.ascending = !this.body.hangtime && this.body.ascending), + (e.descending = !this.body.hangtime && this.body.descending), + (e.rolling = this.roll.isRolling), + (e.dead = !!this.player.deathCause), + (e.hoverboard = this.hoverboard.isOn()), + (e.dodging = this.lane.changing), + (e.playing = !!this.game.state && !this.player.deathCause), + (e.jetpack = this.jetpack.isOn()), + (e.pogo = this.pogo.isOn()), + (e.special = e.jetpack || e.pogo), + (e["catch"] = !!this.player.catchMode), + (e.grinding = this.hoverboard.grinding); + }), + (n.revive = function () { + this.reviveSmoke || + this.add( + We, + { + EntityClass: mi, + container: this.game, + rate: 1, + life: 90, + xMod: [-10, 10], + yMod: [-2, 20], + velocityXMod: [-1, 1], + velocityYMod: [1, 3], + velocityZMod: [-1, -0.5], + scaleXMod: [0.8, 1.3], + scaleYMod: [0.8, 1.3], + scaleZMod: [0.8, 1.3], + growXMod: [0.01, 0.03], + growYMod: [0.01, 0.03], + growZMod: [0.01, 0.03], + }, + "reviveSmoke" + ), + this.reviveHalo || this.add(fi), + this.reviveHalo.play(), + (this.reviveSmoke.data.x = this.x), + (this.reviveSmoke.data.y = 0), + (this.reviveSmoke.data.z = this.z), + this.reviveSmoke.spawn(20), + this.game.sfx.play("hero_revive"); + }), + Character + ); + })(v.a); + var ki = (function () { + function StateIdle() { + (this.playing = !1), (this.dead = !1); + } + return ( + (StateIdle.prototype.begin = function () { + this.entity.anim.play("idle", { + loop: !0, + }); + }), + StateIdle + ); + })(), + xi = (function () { + function StateRunning() { + (this.playing = !0), + (this.landed = !0), + (this.rolling = !1), + (this.dodging = !1), + (this.grinding = !1), + (this.anim = { + normal: "run3", + hoverboard: "h_run", + sneakers: "superRun", + }), + (this.count = 0), + (this.alt = !1), + (this.soundSteps = !1); + } + var t = StateRunning.prototype; + return ( + (t.begin = function () { + this.count = 0; + var t = this.entity.player.getMode(), + e = this.anim[t]; + (this.soundSteps = "sneakers" === t), + "hoverboard" === t && + this.entity.anim.play("h_landing", { + loop: !1, + enforce: 0.3, + }), + this.entity.anim.play(e, { + loop: !0, + }); + }), + (t.update = function (t) { + if (this.soundSteps && ((this.count -= t), this.count <= 0)) { + (this.count = 25), (this.alt = !this.alt); + var e = this.alt ? "hero_sneakers_foot_l" : "hero_sneakers_foot_r"; + this.entity.game.sfx.play(e); + } + }), + StateRunning + ); + })(), + Ci = (function () { + function StateGrinding() { + (this.playing = !0), (this.landed = !0), (this.rolling = !1), (this.dodging = !1), (this.grinding = !0), (this.ascending = !1), (this.descending = !1); + } + var t = StateGrinding.prototype; + return ( + (t.begin = function () { + var t = L.pick("1", "2", "3"); + (this.animName = "h_Grind" + t), + this.entity.anim.play(this.animName + "_land", { + loop: !0, + enforce: 0.2, + }), + this.entity.anim.play(this.animName, { + loop: !0, + }); + }), + (t.update = function (t) { + this.entity.anim.currentName === this.animName && this.entity.hoverboard.updateGrinding(); + }), + StateGrinding + ); + })(), + Si = (function () { + function StateDodging() { + (this.playing = !0), + (this.landed = !0), + (this.rolling = !1), + (this.dodging = !0), + (this.anim = { + normal: { + "-1": "dodgeLeft", + 1: "dodgeRight", + }, + hoverboard: { + "-1": "h_left", + 1: "h_right", + }, + sneakers: { + "-1": "dodgeLeft", + 1: "dodgeRight", + }, + }); + } + return ( + (StateDodging.prototype.begin = function () { + var t = this.entity.player.getMode(), + e = this.anim[t][this.entity.lane.absStep]; + this.entity.anim.play(e); + }), + StateDodging + ); + })(), + zi = (function () { + function StateAscending() { + (this.playing = !0), + (this.landed = !1), + (this.special = !1), + (this.rolling = !1), + (this.ascending = !0), + (this.descending = !1), + (this.anim = { + normal: ["jump", "jump2", "jump3", "jump_salto"], + sneakers: ["jump", "jump2", "jump3", "jump_salto"], + hoverboard: [ + "h_jump2_kickflip_flip", + "h_jump3_bs360grab", + "h_jump4_360_flip", + "h_jump5_Impossible_flip", + "h_jump6_nollie", + "h_jump7_heelflip_flip", + "h_jump8_pop_shuvit_flip", + "h_jump9_fs360grab", + "h_jump10_heel360_flip", + "h_jump11_fs_salto", + ], + }); + } + return ( + (StateAscending.prototype.begin = function () { + var t = this.entity.player.getMode(), + e = this.anim[t]; + e && this.entity.anim.play(e); + }), + StateAscending + ); + })(), + Ti = (function () { + function StateHangtime() { + (this.playing = !0), + (this.landed = !1), + (this.special = !1), + (this.rolling = !1), + (this.ascending = !1), + (this.descending = !1), + (this.anim = { + normal: ["hangtime", "hangtime2", "hangtime3"], + sneakers: ["hangtime", "hangtime2", "hangtime3"], + }); + } + return ( + (StateHangtime.prototype.begin = function () { + var t = this.entity.player.getMode(), + e = this.anim[t]; + e && + this.entity.anim.play(e, { + loop: !0, + }); + }), + StateHangtime + ); + })(), + Pi = (function () { + function StateDescending() { + (this.playing = !0), + (this.landed = !1), + (this.special = !1), + (this.rolling = !1), + (this.ascending = !1), + (this.descending = !0), + (this.anim = { + normal: ["hangtime", "hangtime2", "hangtime3"], + sneakers: ["hangtime", "hangtime2", "hangtime3"], + }); + } + return ( + (StateDescending.prototype.begin = function () { + var t = this.entity.player.getMode(), + e = this.anim[t]; + e && + this.entity.anim.play(e, { + loop: !0, + }); + }), + StateDescending + ); + })(), + Mi = (function () { + function StateRolling() { + (this.playing = !0), + (this.rolling = !0), + (this.anim = { + normal: "roll", + hoverboard: "h_roll", + sneakers: "roll", + }); + } + return ( + (StateRolling.prototype.begin = function () { + var t = this.entity.player.getMode(), + e = this.anim[t]; + "hoverboard" === t && + this.entity.anim.play("h_jump2_kickflip_flip", { + sudden: !0, + }), + this.entity.anim.play(e, { + loop: !0, + }); + }), + StateRolling + ); + })(), + Ei = (function () { + function StateJetpack() { + (this.jetpack = !0), (this.dodging = !1); + } + return ( + (StateJetpack.prototype.begin = function () { + this.entity.anim.play("Jetpack_forward", { + loop: !0, + }); + }), + StateJetpack + ); + })(), + Oi = (function () { + function StateJetpackDodging() { + (this.jetpack = !0), + (this.dodging = !0), + (this.anim = { + "-1": "Jetpack_changeLane_left", + 1: "Jetpack_changeLane_right", + }); + } + return ( + (StateJetpackDodging.prototype.begin = function () { + var t = this.anim[this.entity.lane.absStep]; + this.entity.anim.play(t); + }), + StateJetpackDodging + ); + })(), + Ri = (function () { + function StatePogo() { + (this.pogo = !0), (this.anim = ["pogostick_Hangtime_flying"]); + } + return (StatePogo.prototype.begin = function () {}), StatePogo; + })(), + Ii = (function () { + function StateDead() { + (this.dead = !0), + (this["catch"] = !1), + (this.anim = { + upper: "death_upper", + lower: "death_lower", + train: "death_movingTrain", + bounce: "death_bounce", + out: "death_bounce", + }); + } + return ( + (StateDead.prototype.begin = function () { + var t = this.entity.player.deathCause, + e = this.anim[t] || "death_bounce"; + this.entity.anim.play(e, { + sudden: !0, + }); + }), + StateDead + ); + })(), + Li = (function () { + function StateCatch() { + (this.dead = !0), (this["catch"] = !0); + } + return ( + (StateCatch.prototype.begin = function () { + "train" !== this.entity.player.deathCause && + this.entity.anim.play("Avatar_Catch_Shoulder", { + sudden: !0, + }); + }), + StateCatch + ); + })(); + + function Follower_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var ji = (function (n) { + var t, e; + + function Follower(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).offsetX = e.offsetX || 0), i.reset(), i; + } + (e = n), ((t = Follower).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = Follower.prototype; + return ( + (a.reset = function () { + this.entity.body && + ((this.entity.body.lane = 0), + (this.entity.body.x = 0), + (this.entity.body.z = 999), + (this.entity.body.bottom = 0), + (this.mode = Follower.DISABLED), + (this.entity.active = !1), + (this.near = !1), + (this.enabled = !1), + (this.lastGround = 0), + (this.lastPos = 0)); + }), + (a.update = function (t) { + if (this.enabled) { + t || (t = this.entity.game.delta); + var e = this.entity.game.hero; + e.player.running && + (this.mode === Follower.INTRO + ? this.changeMode(Follower.NEAR) + : this.entity.game.level.isTutorial() + ? this.changeMode(Follower.FAR) + : e.pogo.isOn() || e.jetpack.isOn() + ? this.changeMode(Follower.GO_AWAY) + : e.player.dead + ? "train" === e.player.deathCause + ? this.changeMode(Follower.GO_AWAY) + : this.changeMode(Follower.CATCH) + : e.player.dizzy + ? this.changeMode(Follower.NEAR) + : this.changeMode(Follower.FAR)), + this.time <= this.duration && ((this.time += this.entity.game.deltaSecs), this.time > this.duration && (this.time = this.duration)); + var i = e.body.x + this.offsetX; + (this.entity.body.x = V.lerp(this.entity.body.x, i, 0.5 * t)), e.body.landed && (this.lastGround = e.body.ground); + var n = this.lastGround + 0.5 * (e.body.bottom - this.lastGround); + (this.entity.body.bottom = V.lerp(this.entity.body.bottom, n, 0.5 * t)), this.entity.body.bottom > e.body.bottom && (this.entity.body.bottom = e.body.bottom); + var o = this.curve ? this.curve(this.time / this.duration) : this.time / this.duration, + s = V.lerp(this.distanceStart, this.distanceEnd, o); + (this.distance = s), this.sound && this.sound.volume(1 - V.clamp(this.distance / 70, 0, 1)); + } + }), + (a.changeMode = function (t) { + this.mode !== t && + (this.mode === Follower.FAR && t === Follower.NEAR ? this.entity.game.sfx.play("guard_proximity") : t === Follower.CATCH && this.entity.game.sfx.play("guard_catch"), + (this.mode = t), + (this.distanceStart = this.distance), + (this.distanceEnd = this.mode.distance), + (this.duration = this.mode.duration), + (this.curve = this.mode.curve), + (this.time = 0)); + }), + (a.enable = function () { + (this.enabled = !0), this.changeMode(Follower.NEAR); + }), + (a.disable = function () { + (this.entity.active = !0), (this.entity.body.z = 9999), (this.enabled = !1); + }), + (a.playIntro = function () { + (this.distance = 50), (this.time = this.duration = 0.01), this.enable(), this.changeMode(Follower.INTRO); + }), + (a.dismiss = function () { + this.changeMode(Follower.DISABLED); + }), + (i = Follower), + (o = [ + { + key: "distance", + set: function (t) { + var e = this.entity.game.stats.z + t; + this.entity.body.z = e; + }, + get: function () { + return this.entity.z - this.entity.game.stats.z; + }, + }, + ]) && Follower_defineProperties(i.prototype, o), + s && Follower_defineProperties(i, s), + Follower + ); + })(J.a); + (ji.DISABLED = { + distance: 9999, + duration: 0.1, + curve: null, + }), + (ji.FAR = { + distance: 70, + duration: 3, + curve: St.sineIn, + }), + (ji.GO_AWAY = { + distance: 100, + duration: 1, + curve: St.sineIn, + }), + (ji.NEAR = { + distance: 10, + duration: 0.6, + curve: St.sineOut, + }), + (ji.CATCH = { + distance: 0, + duration: 0.3, + curve: St.sineOut, + }), + (ji.INTRO = { + distance: 10, + duration: 1e-10, + curve: St.expoOut, + }); + var Fi = (function (e) { + var t, i; + + function Cop() { + var t; + return ( + (t = e.call(this) || this).add(it, { + deco: !0, + ghost: !0, + }), + t.add(ji), + t.add(ci), + t.add(di), + (t.body.height = 14), + (t.body.width = 6), + (t.body.depth = 6), + (t.model = new v.a()), + (t.model.y = 0.4 * -t.body.height), + (t.model.ry = Math.PI), + t.addChild(t.model), + (t["catch"] = null), + (t.catchCount = 0), + t.scale.set(0.01), + t + ); + } + (i = e), ((t = Cop).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i); + var n = Cop.prototype; + return ( + (n.init = function () { + this._initialized || ((this._initialized = !0), this.anim.addAnim("movement", hi.guard_movement), this.anim.addAnim("catch", hi.guard_catch)); + }), + (n.playIntro = function () { + var t = this; + this.init(), + this.scale.set(0.01), + (this.active = !0), + (this.z = 999), + (this.body.z = this.z), + this.game.sfx.play("guard_start"), + this.anim.play("Guard_run", { + sudden: !0, + loop: !1, + }), + this.anim.play("Guard_playIntro", { + sudden: !0, + crossfade: 0, + }), + this.follower.playIntro(), + setTimeout(function () { + t.scale.set(1); + }, 100); + }), + (n.run = function () { + this.init(), + (this.active = !0), + this.anim.play("Guard_run", { + loop: !0, + }), + this.follower.enable(); + }), + (n.reset = function () { + this._initialized && (this.follower.reset(), (this.z = 999), (this.body.z = this.z), (this.catchCount = 0), (this["catch"] = null), (this.active = !1)); + }), + (n.onStateUpdate = function () { + var t; + this.statesReady || + (((t = this).state.params = { + playing: !1, + landed: !1, + dodging: !1, + rolling: !1, + catch: !1, + hasDeathCause: !1, + }), + t.state.add("idle", new Ai()), + t.state.add("running", new Di()), + t.state.add("dodging", new Gi()), + t.state.add("airborne", new Bi()), + t.state.add("rolling", new Ni()), + t.state.add("catch", new Xi()), + t.state.addTransition("all", "idle"), + t.state.addTransition("idle", "running"), + t.state.addTransition("running", "dodging", !0), + t.state.addTransition("running", "airborne", !0), + t.state.addTransition("running", "rolling", !0), + t.state.addTransition("airborne", "rolling", !0), + t.state.addTransition("airborne", "dodging", !0), + t.state.addTransition("all", "catch"), + t.state.addTransition("catch", "idle"), + t.state.addTransition("catch", "running")), + (this.statesReady = !0), + this.catchCount && ((this.catchCount -= 1), 0 === this.catchCount && (this["catch"] = !0)); + var e = this.state.params; + (e.landed = this.game.hero.body.landed), + (e.rolling = !!this.game.hero.player.rolling), + (e.dodging = !!this.game.hero.body.dodging), + (e.playing = !!this.game.state && !this.game.hero.player.deathCause), + (e["catch"] = !!this["catch"]), + (e.hasDeathCause = !!this.game.hero.player.deathCause); + }), + (n.catchHero = function () { + this.catchCount = 10; + }), + Cop + ); + })(v.a); + var Ai = (function () { + function StateIdle() { + (this.playing = !1), (this["catch"] = !1); + } + var t = StateIdle.prototype; + return (t.begin = function () {}), (t.end = function () {}), StateIdle; + })(), + Di = (function () { + function StateRunning() { + (this.playing = !0), (this.landed = !0), (this.rolling = !1), (this.dodging = !1); + } + var t = StateRunning.prototype; + return ( + (t.begin = function () { + this.entity.anim.play("Guard_run", { + loop: !0, + }); + }), + (t.update = function () { + !this._catchScene && this.entity.anim.scenes[1] && (this._catchScene = this.entity.anim.scenes[1].pixiTree), this._catchScene && (this._catchScene.active = !1); + }), + StateRunning + ); + })(), + Gi = (function () { + function StateDodging() { + (this.playing = !0), + (this.landed = !0), + (this.rolling = !1), + (this.dodging = !0), + (this.anim = { + 1: "Guard_dodgeLeft", + "-1": "Guard_dodgeRight", + }); + } + return ( + (StateDodging.prototype.begin = function () { + var t = this.anim[this.entity.game.hero.body.dodging]; + this.entity.anim.play(t, { + sudden: !1, + }); + }), + StateDodging + ); + })(), + Bi = (function () { + function StateAirborne() { + (this.playing = !0), (this.landed = !1), (this.rolling = !1); + } + return ( + (StateAirborne.prototype.begin = function () { + this.entity.anim.play("Guard_jump", { + sudden: !0, + }); + }), + StateAirborne + ); + })(), + Ni = (function () { + function StateRolling() { + (this.playing = !0), (this.rolling = !0); + } + return ( + (StateRolling.prototype.begin = function () { + this.entity.anim.play("Guard_roll", { + loop: !0, + sudden: !0, + }); + }), + StateRolling + ); + })(), + Xi = (function () { + function StateCatch() { + (this["catch"] = !0), (this.anims = ["Catch_Shoulder", "Catch_Left_Pickup", "Catch_Right_Pickup"]); + } + return ( + (StateCatch.prototype.begin = function () { + if (!this.playing) + if ("train" === this.entity.game.hero.player.deathCause) this.entity.follower.dismiss(); + else { + var t = this.entity.game.hero, + e = t.body.x, + i = t.body.lane * B.laneWidth, + n = L.item(this.anims); + (n = i - 4 < e && e < i + 4 ? "Catch_Shoulder" : -1 === t.player.bumpSide ? "Catch_Right_Pickup" : 1 === t.player.bumpSide ? "Catch_Left_Pickup" : "Catch_Shoulder"), + this.entity.anim.play("Guard_" + n, { + sudden: !0, + }), + this.entity.game.hero.anim.play("Avatar_" + n, { + sudden: !0, + }); + } + }), + StateCatch + ); + })(); + var Yi = (function (d) { + var t, e; + + function Skyline() { + var t; + (t = d.call(this) || this).rotation.y = Math.PI; + var e = H.whichScene("sl_monument_4", "sl_monument_04"), + i = H.getEntityCloneColor(e, 9490943, !1); + t.addChild(i), (i.x = 100), (i.y = -50), (i.z = 0); + var n = H.whichScene("sl_monument_2", "sl_monument_02"), + o = H.getEntityCloneColor(n, 6533631, !1); + t.addChild(o), (o.x = 0), (o.z = -30); + var s = H.whichScene("sl_monument_3", "sl_monument_03"), + a = H.getEntityCloneColor(s, 9490943, !1); + t.addChild(a), (o.x = 0), (o.y = -200), (a.z = -60); + var r = H.whichScene("sl_monument_1", "sl_monument_01"), + h = H.getEntityCloneColor(r, 6533631, !1); + return t.addChild(h), (o.x = 100), (h.z = -90), t; + } + return (e = d), ((t = Skyline).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), Skyline; + })(v.a), + Hi = {}; + (Hi["new"] = function () { + return H.getEntityClone("tube"); + }), + (Hi.new001 = function () { + return H.getEntityClone("tube001"); + }), + (Hi.new002 = function () { + return H.getEntityClone("tube002"); + }), + (Hi.spawn = function (t, e) { + void 0 === e && (e = {}), e.z || (e.z = t.z); + var i = t.game.pool.get(Hi["new"]); + if (((i.x = 0), (i.y = 0), (i.z = e.z), (i.ry = Math.PI), t.game.addChild(i), "1103_seoul" === B.env)) { + var n = t.game.pool.get(Hi.new001); + (n.x = 0), (n.y = 0), (n.z = e.z), (n.ry = Math.PI), (n.view3d.state.blend = !0), (n.view3d.material.opacity = 0.5), t.game.addChild(n); + var o = t.game.pool.get(Hi.new002); + (o.x = 0), (o.y = 0), (o.z = e.z), (o.ry = Math.PI), t.game.addChild(o); + } + }), + (Hi.mount = function (t) { + for (var e = t.blocks / 2, i = 0; i < e; i++) { + var n = t.z - i * B.blockSize * 2; + Hi.spawn(t, { + z: n, + }); + } + }); + var Ui = i(190), + Vi = i.n(Ui), + qi = i(191), + Wi = i.n(qi); + var Zi = (function (n) { + var t, e; + + function SeaMaterial(t, e) { + void 0 === t && (t = {}), void 0 === e && (e = {}); + var i = [e.fog ? "#define FOG" : "", e.rails ? "#define RAILS" : ""]; + return (t.uniforms = O.unformGroup()), n.call(this, t, Vi.a, Wi.a, i, "sea") || this; + } + return (e = n), ((t = SeaMaterial).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), SeaMaterial; + })(z.a), + Ki = {}, + Ji = Ki; + (Ki.newStart = function () { + return H.getEntityClone("epic_start"); + }), + (Ki.newStart001 = function () { + return H.getEntityClone("epic_start001", "ocean", !1, Zi); + }), + (Ki.newStart002 = function () { + return H.getEntityClone("epic_start002", "", !1, R); + }), + (Ki.newStart003 = function () { + return H.getEntityClone("epic_start003", "", !1, R); + }), + (Ki.newMid = function () { + return H.getEntityClone("epic_mid"); + }), + (Ki.newMid001 = function () { + return H.getEntityClone("epic_mid001", "", !1, R); + }), + (Ki.newMid002 = function () { + return H.getEntityClone("epic_mid002", "ocean", !1, Zi); + }), + (Ki.newMid003 = function () { + return H.getEntityClone("epic_mid003", "", !1, R); + }), + (Ki.newEnd = function () { + return H.getEntityClone("epic_end"); + }), + (Ki.newEnd001 = function () { + return H.getEntityClone("epic_end001", "", !1, R); + }), + (Ki.newEnd002 = function () { + return H.getEntityClone("epic_end002", "ocean", !1, Zi); + }), + (Ki.newEnd003 = function () { + return H.getEntityClone("epic_end003", "", !1, R); + }), + (Ki.newStartAlt = function () { + return H.getEntityClone("epic_1_start"); + }), + (Ki.newStart001Alt = function () { + return H.getEntityClone("epic_1_start001", "ocean", !1, Zi); + }), + (Ki.newStart002Alt = function () { + return H.getEntityClone("epic_1_start002", "", !1, R); + }), + (Ki.newStart003Alt = function () { + return H.getEntityClone("epic_1_start003", "", !1, R); + }), + (Ki.newMidAlt = function () { + return H.getEntityClone("epic_1_mid"); + }), + (Ki.newMid001Alt = function () { + return H.getEntityClone("epic_1_mid001", "", !1, R); + }), + (Ki.newMid002Alt = function () { + return H.getEntityClone("epic_1_mid002", "ocean", !1, Zi); + }), + (Ki.newMid003Alt = function () { + return H.getEntityClone("epic_1_mid003", "", !1, R); + }), + (Ki.newEndAlt = function () { + return H.getEntityClone("epic_1_end"); + }), + (Ki.newEnd001Alt = function () { + return H.getEntityClone("epic_1_end001", "", !1, R); + }), + (Ki.newEnd002Alt = function () { + return H.getEntityClone("epic_1_end002", "ocean", !1, Zi); + }), + (Ki.newEnd003Alt = function () { + return H.getEntityClone("epic_1_end003", "", !1, R); + }), + (Ki.spawn = function (t, e) { + void 0 === e && (e = {}), e.z || (e.z = t.z), e.alt || (e.alt = !1), e.part || (e.part = "Start"), "1103_seoul" === B.env && (e.alt = !1); + var i = "new" + e.part + (e.alt ? "Alt" : ""), + n = "new" + e.part + "001" + (e.alt ? "Alt" : ""), + o = "new" + e.part + "002" + (e.alt ? "Alt" : ""), + s = "new" + e.part + "003" + (e.alt ? "Alt" : ""), + a = t.game.pool.get(Ki[i]); + if (((a.x = 0), (a.y = 0), (a.z = e.z), (a.ry = Math.PI), t.game.addChild(a), "1103_seoul" === B.env)) { + var r = t.game.pool.get(Ki[n]); + (r.x = 0), (r.y = 0), (r.z = e.z), (r.ry = Math.PI), t.game.addChild(r); + var h = t.game.pool.get(Ki[o]); + (h.x = 0), (h.y = 0), (h.z = e.z), (h.ry = Math.PI), t.game.addChild(h); + var d = t.game.pool.get(Ki[s]); + (d.x = 0), (d.y = 0), (d.z = e.z), (d.ry = Math.PI), t.game.addChild(d); + } + }), + (Ki.mount = function (t) { + for (var e = t.blocks, i = 12 < t.blocks, n = 0.25 * e, o = 0, s = 0; s < n; s++) + 0 === s + ? Ki.spawn(t, { + z: t.z - o, + part: "Start", + alt: i, + }) + : s < n - 1 + ? Ki.spawn(t, { + z: t.z - o, + part: "Mid", + alt: i, + }) + : Ki.spawn(t, { + z: t.z - o, + part: "End", + alt: i, + }), + (o += 4 * B.blockSize); + }); + var Qi = (function (n) { + var t, e; + + function EnvironmentSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).models = []), + (i.game = t.game), + i.game.onRun.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + i + ); + } + (e = n), ((t = EnvironmentSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = EnvironmentSystem.prototype; + return ( + (i.run = function () { + this.skyline || ((this.skyline = new Yi()), this.game.addChild(this.skyline)); + }), + (i.update = function () { + this.skyline && (this.skyline.z = this.game.stats.z - 0.95 * B.visibleMaxDistance); + }), + (i.setup = function (t) { + var e = ue.environment(t.node), + i = e ? ue.environmentType(e) : null; + if (!i) + if (t.node.name.match(/tunnel/)) i = ["Gates", "All"]; + else if (t.node.name.match(/epic/)) i = ["Epic", "All"]; + else if (t.node.components.RouteChunk) { + var n = t.node.components.RouteChunk._limitedAllowedEnvironmentKinds; + i = (n.length ? L.item(n)._type.split(",") : ["Fillers", "All"]) || []; + } else i = []; + (t.envTube = !1), + (t.envStation = !1), + (t.envEpic = !1), + (t.envGates = !1), + (t.envEmpty = !1), + (t.envPillars = !1), + B.forceTube || 0 <= i.indexOf("Tube") + ? (t.envTube = this.game.route.canSpawn("tube", t.z)) + : 0 <= i.indexOf("Station") + ? (t.envStation = !0) + : 0 <= i.indexOf("Epic") + ? (t.envEpic = this.game.route.canSpawn("epic", t.z)) + : 0 <= i.indexOf("Gates") + ? (t.envGates = !0) + : 0 <= i.indexOf("Pillars") + ? (t.envPillars = !0) + : 0 <= i.indexOf("Empty") && (t.envEmpty = !0); + }), + (i.mount = function (t) { + t.envTube ? this.spawnTube(t) : t.envStation ? this.spawnStation(t) : t.envEpic ? this.spawnEpic(t) : t.envEmpty ? this.spawnRegular(t) : t.envGates ? this.spawnGates(t) : this.spawnRegular(t); + }), + (i.spawnEpic = function (t) { + Ji.mount(t), this.game.route.setSpawn("epic", t.z - t.length - 1800); + }), + (i.spawnTube = function (t) { + var e = t.node, + i = t.game.pool.get(he); + t.game.addChild(i), i.init(t, e), this.game.route.setSpawn("tube", t.z - t.length - 360); + }), + (i.spawnStation = function (t) { + var e = ue.environment(t.node), + i = (e.components.Transform.position.z, t.game.pool.get(Kt)); + i.init(t, e), t.game.addChild(i), Nt.mount(t), Yt.mount(t); + }), + (i.spawnGates = function (t) {}), + (i.spawnRegular = function (t) { + Yt.mount(t), Nt.mount(t); + }), + EnvironmentSystem + ); + })(f.a), + $i = i(127); + + function RouteSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var tn = (function (n) { + var t, e; + + function RouteSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).game = t.game), + i.game.onReset.add( + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(i) + ), + (i._spawns = {}), + (i.builder = new en(i.game)), + (i.firstPassed = !1), + i + ); + } + (e = n), ((t = RouteSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + o, + s, + a = RouteSystem.prototype; + return ( + (a.reset = function () { + this.builder.reset(), + (this.firstPassed = !1), + (this.game.stats.route = ""), + (this._spawns = { + pickup: -900, + tube: -90, + }); + }), + (a.getSequence = function () { + var t = Math.clamp(this.game.stats.level, 0, 3), + e = [], + i = [], + n = []; + if (B.routeSection) n.push(we.section("default_fallback")), n.push(we.section(B.routeSection)); + else { + if (!this.firstPassed) { + var o = we.section(this.game.tutorial.enabled ? "tutorial" : "default_start"); + n.push(o), (this.firstPassed = !0); + } + n.push(this.builder.getSectionByLevel(t)); + } + for (var s = 0; s < n.length; s++) { + var a = n[s]; + if (!(0 <= i.indexOf(a.name))) { + if ((i.push(a.name), a.start)) for (var r in a.start) e.push(a.start[r]); + if ((a.mid && e.push(L.item(a.mid)), a.end)) for (var h in a.end) e.push(a.end[h]); + } + } + return e; + }), + (a.canSpawn = function (t, e) { + return void 0 === this._spawns[t] || e <= this._spawns[t]; + }), + (a.setSpawn = function (t, e) { + this._spawns[t] = e; + }), + (a.resetSpawns = function () { + (this.builder.picked = {}), + (this._spawns = { + pickup: -900, + tube: -90, + }); + }), + (i = RouteSystem), + (o = [ + { + key: "profile", + get: function () { + return this._profile || (this._profile = {}), this._profile; + }, + }, + ]) && RouteSystem_defineProperties(i.prototype, o), + s && RouteSystem_defineProperties(i, s), + RouteSystem + ); + })(f.a), + en = (function () { + function RouteBuilder(t) { + (this.game = t), + (this.minDistanceBetweenRepeats = 2700), + (this.sectionsStart = { + default_start: 10, + }), + (this.sectionsMid = { + "default_b-s-b": 10, + default_choice: 10, + "default_s-b-s-b": 10, + "default_s-s": 10, + "default_s-s-s-s": 10, + default_train_tops_1: 10, + default_train_tops_2: 10, + default_tunnel_notrain: 10, + default_ramp_1: 10, + default_epic: 20, + default_1_track: 30, + default_2_tracks: 10, + default_train_tops_moving: 10, + default_train_tops_moving_combined: 10, + default_train_tops_moving_multiple: 10, + default_tunnel: 30, + default_epic_various: 20, + "default_4_units_3_tracks_b-s-b": 10, + default_4_units_3_tracks_choice: 10, + "default_4_units_3_tracks_s-b-s-b": 10, + "default_4_units_3_tracks_s-s": 10, + "default_4_units_3_tracks_s-s-s-s": 10, + default_short_1_track: 10, + default_ramp_2: 10, + default_short_2_tracks: 10, + default_short_train_tops_moving_combined: 10, + default_short_train_tops_moving_multiple: 10, + default_short_train_tops_moving: 10, + default_pogostick_start: 5, + default_bonus_short: 10, + default_bonus_long: 10, + }), + (this.levels = ["easy", "normal", "hard", "expert"]), + this.reset(); + } + var t = RouteBuilder.prototype; + return ( + (t.reset = function () { + (this.picked = { + default_tunnel: 1, + default_tunnel_notrain: 1, + }), + (this.level = 0), + (this.availableSections = {}), + this.addAvailableSections("easy"); + }), + (t.getSectionByLevel = function (t) { + if (t > this.level) { + this.level = t; + var e = this.levels[this.level]; + this.addAvailableSections(e), 2 == t && (this.addAvailableSection("default_bonus_short"), this.addAvailableSection("default_bonus_long"), this.addAvailableSection("default_pogostick_start")); + } + for (var i in this.picked) this.game.stats.distance > this.picked[i] && delete this.picked[i]; + var n = Object.assign({}, this.availableSections); + for (var o in this.picked) delete n[o]; + var s = L.item(n); + if (!s) throw new Error("No section available"); + return ( + s.__shortname.match("default_tunnel") + ? ((this.picked.default_tunnel = this.game.stats.distance + 0.75 * this.minDistanceBetweenRepeats), (this.picked.default_tunnel_notrain = this.game.stats.distance + 0.75 * this.minDistanceBetweenRepeats)) + : (this.picked[s.__shortname] = this.game.stats.distance + this.minDistanceBetweenRepeats), + s + ); + }), + (t.addAvailableSections = function (t) { + var e = $i["default"][t]["default"]; + for (var i in e) this.addAvailableSection(e[i]); + }), + (t.addAvailableSection = function (t) { + "string" == typeof t && (t = we.section(t)); + var e = t.name.replace("routeSection_", "").replace("route_section_", ""); + this.sectionsMid[e] && ((this.availableSections[e] = t).__shortname = e); + }), + RouteBuilder + ); + })(), + nn = i(7); + + function Label_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var on = { + basic: { + align: "center", + fill: 16777215, + fontSize: 50, + fontFamily: "Titan One", + lineJoin: "round", + dropShadow: !1, + dropShadowDistance: 3, + anchor: 0.5, + maxWidth: 0, + }, + title: { + align: "center", + fill: 211825, + fontSize: 70, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 2, + anchor: 0.5, + }, + subtitle: { + align: "center", + fill: 211825, + fontSize: 60, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 2, + anchor: 0.5, + }, + small: { + align: "center", + fill: 211825, + fontSize: 50, + fontFamily: "Titan One", + }, + }, + sn = (function (s) { + var t, e; + + function Label(t, e, i) { + var n; + void 0 === e && (e = "basic"), void 0 === i && (i = {}), (n = s.call(this) || this); + var o = "string" == typeof e ? on[e] : e; + return ( + (e = Object.assign({}, on.basic, o, i)), + (n._text = new c.Text(t, e)), + n._text.anchor.set(e.anchor), + n.addChild(n._text), + void 0 !== e.anchorX && (n._text.anchor.x = e.anchorX), + void 0 !== e.anchorY && (n._text.anchor.y = e.anchorY), + e.x && (n.x = e.x), + e.y && (n.y = e.y), + e.maxWidth && (n.maxWidth = e.maxWidth), + e.icon && ((n.icon = c.Sprite.from(e.icon)), n.icon.anchor.set(0.5), n.addChild(n.icon), (n.icon.x = 0), n.update()), + n + ); + } + (e = s), ((t = Label).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + n, + o, + a = Label.prototype; + return ( + (a.update = function () { + if ((this.maxWidth && this._text.width > this.maxWidth && ((this._text.scale.x = 1), (this._text.width = this.maxWidth), (this._text.scale.y = this._text.scale.x)), this.icon)) { + (this._text.x = 0), (this.icon.x = this._text.x - 0.5 * this._text.width - 0.5 * this.icon.width - 5); + var t = 0.5 * (this.width - this._text.width); + (this.icon.x += t), (this._text.x += t); + } + }), + (a.prompt = function () { + if (!this._prompting) { + this._prompting = !0; + var t = window.prompt(this.description, this.text); + t && (this.text = t), (this._prompting = !1), this.emit("change", this.text); + } + }), + (i = Label), + (n = [ + { + key: "text", + get: function () { + return this._text.text; + }, + set: function (t) { + this._text.text !== t && ((this._text.text = t), this.update()); + }, + }, + { + key: "editable", + get: function () { + return !!this._editable; + }, + set: function (t) { + (this._editable = t), (this.interactive = t), (this.buttonMode = t), void 0 === this.description && (this.description = "Set text"), this[this._editable ? "on" : "off"]("pointertap", this.prompt, this); + }, + }, + ]) && Label_defineProperties(i.prototype, n), + o && Label_defineProperties(i, o), + Label + ); + })(c.Container), + an = i(131); + + function TutorialSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function TutorialSystem_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + + function TutorialSystem_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var rn = (function (n) { + function TutorialSystem(t, e) { + var i; + return ( + void 0 === e && (e = {}), + ((i = n.call(this, t, e) || this).view = new c.Container()), + (i.game = t.game), + i.game.onRun.add(TutorialSystem_assertThisInitialized(i)), + i.game.onPause.add(TutorialSystem_assertThisInitialized(i)), + i.game.onResume.add(TutorialSystem_assertThisInitialized(i)), + i + ); + } + TutorialSystem_inheritsLoose(TutorialSystem, n); + var t, + e, + i, + o = TutorialSystem.prototype; + return ( + (o.run = function () { + this._enabled && this.show(); + }), + (o.pause = function () { + this._enabled && (this.view.visible = !1); + }), + (o.resume = function () { + this._enabled && (this.view.visible = !0); + }), + (o.build = function () { + this._built || ((this._built = !0), (this.arrow = new hn()), this.view.addChild(this.arrow), (this.msg = new dn(this.game.app)), this.view.addChild(this.msg), this.resize()); + }), + (o.show = function () { + this._enabled && (this.build(), this.game.stage.addChild(this.view), this.game.hero.hoverboard.lock()); + }), + (o.hide = function () { + this.game.stage.removeChild(this.view); + }), + (o.enterTrigger = function (t) { + this._enabled && (this.game.stats.distanceDelta < 0 || (this.arrow.show(t), this.msg.show(t, this.arrow.visible), "hoverboard" === t && ((this.game.stats.hoverboards = 2), this.game.hero.hoverboard.unlock()))); + }), + (o.exitTrigger = function (t) {}), + (o.update = function (t) { + this._enabled && (this.arrow && this.arrow.update(t), this.msg && this.msg.update(t)); + }), + (o.resize = function (t, e, i) { + (this.sw = t || this.sw), + (this.sh = e || this.sh), + (this.sr = i || this.sr), + (this.s = this.sh / (667 * this.sr)), + (this.w = this.sw / this.s), + (this.h = this.sh / this.s), + this.view.scale.set(this.s), + (this.view.x = this.sw / 2), + (this.view.y = this.sh / 2); + }), + (t = TutorialSystem), + (e = [ + { + key: "enabled", + get: function () { + return this._enabled; + }, + set: function (t) { + this._enabled = t; + }, + }, + ]) && TutorialSystem_defineProperties(t.prototype, e), + i && TutorialSystem_defineProperties(t, i), + TutorialSystem + ); + })(f.a), + hn = (function (e) { + function Arrow() { + var t; + return ( + ((t = e.call(this) || this).img = c.Sprite.from("tutorial_arrow.png")), t.img.anchor.set(0.5), t.img.scale.set(2), (t.img.alpha = 0.5), t.addChild(t.img), (t.visible = !1), (t.time = 0), (t.duration = 0), (t.animRange = 300), t + ); + } + TutorialSystem_inheritsLoose(Arrow, e); + var t = Arrow.prototype; + return ( + (t.show = function (t, e) { + void 0 === e && (e = 40); + var i = { + up: 0, + right: 1, + down: 2, + left: 3, + }; + void 0 !== i[t] && ((this.visible = !0), (this.rotation = V.PI_HALF * i[t]), (this.time = 0), (this.duration = e), (this.img.y = this.animRange)); + }), + (t.hide = function () { + this.visible = !1; + }), + (t.update = function (t) { + if (this.visible) { + this.time += t; + var e = this.time / this.duration; + (this.img.y = this.animRange - 2 * this.animRange * e), 1 < e && this.hide(); + } + }), + Arrow + ); + })(c.Container), + dn = (function (i) { + function Msg(t) { + var e; + return ( + ((e = + i.call(this, "MSG", { + align: "center", + fill: 16777215, + fontSize: 50, + fontFamily: "Lilita One", + stroke: "black", + strokeThickness: 5, + anchor: 0.5, + }) || this).app = t), + (e.visible = !1), + (e.time = 0), + (e.duration = 60), + e + ); + } + TutorialSystem_inheritsLoose(Msg, i); + var t = Msg.prototype; + return ( + (t.show = function (t, e) { + var i = B.mobile ? "" : "_desktop", + n = this.app.strings.get("tutorial_" + t + i); + n || (n = this.app.strings.get("tutorial_" + t)), + n && + ((this.text = n), + (this.visible = !0), + (this.time = 0), + (this.duration = 20 + 2 * n.length), + (this.scale.y = 0), + an.a.to(this.scale, 0.01, { + y: 1, + }), + (this.y = e ? 300 : 0), + (this.showing = !0)); + }), + (t.hide = function () { + var t = this; + (this.showing = !1), + an.a.to(this.scale, 0.1, { + y: 0, + onComplete: function () { + t.visible = !1; + }, + }); + }), + (t.update = function (t) { + this.visible && this.showing && ((this.time += t), this.time > this.duration && this.hide()); + }), + Msg + ); + })(sn); + + function IntroSystem_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function IntroSystem_createClass(t, e, i) { + return e && IntroSystem_defineProperties(t.prototype, e), i && IntroSystem_defineProperties(t, i), t; + } + var ln = (function (n) { + var t, e; + + function IntroSystem(t, e) { + var i; + return void 0 === e && (e = {}), ((i = n.call(this, t, e) || this).game = t.game), (i.onIntroComplete = new et.a("onIntroComplete")), (i.time = 0), (i.duration = 60), (i.playing = !1), i; + } + (e = n), ((t = IntroSystem).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = IntroSystem.prototype; + return ( + (i.update = function (t) { + this.playing && (this.step1 && this.step1.update(t), this.step2 && this.step2.update(t), this.step3 && this.step3.update(t)); + }), + (i.preupdate = function (t) { + this.playing && (t || (t = this.game.delta), (this.time += t), this.time >= this.duration && (this.time = this.duration), this.time >= this.duration && this.complete()); + }), + (i.play = function () { + (this.playing = !0), + this.game.camera.takeControl(), + this.game.camera.updateIdle(0), + (this.time = 0), + (this.step1 = new un("step1", this.game.camera.rig, 0.4 * this.duration)), + (this.step1.to.idleX = -16.49361), + (this.step1.to.idleY = -8.666094), + (this.step1.to.idleZ = 12.52404), + (this.step1.to.idleRotX = 23.52661 * V.DEG_TO_RAD), + (this.step1.to.idleRotY = 55.26425 * V.DEG_TO_RAD), + (this.step1.to.mainX = 0), + (this.step1.to.mainY = B.cameraPosY), + (this.step1.to.mainZ = B.cameraPosZ), + (this.step1.to.mainRotX = B.cameraRotX), + (this.step1.to.mainRotY = 0), + (this.step2 = new un("step2", this.game.camera.rig, 0.3 * this.duration)), + (this.step2.to.idleX = -16.49361), + (this.step2.to.idleY = -8.666094), + (this.step2.to.idleZ = 12.52404), + (this.step2.to.idleRotX = 23.30551 * V.DEG_TO_RAD), + (this.step2.to.idleRotY = 55.47934 * V.DEG_TO_RAD), + (this.step2.to.mainX = 0), + (this.step2.to.mainY = B.cameraPosY), + (this.step2.to.mainZ = B.cameraPosZ), + (this.step2.to.mainRotX = B.cameraRotX), + (this.step2.to.mainRotY = 0), + (this.step3 = new un("step3", this.game.camera.rig, 0.3 * this.duration)), + (this.step3.to.idleX = 0), + (this.step3.to.idleY = 0), + (this.step3.to.idleZ = 0), + (this.step3.to.idleRotX = 0), + (this.step3.to.idleRotY = 0), + (this.step3.to.mainX = 0), + (this.step3.to.mainY = B.cameraPosY), + (this.step3.to.mainZ = B.cameraPosZ), + (this.step3.to.mainRotX = B.cameraRotX), + (this.step3.to.mainRotY = 0), + (this.step3.to.fov = B.cameraFov), + (this.step3.curve = St.quintIn), + (this.step1.next = this.step2), + (this.step2.next = this.step3), + this.step1.play(); + }), + (i.complete = function () { + (this.playing = !1), this.game.camera.releaseControl(), this.onIntroComplete.dispatch(), this.game.runFromIntro(); + }), + IntroSystem_createClass(IntroSystem, [ + { + key: "ratio", + get: function () { + return this.time / this.duration; + }, + }, + ]), + IntroSystem + ); + })(f.a), + cn = (function () { + function CameraPos(t) { + (this.idleX = 0), (this.idleY = 0), (this.idleZ = 0), (this.idleRotX = 0), (this.idleRotY = 0), (this.mainX = 0), (this.mainY = 0), (this.mainZ = 0), (this.mainRotX = 0), (this.mainRotY = 0), (this.fov = 60), t && this.copy(t); + } + return ( + (CameraPos.prototype.copy = function (t) { + (this.idleX = t.idleX), + (this.idleY = t.idleY), + (this.idleZ = t.idleZ), + (this.idleRotX = t.idleRotX), + (this.idleRotY = t.idleRotY), + (this.mainX = t.mainX), + (this.mainY = t.mainY), + (this.mainZ = t.mainZ), + (this.mainRotX = t.mainRotX), + (this.mainRotY = t.mainRotY), + (this.fov = t.fov); + }), + CameraPos + ); + })(), + un = (function () { + function CameraAnim(t, e, i) { + void 0 === i && (i = 1), (this.name = t), (this.rig = e), (this.from = new cn(this.rig)), (this.to = new cn(this.rig)), (this.curve = St.sineInOut), (this.time = 0), (this.duration = i), (this.next = null); + } + var t = CameraAnim.prototype; + return ( + (t.play = function () { + (this.playing = !0), (this.time = 0), (this.from = new cn(this.rig)); + }), + (t.update = function (t) { + if (this.playing) { + (this.time += t), this.time >= this.duration && (this.time = this.duration); + var e = this.curve ? this.curve(this.ratio) : this.ratio; + (this.rig.idleX = V.lerp(this.from.idleX, this.to.idleX, e)), + (this.rig.idleY = V.lerp(this.from.idleY, this.to.idleY, e)), + (this.rig.idleZ = V.lerp(this.from.idleZ, this.to.idleZ, e)), + (this.rig.idleRotX = V.lerp(this.from.idleRotX, this.to.idleRotX, e)), + (this.rig.idleRotY = V.lerp(this.from.idleRotY, this.to.idleRotY, e)), + (this.rig.mainX = V.lerp(this.from.mainX, this.to.mainX, e)), + (this.rig.mainY = V.lerp(this.from.mainY, this.to.mainY, e)), + (this.rig.mainZ = V.lerp(this.from.mainZ, this.to.mainZ, e)), + (this.rig.mainRotX = V.lerp(this.from.mainRotX, this.to.mainRotX, e)), + (this.rig.mainRotY = V.lerp(this.from.mainRotY, this.to.mainRotY, e)), + (this.rig.fov = V.lerp(this.from.fov, this.to.fov, e)), + this.time >= this.duration && this.complete(); + } + }), + (t.complete = function () { + (this.playing = !1), (this.time = this.duration), this.next && this.next.play(); + }), + IntroSystem_createClass(CameraAnim, [ + { + key: "ratio", + get: function () { + return this.time / this.duration; + }, + set: function (t) { + this.time = this.duration * V.clamp(t); + }, + }, + ]), + CameraAnim + ); + })(); + + function Game_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + c.DisplayObject.prototype.depthOffset = 0; + var pn = (function (a) { + var t, e; + + function Game(t, e, i) { + var n; + void 0 === e && (e = null); + var o = new c.Container(), + s = { + stage: o, + camera: new p.a(), + renderer: t.renderer, + }; + return ( + ((n = a.call(this, s) || this).app = t), + (n.w = 512), + (n.h = 512), + (n.s = 1), + n.app.renderer.addSystem(Ne, "bg"), + we.init(t.resourceManager.resources), + H.refreshCache(), + we.refreshCache(), + (n.config = e || B), + (n.stage = o), + (n.bendX = 0), + (n.bendY = 0), + (n.aspectRatio = 1), + (n.blurred = !1), + (n.sfx = i), + (n.onReset = new et.a("reset")), + (n.onIdle = new et.a("idle")), + (n.onRun = new et.a("run")), + (n.onPause = new et.a("pause")), + (n.onResume = new et.a("resume")), + (n.onGameover = new et.a("gameover")), + (n.onRevive = new et.a("revive")), + (n.onEnterTunnel = new et.a("enterTunnel")), + (n.onExitTunnel = new et.a("exitTunnel")), + n.addSystem(W, {}), + n.addSystem(Ie, {}), + n.addSystem(xe, {}), + n.addSystem(ke, {}), + n.addSystem(Pe, {}), + n.addSystem(Re, {}), + n.addSystem(Fe, {}), + n.addSystem(Qi, {}), + n.addSystem(tn, {}), + n.addSystem(rn, {}), + n.addSystem(ln, {}), + (e.debug || e.shortcuts) && n.addSystem(Be, {}), + (n.view3d.ambientLight.intensity = 1), + (n.hero = new wi()), + n.addChild(n.hero), + (n.cop = new Fi()), + n.addChild(n.cop), + n + ); + } + (e = a), ((t = Game).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + n, + o, + s = Game.prototype; + return ( + (s.onFocus = function () { + (this.blurred = !1), this.sfx.unmute(); + }), + (s.onBlur = function () { + (this.blurred = !0), this.sfx.mute(), this.state === Game.RUNNING && this.pause(); + }), + (s.reset = function () { + this.hero && + ((this.time = 0), + (this.timeScale = this.config.timeScale), + (this.delta = 0), + (this.secs = 0), + (this.bendX = 0), + (this.bendY = 0), + this.hero.reset(), + this.cop && this.cop.reset(), + this.dog && this.dog.reset(), + this.hud.clearMessage(), + this.onReset.run(), + this.forceUpdate()); + }), + (s.updateAnimFrame = function (t) { + if (!(B.frameSkip && (this._o || (this._o = 0), (this._o += 1), this._o % B.frameSkip))) { + void 0 === this._lastTime && (this._lastTime = t); + var e = t, + i = e - this._lastTime; + this._lastTime = e; + var n = i / (1e3 / 60); + this.update(n); + } + }), + (s.updateTicker = function (t) { + if (B.frameSkip) { + if ((this._o || (this._o = 0), this._acc || (this._acc = 0), (this._acc += t), (this._o += 1), this._o % B.frameSkip)) return; + (t = this._acc), (this._acc = 0); + } + this.update(t); + }), + (s.update = function (t, e) { + if ((void 0 === e && (e = !0), !this.app.suspended && !this.app.crashed)) { + if ((t > B.deltaCap && (t = B.deltaCap), 1 !== !this.timeScale && (t *= this.timeScale), B.smoothDelta ? (this.delta -= 0.2 * (this.delta - t)) : (this.delta = t), B.frameByFrame && this.state !== Game.RUNNING)) { + if ((this._accDelta || (this._accDelta = 0), (this._accDelta += 1 / 60 / B.frameByFrame), this._accDelta < 1)) return; + (this.delta = 1), (this._accDelta = 0); + } + if (((this.fps = (1 / this.delta) * B.fps), (this.targetDelta = 1), (this.targetDeltaSecs = 1 / B.fps), (this.deltaSecs = this.delta / B.fps), this.state !== Game.PAUSED)) { + if (((this.time += this.delta), (this.secs += this.deltaSecs), this.state === Game.RUNNING ? ((this.stats.time += this.deltaSecs), (this.stats.delta = this.deltaSecs)) : (this.stats.delta = 0), this.config.bend)) { + (this.bendX = B.bendX * this.aspectRatio), (this.bendY = B.bendY); + var i = O.unformGroup(); + (i.uBend[0] = this.bendX), (i.uBend[1] = this.bendY), (i.uTime = this.secs); + } + this._addEntities(), + this._removeEntities(), + this.onPreupdate.run(), + this._removeEntities(), + this.onUpdateEntities.run(this.delta), + this._removeEntities(), + this.onPostupdate.run(), + this.updateAndRender && e && this.render(this.delta, 1); + } + } + }), + (s.forceUpdate = function (t, e) { + void 0 === t && (t = 1), void 0 === e && (e = !1), (this.delta = t), this.update(t, e); + }), + (s.idle = function () { + this.state === Game.RUNNING && nn.a.SDK.gameplayStop(), + (this.tutorial.enabled = this.app.user.tutorial || B.tutorial), + this.reset(), + (this.state = Game.IDLE), + this.hero.player.reset(0, 1.2), + this.cop && this.cop.reset(), + this.onIdle.run(), + B.loadAll && (this.level.queueNextChunk(), this.level.queueNextChunk(), this.level.queueNextChunk()), + this.forceUpdate(); + }), + (s.run = function () { + this.state !== Game.RUNNING && + (H.refreshCache(), + we.refreshCache(), + this.hud.build(), + this.hero.init(), + (this.state = Game.RUNNING), + this.reset(), + this.onRun.run(), + this.playTheme(), + this.cop && this.cop.run(), + this.hero.run(), + nn.a.SDK.gameplayStart(), + nn.a.sendCustomMessage("game", "roundStart", {}), + this.sfx.volume(this.app.settings.sound ? this.config.volume : 0)); + }), + (s.runWithIntro = function () { + var t = this; + B.loadAll || (H.refreshCache(), we.refreshCache(), this.level.queueNextChunk(), this.level.queueNextChunk(), this.level.queueNextChunk()), + nn.a.SDK.gameplayStart(), + nn.a.sendCustomMessage("game", "roundStart", {}), + this.playTheme(), + this.sfx.volume(this.app.settings.sound ? this.config.volume : 0); + var e = this._firstIntroPassed ? 50 : 300; + this.cop.scale.set(0.01), + setTimeout(function () { + t.hero.playIntro(), t.cop.playIntro(), t.intro.play(), t.forceUpdate(3); + }, e), + (this._firstIntroPassed = !0); + }), + (s.runFromIntro = function () { + this.hud.build(), this.cop && this.cop.run(), this.hero.run(), this.camera.run(), this.onRun.run(), (this.state = Game.RUNNING); + }), + (s.pause = function () { + this.state === Game.RUNNING && ((this.state = Game.PAUSED), this.onPause.run(), nn.a.SDK.gameplayStop()); + }), + (s.resume = function (t) { + void 0 === t && (t = 0), this.state === Game.PAUSED && (t ? this.hud.runCountdown(t, this.resume.bind(this)) : ((this.state = Game.RUNNING), this.onResume.run(), nn.a.SDK.gameplayStart())); + }), + (s.gameover = function () { + (this.state = Game.GAMEOVER), this.hero.player.stop(), this.cop && this.cop.catchHero(), this.hero && this.onGameover.run(), nn.a.SDK.gameplayStop(), nn.a.sendCustomMessage("game", "roundEnd", {}); + }), + (s.revive = function (t) { + var e = this; + void 0 === t && (t = 0), + this.state !== Game.RUNNING && + (this.hero.player.reset(this.hero.body.z, this.hero.body.x, this.hero.body.lane), + (this.hero.active = !1), + this.cop && this.cop.reset(), + t + ? this.hud.runCountdown(t, this.revive.bind(this)) + : ((this.state = Game.RUNNING), + (this.hero.active = !0), + this.hero.revive(), + this.onRevive.run(), + this.hero.player.run(0), + this.cop && this.cop.run(), + nn.a.SDK.gameplayStart(), + setTimeout(function () { + e.level.reshuffle(); + }, 0))); + }), + (s.resize = function (t, e, i) { + (this.aspectRatio = e / t), (this.w = t), (this.h = e), (this.s = i), this.hud && this.hud.resize(t, e, i), this.tutorial && this.tutorial.resize(t, e, i), this.controller && this.controller.resize(t, e); + }), + (s.playTheme = function () { + !this.playingTheme && + B.theme && + ((this.playingTheme = !0), + this.sfx.volume(this.config.volume), + this.sfx.play("theme", { + theme: !0, + }), + this.sfx.loadAll()); + }), + (s.enterTunnel = function () { + this.onEnterTunnel.run(); + }), + (s.exitTunnel = function () { + this.onExitTunnel.run(); + }), + (i = Game), + (n = [ + { + key: "profile", + get: function () { + return { + fps: this.fps, + deltaStep: this.delta, + deltaSecs: this.deltaSecs, + objects: this.allEntities.length, + entities: this.level.entities.length, + }; + }, + }, + ]) && Game_defineProperties(i.prototype, n), + o && Game_defineProperties(i, o), + Game + ); + })(s.a); + (pn.IDLE = 0), (pn.RUNNING = 1), (pn.PAUSED = 2), (pn.GAMEOVER = 3); + var mn = i(47), + fn = i(23); + mn.a.addSound = function (t, e, i) { + if (!this.disabled) { + for (var n = "", o = 1, s = null, a = 0; a < e.length; a++) "/" === e[a] && (s = a); + for (null !== s && (n = e.slice(s + 1, e.length)), e = n; this.exists(n); ) (n = e + "-" + o), o++; + i = i || {}; + var r = new Howl({ + src: t, + preload: this.preload, + autoplay: i.autoplay || !1, + loop: i.loop || !1, + volume: i.volume, + }); + i.autoplay && (this.preload || r.load(), r.play()), (r.realVolume = i.volume || 1), (this.sounds[n] = r); + } + }; + var gn = (function () { + function SoundFX(t) { + void 0 === t && (t = !1), + (this._volume = 1), + mn.a.disableVisibility(), + (mn.a._visibilityActive = !1), + (mn.a.mute = function () {}), + (mn.a.unmute = function () {}), + fn.Howler.volume(this._volume), + (this.map = {}), + (this.disabled = t); + } + var t = SoundFX.prototype; + return ( + (t.setup = function (t, e) { + for (var i in t) { + var n = i.split("/").pop(); + this.map[n] = { + key: i, + path: e + "/" + i, + }; + } + }), + (t.load = function (t) { + if (!this.disabled) { + var e = this.map[t]; + if (!e.added) { + e.added = !0; + var i = e.path, + n = [i + ".ogg?h=1gefxm5mjzi8pjlr", i + ".mp3?h=1gefxm5mjzi8pjlr"]; + mn.a.addSound(n, e.key, {}); + } + } + }), + (t.loadAll = function () { + if (!this.disabled) for (var t in this.map) this.load(t); + }), + (t.play = function (t, e) { + var i = void 0 === e ? {} : e, + n = i.volume, + o = void 0 === n ? null : n, + s = i.loop, + a = void 0 === s ? null : s, + r = i.theme, + h = void 0 === r ? null : r; + if (!this.disabled) { + this.load(t); + var d = mn.a.play(t); + return ( + d && + (null !== o && d.volume(o), + null !== a && d.loop(a), + null !== h && + d.on("end", function () { + return d.play(); + })), + d + ); + } + }), + (t.stop = function (t) { + this.disabled || mn.a.stop(t); + }), + (t.volume = function (t) { + if (!this.disabled) return void 0 !== t && ((this._volume = t), fn.Howler.volume(t)), this._volume; + }), + (t.mute = function () { + fn.Howler.mute(!0), fn.Howler.volume(0); + }), + (t.unmute = function () { + fn.Howler.mute(!1), fn.Howler.volume(this._volume); + }), + SoundFX + ); + })(); + + function Section_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + var yn = { + w: 512, + h: 512, + single: !0, + }, + vn = (function () { + function Section(t, e) { + void 0 === t && (t = ""), + void 0 === e && (e = {}), + (this.name = t), + (this.owner = null), + (this.subs = {}), + (this.view = new c.Container()), + (this.container = null), + (this._opened = !1), + (this._built = !1), + (this.ticker = new c.Ticker()), + this.ticker.stop(), + (this.on = { + build: new l.a(), + resize: new l.a(), + open: new l.a(), + close: new l.a(), + openSub: new l.a(), + }), + this.on.build.add(this.onBuild, this), + this.on.resize.add(this.onResize, this), + this.on.open.add(this.onOpen, this), + this.on.close.add(this.onClose, this), + this.setup(e); + } + var t, + e, + i, + n = Section.prototype; + return ( + (n.setup = function (t) { + void 0 === t && (t = {}), (this.config = Object.assign({}, yn, t)), (this.w = this.config.w), (this.h = this.config.h), (this.s = 1); + }), + (n.addSub = function (t) { + this.subs[t.name] || (((this.subs[t.name] = t).w = this.w), (t.h = this.h), (t.owner = this), (t.container = this.view)); + }), + (n.build = function () { + this._built || ((this._built = !0), this.on.build.dispatch()); + }), + (n.open = function (t) { + if ( + (this._opened || + (this.config.single && this.closeOthers(), (this._opened = !0), this.owner && this.owner.open(), this._built || this.build(), this.resize(), this.container && this.container.addChild(this.view), this.on.open.dispatch()), + t && t.length) + ) { + var e = Array.isArray(t) ? t : t.split("/"), + i = e.shift(), + n = this.subs[i]; + if (!n) throw new Error("Sub section not found: " + i + " path to open: " + t); + this.on.openSub.dispatch(n.name), n.open(e); + } + }), + (n.close = function () { + for (var t in this.subs) this.subs[t].close(); + this._opened && this.on.close.dispatch(), this.view.parent && this.view.parent.removeChild(this.view), (this._opened = !1), this.ticker.stop(); + }), + (n.closeOthers = function () { + if (this.owner) + for (var t in this.owner.subs) { + var e = this.owner.subs[t]; + e !== this && e.close(); + } + }), + (n.closeSubs = function () { + for (var t in this.subs) this.subs[t].close(); + }), + (n.resize = function (t, e, i) { + for (var n in ((this.sw = t || this.sw), + (this.sh = e || this.sh), + (this.sr = i || this.sr), + (this.s = this.sh / (667 * this.sr)), + (this.w = this.sw / this.s), + (this.h = this.sh / this.s), + this.owner || this.view.scale.set(this.s), + this.on.resize.dispatch(), + this.subs)) { + var o = this.subs[n]; + (o.w = this.w), (o.h = this.h), (o.s = this.s), (o.sw = this.sw), (o.sh = this.sh), (o.sr = this.sr), o._opened && o.resize(this.sw, this.sh, this.sr); + } + }), + (n.onBuild = function () {}), + (n.onResize = function () {}), + (n.onOpen = function () {}), + (n.onClose = function () {}), + (n.getMap = function (t) { + for (var e in (void 0 === t && (t = {}), this.owner && (t[this.path] = this), this.subs)) this.subs[e].getMap(t); + return t; + }), + (t = Section), + (e = [ + { + key: "path", + get: function () { + return this.owner ? this.owner.path + "/" + this.name : this.name; + }, + }, + { + key: "root", + get: function () { + return this.owner ? this.owner.root : this; + }, + }, + ]) && Section_defineProperties(t.prototype, e), + i && Section_defineProperties(t, i), + Section + ); + })(); + var _n = (function (i) { + var t, e; + + function TitleScreen(t) { + var e; + return ((e = i.call(this) || this).app = t), e; + } + return (e = i), ((t = TitleScreen).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e), TitleScreen; + })(c.Container); + + function Button_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function Button_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + var bn = { + default: { + w: 120, + h: 120, + round: 5, + color: 3246014, + border: 16777215, + }, + small: { + w: 80, + h: 80, + round: 5, + color: 3246014, + border: 16777215, + }, + }, + wn = (function (s) { + var t, e; + + function Button(t) { + var e; + void 0 === t && (t = {}), ((e = s.call(this) || this).name = t.name || ""); + var i = t, + n = i.base, + o = i.icon; + return n && e.setBase(n), o && e.setIcon(o), e.on("pointertap", e.onPointerTap.bind(Button_assertThisInitialized(e)), Button_assertThisInitialized(e)), e; + } + (e = s), ((t = Button).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i, + n, + o, + a = Button.prototype; + return ( + (a.setBase = function (t) { + if ((this.base && (Ce.clear(this.base), this.removeChild(this.base)), t)) { + if ("string" != typeof t || bn[t]) + if (t.children) this.base = t; + else { + var e = Object.assign({}, bn["default"], bn[t] || t); + this.base = Ce.roundRectBorder(e); + } + else (this.base = c.Sprite.from(t)), this.base.anchor.set(0.5); + this.addChildAt(this.base, 0); + } + }), + (a.setIcon = function (t) { + if ((this.icon && this.removeChild(this.icon), t)) { + var e = t.x || 0, + i = t.y || 0, + n = "string" == typeof t ? t : t.image; + (this.icon = c.Sprite.from(n)), this.icon.anchor.set(0.5), (this.icon.x = e), (this.icon.y = i), this.addChild(this.icon); + } + }), + (a.setLabel = function (t, e) { + this.label && this.removeChild(this.label), (this.label = new sn(t, e)), this.addChild(this.label); + }), + (a.onPointerTap = function (t) { + t.data.originalEvent.preventDefault(), this._onTap && this._onTap(); + }), + (a.onKeyDown = function (t) { + this.interactive && this.visible && this._key && !t.repeat && this._onTap && t[this._keyField] === this._key && this._onTap(); + }), + (a._update = function () {}), + (i = Button), + (n = [ + { + key: "key", + get: function () { + return this._key; + }, + set: function (t) { + (this._key = t), + (this._keyField = "string" == typeof t ? "code" : "which"), + this._onKeyDownBind || (this._onKeyDownBind = this.onKeyDown.bind(this)), + document.removeEventListener("keyup", this._onKeyDownBind), + this._key && document.addEventListener("keyup", this._onKeyDownBind); + }, + }, + { + key: "onTap", + set: function (t) { + (this._onTap = t), (this.enabled = !!t); + }, + get: function () { + return this._onTap; + }, + }, + { + key: "enabled", + set: function (t) { + this.interactive = t; + var e = (this.buttonMode = t) ? 1 : 0.5; + this.base && (this.base.alpha = e); + }, + get: function () { + return this.interactive; + }, + }, + { + key: "selected", + set: function (t) { + var e = (this._selected = t) ? 5592405 : 16777215; + this.base && (this.base.fill.tint = e); + }, + get: function () { + return !!this._selected; + }, + }, + ]) && Button_defineProperties(i.prototype, n), + o && Button_defineProperties(i, o), + Button + ); + })(c.Container); + var kn = (function (i) { + var t, e; + + function GameScreen(t) { + var e; + return ((e = i.call(this) || this).app = t), e; + } + (e = i), ((t = GameScreen).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = GameScreen.prototype; + return ( + (n.onShow = function () { + this._firstRun || ((this._firstRun = !0), nn.a.sendCustomMessage("game", "play", {})), + this.app.game.onRun.add(this), + this.app.game.onPause.add(this), + this.app.game.onResume.add(this), + this.app.game.onGameover.add(this), + this.app.game.onRevive.add(this), + this.app.game.runWithIntro(), + this.btnPause || + ((this.btnPause = new wn({ + icon: "icon_pause.png", + base: "small", + })), + (this.btnPause.onTap = this.onBtnPausePress.bind(this)), + this.btnPause.scale.set(0.5), + (this.btnPause.x = 35), + (this.btnPause.y = 35)), + this.addChild(this.btnPause), + (this.btnPause.visible = !0); + }), + (n.onHide = function () { + this.app.game.onPause.remove(this), this.app.game.onResume.remove(this), this.app.game.onGameover.remove(this), this.app.game.onRevive.remove(this); + }), + (n.run = function () { + this.updateView(); + }), + (n.gameover = function () { + var t = this; + this.updateView(), (this.btnPause.visible = !1); + var e = B.mobile ? "Mobile" : "", + i = this.app.config["rewardedBreaks" + e], + n = this.app.game.stats.revivals && !this.app.config.adBlocking; + setTimeout(function () { + i && n ? ((t.btnPause.visible = !1), (t.app.game.stats.revivals -= 1), t.app.sections.open("saveme")) : t.finishRun(); + }, 1500); + }), + (n.finishRun = function () { + var e = this; + if ((this.updateView(), (this.btnPause.visible = !1), this.app.user.load(), (this.app.user.tutorial = !1), !this.app.user.nameSet && this.app.config.leaderboards)) { + var t = this.app.strings.get("nickname_prompt"); + this.app.prompt.open(t, this.app.user.name, function (t) { + t && (e.app.user.name = t), (e.app.user.nameSet = !0), e.finishRunComplete(); + }); + } else this.finishRunComplete(); + }), + (n.finishRunComplete = function () { + var t = this, + e = this.app.game.stats.score; + (this.app.user.coins += this.app.game.stats.coins), + e > this.app.user.score + ? ((this.app.user.score = e), + nn.a.SDK.happyTime(1), + nn.a.hangout.saveHighscore(this.app.user.name, e).then(function () { + t.app.sections.open("gameover"); + })) + : this.app.sections.open("gameover"), + this.app.user.save(); + }), + (n.revive = function () { + this.updateView(); + }), + (n.pause = function () { + this.updateView(); + }), + (n.resume = function () { + this.updateView(); + }), + (n.resize = function (t, e) {}), + (n.onBtnPausePress = function () { + this.app.game.state === pn.PAUSED ? (this.app.game.resume(0), this.updateView(!1)) : (this.app.game.pause(), this.updateView()); + }), + (n.onBtnPlayPress = function () { + this.app.game.resume(3), this.updateView(!1); + }), + (n.onBtnRestartPress = function () { + this.app.game.run(), this.updateView(); + }), + (n.updateView = function (t) { + void 0 === t && (t = this.app.game.state === pn.PAUSED), t ? this.app.sections.open("pause") : this.app.sections.close(), this.btnPause && (this.btnPause.visible = this.app.game.state !== pn.PAUSED); + }), + GameScreen + ); + })(c.Container); + var xn = (function (n) { + var t, e; + + function LoadingScreen(t, e) { + var i; + return ( + void 0 === e && (e = 998272), + ((i = n.call(this) || this).app = t), + (i.w = 512), + (i.h = 512), + (i.color = "string" == typeof e ? c.utils.string2hex(e) : e), + (i.view = new c.Container()), + i.addChild(i.view), + (i.firstLoadPassed = !1), + i + ); + } + (e = n), ((t = LoadingScreen).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = LoadingScreen.prototype; + return ( + (i.build = function () { + this.bg || + ((this.bg = new c.Graphics()), + this.bg.beginFill(this.color, 1), + this.bg.drawRect(0, 0, 16, 16), + this.bg.endFill(), + this.addChildAt(this.bg, 0), + (this.splash = c.Sprite.from("preload/splash")), + this.splash.anchor.set(0.5), + this.view.addChild(this.splash), + this.buildBar(), + this.resize(), + this.updateView()); + }), + (i.buildBar = function () { + if (!this.bar) { + (this.bar = new c.Container()), this.view.addChild(this.bar), (this.bar.y = 160); + var t = 300, + e = 40, + i = new c.Graphics(); + i.beginFill(0, 0.75), i.drawRect(0, 0, t, e), i.position.set(-t / 2, -e / 2), this.bar.addChild(i), (this.bar.bg = i), (t -= 8), (e -= 8); + var n = new c.Graphics(); + n.beginFill(16142336), n.drawRect(0, 0, t, e), n.position.set(-t / 2, -e / 2), (n.scale.x = 0.01), this.bar.addChild(n), (this.bar.fill = n); + var o = "loading_" + this.app.config.lang, + s = (this.app.config[o] || "loading").toUpperCase() + "...", + a = new sn(s, { + fill: 16777215, + fontSize: 20, + dropShadow: !1, + }); + this.bar.addChild(a), (this.bar.label = a); + } + }), + (i.updateProgress = function (t) { + this.bar && (this.bar.fill.scale.x = t / 100); + }), + (i.resize = function (t, e) { + (this.w = t || this.w), (this.h = e || this.h), (this.view.position.x = this.w / 2), (this.view.position.y = this.h / 2), this.bg && ((this.bg.width = this.w), (this.bg.height = this.h)), this.updateView(); + }), + (i.onShow = function () { + this.updateView(!this.app.bgLoading), this.reset(), (this._opened = !0); + }), + (i.onHidden = function () {}), + (i.updateView = function (t) { + void 0 !== t && (this.view.visible = t), this.bg && ((this.bg.visible = this.view.visible), (this.bg.alpha = this.firstLoadPassed ? 0.5 : 1)), this.splash && (this.splash.visible = !this.firstLoadPassed); + }), + (i.holdForFrame = function () { + (this.view.visible = !0), this.bg && (this.bg.visible = !0), this.splash && (this.splash.visible = !0); + }), + (i.reset = function () { + this.updateProgress(0); + }), + LoadingScreen + ); + })(c.Container), + Cn = i(68), + Sn = (function () { + function Event(t) { + this.app = t; + } + var t = Event.prototype; + return ( + (t.init = function () { + var t = this.app.addSignal("onBlur"), + e = this.app.addSignal("onFocus"); + Cn.a.onShow.add(function () { + return e.dispatch(); + }), + Cn.a.onHide.add(function () { + return t.dispatch(); + }), + window.addEventListener("blur", function () { + return t.dispatch(); + }), + window.addEventListener("focus", function () { + return e.dispatch(); + }), + window.addEventListener("pagehide", function () { + return t.dispatch(); + }), + window.addEventListener("pageshow", function () { + return e.dispatch(); + }), + window.addEventListener("gesturestart", this._onEventPreventDefault.bind(this)), + window.addEventListener("gesturechange", this._onEventPreventDefault.bind(this)), + window.addEventListener("gestureend", this._onEventPreventDefault.bind(this)), + window.addEventListener("touchmove", this._onEventPreventDefault.bind(this)); + }), + (t._onEventPreventDefault = function (t) { + t.preventDefault(); + }), + Event + ); + })(), + zn = (function () { + function Message(t) { + this.app = t; + } + var t = Message.prototype; + return ( + (t.init = function () { + var e = this.app.addSignal("onMessage"); + window.addEventListener( + "message", + function (t) { + return e.dispatch(t); + }, + !1 + ); + }), + (t.postToParent = function (t, e) { + var i = window.parent; + if (i) + try { + i.postMessage( + { + type: t, + content: { + event: e, + }, + }, + "*" + ); + } catch (t) {} + }), + (t.sendCustomMessage = function (t, e, i) { + if (window.parent) + try { + window.parent.postMessage( + { + type: "pokiMessageEvent", + content: { + event: "pokiTrackingCustom", + data: { + eventNoun: t, + eventVerb: e, + eventData: i, + }, + }, + }, + "*" + ); + } catch (t) {} + }), + Message + ); + })(), + Tn = (function () { + function Size(t) { + (this.app = t), (this.w = 512), (this.h = 512), (this.sw = 512), (this.sh = 512), (this.scale = 1); + } + var t = Size.prototype; + return ( + (t.init = function () { + var t = this; + (this._onResize = this.app.addSignal("onResize")), + window.addEventListener("resize", function () { + return t.onChange(); + }), + window.addEventListener("orientationchange", function () { + return t.onChange(); + }), + this.update(); + }), + (t.onChange = function () { + var t = this; + this.update(); + for (var e = 0; e < 5; e++) + setTimeout(function () { + return t.update(); + }, 200 * e); + }), + (t.update = function () { + window.scrollTo(0, 0), + (this.w = window.innerWidth), + (this.h = window.innerHeight), + (this.scale = window.devicePixelRatio || 1), + r.a.instance.isMobile && !window.devicePixelRatio && (this.scale = window.screen.deviceXDPI / window.screen.logicalXDPI), + this.scale > B.maxViewportScale && (this.scale = B.maxViewportScale), + (this.sw = this.w * this.scale), + (this.sh = this.h * this.scale), + this._onResize.dispatch(this); + }), + (t.requestFullScreen = function () { + r.a.instance.android && (document.body.mozRequestFullScreen ? document.body.mozRequestFullScreen() : document.body.webkitRequestFullScreen && document.body.webkitRequestFullScreen(), this.update()); + }), + Size + ); + })(), + Pn = (function () { + function Strings(t) { + (this.app = t), (this.lang = "en"), (this.data = {}); + } + var t = Strings.prototype; + return ( + (t.onAppReady = function () { + this.load(this.app.config.lang); + }), + (t.get = function (t) { + return this.data[t]; + }), + (t.upper = function (t) { + return this.data[t].toUpperCase(); + }), + (t.load = function (t) { + t && (this.lang = t), (this.data = this.loadStrings(this.lang)); + }), + (t.loadStrings = function (t) { + var e = this.app.resourceManager.resources, + i = e["data/strings_en"].data, + n = e["data/strings_" + t].data; + return Object.assign({}, i, n); + }), + Strings + ); + })(), + Mn = (function () { + function Debug(t) { + this.app = t; + } + var t = Debug.prototype; + return ( + (t.init = function () { + (this.div = document.createElement("div")), + (this.div.style.backgroundColor = "rgba(255,255,255,0.5)"), + (this.div.style.width = "100%"), + (this.div.style.height = "100%"), + (this.div.style.top = "50px"), + (this.div.style.left = "0px"), + (this.div.style.position = "absolute"), + (this.div.style.zIndex = 999), + window.addEventListener("keydown", this.onKeyDown.bind(this)); + }), + (t.onAppReady = function () {}), + (t.onResize = function (t, e) {}), + (t.onKeyDown = function (t) { + "Escape" === t.key && this.toggle(); + }), + (t.show = function () { + this.shown || ((this.shown = !0), document.body.appendChild(this.div), this.mountSections()); + }), + (t.hide = function () { + this.shown && ((this.shown = !1), document.body.removeChild(this.div)); + }), + (t.toggle = function () { + this.shown ? this.hide() : this.show(); + }), + (t.mountSections = function () { + var o = this; + if (!this.divSections) { + (this.divSections = document.createElement("div")), this.div.appendChild(this.divSections); + var t = this.app.sections.getMap(), + s = Object.keys(t).sort(), + e = function (t) { + var e = s[t], + i = document.createElement("button"), + n = e.replace("root/", ""); + (i.innerText = n), + o.divSections.appendChild(i), + o.divSections.appendChild(document.createElement("br")), + i.addEventListener("click", function () { + return o.app.sections.open(n); + }); + }; + for (var i in s) e(i); + } + }), + Debug + ); + })(); + var En = (function (i) { + var t, e; + + function SectionTitle(t) { + var e; + return ((e = i.call(this) || this).app = t), (e.name = "title"), e; + } + (e = i), ((t = SectionTitle).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = SectionTitle.prototype; + return ( + (n.onBuild = function () { + var t = this; + i.prototype.onBuild.call(this), + (this.labelMsg = new sn("")), + (this.labelMsg.rotation = 0.15), + this.view.addChild(this.labelMsg), + (this.tapToPlayArea = new c.Graphics()), + this.tapToPlayArea.beginFill(16711680), + this.tapToPlayArea.drawRect(0, 0, 16, 16), + this.tapToPlayArea.endFill(), + (this.tapToPlayArea.alpha = 0), + (this.tapToPlayArea.interactive = !0), + this.tapToPlayArea.addListener("pointerdown", this.playGame.bind(this)), + this.view.addChild(this.tapToPlayArea), + (this.bottomMenu = new c.Container()), + this.view.addChild(this.bottomMenu), + (this.btnTopRun = new wn()), + this.btnTopRun.setIcon({ + image: "front_icon_top_run.png", + y: -10, + }), + this.btnTopRun.setBase({ + color: 5254027, + w: 230, + }), + this.bottomMenu.addChild(this.btnTopRun), + (this.btnTopRun.x = this.btnTopRun.width / 2), + (this.btnTopRun.onTap = function () { + return t.app.sections.open("toprun"); + }), + (this.btnSettings = new wn()), + this.btnSettings.setIcon("icon_settings.png"), + this.btnSettings.setBase( + Ce.rectComp( + { + w: 96, + h: 98, + image: "box_border_grey_small.png", + x: 1, + y: 1, + }, + { + w: 80, + h: 80, + color: 3574206, + round: 6, + } + ) + ), + (this.btnSettings.onTap = function () { + return t.app.sections.open("settings"); + }), + this.view.addChild(this.btnSettings), + (this.keyDownBind = this.onKeyDown.bind(this)); + }), + (n.onResize = function () { + i.prototype.onResize.call(this); + var t = this.w, + e = this.h; + (this.labelMsg.x = 0.5 * t - 100), + (this.labelMsg.y = e - 300), + (this.tapToPlayArea.width = t), + (this.tapToPlayArea.height = e), + (this.bottomMenu.x = t / 2 - this.bottomMenu.width / 2), + (this.bottomMenu.y = e - 90), + (this.btnSettings.x = 60), + (this.btnSettings.y = 60); + }), + (n.onOpen = function () { + //alert("n.onOpen"); + dataLayer.push({ + event: "ga_event", + ga_category: "Gamepage", + ga_action: "Main Menu", + ga_label: window.productTitle, + ga_noninteraction: true, + }); + i.prototype.onOpen.call(this), + (this.count = 0), + (this.tapToPlayArea.interactive = !0), + window.addEventListener("keydown", this.keyDownBind), + this.app.screenManager.gotoScreenByID("title"), + this.ticker.add(this.update, this, 1), + this.ticker.start(); + var t = B.mobile ? "tap_to_play" : "tap_to_play_desktop", + e = this.app.strings.get(t); + this.labelMsg.text = e; + }), + (n.onClose = function () { + i.prototype.onClose.call(this), (this.tapToPlayArea.interactive = !1), window.removeEventListener("keydown", this.keyDownBind), this.ticker.remove(this.update, this); + }), + (n.onKeyDown = function (t) { + t.repeat || 32 !== t.which || (this.app.game.state === pn.IDLE && this.playGame()); + }), + (n.playGame = function () { + console.info(window.productTitle); + dataLayer.push({ + event: "ga_event", + ga_category: "Gamepage", + ga_action: "Start", + ga_label: window.productTitle, + ga_noninteraction: false, + }); + + this.app.game.state === pn.IDLE && (nn.a.sendCustomMessage("mainMenu", "pressPlay", {}), (this.tapToPlayArea.interactive = !1), this.app.nav.playGame()); + }), + (n.update = function (t) { + (this.labelMsg.rotation = 0.15 + 0.02 * Math.sin(this.count)), (this.count += 0.1 * t); + }), + SectionTitle + ); + })(vn); + var On = (function (e) { + var t, i; + + function ScreenGlow() { + var t; + return ( + ((t = e.call(this) || this).w = 512), + (t.h = 512), + (t.bottom = c.Sprite.from("base_blurry.png")), + t.addChild(t.bottom), + (t.top = c.Sprite.from("base_blurry.png")), + t.addChild(t.top), + (t.top.scale.y = -1), + (t.top.anchor.y = 1), + (t.bg = new c.Graphics()), + t.bg.beginFill(0, 0.5), + t.bg.drawRect(0, 0, 16, 16), + t.bg.endFill(), + t.addChild(t.bg), + t + ); + } + return ( + (i = e), + ((t = ScreenGlow).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (ScreenGlow.prototype.resize = function (t, e) { + (this.w = t || this.w), + (this.h = e || this.h), + (this.bg.width = this.w), + (this.bg.height = this.h), + (this.bottom.width = this.w), + (this.bottom.height = 230), + (this.bottom.y = this.h - this.bottom.height), + (this.top.width = this.w), + (this.top.height = this.bottom.height); + }), + ScreenGlow + ); + })(c.Container); + var Rn = (function (e) { + var t, i; + + function Clock() { + var t; + return ( + ((t = e.call(this) || this).base = c.Sprite.from("clock_base.png")), + t.base.anchor.set(0.5), + (t.base.x = -1), + (t.base.y = -1), + t.addChild(t.base), + (t.fill = c.Sprite.from("clock_fill.png")), + t.fill.anchor.set(0.5), + t.addChild(t.fill), + (t.onComplete = null), + (t.ticker = new c.Ticker()), + t.ticker.stop(), + t + ); + } + (i = e), ((t = Clock).prototype = Object.create(i.prototype)), ((t.prototype.constructor = t).__proto__ = i); + var n = Clock.prototype; + return ( + (n.run = function (t, e) { + this.secs && this.stop(), (this.time = t), (this.secs = t), (this.onComplete = e), this.update(0), this.ticker.add(this.update, this, 1), this.ticker.start(); + }), + (n.stop = function () { + (this.secs = 0), this.ticker.remove(this.update, this), this.ticker.stop(); + }), + (n.complete = function () { + this.stop(), this.onComplete && this.onComplete(); + }), + (n.update = function (t) { + var e = t / 60; + (this.secs -= e), this.updatePizza(), this.secs < 0 && this.complete(); + }), + (n.updatePizza = function () { + this.pizza || ((this.pizza = new c.Graphics()), this.addChild(this.pizza)); + var t = this.secs / this.time, + e = V.PI_DOUBLE * t; + this.pizza.clear(), this.pizza.beginFill(15658734), this.pizza.moveTo(0, 0), this.pizza.arc(0, 0, 43, 0, e, !0), (this.pizza.rotation = -V.PI_HALF), (this.pizza.scale.y = -1); + }), + Clock + ); + })(c.Container); + var In = (function (i) { + var t, e; + + function SectionSaveMe(t) { + var e; + return ((e = i.call(this) || this).name = "saveme"), (e.app = t), e; + } + (e = i), ((t = SectionSaveMe).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = SectionSaveMe.prototype; + return ( + (n.onBuild = function () { + var t = this; + (this.bg = new On(this.app.color)), + this.view.addChild(this.bg), + (this.bg.interactive = !0), + this.bg.on("pointertap", function () { + return t.exit(); + }), + (this.content = new c.Container()), + this.view.addChild(this.content); + (this.base = Ce.rectComp( + { + w: 500, + h: 250, + color: 15658734, + round: 16, + }, + { + w: 522, + h: 274, + image: "box_border_grey.png", + x: 5, + y: 6, + } + )), + this.content.addChild(this.base), + (this.base.interactive = !0), + this.base.on("pointertap", function () { + return t.exit(); + }); + (this.btnRevive = new wn({ + key: "Space", + })), + this.btnRevive.setBase( + Ce.rectComp( + { + w: 316, + h: 120, + image: "box_border_grey.png", + x: 5, + y: 6, + }, + { + w: 300, + h: 100, + image: "box_fill_green.png", + x: 2, + y: 2, + r: 20, + b: 20, + } + ) + ), + this.btnRevive.setIcon({ + image: "icon_tv.png", + x: 60, + y: -10, + }), + this.btnRevive.setLabel(this.app.strings.upper("free"), { + fontSize: 40, + fontFamily: "Lilita One", + dropShadowDistance: 1, + x: -50, + }), + (this.btnRevive.onTap = function () { + return t.revive(); + }), + this.content.addChild(this.btnRevive); + this.btnRevive.y = 40; + var e = this.app.strings.get("save_me"); + (this.title = new sn(e, { + align: "center", + fill: 19072, + fontSize: 40, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 1, + anchor: 0.5, + })), + this.content.addChild(this.title), + (this.title.y = -80), + (this.clock = new Rn()), + this.content.addChild(this.clock), + (this.clock.x = -230), + (this.clock.y = -105); + }), + (n.onOpen = function () { + var t = this; + i.prototype.onOpen.call(this), + this.clock.run(6, function () { + return t.exit(); + }), + (this.btnRevive.key = "Space"), + (this._allowExit = !1), + setTimeout(function () { + t._allowExit = !0; + }, 500); + }), + (n.onClose = function () { + i.prototype.onClose.call(this), this.clock.stop(), (this.btnRevive.key = null); + }), + (n.exit = function () { + this._allowExit && this.app.gameScreen.finishRun(); + }), + (n.onResize = function () { + i.prototype.onResize.call(this), this.bg.resize(this.w, this.h), (this.content.x = this.w / 2), (this.content.y = this.h / 2); + }), + (n.revive = function () { + var e = this; + this.app.sections.close(), + this.app.suspend(), + B.debug + ? (this.app.resume(), this.app.game.revive(1)) + : nn.a.SDK.rewardedBreak().then(function (t) { + e.app.resume(), t ? e.app.game.revive(5) : e.app.gameScreen.finishRun(); + }); + }), + SectionSaveMe + ); + })(vn); + var Ln = (function (e) { + var t, i; + + function Scoreboard() { + var t; + return ( + ((t = e.call(this) || this).base = c.Sprite.from("scoreboard.png")), + t.base.anchor.set(0.5), + t.addChild(t.base), + (t.title = new sn("", { + align: "center", + fill: 19072, + fontSize: 60, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 1, + })), + t.addChild(t.title), + (t.title.x = -15), + (t.title.y = -110), + (t.title.rotation = -0.07), + (t.score = new sn("", { + align: "center", + fill: 16777215, + fontSize: 55, + fontFamily: "Lilita One", + dropShadow: !0, + dropShadowDistance: 1, + })), + t.addChild(t.score), + (t.score.y = -33), + (t.coins = new sn("", { + align: "center", + fill: 16777215, + fontSize: 45, + fontFamily: "Lilita One", + dropShadow: !0, + dropShadowDistance: 1, + })), + t.addChild(t.coins), + (t.coins.y = 35), + (t.icon = c.Sprite.from("icon_coin.png")), + t.icon.anchor.set(0.5), + t.icon.scale.set(0.75), + t.addChild(t.icon), + (t.icon.x = t.coins.x - 100), + (t.icon.y = t.coins.y), + (t.starL = c.Sprite.from("icon_star.png")), + t.starL.anchor.set(0.5), + (t.starL.x = -120), + t.title.addChild(t.starL), + (t.starR = c.Sprite.from("icon_star.png")), + t.starR.anchor.set(0.5), + (t.starR.x = -t.starL.x), + t.title.addChild(t.starR), + t + ); + } + return ( + (i = e), + ((t = Scoreboard).prototype = Object.create(i.prototype)), + ((t.prototype.constructor = t).__proto__ = i), + (Scoreboard.prototype.update = function (t) { + void 0 === t && (t = {}), + (t = Object.assign( + { + title: "Score", + score: 0, + coins: 0, + }, + t + )), + (this.title.text = t.title), + (this.score.text = t.score), + (this.coins.text = t.coins); + }), + Scoreboard + ); + })(c.Container); + var jn = (function (n) { + var t, e; + + function Spinner(t) { + var e; + e = n.call(this) || this; + var i = Object.assign( + {}, + { + radius: 32, + color: 0, + }, + t + ); + return ( + (e.image = new c.Graphics()), + e.image.lineStyle(i.radius, i.color), + e.image.arc(0, 0, 2 * i.radius, 0, 0.5 * Math.PI, !0), + e.addChild(e.image), + e.image.scale.set(0.5), + (e.image.alpha = 0.25), + (e.visible = !1), + (e.ticker = new c.Ticker()), + e.ticker.stop(), + e.ticker.add( + e.update, + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(e), + 1 + ), + e + ); + } + (e = n), ((t = Spinner).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Spinner.prototype; + return ( + (i.show = function () { + (this.visible = !0), this.ticker.start(); + }), + (i.hide = function () { + (this.visible = !1), this.ticker.stop(); + }), + (i.update = function (t) { + void 0 === t && (t = 0), (this.image.rotation += 0.2 * t); + }), + Spinner + ); + })(c.Container); + var Fn = (function (n) { + var t, e; + + function RemoteImage(t) { + var e; + e = n.call(this) || this; + var i = Object.assign( + {}, + { + path: "", + w: 0, + h: 0, + bg: null, + } + ); + return ( + "string" == typeof t ? (i.path = t) : Object.assign(i, t), + null !== i.bg && ((e.bg = new c.Graphics()), e.bg.beginFill(i.bg), e.bg.drawRect(-i.w / 2, -i.h / 2, i.w, i.h), e.bg.endFill(), e.addChildAt(e.bg, 0)), + (e.image = new c.Sprite()), + i.w && (e.image.width = i.w), + i.h && (e.image.height = i.h), + e.addChild(e.image), + (e.spinner = new jn()), + e.addChild(e.spinner), + e.spinner.show(), + i.path && e.load(i.path), + e + ); + } + (e = n), ((t = RemoteImage).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = RemoteImage.prototype; + return ( + (i.load = function (t) { + if (((this._path = t), (this.image.visible = !1), this.spinner && this.spinner.show(), c.utils.TextureCache[t])) this.onLoadComplete(); + else { + var e = RemoteImage.cache[t]; + e || ((e = new c.Loader()).add(t, t), (RemoteImage.cache[t] = e)), e.onLoad.once(this.onLoadComplete, this), e.load(); + } + }), + (i.onLoadComplete = function () { + (this.image.visible = !0), (this.image.texture = c.Texture.from(this._path)), this.image.anchor.set(0.5), this.spinner && this.spinner.hide(); + }), + RemoteImage + ); + })(c.Container); + Fn.cache = {}; + var An = (function (i) { + var t, e; + + function AvatarIcon(t) { + var e; + return ( + void 0 === t && (t = 64), + ((e = + i.call(this, { + w: t, + h: t, + bg: 16777215, + path: "", + }) || this).w = t), + (e.h = t), + e.spinner.scale.set(0.4), + e + ); + } + return ( + (e = i), + ((t = AvatarIcon).prototype = Object.create(e.prototype)), + ((t.prototype.constructor = t).__proto__ = e), + (AvatarIcon.prototype.update = function (t) { + void 0 === t && (t = {}), + (t = Object.assign( + { + image: "icon_friend", + color: 16777215, + border: 4, + }, + t + )), + (this.bg.width = this.w), + (this.bg.height = this.h), + (this.image.width = this.w - 2 * t.border), + (this.image.height = this.h - 2 * t.border), + this.spinner.scale.set(0.4), + this.load("assets/placeholder/" + t.image + ".png"); + }), + AvatarIcon + ); + })(Fn); + + function Ranking_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Dn = (function (s) { + function Ranking(t, e) { + var i; + void 0 === t && (t = 70), void 0 === e && (e = 10), ((i = s.call(this) || this).entries = []), (i.length = 11), (i.name = ""); + for (var n = (i.score = 0); n < i.length; n++) { + var o = new Gn(t, e); + i.entries.push(o), i.addChild(o); + } + return i.placeholder(), i; + } + Ranking_inheritsLoose(Ranking, s); + var t = Ranking.prototype; + return ( + (t.placeholder = function () { + for (var t = [], e = 0; e < this.length; e++) + t.push({ + index: e + 1, + loading: !0, + }); + this.updateEntries(t); + }), + (t.update = function (t, e) { + var i = this; + (this.name = t), + (this.score = e), + nn.a.hangout.getLeaderboard(this.length, t, e).then(function (t) { + return i.updateScores(t); + }); + }), + (t.updateScores = function (t) { + t = t.sort(function (t, e) { + return e.score - t.score; + }); + for (var e = !1, i = 0; i < this.length; i++) { + var n = t[i]; + n && (n.image || (n.image = "icon_friend"), (n.highlight = n.name === this.name && n.score === this.score), "ME" === n.name && (n.highlight = !0), n.highlight && (e = !0), (n.index = i + 1)); + } + e || + (t[this.length - 1] = { + name: this.name, + score: this.score, + image: "icon_friend", + highlight: !0, + index: "", + }), + this.updateEntries(t); + }), + (t.updateEntries = function (t) { + for (var e = this.title && this.title.text ? 80 : 0, i = 0; i < this.length; i++) { + var n = this.entries[i], + o = t[i]; + (n.visible = !!o), o && (n.update(o), (n.y = i * n.h + e)); + } + }), + (t.setTitle = function (t) { + this.title || + (this.title = new sn("", { + align: "center", + fill: 211825, + fontSize: 70, + fontFamily: "Titan One", + dropShadowDistance: 1, + })), + (this.title.visible = !!t), + (this.title.text = t), + this.addChild(this.title); + }), + Ranking + ); + })(c.Container), + Gn = (function (o) { + function RankingEntry(t, e) { + var i; + void 0 === t && (t = 75), void 0 === e && (e = 10), ((i = o.call(this) || this).w = 640), (i.h = t), (i.clampChars = e); + var n = { + align: "center", + fill: 666451, + fontSize: t * Math.min(t / 100, 0.5), + fontFamily: "Lilita One", + dropShadow: !1, + dropShadowDistance: 1, + maxWidth: 300, + }; + return ( + (i.index = new sn("", n)), + i.addChild(i.index), + (i.index._text.anchor.x = 1), + (i.name = new sn("", n)), + i.addChild(i.name), + (i.name._text.anchor.x = 0), + (i.score = new sn("", n)), + i.addChild(i.score), + (i.score._text.anchor.x = 1), + (i.avatar = new An(0.85 * t)), + i.addChild(i.avatar), + i + ); + } + return ( + Ranking_inheritsLoose(RankingEntry, o), + (RankingEntry.prototype.update = function (t) { + void 0 === t && (t = {}), + (t = Object.assign( + { + name: "", + score: 0, + index: 0, + avatar: "icon_friends", + highlight: !1, + }, + t + )), + (this.name.text = t.name.substr(0, this.clampChars)), + (this.score.text = t.score), + (this.index.text = t.index), + t.loading || + this.avatar.update({ + image: t.image, + }), + this.bg && (this.removeChild(this.bg), Ce.clear(this.bg), this.bg.destroy()); + var e = t.index % 2 ? 13294308 : 11388888; + t.highlight && (e = 16773461), + (this.bg = Ce.rect({ + w: this.w, + h: this.h, + round: 0, + color: e, + })), + this.addChildAt(this.bg, 0); + var i = this.w / 2 - 70; + (this.index.x = 0 - i), (this.avatar.x = 50 - i), (this.name.x = 100 - i), (this.score.x = 540 - i); + }), + RankingEntry + ); + })(c.Container); + var Bn = (function (i) { + var t, e; + + function CoinsTag(t) { + var e; + void 0 === t && (t = 64), + ((e = i.call(this) || this).base = Ce.rectShadow({ + w: 150, + h: 35, + color: 5394255, + round: 10, + shadowDistance: 2, + })), + e.addChild(e.base); + return ( + (e.coins = new sn("9", { + align: "center", + fill: 16185078, + fontSize: 30, + fontFamily: "Lilita One", + dropShadow: !0, + dropShadowDistance: 1, + icon: "icon_coin.png", + })), + e.addChild(e.coins), + e + ); + } + return ( + (e = i), + ((t = CoinsTag).prototype = Object.create(e.prototype)), + ((t.prototype.constructor = t).__proto__ = e), + (CoinsTag.prototype.update = function (t) { + this.coins.text = t; + }), + CoinsTag + ); + })(c.Container); + var Nn = (function (i) { + var t, e; + + function Menu(t) { + var e; + return ((e = i.call(this) || this).btnWidth = 150), (e.btnHeight = 100), (e.btns = []), (e.btn = {}), (e.w = 500), (e.h = 180), e; + } + (e = i), ((t = Menu).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = Menu.prototype; + return ( + (n.addButton = function (t) { + var e = t; + if (!t.on) { + var i = t.name, + n = t.icon, + o = t.color, + s = t.label, + a = t.onTap; + ((e = new wn()).name = i), + e.setIcon(n), + e.setBase( + Ce.rectComp( + { + w: this.btnWidth + 16, + h: this.btnHeight + 20, + image: "box_border_grey.png", + x: 5, + y: 6, + }, + { + w: this.btnWidth - 8, + h: this.btnHeight - 6, + color: o, + round: 12, + } + ) + ), + e.setLabel(s, { + fontSize: 28, + y: this.btnHeight / 2 + 25, + fontFamily: "Lilita One", + }), + (e.onTap = a); + } + this.addChild(e), this.btns.push(e), (this.btn[e.name] = e), this.organise(); + }), + (n.organise = function () { + for (var t = this.btnWidth + 30, e = ((this.btns.length - 1) * t) / 2, i = 0; i < this.btns.length; i++) { + var n = this.btns[i]; + (n.x = t * i - e), (n.y = -12); + } + }), + (n.resize = function (t, e) { + (this.w = t), this.organise(); + }), + (n.select = function (t) { + var e = this.btns, + i = Array.isArray(e), + n = 0; + for (e = i ? e : e[Symbol.iterator](); ; ) { + var o; + if (i) { + if (n >= e.length) break; + o = e[n++]; + } else { + if ((n = e.next()).done) break; + o = n.value; + } + var s = o; + s.selected = s.name === t; + } + }), + Menu + ); + })(c.Container); + + function SectionGameover_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Xn = (function (i) { + function SectionGameover(t) { + var e; + return ((e = i.call(this, t) || this).app = t), (e.name = "gameover"), e; + } + SectionGameover_inheritsLoose(SectionGameover, i); + var t = SectionGameover.prototype; + return ( + (t.onBuild = function () { + var t = this; + i.prototype.onBuild.call(this), (this.bg = new On()), this.view.addChild(this.bg); + (this.base = Ce.rectComp( + { + w: 690, + h: 1e3, + color: 15658734, + round: 16, + }, + { + w: 712, + h: 1024, + image: "box_border_grey.png", + x: 5, + y: 6, + } + )), + this.view.addChild(this.base), + (this.menu = new Nn(this.app)), + this.view.addChild(this.menu), + (this.content = new c.Container()), + this.view.addChild(this.content), + this.menu.addButton({ + name: "back", + icon: "icon_white_house.png", + label: this.app.strings.get("menu"), + color: 3684408, + onTap: function () { + return t.goToTitle(); + }, + }); + var e = new wn({ + name: "play", + }); + e.setBase( + Ce.rectComp( + { + w: 346, + h: 120, + image: "box_border_grey.png", + x: 5, + y: 6, + }, + { + w: 322, + h: 94, + color: 4298538, + round: 12, + } + ) + ), + e.setLabel(this.app.strings.upper("play"), { + fontFamily: "Lilita One", + }), + (e.onTap = function () { + return t.playAgain(); + }), + this.menu.addButton(e), + (this.menu.btn.back.x = -270), + this.menu.btn.boosts && (this.menu.btn.boosts.x = -90), + (this.menu.btn.play.x = 180), + (this.menu.btn.play.y = -12), + (this.jake = new Fn("assets/placeholder/jake.png")), + (this.jake.x = -180), + (this.jake.y = 60), + this.content.addChild(this.jake), + (this.scoreboard = new Ln()), + this.content.addChild(this.scoreboard), + (this.scoreboard.x = 130), + (this.scoreboard.y = 130), + (this.ranking = new Dn(50, this.app.config.maxNicknameChars)), + this.content.addChild(this.ranking), + (this.ranking.y = 300), + (this.coins = new Bn()), + this.content.addChild(this.coins), + (this.coins.x = this.scoreboard.x), + (this.coins.y = -80); + }), + (t.createBox = function (t, e, i, n) {}), + (t.onOpen = function () { + i.prototype.onOpen.call(this), + (this._actionTaken = !1), + this.ranking.update(this.app.user.name, this.app.game.stats.score), + this.coins.update(this.app.user.coins), + this.scoreboard.update({ + score: this.app.game.stats.score, + coins: this.app.game.stats.coins, + title: this.app.strings.get("score"), + }), + (this.menu.btn.play.key = 32); + }), + (t.onClose = function () { + i.prototype.onClose.call(this), (this.menu.btn.play.key = null); + }), + (t.goToTitle = function () { + this._actionTaken || ((this._actionTaken = !0), this.app.nav.goToTitle()); + }), + (t.playAgain = function () { + dataLayer.push({ + event: "ga_event", + ga_category: "Gamepage", + ga_action: "Start", + ga_label: window.productTitle, + ga_noninteraction: false, + }); + + this._actionTaken || ((this._actionTaken = !0), this.app.nav.playAgain()); + }), + (t.onResize = function () { + i.prototype.onResize.call(this), + this.bg.resize(this.w, this.h), + (this.menu.x = this.w / 2), + (this.menu.y = this.h - this.menu.h / 2), + this.base && ((this.base.x = 0.5 * this.w), (this.base.y = 0.5 * (this.h - this.menu.h))), + this.title && ((this.title.x = this.w / 2), (this.title.y = 150)), + (this.content.x = this.w / 2), + (this.content.y = 240); + }), + SectionGameover + ); + })(vn); + c.Container; + var Yn = (function (i) { + var t, e; + + function SectionTopRun(t) { + var e; + return ((e = i.call(this) || this).app = t), (e.name = "toprun"), e; + } + (e = i), ((t = SectionTopRun).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = SectionTopRun.prototype; + return ( + (n.onBuild = function () { + var t = this; + i.prototype.onBuild.call(this), (this.bg = new On()), this.view.addChild(this.bg); + (this.base = Ce.rectComp( + { + w: 690, + h: 1e3, + color: 15658734, + round: 16, + }, + { + w: 712, + h: 1024, + image: "box_border_grey.png", + x: 5, + y: 6, + } + )), + this.view.addChild(this.base), + (this.menu = new Nn(this.app)), + this.view.addChild(this.menu), + this.menu.addButton({ + name: "back", + icon: "icon_white_house.png", + label: this.app.strings.get("menu"), + color: 3684408, + onTap: function () { + return t.app.nav.goToTitle(); + }, + }), + (this.ranking = new Dn(70, this.app.config.maxNicknameChars)), + this.base.addChild(this.ranking), + (this.ranking.y = -310), + (this.title = new sn(this.app.strings.get("highscores"), { + align: "center", + fill: 19072, + fontSize: 70, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 2, + anchor: 0.5, + })), + this.base.addChild(this.title), + (this.title.y = -420); + }), + (n.onOpen = function () { + i.prototype.onOpen.call(this), this.ranking.update(this.app.user.name, this.app.user.score); + }), + (n.onResize = function () { + i.prototype.onResize.call(this), this.bg.resize(this.w, this.h), (this.menu.x = this.w / 2), (this.menu.y = this.h - this.menu.h / 2), (this.base.x = 0.5 * this.w), (this.base.y = 0.5 * (this.h - this.menu.h)); + }), + SectionTopRun + ); + })(vn); + var Hn = (function (i) { + var t, e; + + function SectionPause(t) { + var e; + return ((e = i.call(this) || this).app = t), (e.name = "pause"), e; + } + (e = i), ((t = SectionPause).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var n = SectionPause.prototype; + return ( + (n.onBuild = function () { + var t = this; + i.prototype.onBuild.call(this), + (this.bg = new On()), + this.view.addChild(this.bg), + (this.menu = new Nn(this.app)), + this.view.addChild(this.menu), + this.menu.addButton({ + name: "back", + icon: "icon_white_house.png", + label: this.app.strings.get("menu"), + color: 3684408, + onTap: function () { + t._actionTaken || ((t._actionTaken = !0), nn.a.sendCustomMessage("game", "roundCancel", {}), t.app.nav.goToTitle()); + }, + }); + var e = new wn({ + name: "resume", + }); + e.setBase( + Ce.rectComp( + { + w: 346, + h: 120, + image: "box_border_grey.png", + x: 5, + y: 6, + }, + { + w: 322, + h: 94, + color: 4298538, + round: 12, + } + ) + ), + e.setLabel(this.app.strings.upper("resume"), { + fontFamily: "Lilita One", + }), + (e.onTap = function () { + t.app.sections.close(), t.app.game.resume(3); + }), + this.menu.addButton(e), + (this.menu.btn.back.x = -270), + (this.menu.btn.resume.x = 180), + (this.menu.btn.resume.y = -12), + (this.title = new sn(this.app.strings.upper("pause"), { + align: "center", + fill: 16777215, + fontSize: 70, + fontFamily: "Titan One", + dropShadow: !0, + dropShadowDistance: 2, + anchor: 0.5, + })), + this.view.addChild(this.title); + }), + (n.onOpen = function () { + i.prototype.onOpen.call(this), (this._actionTaken = !1); + }), + (n.onResize = function () { + i.prototype.onResize.call(this), + this.bg.resize(this.w, this.h), + (this.menu.x = this.w / 2), + (this.menu.y = this.h - this.menu.h / 2), + this.base && ((this.base.x = 0.5 * this.w), (this.base.y = 0.5 * (this.h - this.menu.h))), + this.title && ((this.title.x = this.w / 2), (this.title.y = 150)), + (this.title.x = this.w / 2), + (this.title.y = this.h / 2); + }), + SectionPause + ); + })(vn); + + function SectionSettings_defineProperties(t, e) { + for (var i = 0; i < e.length; i++) { + var n = e[i]; + (n.enumerable = n.enumerable || !1), (n.configurable = !0), "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n); + } + } + + function SectionSettings_assertThisInitialized(t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + } + + function SectionSettings_inheritsLoose(t, e) { + (t.prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + } + var Un = (function (i) { + function SectionSettings(t) { + var e; + return ((e = i.call(this) || this).name = "settings"), (e.app = t), e; + } + SectionSettings_inheritsLoose(SectionSettings, i); + var t = SectionSettings.prototype; + return ( + (t.onBuild = function () { + var t = this; + (this.bg = new On(this.app.color)), this.view.addChild(this.bg); + (this.base = Ce.rectComp( + { + w: 690, + h: 1e3, + color: 15658734, + round: 16, + }, + { + w: 712, + h: 1024, + image: "box_border_grey.png", + x: 5, + y: 6, + } + )), + this.view.addChild(this.base), + (this.title = new sn(this.app.strings.get("settings"), { + align: "center", + fill: 19072, + fontSize: 70, + fontFamily: "Titan One", + dropShadow: !1, + dropShadowDistance: 2, + anchor: 0.5, + })), + this.base.addChild(this.title), + (this.title.y = -420), + (this.btnClose = new wn()), + (this.btnClose.x = -325), + (this.btnClose.y = -480), + this.base.addChild(this.btnClose), + this.btnClose.setBase("btn_close.png"), + (this.btnClose.onTap = function () { + return t.app.sections.open("title"); + }), + (this.version = new sn("v0.3.9", { + align: "right", + fill: 3836863, + fontSize: 18, + fontFamily: "Titan One", + anchor: 1, + })), + this.base.addChild(this.version), + (this.version.alpha = 0.5), + (this.version.x = 335), + (this.version.y = 494), + this.updateEntries(); + }), + (t.onOpen = function () { + i.prototype.onOpen.call(this); + }), + (t.onClose = function () { + i.prototype.onClose.call(this); + }), + (t.onResize = function () { + i.prototype.onResize.call(this), this.bg.resize(this.w, this.h), (this.base.x = 0.5 * this.w), (this.base.y = 0.5 * this.h); + }), + (t.buildEntries = function () { + var e = this; + if (!this._entriesBuilt) { + this._entriesBuilt = !0; + for ( + var t = [ + { + name: "nickname", + type: "input", + value: this.app.user.name, + icon: "icon_user.png", + title: this.app.strings.get("nickname"), + description: this.app.strings.get("nickname_prompt"), + onChange: function (t) { + (e.app.user.name = t), e.app.user.save(); + }, + }, + { + name: "sound", + type: "toggle", + value: this.app.settings.sound, + icon: "icon_sound.png", + title: this.app.strings.get("sound"), + subtitleTrue: this.app.strings.get("on"), + subtitleFalse: this.app.strings.get("off"), + onChange: function (t) { + (e.app.settings.sound = t), e.app.settings.save(), e.app.sfx.volume(t ? B.volume : 0); + }, + }, + { + name: "info", + type: "link", + icon: "icon_info.png", + title: this.app.strings.get("privacy_policy"), + link: this.app.strings.get("privacy_policy_link"), + }, + ], + i = 0; + i < t.length; + i++ + ) { + var n = t[i], + o = new Vn(this.app, n); + this.base.addChild(o), (o.y = i * o.h - 250), (this[o.name] = o); + } + } + }), + (t.updateEntries = function () { + this.buildEntries(), (this.nickname.value = this.app.user.name), (this.sound.value = this.app.settings.sound); + }), + SectionSettings + ); + })(vn), + Vn = (function (o) { + function Entry(t, e) { + var i; + void 0 === e && (e = {}), ((i = o.call(this) || this).app = t); + var n = Object.assign( + {}, + { + name: "entry", + type: "toggle", + value: !0, + icon: "icon_white_missions.png", + title: "Title", + subtitleTrue: "", + subtitleFalse: "", + description: "", + onChange: function (t) { + return t; + }, + }, + e + ); + return ( + (i.w = 550), + (i.h = 130), + (i.opts = n), + (i.name = n.name), + (i.type = n.type), + (i.bg = new c.Graphics()), + i.bg.beginFill(16711680, 1), + i.bg.drawRect(-i.w / 2, -i.h / 2, i.w, i.h), + i.addChild(i.bg), + (i.bg.alpha = 0), + (i.bg.interactive = !0), + i.bg.on("pointertap", i.onTap.bind(SectionSettings_assertThisInitialized(i)), SectionSettings_assertThisInitialized(i)), + (i.btn = new wn()), + i.btn.setIcon(n.icon), + i.btn.setBase( + Ce.rectComp( + { + w: 116, + h: 118, + image: "box_border_grey_small.png", + x: 1, + y: 1, + }, + { + w: 100, + h: 100, + color: 3574206, + round: 6, + } + ) + ), + (i.btn.onTap = i.onTap.bind(SectionSettings_assertThisInitialized(i))), + i.addChild(i.btn), + (i.btn.x = -i.w / 2 + 60), + (i.title = new sn(n.title, { + align: "left", + fill: 19072, + fontSize: 40, + fontFamily: "Titan One", + anchor: 0, + })), + i.addChild(i.title), + (i.title.x = i.btn.x + 80), + (i.title.y = n.value ? -35 : -20), + (i.subtitle = new sn(n.subtitleTrue, { + align: "left", + fill: 3836863, + fontSize: 30, + fontFamily: "Titan One", + anchor: 0, + })), + i.addChild(i.subtitle), + (i.subtitle.x = i.title.x), + (i.subtitle.y = i.title.y + 40), + (i.subtitle.description = n.description), + i.subtitle.on("change", function (t) { + return (i.value = t); + }), + (i.diagonal = c.Sprite.from("diagonal.png")), + i.diagonal.anchor.set(0.5), + i.btn.addChild(i.diagonal), + (i.diagonal.visible = !1), + (i.value = n.value), + i + ); + } + SectionSettings_inheritsLoose(Entry, o); + var t, + e, + i, + n = Entry.prototype; + return ( + (n.onTap = function () { + var e = this; + "toggle" === this.type && this.toggle(), + "input" === this.type && + this.app.prompt.open(this.subtitle.description, this.subtitle.text, function (t) { + t && ((e.subtitle.text = t), (e.value = t)); + }), + "link" === this.type && window.open(this.opts.link, "_blank"); + }), + (n.toggle = function () { + this.value = !this.value; + }), + (n.update = function () { + var t = this._value; + this.diagonal.visible = !t; + var e = t ? this.opts.subtitleTrue : this.opts.subtitleFalse, + i = "string" == typeof t ? t : e; + (this.subtitle.text = i), (this.title.y = i ? -35 : -20), (this.subtitle.y = this.title.y + 40); + }), + (t = Entry), + (e = [ + { + key: "value", + get: function () { + return this._value; + }, + set: function (t) { + this._value !== t && ((this._value = t), this.opts.onChange(t), this.update()); + }, + }, + ]) && SectionSettings_defineProperties(t.prototype, e), + i && SectionSettings_defineProperties(t, i), + Entry + ); + })(c.Container), + qn = (function () { + function Navigation(t) { + (this.app = t), this.uiLoaded; + } + var t = Navigation.prototype; + return ( + (t.onAppReady = function () { + this._initialised || + ((this._initialised = !0), + this.app.sections.addSub(new En(this.app)), + this.app.sections.addSub(new In(this.app)), + this.app.sections.addSub(new Xn(this.app)), + this.app.sections.addSub(new Yn(this.app)), + this.app.sections.addSub(new Hn(this.app)), + this.app.sections.addSub(new Un(this.app)), + this.app.resources.isLoaded("ui") && (this.app.size.update(), B.fastplay || this.app.sections.open(B.section))); + }), + (t.onLoadComplete = function (t) { + B.fastplay || ("ui" === t && this.app._ready && (this.app.size.update(), this.app.sections.open(B.section))); + }), + (t.playGame = function () { + var t = this; + this.app.sections.close(), + nn.a.hangout.initHighscoreSession().then(function () { + t.app.startGame(); + }); + }), + (t.goToTitle = function () { + var t = this; + this.app.config.commercials + ? (this.app.sections.close(), + this.app.suspend(), + nn.a.SDK.commercialBreak().then(function () { + return t._goToTitle(); + })) + : this._goToTitle(); + }), + (t._goToTitle = function () { + var t = this; + this.app.game.state !== pn.IDLE && this.app.game.idle(), + setTimeout(function () { + t.app.sections.close(), t.app.screenManager.gotoScreenByID("title"), t.app.sections.open("title"); + }, 30); + }), + (t.playAgain = function () { + var t = this; + this.app.sections.close(), + this.app.config.commercials + ? (this.app.suspend(), + nn.a.SDK.commercialBreak().then(function () { + return t._playAgain(); + })) + : this._playAgain(); + }), + (t._playAgain = function () { + var t = this; + this.app.sections.close(), + nn.a.hangout.initHighscoreSession().then(function () { + t.app.resume(), t.app.game.idle(), t.app.sfx.unmute(), t.app.game.runWithIntro(); + }); + }), + Navigation + ); + })(), + Wn = i(192), + Zn = (function () { + function Store(t, e) { + var i = this; + (this.id = t), (this.cache = Object.assign({}, e)), (this.data = Object.assign({}, e)), (this.storage = new Wn.a(t)), (this.onChange = new l.a()), (this._changes = {}); + var n = function (e) { + Object.defineProperty(i, e, { + get: function () { + return i.get(e); + }, + set: function (t) { + return i.set(e, t); + }, + }); + }; + for (var o in this.data) n(o); + this.load(); + } + var t = Store.prototype; + return ( + (t.get = function (t) { + return this.data[t]; + }), + (t.set = function (t, e) { + (this.data[t] = e), + this.cache[t] !== this.data[t] && + ((this._lastChange = { + field: t, + from: this.cache[t], + to: this[t], + }), + (this._changes[t] = { + from: this.cache[t], + to: this[t], + }), + this.onChange.dispatch(t, this[t], this.cache[t]), + (this.cache[t] = this.data[t])); + }), + (t.update = function () { + for (var t in this.cache) this.set(t, this.data[t]); + }), + (t.load = function () { + var t = this.storage.getObject(this.id); + t && (Object.assign(this.data, t), this.update()); + }), + (t.save = function () { + this.update(), this.storage.storeObject(this.id, this.data); + }), + (t.lastChange = function (t) { + return this._changes[t] || null; + }), + Store + ); + })(); + + function asyncGeneratorStep(t, e, i, n, o, s, a) { + try { + var r = t[s](a), + h = r.value; + } catch (t) { + return void i(t); + } + r.done ? e(h) : Promise.resolve(h).then(n, o); + } + var Kn = [ + { + name: "preload", + assets: ["preload"], + }, + { + name: "idle", + assets: ["idle", "chunks_idle"], + }, + { + name: "ui", + assets: ["ui"], + }, + { + name: "game", + assets: ["game", "chunks_game"], + }, + ], + Jn = (function () { + function Resources(t, e) { + (this.app = t), (this.config = e || Kn), (this.level = -1), (this.levelLoading = -1); + } + var t = Resources.prototype; + return ( + (t.init = function () { + (this.app.resourceManager.version = "low"), (this.onLoadStart = this.app.addSignal("onLoadStart")), (this.onLoadComplete = this.app.addSignal("onLoadComplete")); + }), + (t.load = (function () { + var s, + e = + ((s = regeneratorRuntime.mark(function _callee(e) { + var i, n, o, s; + return regeneratorRuntime.wrap( + function (t) { + for (;;) + switch ((t.prev = t.next)) { + case 0: + if ((i = this.getLevel(e)) <= this.levelLoading) return t.abrupt("return"); + t.next = 3; + break; + case 3: + if (-1 == i) throw new Error("Invalid level ref: " + e); + t.next = 5; + break; + case 5: + for (this.levelLoading = i, this.onLoadStart.dispatch(this.config[i].name, i), n = [], o = 0; o <= i; o++) (s = this.config[o]), (n = n.concat(s.assets)); + return (t.next = 11), this.app.screenManager._loadAssets(n); + case 11: + (this.level = i), (this.levelLoading = -1), this.onLoadComplete.dispatch(this.config[i].name, i); + case 14: + case "end": + return t.stop(); + } + }, + _callee, + this + ); + })), + function () { + var t = this, + o = arguments; + return new Promise(function (e, i) { + var n = s.apply(t, o); + + function _next(t) { + asyncGeneratorStep(n, e, i, _next, _throw, "next", t); + } + + function _throw(t) { + asyncGeneratorStep(n, e, i, _next, _throw, "throw", t); + } + _next(void 0); + }); + }); + return function (t) { + return e.apply(this, arguments); + }; + })()), + (t.getLevel = function (t) { + switch (typeof t) { + case "number": + return t; + case "string": + return this.levelByName(t); + default: + return this.config.indexOf(t); + } + }), + (t.levelByName = function (t) { + return this.config + .map(function (t) { + return t.name; + }) + .indexOf(t); + }), + (t.groupByName = function (t) { + var e = this.levelByName(t); + return this.config[e]; + }), + (t.isLoaded = function (t) { + return this.getLevel(t) <= this.level; + }), + Resources + ); + })(), + Qn = i(198), + $n = i(195), + to = i(196), + eo = i(132), + io = ["Good", "Fabulous", "Grumpy", "Normal", "Zealous", "Brainy", "Awesome", "Cute", "Simple", "Pretty", "Super", "Hyper", "Mega", "Wonder", "Master", "Fast", "Quick", "Giant", "Mega"], + no = ["Toaster", "Machine", "Runner", "Beast", "Genius", "Alien", "Zen", "Guy", "Player", "Llama", "Rider", "Bolt", "Monster", "Bird", "Cat"], + oo = (function () { + function NicknameGen() {} + return ( + (NicknameGen.random = function () { + return this.pick.apply(this, io) + this.pick.apply(this, no); + }), + (NicknameGen.pick = function () { + for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; + return e[Math.floor(Math.random() * e.length)]; + }), + NicknameGen + ); + })(), + so = (function () { + function Prompt(t, e) { + void 0 === e && (e = 10), (this.app = t), (this.w = 400), (this.h = 300), (this.maxChars = e), (this.useDefault = this.app.config.useDefaultPrompt); + } + var t = Prompt.prototype; + return ( + (t.build = function () { + var t = this; + if (!this.element) { + var e = document.createElement("div"); + (e.style.position = "absolute"), + (e.style.left = 0), + (e.style.top = 0), + (e.style.right = 0), + (e.style.bottom = 0), + (e.style.width = "100%"), + (e.style.height = "100%"), + (e.style.backgroundColor = "rgba(0, 0, 0, 0.5)"), + (e.style.zIndex = 99999); + var i = document.createElement("div"); + (i.style.width = this.w + "px"), + (i.style.height = this.h + "px"), + (i.style.position = "absolute"), + (i.style.marginLeft = "50%"), + (i.style.left = -this.w / 2 + "px"), + (i.style.top = "20%"), + (i.style.backgroundImage = 'url("assets/placeholder/box_base.png")'), + (i.style.backgroundPosition = "center"), + (i.style.backgroundRepeat = "no-repeat"), + (i.style.backgroundSize = "contain"), + (i.style.fontFamily = "Lilita One"), + (i.style.textAlign = "center"), + (i.style.color = "#004a80"), + (i.style.display = "block"), + (i.style.overflow = "hidden"), + e.appendChild(i); + var n = document.createElement("div"); + (n.style.fontSize = "2em"), (n.style.width = "100%"), (n.style.marginTop = "20%"), (n.style.textAlign = "center"), i.appendChild(n); + var o = document.createElement("input"); + (o.style.fontSize = "1.5em"), + (o.type = "text"), + (o.maxLength = this.maxChars), + (o.style.width = "80%"), + (o.style.height = "30pt"), + (o.style.textAlign = "center"), + (o.style.fontFamily = "Lilita One"), + (o.style.color = "#3a8bbf"), + (o.style.margin = "5px"), + (o.style.backgroundColor = "#FFFFFF"), + (o.style.border = 0), + i.appendChild(o); + var s = document.createElement("button"); + (s.innerText = this.app.strings.get("cancel")), + (s.onclick = function () { + return t.onCancel(); + }), + (s.style.fontSize = "1em"), + (s.style.width = "120px"), + (s.style.height = "40px"), + (s.style.textAlign = "center"), + (s.style.fontFamily = "Lilita One"), + (s.style.color = "#FFFFFF"), + (s.style.margin = "10px"), + (s.style.backgroundColor = "#c93d48"), + (s.style.border = 0), + (s.style.cursor = "pointer"), + (s.style.borderRadius = "6px"), + i.appendChild(s); + var a = document.createElement("button"); + (a.innerText = this.app.strings.get("ok")), + (a.onclick = function () { + return t.onOk(); + }), + (a.style.fontSize = "1em"), + (a.style.width = "120px"), + (a.style.height = "40px"), + (a.style.textAlign = "center"), + (a.style.fontFamily = "Lilita One"), + (a.style.color = "#FFFFFF"), + (a.style.backgroundColor = "#3689be"), + (a.style.margin = "10px"), + (a.style.border = 0), + (a.style.cursor = "pointer"), + (a.style.borderRadius = "6px"), + i.appendChild(a), + (this.title = n), + (this.input = o), + (this.btnOk = a), + (this.btnCancel = s), + (this.element = e); + } + }), + (t.open = function (t, e, i) { + if (!this.opened) { + if (((this.opened = !0), (this.callback = i), this.useDefault)) { + var n = window.prompt(t, e); + return this.callback && this.callback(n), void (this.opened = !1); + } + this._onKeyDownBind || (this._onKeyDownBind = this.onKeyUp.bind(this)), + document.addEventListener("keyup", this._onKeyDownBind), + this.app.suspend(), + this.build(), + (this.title.innerText = t), + (this.input.value = e), + document.body.appendChild(this.element), + this.select(); + } + }), + (t.close = function () { + this.opened && ((this.opened = !1), document.removeEventListener("keyup", this._onKeyDownBind), this.input.blur(), document.body.removeChild(this.element), this.app.resume()); + }), + (t.onKeyUp = function (t) { + "Enter" != t.code || t.repeat || this.onOk(); + }), + (t.onOk = function () { + this.close(), this.callback && this.callback(this.input.value); + }), + (t.onCancel = function () { + this.close(), this.callback && this.callback(); + }), + (t.select = function () { + this.input.focus(), this.input.select && this.input.select(), this.input.setSelectionRange && this.input.setSelectionRange(0, this.input.value.length); + }), + Prompt + ); + })(); + + function Application_asyncGeneratorStep(t, e, i, n, o, s, a) { + try { + var r = t[s](a), + h = r.value; + } catch (t) { + return void i(t); + } + r.done ? e(h) : Promise.resolve(h).then(n, o); + } + + function Application_asyncToGenerator(s) { + return function () { + var t = this, + o = arguments; + return new Promise(function (e, i) { + var n = s.apply(t, o); + + function _next(t) { + Application_asyncGeneratorStep(n, e, i, _next, _throw, "next", t); + } + + function _throw(t) { + Application_asyncGeneratorStep(n, e, i, _next, _throw, "throw", t); + } + _next(void 0); + }); + }; + } + var ao = "assets"; + + function pathJoin() { + for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; + return e.join("/"); + } + + function prepareManifest(t, e) { + !(function (t, e) { + for (var i in t) + for (var n in t[i]) + for (var o in t[i][n]) + for (var s in t[i][n][o]) { + var a = t[i][n][o][s]; + if ("string" == typeof a) { + var r = pathJoin(e, a); + t[i][n][o][s] = r; + } + } + })(t, ao); + var i = e && e.config ? e.config.lang : "en", + n = { + default: ao + "/data/strings_en.json", + }, + o = { + default: ao + "/data/strings_" + i + ".json", + }; + return ( + (t.idle.json["data/strings_en"] = n), + (t.idle.json["data/strings_" + i] = o), + e.bundle && + (!(function (t) { + var e = t.meta.path; + for (var i in t) + if ("meta" !== i) + for (var n in t[i]) + for (var o in t[i][n]) + for (var s in t[i][n][o]) { + var a = pathJoin(e, t[i][n][o][s]).replace("assets/", ""); + t[i][n][o][s] = a; + } + })(e.bundle), + (function merge(t, e) { + for (var i in e) { + var n = typeof t[i], + o = typeof e[i]; + "object" === n && "object" === o ? merge(t[i], e[i]) : (t[i] = "object" === o ? JSON.parse(JSON.stringify(e[i])) : e[i]); + } + })(t, e.bundle)), + t + ); + } + B.vertp && (c.settings.PRECISION_VERTEX = B.vertp), B.fragp && (c.settings.PRECISION_VERTEX = B.fragp); + var ro = (function (s) { + var t, e; + + function Application(t) { + var e; + void 0 === t && (t = {}), + B.override({ + env: t.config.env, + mobile: r.a.instance.isMobile || r.a.instance.isTouch, + }), + B.override(t.config); + var i, + n = { + manifest: prepareManifest(eo, t), + orientationMode: a.a.orientationModes.BOTH, + screenManagerOptions: { + defaultTransition: new h.a(), + }, + middlewares: [((i = B.workers), i ? Object($n.a)(1 === i) : Qn.a)], + version: B.monitor || B.debug, + fps: B.monitor || B.debug, + upload: !1, + antialias: !1, + transparent: !1, + loadingScreenClass: xn, + }; + ((e = s.call(this, n) || this).resourceManager.middlewaresPre = [to.a]), (e.config = Object.assign({}, t.config)), (e.bundle = Object.assign({}, t.bundle)); + var o = e.bundle.meta.color; + return ( + (e.color = "string" == typeof o ? c.utils.string2hex(o) : o), + (e.resourceManager.version = "low"), + (e.resourceManager.getLoader = function () { + var e = new c.Loader(); + return ( + this.middlewaresPre.forEach(function (t) { + return e.pre(t()); + }), + this.middlewares.forEach(function (t) { + return e.use(t()); + }), + e + ); + }), + (e.crashed = !1), + window.addEventListener("error", function (t) { + e.crash(t.error.message); + }), + c.Ticker.shared.stop(), + c.Ticker._system.stop(), + d.a.instance.remove( + e.update, + (function (t) { + if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return t; + })(e) + ), + B.ticker ? e.runWithTicker() : (d.a.instance.stop(), e.runWithAnimFrame()), + e + ); + } + (e = s), ((t = Application).prototype = Object.create(e.prototype)), ((t.prototype.constructor = t).__proto__ = e); + var i = Application.prototype; + return ( + (i.crash = function () { + (this.crashed = !0), d.a.instance.stop(); + for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; + throw new Error("CRASH! " + e.join(" ")); + }), + (i.getFbxLoader = function () {}), + (i.init = function (t) { + (this.container = t), + this.addSignal("onAppReady", this.onReady), + this.addSystem( + "settings", + new Zn("settings", { + sound: !0, + }) + ), + this.addSystem( + "user", + new Zn("user", { + score: 0, + coins: 0, + keys: 0, + sprays: 0, + hoverboards: 0, + character: "jake", + outfit: "default", + board: "default", + name: "", + nameSet: !1, + tutorial: !0, + }) + ), + this.user.name || ((this.user.name = oo.random()), this.user.save()), + (this.sfx = new gn(0 === B.volume)), + this.sfx.setup(eo.audio.audio, ao), + this.sfx.volume(this.settings.sound ? B.volume : 0), + this.addSystem("resources", new Jn(this)), + this.addSystem("nav", new qn(this)), + this.addSystem("event", new Sn(this)), + this.addSystem("message", new zn(this)), + this.addSystem("size", new Tn(this)), + this.addSystem("strings", new Pn(this)), + this.addSystem("prompt", new so(this, this.config.maxNicknameChars)), + B.debug && this.addSystem("debug", new Mn(this)), + nn.a.hangout.setDebug(this.config.pokiSdkDebug), + nn.a.hangout.getLobbyId(), + this.addScreen("loading", new xn(this, this.color)), + this.preload(); + }), + (i.show = function () { + this.container.appendChild(this.view), (this.view.style.position = "absolute"), (this.view.style.top = 0), (this.view.style.left = 0), this.size.update(); + }), + (i.preload = (function () { + var t = Application_asyncToGenerator( + regeneratorRuntime.mark(function _callee() { + return regeneratorRuntime.wrap( + function (t) { + for (;;) + switch ((t.prev = t.next)) { + case 0: + return nn.a.sendCustomMessage("game", "loaderStart", {}), (t.next = 3), this.resources.load("preload"); + case 3: + this.loadingScreen.build(), this.show(), this.load(); + case 6: + case "end": + return t.stop(); + } + }, + _callee, + this + ); + }) + ); + return function () { + return t.apply(this, arguments); + }; + })()), + (i.load = (function () { + var t = Application_asyncToGenerator( + regeneratorRuntime.mark(function _callee2() { + return regeneratorRuntime.wrap( + function (t) { + for (;;) + switch ((t.prev = t.next)) { + case 0: + if (B.fastplay) return (this.shouldStartGame = !0), nn.a.sendCustomMessage("game", "loaderEnd", {}), nn.a.sendCustomMessage("mainMenu", "secondLoaderStart", {}), (t.next = 6), this.resources.load("game"); + t.next = 11; + break; + case 6: + this.ready(), (this.gameLoaded = !0), nn.a.SDK.gameLoadingFinished(), (t.next = 24); + break; + case 11: + if (B.loadAll) return (t.next = 14), this.resources.load("game"); + t.next = 16; + break; + case 14: + t.next = 18; + break; + case 16: + return (t.next = 18), this.resources.load("idle"); + case 18: + return nn.a.sendCustomMessage("game", "loaderEnd", {}), (this.bgLoading = !0), (t.next = 22), this.resources.load("ui"); + case 22: + (this.bgLoading = !1), B.autoload && this.loadGame(); + case 24: + case "end": + return t.stop(); + } + }, + _callee2, + this + ); + }) + ); + return function () { + return t.apply(this, arguments); + }; + })()), + (i.loadGame = (function () { + var t = Application_asyncToGenerator( + regeneratorRuntime.mark(function _callee3() { + return regeneratorRuntime.wrap( + function (t) { + for (;;) + switch ((t.prev = t.next)) { + case 0: + if (this.bgLoading) return t.abrupt("return"); + t.next = 2; + break; + case 2: + return (this.bgLoading = !0), nn.a.sendCustomMessage("mainMenu", "secondLoaderStart", {}), (t.next = 6), this.resources.load("game"); + case 6: + (this.bgLoading = !1), (this.gameLoaded = !0); + case 8: + case "end": + return t.stop(); + } + }, + _callee3, + this + ); + }) + ); + return function () { + return t.apply(this, arguments); + }; + })()), + (i.onLoadStart = function (t, e) {}), + (i.onLoadComplete = function (t, e) { + switch (t) { + case "preload": + break; + case "idle": + B.fastplay || this.ready(); + break; + case "ui": + B.fastplay || (nn.a.sendCustomMessage("mainMenu", "interactive", {}), nn.a.SDK.gameLoadingFinished()); + break; + case "game": + nn.a.sendCustomMessage("mainMenu", "secondLoaderEnd", {}), this.ready(), (this.gameLoaded = !0), this.shouldStartGame && this.startGame(); + } + this.size.update(); + }), + (i.ready = function () { + var e = this; + this._readyDispatched || + this._ready || + ((this._readyDispatched = !0), + this.strings.onAppReady(), + this.createGame(), + (this.sections = new vn("root")), + (this.sections.container = this.stage), + this.addScreen("title", new _n(this)), + this.addScreen("game", new kn(this)), + setTimeout(function () { + e._ready = !0; + var t = B.fastplay ? "game" : "title"; + e.screenManager.gotoScreenByID(t), e.onReady.dispatch(), (e.loadingScreen.firstLoadPassed = !0); + }, 100)); + }), + (i.createGame = function () { + this.game || ((this.game = new pn(this, B, this.sfx)), this.game.idle(), this.stage.addChildAt(this.game.stage, 0)); + }), + (i.startGame = function () { + if ((this.sections && this.sections.close(), !this.gameLoaded)) return this.loadGame(), (this.shouldStartGame = !0), void (this.loadingScreen && this.loadingScreen.updateView(!0)); + this.loadingScreen.updateView(!1), + this.sfx.play("hero_foot_l", { + volume: 0.01, + }), + this.sfx.volume(this.settings.sound ? B.volume : 0), + this.screenManager.gotoScreenByID("game"); + }), + (i.onFocus = function () { + this.suspended || (this.game && this.game.onFocus()); + }), + (i.onBlur = function () { + this.suspended || (this.game && this.game.onBlur()); + }), + (i.onMessage = function (t) { + "startGame" === t.data && this.startGame(); + }), + (i.onResize = function () { + (this.view.style.width = this.size.w + "px"), + (this.view.style.height = this.size.h + "px"), + (this.view.style.left = "0px"), + (this.view.style.top = "0px"), + this.renderer.resize(this.size.sw, this.size.sh), + this.screenManager.resize(this.size.w, this.size.h), + (this.screenManager.container.scale.x = this.size.scale), + (this.screenManager.container.scale.y = this.size.scale), + this.sections && this.sections.resize(this.size.sw, this.size.sh, 2), + this.game && this.game.resize(this.size.sw, this.size.sh, 2), + this.blocker && this.blocker.visible && ((this.blocker.width = this.size.sw), (this.blocker.height = this.size.sh)); + }), + (i.suspend = function () { + this.suspended || + ((this.suspended = !0), + this.sfx.mute(), + this.blocker || ((this.blocker = new c.Graphics()), this.blocker.beginFill(this.color, 0.5), this.blocker.drawRect(0, 0, 16, 16), this.blocker.endFill()), + (this.blocker.visible = !0), + this.stage.addChild(this.blocker), + (this.blocker.width = this.size.sw), + (this.blocker.height = this.size.sh)); + }), + (i.resume = function () { + this.suspended && ((this.suspended = !1), this.view.focus(), this.sfx.unmute(), this.blocker && (this.blocker.visible = !1), this.blocker && this.blocker.parent && this.stage.addChild(this.blocker)); + }), + (i.runWithTicker = function () { + d.a.instance.add(this.updateTicker, this, 1); + }), + (i.runWithAnimFrame = function () { + (this.updateAnimFrameBind = this.updateAnimFrame.bind(this)), requestAnimationFrame(this.updateAnimFrameBind); + }), + (i.updateTicker = function (t) { + this.game && this.game.updateTicker(t), this.renderer.render(this.stage); + }), + (i.updateAnimFrame = function (t) { + requestAnimationFrame(this.updateAnimFrameBind), this.game && this.game.updateAnimFrame(t), this.renderer.render(this.stage); + }), + (i.addScreen = function (t, e) { + (this[t + "Screen"] = e), "loading" === t ? (this.screenManager.loadingScreen = e) : this.screenManager.addScreen(e, t); + }), + (i.addSystem = function (t, e) { + if ((this.system || (this.system = {}), this.system[t])) throw new Error("App already have system: " + t); + if (this[t]) throw new Error("App already have a member with the same name as the system: " + t); + for (var i in ((this.system[t] = e), (this[t] = e), this.signal)) e[i] && this.signal[i].add(e[i], e); + e.init && e.init(this); + }), + (i.addSignal = function (t, e) { + if ((this.signal || (this.signal = {}), this.signal[t])) throw new Error("App already have signal: " + t); + for (var i in (e || (e = new l.a()), (this.signal[t] = e), this.system)) { + var n = this.system[i]; + n[t] && "function" == typeof this[t] && e.add(n[t], n); + } + return this[t] && "function" == typeof this[t] && e.add(this[t], this), e; + }), + Application + ); + })(a.a), + ho = i(197), + lo = { + gid: "ssurfers", + gameDir: "", + gameContainerId: "og-game-holder", + isFullScreen: window.self === window.top, + exitGameUrl: "", + environment: "test", + embedVars: { + statsCounterName: "testCounterName", + statsAppName: "TestAppName", + }, + }; + !(function () { + if ((window.VConsole && new window.VConsole(), new ho.a().supported)) { + window.og || (window.og = lo), + window.ASSET_URL || (window.ASSET_URL = ""), + window.DEVICE_SCALE || (window.DEVICE_SCALE = window.devicePixelRatio || 1), + window.console || (window.console = {}), + window.console.log || + (window.console.log = function (t) { + return t; + }); + var t = document.getElementById(lo.gameContainerId); + new ro(window.sharedAppData).init(t); + } + })(); + }, + }); + \ No newline at end of file diff --git a/subway-surfers-ny/js/vendor.js b/subway-surfers-ny/js/vendor.js new file mode 100644 index 00000000..2564a86d --- /dev/null +++ b/subway-surfers-ny/js/vendor.js @@ -0,0 +1,28979 @@ +(this.webpackJsonp = this.webpackJsonp || []).push([ + [2], + [function(t, e, r) { + "use strict"; + r.r(e); + var n = {}; + r.r(n), r.d(n, "isMobile", function() { + return h.a + }), r.d(n, "EventEmitter", function() { + return l.a + }), r.d(n, "earcut", function() { + return d.a + }), r.d(n, "url", function() { + return m.a + }), r.d(n, "BaseTextureCache", function() { + return k + }), r.d(n, "CanvasRenderTarget", function() { + return L + }), r.d(n, "DATA_URI", function() { + return U + }), r.d(n, "ProgramCache", function() { + return D + }), r.d(n, "TextureCache", function() { + return F + }), r.d(n, "clearTextureCache", function() { + return clearTextureCache + }), r.d(n, "correctBlendMode", function() { + return correctBlendMode + }), r.d(n, "createIndicesForQuads", function() { + return createIndicesForQuads + }), r.d(n, "decomposeDataUri", function() { + return decomposeDataUri + }), r.d(n, "deprecation", function() { + return deprecation + }), r.d(n, "destroyTextureCache", function() { + return destroyTextureCache + }), r.d(n, "determineCrossOrigin", function() { + return determineCrossOrigin + }), r.d(n, "getResolutionOfUrl", function() { + return getResolutionOfUrl + }), r.d(n, "hex2rgb", function() { + return hex2rgb + }), r.d(n, "hex2string", function() { + return hex2string + }), r.d(n, "isPow2", function() { + return isPow2 + }), r.d(n, "isWebGLSupported", function() { + return isWebGLSupported + }), r.d(n, "log2", function() { + return log2 + }), r.d(n, "nextPow2", function() { + return nextPow2 + }), r.d(n, "premultiplyBlendMode", function() { + return C + }), r.d(n, "premultiplyRgba", function() { + return premultiplyRgba + }), r.d(n, "premultiplyTint", function() { + return premultiplyTint + }), r.d(n, "premultiplyTintToRgba", function() { + return premultiplyTintToRgba + }), r.d(n, "removeItems", function() { + return removeItems + }), r.d(n, "rgb2hex", function() { + return rgb2hex + }), r.d(n, "sayHello", function() { + return sayHello + }), r.d(n, "sign", function() { + return sign + }), r.d(n, "skipHello", function() { + return skipHello + }), r.d(n, "string2hex", function() { + return string2hex + }), r.d(n, "trimCanvas", function() { + return trimCanvas + }), r.d(n, "uid", function() { + return uid + }); + var i = {}; + r.r(i), r.d(i, "AccessibilityManager", function() { + return vt + }), r.d(i, "accessibleTarget", function() { + return mt + }); + var o = {}; + r.r(o), r.d(o, "Extract", function() { + return fr + }); + var a = {}; + r.r(a), r.d(a, "InteractionData", function() { + return dr + }), r.d(a, "InteractionEvent", function() { + return mr + }), r.d(a, "InteractionManager", function() { + return br + }), r.d(a, "InteractionTrackingData", function() { + return vr + }), r.d(a, "interactiveTarget", function() { + return yr + }); + var s = {}; + r.r(s), r.d(s, "BasePrepare", function() { + return $r + }), r.d(s, "CountLimiter", function() { + return Jr + }), r.d(s, "Prepare", function() { + return tn + }), r.d(s, "TimeLimiter", function() { + return en + }); + r(401); + var u = r(46), + h = r.n(u); + var M = { + MIPMAP_TEXTURES: 1, + RESOLUTION: 1, + FILTER_RESOLUTION: 1, + SPRITE_MAX_TEXTURES: function(t) { + var e = !0; + if (h.a.tablet || h.a.phone) { + if (e = !1, h.a.apple.device) { + var r = navigator.userAgent.match(/OS (\d+)_(\d+)?/); + r && 11 <= parseInt(r[1], 10) && (e = !0) + } + if (h.a.android.device) { + var n = navigator.userAgent.match(/Android\s([0-9.]*)/); + n && 7 <= parseInt(n[1], 10) && (e = !0) + } + } + return e ? t : 4 + }(32), + SPRITE_BATCH_SIZE: 4096, + RENDER_OPTIONS: { + view: null, + antialias: !1, + forceFXAA: !1, + autoResize: !1, + transparent: !1, + backgroundColor: 0, + clearBeforeRender: !0, + preserveDrawingBuffer: !1, + width: 800, + height: 600, + legacy: !1 + }, + GC_MODE: 0, + GC_MAX_IDLE: 3600, + GC_MAX_CHECK_COUNT: 600, + WRAP_MODE: 33071, + SCALE_MODE: 1, + PRECISION_VERTEX: "highp", + PRECISION_FRAGMENT: h.a.apple.device ? "highp" : "mediump", + CAN_UPLOAD_SAME_BUFFER: !h.a.apple.device, + CREATE_IMAGE_BITMAP: !0, + ROUND_PIXELS: !1 + }, + c = r(170), + l = r.n(c), + f = r(171), + d = r.n(f), + p = r(89), + m = r.n(p), + v = { + WEBGL_LEGACY: 0, + WEBGL: 1, + WEBGL2: 2 + }, + g = { + UNKNOWN: 0, + WEBGL: 1, + CANVAS: 2 + }, + y = { + NORMAL: 0, + ADD: 1, + MULTIPLY: 2, + SCREEN: 3, + OVERLAY: 4, + DARKEN: 5, + LIGHTEN: 6, + COLOR_DODGE: 7, + COLOR_BURN: 8, + HARD_LIGHT: 9, + SOFT_LIGHT: 10, + DIFFERENCE: 11, + EXCLUSION: 12, + HUE: 13, + SATURATION: 14, + COLOR: 15, + LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, + NONE: 20, + SRC_OVER: 0, + SRC_IN: 21, + SRC_OUT: 22, + SRC_ATOP: 23, + DST_OVER: 24, + DST_IN: 25, + DST_OUT: 26, + DST_ATOP: 27, + ERASE: 26, + SUBTRACT: 28 + }, + x = { + POINTS: 0, + LINES: 1, + LINE_LOOP: 2, + LINE_STRIP: 3, + TRIANGLES: 4, + TRIANGLE_STRIP: 5, + TRIANGLE_FAN: 6 + }, + _ = { + RGBA: 6408, + RGB: 6407, + ALPHA: 6406, + LUMINANCE: 6409, + LUMINANCE_ALPHA: 6410, + DEPTH_COMPONENT: 6402, + DEPTH_STENCIL: 34041 + }, + b = { + TEXTURE_2D: 3553, + TEXTURE_CUBE_MAP: 34067, + TEXTURE_2D_ARRAY: 35866, + TEXTURE_CUBE_MAP_POSITIVE_X: 34069, + TEXTURE_CUBE_MAP_NEGATIVE_X: 34070, + TEXTURE_CUBE_MAP_POSITIVE_Y: 34071, + TEXTURE_CUBE_MAP_NEGATIVE_Y: 34072, + TEXTURE_CUBE_MAP_POSITIVE_Z: 34073, + TEXTURE_CUBE_MAP_NEGATIVE_Z: 34074 + }, + w = { + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + UNSIGNED_SHORT_5_6_5: 33635, + UNSIGNED_SHORT_4_4_4_4: 32819, + UNSIGNED_SHORT_5_5_5_1: 32820, + FLOAT: 5126, + HALF_FLOAT: 36193 + }, + T = { + LINEAR: 1, + NEAREST: 0 + }, + S = { + CLAMP: 33071, + REPEAT: 10497, + MIRRORED_REPEAT: 33648 + }, + E = { + OFF: 0, + POW2: 1, + ON: 2 + }, + A = { + AUTO: 0, + MANUAL: 1 + }, + P = { + LOW: "lowp", + MEDIUM: "mediump", + HIGH: "highp" + }, + I = !(M.RETINA_PREFIX = /@([0-9\.]+)x/), + O = "5.0.0-rc.3"; + + function skipHello() { + I = !0 + } + + function sayHello(t) { + if (!I) { + if (-1 < navigator.userAgent.toLowerCase().indexOf("chrome")) { + var e = ["\n %c %c %c PixiJS " + O + " - ✰ " + t + " ✰ %c %c http://www.pixijs.com/ %c %c ♥%c♥%c♥ \n\n", "background: #ff66a5; padding:5px 0;", "background: #ff66a5; padding:5px 0;", "color: #ff66a5; background: #030307; padding:5px 0;", "background: #ff66a5; padding:5px 0;", "background: #ffc3dc; padding:5px 0;", "background: #ff66a5; padding:5px 0;", "color: #ff2424; background: #fff; padding:5px 0;", "color: #ff2424; background: #fff; padding:5px 0;", "color: #ff2424; background: #fff; padding:5px 0;"]; + window.console.log.apply(console, e) + } else window.console && window.console.log("PixiJS " + O + " - " + t + " - http://www.pixijs.com/"); + I = !0 + } + } + + function isWebGLSupported() { + var t = { + stencil: !0, + failIfMajorPerformanceCaveat: !0 + }; + try { + if (!window.WebGLRenderingContext) return !1; + var e = document.createElement("canvas"), + r = e.getContext("webgl", t) || e.getContext("experimental-webgl", t), + n = !(!r || !r.getContextAttributes().stencil); + if (r) { + var i = r.getExtension("WEBGL_lose_context"); + i && i.loseContext() + } + return r = null, n + } catch (t) { + return !1 + } + } + + function hex2rgb(t, e) { + return (e = e || [])[0] = (t >> 16 & 255) / 255, e[1] = (t >> 8 & 255) / 255, e[2] = (255 & t) / 255, e + } + + function hex2string(t) { + return t = t.toString(16), "#" + (t = "000000".substr(0, 6 - t.length) + t) + } + + function string2hex(t) { + return "string" == typeof t && "#" === t[0] && (t = t.substr(1)), parseInt(t, 16) + } + + function rgb2hex(t) { + return (255 * t[0] << 16) + (255 * t[1] << 8) + (255 * t[2] | 0) + } + var C = function() { + for (var t = [], e = [], r = 0; r < 32; r++) e[t[r] = r] = r; + t[y.NORMAL_NPM] = y.NORMAL, t[y.ADD_NPM] = y.ADD, t[y.SCREEN_NPM] = y.SCREEN, e[y.NORMAL] = y.NORMAL_NPM, e[y.ADD] = y.ADD_NPM, e[y.SCREEN] = y.SCREEN_NPM; + var n = []; + return n.push(e), n.push(t), n + }(); + + function correctBlendMode(t, e) { + return C[e ? 1 : 0][t] + } + + function premultiplyRgba(t, e, r, n) { + return (r = r || new Float32Array(4))[2] = n || void 0 === n ? (r[0] = t[0] * e, r[1] = t[1] * e, t[2] * e) : (r[0] = t[0], r[1] = t[1], t[2]), r[3] = e, r + } + + function premultiplyTint(t, e) { + if (1 === e) return (255 * e << 24) + t; + if (0 === e) return 0; + var r = t >> 16 & 255, + n = t >> 8 & 255, + i = 255 & t; + return (255 * e << 24) + ((r = r * e + .5 | 0) << 16) + ((n = n * e + .5 | 0) << 8) + (i = i * e + .5 | 0) + } + + function premultiplyTintToRgba(t, e, r, n) { + return (r = r || new Float32Array(4))[0] = (t >> 16 & 255) / 255, r[1] = (t >> 8 & 255) / 255, r[2] = (255 & t) / 255, (n || void 0 === n) && (r[0] *= e, r[1] *= e, r[2] *= e), r[3] = e, r + } + + function createIndicesForQuads(t) { + for (var e = 6 * t, r = new Uint16Array(e), n = 0, i = 0; n < e; n += 6, i += 4) r[n + 0] = i + 0, r[n + 1] = i + 1, r[n + 2] = i + 2, r[n + 3] = i + 0, r[n + 4] = i + 2, r[n + 5] = i + 3; + return r + } + + function removeItems(t, e, r) { + var n, i = t.length; + if (!(i <= e || 0 === r)) { + var o = i - (r = i < e + r ? i - e : r); + for (n = e; n < o; ++n) t[n] = t[n + r]; + t.length = o + } + } + var R = 0; + + function uid() { + return ++R + } + + function sign(t) { + return 0 === t ? 0 : t < 0 ? -1 : 1 + } + + function nextPow2(t) { + return t += 0 === t, --t, t |= t >>> 1, t |= t >>> 2, t |= t >>> 4, t |= t >>> 8, (t |= t >>> 16) + 1 + } + + function isPow2(t) { + return !(t & t - 1 || !t) + } + + function log2(t) { + var e = (65535 < t) << 4, + r = (255 < (t >>>= e)) << 3; + return e |= r, e |= r = (15 < (t >>>= r)) << 2, (e |= r = (3 < (t >>>= r)) << 1) | (t >>>= r) >> 1 + } + var D = {}, + F = Object.create(null), + k = Object.create(null); + + function destroyTextureCache() { + var t; + for (t in F) F[t].destroy(); + for (t in k) k[t].destroy() + } + + function clearTextureCache() { + var t; + for (t in F) delete F[t]; + for (t in k) delete k[t] + } + + function trimCanvas(t) { + var e, r, n, i = t.width, + o = t.height, + a = t.getContext("2d"), + s = a.getImageData(0, 0, i, o).data, + u = s.length, + h = { + top: null, + left: null, + right: null, + bottom: null + }, + c = null; + for (e = 0; e < u; e += 4) 0 !== s[e + 3] && (r = e / 4 % i, n = ~~(e / 4 / i), null === h.top && (h.top = n), null === h.left ? h.left = r : r < h.left && (h.left = r), null === h.right ? h.right = r + 1 : h.right < r && (h.right = r + 1), null === h.bottom ? h.bottom = n : h.bottom < n && (h.bottom = n)); + return null !== h.top && (i = h.right - h.left, o = h.bottom - h.top + 1, c = a.getImageData(h.left, h.top, i, o)), { + height: o, + width: i, + data: c + } + } + var L = function(t, e, r) { + this.canvas = document.createElement("canvas"), this.context = this.canvas.getContext("2d"), this.resolution = r || M.RESOLUTION, this.resize(t, e) + }, + N = { + width: { + configurable: !0 + }, + height: { + configurable: !0 + } + }; + L.prototype.clear = function() { + this.context.setTransform(1, 0, 0, 1, 0, 0), this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, L.prototype.resize = function(t, e) { + this.canvas.width = t * this.resolution, this.canvas.height = e * this.resolution + }, L.prototype.destroy = function() { + this.context = null, this.canvas = null + }, N.width.get = function() { + return this.canvas.width + }, N.width.set = function(t) { + this.canvas.width = t + }, N.height.get = function() { + return this.canvas.height + }, N.height.set = function(t) { + this.canvas.height = t + }, Object.defineProperties(L.prototype, N); + var B, U = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i; + + function decomposeDataUri(t) { + var e = U.exec(t); + if (e) return { + mediaType: e[1] ? e[1].toLowerCase() : void 0, + subType: e[2] ? e[2].toLowerCase() : void 0, + charset: e[3] ? e[3].toLowerCase() : void 0, + encoding: e[4] ? e[4].toLowerCase() : void 0, + data: e[5] + } + } + + function determineCrossOrigin(t, e) { + if (void 0 === e && (e = window.location), 0 === t.indexOf("data:")) return ""; + e = e || window.location, B || (B = document.createElement("a")), B.href = t; + var r = !(t = m.a.parse(B.href)).port && "" === e.port || t.port === e.port; + return t.hostname === e.hostname && r && t.protocol === e.protocol ? "" : "anonymous" + } + + function getResolutionOfUrl(t, e) { + var r = M.RETINA_PREFIX.exec(t); + return r ? parseFloat(r[1]) : void 0 !== e ? e : 1 + } + var G = {}; + + function deprecation(t, e, r) { + if (void 0 === r && (r = 3), !G[e]) { + var n = (new Error).stack; + void 0 === n || (n = n.split("\n").splice(r).join("\n"), console.groupCollapsed), G[e] = !0 + } + } + var q = function(t, e) { + void 0 === t && (t = 0), void 0 === e && (e = 0), this.x = t, this.y = e + }; + q.prototype.clone = function() { + return new q(this.x, this.y) + }, q.prototype.copyFrom = function(t) { + return this.set(t.x, t.y), this + }, q.prototype.copyTo = function(t) { + return t.set(this.x, this.y), t + }, q.prototype.equals = function(t) { + return t.x === this.x && t.y === this.y + }, q.prototype.set = function(t, e) { + this.x = t || 0, this.y = e || (0 !== e ? this.x : 0) + }; + var j = function(t, e, r, n) { + void 0 === r && (r = 0), void 0 === n && (n = 0), this._x = r, this._y = n, this.cb = t, this.scope = e + }, + z = { + x: { + configurable: !0 + }, + y: { + configurable: !0 + } + }; + j.prototype.clone = function(t, e) { + void 0 === t && (t = null), void 0 === e && (e = null); + var r = t || this.cb, + n = e || this.scope; + return new j(r, n, this._x, this._y) + }, j.prototype.set = function(t, e) { + var r = t || 0, + n = e || (0 !== e ? r : 0); + this._x === r && this._y === n || (this._x = r, this._y = n, this.cb.call(this.scope)) + }, j.prototype.copyFrom = function(t) { + return this._x === t.x && this._y === t.y || (this._x = t.x, this._y = t.y, this.cb.call(this.scope)), this + }, j.prototype.copyTo = function(t) { + return t.set(this._x, this._y), t + }, j.prototype.equals = function(t) { + return t.x === this._x && t.y === this._y + }, z.x.get = function() { + return this._x + }, z.x.set = function(t) { + this._x !== t && (this._x = t, this.cb.call(this.scope)) + }, z.y.get = function() { + return this._y + }, z.y.set = function(t) { + this._y !== t && (this._y = t, this.cb.call(this.scope)) + }, Object.defineProperties(j.prototype, z); + var X = 2 * Math.PI, + V = 180 / Math.PI, + H = Math.PI / 180, + W = { + POLY: 0, + RECT: 1, + CIRC: 2, + ELIP: 3, + RREC: 4 + }, + Y = function(t, e, r, n, i, o) { + void 0 === t && (t = 1), void 0 === e && (e = 0), void 0 === r && (r = 0), void 0 === n && (n = 1), void 0 === i && (i = 0), void 0 === o && (o = 0), this.a = t, this.b = e, this.c = r, this.d = n, this.tx = i, this.ty = o, this.array = null + }, + K = { + IDENTITY: { + configurable: !0 + }, + TEMP_MATRIX: { + configurable: !0 + } + }; + Y.prototype.fromArray = function(t) { + this.a = t[0], this.b = t[1], this.c = t[3], this.d = t[4], this.tx = t[2], this.ty = t[5] + }, Y.prototype.set = function(t, e, r, n, i, o) { + return this.a = t, this.b = e, this.c = r, this.d = n, this.tx = i, this.ty = o, this + }, Y.prototype.toArray = function(t, e) { + this.array || (this.array = new Float32Array(9)); + var r = e || this.array; + return r[8] = (r[7] = t ? (r[0] = this.a, r[1] = this.b, r[2] = 0, r[3] = this.c, r[4] = this.d, r[5] = 0, r[6] = this.tx, this.ty) : (r[0] = this.a, r[1] = this.c, r[2] = this.tx, r[3] = this.b, r[4] = this.d, r[5] = this.ty, r[6] = 0), 1), r + }, Y.prototype.apply = function(t, e) { + e = e || new q; + var r = t.x, + n = t.y; + return e.x = this.a * r + this.c * n + this.tx, e.y = this.b * r + this.d * n + this.ty, e + }, Y.prototype.applyInverse = function(t, e) { + e = e || new q; + var r = 1 / (this.a * this.d + this.c * -this.b), + n = t.x, + i = t.y; + return e.x = this.d * r * n + -this.c * r * i + (this.ty * this.c - this.tx * this.d) * r, e.y = this.a * r * i + -this.b * r * n + (-this.ty * this.a + this.tx * this.b) * r, e + }, Y.prototype.translate = function(t, e) { + return this.tx += t, this.ty += e, this + }, Y.prototype.scale = function(t, e) { + return this.a *= t, this.d *= e, this.c *= t, this.b *= e, this.tx *= t, this.ty *= e, this + }, Y.prototype.rotate = function(t) { + var e = Math.cos(t), + r = Math.sin(t), + n = this.a, + i = this.c, + o = this.tx; + return this.a = n * e - this.b * r, this.b = n * r + this.b * e, this.c = i * e - this.d * r, this.d = i * r + this.d * e, this.tx = o * e - this.ty * r, this.ty = o * r + this.ty * e, this + }, Y.prototype.append = function(t) { + var e = this.a, + r = this.b, + n = this.c, + i = this.d; + return this.a = t.a * e + t.b * n, this.b = t.a * r + t.b * i, this.c = t.c * e + t.d * n, this.d = t.c * r + t.d * i, this.tx = t.tx * e + t.ty * n + this.tx, this.ty = t.tx * r + t.ty * i + this.ty, this + }, Y.prototype.setTransform = function(t, e, r, n, i, o, a, s, u) { + return this.a = Math.cos(a + u) * i, this.b = Math.sin(a + u) * i, this.c = -Math.sin(a - s) * o, this.d = Math.cos(a - s) * o, this.tx = t - (r * this.a + n * this.c), this.ty = e - (r * this.b + n * this.d), this + }, Y.prototype.prepend = function(t) { + var e = this.tx; + if (1 !== t.a || 0 !== t.b || 0 !== t.c || 1 !== t.d) { + var r = this.a, + n = this.c; + this.a = r * t.a + this.b * t.c, this.b = r * t.b + this.b * t.d, this.c = n * t.a + this.d * t.c, this.d = n * t.b + this.d * t.d + } + return this.tx = e * t.a + this.ty * t.c + t.tx, this.ty = e * t.b + this.ty * t.d + t.ty, this + }, Y.prototype.decompose = function(t) { + var e = this.a, + r = this.b, + n = this.c, + i = this.d, + o = -Math.atan2(-n, i), + a = Math.atan2(r, e), + s = Math.abs(o + a); + return s < 1e-5 || Math.abs(X - s) < 1e-5 ? (t.rotation = a, t.skew.x = t.skew.y = 0) : (t.rotation = 0, t.skew.x = o, t.skew.y = a), t.scale.x = Math.sqrt(e * e + r * r), t.scale.y = Math.sqrt(n * n + i * i), t.position.x = this.tx, t.position.y = this.ty, t + }, Y.prototype.invert = function() { + var t = this.a, + e = this.b, + r = this.c, + n = this.d, + i = this.tx, + o = t * n - e * r; + return this.a = n / o, this.b = -e / o, this.c = -r / o, this.d = t / o, this.tx = (r * this.ty - n * i) / o, this.ty = -(t * this.ty - e * i) / o, this + }, Y.prototype.identity = function() { + return this.a = 1, this.b = 0, this.c = 0, this.d = 1, this.tx = 0, this.ty = 0, this + }, Y.prototype.clone = function() { + var t = new Y; + return t.a = this.a, t.b = this.b, t.c = this.c, t.d = this.d, t.tx = this.tx, t.ty = this.ty, t + }, Y.prototype.copyTo = function(t) { + return t.a = this.a, t.b = this.b, t.c = this.c, t.d = this.d, t.tx = this.tx, t.ty = this.ty, t + }, Y.prototype.copyFrom = function(t) { + return this.a = t.a, this.b = t.b, this.c = t.c, this.d = t.d, this.tx = t.tx, this.ty = t.ty, this + }, K.IDENTITY.get = function() { + return new Y + }, K.TEMP_MATRIX.get = function() { + return new Y + }, Object.defineProperties(Y, K); + var Z = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1], + Q = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1], + J = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1], + $ = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1], + tt = [], + et = []; + + function signum(t) { + return t < 0 ? -1 : 0 < t ? 1 : 0 + }! function() { + for (var t = 0; t < 16; t++) { + var e = []; + et.push(e); + for (var r = 0; r < 16; r++) + for (var n = signum(Z[t] * Z[r] + J[t] * Q[r]), i = signum(Q[t] * Z[r] + $[t] * Q[r]), o = signum(Z[t] * J[r] + J[t] * $[r]), a = signum(Q[t] * J[r] + $[t] * $[r]), s = 0; s < 16; s++) + if (Z[s] === n && Q[s] === i && J[s] === o && $[s] === a) { + e.push(s); + break + } + } + for (var u = 0; u < 16; u++) { + var h = new Y; + h.set(Z[u], Q[u], J[u], $[u], 0, 0), tt.push(h) + } + }(); + var rt = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function(t) { + return Z[t] + }, + uY: function(t) { + return Q[t] + }, + vX: function(t) { + return J[t] + }, + vY: function(t) { + return $[t] + }, + inv: function(t) { + return 8 & t ? 15 & t : 7 & -t + }, + add: function(t, e) { + return et[t][e] + }, + sub: function(t, e) { + return et[t][rt.inv(e)] + }, + rotate180: function(t) { + return 4 ^ t + }, + isVertical: function(t) { + return 2 == (3 & t) + }, + byDirection: function(t, e) { + return 2 * Math.abs(t) <= Math.abs(e) ? 0 <= e ? rt.S : rt.N : 2 * Math.abs(e) <= Math.abs(t) ? 0 < t ? rt.E : rt.W : 0 < e ? 0 < t ? rt.SE : rt.SW : 0 < t ? rt.NE : rt.NW + }, + matrixAppendRotationInv: function(t, e, r, n) { + void 0 === r && (r = 0), void 0 === n && (n = 0); + var i = tt[rt.inv(e)]; + i.tx = r, i.ty = n, t.append(i) + } + }, + nt = function() { + this.worldTransform = new Y, this.localTransform = new Y, this.position = new j(this.onChange, this, 0, 0), this.scale = new j(this.onChange, this, 1, 1), this.pivot = new j(this.onChange, this, 0, 0), this.skew = new j(this.updateSkew, this, 0, 0), this._rotation = 0, this._cx = 1, this._sx = 0, this._cy = 0, this._sy = 1, this._localID = 0, this._currentLocalID = 0, this._worldID = 0, this._parentID = 0 + }, + it = { + rotation: { + configurable: !0 + } + }; + nt.prototype.onChange = function() { + this._localID++ + }, nt.prototype.updateSkew = function() { + this._cx = Math.cos(this._rotation + this.skew._y), this._sx = Math.sin(this._rotation + this.skew._y), this._cy = -Math.sin(this._rotation - this.skew._x), this._sy = Math.cos(this._rotation - this.skew._x), this._localID++ + }, nt.prototype.updateLocalTransform = function() { + var t = this.localTransform; + this._localID !== this._currentLocalID && (t.a = this._cx * this.scale._x, t.b = this._sx * this.scale._x, t.c = this._cy * this.scale._y, t.d = this._sy * this.scale._y, t.tx = this.position._x - (this.pivot._x * t.a + this.pivot._y * t.c), t.ty = this.position._y - (this.pivot._x * t.b + this.pivot._y * t.d), this._currentLocalID = this._localID, this._parentID = -1) + }, nt.prototype.updateTransform = function(t) { + var e = this.localTransform; + if (this._localID !== this._currentLocalID && (e.a = this._cx * this.scale._x, e.b = this._sx * this.scale._x, e.c = this._cy * this.scale._y, e.d = this._sy * this.scale._y, e.tx = this.position._x - (this.pivot._x * e.a + this.pivot._y * e.c), e.ty = this.position._y - (this.pivot._x * e.b + this.pivot._y * e.d), this._currentLocalID = this._localID, this._parentID = -1), this._parentID !== t._worldID) { + var r = t.worldTransform, + n = this.worldTransform; + n.a = e.a * r.a + e.b * r.c, n.b = e.a * r.b + e.b * r.d, n.c = e.c * r.a + e.d * r.c, n.d = e.c * r.b + e.d * r.d, n.tx = e.tx * r.a + e.ty * r.c + r.tx, n.ty = e.tx * r.b + e.ty * r.d + r.ty, this._parentID = t._worldID, this._worldID++ + } + }, nt.prototype.setFromMatrix = function(t) { + t.decompose(this), this._localID++ + }, it.rotation.get = function() { + return this._rotation + }, it.rotation.set = function(t) { + this._rotation !== t && (this._rotation = t, this.updateSkew()) + }, Object.defineProperties(nt.prototype, it), nt.IDENTITY = new nt; + var ot = function(t, e, r, n) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 0), void 0 === n && (n = 0), this.x = Number(t), this.y = Number(e), this.width = Number(r), this.height = Number(n), this.type = W.RECT + }, + at = { + left: { + configurable: !0 + }, + right: { + configurable: !0 + }, + top: { + configurable: !0 + }, + bottom: { + configurable: !0 + } + }, + st = { + EMPTY: { + configurable: !0 + } + }; + at.left.get = function() { + return this.x + }, at.right.get = function() { + return this.x + this.width + }, at.top.get = function() { + return this.y + }, at.bottom.get = function() { + return this.y + this.height + }, st.EMPTY.get = function() { + return new ot(0, 0, 0, 0) + }, ot.prototype.clone = function() { + return new ot(this.x, this.y, this.width, this.height) + }, ot.prototype.copyFrom = function(t) { + return this.x = t.x, this.y = t.y, this.width = t.width, this.height = t.height, this + }, ot.prototype.copyTo = function(t) { + return t.x = this.x, t.y = this.y, t.width = this.width, t.height = this.height, t + }, ot.prototype.contains = function(t, e) { + return !(this.width <= 0 || this.height <= 0) && (t >= this.x && t < this.x + this.width && e >= this.y && e < this.y + this.height) + }, ot.prototype.pad = function(t, e) { + t = t || 0, e = e || (0 !== e ? t : 0), this.x -= t, this.y -= e, this.width += 2 * t, this.height += 2 * e + }, ot.prototype.fit = function(t) { + var e = Math.max(this.x, t.x), + r = Math.min(this.x + this.width, t.x + t.width), + n = Math.max(this.y, t.y), + i = Math.min(this.y + this.height, t.y + t.height); + this.x = e, this.width = Math.max(r - e, 0), this.y = n, this.height = Math.max(i - n, 0) + }, ot.prototype.ceil = function(t, e) { + void 0 === t && (t = 1), void 0 === e && (e = .001); + var r = Math.ceil((this.x + this.width - e) * t) / t, + n = Math.ceil((this.y + this.height - e) * t) / t; + this.x = Math.floor((this.x + e) * t) / t, this.y = Math.floor((this.y + e) * t) / t, this.width = r - this.x, this.height = n - this.y + }, ot.prototype.enlarge = function(t) { + var e = Math.min(this.x, t.x), + r = Math.max(this.x + this.width, t.x + t.width), + n = Math.min(this.y, t.y), + i = Math.max(this.y + this.height, t.y + t.height); + this.x = e, this.width = r - e, this.y = n, this.height = i - n + }, Object.defineProperties(ot.prototype, at), Object.defineProperties(ot, st); + var ut = function(t, e, r) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 0), this.x = t, this.y = e, this.radius = r, this.type = W.CIRC + }; + ut.prototype.clone = function() { + return new ut(this.x, this.y, this.radius) + }, ut.prototype.contains = function(t, e) { + if (this.radius <= 0) return !1; + var r = this.radius * this.radius, + n = this.x - t, + i = this.y - e; + return (n *= n) + (i *= i) <= r + }, ut.prototype.getBounds = function() { + return new ot(this.x - this.radius, this.y - this.radius, 2 * this.radius, 2 * this.radius) + }; + var ht = function(t, e, r, n) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 0), void 0 === n && (n = 0), this.x = t, this.y = e, this.width = r, this.height = n, this.type = W.ELIP + }; + ht.prototype.clone = function() { + return new ht(this.x, this.y, this.width, this.height) + }, ht.prototype.contains = function(t, e) { + if (this.width <= 0 || this.height <= 0) return !1; + var r = (t - this.x) / this.width, + n = (e - this.y) / this.height; + return (r *= r) + (n *= n) <= 1 + }, ht.prototype.getBounds = function() { + return new ot(this.x - this.width, this.y - this.height, this.width, this.height) + }; + var ct = function() { + for (var t = [], e = arguments.length; e--;) t[e] = arguments[e]; + if (Array.isArray(t[0]) && (t = t[0]), t[0] instanceof q) { + for (var r = [], n = 0, i = t.length; n < i; n++) r.push(t[n].x, t[n].y); + t = r + } + this.points = t, this.type = W.POLY, this.closeStroke = !0 + }; + ct.prototype.clone = function() { + var t = new ct(this.points.slice()); + return t.closeStroke = this.closeStroke, t + }, ct.prototype.contains = function(t, e) { + for (var r = !1, n = this.points.length / 2, i = 0, o = n - 1; i < n; o = i++) { + var a = this.points[2 * i], + s = this.points[2 * i + 1], + u = this.points[2 * o], + h = this.points[2 * o + 1]; + e < s != e < h && t < (e - s) / (h - s) * (u - a) + a && (r = !r) + } + return r + }; + var lt = function(t, e, r, n, i) { + void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 0), void 0 === n && (n = 0), void 0 === i && (i = 20), this.x = t, this.y = e, this.width = r, this.height = n, this.radius = i, this.type = W.RREC + }; + lt.prototype.clone = function() { + return new lt(this.x, this.y, this.width, this.height, this.radius) + }, lt.prototype.contains = function(t, e) { + if (this.width <= 0 || this.height <= 0) return !1; + if (t >= this.x && t <= this.x + this.width && e >= this.y && e <= this.y + this.height) { + if (e >= this.y + this.radius && e <= this.y + this.height - this.radius || t >= this.x + this.radius && t <= this.x + this.width - this.radius) return !0; + var r = t - (this.x + this.radius), + n = e - (this.y + this.radius), + i = this.radius * this.radius; + if (r * r + n * n <= i) return !0; + if ((r = t - (this.x + this.width - this.radius)) * r + n * n <= i) return !0; + if (r * r + (n = e - (this.y + this.height - this.radius)) * n <= i) return !0; + if ((r = t - (this.x + this.radius)) * r + n * n <= i) return !0 + } + return !1 + }, M.SORTABLE_CHILDREN = !1; + var ft = function() { + this.minX = 1 / 0, this.minY = 1 / 0, this.maxX = -1 / 0, this.maxY = -1 / 0, this.rect = null + }; + ft.prototype.isEmpty = function() { + return this.minX > this.maxX || this.minY > this.maxY + }, ft.prototype.clear = function() { + this.updateID++, this.minX = 1 / 0, this.minY = 1 / 0, this.maxX = -1 / 0, this.maxY = -1 / 0 + }, ft.prototype.getRectangle = function(t) { + return this.minX > this.maxX || this.minY > this.maxY ? ot.EMPTY : ((t = t || new ot(0, 0, 1, 1)).x = this.minX, t.y = this.minY, t.width = this.maxX - this.minX, t.height = this.maxY - this.minY, t) + }, ft.prototype.addPoint = function(t) { + this.minX = Math.min(this.minX, t.x), this.maxX = Math.max(this.maxX, t.x), this.minY = Math.min(this.minY, t.y), this.maxY = Math.max(this.maxY, t.y) + }, ft.prototype.addQuad = function(t) { + var e = this.minX, + r = this.minY, + n = this.maxX, + i = this.maxY, + o = t[0], + a = t[1]; + e = o < e ? o : e, r = a < r ? a : r, n = n < o ? o : n, i = i < a ? a : i, e = (o = t[2]) < e ? o : e, r = (a = t[3]) < r ? a : r, n = n < o ? o : n, i = i < a ? a : i, e = (o = t[4]) < e ? o : e, r = (a = t[5]) < r ? a : r, n = n < o ? o : n, i = i < a ? a : i, e = (o = t[6]) < e ? o : e, r = (a = t[7]) < r ? a : r, n = n < o ? o : n, i = i < a ? a : i, this.minX = e, this.minY = r, this.maxX = n, this.maxY = i + }, ft.prototype.addFrame = function(t, e, r, n, i) { + var o = t.worldTransform, + a = o.a, + s = o.b, + u = o.c, + h = o.d, + c = o.tx, + l = o.ty, + f = this.minX, + d = this.minY, + p = this.maxX, + m = this.maxY, + v = a * e + u * r + c, + g = s * e + h * r + l; + f = v < f ? v : f, d = g < d ? g : d, p = p < v ? v : p, m = m < g ? g : m, f = (v = a * n + u * r + c) < f ? v : f, d = (g = s * n + h * r + l) < d ? g : d, p = p < v ? v : p, m = m < g ? g : m, f = (v = a * e + u * i + c) < f ? v : f, d = (g = s * e + h * i + l) < d ? g : d, p = p < v ? v : p, m = m < g ? g : m, f = (v = a * n + u * i + c) < f ? v : f, d = (g = s * n + h * i + l) < d ? g : d, p = p < v ? v : p, m = m < g ? g : m, this.minX = f, this.minY = d, this.maxX = p, this.maxY = m + }, ft.prototype.addVertexData = function(t, e, r) { + for (var n = this.minX, i = this.minY, o = this.maxX, a = this.maxY, s = e; s < r; s += 2) { + var u = t[s], + h = t[s + 1]; + n = u < n ? u : n, i = h < i ? h : i, o = o < u ? u : o, a = a < h ? h : a + } + this.minX = n, this.minY = i, this.maxX = o, this.maxY = a + }, ft.prototype.addVertices = function(t, e, r, n) { + for (var i = t.worldTransform, o = i.a, a = i.b, s = i.c, u = i.d, h = i.tx, c = i.ty, l = this.minX, f = this.minY, d = this.maxX, p = this.maxY, m = r; m < n; m += 2) { + var v = e[m], + g = e[m + 1], + y = o * v + s * g + h, + _ = u * g + a * v + c; + l = y < l ? y : l, f = _ < f ? _ : f, d = d < y ? y : d, p = p < _ ? _ : p + } + this.minX = l, this.minY = f, this.maxX = d, this.maxY = p + }, ft.prototype.addBounds = function(t) { + var e = this.minX, + r = this.minY, + n = this.maxX, + i = this.maxY; + this.minX = t.minX < e ? t.minX : e, this.minY = t.minY < r ? t.minY : r, this.maxX = t.maxX > n ? t.maxX : n, this.maxY = t.maxY > i ? t.maxY : i + }, ft.prototype.addBoundsMask = function(t, e) { + var r = t.minX > e.minX ? t.minX : e.minX, + n = t.minY > e.minY ? t.minY : e.minY, + i = t.maxX < e.maxX ? t.maxX : e.maxX, + o = t.maxY < e.maxY ? t.maxY : e.maxY; + if (r <= i && n <= o) { + var a = this.minX, + s = this.minY, + u = this.maxX, + h = this.maxY; + this.minX = r < a ? r : a, this.minY = n < s ? n : s, this.maxX = u < i ? i : u, this.maxY = h < o ? o : h + } + }, ft.prototype.addBoundsArea = function(t, e) { + var r = t.minX > e.x ? t.minX : e.x, + n = t.minY > e.y ? t.minY : e.y, + i = t.maxX < e.x + e.width ? t.maxX : e.x + e.width, + o = t.maxY < e.y + e.height ? t.maxY : e.y + e.height; + if (r <= i && n <= o) { + var a = this.minX, + s = this.minY, + u = this.maxX, + h = this.maxY; + this.minX = r < a ? r : a, this.minY = n < s ? n : s, this.maxX = u < i ? i : u, this.maxY = h < o ? o : h + } + }; + var dt = function(t) { + function DisplayObject() { + t.call(this), this.tempDisplayObjectParent = null, this.transform = new nt, this.alpha = 1, this.visible = !0, this.renderable = !0, this.parent = null, this.worldAlpha = 1, this._lastSortedIndex = 0, this._zIndex = 0, this.filterArea = null, this.filters = null, this._enabledFilters = null, this._bounds = new ft, this._boundsID = 0, this._lastBoundsID = -1, this._boundsRect = null, this._localBoundsRect = null, this._mask = null, this._destroyed = !1, this.isSprite = !1 + } + t && (DisplayObject.__proto__ = t); + var e = { + _tempDisplayObjectParent: { + configurable: !0 + }, + x: { + configurable: !0 + }, + y: { + configurable: !0 + }, + worldTransform: { + configurable: !0 + }, + localTransform: { + configurable: !0 + }, + position: { + configurable: !0 + }, + scale: { + configurable: !0 + }, + pivot: { + configurable: !0 + }, + skew: { + configurable: !0 + }, + rotation: { + configurable: !0 + }, + angle: { + configurable: !0 + }, + zIndex: { + configurable: !0 + }, + worldVisible: { + configurable: !0 + }, + mask: { + configurable: !0 + } + }; + return ((DisplayObject.prototype = Object.create(t && t.prototype)).constructor = DisplayObject).mixin = function(t) { + for (var e = Object.keys(t), r = 0; r < e.length; ++r) { + var n = e[r]; + Object.defineProperty(DisplayObject.prototype, n, Object.getOwnPropertyDescriptor(t, n)) + } + }, e._tempDisplayObjectParent.get = function() { + return null === this.tempDisplayObjectParent && (this.tempDisplayObjectParent = new DisplayObject), this.tempDisplayObjectParent + }, DisplayObject.prototype.updateTransform = function() { + this.transform.updateTransform(this.parent.transform), this.worldAlpha = this.alpha * this.parent.worldAlpha, this._bounds.updateID++ + }, DisplayObject.prototype._recursivePostUpdateTransform = function() { + this.parent ? (this.parent._recursivePostUpdateTransform(), this.transform.updateTransform(this.parent.transform)) : this.transform.updateTransform(this._tempDisplayObjectParent.transform) + }, DisplayObject.prototype.getBounds = function(t, e) { + return t || (this.parent ? (this._recursivePostUpdateTransform(), this.updateTransform()) : (this.parent = this._tempDisplayObjectParent, this.updateTransform(), this.parent = null)), this._boundsID !== this._lastBoundsID && this.calculateBounds(), e || (this._boundsRect || (this._boundsRect = new ot), e = this._boundsRect), this._bounds.getRectangle(e) + }, DisplayObject.prototype.getLocalBounds = function(t) { + var e = this.transform, + r = this.parent; + this.parent = null, this.transform = this._tempDisplayObjectParent.transform, t || (this._localBoundsRect || (this._localBoundsRect = new ot), t = this._localBoundsRect); + var n = this.getBounds(!1, t); + return this.parent = r, this.transform = e, n + }, DisplayObject.prototype.toGlobal = function(t, e, r) { + return void 0 === r && (r = !1), r || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.apply(t, e) + }, DisplayObject.prototype.toLocal = function(t, e, r, n) { + return e && (t = e.toGlobal(t, r, n)), n || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.applyInverse(t, r) + }, DisplayObject.prototype.render = function(t) {}, DisplayObject.prototype.setParent = function(t) { + if (!t || !t.addChild) throw new Error("setParent: Argument must be a Container"); + return t.addChild(this), t + }, DisplayObject.prototype.setTransform = function(t, e, r, n, i, o, a, s, u) { + return void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 1), void 0 === n && (n = 1), void 0 === i && (i = 0), void 0 === o && (o = 0), void 0 === a && (a = 0), void 0 === s && (s = 0), void 0 === u && (u = 0), this.position.x = t, this.position.y = e, this.scale.x = r || 1, this.scale.y = n || 1, this.rotation = i, this.skew.x = o, this.skew.y = a, this.pivot.x = s, this.pivot.y = u, this + }, DisplayObject.prototype.destroy = function() { + this.removeAllListeners(), this.parent && this.parent.removeChild(this), this.transform = null, this.parent = null, this._bounds = null, this._currentBounds = null, this._mask = null, this.filterArea = null, this.interactive = !1, this.interactiveChildren = !1, this._destroyed = !0 + }, e.x.get = function() { + return this.position.x + }, e.x.set = function(t) { + this.transform.position.x = t + }, e.y.get = function() { + return this.position.y + }, e.y.set = function(t) { + this.transform.position.y = t + }, e.worldTransform.get = function() { + return this.transform.worldTransform + }, e.localTransform.get = function() { + return this.transform.localTransform + }, e.position.get = function() { + return this.transform.position + }, e.position.set = function(t) { + this.transform.position.copyFrom(t) + }, e.scale.get = function() { + return this.transform.scale + }, e.scale.set = function(t) { + this.transform.scale.copyFrom(t) + }, e.pivot.get = function() { + return this.transform.pivot + }, e.pivot.set = function(t) { + this.transform.pivot.copyFrom(t) + }, e.skew.get = function() { + return this.transform.skew + }, e.skew.set = function(t) { + this.transform.skew.copyFrom(t) + }, e.rotation.get = function() { + return this.transform.rotation + }, e.rotation.set = function(t) { + this.transform.rotation = t + }, e.angle.get = function() { + return this.transform.rotation * V + }, e.angle.set = function(t) { + this.transform.rotation = t * H + }, e.zIndex.get = function() { + return this._zIndex + }, e.zIndex.set = function(t) { + this._zIndex = t, this.parent && (this.parent.sortDirty = !0) + }, e.worldVisible.get = function() { + var t = this; + do { + if (!t.visible) return !1; + t = t.parent + } while (t); + return !0 + }, e.mask.get = function() { + return this._mask + }, e.mask.set = function(t) { + this._mask && (this._mask.renderable = !0, this._mask.isMask = !1), this._mask = t, this._mask && (this._mask.renderable = !1, this._mask.isMask = !0) + }, Object.defineProperties(DisplayObject.prototype, e), DisplayObject + }(l.a); + + function sortChildren(t, e) { + return t.zIndex === e.zIndex ? t._lastSortedIndex - e._lastSortedIndex : t.zIndex - e.zIndex + } + dt.prototype.displayObjectUpdateTransform = dt.prototype.updateTransform; + var pt = function(i) { + function Container() { + i.call(this), this.children = [], this.sortableChildren = M.SORTABLE_CHILDREN, this.sortDirty = !1 + } + i && (Container.__proto__ = i); + var t = { + width: { + configurable: !0 + }, + height: { + configurable: !0 + } + }; + return ((Container.prototype = Object.create(i && i.prototype)).constructor = Container).prototype.onChildrenChange = function() {}, Container.prototype.addChild = function(t) { + var e = arguments, + r = arguments.length; + if (1 < r) + for (var n = 0; n < r; n++) this.addChild(e[n]); + else t.parent && t.parent.removeChild(t), (t.parent = this).sortDirty = !0, t.transform._parentID = -1, this.children.push(t), this._boundsID++, this.onChildrenChange(this.children.length - 1), this.emit("childAdded", t, this, this.children.length - 1), t.emit("added", this); + return t + }, Container.prototype.addChildAt = function(t, e) { + if (e < 0 || e > this.children.length) throw new Error(t + "addChildAt: The index " + e + " supplied is out of bounds " + this.children.length); + return t.parent && t.parent.removeChild(t), (t.parent = this).sortDirty = !0, t.transform._parentID = -1, this.children.splice(e, 0, t), this._boundsID++, this.onChildrenChange(e), t.emit("added", this), this.emit("childAdded", t, this, e), t + }, Container.prototype.swapChildren = function(t, e) { + if (t !== e) { + var r = this.getChildIndex(t), + n = this.getChildIndex(e); + this.children[r] = e, this.children[n] = t, this.onChildrenChange(r < n ? r : n) + } + }, Container.prototype.getChildIndex = function(t) { + var e = this.children.indexOf(t); + if (-1 === e) throw new Error("The supplied DisplayObject must be a child of the caller"); + return e + }, Container.prototype.setChildIndex = function(t, e) { + if (e < 0 || e >= this.children.length) throw new Error("The index " + e + " supplied is out of bounds " + this.children.length); + var r = this.getChildIndex(t); + removeItems(this.children, r, 1), this.children.splice(e, 0, t), this.onChildrenChange(e) + }, Container.prototype.getChildAt = function(t) { + if (t < 0 || t >= this.children.length) throw new Error("getChildAt: Index (" + t + ") does not exist."); + return this.children[t] + }, Container.prototype.removeChild = function(t) { + var e = arguments, + r = arguments.length; + if (1 < r) + for (var n = 0; n < r; n++) this.removeChild(e[n]); + else { + var i = this.children.indexOf(t); + if (-1 === i) return null; + t.parent = null, t.transform._parentID = -1, removeItems(this.children, i, 1), this._boundsID++, this.onChildrenChange(i), t.emit("removed", this), this.emit("childRemoved", t, this, i) + } + return t + }, Container.prototype.removeChildAt = function(t) { + var e = this.getChildAt(t); + return e.parent = null, e.transform._parentID = -1, removeItems(this.children, t, 1), this._boundsID++, this.onChildrenChange(t), e.emit("removed", this), this.emit("childRemoved", e, this, t), e + }, Container.prototype.removeChildren = function(t, e) { + void 0 === t && (t = 0); + var r, n = t, + i = "number" == typeof e ? e : this.children.length, + o = i - n; + if (0 < o && o <= i) { + r = this.children.splice(n, o); + for (var a = 0; a < r.length; ++a) r[a].parent = null, r[a].transform && (r[a].transform._parentID = -1); + this._boundsID++, this.onChildrenChange(t); + for (var s = 0; s < r.length; ++s) r[s].emit("removed", this), this.emit("childRemoved", r[s], this, s); + return r + } + if (0 === o && 0 === this.children.length) return []; + throw new RangeError("removeChildren: numeric values are outside the acceptable range.") + }, Container.prototype.sortChildren = function() { + for (var t = !1, e = 0, r = this.children.length; e < r; ++e) { + var n = this.children[e]; + n._lastSortedIndex = e, t || 0 === n.zIndex || (t = !0) + } + t && 1 < this.children.length && this.children.sort(sortChildren), this.sortDirty = !1 + }, Container.prototype.updateTransform = function() { + this.sortableChildren && this.sortDirty && this.sortChildren(), this._boundsID++, this.transform.updateTransform(this.parent.transform), this.worldAlpha = this.alpha * this.parent.worldAlpha; + for (var t = 0, e = this.children.length; t < e; ++t) { + var r = this.children[t]; + r.visible && r.updateTransform() + } + }, Container.prototype.calculateBounds = function() { + this._bounds.clear(), this._calculateBounds(); + for (var t = 0; t < this.children.length; t++) { + var e = this.children[t]; + e.visible && e.renderable && (e.calculateBounds(), e._mask ? (e._mask.calculateBounds(), this._bounds.addBoundsMask(e._bounds, e._mask._bounds)) : e.filterArea ? this._bounds.addBoundsArea(e._bounds, e.filterArea) : this._bounds.addBounds(e._bounds)) + } + this._lastBoundsID = this._boundsID + }, Container.prototype._calculateBounds = function() {}, Container.prototype.render = function(t) { + if (this.visible && !(this.worldAlpha <= 0) && this.renderable) + if (this._mask || this.filters) this.renderAdvanced(t); + else { + this._render(t); + for (var e = 0, r = this.children.length; e < r; ++e) this.children[e].render(t) + } + }, Container.prototype.renderAdvanced = function(t) { + t.batch.flush(); + var e = this.filters, + r = this._mask; + if (e) { + this._enabledFilters || (this._enabledFilters = []); + for (var n = this._enabledFilters.length = 0; n < e.length; n++) e[n].enabled && this._enabledFilters.push(e[n]); + this._enabledFilters.length && t.filter.push(this, this._enabledFilters) + } + r && t.mask.push(this, this._mask), this._render(t); + for (var i = 0, o = this.children.length; i < o; i++) this.children[i].render(t); + t.batch.flush(), r && t.mask.pop(this, this._mask), e && this._enabledFilters && this._enabledFilters.length && t.filter.pop() + }, Container.prototype._render = function(t) {}, Container.prototype.destroy = function(t) { + i.prototype.destroy.call(this), this.sortDirty = !1; + var e = "boolean" == typeof t ? t : t && t.children, + r = this.removeChildren(0, this.children.length); + if (e) + for (var n = 0; n < r.length; ++n) r[n].destroy(t) + }, t.width.get = function() { + return this.scale.x * this.getLocalBounds().width + }, t.width.set = function(t) { + var e = this.getLocalBounds().width; + this.scale.x = 0 !== e ? t / e : 1, this._width = t + }, t.height.get = function() { + return this.scale.y * this.getLocalBounds().height + }, t.height.set = function(t) { + var e = this.getLocalBounds().height; + this.scale.y = 0 !== e ? t / e : 1, this._height = t + }, Object.defineProperties(Container.prototype, t), Container + }(dt); + pt.prototype.containerUpdateTransform = pt.prototype.updateTransform; + var mt = { + accessible: !1, + accessibleTitle: null, + accessibleHint: null, + tabIndex: 0, + _accessibleActive: !1, + _accessibleDiv: !1 + }; + dt.mixin(mt); + var vt = function(t) { + this._hookDiv = null, (h.a.tablet || h.a.phone) && this.createTouchHook(); + var e = document.createElement("div"); + e.style.width = "100px", e.style.height = "100px", e.style.position = "absolute", e.style.top = "0px", e.style.left = "0px", e.style.zIndex = 2, this.div = e, this.pool = [], this.renderId = 0, this.debug = !1, this.renderer = t, this.children = [], this._onKeyDown = this._onKeyDown.bind(this), this._onMouseMove = this._onMouseMove.bind(this), this.isActive = !1, this.isMobileAccessibility = !1, window.addEventListener("keydown", this._onKeyDown, !1) + }; + vt.prototype.createTouchHook = function() { + var t = this, + e = document.createElement("button"); + e.style.width = "1px", e.style.height = "1px", e.style.position = "absolute", e.style.top = "-1000px", e.style.left = "-1000px", e.style.zIndex = 2, e.style.backgroundColor = "#FF0000", e.title = "HOOK DIV", e.addEventListener("focus", function() { + t.isMobileAccessibility = !0, t.activate(), t.destroyTouchHook() + }), document.body.appendChild(e), this._hookDiv = e + }, vt.prototype.destroyTouchHook = function() { + this._hookDiv && (document.body.removeChild(this._hookDiv), this._hookDiv = null) + }, vt.prototype.activate = function() { + this.isActive || (this.isActive = !0, window.document.addEventListener("mousemove", this._onMouseMove, !0), window.removeEventListener("keydown", this._onKeyDown, !1), this.renderer.on("postrender", this.update, this), this.renderer.view.parentNode && this.renderer.view.parentNode.appendChild(this.div)) + }, vt.prototype.deactivate = function() { + this.isActive && !this.isMobileAccessibility && (this.isActive = !1, window.document.removeEventListener("mousemove", this._onMouseMove, !0), window.addEventListener("keydown", this._onKeyDown, !1), this.renderer.off("postrender", this.update), this.div.parentNode && this.div.parentNode.removeChild(this.div)) + }, vt.prototype.updateAccessibleObjects = function(t) { + if (t.visible) { + t.accessible && t.interactive && (t._accessibleActive || this.addChild(t), t.renderId = this.renderId); + for (var e = t.children, r = 0; r < e.length; r++) this.updateAccessibleObjects(e[r]) + } + }, vt.prototype.update = function() { + if (this.renderer.renderingToScreen) { + this.updateAccessibleObjects(this.renderer._lastObjectRendered); + var t = this.renderer.view.getBoundingClientRect(), + e = t.width / this.renderer.width, + r = t.height / this.renderer.height, + n = this.div; + n.style.left = t.left + "px", n.style.top = t.top + "px", n.style.width = this.renderer.width + "px", n.style.height = this.renderer.height + "px"; + for (var i = 0; i < this.children.length; i++) { + var o = this.children[i]; + if (o.renderId !== this.renderId) o._accessibleActive = !1, removeItems(this.children, i, 1), this.div.removeChild(o._accessibleDiv), this.pool.push(o._accessibleDiv), o._accessibleDiv = null, i--, 0 === this.children.length && this.deactivate(); + else { + n = o._accessibleDiv; + var a = o.hitArea, + s = o.worldTransform; + o.hitArea ? (n.style.left = (s.tx + a.x * s.a) * e + "px", n.style.top = (s.ty + a.y * s.d) * r + "px", n.style.width = a.width * s.a * e + "px", n.style.height = a.height * s.d * r + "px") : (a = o.getBounds(), this.capHitArea(a), n.style.left = a.x * e + "px", n.style.top = a.y * r + "px", n.style.width = a.width * e + "px", n.style.height = a.height * r + "px", n.title !== o.accessibleTitle && null !== o.accessibleTitle && (n.title = o.accessibleTitle), n.getAttribute("aria-label") !== o.accessibleHint && null !== o.accessibleHint && n.setAttribute("aria-label", o.accessibleHint)) + } + } + this.renderId++ + } + }, vt.prototype.capHitArea = function(t) { + t.x < 0 && (t.width += t.x, t.x = 0), t.y < 0 && (t.height += t.y, t.y = 0), t.x + t.width > this.renderer.width && (t.width = this.renderer.width - t.x), t.y + t.height > this.renderer.height && (t.height = this.renderer.height - t.y) + }, vt.prototype.addChild = function(t) { + var e = this.pool.pop(); + e || ((e = document.createElement("button")).style.width = "100px", e.style.height = "100px", e.style.backgroundColor = this.debug ? "rgba(255,0,0,0.5)" : "transparent", e.style.position = "absolute", e.style.zIndex = 2, e.style.borderStyle = "none", -1 < navigator.userAgent.toLowerCase().indexOf("chrome") ? e.setAttribute("aria-live", "off") : e.setAttribute("aria-live", "polite"), navigator.userAgent.match(/rv:.*Gecko\//) ? e.setAttribute("aria-relevant", "additions") : e.setAttribute("aria-relevant", "text"), e.addEventListener("click", this._onClick.bind(this)), e.addEventListener("focus", this._onFocus.bind(this)), e.addEventListener("focusout", this._onFocusOut.bind(this))), t.accessibleTitle && null !== t.accessibleTitle ? e.title = t.accessibleTitle : t.accessibleHint && null !== t.accessibleHint || (e.title = "displayObject " + t.tabIndex), t.accessibleHint && null !== t.accessibleHint && e.setAttribute("aria-label", t.accessibleHint), t._accessibleActive = !0, (t._accessibleDiv = e).displayObject = t, this.children.push(t), this.div.appendChild(t._accessibleDiv), t._accessibleDiv.tabIndex = t.tabIndex + }, vt.prototype._onClick = function(t) { + var e = this.renderer.plugins.interaction; + e.dispatchEvent(t.target.displayObject, "click", e.eventData) + }, vt.prototype._onFocus = function(t) { + t.target.getAttribute("aria-live", "off") || t.target.setAttribute("aria-live", "assertive"); + var e = this.renderer.plugins.interaction; + e.dispatchEvent(t.target.displayObject, "mouseover", e.eventData) + }, vt.prototype._onFocusOut = function(t) { + t.target.getAttribute("aria-live", "off") || t.target.setAttribute("aria-live", "polite"); + var e = this.renderer.plugins.interaction; + e.dispatchEvent(t.target.displayObject, "mouseout", e.eventData) + }, vt.prototype._onKeyDown = function(t) { + 9 === t.keyCode && this.activate() + }, vt.prototype._onMouseMove = function(t) { + 0 === t.movementX && 0 === t.movementY || this.deactivate() + }, vt.prototype.destroy = function() { + this.destroyTouchHook(), this.div = null; + for (var t = 0; t < this.children.length; t++) this.children[t].div = null; + window.document.removeEventListener("mousemove", this._onMouseMove, !0), window.removeEventListener("keydown", this._onKeyDown), this.pool = null, this.children = null, this.renderer = null + }; + var gt = function(t) { + this.items = [], this._name = t + }, + yt = { + empty: { + configurable: !0 + }, + name: { + configurable: !0 + } + }; + gt.prototype.emit = function(t, e, r, n, i, o, a, s) { + if (8 < arguments.length) throw new Error("max arguments reached"); + for (var u = this.name, h = this.items, c = 0, l = h.length; c < l; c++) h[c][u](t, e, r, n, i, o, a, s); + return this + }, gt.prototype.add = function(t) { + return t[this._name] && (this.remove(t), this.items.push(t)), this + }, gt.prototype.remove = function(t) { + var e = this.items.indexOf(t); + return -1 !== e && this.items.splice(e, 1), this + }, gt.prototype.contains = function(t) { + return -1 !== this.items.indexOf(t) + }, gt.prototype.removeAll = function() { + return this.items.length = 0, this + }, gt.prototype.destroy = function() { + this.removeAll(), this.items = null, this._name = null + }, yt.empty.get = function() { + return 0 === this.items.length + }, yt.name.get = function() { + return this._name + }, Object.defineProperties(gt.prototype, yt), gt.prototype.dispatch = gt.prototype.emit, gt.prototype.run = gt.prototype.emit, M.TARGET_FPMS = .06; + var _t = { + INTERACTION: 50, + HIGH: 25, + NORMAL: 0, + LOW: -25, + UTILITY: -50 + }, + bt = function(t, e, r, n) { + void 0 === e && (e = null), void 0 === r && (r = 0), void 0 === n && (n = !1), this.fn = t, this.context = e, this.priority = r, this.once = n, this.next = null, this.previous = null, this._destroyed = !1 + }; + bt.prototype.match = function(t, e) { + return e = e || null, this.fn === t && this.context === e + }, bt.prototype.emit = function(t) { + this.fn && (this.context ? this.fn.call(this.context, t) : this.fn(t)); + var e = this.next; + return this.once && this.destroy(!0), this._destroyed && (this.next = null), e + }, bt.prototype.connect = function(t) { + (this.previous = t).next && (t.next.previous = this), this.next = t.next, t.next = this + }, bt.prototype.destroy = function(t) { + void 0 === t && (t = !1), this._destroyed = !0, this.fn = null, this.context = null, this.previous && (this.previous.next = this.next), this.next && (this.next.previous = this.previous); + var e = this.next; + return this.next = t ? null : e, this.previous = null, e + }; + var xt = function() { + var e = this; + this._head = new bt(null, null, 1 / 0), this._requestId = null, this._maxElapsedMS = 100, this._minElapsedMS = 0, this.autoStart = !1, this.deltaTime = 1, this.deltaMS = 1 / M.TARGET_FPMS, this.elapsedMS = 1 / M.TARGET_FPMS, this.lastTime = -1, this.speed = 1, this.started = !1, this._protected = !1, this._tick = function(t) { + e._requestId = null, e.started && (e.update(t), e.started && null === e._requestId && e._head.next && (e._requestId = requestAnimationFrame(e._tick))) + } + }, + wt = { + FPS: { + configurable: !0 + }, + minFPS: { + configurable: !0 + }, + maxFPS: { + configurable: !0 + } + }, + Tt = { + shared: { + configurable: !0 + }, + system: { + configurable: !0 + } + }; + xt.prototype._requestIfNeeded = function() { + null === this._requestId && this._head.next && (this.lastTime = performance.now(), this._requestId = requestAnimationFrame(this._tick)) + }, xt.prototype._cancelIfNeeded = function() { + null !== this._requestId && (cancelAnimationFrame(this._requestId), this._requestId = null) + }, xt.prototype._startIfPossible = function() { + this.started ? this._requestIfNeeded() : this.autoStart && this.start() + }, xt.prototype.add = function(t, e, r) { + return void 0 === r && (r = _t.NORMAL), this._addListener(new bt(t, e, r)) + }, xt.prototype.addOnce = function(t, e, r) { + return void 0 === r && (r = _t.NORMAL), this._addListener(new bt(t, e, r, !0)) + }, xt.prototype._addListener = function(t) { + var e = this._head.next, + r = this._head; + if (e) { + for (; e;) { + if (t.priority > e.priority) { + t.connect(r); + break + } + e = (r = e).next + } + t.previous || t.connect(r) + } else t.connect(r); + return this._startIfPossible(), this + }, xt.prototype.remove = function(t, e) { + for (var r = this._head.next; r;) r = r.match(t, e) ? r.destroy() : r.next; + return this._head.next || this._cancelIfNeeded(), this + }, xt.prototype.start = function() { + this.started || (this.started = !0, this._requestIfNeeded()) + }, xt.prototype.stop = function() { + this.started && (this.started = !1, this._cancelIfNeeded()) + }, xt.prototype.destroy = function() { + if (!this._protected) { + this.stop(); + for (var t = this._head.next; t;) t = t.destroy(!0); + this._head.destroy(), this._head = null + } + }, xt.prototype.update = function(t) { + var e; + if (void 0 === t && (t = performance.now()), t > this.lastTime) { + if ((e = this.elapsedMS = t - this.lastTime) > this._maxElapsedMS && (e = this._maxElapsedMS), e *= this.speed, this._minElapsedMS && e + 1 < this._minElapsedMS) return; + this.deltaMS = e, this.deltaTime = this.deltaMS * M.TARGET_FPMS; + for (var r = this._head, n = r.next; n;) n = n.emit(this.deltaTime); + r.next || this._cancelIfNeeded() + } else this.deltaTime = this.deltaMS = this.elapsedMS = 0; + this.lastTime = t + }, wt.FPS.get = function() { + return 1e3 / this.elapsedMS + }, wt.minFPS.get = function() { + return 1e3 / this._maxElapsedMS + }, wt.minFPS.set = function(t) { + var e = Math.min(this.maxFPS, t), + r = Math.min(Math.max(0, e) / 1e3, M.TARGET_FPMS); + this._maxElapsedMS = 1 / r + }, wt.maxFPS.get = function() { + return this._minElapsedMS ? 1e3 / this._minElapsedMS : 1e3 * M.TARGET_FPMS + }, wt.maxFPS.set = function(t) { + if (t / 1e3 >= M.TARGET_FPMS) this._minElapsedMS = 0; + else { + var e = Math.max(this.minFPS, t), + r = Math.min(Math.max(1, e) / 1e3, M.TARGET_FPMS); + this._minElapsedMS = 1 / r + } + }, Tt.shared.get = function() { + if (!xt._shared) { + var t = xt._shared = new xt; + t.autoStart = !0, t._protected = !0 + } + return xt._shared + }, Tt.system.get = function() { + if (!xt._system) { + var t = xt._system = new xt; + t.autoStart = !0, t._protected = !0 + } + return xt._system + }, Object.defineProperties(xt.prototype, wt), Object.defineProperties(xt, Tt); + var St = function() {}; + St.init = function(t) { + var e = this; + t = Object.assign({ + autoStart: !0, + sharedTicker: !1 + }, t), Object.defineProperty(this, "ticker", { + set: function(t) { + this._ticker && this._ticker.remove(this.render, this), (this._ticker = t) && t.add(this.render, this, _t.LOW) + }, + get: function() { + return this._ticker + } + }), this.stop = function() { + e._ticker.stop() + }, this.start = function() { + e._ticker.start() + }, this._ticker = null, this.ticker = t.sharedTicker ? xt.shared : new xt, t.autoStart && this.start() + }, St.destroy = function() { + if (this._ticker) { + var t = this._ticker; + this.ticker = null, t.destroy() + } + }; + var Mt = function(t, e) { + void 0 === t && (t = 0), void 0 === e && (e = 0), this._width = t, this._height = e, this.destroyed = !1, this.internal = !1, this.onResize = new gt("setRealSize", 2), this.onUpdate = new gt("update") + }, + Et = { + valid: { + configurable: !0 + }, + width: { + configurable: !0 + }, + height: { + configurable: !0 + } + }; + Mt.prototype.bind = function(t) { + this.onResize.add(t), this.onUpdate.add(t), (this._width || this._height) && this.onResize.run(this._width, this._height) + }, Mt.prototype.unbind = function(t) { + this.onResize.remove(t), this.onUpdate.remove(t) + }, Mt.prototype.resize = function(t, e) { + t === this._width && e === this._height || (this._width = t, this._height = e, this.onResize.run(t, e)) + }, Et.valid.get = function() { + return !!this._width && !!this._height + }, Mt.prototype.update = function() { + this.destroyed || this.onUpdate.run() + }, Mt.prototype.load = function() { + return Promise.resolve() + }, Et.width.get = function() { + return this._width + }, Et.height.get = function() { + return this._height + }, Mt.prototype.upload = function(t, e, r) { + return !1 + }, Mt.prototype.style = function(t, e, r) { + return !1 + }, Mt.prototype.dispose = function() {}, Mt.prototype.destroy = function() { + this.destroyed || (this.onResize.removeAll(), this.onResize = null, this.onUpdate.removeAll(), this.onUpdate = null, this.destroyed = !0, this.dispose()) + }, Object.defineProperties(Mt.prototype, Et); + var At = function(e) { + function BaseImageResource(t) { + e.call(this, t.width, t.height), this.source = t + } + return e && (BaseImageResource.__proto__ = e), ((BaseImageResource.prototype = Object.create(e && e.prototype)).constructor = BaseImageResource).crossOrigin = function(t, e, r) { + void 0 === r && 0 !== e.indexOf("data:") ? t.crossOrigin = determineCrossOrigin(e) : !1 !== r && (t.crossOrigin = "string" == typeof r ? r : "anonymous") + }, BaseImageResource.prototype.upload = function(t, e, r, n) { + var i = t.gl, + o = e.realWidth, + a = e.realHeight; + return n = n || this.source, i.pixelStorei(i.UNPACK_PREMULTIPLY_ALPHA_WEBGL, e.premultiplyAlpha), e.target === i.TEXTURE_2D && r.width === o && r.height === a ? i.texSubImage2D(i.TEXTURE_2D, 0, 0, 0, e.format, e.type, n) : (r.width = o, r.height = a, i.texImage2D(e.target, 0, e.format, e.format, e.type, n)), !0 + }, BaseImageResource.prototype.dispose = function() { + this.source = null + }, BaseImageResource + }(Mt), + Pt = function(a) { + function ImageResource(t, e) { + if (e = e || {}, !(t instanceof HTMLImageElement)) { + var r = new Image; + a.crossOrigin(r, t, e.crossorigin), r.src = t, t = r + } + a.call(this, t), this.url = t.src, this._process = null, this.preserveBitmap = !1, this.createBitmap = !1 !== e.createBitmap && M.CREATE_IMAGE_BITMAP && !!window.createImageBitmap, this.premultiplyAlpha = !1 !== e.premultiplyAlpha, this.bitmap = null, this._load = null, !1 !== e.autoLoad && this.load() + } + return a && (ImageResource.__proto__ = a), ((ImageResource.prototype = Object.create(a && a.prototype)).constructor = ImageResource).prototype.load = function(t) { + var n = this; + return void 0 !== t && (this.createBitmap = t), this._load || (this._load = new Promise(function(t) { + n.url = n.source.src; + var e = n.source, + r = function() { + n.destroyed || (e.onload = null, e.onerror = null, n.resize(e.width, e.height), n._load = null, n.createBitmap ? t(n.process()) : t(n)) + }; + e.complete && e.src ? r() : e.onload = r + })), this._load + }, ImageResource.prototype.process = function() { + var e = this; + return null !== this._process ? this._process : null === this.bitmap && window.createImageBitmap ? (this._process = window.createImageBitmap(this.source, 0, 0, this.source.width, this.source.height, { + premultiplyAlpha: this.premultiplyAlpha ? "premultiply" : "none" + }).then(function(t) { + return e.destroyed ? Promise.reject() : (e.bitmap = t, e.update(), e._process = null, Promise.resolve(e)) + }), this._process) : Promise.resolve(this) + }, ImageResource.prototype.upload = function(t, e, r) { + if (e.premultiplyAlpha = this.premultiplyAlpha, !this.createBitmap) return a.prototype.upload.call(this, t, e, r); + if (!this.bitmap && (this.process(), !this.bitmap)) return !1; + if (a.prototype.upload.call(this, t, e, r, this.bitmap), !this.preserveBitmap) { + var n = !0; + for (var i in e._glTextures) { + var o = e._glTextures[i]; + if (o !== r && o.dirtyId !== e.dirtyId) { + n = !1; + break + } + } + n && (this.bitmap.close && this.bitmap.close(), this.bitmap = null) + } + return !0 + }, ImageResource.prototype.dispose = function() { + a.prototype.dispose.call(this), this.bitmap && (this.bitmap.close(), this.bitmap = null), this._process = null, this._load = null + }, ImageResource + }(At), + It = []; + + function autoDetectResource(t, e) { + if (!t) return null; + var r = ""; + if ("string" == typeof t) { + var n = /\.(\w{3,4})(?:$|\?|#)/i.exec(t); + n && (r = n[1].toLowerCase()) + } + for (var i = It.length - 1; 0 <= i; --i) { + var o = It[i]; + if (o.test && o.test(t, r)) return new o(t, e) + } + return new Pt(t, e) + } + var Ot = function(o) { + function BufferResource(t, e) { + var r = e || {}, + n = r.width, + i = r.height; + if (!n || !i) throw new Error("BufferResource width or height invalid"); + o.call(this, n, i), this.data = t + } + return o && (BufferResource.__proto__ = o), ((BufferResource.prototype = Object.create(o && o.prototype)).constructor = BufferResource).prototype.upload = function(t, e, r) { + var n = t.gl; + if (n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL, e.premultiplyAlpha), r.width === e.width && r.height === e.height) n.texSubImage2D(e.target, 0, 0, 0, e.width, e.height, e.format, e.type, this.data); + else { + r.width = e.width, r.height = e.height; + var i = e.format; + 2 === t.context.webGLVersion && e.type === t.gl.FLOAT && e.format === t.gl.RGBA && (i = t.gl.RGBA32F), n.texImage2D(e.target, 0, i, e.width, e.height, 0, e.format, e.type, this.data) + } + return !0 + }, BufferResource.prototype.dispose = function() { + this.data = null + }, BufferResource.test = function(t) { + return t instanceof Float32Array || t instanceof Uint8Array || t instanceof Uint32Array + }, BufferResource + }(Mt), + Ct = { + scaleMode: T.NEAREST, + format: _.RGBA, + premultiplyAlpha: !1 + }, + Rt = function(d) { + function BaseTexture(t, e) { + void 0 === t && (t = null), void 0 === e && (e = null), d.call(this); + var r = (e = e || {}).premultiplyAlpha, + n = e.mipmap, + i = e.scaleMode, + o = e.width, + a = e.height, + s = e.wrapMode, + u = e.format, + h = e.type, + c = e.target, + l = e.resolution, + f = e.resourceOptions; + !t || t instanceof Mt || ((t = autoDetectResource(t, f)).internal = !0), this.width = o || 0, this.height = a || 0, this.resolution = l || M.RESOLUTION, this.mipmap = void 0 !== n ? n : M.MIPMAP_TEXTURES, this.wrapMode = s || M.WRAP_MODE, this.scaleMode = void 0 !== i ? i : M.SCALE_MODE, this.format = u || _.RGBA, this.type = h || w.UNSIGNED_BYTE, this.target = c || b.TEXTURE_2D, this.premultiplyAlpha = !1 !== r, this.uid = uid(), this.touched = 0, this.isPowerOfTwo = !1, this._refreshPOT(), this._glTextures = {}, this.dirtyId = 0, this.dirtyStyleId = 0, this.cacheId = null, this.valid = 0 < o && 0 < a, this.textureCacheIds = [], this.destroyed = !1, this.resource = null, this.setResource(t) + } + d && (BaseTexture.__proto__ = d), (BaseTexture.prototype = Object.create(d && d.prototype)).constructor = BaseTexture; + var t = { + realWidth: { + configurable: !0 + }, + realHeight: { + configurable: !0 + } + }; + return t.realWidth.get = function() { + return this.width * this.resolution + }, t.realHeight.get = function() { + return this.height * this.resolution + }, BaseTexture.prototype.setStyle = function(t, e) { + var r; + return void 0 !== t && t !== this.scaleMode && (this.scaleMode = t, r = !0), void 0 !== e && e !== this.mipmap && (this.mipmap = e, r = !0), r && this.dirtyStyleId++, this + }, BaseTexture.prototype.setSize = function(t, e, r) { + return this.resolution = r || this.resolution, this.width = t, this.height = e, this._refreshPOT(), this.update(), this + }, BaseTexture.prototype.setRealSize = function(t, e, r) { + return this.resolution = r || this.resolution, this.width = t / this.resolution, this.height = e / this.resolution, this._refreshPOT(), this.update(), this + }, BaseTexture.prototype._refreshPOT = function() { + this.isPowerOfTwo = isPow2(this.realWidth) && isPow2(this.realHeight) + }, BaseTexture.prototype.setResolution = function(t) { + var e = this.resolution; + return e === t || (this.resolution = t, this.valid && (this.width = this.width * e / t, this.height = this.height * e / t, this.emit("update")), this._refreshPOT()), this + }, BaseTexture.prototype.setResource = function(t) { + if (this.resource === t) return this; + if (this.resource) throw new Error("Resource can be set only once"); + return t.bind(this), this.resource = t, this + }, BaseTexture.prototype.update = function() { + this.valid ? (this.dirtyId++, this.dirtyStyleId++, this.emit("update", this)) : 0 < this.width && 0 < this.height && (this.valid = !0, this.emit("loaded", this), this.emit("update", this)) + }, BaseTexture.prototype.destroy = function() { + this.resource && (this.resource.unbind(this), this.resource.internal && this.resource.destroy(), this.resource = null), this.cacheId && (delete k[this.cacheId], delete F[this.cacheId], this.cacheId = null), this.dispose(), BaseTexture.removeFromCache(this), this.textureCacheIds = null, this.destroyed = !0 + }, BaseTexture.prototype.dispose = function() { + this.emit("dispose", this) + }, BaseTexture.from = function(t, e) { + var r = null; + r = "string" == typeof t ? t : (t._pixiId || (t._pixiId = "pixiid_" + uid()), t._pixiId); + var n = k[r]; + return n || ((n = new BaseTexture(t, e)).cacheId = r, BaseTexture.addToCache(n, r)), n + }, BaseTexture.fromBuffer = function(t, e, r, n) { + t = t || new Float32Array(e * r * 4); + var i = new Ot(t, { + width: e, + height: r + }), + o = t instanceof Float32Array ? w.FLOAT : w.UNSIGNED_BYTE; + return new BaseTexture(i, Object.assign(Ct, n || { + width: e, + height: r, + type: o + })) + }, BaseTexture.addToCache = function(t, e) { + e && (-1 === t.textureCacheIds.indexOf(e) && t.textureCacheIds.push(e), k[e], k[e] = t) + }, BaseTexture.removeFromCache = function(t) { + if ("string" == typeof t) { + var e = k[t]; + if (e) { + var r = e.textureCacheIds.indexOf(t); + return -1 < r && e.textureCacheIds.splice(r, 1), delete k[t], e + } + } else if (t && t.textureCacheIds) { + for (var n = 0; n < t.textureCacheIds.length; ++n) delete k[t.textureCacheIds[n]]; + return t.textureCacheIds.length = 0, t + } + return null + }, Object.defineProperties(BaseTexture.prototype, t), BaseTexture + }(l.a), + Dt = function(s) { + function ArrayResource(t, e) { + var r; + e = e || {}; + var n = t; + Array.isArray(t) && (n = (r = t).length), s.call(this, e.width, e.height), this.items = [], this.itemDirtyIds = []; + for (var i = 0; i < n; i++) { + var o = new Rt; + this.items.push(o), this.itemDirtyIds.push(-1) + } + if (this.length = n, this._load = null, r) + for (var a = 0; a < n; a++) this.addResourceAt(autoDetectResource(r[a], e), a) + } + return s && (ArrayResource.__proto__ = s), ((ArrayResource.prototype = Object.create(s && s.prototype)).constructor = ArrayResource).prototype.dispose = function() { + for (var t = 0, e = this.length; t < e; t++) this.items[t].destroy(); + this.items = null, this.itemDirtyIds = null, this._load = null + }, ArrayResource.prototype.addResourceAt = function(t, e) { + if (!this.items[e]) throw new Error("Index " + e + " is out of bounds"); + return t.valid && !this.valid && this.resize(t.width, t.height), this.items[e].setResource(t), this + }, ArrayResource.prototype.bind = function(t) { + s.prototype.bind.call(this, t), t.target = b.TEXTURE_2D_ARRAY; + for (var e = 0; e < this.length; e++) this.items[e].on("update", t.update, t) + }, ArrayResource.prototype.unbind = function(t) { + s.prototype.unbind.call(this, t); + for (var e = 0; e < this.length; e++) this.items[e].off("update", t.update, t) + }, ArrayResource.prototype.load = function() { + var n = this; + if (this._load) return this._load; + var i = this.items.map(function(t) { + return t.resource + }), + t = i.map(function(t) { + return t.load() + }); + return this._load = Promise.all(t).then(function() { + var t = i[0], + e = t.width, + r = t.height; + return n.resize(e, r), Promise.resolve(n) + }), this._load + }, ArrayResource.prototype.upload = function(t, e, r) { + var n = this.length, + i = this.itemDirtyIds, + o = this.items, + a = t.gl; + r.dirtyId < 0 && a.texImage3D(a.TEXTURE_2D_ARRAY, 0, e.format, this._width, this._height, n, 0, e.format, e.type, null); + for (var s = 0; s < n; s++) { + var u = o[s]; + i[s] < u.dirtyId && (i[s] = u.dirtyId, u.valid && a.texSubImage3D(a.TEXTURE_2D_ARRAY, 0, 0, 0, s, u.resource.width, u.resource.height, 1, e.format, e.type, u.resource.source)) + } + return !0 + }, ArrayResource + }(Mt), + Ft = function(t) { + function CanvasResource() { + t.apply(this, arguments) + } + return t && (CanvasResource.__proto__ = t), ((CanvasResource.prototype = Object.create(t && t.prototype)).constructor = CanvasResource).test = function(t) { + return t instanceof HTMLCanvasElement + }, CanvasResource + }(At), + kt = function(n) { + function CubeResource(t, e) { + if (e = e || {}, n.call(this, t, e), this.length !== CubeResource.SIDES) throw new Error("Invalid length. Got " + this.length + ", expected 6"); + for (var r = 0; r < CubeResource.SIDES; r++) this.items[r].target = b.TEXTURE_CUBE_MAP_POSITIVE_X + r; + !1 !== e.autoLoad && this.load() + } + return n && (CubeResource.__proto__ = n), ((CubeResource.prototype = Object.create(n && n.prototype)).constructor = CubeResource).prototype.bind = function(t) { + n.prototype.bind.call(this, t), t.target = b.TEXTURE_CUBE_MAP + }, CubeResource.prototype.upload = function(t, e, r) { + for (var n = this.itemDirtyIds, i = 0; i < CubeResource.SIDES; i++) { + var o = this.items[i]; + n[i] < o.dirtyId && (n[i] = o.dirtyId, o.valid && o.resource.upload(t, o, r)) + } + return !0 + }, CubeResource + }(Dt); + kt.SIDES = 6; + var Lt = function(r) { + function SVGResource(t, e) { + e = e || {}, r.call(this, document.createElement("canvas")), this.svg = t, this.scale = e.scale || 1, this._resolve = null, this._load = null, !1 !== e.autoLoad && this.load() + } + return r && (SVGResource.__proto__ = r), ((SVGResource.prototype = Object.create(r && r.prototype)).constructor = SVGResource).prototype.load = function() { + var r = this; + return this._load || (this._load = new Promise(function(t) { + r._resolve = function() { + r.resize(r.source.width, r.source.height), t(r) + }, /^\]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*>/i; + var Nt = function(c) { + function VideoResource(t, e) { + if (e = e || {}, !(t instanceof HTMLVideoElement)) { + var r = document.createElement("video"); + r.setAttribute("webkit-playsinline", ""), r.setAttribute("playsinline", ""), "string" == typeof t && (t = [t]), c.crossOrigin(r, t[0].src || t[0], e.crossorigin); + for (var n = 0; n < t.length; ++n) { + var i = document.createElement("source"), + o = t[n], + a = o.src, + s = o.mime, + u = (a = a || t[n]).split("?").shift().toLowerCase(), + h = u.substr(u.lastIndexOf(".") + 1); + s = s || "video/" + h, i.src = a, i.type = s, r.appendChild(i) + } + t = r + } + c.call(this, t), this._autoUpdate = !0, this._isAutoUpdating = !1, this._updateFPS = e.updateFPS || 0, this._msToNextUpdate = 0, this.autoPlay = !1 !== e.autoPlay, this._load = null, this._resolve = null, this._onCanPlay = this._onCanPlay.bind(this), !1 !== e.autoLoad && this.load() + } + c && (VideoResource.__proto__ = c); + var t = { + autoUpdate: { + configurable: !0 + }, + updateFPS: { + configurable: !0 + } + }; + return ((VideoResource.prototype = Object.create(c && c.prototype)).constructor = VideoResource).prototype.update = function(t) { + if (void 0 === t && (t = 0), !this.destroyed) { + var e = xt.shared.elapsedMS * this.source.playbackRate; + this._msToNextUpdate = Math.floor(this._msToNextUpdate - e), (!this._updateFPS || this._msToNextUpdate <= 0) && (c.prototype.update.call(this, t), this._msToNextUpdate = this._updateFPS ? Math.floor(1e3 / this._updateFPS) : 0) + } + }, VideoResource.prototype.load = function() { + var e = this; + if (this._load) return this._load; + var r = this.source; + return (r.readyState === r.HAVE_ENOUGH_DATA || r.readyState === r.HAVE_FUTURE_DATA) && r.width && r.height && (r.complete = !0), r.addEventListener("play", this._onPlayStart.bind(this)), r.addEventListener("pause", this._onPlayStop.bind(this)), this._isSourceReady() ? this._onCanPlay() : (r.addEventListener("canplay", this._onCanPlay), r.addEventListener("canplaythrough", this._onCanPlay)), this._load = new Promise(function(t) { + e.valid ? t(e) : (e._resolve = t, r.load()) + }), this._load + }, VideoResource.prototype._isSourcePlaying = function() { + var t = this.source; + return 0 < t.currentTime && !1 === t.paused && !1 === t.ended && 2 < t.readyState + }, VideoResource.prototype._isSourceReady = function() { + return 3 === this.source.readyState || 4 === this.source.readyState + }, VideoResource.prototype._onPlayStart = function() { + this.valid || this._onCanPlay(), !this._isAutoUpdating && this.autoUpdate && (xt.shared.add(this.update, this), this._isAutoUpdating = !0) + }, VideoResource.prototype._onPlayStop = function() { + this._isAutoUpdating && (xt.shared.remove(this.update, this), this._isAutoUpdating = !1) + }, VideoResource.prototype._onCanPlay = function() { + var t = this.source; + t.removeEventListener("canplay", this._onCanPlay), t.removeEventListener("canplaythrough", this._onCanPlay); + var e = this.valid; + this.resize(t.videoWidth, t.videoHeight), !e && this._resolve && (this._resolve(this), this._resolve = null), this._isSourcePlaying() ? this._onPlayStart() : this.autoPlay && t.play() + }, VideoResource.prototype.dispose = function() { + this._isAutoUpdating && xt.shared.remove(this.update, this), this.source && (this.source.pause(), this.source.src = "", this.source.load()), c.prototype.dispose.call(this) + }, t.autoUpdate.get = function() { + return this._autoUpdate + }, t.autoUpdate.set = function(t) { + t !== this._autoUpdate && (this._autoUpdate = t, !this._autoUpdate && this._isAutoUpdating ? (xt.shared.remove(this.update, this), this._isAutoUpdating = !1) : this._autoUpdate && !this._isAutoUpdating && (xt.shared.add(this.update, this), this._isAutoUpdating = !0)) + }, t.updateFPS.get = function() { + return this._updateFPS + }, t.updateFPS.set = function(t) { + t !== this._updateFPS && (this._updateFPS = t) + }, VideoResource.test = function(t, e) { + return t instanceof HTMLVideoElement || -1 < VideoResource.TYPES.indexOf(e) + }, Object.defineProperties(VideoResource.prototype, t), VideoResource + }(At); + Nt.TYPES = ["mp4", "m4v", "webm", "ogg", "ogv", "h264", "avi", "mov"], It.push(Pt, Ft, Nt, Lt, Ot, kt, Dt); + var Bt = { + INSTALLED: It, + autoDetectResource: autoDetectResource, + ArrayResource: Dt, + BufferResource: Ot, + CanvasResource: Ft, + CubeResource: kt, + ImageResource: Pt, + SVGResource: Lt, + VideoResource: Nt, + Resource: Mt, + BaseImageResource: At + }, + Ut = function(t) { + this.renderer = t, this.renderer.runners.contextChange.add(this) + }; + Ut.prototype.contextChange = function(t) {}, Ut.prototype.destroy = function() { + this.renderer.runners.contextChange.remove(this), this.renderer = null + }; + var Gt = function(t) { + function DepthResource() { + t.apply(this, arguments) + } + return t && (DepthResource.__proto__ = t), ((DepthResource.prototype = Object.create(t && t.prototype)).constructor = DepthResource).prototype.upload = function(t, e, r) { + var n = t.gl; + return n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL, e.premultiplyAlpha), r.width === e.width && r.height === e.height ? n.texSubImage2D(e.target, 0, 0, 0, e.width, e.height, e.format, e.type, this.data) : (r.width = e.width, r.height = e.height, n.texImage2D(e.target, 0, n.DEPTH_COMPONENT16, e.width, e.height, 0, e.format, e.type, this.data)), !0 + }, DepthResource + }(Ot), + jt = function(t, e) { + this.width = Math.ceil(t || 100), this.height = Math.ceil(e || 100), this.stencil = !1, this.depth = !1, this.dirtyId = 0, this.dirtyFormat = 0, this.dirtySize = 0, this.depthTexture = null, this.colorTextures = [], this.glFramebuffers = {}, this.disposeRunner = new gt("disposeFramebuffer", 2) + }, + zt = { + colorTexture: { + configurable: !0 + } + }; + zt.colorTexture.get = function() { + return this.colorTextures[0] + }, jt.prototype.addColorTexture = function(t, e) { + return void 0 === t && (t = 0), this.colorTextures[t] = e || new Rt(null, { + scaleMode: 0, + resolution: 1, + mipmap: !1, + width: this.width, + height: this.height + }), this.dirtyId++, this.dirtyFormat++, this + }, jt.prototype.addDepthTexture = function(t) { + return this.depthTexture = t || new Rt(new Gt(null, { + width: this.width, + height: this.height + }), { + scaleMode: 0, + resolution: 1, + width: this.width, + height: this.height, + mipmap: !1, + format: _.DEPTH_COMPONENT, + type: w.UNSIGNED_SHORT + }), this.dirtyId++, this.dirtyFormat++, this + }, jt.prototype.enableDepth = function() { + return this.depth = !0, this.dirtyId++, this.dirtyFormat++, this + }, jt.prototype.enableStencil = function() { + return this.stencil = !0, this.dirtyId++, this.dirtyFormat++, this + }, jt.prototype.resize = function(t, e) { + if (t = Math.ceil(t), e = Math.ceil(e), t !== this.width || e !== this.height) { + this.width = t, this.height = e, this.dirtyId++, this.dirtySize++; + for (var r = 0; r < this.colorTextures.length; r++) { + var n = this.colorTextures[r], + i = n.resolution; + n.setSize(t / i, e / i) + } + if (this.depthTexture) { + var o = this.depthTexture.resolution; + this.depthTexture.setSize(t / o, e / o) + } + } + }, jt.prototype.dispose = function() { + this.disposeRunner.run(this, !1) + }, Object.defineProperties(jt.prototype, zt); + var Xt = function(i) { + function BaseRenderTexture(t) { + "number" == typeof t && (t = { + width: arguments[0], + height: arguments[1], + scaleMode: arguments[2], + resolution: arguments[3] + }); + i.call(this, null, t); + var e = t || {}, + r = e.width, + n = e.height; + this.mipmap = !1, this.width = Math.ceil(r) || 100, this.height = Math.ceil(n) || 100, this.valid = !0, this._canvasRenderTarget = null, this.clearColor = [0, 0, 0, 0], this.framebuffer = new jt(this.width * this.resolution, this.height * this.resolution).addColorTexture(0, this).enableStencil(), this.stencilMaskStack = [], this.filterStack = [{}] + } + return i && (BaseRenderTexture.__proto__ = i), ((BaseRenderTexture.prototype = Object.create(i && i.prototype)).constructor = BaseRenderTexture).prototype.resize = function(t, e) { + t = Math.ceil(t), e = Math.ceil(e), this.framebuffer.resize(t * this.resolution, e * this.resolution) + }, BaseRenderTexture.prototype.dispose = function() { + this.framebuffer.dispose(), i.prototype.dispose.call(this) + }, BaseRenderTexture.prototype.destroy = function() { + i.prototype.destroy.call(this, !0), this.framebuffer = null, this.renderer = null + }, BaseRenderTexture + }(Rt), + Vt = function() { + this.x0 = 0, this.y0 = 0, this.x1 = 1, this.y1 = 0, this.x2 = 1, this.y2 = 1, this.x3 = 0, this.y3 = 1, this.uvsFloat32 = new Float32Array(8) + }; + Vt.prototype.set = function(t, e, r) { + var n = e.width, + i = e.height; + if (r) { + var o = t.width / 2 / n, + a = t.height / 2 / i, + s = t.x / n + o, + u = t.y / i + a; + r = rt.add(r, rt.NW), this.x0 = s + o * rt.uX(r), this.y0 = u + a * rt.uY(r), r = rt.add(r, 2), this.x1 = s + o * rt.uX(r), this.y1 = u + a * rt.uY(r), r = rt.add(r, 2), this.x2 = s + o * rt.uX(r), this.y2 = u + a * rt.uY(r), r = rt.add(r, 2), this.x3 = s + o * rt.uX(r), this.y3 = u + a * rt.uY(r) + } else this.x0 = t.x / n, this.y0 = t.y / i, this.x1 = (t.x + t.width) / n, this.y1 = t.y / i, this.x2 = (t.x + t.width) / n, this.y2 = (t.y + t.height) / i, this.x3 = t.x / n, this.y3 = (t.y + t.height) / i; + this.uvsFloat32[0] = this.x0, this.uvsFloat32[1] = this.y0, this.uvsFloat32[2] = this.x1, this.uvsFloat32[3] = this.y1, this.uvsFloat32[4] = this.x2, this.uvsFloat32[5] = this.y2, this.uvsFloat32[6] = this.x3, this.uvsFloat32[7] = this.y3 + }; + var qt = new Vt, + Ht = function(a) { + function Texture(t, e, r, n, i, o) { + if (a.call(this), this.noFrame = !1, e || (this.noFrame = !0, e = new ot(0, 0, 1, 1)), t instanceof Texture && (t = t.baseTexture), this.baseTexture = t, this._frame = e, this.trim = n, this.valid = !1, this.requiresUpdate = !1, this._uvs = qt, this.uvMatrix = null, this.orig = r || e, this._rotate = Number(i || 0), !0 === i) this._rotate = 2; + else if (this._rotate % 2 != 0) throw new Error("attempt to use diamond-shaped UVs. If you are sure, set rotation manually"); + t.valid ? (this.noFrame && (e = new ot(0, 0, t.width, t.height), t.on("update", this.onBaseTextureUpdated, this)), this.frame = e) : t.once("loaded", this.onBaseTextureUpdated, this), this.defaultAnchor = o ? new q(o.x, o.y) : new q(0, 0), this._updateID = 0, this.textureCacheIds = [] + } + a && (Texture.__proto__ = a); + var t = { + frame: { + configurable: !0 + }, + rotate: { + configurable: !0 + }, + width: { + configurable: !0 + }, + height: { + configurable: !0 + } + }; + return ((Texture.prototype = Object.create(a && a.prototype)).constructor = Texture).prototype.update = function() { + this.baseTexture.update() + }, Texture.prototype.onBaseTextureUpdated = function(t) { + this._updateID++, this.noFrame ? this.frame = new ot(0, 0, t.width, t.height) : this.frame = this._frame, this.emit("update", this) + }, Texture.prototype.destroy = function(t) { + this.baseTexture && (t && (F[this.baseTexture.imageUrl] && Texture.removeFromCache(this.baseTexture.imageUrl), this.baseTexture.destroy()), this.baseTexture.off("update", this.onBaseTextureUpdated, this), this.baseTexture = null), this._frame = null, this._uvs = null, this.trim = null, this.orig = null, this.valid = !1, Texture.removeFromCache(this), this.textureCacheIds = null + }, Texture.prototype.clone = function() { + return new Texture(this.baseTexture, this.frame, this.orig, this.trim, this.rotate, this.defaultAnchor) + }, Texture.prototype.updateUvs = function() { + this._uvs === qt && (this._uvs = new Vt), this._uvs.set(this._frame, this.baseTexture, this.rotate), this._updateID++ + }, Texture.from = function(t, e) { + void 0 === e && (e = {}); + var r = null; + r = "string" == typeof t ? t : (t._pixiId || (t._pixiId = "pixiid_" + uid()), t._pixiId); + var n = F[r]; + return n || (e.resolution || (e.resolution = getResolutionOfUrl(t)), (n = new Texture(new Rt(t, e))).baseTexture.cacheId = r, Rt.addToCache(n.baseTexture, r), Texture.addToCache(n, r)), n + }, Texture.fromBuffer = function(t, e, r, n) { + return new Texture(Rt.fromBuffer(t, e, r, n)) + }, Texture.fromLoader = function(t, e, r) { + var n = new Pt(t); + n.url = e; + var i = new Texture(new Rt(n, { + scaleMode: M.SCALE_MODE, + resolution: getResolutionOfUrl(e) + })); + return r || (r = e), Rt.addToCache(i.baseTexture, r), Texture.addToCache(i, r), r !== e && (Rt.addToCache(i.baseTexture, e), Texture.addToCache(i, e)), i + }, Texture.addToCache = function(t, e) { + e && (-1 === t.textureCacheIds.indexOf(e) && t.textureCacheIds.push(e), F[e], F[e] = t) + }, Texture.removeFromCache = function(t) { + if ("string" == typeof t) { + var e = F[t]; + if (e) { + var r = e.textureCacheIds.indexOf(t); + return -1 < r && e.textureCacheIds.splice(r, 1), delete F[t], e + } + } else if (t && t.textureCacheIds) { + for (var n = 0; n < t.textureCacheIds.length; ++n) F[t.textureCacheIds[n]] === t && delete F[t.textureCacheIds[n]]; + return t.textureCacheIds.length = 0, t + } + return null + }, t.frame.get = function() { + return this._frame + }, t.frame.set = function(t) { + this._frame = t, this.noFrame = !1; + var e = t.x, + r = t.y, + n = t.width, + i = t.height, + o = e + n > this.baseTexture.width, + a = r + i > this.baseTexture.height; + if (o || a) { + var s = o && a ? "and" : "or", + u = "X: " + e + " + " + n + " = " + (e + n) + " > " + this.baseTexture.width, + h = "Y: " + r + " + " + i + " = " + (r + i) + " > " + this.baseTexture.height; + throw new Error("Texture Error: frame does not fit inside the base Texture dimensions: " + u + " " + s + " " + h) + } + this.valid = n && i && this.baseTexture.valid, this.trim || this.rotate || (this.orig = t), this.valid && this.updateUvs() + }, t.rotate.get = function() { + return this._rotate + }, t.rotate.set = function(t) { + this._rotate = t, this.valid && this.updateUvs() + }, t.width.get = function() { + return this.orig.width + }, t.height.get = function() { + return this.orig.height + }, Object.defineProperties(Texture.prototype, t), Texture + }(l.a); + + function removeAllHandlers(t) { + t.destroy = function() {}, t.on = function() {}, t.once = function() {}, t.emit = function() {} + } + Ht.EMPTY = new Ht(new Rt), removeAllHandlers(Ht.EMPTY), removeAllHandlers(Ht.EMPTY.baseTexture), Ht.WHITE = function() { + var t = document.createElement("canvas"); + t.width = 16, t.height = 16; + var e = t.getContext("2d"); + return e.fillStyle = "white", e.fillRect(0, 0, 16, 16), new Ht(new Rt(new Ft(t))) + }(), removeAllHandlers(Ht.WHITE), removeAllHandlers(Ht.WHITE.baseTexture); + var Wt = function(s) { + function RenderTexture(t, e) { + var r = null; + if (!(t instanceof Xt)) { + var n = arguments[1], + i = arguments[2], + o = arguments[3], + a = arguments[4]; + r = arguments[0], e = null, t = new Xt({ + width: n, + height: i, + scaleMode: o, + resolution: a + }) + } + s.call(this, t, e), this.legacyRenderer = r, this.valid = !0, this.filterFrame = null, this.filterPoolKey = null, this.updateUvs() + } + return s && (RenderTexture.__proto__ = s), ((RenderTexture.prototype = Object.create(s && s.prototype)).constructor = RenderTexture).prototype.resize = function(t, e, r) { + void 0 === r && (r = !0), t = Math.ceil(t), e = Math.ceil(e), this.valid = 0 < t && 0 < e, this._frame.width = this.orig.width = t, this._frame.height = this.orig.height = e, r && this.baseTexture.resize(t, e), this.updateUvs() + }, RenderTexture.create = function(t) { + return "number" == typeof t && (t = { + width: t, + height: arguments[1], + scaleMode: arguments[2], + resolution: arguments[3] + }), new RenderTexture(new Xt(t)) + }, RenderTexture + }(Ht), + Yt = function(t, e, r, n, i, o, a) { + void 0 === r && (r = !1), void 0 === n && (n = 5126), this.buffer = t, this.size = e, this.normalized = r, this.type = n, this.stride = i, this.start = o, this.instance = a + }; + Yt.prototype.destroy = function() { + this.buffer = null + }, Yt.from = function(t, e, r, n, i) { + return new Yt(t, e, r, n, i) + }; + var Kt = 0, + Zt = function(t, e, r) { + void 0 === e && (e = !0), void 0 === r && (r = !1), this.data = t || new Float32Array(1), this._glBuffers = {}, this._updateID = 0, this.index = r, this.static = e, this.id = Kt++, this.disposeRunner = new gt("disposeBuffer", 2) + }; + + function getBufferType(t) { + if (4 === t.BYTES_PER_ELEMENT) return t instanceof Float32Array ? "Float32Array" : t instanceof Uint32Array ? "Uint32Array" : "Int32Array"; + if (2 === t.BYTES_PER_ELEMENT) { + if (t instanceof Uint16Array) return "Uint16Array" + } else if (1 === t.BYTES_PER_ELEMENT && t instanceof Uint8Array) return "Uint8Array"; + return null + } + Zt.prototype.update = function(t) { + this.data = t || this.data, this._updateID++ + }, Zt.prototype.dispose = function() { + this.disposeRunner.run(this, !1) + }, Zt.prototype.destroy = function() { + this.dispose(), this.data = null + }, Zt.from = function(t) { + return t instanceof Array && (t = new Float32Array(t)), new Zt(t) + }; + var Qt = { + Float32Array: Float32Array, + Uint32Array: Uint32Array, + Int32Array: Int32Array, + Uint8Array: Uint8Array + }; + var Jt = { + 5126: 4, + 5123: 2, + 5121: 1 + }, + $t = 0, + te = { + Float32Array: Float32Array, + Uint32Array: Uint32Array, + Int32Array: Int32Array, + Uint8Array: Uint8Array, + Uint16Array: Uint16Array + }, + ee = function(t, e) { + void 0 === t && (t = []), void 0 === e && (e = {}), this.buffers = t, this.indexBuffer = null, this.attributes = e, this.glVertexArrayObjects = {}, this.id = $t++, this.instanced = !1, this.instanceCount = 1, this._size = null, this.disposeRunner = new gt("disposeGeometry", 2), this.refCount = 0 + }; + ee.prototype.addAttribute = function(t, e, r, n, i, o, a, s) { + if (void 0 === n && (n = !1), void 0 === s && (s = !1), !e) throw new Error("You must pass a buffer when creating an attribute"); + e.data || (e instanceof Array && (e = new Float32Array(e)), e = new Zt(e)); + var u = t.split("|"); + if (1 < u.length) { + for (var h = 0; h < u.length; h++) this.addAttribute(u[h], e, r, n, i); + return this + } + var c = this.buffers.indexOf(e); + return -1 === c && (this.buffers.push(e), c = this.buffers.length - 1), this.attributes[t] = new Yt(c, r, n, i, o, a, s), this.instanced = this.instanced || s, this + }, ee.prototype.getAttribute = function(t) { + return this.buffers[this.attributes[t].buffer] + }, ee.prototype.addIndex = function(t) { + return t.data || (t instanceof Array && (t = new Uint16Array(t)), t = new Zt(t)), t.index = !0, this.indexBuffer = t, -1 === this.buffers.indexOf(t) && this.buffers.push(t), this + }, ee.prototype.getIndex = function() { + return this.indexBuffer + }, ee.prototype.interleave = function() { + if (1 === this.buffers.length || 2 === this.buffers.length && this.indexBuffer) return this; + var t, e = [], + r = [], + n = new Zt; + for (t in this.attributes) { + var i = this.attributes[t], + o = this.buffers[i.buffer]; + e.push(o.data), r.push(i.size * Jt[i.type] / 4), i.buffer = 0 + } + for (n.data = function(t, e) { + for (var r = 0, n = 0, i = {}, o = 0; o < t.length; o++) n += e[o], r += t[o].length; + for (var a = new ArrayBuffer(4 * r), s = null, u = 0, h = 0; h < t.length; h++) { + var c = e[h], + l = t[h], + f = getBufferType(l); + i[f] || (i[f] = new Qt[f](a)), s = i[f]; + for (var d = 0; d < l.length; d++) s[(d / c | 0) * n + u + d % c] = l[d]; + u += c + } + return new Float32Array(a) + }(e, r), t = 0; t < this.buffers.length; t++) this.buffers[t] !== this.indexBuffer && this.buffers[t].destroy(); + return this.buffers = [n], this.indexBuffer && this.buffers.push(this.indexBuffer), this + }, ee.prototype.getSize = function() { + for (var t in this.attributes) { + var e = this.attributes[t]; + return this.buffers[e.buffer].data.length / (e.stride / 4 || e.size) + } + return 0 + }, ee.prototype.dispose = function() { + this.disposeRunner.run(this, !1) + }, ee.prototype.destroy = function() { + this.dispose(), this.buffers = null, this.indexBuffer.destroy(), this.attributes = null + }, ee.prototype.clone = function() { + for (var t = new ee, e = 0; e < this.buffers.length; e++) t.buffers[e] = new Zt(this.buffers[e].data.slice()); + for (var r in this.attributes) { + var n = this.attributes[r]; + t.attributes[r] = new Yt(n.buffer, n.size, n.normalized, n.type, n.stride, n.start, n.instance) + } + return this.indexBuffer && (t.indexBuffer = t.buffers[this.buffers.indexOf(this.indexBuffer)], t.indexBuffer.index = !0), t + }, ee.merge = function(t) { + for (var e, r = new ee, n = [], i = [], o = [], a = 0; a < t.length; a++) { + e = t[a]; + for (var s = 0; s < e.buffers.length; s++) i[s] = i[s] || 0, i[s] += e.buffers[s].data.length, o[s] = 0 + } + for (var u = 0; u < e.buffers.length; u++) n[u] = new(te[getBufferType(e.buffers[u].data)])(i[u]), r.buffers[u] = new Zt(n[u]); + for (var h = 0; h < t.length; h++) { + e = t[h]; + for (var c = 0; c < e.buffers.length; c++) n[c].set(e.buffers[c].data, o[c]), o[c] += e.buffers[c].data.length + } + if (r.attributes = e.attributes, e.indexBuffer) { + r.indexBuffer = r.buffers[e.buffers.indexOf(e.indexBuffer)], r.indexBuffer.index = !0; + for (var l = 0, f = 0, d = 0, p = 0, m = 0; m < e.buffers.length; m++) + if (e.buffers[m] !== e.indexBuffer) { + p = m; + break + } + for (var v in e.attributes) { + var g = e.attributes[v]; + (0 | g.buffer) === p && (f += g.size * Jt[g.type] / 4) + } + for (var y = 0; y < t.length; y++) { + for (var _ = t[y].indexBuffer.data, b = 0; b < _.length; b++) r.indexBuffer.data[b + d] += l; + l += e.buffers[p].data.length / f, d += _.length + } + } + return r + }; + var re = function(t) { + function Quad() { + t.call(this), this.addAttribute("aVertexPosition", [0, 0, 1, 0, 1, 1, 0, 1]).addIndex([0, 1, 3, 2]) + } + return t && (Quad.__proto__ = t), (Quad.prototype = Object.create(t && t.prototype)).constructor = Quad + }(ee), + ne = function(t) { + function QuadUv() { + t.call(this), this.vertices = new Float32Array([-1, -1, 1, -1, 1, 1, -1, 1]), this.uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), this.vertexBuffer = new Zt(this.vertices), this.uvBuffer = new Zt(this.uvs), this.addAttribute("aVertexPosition", this.vertexBuffer).addAttribute("aTextureCoord", this.uvBuffer).addIndex([0, 1, 2, 0, 2, 3]) + } + return t && (QuadUv.__proto__ = t), ((QuadUv.prototype = Object.create(t && t.prototype)).constructor = QuadUv).prototype.map = function(t, e) { + var r = 0, + n = 0; + return this.uvs[0] = r, this.uvs[1] = n, this.uvs[2] = r + e.width / t.width, this.uvs[3] = n, this.uvs[4] = r + e.width / t.width, this.uvs[5] = n + e.height / t.height, this.uvs[6] = r, this.uvs[7] = n + e.height / t.height, r = e.x, n = e.y, this.vertices[0] = r, this.vertices[1] = n, this.vertices[2] = r + e.width, this.vertices[3] = n, this.vertices[4] = r + e.width, this.vertices[5] = n + e.height, this.vertices[6] = r, this.vertices[7] = n + e.height, this.invalidate(), this + }, QuadUv.prototype.invalidate = function() { + return this.vertexBuffer._updateID++, this.uvBuffer._updateID++, this + }, QuadUv + }(ee); + var ie = 0, + oe = function(t, e) { + this.uniforms = t, this.group = !0, this.syncUniforms = {}, this.dirtyId = 0, this.id = ie++, this.static = !!e + }; + oe.prototype.update = function() { + this.dirtyId++ + }, oe.prototype.add = function(t, e, r) { + this.uniforms[t] = new oe(e, r) + }, oe.from = function(t, e) { + return new oe(t, e) + }; + var ae = function() { + this.renderTexture = null, this.target = null, this.legacy = !1, this.resolution = 1, this.sourceFrame = new ot, this.destinationFrame = new ot, this.filters = [] + }; + ae.prototype.clear = function() { + this.target = null, this.filters = null, this.renderTexture = null + }; + var se = "screen", + ue = function(e) { + function FilterSystem(t) { + e.call(this, t), this.defaultFilterStack = [{}], this.texturePool = {}, this.statePool = [], this.quad = new re, this.quadUv = new ne, this.tempRect = new ot, this.activeState = {}, this.globalUniforms = new oe({ + outputFrame: this.tempRect, + inputSize: new Float32Array(4), + inputPixel: new Float32Array(4), + inputClamp: new Float32Array(4), + resolution: 1, + filterArea: new Float32Array(4), + filterClamp: new Float32Array(4) + }, !0), this._pixelsWidth = t.view.width, this._pixelsHeight = t.view.height + } + return e && (FilterSystem.__proto__ = e), ((FilterSystem.prototype = Object.create(e && e.prototype)).constructor = FilterSystem).prototype.push = function(t, e) { + for (var r = this.renderer, n = this.defaultFilterStack, i = this.statePool.pop() || new ae, o = e[0].resolution, a = e[0].padding, s = e[0].autoFit, u = e[0].legacy, h = 1; h < e.length; h++) { + var c = e[h]; + o = Math.min(o, c.resolution), a = Math.max(a, c.padding), s = s || c.autoFit, u = u || c.legacy + } + 1 === n.length && (this.defaultFilterStack[0].renderTexture = r.renderTexture.current), n.push(i), i.resolution = o, i.legacy = u, i.target = t, i.sourceFrame.copyFrom(t.filterArea || t.getBounds(!0)), i.sourceFrame.pad(a), s && i.sourceFrame.fit(this.renderer.renderTexture.sourceFrame), i.sourceFrame.ceil(o), i.renderTexture = this.getOptimalFilterTexture(i.sourceFrame.width, i.sourceFrame.height, o), i.filters = e, i.destinationFrame.width = i.renderTexture.width, i.destinationFrame.height = i.renderTexture.height, i.renderTexture.filterFrame = i.sourceFrame, r.renderTexture.bind(i.renderTexture, i.sourceFrame), r.renderTexture.clear() + }, FilterSystem.prototype.pop = function() { + var t = this.defaultFilterStack, + e = t.pop(), + r = e.filters; + this.activeState = e; + var n = this.globalUniforms.uniforms; + n.outputFrame = e.sourceFrame, n.resolution = e.resolution; + var i = n.inputSize, + o = n.inputPixel, + a = n.inputClamp; + if (i[0] = e.destinationFrame.width, i[1] = e.destinationFrame.height, i[2] = 1 / i[0], i[3] = 1 / i[1], o[0] = i[0] * e.resolution, o[1] = i[1] * e.resolution, o[2] = 1 / o[0], o[3] = 1 / o[1], a[0] = .5 * o[2], a[1] = .5 * o[3], a[2] = e.sourceFrame.width * i[2] - .5 * o[2], a[3] = e.sourceFrame.height * i[3] - .5 * o[3], e.legacy) { + var s = n.filterArea; + s[0] = e.destinationFrame.width, s[1] = e.destinationFrame.height, s[2] = e.sourceFrame.x, s[3] = e.sourceFrame.y, n.filterClamp = n.inputClamp + } + this.globalUniforms.update(); + var u = t[t.length - 1]; + if (1 === r.length) r[0].apply(this, e.renderTexture, u.renderTexture, !1, e), this.returnFilterTexture(e.renderTexture); + else { + var h = e.renderTexture, + c = this.getOptimalFilterTexture(h.width, h.height, e.resolution); + c.filterFrame = h.filterFrame; + var l = 0; + for (l = 0; l < r.length - 1; ++l) { + r[l].apply(this, h, c, !0, e); + var f = h; + h = c, c = f + } + r[l].apply(this, h, u.renderTexture, !1, e), this.returnFilterTexture(h), this.returnFilterTexture(c) + } + e.clear(), this.statePool.push(e) + }, FilterSystem.prototype.applyFilter = function(t, e, r, n) { + var i = this.renderer; + i.renderTexture.bind(r, r ? r.filterFrame : null), n && i.renderTexture.clear(), t.uniforms.uSampler = e, t.uniforms.filterGlobals = this.globalUniforms, i.state.setState(t.state), i.shader.bind(t), t.legacy ? (this.quadUv.map(e._frame, e.filterFrame), i.geometry.bind(this.quadUv), i.geometry.draw(x.TRIANGLES)) : (i.geometry.bind(this.quad), i.geometry.draw(x.TRIANGLE_STRIP)) + }, FilterSystem.prototype.calculateScreenSpaceMatrix = function(t) { + var e, r, n, i, o = this.activeState; + return e = t, r = o.sourceFrame, n = o.destinationFrame, (i = e.identity()).translate(r.x / n.width, r.y / n.height), i.scale(n.width, n.height), i + }, FilterSystem.prototype.calculateSpriteMatrix = function(t, e) { + var r, n, i, o, a, s, u, h = this.activeState; + return r = t, n = h.sourceFrame, i = h.destinationFrame, a = (o = e)._texture.orig, s = r.set(i.width, 0, 0, i.height, n.x, n.y), (u = o.worldTransform.copyTo(Y.TEMP_MATRIX)).invert(), s.prepend(u), s.scale(1 / a.width, 1 / a.height), s.translate(o.anchor.x, o.anchor.y), s + }, FilterSystem.prototype.destroy = function(t) { + void 0 === t && (t = !1), t ? this.texturePool = {} : this.emptyPool() + }, FilterSystem.prototype.getOptimalFilterTexture = function(t, e, r) { + void 0 === r && (r = 1); + var n = se; + e *= r, (t *= r) === this._pixelsWidth && e === this._pixelsHeight || (n = (65535 & (t = nextPow2(t))) << 16 | 65535 & (e = nextPow2(e))), this.texturePool[n] || (this.texturePool[n] = []); + var i = this.texturePool[n].pop(); + return i || (i = Wt.create({ + width: t / r, + height: e / r, + resolution: r + })), i.filterPoolKey = n, i + }, FilterSystem.prototype.getFilterTexture = function(t) { + var e = this.activeState.renderTexture, + r = this.getOptimalFilterTexture(e.width, e.height, t || e.baseTexture.resolution); + return r.filterFrame = e.filterFrame, r + }, FilterSystem.prototype.returnFilterTexture = function(t) { + var e = t.filterPoolKey; + t.filterFrame = null, this.texturePool[e].push(t) + }, FilterSystem.prototype.emptyPool = function() { + for (var t in this.texturePool) { + var e = this.texturePool[t]; + if (e) + for (var r = 0; r < e.length; r++) e[r].destroy(!0) + } + this.texturePool = {} + }, FilterSystem.prototype.resize = function() { + var t = this.texturePool[se]; + if (t) + for (var e = 0; e < t.length; e++) t[e].destroy(!0); + this.texturePool[se] = [], this._pixelsWidth = this.renderer.view.width, this._pixelsHeight = this.renderer.view.height + }, FilterSystem + }(Ut), + he = function(t) { + function ObjectRenderer() { + t.apply(this, arguments) + } + return t && (ObjectRenderer.__proto__ = t), ((ObjectRenderer.prototype = Object.create(t && t.prototype)).constructor = ObjectRenderer).prototype.start = function() {}, ObjectRenderer.prototype.stop = function() { + this.flush() + }, ObjectRenderer.prototype.flush = function() {}, ObjectRenderer.prototype.render = function(t) {}, ObjectRenderer + }(Ut), + ce = function(e) { + function BatchSystem(t) { + e.call(this, t), this.emptyRenderer = new he(t), this.currentRenderer = this.emptyRenderer + } + return e && (BatchSystem.__proto__ = e), ((BatchSystem.prototype = Object.create(e && e.prototype)).constructor = BatchSystem).prototype.setObjectRenderer = function(t) { + this.currentRenderer !== t && (this.currentRenderer.stop(), this.currentRenderer = t, this.currentRenderer.start()) + }, BatchSystem.prototype.flush = function() { + this.setObjectRenderer(this.emptyRenderer) + }, BatchSystem.prototype.reset = function() { + this.setObjectRenderer(this.emptyRenderer) + }, BatchSystem + }(Ut); + M.PREFER_ENV = h.a.any ? v.WEBGL : v.WEBGL2; + var le = 0, + fe = function(e) { + function ContextSystem(t) { + e.call(this, t), this.webGLVersion = 1, this.extensions = {}, this.handleContextLost = this.handleContextLost.bind(this), this.handleContextRestored = this.handleContextRestored.bind(this), t.view.addEventListener("webglcontextlost", this.handleContextLost, !1), t.view.addEventListener("webglcontextrestored", this.handleContextRestored, !1) + } + e && (ContextSystem.__proto__ = e), (ContextSystem.prototype = Object.create(e && e.prototype)).constructor = ContextSystem; + var t = { + isLost: { + configurable: !0 + } + }; + return t.isLost.get = function() { + return !this.gl || this.gl.isContextLost() + }, ContextSystem.prototype.contextChange = function(t) { + (this.gl = t).isContextLost() && t.getExtension("WEBGL_lose_context") && t.getExtension("WEBGL_lose_context").restoreContext() + }, ContextSystem.prototype.initFromContext = function(t) { + this.gl = t, this.validateContext(t), this.renderer.gl = t, this.renderer.CONTEXT_UID = le++, this.renderer.runners.contextChange.run(t) + }, ContextSystem.prototype.initFromOptions = function(t) { + var e = this.createContext(this.renderer.view, t); + this.initFromContext(e) + }, ContextSystem.prototype.createContext = function(t, e) { + var r; + if (M.PREFER_ENV >= v.WEBGL2 && (r = t.getContext("webgl2", e)), r) this.webGLVersion = 2; + else if (this.webGLVersion = 1, !(r = t.getContext("webgl", e) || t.getContext("experimental-webgl", e))) throw new Error("This browser does not support WebGL. Try using the canvas renderer"); + return this.gl = r, this.getExtensions(), r + }, ContextSystem.prototype.getExtensions = function() { + var t = this.gl; + 1 === this.webGLVersion && Object.assign(this.extensions, { + drawBuffers: t.getExtension("WEBGL_draw_buffers"), + depthTexture: t.getExtension("WEBKIT_WEBGL_depth_texture"), + floatTexture: t.getExtension("OES_texture_float"), + loseContext: t.getExtension("WEBGL_lose_context"), + vertexArrayObject: t.getExtension("OES_vertex_array_object") || t.getExtension("MOZ_OES_vertex_array_object") || t.getExtension("WEBKIT_OES_vertex_array_object") + }) + }, ContextSystem.prototype.handleContextLost = function(t) { + t.preventDefault() + }, ContextSystem.prototype.handleContextRestored = function() { + this.renderer.runners.contextChange.run(this.gl) + }, ContextSystem.prototype.destroy = function() { + var t = this.renderer.view; + t.removeEventListener("webglcontextlost", this.handleContextLost), t.removeEventListener("webglcontextrestored", this.handleContextRestored), this.gl.useProgram(null), this.extensions.loseContext && this.extensions.loseContext.loseContext() + }, ContextSystem.prototype.postrender = function() { + this.gl.flush() + }, ContextSystem.prototype.validateContext = function(t) { + t.getContextAttributes().stencil + }, Object.defineProperties(ContextSystem.prototype, t), ContextSystem + }(Ut), + de = function(e) { + function FramebufferSystem(t) { + e.call(this, t), this.managedFramebuffers = [], this.unknownFramebuffer = new jt(10, 10) + } + e && (FramebufferSystem.__proto__ = e); + var t = { + size: { + configurable: !0 + } + }; + return ((FramebufferSystem.prototype = Object.create(e && e.prototype)).constructor = FramebufferSystem).prototype.contextChange = function() { + var t = this.gl = this.renderer.gl; + if (this.CONTEXT_UID = this.renderer.CONTEXT_UID, this.current = this.unknownFramebuffer, this.viewport = new ot, this.hasMRT = !0, this.writeDepthTexture = !0, this.disposeAll(!0), 1 === this.renderer.context.webGLVersion) { + var e = this.renderer.context.extensions.drawBuffers, + r = this.renderer.context.extensions.depthTexture; + M.PREFER_ENV === v.WEBGL_LEGACY && (r = e = null), t.drawBuffers = e ? function(t) { + return e.drawBuffersWEBGL(t) + } : (this.hasMRT = !1, function() {}), r || (this.writeDepthTexture = !1) + } + }, FramebufferSystem.prototype.bind = function(t, e) { + var r = this.gl; + if (t) { + var n = t.glFramebuffers[this.CONTEXT_UID] || this.initFramebuffer(t); + this.current !== t && (this.current = t, r.bindFramebuffer(r.FRAMEBUFFER, n.framebuffer)), n.dirtyId !== t.dirtyId && (n.dirtyId = t.dirtyId, n.dirtyFormat !== t.dirtyFormat ? (n.dirtyFormat = t.dirtyFormat, this.updateFramebuffer(t)) : n.dirtySize !== t.dirtySize && (n.dirtySize = t.dirtySize, this.resizeFramebuffer(t))); + for (var i = 0; i < t.colorTextures.length; i++) t.colorTextures[i].texturePart ? this.renderer.texture.unbind(t.colorTextures[i].texture) : this.renderer.texture.unbind(t.colorTextures[i]); + t.depthTexture && this.renderer.texture.unbind(t.depthTexture), e ? this.setViewport(e.x, e.y, e.width, e.height) : this.setViewport(0, 0, t.width, t.height) + } else this.current && (this.current = null, r.bindFramebuffer(r.FRAMEBUFFER, null)), e ? this.setViewport(e.x, e.y, e.width, e.height) : this.setViewport(0, 0, this.renderer.width, this.renderer.height) + }, FramebufferSystem.prototype.setViewport = function(t, e, r, n) { + var i = this.viewport; + i.width === r && i.height === n && i.x === t && i.y === e || (i.x = t, i.y = e, i.width = r, i.height = n, this.gl.viewport(t, e, r, n)) + }, t.size.get = function() { + return this.current ? { + x: 0, + y: 0, + width: this.current.width, + height: this.current.height + } : { + x: 0, + y: 0, + width: this.renderer.width, + height: this.renderer.height + } + }, FramebufferSystem.prototype.clear = function(t, e, r, n) { + var i = this.gl; + i.clearColor(t, e, r, n), i.clear(i.COLOR_BUFFER_BIT | i.DEPTH_BUFFER_BIT) + }, FramebufferSystem.prototype.initFramebuffer = function(t) { + var e = { + framebuffer: this.gl.createFramebuffer(), + stencil: null, + dirtyId: 0, + dirtyFormat: 0, + dirtySize: 0 + }; + return t.glFramebuffers[this.CONTEXT_UID] = e, this.managedFramebuffers.push(t), t.disposeRunner.add(this), e + }, FramebufferSystem.prototype.resizeFramebuffer = function(t) { + var e = this.gl, + r = t.glFramebuffers[this.CONTEXT_UID]; + r.stencil && (e.bindRenderbuffer(e.RENDERBUFFER, r.stencil), e.renderbufferStorage(e.RENDERBUFFER, e.DEPTH_STENCIL, t.width, t.height)); + for (var n = t.colorTextures, i = 0; i < n.length; i++) this.renderer.texture.bind(n[i], 0); + t.depthTexture && this.renderer.texture.bind(t.depthTexture, 0) + }, FramebufferSystem.prototype.updateFramebuffer = function(t) { + var e = this.gl, + r = t.glFramebuffers[this.CONTEXT_UID], + n = t.colorTextures.length; + e.drawBuffers || (n = Math.min(n, 1)); + for (var i = [], o = 0; o < n; o++) { + var a = t.colorTextures[o]; + a.texturePart ? (this.renderer.texture.bind(a.texture, 0), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0 + o, e.TEXTURE_CUBE_MAP_NEGATIVE_X + a.side, a.texture._glTextures[this.CONTEXT_UID].texture, 0)) : (this.renderer.texture.bind(a, 0), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0 + o, e.TEXTURE_2D, a._glTextures[this.CONTEXT_UID].texture, 0)), i.push(e.COLOR_ATTACHMENT0 + o) + } + if ((1 < i.length && e.drawBuffers(i), t.depthTexture) && this.writeDepthTexture) { + var s = t.depthTexture; + this.renderer.texture.bind(s, 0), e.framebufferTexture2D(e.FRAMEBUFFER, e.DEPTH_ATTACHMENT, e.TEXTURE_2D, s._glTextures[this.CONTEXT_UID].texture, 0) + } + r.stencil || !t.stencil && !t.depth || (r.stencil = e.createRenderbuffer(), e.bindRenderbuffer(e.RENDERBUFFER, r.stencil), t.depthTexture || e.framebufferRenderbuffer(e.FRAMEBUFFER, e.DEPTH_STENCIL_ATTACHMENT, e.RENDERBUFFER, r.stencil), e.renderbufferStorage(e.RENDERBUFFER, e.DEPTH_STENCIL, t.width, t.height)) + }, FramebufferSystem.prototype.disposeFramebuffer = function(t, e) { + var r = t.glFramebuffers[this.CONTEXT_UID], + n = this.gl; + if (r) { + delete t.glFramebuffers[this.CONTEXT_UID]; + var i = this.managedFramebuffers.indexOf(t); + 0 <= i && this.managedFramebuffers.splice(i, 1), t.disposeRunner.remove(this), e || (n.deleteFramebuffer(r.framebuffer), r.stencil && n.deleteRenderbuffer(r.stencil)) + } + }, FramebufferSystem.prototype.disposeAll = function(t) { + var e = this.managedFramebuffers; + this.managedFramebuffers = []; + for (var r = 0; r < e.count; r++) this.disposeFramebuffer(e[r], t) + }, FramebufferSystem.prototype.reset = function() { + this.current = this.unknownFramebuffer, this.viewport = new ot + }, Object.defineProperties(FramebufferSystem.prototype, t), FramebufferSystem + }(Ut), + pe = function(t) { + this.buffer = t, this.updateID = -1, this.byteLength = -1, this.refCount = 0 + }, + me = { + 5126: 4, + 5123: 2, + 5121: 1 + }, + ve = function(e) { + function GeometrySystem(t) { + e.call(this, t), this._activeGeometry = null, this._activeVao = null, this.hasVao = !0, this.hasInstance = !0, this.boundBuffers = {}, this.managedGeometries = {}, this.managedBuffers = {} + } + return e && (GeometrySystem.__proto__ = e), ((GeometrySystem.prototype = Object.create(e && e.prototype)).constructor = GeometrySystem).prototype.contextChange = function() { + this.disposeAll(!0); + var t = this.gl = this.renderer.gl; + if (this.CONTEXT_UID = this.renderer.CONTEXT_UID, !t.createVertexArray) { + var e = this.renderer.context.extensions.vertexArrayObject; + M.PREFER_ENV === v.WEBGL_LEGACY && (e = null), t.deleteVertexArray = e ? (t.createVertexArray = function() { + return e.createVertexArrayOES() + }, t.bindVertexArray = function(t) { + return e.bindVertexArrayOES(t) + }, function(t) { + return e.deleteVertexArrayOES(t) + }) : (this.hasVao = !1, t.createVertexArray = function() {}, t.bindVertexArray = function() {}, function() {}) + } + if (!t.vertexAttribDivisor) { + var o = t.getExtension("ANGLE_instanced_arrays"); + o ? (t.vertexAttribDivisor = function(t, e) { + return o.vertexAttribDivisorANGLE(t, e) + }, t.drawElementsInstanced = function(t, e, r, n, i) { + return o.drawElementsInstancedANGLE(t, e, r, n, i) + }, t.drawArraysInstanced = function(t, e, r, n) { + return o.drawArraysInstancedANGLE(t, e, r, n) + }) : this.hasInstance = !1 + } + }, GeometrySystem.prototype.bind = function(t, e) { + e = e || this.renderer.shader.shader; + var r = this.gl, + n = t.glVertexArrayObjects[this.CONTEXT_UID]; + n || ((this.managedGeometries[t.id] = t).disposeRunner.add(this), t.glVertexArrayObjects[this.CONTEXT_UID] = n = {}); + var i = n[e.program.id] || this.initGeometryVao(t, e.program); + this._activeGeometry = t, this._activeVao !== i && (this._activeVao = i, this.hasVao ? r.bindVertexArray(i) : this.activateVao(t, e.program)), this.updateBuffers() + }, GeometrySystem.prototype.reset = function() { + this.unbind() + }, GeometrySystem.prototype.updateBuffers = function() { + for (var t = this._activeGeometry, e = this.gl, r = 0; r < t.buffers.length; r++) { + var n = t.buffers[r], + i = n._glBuffers[this.CONTEXT_UID]; + if (n._updateID !== i.updateID) { + i.updateID = n._updateID; + var o = n.index ? e.ELEMENT_ARRAY_BUFFER : e.ARRAY_BUFFER; + if (e.bindBuffer(o, i.buffer), (this._boundBuffer = i).byteLength >= n.data.byteLength) e.bufferSubData(o, 0, n.data); + else { + var a = n.static ? e.STATIC_DRAW : e.DYNAMIC_DRAW; + i.byteLength = n.data.byteLength, e.bufferData(o, n.data, a) + } + } + } + }, GeometrySystem.prototype.checkCompatibility = function(t, e) { + var r = t.attributes, + n = e.attributeData; + for (var i in n) + if (!r[i]) throw new Error('shader and geometry incompatible, geometry missing the "' + i + '" attribute') + }, GeometrySystem.prototype.getSignature = function(t, e) { + var r = t.attributes, + n = e.attributeData, + i = ["g", t.id]; + for (var o in r) n[o] && i.push(o); + return i.join("-") + }, GeometrySystem.prototype.initGeometryVao = function(t, e) { + this.checkCompatibility(t, e); + var r = this.gl, + n = this.CONTEXT_UID, + i = this.getSignature(t, e), + o = t.glVertexArrayObjects[this.CONTEXT_UID], + a = o[i]; + if (a) return o[e.id] = a; + var s = t.buffers, + u = t.attributes, + h = {}, + c = {}; + for (var l in s) h[l] = 0, c[l] = 0; + for (var f in u) !u[f].size && e.attributeData[f] ? u[f].size = e.attributeData[f].size : u[f].size, h[u[f].buffer] += u[f].size * me[u[f].type]; + for (var d in u) { + var p = u[d], + m = p.size; + void 0 === p.stride && (h[p.buffer] === m * me[p.type] ? p.stride = 0 : p.stride = h[p.buffer]), void 0 === p.start && (p.start = c[p.buffer], c[p.buffer] += m * me[p.type]) + } + a = r.createVertexArray(), r.bindVertexArray(a); + for (var v = 0; v < s.length; v++) { + var g = s[v]; + g._glBuffers[n] || (g._glBuffers[n] = new pe(r.createBuffer()), (this.managedBuffers[g.id] = g).disposeRunner.add(this)), g._glBuffers[n].refCount++ + } + return this.activateVao(t, e), r.bindVertexArray(this._activeVao), o[e.id] = a, o[i] = a + }, GeometrySystem.prototype.disposeBuffer = function(t, e) { + if (this.managedBuffers[t.id]) { + delete this.managedBuffers[t.id]; + var r = t._glBuffers[this.CONTEXT_UID], + n = this.gl; + t.disposeRunner.remove(this), r && (e || n.deleteBuffer(r.buffer), delete t._glBuffers[this.CONTEXT_UID]) + } + }, GeometrySystem.prototype.disposeGeometry = function(t, e) { + if (this.managedGeometries[t.id]) { + delete this.managedGeometries[t.id]; + var r = t.glVertexArrayObjects[this.CONTEXT_UID], + n = this.gl, + i = t.buffers; + if (t.disposeRunner.remove(this), r) { + for (var o = 0; o < i.length; o++) { + var a = i[o]._glBuffers[this.CONTEXT_UID]; + a.refCount--, 0 !== a.refCount || e || this.disposeBuffer(i[o], e) + } + if (!e) + for (var s in r) "g" === s[0] && n.deleteVertexArray(r[s]); + delete t.glVertexArrayObjects[this.CONTEXT_UID] + } + } + }, GeometrySystem.prototype.disposeAll = function(t) { + for (var e = Object.keys(this.managedGeometries), r = 0; r < e.length; r++) this.disposeGeometry(this.managedGeometries[e[r]], t); + e = Object.keys(this.managedBuffers); + for (var n = 0; n < e.length; n++) this.disposeBuffer(this.managedBuffers[e[n]], t) + }, GeometrySystem.prototype.activateVao = function(t, e) { + var r = this.gl, + n = this.CONTEXT_UID, + i = t.buffers, + o = t.attributes; + t.indexBuffer && r.bindBuffer(r.ELEMENT_ARRAY_BUFFER, t.indexBuffer._glBuffers[n].buffer); + var a = null; + for (var s in o) { + var u = o[s], + h = i[u.buffer]._glBuffers[n]; + if (e.attributeData[s]) { + a !== h && (r.bindBuffer(r.ARRAY_BUFFER, h.buffer), a = h); + var c = e.attributeData[s].location; + if (r.enableVertexAttribArray(c), r.vertexAttribPointer(c, u.size, u.type || r.FLOAT, u.normalized, u.stride, u.start), u.instance) { + if (!this.hasInstance) throw new Error("geometry error, GPU Instancing is not supported on this device"); + r.vertexAttribDivisor(c, 1) + } + } + } + }, GeometrySystem.prototype.draw = function(t, e, r, n) { + var i = this.gl, + o = this._activeGeometry; + return o.indexBuffer ? o.instanced ? i.drawElementsInstanced(t, e || o.indexBuffer.data.length, i.UNSIGNED_SHORT, 2 * (r || 0), n || 1) : i.drawElements(t, e || o.indexBuffer.data.length, i.UNSIGNED_SHORT, 2 * (r || 0)) : o.instanced ? i.drawArraysInstanced(t, r, e || o.getSize(), n || 1) : i.drawArrays(t, r, e || o.getSize()), this + }, GeometrySystem.prototype.unbind = function() { + this.gl.bindVertexArray(null), this._activeVao = null, this._activeGeometry = null + }, GeometrySystem + }(Ut); + + function compileProgram(t, e, r, n) { + var i = compileShader(t, t.VERTEX_SHADER, e), + o = compileShader(t, t.FRAGMENT_SHADER, r), + a = t.createProgram(); + if (t.attachShader(a, i), t.attachShader(a, o), n) + for (var s in n) t.bindAttribLocation(a, n[s], s); + return t.linkProgram(a), t.getProgramParameter(a, t.LINK_STATUS) || (t.getProgramInfoLog(a), t.deleteProgram(a), a = null), t.deleteShader(i), t.deleteShader(o), a + } + + function compileShader(t, e, r) { + var n = t.createShader(e); + return t.shaderSource(n, r), t.compileShader(n), t.getShaderParameter(n, t.COMPILE_STATUS) ? n : null + } + + function core_es_defaultValue(t, e) { + switch (t) { + case "float": + return 0; + case "vec2": + return new Float32Array(2 * e); + case "vec3": + return new Float32Array(3 * e); + case "vec4": + return new Float32Array(4 * e); + case "int": + case "sampler2D": + case "sampler2DArray": + return 0; + case "ivec2": + return new Int32Array(2 * e); + case "ivec3": + return new Int32Array(3 * e); + case "ivec4": + return new Int32Array(4 * e); + case "bool": + return !1; + case "bvec2": + return booleanArray(2 * e); + case "bvec3": + return booleanArray(3 * e); + case "bvec4": + return booleanArray(4 * e); + case "mat2": + return new Float32Array([1, 0, 0, 1]); + case "mat3": + return new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]); + case "mat4": + return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) + } + return null + } + + function booleanArray(t) { + for (var e = new Array(t), r = 0; r < e.length; r++) e[r] = !1; + return e + } + + function setPrecision(t, e) { + return "precision" !== t.substring(0, 9) ? "precision " + e + " float;\n" + t : t + } + var ge = { + float: 1, + vec2: 2, + vec3: 3, + vec4: 4, + int: 1, + ivec2: 2, + ivec3: 3, + ivec4: 4, + bool: 1, + bvec2: 2, + bvec3: 3, + bvec4: 4, + mat2: 4, + mat3: 9, + mat4: 16, + sampler2D: 1 + }; + var ye = null, + _e = { + FLOAT: "float", + FLOAT_VEC2: "vec2", + FLOAT_VEC3: "vec3", + FLOAT_VEC4: "vec4", + INT: "int", + INT_VEC2: "ivec2", + INT_VEC3: "ivec3", + INT_VEC4: "ivec4", + BOOL: "bool", + BOOL_VEC2: "bvec2", + BOOL_VEC3: "bvec3", + BOOL_VEC4: "bvec4", + FLOAT_MAT2: "mat2", + FLOAT_MAT3: "mat3", + FLOAT_MAT4: "mat4", + SAMPLER_2D: "sampler2D", + SAMPLER_CUBE: "samplerCube", + SAMPLER_2D_ARRAY: "sampler2DArray" + }; + + function mapType(t, e) { + if (!ye) { + var r = Object.keys(_e); + ye = {}; + for (var n = 0; n < r.length; ++n) { + var i = r[n]; + ye[t[i]] = _e[i] + } + } + return ye[e] + } + var be = { + float: "\n if(cv !== v)\n {\n cv.v = v;\n gl.uniform1f(location, v)\n }", + vec2: "\n if(cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n gl.uniform2f(location, v[0], v[1])\n }", + vec3: "\n if(cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n\n gl.uniform3f(location, v[0], v[1], v[2])\n }", + vec4: "gl.uniform4f(location, v[0], v[1], v[2], v[3])", + int: "gl.uniform1i(location, v)", + ivec2: "gl.uniform2i(location, v[0], v[1])", + ivec3: "gl.uniform3i(location, v[0], v[1], v[2])", + ivec4: "gl.uniform4i(location, v[0], v[1], v[2], v[3])", + bool: "gl.uniform1i(location, v)", + bvec2: "gl.uniform2i(location, v[0], v[1])", + bvec3: "gl.uniform3i(location, v[0], v[1], v[2])", + bvec4: "gl.uniform4i(location, v[0], v[1], v[2], v[3])", + mat2: "gl.uniformMatrix2fv(location, false, v)", + mat3: "gl.uniformMatrix3fv(location, false, v)", + mat4: "gl.uniformMatrix4fv(location, false, v)", + sampler2D: "gl.uniform1i(location, v)", + samplerCube: "gl.uniform1i(location, v)", + sampler2DArray: "gl.uniform1i(location, v)" + }, + xe = { + float: "gl.uniform1fv(location, v)", + vec2: "gl.uniform2fv(location, v)", + vec3: "gl.uniform3fv(location, v)", + vec4: "gl.uniform4fv(location, v)", + mat4: "gl.uniformMatrix4fv(location, false, v)", + mat3: "gl.uniformMatrix3fv(location, false, v)", + mat2: "gl.uniformMatrix2fv(location, false, v)", + int: "gl.uniform1iv(location, v)", + ivec2: "gl.uniform2iv(location, v)", + ivec3: "gl.uniform3iv(location, v)", + ivec4: "gl.uniform4iv(location, v)", + bool: "gl.uniform1iv(location, v)", + bvec2: "gl.uniform2iv(location, v)", + bvec3: "gl.uniform3iv(location, v)", + bvec4: "gl.uniform4iv(location, v)", + sampler2D: "gl.uniform1iv(location, v)", + samplerCube: "gl.uniform1iv(location, v)", + sampler2DArray: "gl.uniform1iv(location, v)" + }; + var we = null; + var Te, Se = ["precision mediump float;", "void main(void){", "float test = 0.1;", "%forloop%", "gl_FragColor = vec4(0.0);", "}"].join("\n"); + + function checkMaxIfStatementsInShader(t, e) { + if (0 === t) throw new Error("Invalid value of `0` passed to `checkMaxIfStatementsInShader`"); + for (var r = e.createShader(e.FRAGMENT_SHADER);;) { + var n = Se.replace(/%forloop%/gi, generateIfTestSrc(t)); + if (e.shaderSource(r, n), e.compileShader(r), e.getShaderParameter(r, e.COMPILE_STATUS)) break; + t = t / 2 | 0 + } + return t + } + + function generateIfTestSrc(t) { + for (var e = "", r = 0; r < t; ++r) 0 < r && (e += "\nelse "), r < t - 1 && (e += "if(test == " + r + ".0){}"); + return e + } + var Me = 0, + Ee = {}, + Ae = function Program(t, e, r) { + void 0 === r && (r = "pixi-shader"), this.id = Me++, this.vertexSrc = t || Program.defaultVertexSrc, this.fragmentSrc = e || Program.defaultFragmentSrc, this.vertexSrc = this.vertexSrc.trim(), this.fragmentSrc = this.fragmentSrc.trim(), "#version" !== this.vertexSrc.substring(0, 8) && (r = r.replace(/\s+/g, "-"), Ee[r] ? (Ee[r]++, r += "-" + Ee[r]) : Ee[r] = 1, this.vertexSrc = "#define SHADER_NAME " + r + "\n" + this.vertexSrc, this.fragmentSrc = "#define SHADER_NAME " + r + "\n" + this.fragmentSrc, this.vertexSrc = setPrecision(this.vertexSrc, M.PRECISION_VERTEX), this.fragmentSrc = setPrecision(this.fragmentSrc, M.PRECISION_FRAGMENT)), this.extractData(this.vertexSrc, this.fragmentSrc), this.glPrograms = {}, this.syncUniforms = null + }, + Pe = { + defaultVertexSrc: { + configurable: !0 + }, + defaultFragmentSrc: { + configurable: !0 + } + }; + Ae.prototype.extractData = function(t, e) { + var r = function() { + if (we) return we; + var t, e = document.createElement("canvas"); + if (M.PREFER_ENV >= v.WEBGL2 && (t = e.getContext("webgl2", {})), !t) { + if (!(t = e.getContext("webgl", {}) || e.getContext("experimental-webgl", {}))) throw new Error("This browser does not support WebGL. Try using the canvas renderer"); + t.getExtension("WEBGL_draw_buffers") + } + return we = t + }(); + if (r) { + var n = compileProgram(r, t, e); + this.attributeData = this.getAttributeData(n, r), this.uniformData = this.getUniformData(n, r), r.deleteProgram(n) + } else this.uniformData = {}, this.attributeData = {} + }, Ae.prototype.getAttributeData = function(t, e) { + for (var r, n = {}, i = [], o = e.getProgramParameter(t, e.ACTIVE_ATTRIBUTES), a = 0; a < o; a++) { + var s = e.getActiveAttrib(t, a), + u = mapType(e, s.type), + h = { + type: u, + name: s.name, + size: (r = u, ge[r]), + location: 0 + }; + n[s.name] = h, i.push(h) + } + i.sort(function(t, e) { + return t.name > e.name ? 1 : -1 + }); + for (var c = 0; c < i.length; c++) i[c].location = c; + return n + }, Ae.prototype.getUniformData = function(t, e) { + for (var r = {}, n = e.getProgramParameter(t, e.ACTIVE_UNIFORMS), i = 0; i < n; i++) { + var o = e.getActiveUniform(t, i), + a = o.name.replace(/\[.*?\]/, ""), + s = o.name.match(/\[.*?\]/, ""), + u = mapType(e, o.type); + r[a] = { + type: u, + size: o.size, + isArray: s, + value: core_es_defaultValue(u, o.size) + } + } + return r + }, Pe.defaultVertexSrc.get = function() { + return "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void){\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n vTextureCoord = aTextureCoord;\n}\n" + }, Pe.defaultFragmentSrc.get = function() { + return "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n gl_FragColor *= texture2D(uSampler, vTextureCoord);\n}" + }, Ae.from = function(t, e, r) { + var n = t + e, + i = D[n]; + return i || (D[n] = i = new Ae(t, e, r)), i + }, Object.defineProperties(Ae, Pe); + var Ie = function(t, e) { + for (var r in this.program = t, this.uniformGroup = e ? e instanceof oe ? e : new oe(e) : new oe({}), t.uniformData) this.uniformGroup.uniforms[r] instanceof Array && (this.uniformGroup.uniforms[r] = new Float32Array(this.uniformGroup.uniforms[r])) + }, + Oe = { + uniforms: { + configurable: !0 + } + }; + Ie.prototype.checkUniformExists = function(t, e) { + if (e.uniforms[t]) return !0; + for (var r in e.uniforms) { + var n = e.uniforms[r]; + if (n.group && this.checkUniformExists(t, n)) return !0 + } + return !1 + }, Ie.prototype.destroy = function() { + this.uniformGroup = null + }, Oe.uniforms.get = function() { + return this.uniformGroup.uniforms + }, Ie.from = function(t, e, r) { + var n = Ae.from(t, e); + return new Ie(n, r) + }, Object.defineProperties(Ie.prototype, Oe); + var Ce = function() { + this.data = 0, this.blendMode = y.NORMAL, this.polygonOffset = 0, this.blend = !0 + }, + Re = { + blend: { + configurable: !0 + }, + offsets: { + configurable: !0 + }, + culling: { + configurable: !0 + }, + depthTest: { + configurable: !0 + }, + clockwiseFrontFace: { + configurable: !0 + }, + blendMode: { + configurable: !0 + }, + polygonOffset: { + configurable: !0 + } + }; + Re.blend.get = function() { + return !!(1 & this.data) + }, Re.blend.set = function(t) { + !!(1 & this.data) !== t && (this.data ^= 1) + }, Re.offsets.get = function() { + return !!(2 & this.data) + }, Re.offsets.set = function(t) { + !!(2 & this.data) !== t && (this.data ^= 2) + }, Re.culling.get = function() { + return !!(4 & this.data) + }, Re.culling.set = function(t) { + !!(4 & this.data) !== t && (this.data ^= 4) + }, Re.depthTest.get = function() { + return !!(8 & this.data) + }, Re.depthTest.set = function(t) { + !!(8 & this.data) !== t && (this.data ^= 8) + }, Re.clockwiseFrontFace.get = function() { + return !!(16 & this.data) + }, Re.clockwiseFrontFace.set = function(t) { + !!(16 & this.data) !== t && (this.data ^= 16) + }, Re.blendMode.get = function() { + return this._blendMode + }, Re.blendMode.set = function(t) { + this.blend = t !== y.NONE, this._blendMode = t + }, Re.polygonOffset.get = function() { + return this._polygonOffset + }, Re.polygonOffset.set = function(t) { + this.offsets = !!t, this._polygonOffset = t + }, Ce.for2d = function() { + var t = new Ce; + return t.depthTest = !1, t.blend = !0, t + }, Object.defineProperties(Ce.prototype, Re); + var De = function(i) { + function Filter(t, e, r) { + var n = Ae.from(t || Filter.defaultVertexSrc, e || Filter.defaultFragmentSrc); + i.call(this, n, r), this.padding = 0, this.resolution = M.FILTER_RESOLUTION, this.enabled = !0, this.autoFit = !0, this.legacy = !!this.program.attributeData.aTextureCoord, this.state = new Ce + } + i && (Filter.__proto__ = i); + var t = { + blendMode: { + configurable: !0 + } + }, + e = { + defaultVertexSrc: { + configurable: !0 + }, + defaultFragmentSrc: { + configurable: !0 + } + }; + return ((Filter.prototype = Object.create(i && i.prototype)).constructor = Filter).prototype.apply = function(t, e, r, n, i, o) { + t.applyFilter(this, e, r, n, i, o) + }, t.blendMode.get = function() { + return this.state.blendMode + }, t.blendMode.set = function(t) { + this.state.blendMode = t + }, e.defaultVertexSrc.get = function() { + return "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n" + }, e.defaultFragmentSrc.get = function() { + return "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n gl_FragColor = texture2D(uSampler, vTextureCoord);\n}\n" + }, Object.defineProperties(Filter.prototype, t), Object.defineProperties(Filter, e), Filter + }(Ie); + De.SOURCE_KEY_MAP = {}; + var Fe = new Y, + ke = function(t, e) { + this._texture = t, this.mapCoord = new Y, this.uClampFrame = new Float32Array(4), this.uClampOffset = new Float32Array(2), this._updateID = -1, this.clampOffset = 0, this.clampMargin = void 0 === e ? .5 : e, this.isSimple = !1 + }, + Le = { + texture: { + configurable: !0 + } + }; + Le.texture.get = function() { + return this._texture + }, Le.texture.set = function(t) { + this._texture = t, this._updateID = -1 + }, ke.prototype.multiplyUvs = function(t, e) { + void 0 === e && (e = t); + for (var r = this.mapCoord, n = 0; n < t.length; n += 2) { + var i = t[n], + o = t[n + 1]; + e[n] = i * r.a + o * r.c + r.tx, e[n + 1] = i * r.b + o * r.d + r.ty + } + return e + }, ke.prototype.update = function(t) { + var e = this._texture; + if (!e || !e.valid) return !1; + if (!t && this._updateID === e._updateID) return !1; + this._updateID = e._updateID; + var r = e._uvs; + this.mapCoord.set(r.x1 - r.x0, r.y1 - r.y0, r.x3 - r.x0, r.y3 - r.y0, r.x0, r.y0); + var n = e.orig, + i = e.trim; + i && (Fe.set(n.width / i.width, 0, 0, n.height / i.height, -i.x / i.width, -i.y / i.height), this.mapCoord.append(Fe)); + var o = e.baseTexture, + a = this.uClampFrame, + s = this.clampMargin / o.resolution, + u = this.clampOffset; + return a[0] = (e._frame.x + s + u) / o.width, a[1] = (e._frame.y + s + u) / o.height, a[2] = (e._frame.x + e._frame.width - s + u) / o.width, a[3] = (e._frame.y + e._frame.height - s + u) / o.height, this.uClampOffset[0] = u / o.realWidth, this.uClampOffset[1] = u / o.realHeight, this.isSimple = e._frame.width === o.width && e._frame.height === o.height && 0 === e.rotate, !0 + }, Object.defineProperties(ke.prototype, Le); + var Ne = function(r) { + function SpriteMaskFilter(t) { + var e = new Y; + r.call(this, "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 otherMatrix;\n\nvarying vec2 vMaskCoord;\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;\n}\n", "varying vec2 vMaskCoord;\nvarying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform sampler2D mask;\nuniform float alpha;\nuniform float npmAlpha;\nuniform vec4 maskClamp;\n\nvoid main(void)\n{\n float clip = step(3.5,\n step(maskClamp.x, vMaskCoord.x) +\n step(maskClamp.y, vMaskCoord.y) +\n step(vMaskCoord.x, maskClamp.z) +\n step(vMaskCoord.y, maskClamp.w));\n\n vec4 original = texture2D(uSampler, vTextureCoord);\n vec4 masky = texture2D(mask, vMaskCoord);\n float alphaMul = 1.0 - npmAlpha * (1.0 - masky.a);\n\n original *= (alphaMul * masky.r * alpha * clip);\n\n gl_FragColor = original;\n}\n"), t.renderable = !1, this.maskSprite = t, this.maskMatrix = e + } + return r && (SpriteMaskFilter.__proto__ = r), ((SpriteMaskFilter.prototype = Object.create(r && r.prototype)).constructor = SpriteMaskFilter).prototype.apply = function(t, e, r, n) { + var i = this.maskSprite, + o = this.maskSprite.texture; + o.valid && (o.transform || (o.transform = new ke(o, 0)), o.transform.update(), this.uniforms.npmAlpha = o.baseTexture.premultiplyAlpha ? 0 : 1, this.uniforms.mask = o, this.uniforms.otherMatrix = t.calculateSpriteMatrix(this.maskMatrix, i).prepend(o.transform.mapCoord), this.uniforms.alpha = i.worldAlpha, this.uniforms.maskClamp = o.transform.uClampFrame, t.applyFilter(this, e, r, n)) + }, SpriteMaskFilter + }(De), + Be = function(e) { + function MaskSystem(t) { + e.call(this, t), this.scissor = !1, this.scissorData = null, this.scissorRenderTarget = null, this.enableScissor = !1, this.alphaMaskPool = [], this.alphaMaskIndex = 0 + } + return e && (MaskSystem.__proto__ = e), ((MaskSystem.prototype = Object.create(e && e.prototype)).constructor = MaskSystem).prototype.push = function(t, e) { + if (e.isSprite) this.pushSpriteMask(t, e); + else if (this.enableScissor && !this.scissor && this.renderer._activeRenderTarget.root && !this.renderer.stencil.stencilMaskStack.length && e.isFastRect()) { + var r = e.worldTransform, + n = Math.atan2(r.b, r.a); + (n = Math.round(n * (180 / Math.PI))) % 90 ? this.pushStencilMask(e) : this.pushScissorMask(t, e) + } else this.pushStencilMask(e) + }, MaskSystem.prototype.pop = function(t, e) { + e.isSprite ? this.popSpriteMask(t, e) : this.enableScissor && !this.renderer.stencil.stencilMaskStack.length ? this.popScissorMask(t, e) : this.popStencilMask(t, e) + }, MaskSystem.prototype.pushSpriteMask = function(t, e) { + var r = this.alphaMaskPool[this.alphaMaskIndex]; + r || (r = this.alphaMaskPool[this.alphaMaskIndex] = [new Ne(e)]), r[0].resolution = this.renderer.resolution, r[0].maskSprite = e; + var n = t.filterArea; + t.filterArea = e.getBounds(!0), this.renderer.filter.push(t, r), t.filterArea = n, this.alphaMaskIndex++ + }, MaskSystem.prototype.popSpriteMask = function() { + this.renderer.filter.pop(), this.alphaMaskIndex-- + }, MaskSystem.prototype.pushStencilMask = function(t) { + this.renderer.batch.flush(), this.renderer.stencil.pushStencil(t) + }, MaskSystem.prototype.popStencilMask = function() { + this.renderer.stencil.popStencil() + }, MaskSystem.prototype.pushScissorMask = function(t, e) { + e.renderable = !0; + var r = this.renderer._activeRenderTarget, + n = e.getBounds(); + n.fit(r.size), e.renderable = !1, this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + var i = this.renderer.resolution; + this.renderer.gl.scissor(n.x * i, (r.root ? r.size.height - n.y - n.height : n.y) * i, n.width * i, n.height * i), this.scissorRenderTarget = r, this.scissorData = e, this.scissor = !0 + }, MaskSystem.prototype.popScissorMask = function() { + this.scissorRenderTarget = null, this.scissorData = null, this.scissor = !1; + var t = this.renderer.gl; + t.disable(t.SCISSOR_TEST) + }, MaskSystem + }(Ut), + Ue = function(e) { + function StencilSystem(t) { + e.call(this, t), this.stencilMaskStack = [] + } + return e && (StencilSystem.__proto__ = e), ((StencilSystem.prototype = Object.create(e && e.prototype)).constructor = StencilSystem).prototype.setMaskStack = function(t) { + var e = this.renderer.gl; + t.length !== this.stencilMaskStack.length && (0 === t.length ? e.disable(e.STENCIL_TEST) : e.enable(e.STENCIL_TEST)), this.stencilMaskStack = t + }, StencilSystem.prototype.pushStencil = function(t) { + var e = this.renderer.gl, + r = this.stencilMaskStack.length; + 0 === r && e.enable(e.STENCIL_TEST), this.stencilMaskStack.push(t), e.colorMask(!1, !1, !1, !1), e.stencilFunc(e.EQUAL, r, this._getBitwiseMask()), e.stencilOp(e.KEEP, e.KEEP, e.INCR), t.renderable = !0, t.render(this.renderer), this.renderer.batch.flush(), t.renderable = !1, this._useCurrent() + }, StencilSystem.prototype.popStencil = function() { + var t = this.renderer.gl, + e = this.stencilMaskStack.pop(); + 0 === this.stencilMaskStack.length ? (t.disable(t.STENCIL_TEST), t.clear(t.STENCIL_BUFFER_BIT), t.clearStencil(0)) : (t.colorMask(!1, !1, !1, !1), t.stencilOp(t.KEEP, t.KEEP, t.DECR), e.renderable = !0, e.render(this.renderer), this.renderer.batch.flush(), e.renderable = !1, this._useCurrent()) + }, StencilSystem.prototype._useCurrent = function() { + var t = this.renderer.gl; + t.colorMask(!0, !0, !0, !0), t.stencilFunc(t.EQUAL, this.stencilMaskStack.length, this._getBitwiseMask()), t.stencilOp(t.KEEP, t.KEEP, t.KEEP) + }, StencilSystem.prototype._getBitwiseMask = function() { + return (1 << this.stencilMaskStack.length) - 1 + }, StencilSystem.prototype.destroy = function() { + e.prototype.destroy.call(this, this), this.stencilMaskStack = null + }, StencilSystem + }(Ut), + Ge = function(e) { + function ProjectionSystem(t) { + e.call(this, t), this.destinationFrame = null, this.sourceFrame = null, this.defaultFrame = null, this.projectionMatrix = new Y, this.transform = null + } + return e && (ProjectionSystem.__proto__ = e), ((ProjectionSystem.prototype = Object.create(e && e.prototype)).constructor = ProjectionSystem).prototype.update = function(t, e, r, n) { + this.destinationFrame = t || this.destinationFrame || this.defaultFrame, this.sourceFrame = e || this.sourceFrame || t, this.calculateProjection(this.destinationFrame, this.sourceFrame, r, n), this.transform && this.projectionMatrix.append(this.transform); + var i = this.renderer; + i.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix, i.globalUniforms.update(), i.shader.shader && i.shader.syncUniformGroup(i.shader.shader.uniforms.globals) + }, ProjectionSystem.prototype.calculateProjection = function(t, e, r, n) { + var i = this.projectionMatrix; + i.ty = n ? (i.a = 1 / t.width * 2 * r, i.d = -1 / t.height * 2 * r, i.tx = -1 - e.x * i.a, 1 - e.y * i.d) : (i.a = 1 / t.width * 2 * r, i.d = 1 / t.height * 2 * r, i.tx = -1 - e.x * i.a, -1 - e.y * i.d) + }, ProjectionSystem.prototype.setTransform = function() {}, ProjectionSystem + }(Ut), + je = new ot, + ze = function(e) { + function RenderTextureSystem(t) { + e.call(this, t), this.clearColor = t._backgroundColorRgba, this.defaultMaskStack = [], this.current = null, this.sourceFrame = new ot, this.destinationFrame = new ot + } + return e && (RenderTextureSystem.__proto__ = e), ((RenderTextureSystem.prototype = Object.create(e && e.prototype)).constructor = RenderTextureSystem).prototype.bind = function(t, e, r) { + void 0 === t && (t = null), this.current = t; + var n, i = this.renderer; + if (t) { + var o = t.baseTexture; + n = o.resolution, r || (je.width = o.realWidth, je.height = o.realHeight, r = je), e || (e = r), this.renderer.framebuffer.bind(o.framebuffer, r), this.renderer.projection.update(r, e, n, !1), this.renderer.stencil.setMaskStack(o.stencilMaskStack) + } else n = this.renderer.resolution, r || (je.width = i.width, je.height = i.height, r = je), e || (e = r), i.framebuffer.bind(null, r), this.renderer.projection.update(r, e, n, !0), this.renderer.stencil.setMaskStack(this.defaultMaskStack); + this.sourceFrame.copyFrom(e), this.destinationFrame.x = r.x / n, this.destinationFrame.y = r.y / n, this.destinationFrame.width = r.width / n, this.destinationFrame.height = r.height / n + }, RenderTextureSystem.prototype.clear = function(t) { + t = this.current ? t || this.current.baseTexture.clearColor : t || this.clearColor, this.renderer.framebuffer.clear(t[0], t[1], t[2], t[3]) + }, RenderTextureSystem.prototype.resize = function() { + this.bind(null) + }, RenderTextureSystem.prototype.reset = function() { + this.bind(null) + }, RenderTextureSystem + }(Ut), + Xe = function(t, e) { + this.program = t, this.uniformData = e, this.uniformGroups = {} + }; + Xe.prototype.destroy = function() { + this.uniformData = null, this.uniformGroups = null, this.program = null + }; + var Ve = 0, + qe = function(e) { + function ShaderSystem(t) { + e.call(this, t), this.systemCheck(), this.gl = null, this.shader = null, this.program = null, this.cache = {}, this.id = Ve++ + } + return e && (ShaderSystem.__proto__ = e), ((ShaderSystem.prototype = Object.create(e && e.prototype)).constructor = ShaderSystem).prototype.systemCheck = function() { + if (! function() { + if ("boolean" == typeof Te) return Te; + try { + var t = new Function("param1", "param2", "param3", "return param1[param2] === param3;"); + Te = !0 === t({ + a: "b" + }, "a", "b") + } catch (t) { + Te = !1 + } + return Te + }()) throw new Error("Current environment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.") + }, ShaderSystem.prototype.contextChange = function(t) { + this.gl = t + }, ShaderSystem.prototype.bind = function(t, e) { + t.uniforms.globals = this.renderer.globalUniforms; + var r = t.program, + n = r.glPrograms[this.renderer.CONTEXT_UID] || this.generateShader(t); + return this.shader = t, this.program !== r && (this.program = r, this.gl.useProgram(n.program)), e || this.syncUniformGroup(t.uniformGroup), n + }, ShaderSystem.prototype.setUniforms = function(t) { + var e = this.shader.program, + r = e.glPrograms[this.renderer.CONTEXT_UID]; + e.syncUniforms(r.uniformData, t, this.renderer) + }, ShaderSystem.prototype.syncUniformGroup = function(t) { + var e = this.getglProgram(); + t.static && t.dirtyId === e.uniformGroups[t.id] || (e.uniformGroups[t.id] = t.dirtyId, this.syncUniforms(t, e)) + }, ShaderSystem.prototype.syncUniforms = function(t, e) { + (t.syncUniforms[this.shader.program.id] || this.createSyncGroups(t))(e.uniformData, t.uniforms, this.renderer) + }, ShaderSystem.prototype.createSyncGroups = function(t) { + var e = this.getSignature(t, this.shader.program.uniformData); + return this.cache[e] || (this.cache[e] = function(t, e) { + var r = 0, + n = "var v = null;\n var cv = null\n var gl = renderer.gl"; + for (var i in t.uniforms) { + var o = e[i]; + o ? "float" === o.type && 1 === o.size ? n += "\n if(uv." + i + " !== ud." + i + ".value)\n {\n ud." + i + ".value = uv." + i + "\n gl.uniform1f(ud." + i + ".location, uv." + i + ")\n }\n" : "sampler2D" !== o.type && "samplerCube" !== o.type && "sampler2DArray" !== o.type || 1 !== o.size || o.isArray ? "mat3" === o.type && 1 === o.size ? void 0 !== t.uniforms[i].a ? n += "\n gl.uniformMatrix3fv(ud." + i + ".location, false, uv." + i + ".toArray(true));\n \n" : n += "\n gl.uniformMatrix3fv(ud." + i + ".location, false, uv." + i + ");\n \n" : "vec2" === o.type && 1 === o.size ? void 0 !== t.uniforms[i].x ? n += "\n cv = ud." + i + ".value;\n v = uv." + i + ";\n\n if(cv[0] !== v.x || cv[1] !== v.y)\n {\n cv[0] = v.x;\n cv[1] = v.y;\n gl.uniform2f(ud." + i + ".location, v.x, v.y);\n }\n" : n += "\n cv = ud." + i + ".value;\n v = uv." + i + ";\n\n if(cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n gl.uniform2f(ud." + i + ".location, v[0], v[1]);\n }\n \n" : "vec4" === o.type && 1 === o.size ? void 0 !== t.uniforms[i].width ? n += "\n cv = ud." + i + ".value;\n v = uv." + i + ";\n\n if(cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height)\n {\n cv[0] = v.x;\n cv[1] = v.y;\n cv[2] = v.width;\n cv[3] = v.height;\n gl.uniform4f(ud." + i + ".location, v.x, v.y, v.width, v.height)\n }\n" : n += "\n cv = ud." + i + ".value;\n v = uv." + i + ";\n\n if(cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4f(ud." + i + ".location, v[0], v[1], v[2], v[3])\n }\n \n" : n += "\n cv = ud." + i + ".value;\n v = uv." + i + ";\n " + (1 === o.size ? be : xe)[o.type].replace("location", "ud." + i + ".location") + ";\n" : (n += "\n renderer.texture.bind(uv." + i + ", " + r + ");\n\n if(ud." + i + ".value !== " + r + ")\n {\n ud." + i + ".value = " + r + ";\n gl.uniform1i(ud." + i + ".location, " + r + ");\n; // eslint-disable-line max-len\n }\n", r++) : t.uniforms[i].group && (n += "\n renderer.shader.syncUniformGroup(uv." + i + ");\n ") + } + return new Function("ud", "uv", "renderer", n) + }(t, this.shader.program.uniformData)), t.syncUniforms[this.shader.program.id] = this.cache[e], t.syncUniforms[this.shader.program.id] + }, ShaderSystem.prototype.getSignature = function(t, e) { + var r = t.uniforms, + n = []; + for (var i in r) n.push(i), e[i] && n.push(e[i].type); + return n.join("-") + }, ShaderSystem.prototype.getglProgram = function() { + return this.shader ? this.shader.program.glPrograms[this.renderer.CONTEXT_UID] : null + }, ShaderSystem.prototype.generateShader = function(t) { + var e = this.gl, + r = t.program, + n = {}; + for (var i in r.attributeData) n[i] = r.attributeData[i].location; + var o = compileProgram(e, r.vertexSrc, r.fragmentSrc, n), + a = {}; + for (var s in r.uniformData) { + var u = r.uniformData[s]; + a[s] = { + location: e.getUniformLocation(o, s), + value: core_es_defaultValue(u.type, u.size) + } + } + var h = new Xe(o, a); + return r.glPrograms[this.renderer.CONTEXT_UID] = h + }, ShaderSystem.prototype.reset = function() { + this.program = null, this.shader = null + }, ShaderSystem.prototype.destroy = function() { + this.destroyed = !0 + }, ShaderSystem + }(Ut); + var He = function(e) { + function StateSystem(t) { + e.call(this, t), this.gl = null, this.stateId = 0, this.polygonOffset = 0, this.blendMode = y.NONE, this._blendEq = !1, this.map = [], this.map[0] = this.setBlend, this.map[1] = this.setOffset, this.map[2] = this.setCullFace, this.map[3] = this.setDepthTest, this.map[4] = this.setFrontFace, this.checks = [], this.defaultState = new Ce, this.defaultState.blend = !0, this.defaultState.depth = !0 + } + return e && (StateSystem.__proto__ = e), ((StateSystem.prototype = Object.create(e && e.prototype)).constructor = StateSystem).prototype.contextChange = function(t) { + var e, r; + this.gl = t, this.blendModes = (e = t, void 0 === r && (r = []), r[y.NORMAL] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.ADD] = [e.ONE, e.DST_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.MULTIPLY] = [e.DST_COLOR, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.SCREEN] = [e.ONE, e.ONE_MINUS_SRC_COLOR, e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.OVERLAY] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.DARKEN] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.LIGHTEN] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.COLOR_DODGE] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.COLOR_BURN] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.HARD_LIGHT] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.SOFT_LIGHT] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.DIFFERENCE] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.EXCLUSION] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.HUE] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.SATURATION] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.COLOR] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.LUMINOSITY] = [e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.NONE] = [0, 0], r[y.NORMAL_NPM] = [e.SRC_ALPHA, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA], r[y.ADD_NPM] = [e.SRC_ALPHA, e.DST_ALPHA, e.ONE, e.DST_ALPHA], r[y.SCREEN_NPM] = [e.SRC_ALPHA, e.ONE_MINUS_SRC_COLOR, e.ONE, e.ONE_MINUS_SRC_COLOR], r[y.SRC_IN] = [e.DST_ALPHA, e.ZERO], r[y.SRC_OUT] = [e.ONE_MINUS_DST_ALPHA, e.ZERO], r[y.SRC_ATOP] = [e.DST_ALPHA, e.ONE_MINUS_SRC_ALPHA], r[y.DST_OVER] = [e.ONE_MINUS_DST_ALPHA, e.ONE], r[y.DST_IN] = [e.ZERO, e.SRC_ALPHA], r[y.DST_OUT] = [e.ZERO, e.ONE_MINUS_SRC_ALPHA], r[y.DST_ATOP] = [e.ONE_MINUS_DST_ALPHA, e.SRC_ALPHA], r[y.SUBTRACT] = [e.ONE, e.ONE, e.ONE, e.ONE, e.FUNC_REVERSE_SUBTRACT, e.FUNC_ADD], r), this.setState(this.defaultState), this.reset() + }, StateSystem.prototype.setState = function(t) { + if (t = t || this.defaultState, this.stateId !== t.data) { + for (var e = this.stateId ^ t.data, r = 0; e;) 1 & e && this.map[r].call(this, !!(t.data & 1 << r)), e >>= 1, r++; + this.stateId = t.data + } + for (var n = 0; n < this.checks.length; n++) this.checks[n](this, t) + }, StateSystem.prototype.forceState = function(t) { + t = t || this.defaultState; + for (var e = 0; e < this.map.length; e++) this.map[e].call(this, !!(t.data & 1 << e)); + for (var r = 0; r < this.checks.length; r++) this.checks[r](this, t); + this.stateId = t.data + }, StateSystem.prototype.setBlend = function(t) { + this.updateCheck(StateSystem.checkBlendMode, t), this.gl[t ? "enable" : "disable"](this.gl.BLEND) + }, StateSystem.prototype.setOffset = function(t) { + this.gl[t ? "enable" : "disable"](this.gl.POLYGON_OFFSET_FILL) + }, StateSystem.prototype.setDepthTest = function(t) { + this.gl[t ? "enable" : "disable"](this.gl.DEPTH_TEST) + }, StateSystem.prototype.setCullFace = function(t) { + this.gl[t ? "enable" : "disable"](this.gl.CULL_FACE) + }, StateSystem.prototype.setFrontFace = function(t) { + this.gl.frontFace(this.gl[t ? "CW" : "CCW"]) + }, StateSystem.prototype.setBlendMode = function(t) { + if (t !== this.blendMode) { + this.blendMode = t; + var e = this.blendModes[t], + r = this.gl; + 2 === e.length ? r.blendFunc(e[0], e[1]) : r.blendFuncSeparate(e[0], e[1], e[2], e[3]), 6 === e.length ? (this._blendEq = !0, r.blendEquationSeparate(e[4], e[5])) : this._blendEq && (this._blendEq = !1, r.blendEquationSeparate(r.FUNC_ADD, r.FUNC_ADD)) + } + }, StateSystem.prototype.setPolygonOffset = function(t, e) { + this.gl.polygonOffset(t, e) + }, StateSystem.prototype.reset = function() { + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, !1), this.forceState(0), this._blendEq = !0, this.blendMode = -1, this.setBlendMode(0) + }, StateSystem.prototype.updateCheck = function(t, e) { + var r = this.checks.indexOf(t); + e && -1 === r ? this.checks.push(t) : e || -1 === r || this.checks.splice(r, 1) + }, StateSystem.checkBlendMode = function(t, e) { + t.setBlendMode(e.blendMode) + }, StateSystem + }(Ut), + We = function(e) { + function TextureGCSystem(t) { + e.call(this, t), this.count = 0, this.checkCount = 0, this.maxIdle = M.GC_MAX_IDLE, this.checkCountMax = M.GC_MAX_CHECK_COUNT, this.mode = M.GC_MODE + } + return e && (TextureGCSystem.__proto__ = e), ((TextureGCSystem.prototype = Object.create(e && e.prototype)).constructor = TextureGCSystem).prototype.postrender = function() { + this.count++, this.mode !== A.MANUAL && (this.checkCount++, this.checkCount > this.checkCountMax && (this.checkCount = 0, this.run())) + }, TextureGCSystem.prototype.run = function() { + for (var t = this.renderer.texture, e = t.managedTextures, r = !1, n = 0; n < e.length; n++) { + var i = e[n]; + !i.framebuffer && this.count - i.touched > this.maxIdle && (t.destroyTexture(i, !0), r = !(e[n] = null)) + } + if (r) { + for (var o = 0, a = 0; a < e.length; a++) null !== e[a] && (e[o++] = e[a]); + e.length = o + } + }, TextureGCSystem.prototype.unload = function(t) { + var e = this.renderer.textureSystem; + t._texture && t._texture._glRenderTargets && e.destroyTexture(t._texture); + for (var r = t.children.length - 1; 0 <= r; r--) this.unload(t.children[r]) + }, TextureGCSystem + }(Ut), + Ye = function(t) { + this.texture = t, this.width = -1, this.height = -1, this.dirtyId = -1, this.dirtyStyleId = -1, this.mipmap = !1, this.wrapMode = 33071 + }, + Ke = function(e) { + function TextureSystem(t) { + e.call(this, t), this.boundTextures = [], this.currentLocation = -1, this.managedTextures = [], this._unknownBoundTextures = !1, this.unknownTexture = new Rt + } + return e && (TextureSystem.__proto__ = e), ((TextureSystem.prototype = Object.create(e && e.prototype)).constructor = TextureSystem).prototype.contextChange = function() { + var t = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID, this.webGLVersion = this.renderer.context.webGLVersion; + var e = t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS); + this.boundTextures.length = e; + for (var r = 0; r < e; r++) this.boundTextures[r] = null; + this.emptyTextures = {}; + var n = new Ye(t.createTexture()); + t.bindTexture(t.TEXTURE_2D, n.texture), t.texImage2D(t.TEXTURE_2D, 0, t.RGBA, 1, 1, 0, t.RGBA, t.UNSIGNED_BYTE, new Uint8Array(4)), this.emptyTextures[t.TEXTURE_2D] = n, this.emptyTextures[t.TEXTURE_CUBE_MAP] = new Ye(t.createTexture()), t.bindTexture(t.TEXTURE_CUBE_MAP, this.emptyTextures[t.TEXTURE_CUBE_MAP].texture); + for (var i = 0; i < 6; i++) t.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, t.RGBA, 1, 1, 0, t.RGBA, t.UNSIGNED_BYTE, null); + t.texParameteri(t.TEXTURE_CUBE_MAP, t.TEXTURE_MAG_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_CUBE_MAP, t.TEXTURE_MIN_FILTER, t.LINEAR); + for (var o = 0; o < this.boundTextures.length; o++) this.bind(null, o) + }, TextureSystem.prototype.bind = function(t, e) { + void 0 === e && (e = 0); + var r = this.gl; + if (t) { + if ((t = t.baseTexture || t).valid) { + t.touched = this.renderer.textureGC.count; + var n = t._glTextures[this.CONTEXT_UID] || this.initTexture(t); + this.currentLocation !== e && (this.currentLocation = e, r.activeTexture(r.TEXTURE0 + e)), this.boundTextures[e] !== t && r.bindTexture(t.target, n.texture), n.dirtyId !== t.dirtyId && this.updateTexture(t), this.boundTextures[e] = t + } + } else this.currentLocation !== e && (this.currentLocation = e, r.activeTexture(r.TEXTURE0 + e)), r.bindTexture(r.TEXTURE_2D, this.emptyTextures[r.TEXTURE_2D].texture), this.boundTextures[e] = null + }, TextureSystem.prototype.reset = function() { + this._unknownBoundTextures = !0, this.currentLocation = -1; + for (var t = 0; t < this.boundTextures.length; t++) this.boundTextures[t] = this.unknownTexture + }, TextureSystem.prototype.unbind = function(t) { + var e = this.gl, + r = this.boundTextures; + if (this._unknownBoundTextures) { + this._unknownBoundTextures = !1; + for (var n = 0; n < r.length; n++) r[n] === this.unknownTexture && this.bind(null, n) + } + for (var i = 0; i < r.length; i++) r[i] === t && (this.currentLocation !== i && (e.activeTexture(e.TEXTURE0 + i), this.currentLocation = i), e.bindTexture(e.TEXTURE_2D, this.emptyTextures[t.target].texture), r[i] = null) + }, TextureSystem.prototype.initTexture = function(t) { + var e = new Ye(this.gl.createTexture()); + return e.dirtyId = -1, t._glTextures[this.CONTEXT_UID] = e, this.managedTextures.push(t), t.on("dispose", this.destroyTexture, this), e + }, TextureSystem.prototype.updateTexture = function(t) { + var e = t._glTextures[this.CONTEXT_UID], + r = this.renderer; + if (t.resource && t.resource.upload(r, t, e)); + else { + var n = t.realWidth, + i = t.realHeight, + o = r.gl; + (e.width !== n || e.height !== i || e.dirtyId < 0) && (e.width = n, e.height = i, o.texImage2D(t.target, 0, t.format, n, i, 0, t.format, t.type, null)) + } + t.dirtyStyleId !== e.dirtyStyleId && this.updateTextureStyle(t), e.dirtyId = t.dirtyId + }, TextureSystem.prototype.destroyTexture = function(t, e) { + var r = this.gl; + if ((t = t.baseTexture || t)._glTextures[this.renderer.CONTEXT_UID] && (this.unbind(t), r.deleteTexture(t._glTextures[this.renderer.CONTEXT_UID].texture), t.off("dispose", this.destroyTexture, this), delete t._glTextures[this.renderer.CONTEXT_UID], !e)) { + var n = this.managedTextures.indexOf(t); - 1 !== n && removeItems(this.managedTextures, n, 1) + } + }, TextureSystem.prototype.updateTextureStyle = function(t) { + var e = t._glTextures[this.CONTEXT_UID]; + e && (t.mipmap !== E.POW2 && 2 === this.webGLVersion || t.isPowerOfTwo ? (e.mipmap = 1 <= t.mipmap, e.wrapMode = t.wrapMode) : (e.mipmap = 0, e.wrapMode = S.CLAMP), t.resource && t.resource.style(this.renderer, t, e) || this.setStyle(t, e), e.dirtyStyleId = t.dirtyStyleId) + }, TextureSystem.prototype.setStyle = function(t, e) { + var r = this.gl; + e.mipmap && r.generateMipmap(t.target), r.texParameteri(t.target, r.TEXTURE_WRAP_S, e.wrapMode), r.texParameteri(t.target, r.TEXTURE_WRAP_T, e.wrapMode), e.mipmap ? r.texParameteri(t.target, r.TEXTURE_MIN_FILTER, t.scaleMode ? r.LINEAR_MIPMAP_LINEAR : r.NEAREST_MIPMAP_NEAREST) : r.texParameteri(t.target, r.TEXTURE_MIN_FILTER, t.scaleMode ? r.LINEAR : r.NEAREST), r.texParameteri(t.target, r.TEXTURE_MAG_FILTER, t.scaleMode ? r.LINEAR : r.NEAREST) + }, TextureSystem + }(Ut), + Ze = { + FilterSystem: ue, + BatchSystem: ce, + ContextSystem: fe, + FramebufferSystem: de, + GeometrySystem: ve, + MaskSystem: Be, + StencilSystem: Ue, + ProjectionSystem: Ge, + RenderTextureSystem: ze, + ShaderSystem: qe, + StateSystem: He, + TextureGCSystem: We, + TextureSystem: Ke + }, + Qe = new Y, + Je = function(r) { + function AbstractRenderer(t, e) { + r.call(this), (e = Object.assign({}, M.RENDER_OPTIONS, e)).roundPixels && (M.ROUND_PIXELS = e.roundPixels, deprecation(0, "Renderer roundPixels option is deprecated, please use PIXI.settings.ROUND_PIXELS", 2)), this.options = e, this.type = g.UNKNOWN, this.screen = new ot(0, 0, e.width, e.height), this.view = e.view || document.createElement("canvas"), this.resolution = e.resolution || M.RESOLUTION, this.transparent = e.transparent, this.autoDensity = e.autoDensity || e.autoResize || !1, this.preserveDrawingBuffer = e.preserveDrawingBuffer, this.clearBeforeRender = e.clearBeforeRender, this._backgroundColor = 0, this._backgroundColorRgba = [0, 0, 0, 0], this._backgroundColorString = "#000000", this.backgroundColor = e.backgroundColor || this._backgroundColor, this._tempDisplayObjectParent = new pt, this._lastObjectRendered = this._tempDisplayObjectParent, this.plugins = {} + } + r && (AbstractRenderer.__proto__ = r); + var t = { + width: { + configurable: !0 + }, + height: { + configurable: !0 + }, + backgroundColor: { + configurable: !0 + } + }; + return ((AbstractRenderer.prototype = Object.create(r && r.prototype)).constructor = AbstractRenderer).prototype.initPlugins = function(t) { + for (var e in t) this.plugins[e] = new t[e](this) + }, t.width.get = function() { + return this.view.width + }, t.height.get = function() { + return this.view.height + }, AbstractRenderer.prototype.resize = function(t, e) { + this.screen.width = t, this.screen.height = e, this.view.width = t * this.resolution, this.view.height = e * this.resolution, this.autoDensity && (this.view.style.width = t + "px", this.view.style.height = e + "px") + }, AbstractRenderer.prototype.generateTexture = function(t, e, r, n) { + 0 === (n = n || t.getLocalBounds()).width && (n.width = 1), 0 === n.height && (n.height = 1); + var i = Wt.create(0 | n.width, 0 | n.height, e, r); + return Qe.tx = -n.x, Qe.ty = -n.y, this.render(t, i, !1, Qe, !!t.parent), i + }, AbstractRenderer.prototype.destroy = function(t) { + for (var e in this.plugins) this.plugins[e].destroy(), this.plugins[e] = null; + t && this.view.parentNode && this.view.parentNode.removeChild(this.view), this.plugins = null, this.type = g.UNKNOWN, this.view = null, this.screen = null, this.resolution = 0, this.transparent = !1, this.autoDensity = !1, this.blendModes = null, this.options = null, this.preserveDrawingBuffer = !1, this.clearBeforeRender = !1, this._backgroundColor = 0, this._backgroundColorRgba = null, this._backgroundColorString = null, this._tempDisplayObjectParent = null, this._lastObjectRendered = null + }, t.backgroundColor.get = function() { + return this._backgroundColor + }, t.backgroundColor.set = function(t) { + this._backgroundColor = t, this._backgroundColorString = hex2string(t), hex2rgb(t, this._backgroundColorRgba) + }, Object.defineProperties(AbstractRenderer.prototype, t), AbstractRenderer + }(l.a), + $e = function(r) { + function Renderer(t) { + void 0 === t && (t = {}), r.call(this, "WebGL", t), t = this.options, this.type = g.WEBGL, this.gl = null, this.CONTEXT_UID = 0, this.runners = { + destroy: new gt("destroy"), + contextChange: new gt("contextChange", 1), + reset: new gt("reset"), + update: new gt("update"), + postrender: new gt("postrender"), + prerender: new gt("prerender"), + resize: new gt("resize", 2) + }, this.globalUniforms = new oe({ + projectionMatrix: new Y + }, !0), this.addSystem(Be, "mask").addSystem(fe, "context").addSystem(He, "state").addSystem(qe, "shader").addSystem(Ke, "texture").addSystem(ve, "geometry").addSystem(de, "framebuffer").addSystem(Ue, "stencil").addSystem(Ge, "projection").addSystem(We, "textureGC").addSystem(ue, "filter").addSystem(ze, "renderTexture").addSystem(ce, "batch"), this.initPlugins(Renderer.__plugins), t.context ? this.context.initFromContext(t.context) : this.context.initFromOptions({ + alpha: this.transparent, + antialias: t.antialias, + premultipliedAlpha: this.transparent && "notMultiplied" !== this.transparent, + stencil: !0, + preserveDrawingBuffer: t.preserveDrawingBuffer, + powerPreference: this.options.powerPreference + }), this.renderingToScreen = !0, sayHello(2 === this.context.webGLVersion ? "WebGL 2" : "WebGL 1"), this.resize(this.options.width, this.options.height) + } + return r && (Renderer.__proto__ = r), ((Renderer.prototype = Object.create(r && r.prototype)).constructor = Renderer).create = function(t) { + if (isWebGLSupported()) return new Renderer(t); + throw new Error('WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.') + }, Renderer.prototype.addSystem = function(t, e) { + e || (e = t.name); + var r = new t(this); + if (this[e]) throw new Error('Whoops! The name "' + e + '" is already in use'); + for (var n in this[e] = r, this.runners) this.runners[n].add(r); + return this + }, Renderer.prototype.render = function(t, e, r, n, i) { + if (this.renderingToScreen = !e, this.runners.prerender.run(), this.emit("prerender"), !this.context.isLost) { + if (e || (this._lastObjectRendered = t), !i) { + var o = t.parent; + t.parent = this._tempDisplayObjectParent, t.updateTransform(), t.parent = o + } + this.renderTexture.bind(e), this.batch.currentRenderer.start(), (void 0 !== r ? r : this.clearBeforeRender) && this.renderTexture.clear(), t.render(this), this.batch.currentRenderer.flush(), e && e.baseTexture.update(), this.runners.postrender.run(), this.emit("postrender") + } + }, Renderer.prototype.resize = function(t, e) { + r.prototype.resize.call(this, t, e), this.runners.resize.run(t, e) + }, Renderer.prototype.reset = function() { + return this.runners.reset.run(), this + }, Renderer.prototype.clear = function() { + this.framebuffer.bind(), this.framebuffer.clear() + }, Renderer.prototype.destroy = function(t) { + this.runners.destroy.run(), r.prototype.destroy.call(this, t), this.gl = null + }, Renderer.registerPlugin = function(t, e) { + (Renderer.__plugins = Renderer.__plugins || {})[t] = e + }, Renderer + }(Je); + + function autoDetectRenderer(t) { + return $e.create(t) + } + var tr = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n vTextureCoord = aTextureCoord;\n}", + er = "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n", + rr = function(t) { + function CubeTexture() { + t.apply(this, arguments) + } + return t && (CubeTexture.__proto__ = t), ((CubeTexture.prototype = Object.create(t && t.prototype)).constructor = CubeTexture).from = function(t, e) { + return new CubeTexture(new kt(t, e)) + }, CubeTexture + }(Rt), + nr = function(e) { + function BatchGeometry(t) { + void 0 === t && (t = !1), e.call(this), this._buffer = new Zt(null, t, !1), this._indexBuffer = new Zt(null, t, !0), this.addAttribute("aVertexPosition", this._buffer, 2, !1, w.FLOAT).addAttribute("aTextureCoord", this._buffer, 2, !1, w.FLOAT).addAttribute("aColor", this._buffer, 4, !0, w.UNSIGNED_BYTE).addAttribute("aTextureId", this._buffer, 1, !0, w.FLOAT).addIndex(this._indexBuffer) + } + return e && (BatchGeometry.__proto__ = e), (BatchGeometry.prototype = Object.create(e && e.prototype)).constructor = BatchGeometry + }(ee), + ir = function() { + this.textures = [], this.ids = [], this.blend = 0, this.textureCount = 0, this.start = 0, this.size = 0, this.type = 4 + }, + or = function(t) { + this.vertices = new ArrayBuffer(t), this.float32View = new Float32Array(this.vertices), this.uint32View = new Uint32Array(this.vertices) + }; + or.prototype.destroy = function() { + this.vertices = null, this.float32View = null, this.uint32View = null + }; + var ar = ["varying vec2 vTextureCoord;", "varying vec4 vColor;", "varying float vTextureId;", "uniform sampler2D uSamplers[%count%];", "void main(void){", "vec4 color;", "%forloop%", "gl_FragColor = color * vColor;", "}"].join("\n"), + sr = {}, + ur = {}; + + function generateMultiTextureShader(t, e) { + if (!ur[e]) { + for (var r = new Int32Array(e), n = 0; n < e; n++) r[n] = n; + sr[e] = oe.from({ + uSamplers: r + }, !0); + var i = ar; + i = (i = i.replace(/%count%/gi, e)).replace(/%forloop%/gi, function(t) { + var e = ""; + e += "\n", e += "\n"; + for (var r = 0; r < t; r++) 0 < r && (e += "\nelse "), r < t - 1 && (e += "if(vTextureId < " + r + ".5)"), e += "\n{", e += "\n\tcolor = texture2D(uSamplers[" + r + "], vTextureCoord);", e += "\n}"; + return e += "\n", e += "\n" + }(e)), ur[e] = new Ae("precision highp float;\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\nattribute float aTextureId;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform vec4 tint;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\nvarying float vTextureId;\n\nvoid main(void){\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vTextureId = aTextureId;\n vColor = aColor * tint;\n}\n", i) + } + var o = { + tint: new Float32Array([1, 1, 1, 1]), + translationMatrix: new Y, + default: sr[e] + }; + return new Ie(ur[e], o) + } + var hr = 0, + cr = function(r) { + function BatchRenderer(t) { + r.call(this, t), this.vertSize = 6, this.vertByteSize = 4 * this.vertSize, this.size = 8e3, this.currentSize = 0, this.currentIndexSize = 0, this.attributeBuffers = {}, this.aBuffers = {}, this.iBuffers = {}, this.onlySprites = !1, this.shader = null, this.currentIndex = 0, this.groups = []; + for (var e = 0; e < this.size / 4; e++) this.groups[e] = new ir; + this.elements = [], this.vaos = [], this.vaoMax = 2, this.vertexCount = 0, this.renderer.on("prerender", this.onPrerender, this), this.state = Ce.for2d() + } + return r && (BatchRenderer.__proto__ = r), ((BatchRenderer.prototype = Object.create(r && r.prototype)).constructor = BatchRenderer).prototype.contextChange = function() { + var t = this.renderer.gl; + M.PREFER_ENV === v.WEBGL_LEGACY ? this.MAX_TEXTURES = 1 : (this.MAX_TEXTURES = Math.min(t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS), M.SPRITE_MAX_TEXTURES), this.MAX_TEXTURES = checkMaxIfStatementsInShader(this.MAX_TEXTURES, t)), this.shader = generateMultiTextureShader(0, this.MAX_TEXTURES); + for (var e = 0; e < this.vaoMax; e++) this.vaos[e] = new nr + }, BatchRenderer.prototype.onPrerender = function() { + this.vertexCount = 0 + }, BatchRenderer.prototype.render = function(t) { + t._texture.valid && (this.currentSize + t.vertexData.length / 2 > this.size && this.flush(), this.elements[this.currentIndex++] = t, this.currentSize += t.vertexData.length / 2, this.currentIndexSize += t.indices.length) + }, BatchRenderer.prototype.getIndexBuffer = function(t) { + var e = nextPow2(Math.ceil(t / 12)), + r = log2(e), + n = 12 * e; + this.iBuffers.length <= r && (this.iBuffers.length = r + 1); + var i = this.iBuffers[r]; + return i || (this.iBuffers[r] = i = new Uint16Array(n)), i + }, BatchRenderer.prototype.getAttributeBuffer = function(t) { + var e = nextPow2(Math.ceil(t / 8)), + r = log2(e), + n = 8 * e; + this.aBuffers.length <= r && (this.iBuffers.length = r + 1); + var i = this.aBuffers[n]; + return i || (this.aBuffers[n] = i = new or(n * this.vertByteSize)), i + }, BatchRenderer.prototype.flush = function() { + if (0 !== this.currentSize) { + var t, e, r, n = this.renderer.gl, + i = this.MAX_TEXTURES, + o = this.getAttributeBuffer(this.currentSize), + a = this.getIndexBuffer(this.currentIndexSize), + s = this.elements, + u = this.groups, + h = o.float32View, + c = o.uint32View, + l = this.renderer.textureGC.count, + f = 0, + d = 0, + p = 0, + m = 0, + v = u[0], + g = -1; + for (v.textureCount = 0, v.start = 0, v.blend = g, hr++, r = 0; r < this.currentIndex; ++r) { + var y = s[r]; + s[r] = null, t = y._texture.baseTexture; + var _ = C[t.premultiplyAlpha ? 1 : 0][y.blendMode]; + g !== _ && (g = _, e = null, m = i, hr++), e !== t && (e = t)._enabled !== hr && (m === i && (hr++, m = 0, v.size = d - v.start, (v = u[p++]).textureCount = 0, v.blend = g, v.start = d), t.touched = l, t._enabled = hr, t._id = m, v.textures[v.textureCount++] = t, m++), this.packGeometry(y, h, c, a, f, d), f += y.vertexData.length / 2 * this.vertSize, d += y.indices.length + } + v.size = d - v.start, M.CAN_UPLOAD_SAME_BUFFER ? (this.vaos[this.vertexCount]._buffer.update(o.vertices, 0), this.vaos[this.vertexCount]._indexBuffer.update(a, 0), this.renderer.geometry.updateBuffers()) : (this.vaoMax <= this.vertexCount && (this.vaoMax++, this.vaos[this.vertexCount] = new nr), this.vaos[this.vertexCount]._buffer.update(o.vertices, 0), this.vaos[this.vertexCount]._indexBuffer.update(a, 0), this.renderer.geometry.bind(this.vaos[this.vertexCount]), this.renderer.geometry.updateBuffers(), this.vertexCount++); + var b = this.renderer.texture, + x = this.renderer.state; + for (r = 0; r < p; r++) { + for (var w = u[r], T = w.textureCount, S = 0; S < T; S++) b.bind(w.textures[S], S), w.textures[S] = null; + x.setBlendMode(w.blend), n.drawElements(w.type, w.size, n.UNSIGNED_SHORT, 2 * w.start) + } + this.currentIndex = 0, this.currentSize = 0, this.currentIndexSize = 0 + } + }, BatchRenderer.prototype.packGeometry = function(t, e, r, n, i, o) { + for (var a = i / this.vertSize, s = t.uvs, u = t.indices, h = t.vertexData, c = t._texture.baseTexture._id, l = Math.min(t.worldAlpha, 1), f = l < 1 && t._texture.baseTexture.premultiplyAlpha ? premultiplyTint(t._tintRGB, l) : t._tintRGB + (255 * l << 24), d = 0; d < h.length; d += 2) e[i++] = h[d], e[i++] = h[d + 1], e[i++] = s[d], e[i++] = s[d + 1], r[i++] = f, e[i++] = c; + for (var p = 0; p < u.length; p++) n[o++] = a + u[p] + }, BatchRenderer.prototype.start = function() { + this.renderer.state.setState(this.state), this.renderer.shader.bind(this.shader), M.CAN_UPLOAD_SAME_BUFFER && this.renderer.geometry.bind(this.vaos[this.vertexCount]) + }, BatchRenderer.prototype.stop = function() { + this.flush() + }, BatchRenderer.prototype.destroy = function() { + for (var t = 0; t < this.vaoMax; t++) this.vaos[t] && this.vaos[t].destroy(); + this.indexBuffer && this.indexBuffer.destroy(), this.renderer.off("prerender", this.onPrerender, this), this.shader && (this.shader.destroy(), this.shader = null), this.vaos = null, this.indexBuffer = null, this.indices = null, this.sprites = null, r.prototype.destroy.call(this) + }, BatchRenderer + }(he), + lr = new ot, + fr = function(t) { + (this.renderer = t).extract = this + }; + fr.prototype.image = function(t) { + var e = new Image; + return e.src = this.base64(t), e + }, fr.prototype.base64 = function(t) { + return this.canvas(t).toDataURL() + }, fr.prototype.canvas = function(t) { + var e, r, n, i = this.renderer, + o = !1, + a = !1; + t && (t instanceof Wt ? n = t : (n = this.renderer.generateTexture(t), a = !0)), n ? (e = n.baseTexture.resolution, r = n.frame, o = !1, i.renderTexture.bind(n)) : (e = this.renderer.resolution, o = !0, (r = lr).width = this.renderer.width, r.height = this.renderer.height, i.renderTexture.bind(null)); + var s = r.width * e, + u = r.height * e, + h = new L(s, u, 1), + c = new Uint8Array(4 * s * u), + l = i.gl; + l.readPixels(r.x * e, r.y * e, s, u, l.RGBA, l.UNSIGNED_BYTE, c); + var f = h.context.getImageData(0, 0, s, u); + return f.data.set(c), h.context.putImageData(f, 0, 0), o && (h.context.scale(1, -1), h.context.drawImage(h.canvas, 0, -u)), a && n.destroy(!0), h.canvas + }, fr.prototype.pixels = function(t) { + var e, r, n, i = this.renderer, + o = !1; + t && (t instanceof Wt ? n = t : (n = this.renderer.generateTexture(t), o = !0)), n ? (e = n.baseTexture.resolution, r = n.frame, i.renderTexture.bind(n)) : (e = i.resolution, (r = lr).width = i.width, r.height = i.height, i.renderTexture.bind(null)); + var a = r.width * e, + s = r.height * e, + u = new Uint8Array(4 * a * s), + h = i.gl; + return h.readPixels(r.x * e, r.y * e, a, s, h.RGBA, h.UNSIGNED_BYTE, u), o && n.destroy(!0), u + }, fr.prototype.destroy = function() { + this.renderer.extract = null, this.renderer = null + }; + var dr = function() { + this.global = new q, this.target = null, this.originalEvent = null, this.identifier = null, this.isPrimary = !1, this.button = 0, this.buttons = 0, this.width = 0, this.height = 0, this.tiltX = 0, this.tiltY = 0, this.pointerType = null, this.pressure = 0, this.rotationAngle = 0, this.twist = 0, this.tangentialPressure = 0 + }, + pr = { + pointerId: { + configurable: !0 + } + }; + pr.pointerId.get = function() { + return this.identifier + }, dr.prototype.getLocalPosition = function(t, e, r) { + return t.worldTransform.applyInverse(r || this.global, e) + }, dr.prototype.copyEvent = function(t) { + t.isPrimary && (this.isPrimary = !0), this.button = t.button, this.buttons = Number.isInteger(t.buttons) ? t.buttons : t.which, this.width = t.width, this.height = t.height, this.tiltX = t.tiltX, this.tiltY = t.tiltY, this.pointerType = t.pointerType, this.pressure = t.pressure, this.rotationAngle = t.rotationAngle, this.twist = t.twist || 0, this.tangentialPressure = t.tangentialPressure || 0 + }, dr.prototype.reset = function() { + this.isPrimary = !1 + }, Object.defineProperties(dr.prototype, pr); + var mr = function() { + this.stopped = !1, this.target = null, this.currentTarget = null, this.type = null, this.data = null + }; + mr.prototype.stopPropagation = function() { + this.stopped = !0 + }, mr.prototype.reset = function() { + this.stopped = !1, this.currentTarget = null, this.target = null + }; + var vr = function InteractionTrackingData(t) { + this._pointerId = t, this._flags = InteractionTrackingData.FLAGS.NONE + }, + gr = { + pointerId: { + configurable: !0 + }, + flags: { + configurable: !0 + }, + none: { + configurable: !0 + }, + over: { + configurable: !0 + }, + rightDown: { + configurable: !0 + }, + leftDown: { + configurable: !0 + } + }; + vr.prototype._doSet = function(t, e) { + this._flags = e ? this._flags | t : this._flags & ~t + }, gr.pointerId.get = function() { + return this._pointerId + }, gr.flags.get = function() { + return this._flags + }, gr.flags.set = function(t) { + this._flags = t + }, gr.none.get = function() { + return this._flags === this.constructor.FLAGS.NONE + }, gr.over.get = function() { + return 0 != (this._flags & this.constructor.FLAGS.OVER) + }, gr.over.set = function(t) { + this._doSet(this.constructor.FLAGS.OVER, t) + }, gr.rightDown.get = function() { + return 0 != (this._flags & this.constructor.FLAGS.RIGHT_DOWN) + }, gr.rightDown.set = function(t) { + this._doSet(this.constructor.FLAGS.RIGHT_DOWN, t) + }, gr.leftDown.get = function() { + return 0 != (this._flags & this.constructor.FLAGS.LEFT_DOWN) + }, gr.leftDown.set = function(t) { + this._doSet(this.constructor.FLAGS.LEFT_DOWN, t) + }, Object.defineProperties(vr.prototype, gr), vr.FLAGS = Object.freeze({ + NONE: 0, + OVER: 1, + LEFT_DOWN: 2, + RIGHT_DOWN: 4 + }); + var yr = { + interactive: !1, + interactiveChildren: !0, + hitArea: null, + get buttonMode() { + return "pointer" === this.cursor + }, + set buttonMode(t) { + t ? this.cursor = "pointer" : "pointer" === this.cursor && (this.cursor = null) + }, + cursor: null, + get trackedPointers() { + return void 0 === this._trackedPointers && (this._trackedPointers = {}), this._trackedPointers + }, + _trackedPointers: void 0 + }; + dt.mixin(yr); + var _r = { + target: null, + data: { + global: null + } + }, + br = function(r) { + function InteractionManager(t, e) { + r.call(this), e = e || {}, this.renderer = t, this.autoPreventDefault = void 0 === e.autoPreventDefault || e.autoPreventDefault, this.interactionFrequency = e.interactionFrequency || 10, this.mouse = new dr, this.mouse.identifier = 1, this.mouse.global.set(-999999), this.activeInteractionData = {}, this.activeInteractionData[1] = this.mouse, this.interactionDataPool = [], this.eventData = new mr, this.interactionDOMElement = null, this.moveWhenInside = !1, this.eventsAdded = !1, this.mouseOverRenderer = !1, this.supportsTouchEvents = "ontouchstart" in window, this.supportsPointerEvents = !!window.PointerEvent, this.onPointerUp = this.onPointerUp.bind(this), this.processPointerUp = this.processPointerUp.bind(this), this.onPointerCancel = this.onPointerCancel.bind(this), this.processPointerCancel = this.processPointerCancel.bind(this), this.onPointerDown = this.onPointerDown.bind(this), this.processPointerDown = this.processPointerDown.bind(this), this.onPointerMove = this.onPointerMove.bind(this), this.processPointerMove = this.processPointerMove.bind(this), this.onPointerOut = this.onPointerOut.bind(this), this.processPointerOverOut = this.processPointerOverOut.bind(this), this.onPointerOver = this.onPointerOver.bind(this), this.cursorStyles = { + default: "inherit", + pointer: "pointer" + }, this.currentCursorMode = null, this.cursor = null, this._tempPoint = new q, this.resolution = 1, this.setTargetElement(this.renderer.view, this.renderer.resolution) + } + return r && (InteractionManager.__proto__ = r), ((InteractionManager.prototype = Object.create(r && r.prototype)).constructor = InteractionManager).prototype.hitTest = function(t, e) { + return _r.target = null, _r.data.global = t, e || (e = this.renderer._lastObjectRendered), this.processInteractive(_r, e, null, !0), _r.target + }, InteractionManager.prototype.setTargetElement = function(t, e) { + void 0 === e && (e = 1), this.removeEvents(), this.interactionDOMElement = t, this.resolution = e, this.addEvents() + }, InteractionManager.prototype.addEvents = function() { + this.interactionDOMElement && (xt.system.add(this.update, this, _t.INTERACTION), window.navigator.msPointerEnabled ? (this.interactionDOMElement.style["-ms-content-zooming"] = "none", this.interactionDOMElement.style["-ms-touch-action"] = "none") : this.supportsPointerEvents && (this.interactionDOMElement.style["touch-action"] = "none"), this.supportsPointerEvents ? (window.document.addEventListener("pointermove", this.onPointerMove, !0), this.interactionDOMElement.addEventListener("pointerdown", this.onPointerDown, !0), this.interactionDOMElement.addEventListener("pointerleave", this.onPointerOut, !0), this.interactionDOMElement.addEventListener("pointerover", this.onPointerOver, !0), window.addEventListener("pointercancel", this.onPointerCancel, !0), window.addEventListener("pointerup", this.onPointerUp, !0)) : (window.document.addEventListener("mousemove", this.onPointerMove, !0), this.interactionDOMElement.addEventListener("mousedown", this.onPointerDown, !0), this.interactionDOMElement.addEventListener("mouseout", this.onPointerOut, !0), this.interactionDOMElement.addEventListener("mouseover", this.onPointerOver, !0), window.addEventListener("mouseup", this.onPointerUp, !0)), this.supportsTouchEvents && (this.interactionDOMElement.addEventListener("touchstart", this.onPointerDown, !0), this.interactionDOMElement.addEventListener("touchcancel", this.onPointerCancel, !0), this.interactionDOMElement.addEventListener("touchend", this.onPointerUp, !0), this.interactionDOMElement.addEventListener("touchmove", this.onPointerMove, !0)), this.eventsAdded = !0) + }, InteractionManager.prototype.removeEvents = function() { + this.interactionDOMElement && (xt.system.remove(this.update, this), window.navigator.msPointerEnabled ? (this.interactionDOMElement.style["-ms-content-zooming"] = "", this.interactionDOMElement.style["-ms-touch-action"] = "") : this.supportsPointerEvents && (this.interactionDOMElement.style["touch-action"] = ""), this.supportsPointerEvents ? (window.document.removeEventListener("pointermove", this.onPointerMove, !0), this.interactionDOMElement.removeEventListener("pointerdown", this.onPointerDown, !0), this.interactionDOMElement.removeEventListener("pointerleave", this.onPointerOut, !0), this.interactionDOMElement.removeEventListener("pointerover", this.onPointerOver, !0), window.removeEventListener("pointercancel", this.onPointerCancel, !0), window.removeEventListener("pointerup", this.onPointerUp, !0)) : (window.document.removeEventListener("mousemove", this.onPointerMove, !0), this.interactionDOMElement.removeEventListener("mousedown", this.onPointerDown, !0), this.interactionDOMElement.removeEventListener("mouseout", this.onPointerOut, !0), this.interactionDOMElement.removeEventListener("mouseover", this.onPointerOver, !0), window.removeEventListener("mouseup", this.onPointerUp, !0)), this.supportsTouchEvents && (this.interactionDOMElement.removeEventListener("touchstart", this.onPointerDown, !0), this.interactionDOMElement.removeEventListener("touchcancel", this.onPointerCancel, !0), this.interactionDOMElement.removeEventListener("touchend", this.onPointerUp, !0), this.interactionDOMElement.removeEventListener("touchmove", this.onPointerMove, !0)), this.interactionDOMElement = null, this.eventsAdded = !1) + }, InteractionManager.prototype.update = function(t) { + if (this._deltaTime += t, !(this._deltaTime < this.interactionFrequency) && (this._deltaTime = 0, this.interactionDOMElement)) + if (this.didMove) this.didMove = !1; + else { + for (var e in this.cursor = null, this.activeInteractionData) + if (this.activeInteractionData.hasOwnProperty(e)) { + var r = this.activeInteractionData[e]; + if (r.originalEvent && "touch" !== r.pointerType) { + var n = this.configureInteractionEventForDOMEvent(this.eventData, r.originalEvent, r); + this.processInteractive(n, this.renderer._lastObjectRendered, this.processPointerOverOut, !0) + } + } + this.setCursorMode(this.cursor) + } + }, InteractionManager.prototype.setCursorMode = function(t) { + if (t = t || "default", this.currentCursorMode !== t) { + this.currentCursorMode = t; + var e = this.cursorStyles[t]; + if (e) switch (typeof e) { + case "string": + this.interactionDOMElement.style.cursor = e; + break; + case "function": + e(t); + break; + case "object": + Object.assign(this.interactionDOMElement.style, e) + } else "string" != typeof t || Object.prototype.hasOwnProperty.call(this.cursorStyles, t) || (this.interactionDOMElement.style.cursor = t) + } + }, InteractionManager.prototype.dispatchEvent = function(t, e, r) { + r.stopped || (r.currentTarget = t, r.type = e, t.emit(e, r), t[e] && t[e](r)) + }, InteractionManager.prototype.mapPositionToPoint = function(t, e, r) { + var n; + n = this.interactionDOMElement.parentElement ? this.interactionDOMElement.getBoundingClientRect() : { + x: 0, + y: 0, + width: 0, + height: 0 + }; + var i = 1 / this.resolution; + t.x = (e - n.left) * (this.interactionDOMElement.width / n.width) * i, t.y = (r - n.top) * (this.interactionDOMElement.height / n.height) * i + }, InteractionManager.prototype.processInteractive = function(t, e, r, n, i) { + if (!e || !e.visible) return !1; + var o = t.data.global, + a = !1, + s = i = e.interactive || i, + u = !0; + if (e.hitArea ? (n && (e.worldTransform.applyInverse(o, this._tempPoint), e.hitArea.contains(this._tempPoint.x, this._tempPoint.y) ? a = !0 : u = n = !1), s = !1) : e._mask && n && (e._mask.containsPoint && e._mask.containsPoint(o) || (u = n = !1)), u && e.interactiveChildren && e.children) + for (var h = e.children, c = h.length - 1; 0 <= c; c--) { + var l = h[c], + f = this.processInteractive(t, l, r, n, s); + if (f) { + if (!l.parent) continue; + s = !1, f && (t.target && (n = !1), a = !0) + } + } + return i && (n && !t.target && !e.hitArea && e.containsPoint && e.containsPoint(o) && (a = !0), e.interactive && (a && !t.target && (t.target = e), r && r(t, e, !!a))), a + }, InteractionManager.prototype.onPointerDown = function(t) { + if (!this.supportsTouchEvents || "touch" !== t.pointerType) { + var e = this.normalizeToPointerData(t); + if (this.autoPreventDefault && e[0].isNormalized)(t.cancelable || !("cancelable" in t)) && t.preventDefault(); + for (var r = e.length, n = 0; n < r; n++) { + var i = e[n], + o = this.getInteractionDataForPointerId(i), + a = this.configureInteractionEventForDOMEvent(this.eventData, i, o); + if (a.data.originalEvent = t, this.processInteractive(a, this.renderer._lastObjectRendered, this.processPointerDown, !0), this.emit("pointerdown", a), "touch" === i.pointerType) this.emit("touchstart", a); + else if ("mouse" === i.pointerType || "pen" === i.pointerType) { + var s = 2 === i.button; + this.emit(s ? "rightdown" : "mousedown", this.eventData) + } + } + } + }, InteractionManager.prototype.processPointerDown = function(t, e, r) { + var n = t.data, + i = t.data.identifier; + if (r) + if (e.trackedPointers[i] || (e.trackedPointers[i] = new vr(i)), this.dispatchEvent(e, "pointerdown", t), "touch" === n.pointerType) this.dispatchEvent(e, "touchstart", t); + else if ("mouse" === n.pointerType || "pen" === n.pointerType) { + var o = 2 === n.button; + o ? e.trackedPointers[i].rightDown = !0 : e.trackedPointers[i].leftDown = !0, this.dispatchEvent(e, o ? "rightdown" : "mousedown", t) + } + }, InteractionManager.prototype.onPointerComplete = function(t, e, r) { + for (var n = this.normalizeToPointerData(t), i = n.length, o = t.target !== this.interactionDOMElement ? "outside" : "", a = 0; a < i; a++) { + var s = n[a], + u = this.getInteractionDataForPointerId(s), + h = this.configureInteractionEventForDOMEvent(this.eventData, s, u); + if (h.data.originalEvent = t, this.processInteractive(h, this.renderer._lastObjectRendered, r, e || !o), this.emit(e ? "pointercancel" : "pointerup" + o, h), "mouse" === s.pointerType || "pen" === s.pointerType) { + var c = 2 === s.button; + this.emit(c ? "rightup" + o : "mouseup" + o, h) + } else "touch" === s.pointerType && (this.emit(e ? "touchcancel" : "touchend" + o, h), this.releaseInteractionDataForPointerId(s.pointerId, u)) + } + }, InteractionManager.prototype.onPointerCancel = function(t) { + this.supportsTouchEvents && "touch" === t.pointerType || this.onPointerComplete(t, !0, this.processPointerCancel) + }, InteractionManager.prototype.processPointerCancel = function(t, e) { + var r = t.data, + n = t.data.identifier; + void 0 !== e.trackedPointers[n] && (delete e.trackedPointers[n], this.dispatchEvent(e, "pointercancel", t), "touch" === r.pointerType && this.dispatchEvent(e, "touchcancel", t)) + }, InteractionManager.prototype.onPointerUp = function(t) { + this.supportsTouchEvents && "touch" === t.pointerType || this.onPointerComplete(t, !1, this.processPointerUp) + }, InteractionManager.prototype.processPointerUp = function(t, e, r) { + var n = t.data, + i = t.data.identifier, + o = e.trackedPointers[i], + a = "touch" === n.pointerType, + s = "mouse" === n.pointerType || "pen" === n.pointerType, + u = !1; + if (s) { + var h = 2 === n.button, + c = vr.FLAGS, + l = h ? c.RIGHT_DOWN : c.LEFT_DOWN, + f = void 0 !== o && o.flags & l; + r ? (this.dispatchEvent(e, h ? "rightup" : "mouseup", t), f && (this.dispatchEvent(e, h ? "rightclick" : "click", t), u = !0)) : f && this.dispatchEvent(e, h ? "rightupoutside" : "mouseupoutside", t), o && (h ? o.rightDown = !1 : o.leftDown = !1) + } + r ? (this.dispatchEvent(e, "pointerup", t), a && this.dispatchEvent(e, "touchend", t), o && (s && !u || this.dispatchEvent(e, "pointertap", t), a && (this.dispatchEvent(e, "tap", t), o.over = !1))) : o && (this.dispatchEvent(e, "pointerupoutside", t), a && this.dispatchEvent(e, "touchendoutside", t)), o && o.none && delete e.trackedPointers[i] + }, InteractionManager.prototype.onPointerMove = function(t) { + if (!this.supportsTouchEvents || "touch" !== t.pointerType) { + var e = this.normalizeToPointerData(t); + "mouse" !== e[0].pointerType && "pen" !== e[0].pointerType || (this.didMove = !0, this.cursor = null); + for (var r = e.length, n = 0; n < r; n++) { + var i = e[n], + o = this.getInteractionDataForPointerId(i), + a = this.configureInteractionEventForDOMEvent(this.eventData, i, o); + a.data.originalEvent = t; + var s = "touch" !== i.pointerType || this.moveWhenInside; + this.processInteractive(a, this.renderer._lastObjectRendered, this.processPointerMove, s), this.emit("pointermove", a), "touch" === i.pointerType && this.emit("touchmove", a), "mouse" !== i.pointerType && "pen" !== i.pointerType || this.emit("mousemove", a) + } + "mouse" === e[0].pointerType && this.setCursorMode(this.cursor) + } + }, InteractionManager.prototype.processPointerMove = function(t, e, r) { + var n = t.data, + i = "touch" === n.pointerType, + o = "mouse" === n.pointerType || "pen" === n.pointerType; + o && this.processPointerOverOut(t, e, r), this.moveWhenInside && !r || (this.dispatchEvent(e, "pointermove", t), i && this.dispatchEvent(e, "touchmove", t), o && this.dispatchEvent(e, "mousemove", t)) + }, InteractionManager.prototype.onPointerOut = function(t) { + if (!this.supportsTouchEvents || "touch" !== t.pointerType) { + var e = this.normalizeToPointerData(t)[0]; + "mouse" === e.pointerType && (this.mouseOverRenderer = !1, this.setCursorMode(null)); + var r = this.getInteractionDataForPointerId(e), + n = this.configureInteractionEventForDOMEvent(this.eventData, e, r); + n.data.originalEvent = e, this.processInteractive(n, this.renderer._lastObjectRendered, this.processPointerOverOut, !1), this.emit("pointerout", n), "mouse" === e.pointerType || "pen" === e.pointerType ? this.emit("mouseout", n) : this.releaseInteractionDataForPointerId(r.identifier) + } + }, InteractionManager.prototype.processPointerOverOut = function(t, e, r) { + var n = t.data, + i = t.data.identifier, + o = "mouse" === n.pointerType || "pen" === n.pointerType, + a = e.trackedPointers[i]; + r && !a && (a = e.trackedPointers[i] = new vr(i)), void 0 !== a && (r && this.mouseOverRenderer ? (a.over || (a.over = !0, this.dispatchEvent(e, "pointerover", t), o && this.dispatchEvent(e, "mouseover", t)), o && null === this.cursor && (this.cursor = e.cursor)) : a.over && (a.over = !1, this.dispatchEvent(e, "pointerout", this.eventData), o && this.dispatchEvent(e, "mouseout", t), a.none && delete e.trackedPointers[i])) + }, InteractionManager.prototype.onPointerOver = function(t) { + var e = this.normalizeToPointerData(t)[0], + r = this.getInteractionDataForPointerId(e), + n = this.configureInteractionEventForDOMEvent(this.eventData, e, r); + "mouse" === (n.data.originalEvent = e).pointerType && (this.mouseOverRenderer = !0), this.emit("pointerover", n), "mouse" !== e.pointerType && "pen" !== e.pointerType || this.emit("mouseover", n) + }, InteractionManager.prototype.getInteractionDataForPointerId = function(t) { + var e, r = t.pointerId; + return 1 === r || "mouse" === t.pointerType ? e = this.mouse : this.activeInteractionData[r] ? e = this.activeInteractionData[r] : ((e = this.interactionDataPool.pop() || new dr).identifier = r, this.activeInteractionData[r] = e), e.copyEvent(t), e + }, InteractionManager.prototype.releaseInteractionDataForPointerId = function(t) { + var e = this.activeInteractionData[t]; + e && (delete this.activeInteractionData[t], e.reset(), this.interactionDataPool.push(e)) + }, InteractionManager.prototype.configureInteractionEventForDOMEvent = function(t, e, r) { + return t.data = r, this.mapPositionToPoint(r.global, e.clientX, e.clientY), "touch" === e.pointerType && (e.globalX = r.global.x, e.globalY = r.global.y), r.originalEvent = e, t.reset(), t + }, InteractionManager.prototype.normalizeToPointerData = function(t) { + var e = []; + if (this.supportsTouchEvents && t instanceof TouchEvent) + for (var r = 0, n = t.changedTouches.length; r < n; r++) { + var i = t.changedTouches[r]; + void 0 === i.button && (i.button = t.touches.length ? 1 : 0), void 0 === i.buttons && (i.buttons = t.touches.length ? 1 : 0), void 0 === i.isPrimary && (i.isPrimary = 1 === t.touches.length && "touchstart" === t.type), void 0 === i.width && (i.width = i.radiusX || 1), void 0 === i.height && (i.height = i.radiusY || 1), void 0 === i.tiltX && (i.tiltX = 0), void 0 === i.tiltY && (i.tiltY = 0), void 0 === i.pointerType && (i.pointerType = "touch"), void 0 === i.pointerId && (i.pointerId = i.identifier || 0), void 0 === i.pressure && (i.pressure = i.force || .5), void 0 === i.twist && (i.twist = 0), void 0 === i.tangentialPressure && (i.tangentialPressure = 0), void 0 === i.layerX && (i.layerX = i.offsetX = i.clientX), void 0 === i.layerY && (i.layerY = i.offsetY = i.clientY), i.isNormalized = !0, e.push(i) + } else !(t instanceof MouseEvent) || this.supportsPointerEvents && t instanceof window.PointerEvent || (void 0 === t.isPrimary && (t.isPrimary = !0), void 0 === t.width && (t.width = 1), void 0 === t.height && (t.height = 1), void 0 === t.tiltX && (t.tiltX = 0), void 0 === t.tiltY && (t.tiltY = 0), void 0 === t.pointerType && (t.pointerType = "mouse"), void 0 === t.pointerId && (t.pointerId = 1), void 0 === t.pressure && (t.pressure = .5), void 0 === t.twist && (t.twist = 0), void 0 === t.tangentialPressure && (t.tangentialPressure = 0), t.isNormalized = !0), e.push(t); + return e + }, InteractionManager.prototype.destroy = function() { + this.removeEvents(), this.removeAllListeners(), this.renderer = null, this.mouse = null, this.eventData = null, this.interactionDOMElement = null, this.onPointerDown = null, this.processPointerDown = null, this.onPointerUp = null, this.processPointerUp = null, this.onPointerCancel = null, this.processPointerCancel = null, this.onPointerMove = null, this.processPointerMove = null, this.onPointerOut = null, this.processPointerOverOut = null, this.onPointerOver = null, this._tempPoint = null + }, InteractionManager + }(l.a), + xr = { + adaptive: !0, + maxLength: 10, + minSegments: 8, + maxSegments: 2048, + _segmentsCount: function(t, e) { + if (void 0 === e && (e = 20), !this.adaptive) return e; + var r = Math.ceil(t / this.maxLength); + return r < this.minSegments ? r = this.minSegments : r > this.maxSegments && (r = this.maxSegments), r + } + }, + wr = function() { + this.reset() + }; + wr.prototype.clone = function() { + var t = new wr; + return t.color = this.color, t.alpha = this.alpha, t.texture = this.texture, t.matrix = this.matrix, t.visible = this.visible, t + }, wr.prototype.reset = function() { + this.color = 16777215, this.alpha = 1, this.texture = Ht.WHITE, this.matrix = null, this.visible = !1 + }, wr.prototype.destroy = function() { + this.texture = null, this.matrix = null + }; + var Tr = function(t, e, r, n) { + void 0 === e && (e = null), void 0 === r && (r = null), void 0 === n && (n = null), this.shape = t, this.lineStyle = r, this.fillStyle = e, this.matrix = n, this.type = t.type, this.points = [], this.holes = [] + }; + Tr.prototype.clone = function() { + return new Tr(this.shape, this.fillStyle, this.lineStyle, this.matrix) + }, Tr.prototype.destroy = function() { + this.shape = null, this.holes.length = 0, this.holes = null, this.points.length = 0, this.points = null, this.lineStyle = null, this.fillStyle = null + }; + var Sr = { + build: function(t) { + var e, r, n = t.shape, + i = t.points, + o = n.x, + a = n.y; + if (i.length = 0, r = t.type === W.CIRC ? (e = n.radius, n.radius) : (e = n.width, n.height), 0 !== e && 0 !== r) { + var s = Math.floor(30 * Math.sqrt(n.radius)) || Math.floor(15 * Math.sqrt(n.width + n.height)); + s /= 2.3; + for (var u = 2 * Math.PI / s, h = 0; h < s; h++) i.push(o + Math.sin(u * h) * e, a + Math.cos(u * h) * r); + i.push(i[0], i[1]) + } + }, + triangulate: function(t, e) { + var r = t.points, + n = e.points, + i = e.indices, + o = n.length / 2, + a = o; + n.push(t.shape.x, t.shape.y); + for (var s = 0; s < r.length; s += 2) n.push(r[s], r[s + 1]), i.push(o++, a, o) + } + }; + + function buildLine(t, e) { + t.lineStyle.native ? function(t, e) { + var r = 0, + n = t.points || t.shape.points; + if (0 === n.length) return; + var i = e.points, + o = e.indices, + a = n.length / 2, + s = i.length / 2; + for (r = 1; r < a; r++) { + var u = n[2 * (r - 1)], + h = n[2 * (r - 1) + 1], + c = n[2 * r], + l = n[2 * r + 1]; + i.push(u, h), i.push(c, l), o.push(s++, s++) + } + }(t, e) : function(t, e) { + var r = t.shape, + n = t.points || r.points.slice(); + if (0 === n.length) return; + var i = t.lineStyle, + o = new q(n[0], n[1]), + a = new q(n[n.length - 2], n[n.length - 1]), + s = r.type !== W.POLY || r.closeStroke, + u = o.x === a.x && o.y === a.y; + if (s) { + n = n.slice(), u && (n.pop(), n.pop(), a.set(n[n.length - 2], n[n.length - 1])); + var h = a.x + .5 * (o.x - a.x), + c = a.y + .5 * (o.y - a.y); + n.unshift(h, c), n.push(h, c) + } + var l = e.points, + f = n.length / 2, + d = n.length, + p = l.length / 2, + m = i.width / 2, + v = n[0], + g = n[1], + y = n[2], + _ = n[3], + b = 0, + x = 0, + w = -(g - _), + T = v - y, + S = 0, + M = 0, + E = 0, + A = 0, + P = Math.sqrt(w * w + T * T); + w /= P, T /= P, w *= m, T *= m; + var I = i.alignment, + O = 2 * (1 - I), + C = 2 * I; + l.push(v - w * O, g - T * O), l.push(v + w * C, g + T * C); + for (var R = 1; R < f - 1; ++R) { + v = n[2 * (R - 1)], g = n[2 * (R - 1) + 1], y = n[2 * R], _ = n[2 * R + 1], b = n[2 * (R + 1)], x = n[2 * (R + 1) + 1], w = -(g - _), T = v - y, P = Math.sqrt(w * w + T * T), w /= P, T /= P, w *= m, T *= m, S = -(_ - x), M = y - b, P = Math.sqrt(S * S + M * M), S /= P, M /= P; + var D = -T + g - (-T + _), + F = -w + y - (-w + v), + k = (-w + v) * (-T + _) - (-w + y) * (-T + g), + L = -(M *= m) + x - (-M + _), + N = -(S *= m) + y - (-S + b), + B = (-S + b) * (-M + _) - (-S + y) * (-M + x), + U = D * N - L * F; + if (Math.abs(U) < .1) U += 10.1, l.push(y - w * O, _ - T * O), l.push(y + w * C, _ + T * C); + else { + var G = (F * B - N * k) / U, + j = (L * k - D * B) / U, + z = (G - y) * (G - y) + (j - _) * (j - _); + 196 * m * m < z ? (E = w - S, A = T - M, P = Math.sqrt(E * E + A * A), E /= P, A /= P, E *= m, A *= m, l.push(y - E * O, _ - A * O), l.push(y + E * C, _ + A * C), l.push(y - E * C * O, _ - A * O), d++) : (l.push(y + (G - y) * O, _ + (j - _) * O), l.push(y - (G - y) * C, _ - (j - _) * C)) + } + } + v = n[2 * (f - 2)], g = n[2 * (f - 2) + 1], y = n[2 * (f - 1)], _ = n[2 * (f - 1) + 1], w = -(g - _), T = v - y, P = Math.sqrt(w * w + T * T), w /= P, T /= P, w *= m, T *= m, l.push(y - w * O, _ - T * O), l.push(y + w * C, _ + T * C); + for (var X = e.indices, V = 0; V < d - 2; ++V) X.push(p, p + 1, p + 2), p++ + }(t, e) + } + var Mr = { + build: function(t) { + t.points = t.shape.points.slice() + }, + triangulate: function(t, e) { + var r = t.points, + n = t.holes, + i = e.points, + o = e.indices; + if (6 <= r.length) { + for (var a = [], s = 0; s < n.length; s++) { + var u = n[s]; + a.push(r.length / 2), r = r.concat(u.points) + } + var h = d()(r, a, 2); + if (!h) return; + for (var c = i.length / 2, l = 0; l < h.length; l += 3) o.push(h[l] + c), o.push(h[l + 1] + c), o.push(h[l + 2] + c); + for (var f = 0; f < r.length; f++) i.push(r[f]) + } + } + }, + Er = { + build: function(t) { + var e = t.shape, + r = t.points, + n = e.x, + i = e.y, + o = e.width, + a = e.height, + s = e.radius; + r.length = 0, r.push(n, i + s), quadraticBezierCurve(n, i + a - s, n, i + a, n + s, i + a, r), quadraticBezierCurve(n + o - s, i + a, n + o, i + a, n + o, i + a - s, r), quadraticBezierCurve(n + o, i + s, n + o, i, n + o - s, i, r), quadraticBezierCurve(n + s, i, n, i, n, i + s + 1e-10, r) + }, + triangulate: function(t, e) { + for (var r = t.points, n = e.points, i = e.indices, o = n.length / 2, a = d()(r, null, 2), s = 0, u = a.length; s < u; s += 3) i.push(a[s] + o), i.push(a[s + 1] + o), i.push(a[s + 2] + o); + for (var h = 0, c = r.length; h < c; h++) n.push(r[h], r[++h]) + } + }; + + function getPt(t, e, r) { + return t + (e - t) * r + } + + function quadraticBezierCurve(t, e, r, n, i, o, a) { + void 0 === a && (a = []); + for (var s = a, u = 0, h = 0, c = 0, l = 0, f = 0, d = 0, p = 0, m = 0; p <= 20; ++p) u = getPt(t, r, m = p / 20), h = getPt(e, n, m), c = getPt(r, i, m), l = getPt(n, o, m), f = getPt(u, c, m), d = getPt(h, l, m), s.push(f, d); + return s + } + var Ar = [], + Pr = [], + Ir = 0, + Or = {}; + Or[W.POLY] = Mr, Or[W.CIRC] = Sr, Or[W.ELIP] = Sr, Or[W.RECT] = { + build: function(t) { + var e = t.shape, + r = e.x, + n = e.y, + i = e.width, + o = e.height, + a = t.points; + a.length = 0, a.push(r, n, r + i, n, r + i, n + o, r, n + o) + }, + triangulate: function(t, e) { + var r = t.points, + n = e.points, + i = n.length / 2; + n.push(r[0], r[1], r[2], r[3], r[6], r[7], r[4], r[5]), e.indices.push(i, i + 1, i + 2, i + 1, i + 2, i + 3) + } + }, Or[W.RREC] = Er; + var Cr = function() { + this.style = null, this.size = 0, this.start = 0, this.attribStart = 0, this.attribSize = 0 + }, + Rr = function(r) { + function GraphicsGeometry() { + r.call(this), this.points = [], this.colors = [], this.uvs = [], this.indices = [], this.textureIds = [], this.graphicsData = [], this.dirty = 0, this.batchDirty = -1, this.cacheDirty = -1, this.clearDirty = 0, this.drawCalls = [], this.batches = [], this.shapeIndex = 0, this._bounds = new ft, this.boundsDirty = -1, this.boundsPadding = 0, this.batchable = !1, this.indicesUint16 = null, this.uvsFloat32 = null + } + r && (GraphicsGeometry.__proto__ = r), (GraphicsGeometry.prototype = Object.create(r && r.prototype)).constructor = GraphicsGeometry; + var t = { + bounds: { + configurable: !0 + } + }; + return t.bounds.get = function() { + return this.boundsDirty !== this.dirty && (this.boundsDirty = this.dirty, this.calculateBounds()), this._bounds + }, GraphicsGeometry.prototype.clear = function() { + if (0 < this.graphicsData.length) { + this.boundsDirty = -1, this.dirty++, this.clearDirty++, this.batchDirty++, this.graphicsData.length = 0, this.shapeIndex = 0, this.points.length = 0, this.colors.length = 0, this.uvs.length = 0, this.indices.length = 0; + for (var t = this.textureIds.length = 0; t < this.drawCalls.length; t++) this.drawCalls[t].textures.length = 0, Pr.push(this.drawCalls[t]); + for (var e = this.drawCalls.length = 0; e < this.batches.length; e++) { + var r = this.batches[e]; + r.start = 0, r.attribStart = 0, r.style = null, Ar.push(r) + } + this.batches.length = 0 + } + return this + }, GraphicsGeometry.prototype.drawShape = function(t, e, r, n) { + var i = new Tr(t, e, r, n); + return this.graphicsData.push(i), this.dirty++, this + }, GraphicsGeometry.prototype.drawHole = function(t, e) { + if (!this.graphicsData.length) return null; + var r = new Tr(t, null, null, e), + n = this.graphicsData[this.graphicsData.length - 1]; + return r.lineStyle = n.lineStyle, n.holes.push(r), this.dirty++, r + }, GraphicsGeometry.prototype.destroy = function(t) { + r.prototype.destroy.call(this, t); + for (var e = 0; e < this.graphicsData.length; ++e) this.graphicsData[e].destroy(); + this.points.length = 0, this.points = null, this.colors.length = 0, this.colors = null, this.uvs.length = 0, this.uvs = null, this.indices.length = 0, this.indices = null, this.indexBuffer.destroy(), this.indexBuffer = null, this.graphicsData.length = 0, this.graphicsData = null, this.drawCalls.length = 0, this.drawCalls = null, this.batches.length = 0, this.batches = null, this._bounds = null + }, GraphicsGeometry.prototype.containsPoint = function(t) { + for (var e = this.graphicsData, r = 0; r < e.length; ++r) { + var n = e[r]; + if (n.fillStyle.visible && (n.shape && n.shape.contains(t.x, t.y))) { + if (n.holes) + for (var i = 0; i < n.holes.length; i++) { + if (n.holes[i].shape.contains(t.x, t.y)) return !1 + } + return !0 + } + } + return !1 + }, GraphicsGeometry.prototype.updateBatches = function() { + if (this.dirty !== this.cacheDirty && 0 !== this.graphicsData.length) { + if (this.dirty !== this.cacheDirty) + for (var t = 0; t < this.graphicsData.length; t++) { + var e = this.graphicsData[t]; + if (e.fillStyle && !e.fillStyle.texture.baseTexture.valid) return; + if (e.lineStyle && !e.lineStyle.texture.baseTexture.valid) return + } + this.cacheDirty = this.dirty; + var r = this.uvs, + n = this.batches.pop() || Ar.pop() || new Cr; + n.style = n.style || this.graphicsData[0].fillStyle || this.graphicsData[0].lineStyle; + var i = n.style.texture.baseTexture, + o = n.style.color + n.style.alpha; + this.batches.push(n); + for (var a = this.shapeIndex; a < this.graphicsData.length; a++) { + this.shapeIndex++; + var s = this.graphicsData[a], + u = Or[s.type], + h = s.fillStyle, + c = s.lineStyle; + u.build(s), s.matrix && this.transformPoints(s.points, s.matrix); + for (var l = 0; l < 2; l++) { + var f = 0 === l ? h : c; + if (f.visible) { + var d = f.texture.baseTexture; + if (i !== d || f.color + f.alpha !== o) { + d.wrapMode = 10497, i = d, o = f.color + f.alpha; + var p = this.indices.length, + m = this.points.length / 2; + n.size = p - n.start, n.attribSize = m - n.attribStart, 0 < n.size && (n = Ar.pop() || new Cr, this.batches.push(n)), n.style = f, n.start = p, n.attribStart = m + } + var v = this.points.length / 2; + if (0 === l) s.holes.length ? (this.processHoles(s.holes), Mr.triangulate(s, this)) : u.triangulate(s, this); + else { + buildLine(s, this); + for (var g = 0; g < s.holes.length; g++) buildLine(s.holes[g], this) + } + var y = this.points.length / 2 - v; + this.addUvs(this.points, r, f.texture, v, y, f.matrix) + } + } + } + var _ = this.indices.length, + b = this.points.length / 2; + if (n.size = _ - n.start, n.attribSize = b - n.attribStart, this.indicesUint16 = new Uint16Array(this.indices), this.batchable = this.isBatchable(), this.batchable) { + this.batchDirty++, this.uvsFloat32 = new Float32Array(this.uvs); + for (var x = 0; x < this.batches.length; x++) + for (var w = this.batches[x], T = 0; T < w.size; T++) { + var S = w.start + T; + this.indicesUint16[S] = this.indicesUint16[S] - w.attribStart + } + } else this.buildDrawCalls() + } + }, GraphicsGeometry.prototype.isBatchable = function() { + for (var t = this.batches, e = 0; e < t.length; e++) + if (t[e].style.native) return !1; + return this.points.length < 2 * GraphicsGeometry.BATCHABLE_SIZE + }, GraphicsGeometry.prototype.buildDrawCalls = function() { + Ir++; + for (var t = 0; t < this.drawCalls.length; t++) this.drawCalls[t].textures.length = 0, Pr.push(this.drawCalls[t]); + this.drawCalls.length = 0; + var e = this.uvs, + r = this.colors, + n = this.textureIds, + i = Pr.pop() || new ir; + i.textureCount = 0, i.start = 0, i.size = 0, i.type = x.TRIANGLES; + var o = 0, + a = null, + s = 0, + u = !1, + h = x.TRIANGLES, + c = 0; + this.drawCalls.push(i); + for (var l = 0; l < this.batches.length; l++) { + var f = this.batches[l], + d = f.style, + p = d.texture.baseTexture; + u !== d.native && (h = (u = d.native) ? x.LINES : x.TRIANGLES, a = null, o = 8, Ir++), a !== p && (a = p)._enabled !== Ir && (8 === o && (Ir++, (o = 0) < i.size && (i = Pr.pop() || new ir, this.drawCalls.push(i)), i.start = c, i.size = 0, i.textureCount = 0, i.type = h), p.touched = 1, p._enabled = Ir, p._id = o, p.wrapMode = 10497, i.textures[i.textureCount++] = p, o++), i.size += f.size, c += f.size, s = p._id, this.addColors(r, d.color, d.alpha, f.attribSize), this.addTextureIds(n, s, f.attribSize) + } + for (var m = this.points, v = new ArrayBuffer(3 * m.length * 4), g = new Float32Array(v), y = new Uint32Array(v), _ = 0, b = 0; b < m.length / 2; b++) g[_++] = m[2 * b], g[_++] = m[2 * b + 1], g[_++] = e[2 * b], g[_++] = e[2 * b + 1], y[_++] = r[b], g[_++] = n[b]; + this._buffer.update(v), this._indexBuffer.update(this.indicesUint16) + }, GraphicsGeometry.prototype.processHoles = function(t) { + for (var e = 0; e < t.length; e++) { + var r = t[e]; + Or[r.type].build(r), r.matrix && this.transformPoints(r.points, r.matrix) + } + }, GraphicsGeometry.prototype.calculateBounds = function() { + var t = 1 / 0, + e = -1 / 0, + r = 1 / 0, + n = -1 / 0; + if (this.graphicsData.length) + for (var i = null, o = 0, a = 0, s = 0, u = 0, h = 0; h < this.graphicsData.length; h++) { + var c = this.graphicsData[h], + l = c.type, + f = c.lineStyle ? c.lineStyle.width : 0; + if (i = c.shape, l === W.RECT || l === W.RREC) o = i.x - f / 2, a = i.y - f / 2, t = o < t ? o : t, e = e < o + (s = i.width + f) ? o + s : e, r = a < r ? a : r, n = n < a + (u = i.height + f) ? a + u : n; + else if (l === W.CIRC) o = i.x, a = i.y, t = o - (s = i.radius + f / 2) < t ? o - s : t, e = e < o + s ? o + s : e, r = a - (u = i.radius + f / 2) < r ? a - u : r, n = n < a + u ? a + u : n; + else if (l === W.ELIP) o = i.x, a = i.y, t = o - (s = i.width + f / 2) < t ? o - s : t, e = e < o + s ? o + s : e, r = a - (u = i.height + f / 2) < r ? a - u : r, n = n < a + u ? a + u : n; + else + for (var d = i.points, p = 0, m = 0, v = 0, g = 0, y = 0, _ = 0, b = 0, x = 0, w = 0; w + 2 < d.length; w += 2) o = d[w], a = d[w + 1], p = d[w + 2], m = d[w + 3], v = Math.abs(p - o), g = Math.abs(m - a), u = f, (s = Math.sqrt(v * v + g * g)) < 1e-9 || (t = (b = (p + o) / 2) - (y = (u / s * g + v) / 2) < t ? b - y : t, e = e < b + y ? b + y : e, r = (x = (m + a) / 2) - (_ = (u / s * v + g) / 2) < r ? x - _ : r, n = n < x + _ ? x + _ : n) + } else n = r = e = t = 0; + var T = this.boundsPadding; + this._bounds.minX = t - T, this._bounds.maxX = e + T, this._bounds.minY = r - T, this._bounds.maxY = n + T + }, GraphicsGeometry.prototype.transformPoints = function(t, e) { + for (var r = 0; r < t.length / 2; r++) { + var n = t[2 * r], + i = t[2 * r + 1]; + t[2 * r] = e.a * n + e.c * i + e.tx, t[2 * r + 1] = e.b * n + e.d * i + e.ty + } + }, GraphicsGeometry.prototype.addColors = function(t, e, r, n) { + for (var i = premultiplyTint((e >> 16) + (65280 & e) + ((255 & e) << 16), r); 0 < n--;) t.push(i) + }, GraphicsGeometry.prototype.addTextureIds = function(t, e, r) { + for (; 0 < r--;) t.push(e) + }, GraphicsGeometry.prototype.addUvs = function(t, e, r, n, i, o) { + for (var a = 0, s = e.length, u = r.frame; a < i;) { + var h = t[2 * (n + a)], + c = t[2 * (n + a) + 1]; + if (o) { + var l = o.a * h + o.c * c + o.tx; + c = o.b * h + o.d * c + o.ty, h = l + } + a++, e.push(h / u.width, c / u.height) + } + var f = r.baseTexture; + (u.width < f.width || u.height < f.height) && this.adjustUvs(e, r, s, i) + }, GraphicsGeometry.prototype.adjustUvs = function(t, e, r, n) { + for (var i = e.baseTexture, o = r + 2 * n, a = e.frame, s = a.width / i.width, u = a.height / i.height, h = a.x / a.width, c = a.y / a.width, l = Math.floor(t[r] + 1e-6), f = Math.floor(t[r + 1] + 1e-6), d = r + 2; d < o; d += 2) l = Math.min(l, Math.floor(t[d] + 1e-6)), f = Math.min(f, Math.floor(t[d + 1] + 1e-6)); + h -= l, c -= f; + for (var p = r; p < o; p += 2) t[p] = (t[p] + h) * s, t[p + 1] = (t[p + 1] + c) * u + }, Object.defineProperties(GraphicsGeometry.prototype, t), GraphicsGeometry + }(nr); + Rr.BATCHABLE_SIZE = 100; + var Dr = function(t) { + function LineStyle() { + t.apply(this, arguments) + } + return t && (LineStyle.__proto__ = t), ((LineStyle.prototype = Object.create(t && t.prototype)).constructor = LineStyle).prototype.clone = function() { + var t = new LineStyle; + return t.color = this.color, t.alpha = this.alpha, t.texture = this.texture, t.matrix = this.matrix, t.visible = this.visible, t.width = this.width, t.alignment = this.alignment, t.native = this.native, t + }, LineStyle.prototype.reset = function() { + t.prototype.reset.call(this), this.color = 0, this.width = 0, this.alignment = .5, this.native = !1 + }, LineStyle + }(wr), + Fr = function() {}; + Fr.curveLength = function(t, e, r, n, i, o, a, s) { + for (var u = 0, h = 0, c = 0, l = 0, f = 0, d = 0, p = 0, m = 0, v = 0, g = 0, y = 0, _ = t, b = e, x = 1; x <= 10; ++x) g = _ - (m = (p = (d = (f = 1 - (h = x / 10)) * f) * f) * t + 3 * d * h * r + 3 * f * (c = h * h) * i + (l = c * h) * a), y = b - (v = p * e + 3 * d * h * n + 3 * f * c * o + l * s), _ = m, b = v, u += Math.sqrt(g * g + y * y); + return u + }, Fr.curveTo = function(t, e, r, n, i, o, a) { + var s = a[a.length - 2], + u = a[a.length - 1]; + a.length -= 2; + var h = xr._segmentsCount(Fr.curveLength(s, u, t, e, r, n, i, o)), + c = 0, + l = 0, + f = 0, + d = 0, + p = 0; + a.push(s, u); + for (var m = 1, v = 0; m <= h; ++m) f = (l = (c = 1 - (v = m / h)) * c) * c, p = (d = v * v) * v, a.push(f * s + 3 * l * v * t + 3 * c * d * r + p * i, f * u + 3 * l * v * e + 3 * c * d * n + p * o) + }; + var kr = function() {}; + kr.curveLength = function(t, e, r, n, i, o) { + var a = t - 2 * r + i, + s = e - 2 * n + o, + u = 2 * r - 2 * t, + h = 2 * n - 2 * e, + c = 4 * (a * a + s * s), + l = 4 * (a * u + s * h), + f = u * u + h * h, + d = 2 * Math.sqrt(c + l + f), + p = Math.sqrt(c), + m = 2 * c * p, + v = 2 * Math.sqrt(f), + g = l / p; + return (m * d + p * l * (d - v) + (4 * f * c - l * l) * Math.log((2 * p + g + d) / (g + v))) / (4 * m) + }, kr.curveTo = function(t, e, r, n, i) { + for (var o = i[i.length - 2], a = i[i.length - 1], s = xr._segmentsCount(kr.curveLength(o, a, t, e, r, n)), u = 0, h = 0, c = 1; c <= s; ++c) { + var l = c / s; + u = o + (t - o) * l, h = a + (e - a) * l, i.push(u + (t + (r - t) * l - u) * l, h + (e + (n - e) * l - h) * l) + } + }; + var Lr = function() {}; + Lr.curveTo = function(t, e, r, n, i, o) { + var a = o[o.length - 2], + s = o[o.length - 1] - e, + u = a - t, + h = n - e, + c = r - t, + l = Math.abs(s * c - u * h); + if (l < 1e-8 || 0 === i) return o[o.length - 2] === t && o[o.length - 1] === e || o.push(t, e), null; + var f = s * s + u * u, + d = h * h + c * c, + p = s * h + u * c, + m = i * Math.sqrt(f) / l, + v = i * Math.sqrt(d) / l, + g = m * p / f, + y = v * p / d, + _ = m * c + v * u, + b = m * h + v * s, + x = u * (v + g), + w = s * (v + g), + T = c * (m + y), + S = h * (m + y); + return { + cx: _ + t, + cy: b + e, + radius: i, + startAngle: Math.atan2(w - b, x - _), + endAngle: Math.atan2(S - b, T - _), + anticlockwise: c * s < u * h + } + }, Lr.arc = function(t, e, r, n, i, o, a, s, u) { + for (var h = a - o, c = xr._segmentsCount(Math.abs(h) * i, 40 * Math.ceil(Math.abs(h) / X)), l = h / (2 * c), f = 2 * l, d = Math.cos(l), p = Math.sin(l), m = c - 1, v = m % 1 / m, g = 0; g <= m; ++g) { + var y = l + o + f * (g + v * g), + _ = Math.cos(y), + b = -Math.sin(y); + u.push((d * _ + p * b) * i + r, (d * -b + p * _) * i + n) + } + }; + var Nr = function(d) { + function Star(t, e, r, n, i, o) { + i = i || n / 2; + for (var a = -1 * Math.PI / 2 + o, s = 2 * r, u = X / s, h = [], c = 0; c < s; c++) { + var l = c % 2 ? i : n, + f = c * u + a; + h.push(t + l * Math.cos(f), e + l * Math.sin(f)) + } + d.call(this, h) + } + return d && (Star.__proto__ = d), (Star.prototype = Object.create(d && d.prototype)).constructor = Star + }(ct), + Br = new Float32Array(3), + Ur = null, + Gr = function(e) { + function Graphics(t) { + void 0 === t && (t = null), e.call(this), this.geometry = t || new Rr, this.geometry.refCount++, this.shader = null, this.state = Ce.for2d(), this._fillStyle = new wr, this._lineStyle = new Dr, this._matrix = null, this._holeMode = !1, this.currentPath = null, this.batches = [], this.batchTint = -1, this.vertexData = null, this._transformID = -1, this.batchDirty = -1, this.tint = 16777215, this.blendMode = y.NORMAL + } + e && (Graphics.__proto__ = e); + var t = { + blendMode: { + configurable: !0 + }, + tint: { + configurable: !0 + }, + fill: { + configurable: !0 + }, + line: { + configurable: !0 + } + }; + return ((Graphics.prototype = Object.create(e && e.prototype)).constructor = Graphics).prototype.clone = function() { + return this.finishPoly(), new Graphics(this.geometry) + }, t.blendMode.set = function(t) { + this.state.blendMode = t + }, t.blendMode.get = function() { + return this.state.blendMode + }, t.tint.get = function() { + return this._tint + }, t.tint.set = function(t) { + this._tint = t + }, t.fill.get = function() { + return this._fillStyle + }, t.line.get = function() { + return this._lineStyle + }, Graphics.prototype.lineStyle = function(t, e, r, n, i) { + return void 0 === t && (t = 0), void 0 === e && (e = 0), void 0 === r && (r = 1), void 0 === n && (n = .5), void 0 === i && (i = !1), this.lineTextureStyle(t, Ht.WHITE, e, r, null, n, i), this + }, Graphics.prototype.lineTextureStyle = function(t, e, r, n, i, o, a) { + void 0 === t && (t = 0), void 0 === e && (e = Ht.WHITE), void 0 === r && (r = 16777215), void 0 === n && (n = 1), void 0 === i && (i = null), void 0 === o && (o = .5), void 0 === a && (a = !1), this.currentPath && this.startPoly(); + var s = 0 < t && 0 < n; + return s ? (i && (i = i.clone()).invert(), Object.assign(this._lineStyle, { + color: r, + width: t, + alpha: n, + matrix: i, + texture: e, + alignment: o, + native: a, + visible: s + })) : this._lineStyle.reset(), this + }, Graphics.prototype.startPoly = function() { + if (this.currentPath) { + var t = this.currentPath.points, + e = this.currentPath.points.length; + 2 < e && (this.drawShape(this.currentPath), this.currentPath = new ct, this.currentPath.closeStroke = !1, this.currentPath.points.push(t[e - 2], t[e - 1])) + } else this.currentPath = new ct, this.currentPath.closeStroke = !1 + }, Graphics.prototype.finishPoly = function() { + this.currentPath && (2 < this.currentPath.points.length ? (this.drawShape(this.currentPath), this.currentPath = null) : this.currentPath.points.length = 0) + }, Graphics.prototype.moveTo = function(t, e) { + return this.startPoly(), this.currentPath.points[0] = t, this.currentPath.points[1] = e, this + }, Graphics.prototype.lineTo = function(t, e) { + this.currentPath || this.moveTo(0, 0); + var r = this.currentPath.points, + n = r[r.length - 2], + i = r[r.length - 1]; + return n === t && i === e || r.push(t, e), this + }, Graphics.prototype._initCurve = function(t, e) { + void 0 === t && (t = 0), void 0 === e && (e = 0), this.currentPath ? 0 === this.currentPath.points.length && (this.currentPath.points = [t, e]) : this.moveTo(t, e) + }, Graphics.prototype.quadraticCurveTo = function(t, e, r, n) { + this._initCurve(); + var i = this.currentPath.points; + return 0 === i.length && this.moveTo(0, 0), kr.curveTo(t, e, r, n, i), this + }, Graphics.prototype.bezierCurveTo = function(t, e, r, n, i, o) { + return this._initCurve(), Fr.curveTo(t, e, r, n, i, o, this.currentPath.points), this + }, Graphics.prototype.arcTo = function(t, e, r, n, i) { + this._initCurve(t, e); + var o = this.currentPath.points, + a = Lr.curveTo(t, e, r, n, i, o); + if (a) { + var s = a.cx, + u = a.cy, + h = a.radius, + c = a.startAngle, + l = a.endAngle, + f = a.anticlockwise; + this.arc(s, u, h, c, l, f) + } + return this + }, Graphics.prototype.arc = function(t, e, r, n, i, o) { + if (void 0 === o && (o = !1), n === i) return this; + if (!o && i <= n ? i += X : o && n <= i && (n += X), 0 === i - n) return this; + var a = t + Math.cos(n) * r, + s = e + Math.sin(n) * r, + u = this.currentPath ? this.currentPath.points : null; + if (u) { + var h = Math.abs(u[u.length - 2] - a), + c = Math.abs(u[u.length - 1] - s); + h < .001 && c < .001 || u.push(a, s) + } else this.moveTo(a, s), u = this.currentPath.points; + return Lr.arc(a, s, t, e, r, n, i, o, u), this + }, Graphics.prototype.beginFill = function(t, e) { + return void 0 === t && (t = 0), void 0 === e && (e = 1), this.beginTextureFill(Ht.WHITE, t, e) + }, Graphics.prototype.beginTextureFill = function(t, e, r, n) { + void 0 === t && (t = Ht.WHITE), void 0 === e && (e = 16777215), void 0 === r && (r = 1), void 0 === n && (n = null), this.currentPath && this.startPoly(); + var i = 0 < r; + return i ? (n && (n = n.clone()).invert(), Object.assign(this._fillStyle, { + color: e, + alpha: r, + texture: t, + matrix: n, + visible: i + })) : this._fillStyle.reset(), this + }, Graphics.prototype.endFill = function() { + return this.finishPoly(), this._fillStyle.reset(), this + }, Graphics.prototype.drawRect = function(t, e, r, n) { + return this.drawShape(new ot(t, e, r, n)) + }, Graphics.prototype.drawRoundedRect = function(t, e, r, n, i) { + return this.drawShape(new lt(t, e, r, n, i)) + }, Graphics.prototype.drawCircle = function(t, e, r) { + return this.drawShape(new ut(t, e, r)) + }, Graphics.prototype.drawEllipse = function(t, e, r, n) { + return this.drawShape(new ht(t, e, r, n)) + }, Graphics.prototype.drawPolygon = function(t) { + var e = arguments, + r = t, + n = !0; + if (r.points && (n = r.closeStroke, r = r.points), !Array.isArray(r)) { + r = new Array(arguments.length); + for (var i = 0; i < r.length; ++i) r[i] = e[i] + } + var o = new ct(r); + return o.closeStroke = n, this.drawShape(o), this + }, Graphics.prototype.drawShape = function(t) { + return this._holeMode ? this.geometry.drawHole(t, this._matrix) : this.geometry.drawShape(t, this._fillStyle.clone(), this._lineStyle.clone(), this._matrix), this + }, Graphics.prototype.drawStar = function(t, e, r, n, i, o) { + return void 0 === o && (o = 0), this.drawPolygon(new Nr(t, e, r, n, i, o)) + }, Graphics.prototype.clear = function() { + return this.geometry.clear(), this._matrix = null, this._holeMode = !1, this.currentPath = null, this._spriteRect = null, this + }, Graphics.prototype.isFastRect = function() { + return !1 + }, Graphics.prototype._render = function(t) { + this.finishPoly(); + var e = this.geometry; + if (e.updateBatches(), e.batchable) { + if (this.batchDirty !== e.batchDirty) { + this.batches = [], this.batchTint = -1, this._transformID = -1, this.batchDirty = e.batchDirty, this.vertexData = new Float32Array(e.points); + for (var r = this.blendMode, n = 0; n < e.batches.length; n++) { + var i = e.batches[n], + o = i.style.color, + a = new Float32Array(this.vertexData.buffer, 4 * i.attribStart * 2, 2 * i.attribSize), + s = new Float32Array(e.uvsFloat32.buffer, 4 * i.attribStart * 2, 2 * i.attribSize), + u = { + vertexData: a, + blendMode: r, + indices: new Uint16Array(e.indicesUint16.buffer, 2 * i.start, i.size), + uvs: s, + _batchRGB: hex2rgb(o), + _tintRGB: o, + _texture: i.style.texture, + alpha: i.style.alpha, + worldAlpha: 1 + }; + this.batches[n] = u + } + } + if (t.batch.setObjectRenderer(t.plugins.batch), this.batches.length) { + this.calculateVertices(), this.calculateTints(); + for (var h = 0; h < this.batches.length; h++) { + var c = this.batches[h]; + c.worldAlpha = this.worldAlpha * c.alpha, t.plugins.batch.render(c) + } + } + } else { + if (t.batch.flush(), !this.shader) { + if (!Ur) { + for (var l = new Int32Array(16), f = 0; f < 16; f++) l[f] = f; + var d = { + tint: new Float32Array([1, 1, 1, 1]), + translationMatrix: new Y, + default: oe.from({ + uSamplers: l + }, !0) + }, + p = t.plugins.batch.shader.program; + Ur = new Ie(p, d) + } + this.shader = Ur + } + var m = this.shader.uniforms; + m.translationMatrix = this.transform.worldTransform; + var v = this.tint, + g = this.worldAlpha; + m.tint[0] = (v >> 16 & 255) / 255 * g, m.tint[1] = (v >> 8 & 255) / 255 * g, m.tint[2] = (255 & v) / 255 * g, m.tint[3] = g, t.shader.bind(this.shader), t.geometry.bind(e, this.shader), t.state.setState(this.state); + for (var y = 0; y < e.drawCalls.length; y++) { + for (var _ = e.drawCalls[y], b = _.textureCount, x = 0; x < b; x++) t.texture.bind(_.textures[x], x); + t.geometry.draw(_.type, _.size, _.start) + } + } + }, Graphics.prototype._calculateBounds = function() { + this.finishPoly(); + var t = this.geometry.bounds; + this._bounds.addFrame(this.transform, t.minX, t.minY, t.maxX, t.maxY) + }, Graphics.prototype.containsPoint = function(t) { + return this.worldTransform.applyInverse(t, Graphics._TEMP_POINT), this.geometry.containsPoint(Graphics._TEMP_POINT) + }, Graphics.prototype.calculateTints = function() { + if (this.batchTint !== this.tint) { + this.batchTint = this.tint; + for (var t = hex2rgb(this.tint, Br), e = 0; e < this.batches.length; e++) { + var r = this.batches[e], + n = r._batchRGB, + i = (t[0] * n[0] * 255 << 16) + (t[1] * n[1] * 255 << 8) + (0 | t[2] * n[2] * 255); + r._tintRGB = (i >> 16) + (65280 & i) + ((255 & i) << 16) + } + } + }, Graphics.prototype.calculateVertices = function() { + if (this._transformID !== this.transform._worldID) { + this._transformID = this.transform._worldID; + for (var t = this.transform.worldTransform, e = t.a, r = t.b, n = t.c, i = t.d, o = t.tx, a = t.ty, s = this.geometry.points, u = this.vertexData, h = 0, c = 0; c < s.length; c += 2) { + var l = s[c], + f = s[c + 1]; + u[h++] = e * l + n * f + o, u[h++] = i * f + r * l + a + } + } + }, Graphics.prototype.closePath = function() { + var t = this.currentPath; + return t && (t.closeStroke = !0), this + }, Graphics.prototype.setMatrix = function(t) { + return this._matrix = t, this + }, Graphics.prototype.beginHole = function() { + return this.finishPoly(), this._holeMode = !0, this + }, Graphics.prototype.endHole = function() { + return this.finishPoly(), this._holeMode = !1, this + }, Graphics.prototype.destroy = function(t) { + e.prototype.destroy.call(this, t), this.geometry.refCount--, 0 === this.geometry.refCount && this.geometry.dispose(), this._matrix = null, this.currentPath = null, this._lineStyle.destroy(), this._lineStyle = null, this._fillStyle.destroy(), this._fillStyle = null, this.geometry = null, this.shader = null, this.vertexData = null, this.batches.length = 0, this.batches = null, e.prototype.destroy.call(this, t) + }, Object.defineProperties(Graphics.prototype, t), Graphics + }(pt); + Gr._TEMP_POINT = new q; + var jr = new q, + zr = new Uint16Array([0, 1, 2, 0, 2, 3]), + Xr = function(r) { + function Sprite(t) { + r.call(this), this._anchor = new j(this._onAnchorUpdate, this, t ? t.defaultAnchor.x : 0, t ? t.defaultAnchor.y : 0), this._texture = null, this._width = 0, this._height = 0, this._tint = null, this._tintRGB = null, this.tint = 16777215, this.blendMode = y.NORMAL, this.shader = null, this.cachedTint = 16777215, this.uvs = null, this.texture = t || Ht.EMPTY, this.vertexData = new Float32Array(8), this.vertexTrimmedData = null, this._transformID = -1, this._textureID = -1, this._transformTrimmedID = -1, this._textureTrimmedID = -1, this.indices = zr, this.size = 4, this.start = 0, this.pluginName = "batch", this.isSprite = !0, this._roundPixels = M.ROUND_PIXELS + } + r && (Sprite.__proto__ = r); + var t = { + roundPixels: { + configurable: !0 + }, + width: { + configurable: !0 + }, + height: { + configurable: !0 + }, + anchor: { + configurable: !0 + }, + tint: { + configurable: !0 + }, + texture: { + configurable: !0 + } + }; + return ((Sprite.prototype = Object.create(r && r.prototype)).constructor = Sprite).prototype._onTextureUpdate = function() { + this._textureID = -1, this._textureTrimmedID = -1, this.cachedTint = 16777215, this.uvs = this._texture._uvs.uvsFloat32, this._width && (this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width), this._height && (this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height) + }, Sprite.prototype._onAnchorUpdate = function() { + this._transformID = -1, this._transformTrimmedID = -1 + }, Sprite.prototype.calculateVertices = function() { + var t = this._texture; + if (this._transformID !== this.transform._worldID || this._textureID !== t._updateID) { + this._transformID = this.transform._worldID, this._textureID = t._updateID; + var e = this.transform.worldTransform, + r = e.a, + n = e.b, + i = e.c, + o = e.d, + a = e.tx, + s = e.ty, + u = this.vertexData, + h = t.trim, + c = t.orig, + l = this._anchor, + f = 0, + d = 0, + p = 0, + m = 0; + if (p = h ? (f = (d = h.x - l._x * c.width) + h.width, (m = h.y - l._y * c.height) + h.height) : (f = (d = -l._x * c.width) + c.width, (m = -l._y * c.height) + c.height), u[0] = r * d + i * m + a, u[1] = o * m + n * d + s, u[2] = r * f + i * m + a, u[3] = o * m + n * f + s, u[4] = r * f + i * p + a, u[5] = o * p + n * f + s, u[6] = r * d + i * p + a, u[7] = o * p + n * d + s, this._roundPixels) + for (var v = 0; v < 8; v++) u[v] = Math.round(u[v]) + } + }, Sprite.prototype.calculateTrimmedVertices = function() { + if (this.vertexTrimmedData) { + if (this._transformTrimmedID === this.transform._worldID && this._textureTrimmedID === this._texture._updateID) return + } else this.vertexTrimmedData = new Float32Array(8); + this._transformTrimmedID = this.transform._worldID, this._textureTrimmedID = this._texture._updateID; + var t = this._texture, + e = this.vertexTrimmedData, + r = t.orig, + n = this._anchor, + i = this.transform.worldTransform, + o = i.a, + a = i.b, + s = i.c, + u = i.d, + h = i.tx, + c = i.ty, + l = -n._x * r.width, + f = l + r.width, + d = -n._y * r.height, + p = d + r.height; + e[0] = o * l + s * d + h, e[1] = u * d + a * l + c, e[2] = o * f + s * d + h, e[3] = u * d + a * f + c, e[4] = o * f + s * p + h, e[5] = u * p + a * f + c, e[6] = o * l + s * p + h, e[7] = u * p + a * l + c + }, Sprite.prototype._render = function(t) { + this.calculateVertices(), t.batch.setObjectRenderer(t.plugins[this.pluginName]), t.plugins[this.pluginName].render(this) + }, Sprite.prototype._calculateBounds = function() { + var t = this._texture.trim, + e = this._texture.orig; + !t || t.width === e.width && t.height === e.height ? (this.calculateVertices(), this._bounds.addQuad(this.vertexData)) : (this.calculateTrimmedVertices(), this._bounds.addQuad(this.vertexTrimmedData)) + }, Sprite.prototype.getLocalBounds = function(t) { + return 0 === this.children.length ? (this._bounds.minX = this._texture.orig.width * -this._anchor._x, this._bounds.minY = this._texture.orig.height * -this._anchor._y, this._bounds.maxX = this._texture.orig.width * (1 - this._anchor._x), this._bounds.maxY = this._texture.orig.height * (1 - this._anchor._y), t || (this._localBoundsRect || (this._localBoundsRect = new ot), t = this._localBoundsRect), this._bounds.getRectangle(t)) : r.prototype.getLocalBounds.call(this, t) + }, Sprite.prototype.containsPoint = function(t) { + this.worldTransform.applyInverse(t, jr); + var e = this._texture.orig.width, + r = this._texture.orig.height, + n = -e * this.anchor.x, + i = 0; + return jr.x >= n && jr.x < n + e && (i = -r * this.anchor.y, jr.y >= i && jr.y < i + r) + }, Sprite.prototype.destroy = function(t) { + if (r.prototype.destroy.call(this, t), this._texture.off("update", this._onTextureUpdate, this), this._anchor = null, "boolean" == typeof t ? t : t && t.texture) { + var e = "boolean" == typeof t ? t : t && t.baseTexture; + this._texture.destroy(!!e) + } + this._texture = null, this.shader = null + }, Sprite.from = function(t, e) { + return new Sprite(t instanceof Ht ? t : new Ht.from(t, e)) + }, t.roundPixels.set = function(t) { + this._roundPixels !== t && (this._transformID = -1), this._roundPixels = t + }, t.roundPixels.get = function() { + return this._roundPixels + }, t.width.get = function() { + return Math.abs(this.scale.x) * this._texture.orig.width + }, t.width.set = function(t) { + var e = sign(this.scale.x) || 1; + this.scale.x = e * t / this._texture.orig.width, this._width = t + }, t.height.get = function() { + return Math.abs(this.scale.y) * this._texture.orig.height + }, t.height.set = function(t) { + var e = sign(this.scale.y) || 1; + this.scale.y = e * t / this._texture.orig.height, this._height = t + }, t.anchor.get = function() { + return this._anchor + }, t.anchor.set = function(t) { + this._anchor.copyFrom(t) + }, t.tint.get = function() { + return this._tint + }, t.tint.set = function(t) { + this._tint = t, this._tintRGB = (t >> 16) + (65280 & t) + ((255 & t) << 16) + }, t.texture.get = function() { + return this._texture + }, t.texture.set = function(t) { + this._texture !== t && (this._texture = t || Ht.EMPTY, this.cachedTint = 16777215, this._textureID = -1, this._textureTrimmedID = -1, t && (t.baseTexture.valid ? this._onTextureUpdate() : t.once("update", this._onTextureUpdate, this))) + }, Object.defineProperties(Sprite.prototype, t), Sprite + }(pt), + Vr = { + LINEAR_VERTICAL: 0, + LINEAR_HORIZONTAL: 1 + }, + qr = { + align: "left", + breakWords: !1, + dropShadow: !1, + dropShadowAlpha: 1, + dropShadowAngle: Math.PI / 6, + dropShadowBlur: 0, + dropShadowColor: "black", + dropShadowDistance: 5, + fill: "black", + fillGradientType: Vr.LINEAR_VERTICAL, + fillGradientStops: [], + fontFamily: "Arial", + fontSize: 26, + fontStyle: "normal", + fontVariant: "normal", + fontWeight: "normal", + letterSpacing: 0, + lineHeight: 0, + lineJoin: "miter", + miterLimit: 10, + padding: 0, + stroke: "black", + strokeThickness: 0, + textBaseline: "alphabetic", + trim: !1, + whiteSpace: "pre", + wordWrap: !1, + wordWrapWidth: 100, + leading: 0 + }, + Hr = function(t) { + this.styleID = 0, this.reset(), deepCopyProperties(this, t, t) + }, + Wr = { + align: { + configurable: !0 + }, + breakWords: { + configurable: !0 + }, + dropShadow: { + configurable: !0 + }, + dropShadowAlpha: { + configurable: !0 + }, + dropShadowAngle: { + configurable: !0 + }, + dropShadowBlur: { + configurable: !0 + }, + dropShadowColor: { + configurable: !0 + }, + dropShadowDistance: { + configurable: !0 + }, + fill: { + configurable: !0 + }, + fillGradientType: { + configurable: !0 + }, + fillGradientStops: { + configurable: !0 + }, + fontFamily: { + configurable: !0 + }, + fontSize: { + configurable: !0 + }, + fontStyle: { + configurable: !0 + }, + fontVariant: { + configurable: !0 + }, + fontWeight: { + configurable: !0 + }, + letterSpacing: { + configurable: !0 + }, + lineHeight: { + configurable: !0 + }, + leading: { + configurable: !0 + }, + lineJoin: { + configurable: !0 + }, + miterLimit: { + configurable: !0 + }, + padding: { + configurable: !0 + }, + stroke: { + configurable: !0 + }, + strokeThickness: { + configurable: !0 + }, + textBaseline: { + configurable: !0 + }, + trim: { + configurable: !0 + }, + whiteSpace: { + configurable: !0 + }, + wordWrap: { + configurable: !0 + }, + wordWrapWidth: { + configurable: !0 + } + }; + + function getSingleColor(t) { + return "number" == typeof t ? hex2string(t) : ("string" == typeof t && 0 === t.indexOf("0x") && (t = t.replace("0x", "#")), t) + } + + function getColor(t) { + if (Array.isArray(t)) { + for (var e = 0; e < t.length; ++e) t[e] = getSingleColor(t[e]); + return t + } + return getSingleColor(t) + } + + function deepCopyProperties(t, e, r) { + for (var n in r) Array.isArray(e[n]) ? t[n] = e[n].slice() : t[n] = e[n] + } + Hr.prototype.clone = function() { + var t = {}; + return deepCopyProperties(t, this, qr), new Hr(t) + }, Hr.prototype.reset = function() { + deepCopyProperties(this, qr, qr) + }, Wr.align.get = function() { + return this._align + }, Wr.align.set = function(t) { + this._align !== t && (this._align = t, this.styleID++) + }, Wr.breakWords.get = function() { + return this._breakWords + }, Wr.breakWords.set = function(t) { + this._breakWords !== t && (this._breakWords = t, this.styleID++) + }, Wr.dropShadow.get = function() { + return this._dropShadow + }, Wr.dropShadow.set = function(t) { + this._dropShadow !== t && (this._dropShadow = t, this.styleID++) + }, Wr.dropShadowAlpha.get = function() { + return this._dropShadowAlpha + }, Wr.dropShadowAlpha.set = function(t) { + this._dropShadowAlpha !== t && (this._dropShadowAlpha = t, this.styleID++) + }, Wr.dropShadowAngle.get = function() { + return this._dropShadowAngle + }, Wr.dropShadowAngle.set = function(t) { + this._dropShadowAngle !== t && (this._dropShadowAngle = t, this.styleID++) + }, Wr.dropShadowBlur.get = function() { + return this._dropShadowBlur + }, Wr.dropShadowBlur.set = function(t) { + this._dropShadowBlur !== t && (this._dropShadowBlur = t, this.styleID++) + }, Wr.dropShadowColor.get = function() { + return this._dropShadowColor + }, Wr.dropShadowColor.set = function(t) { + var e = getColor(t); + this._dropShadowColor !== e && (this._dropShadowColor = e, this.styleID++) + }, Wr.dropShadowDistance.get = function() { + return this._dropShadowDistance + }, Wr.dropShadowDistance.set = function(t) { + this._dropShadowDistance !== t && (this._dropShadowDistance = t, this.styleID++) + }, Wr.fill.get = function() { + return this._fill + }, Wr.fill.set = function(t) { + var e = getColor(t); + this._fill !== e && (this._fill = e, this.styleID++) + }, Wr.fillGradientType.get = function() { + return this._fillGradientType + }, Wr.fillGradientType.set = function(t) { + this._fillGradientType !== t && (this._fillGradientType = t, this.styleID++) + }, Wr.fillGradientStops.get = function() { + return this._fillGradientStops + }, Wr.fillGradientStops.set = function(t) { + (function(t, e) { + if (!Array.isArray(t) || !Array.isArray(e)) return !1; + if (t.length !== e.length) return !1; + for (var r = 0; r < t.length; ++r) + if (t[r] !== e[r]) return !1; + return !0 + })(this._fillGradientStops, t) || (this._fillGradientStops = t, this.styleID++) + }, Wr.fontFamily.get = function() { + return this._fontFamily + }, Wr.fontFamily.set = function(t) { + this.fontFamily !== t && (this._fontFamily = t, this.styleID++) + }, Wr.fontSize.get = function() { + return this._fontSize + }, Wr.fontSize.set = function(t) { + this._fontSize !== t && (this._fontSize = t, this.styleID++) + }, Wr.fontStyle.get = function() { + return this._fontStyle + }, Wr.fontStyle.set = function(t) { + this._fontStyle !== t && (this._fontStyle = t, this.styleID++) + }, Wr.fontVariant.get = function() { + return this._fontVariant + }, Wr.fontVariant.set = function(t) { + this._fontVariant !== t && (this._fontVariant = t, this.styleID++) + }, Wr.fontWeight.get = function() { + return this._fontWeight + }, Wr.fontWeight.set = function(t) { + this._fontWeight !== t && (this._fontWeight = t, this.styleID++) + }, Wr.letterSpacing.get = function() { + return this._letterSpacing + }, Wr.letterSpacing.set = function(t) { + this._letterSpacing !== t && (this._letterSpacing = t, this.styleID++) + }, Wr.lineHeight.get = function() { + return this._lineHeight + }, Wr.lineHeight.set = function(t) { + this._lineHeight !== t && (this._lineHeight = t, this.styleID++) + }, Wr.leading.get = function() { + return this._leading + }, Wr.leading.set = function(t) { + this._leading !== t && (this._leading = t, this.styleID++) + }, Wr.lineJoin.get = function() { + return this._lineJoin + }, Wr.lineJoin.set = function(t) { + this._lineJoin !== t && (this._lineJoin = t, this.styleID++) + }, Wr.miterLimit.get = function() { + return this._miterLimit + }, Wr.miterLimit.set = function(t) { + this._miterLimit !== t && (this._miterLimit = t, this.styleID++) + }, Wr.padding.get = function() { + return this._padding + }, Wr.padding.set = function(t) { + this._padding !== t && (this._padding = t, this.styleID++) + }, Wr.stroke.get = function() { + return this._stroke + }, Wr.stroke.set = function(t) { + var e = getColor(t); + this._stroke !== e && (this._stroke = e, this.styleID++) + }, Wr.strokeThickness.get = function() { + return this._strokeThickness + }, Wr.strokeThickness.set = function(t) { + this._strokeThickness !== t && (this._strokeThickness = t, this.styleID++) + }, Wr.textBaseline.get = function() { + return this._textBaseline + }, Wr.textBaseline.set = function(t) { + this._textBaseline !== t && (this._textBaseline = t, this.styleID++) + }, Wr.trim.get = function() { + return this._trim + }, Wr.trim.set = function(t) { + this._trim !== t && (this._trim = t, this.styleID++) + }, Wr.whiteSpace.get = function() { + return this._whiteSpace + }, Wr.whiteSpace.set = function(t) { + this._whiteSpace !== t && (this._whiteSpace = t, this.styleID++) + }, Wr.wordWrap.get = function() { + return this._wordWrap + }, Wr.wordWrap.set = function(t) { + this._wordWrap !== t && (this._wordWrap = t, this.styleID++) + }, Wr.wordWrapWidth.get = function() { + return this._wordWrapWidth + }, Wr.wordWrapWidth.set = function(t) { + this._wordWrapWidth !== t && (this._wordWrapWidth = t, this.styleID++) + }, Hr.prototype.toFontString = function() { + var t = "number" == typeof this.fontSize ? this.fontSize + "px" : this.fontSize, + e = this.fontFamily; + Array.isArray(this.fontFamily) || (e = this.fontFamily.split(",")); + for (var r = e.length - 1; 0 <= r; r--) { + var n = e[r].trim(); + /([\"\'])[^\'\"]+\1/.test(n) || (n = '"' + n + '"'), e[r] = n + } + return this.fontStyle + " " + this.fontVariant + " " + this.fontWeight + " " + t + " " + e.join(",") + }, Object.defineProperties(Hr.prototype, Wr); + var Yr = function(t, e, r, n, i, o, a, s, u) { + this.text = t, this.style = e, this.width = r, this.height = n, this.lines = i, this.lineWidths = o, this.lineHeight = a, this.maxLineWidth = s, this.fontProperties = u + }; + Yr.measureText = function(t, e, r, n) { + void 0 === n && (n = Yr._canvas), r = null == r ? e.wordWrap : r; + var i = e.toFontString(), + o = Yr.measureFont(i); + 0 === o.fontSize && (o.fontSize = e.fontSize, o.ascent = e.fontSize); + var a = n.getContext("2d"); + a.font = i; + for (var s = (r ? Yr.wordWrap(t, e, n) : t).split(/(?:\r\n|\r|\n)/), u = new Array(s.length), h = 0, c = 0; c < s.length; c++) { + var l = a.measureText(s[c]).width + (s[c].length - 1) * e.letterSpacing; + u[c] = l, h = Math.max(h, l) + } + var f = h + e.strokeThickness; + e.dropShadow && (f += e.dropShadowDistance); + var d = e.lineHeight || o.fontSize + e.strokeThickness, + p = Math.max(d, o.fontSize + e.strokeThickness) + (s.length - 1) * (d + e.leading); + return e.dropShadow && (p += e.dropShadowDistance), new Yr(t, e, f, p, s, u, d + e.leading, h, o) + }, Yr.wordWrap = function(t, e, r) { + void 0 === r && (r = Yr._canvas); + for (var n = r.getContext("2d"), i = 0, o = "", a = "", s = {}, u = e.letterSpacing, h = e.whiteSpace, c = Yr.collapseSpaces(h), l = Yr.collapseNewlines(h), f = !c, d = e.wordWrapWidth + u, p = Yr.tokenize(t), m = 0; m < p.length; m++) { + var v = p[m]; + if (Yr.isNewline(v)) { + if (!l) { + a += Yr.addLine(o), f = !c, o = "", i = 0; + continue + } + v = " " + } + if (c) { + var g = Yr.isBreakingSpace(v), + y = Yr.isBreakingSpace(o[o.length - 1]); + if (g && y) continue + } + var _ = Yr.getFromCache(v, u, s, n); + if (d < _) + if ("" !== o && (a += Yr.addLine(o), o = "", i = 0), Yr.canBreakWords(v, e.breakWords)) + for (var b = v.split(""), x = 0; x < b.length; x++) { + for (var w = b[x], T = 1; b[x + T];) { + var S = b[x + T], + M = w[w.length - 1]; + if (Yr.canBreakChars(M, S, v, x, e.breakWords)) break; + w += S, T++ + } + x += w.length - 1; + var E = Yr.getFromCache(w, u, s, n); + d < E + i && (a += Yr.addLine(o), f = !1, o = "", i = 0), o += w, i += E + } else { + 0 < o.length && (a += Yr.addLine(o), o = "", i = 0); + var A = m === p.length - 1; + a += Yr.addLine(v, !A), f = !1, o = "", i = 0 + } else d < _ + i && (f = !1, a += Yr.addLine(o), o = "", i = 0), (0 < o.length || !Yr.isBreakingSpace(v) || f) && (o += v, i += _) + } + return a += Yr.addLine(o, !1) + }, Yr.addLine = function(t, e) { + return void 0 === e && (e = !0), t = Yr.trimRight(t), t = e ? t + "\n" : t + }, Yr.getFromCache = function(t, e, r, n) { + var i = r[t]; + if (void 0 === i) { + var o = t.length * e; + i = n.measureText(t).width + o, r[t] = i + } + return i + }, Yr.collapseSpaces = function(t) { + return "normal" === t || "pre-line" === t + }, Yr.collapseNewlines = function(t) { + return "normal" === t + }, Yr.trimRight = function(t) { + if ("string" != typeof t) return ""; + for (var e = t.length - 1; 0 <= e; e--) { + var r = t[e]; + if (!Yr.isBreakingSpace(r)) break; + t = t.slice(0, -1) + } + return t + }, Yr.isNewline = function(t) { + return "string" == typeof t && 0 <= Yr._newlines.indexOf(t.charCodeAt(0)) + }, Yr.isBreakingSpace = function(t) { + return "string" == typeof t && 0 <= Yr._breakingSpaces.indexOf(t.charCodeAt(0)) + }, Yr.tokenize = function(t) { + var e = [], + r = ""; + if ("string" != typeof t) return e; + for (var n = 0; n < t.length; n++) { + var i = t[n]; + Yr.isBreakingSpace(i) || Yr.isNewline(i) ? ("" !== r && (e.push(r), r = ""), e.push(i)) : r += i + } + return "" !== r && e.push(r), e + }, Yr.canBreakWords = function(t, e) { + return e + }, Yr.canBreakChars = function(t, e, r, n, i) { + return !0 + }, Yr.measureFont = function(t) { + if (Yr._fonts[t]) return Yr._fonts[t]; + var e = {}, + r = Yr._canvas, + n = Yr._context; + n.font = t; + var i = Yr.METRICS_STRING + Yr.BASELINE_SYMBOL, + o = Math.ceil(n.measureText(i).width), + a = Math.ceil(n.measureText(Yr.BASELINE_SYMBOL).width), + s = 2 * a; + a = a * Yr.BASELINE_MULTIPLIER | 0, r.width = o, r.height = s, n.fillStyle = "#f00", n.fillRect(0, 0, o, s), n.font = t, n.textBaseline = "alphabetic", n.fillStyle = "#000", n.fillText(i, 0, a); + var u = n.getImageData(0, 0, o, s).data, + h = u.length, + c = 4 * o, + l = 0, + f = 0, + d = !1; + for (l = 0; l < a; ++l) { + for (var p = 0; p < c; p += 4) + if (255 !== u[f + p]) { + d = !0; + break + } + if (d) break; + f += c + } + for (e.ascent = a - l, f = h - c, d = !1, l = s; a < l; --l) { + for (var m = 0; m < c; m += 4) + if (255 !== u[f + m]) { + d = !0; + break + } + if (d) break; + f -= c + } + return e.descent = l - a, e.fontSize = e.ascent + e.descent, Yr._fonts[t] = e + }, Yr.clearMetrics = function(t) { + void 0 === t && (t = ""), t ? delete Yr._fonts[t] : Yr._fonts = {} + }; + var Kr = document.createElement("canvas"); + Kr.width = Kr.height = 10, Yr._canvas = Kr, Yr._context = Kr.getContext("2d"), Yr._fonts = {}, Yr.METRICS_STRING = "|Éq", Yr.BASELINE_SYMBOL = "M", Yr.BASELINE_MULTIPLIER = 1.4, Yr._newlines = [10, 13]; + var Zr = { + texture: !0, + children: !(Yr._breakingSpaces = [9, 32, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8200, 8201, 8202, 8287, 12288]), + baseTexture: !0 + }, + Qr = function(i) { + function Text(t, e, r) { + (r = r || document.createElement("canvas")).width = 3, r.height = 3; + var n = Ht.from(r); + n.orig = new ot, n.trim = new ot, i.call(this, n), this.canvas = r, this.context = this.canvas.getContext("2d"), this._resolution = M.RESOLUTION, this._autoResolution = !0, this._text = null, this._style = null, this._styleListener = null, this._font = "", this.text = t, this.style = e, this.localStyleID = -1 + } + i && (Text.__proto__ = i); + var t = { + width: { + configurable: !0 + }, + height: { + configurable: !0 + }, + style: { + configurable: !0 + }, + text: { + configurable: !0 + }, + resolution: { + configurable: !0 + } + }; + return ((Text.prototype = Object.create(i && i.prototype)).constructor = Text).prototype.updateText = function(t) { + var e = this._style; + if (this.localStyleID !== e.styleID && (this.dirty = !0, this.localStyleID = e.styleID), this.dirty || !t) { + this._font = this._style.toFontString(); + var r, n, i = this.context, + o = Yr.measureText(this._text || " ", this._style, this._style.wordWrap, this.canvas), + a = o.width, + s = o.height, + u = o.lines, + h = o.lineHeight, + c = o.lineWidths, + l = o.maxLineWidth, + f = o.fontProperties; + if (this.canvas.width = Math.ceil((Math.max(1, a) + 2 * e.padding) * this._resolution), this.canvas.height = Math.ceil((Math.max(1, s) + 2 * e.padding) * this._resolution), i.scale(this._resolution, this._resolution), i.clearRect(0, 0, this.canvas.width, this.canvas.height), i.font = this._font, i.strokeStyle = e.stroke, i.lineWidth = e.strokeThickness, i.textBaseline = e.textBaseline, i.lineJoin = e.lineJoin, i.miterLimit = e.miterLimit, e.dropShadow) { + var d = e.dropShadowColor, + p = hex2rgb("number" == typeof d ? d : string2hex(d)); + i.shadowColor = "rgba(" + 255 * p[0] + "," + 255 * p[1] + "," + 255 * p[2] + "," + e.dropShadowAlpha + ")", i.shadowBlur = e.dropShadowBlur, i.shadowOffsetX = Math.cos(e.dropShadowAngle) * e.dropShadowDistance, i.shadowOffsetY = Math.sin(e.dropShadowAngle) * e.dropShadowDistance + } else i.shadowColor = 0, i.shadowBlur = 0, i.shadowOffsetX = 0, i.shadowOffsetY = 0; + i.fillStyle = this._generateFillStyle(e, u); + for (var m = 0; m < u.length; m++) r = e.strokeThickness / 2, n = e.strokeThickness / 2 + m * h + f.ascent, "right" === e.align ? r += l - c[m] : "center" === e.align && (r += (l - c[m]) / 2), e.stroke && e.strokeThickness && this.drawLetterSpacing(u[m], r + e.padding, n + e.padding, !0), e.fill && this.drawLetterSpacing(u[m], r + e.padding, n + e.padding); + this.updateTexture() + } + }, Text.prototype.drawLetterSpacing = function(t, e, r, n) { + void 0 === n && (n = !1); + var i = this._style.letterSpacing; + if (0 !== i) + for (var o = String.prototype.split.call(t, ""), a = e, s = 0, u = ""; s < t.length;) u = o[s++], n ? this.context.strokeText(u, a, r) : this.context.fillText(u, a, r), a += this.context.measureText(u).width + i; + else n ? this.context.strokeText(t, e, r) : this.context.fillText(t, e, r) + }, Text.prototype.updateTexture = function() { + var t = this.canvas; + if (this._style.trim) { + var e = trimCanvas(t); + e.data && (t.width = e.width, t.height = e.height, this.context.putImageData(e.data, 0, 0)) + } + var r = this._texture, + n = this._style, + i = n.trim ? 0 : n.padding, + o = r.baseTexture; + r.trim.width = r._frame.width = t.width / this._resolution, r.trim.height = r._frame.height = t.height / this._resolution, r.trim.x = -i, r.trim.y = -i, r.orig.width = r._frame.width - 2 * i, r.orig.height = r._frame.height - 2 * i, this._onTextureUpdate(), o.setRealSize(t.width, t.height, this._resolution), this.dirty = !1 + }, Text.prototype.render = function(t) { + this._autoResolution && this._resolution !== t.resolution && (this._resolution = t.resolution, this.dirty = !0), this.updateText(!0), i.prototype.render.call(this, t) + }, Text.prototype._renderCanvas = function(t) { + this._autoResolution && this._resolution !== t.resolution && (this._resolution = t.resolution, this.dirty = !0), this.updateText(!0), i.prototype._renderCanvas.call(this, t) + }, Text.prototype.getLocalBounds = function(t) { + return this.updateText(!0), i.prototype.getLocalBounds.call(this, t) + }, Text.prototype._calculateBounds = function() { + this.updateText(!0), this.calculateVertices(), this._bounds.addQuad(this.vertexData) + }, Text.prototype._onStyleChange = function() { + this.dirty = !0 + }, Text.prototype._generateFillStyle = function(t, e) { + if (!Array.isArray(t.fill)) return t.fill; + var r, n, i, o, a = this.canvas.width / this._resolution, + s = this.canvas.height / this._resolution, + u = t.fill.slice(), + h = t.fillGradientStops.slice(); + if (!h.length) + for (var c = u.length + 1, l = 1; l < c; ++l) h.push(l / c); + if (u.unshift(t.fill[0]), h.unshift(0), u.push(t.fill[t.fill.length - 1]), h.push(1), t.fillGradientType === Vr.LINEAR_VERTICAL) { + r = this.context.createLinearGradient(a / 2, 0, a / 2, s), n = (u.length + 1) * e.length; + for (var f = i = 0; f < e.length; f++) { + i += 1; + for (var d = 0; d < u.length; d++) o = "number" == typeof h[d] ? h[d] / e.length + f / e.length : i / n, r.addColorStop(o, u[d]), i++ + } + } else { + r = this.context.createLinearGradient(0, s / 2, a, s / 2), n = u.length + 1, i = 1; + for (var p = 0; p < u.length; p++) o = "number" == typeof h[p] ? h[p] : i / n, r.addColorStop(o, u[p]), i++ + } + return r + }, Text.prototype.destroy = function(t) { + "boolean" == typeof t && (t = { + children: t + }), t = Object.assign({}, Zr, t), i.prototype.destroy.call(this, t), this.context = null, this.canvas = null, this._style = null + }, t.width.get = function() { + return this.updateText(!0), Math.abs(this.scale.x) * this._texture.orig.width + }, t.width.set = function(t) { + this.updateText(!0); + var e = sign(this.scale.x) || 1; + this.scale.x = e * t / this._texture.orig.width, this._width = t + }, t.height.get = function() { + return this.updateText(!0), Math.abs(this.scale.y) * this._texture.orig.height + }, t.height.set = function(t) { + this.updateText(!0); + var e = sign(this.scale.y) || 1; + this.scale.y = e * t / this._texture.orig.height, this._height = t + }, t.style.get = function() { + return this._style + }, t.style.set = function(t) { + t = t || {}, this._style = t instanceof Hr ? t : new Hr(t), this.localStyleID = -1, this.dirty = !0 + }, t.text.get = function() { + return this._text + }, t.text.set = function(t) { + t = String(null == t ? "" : t), this._text !== t && (this._text = t, this.dirty = !0) + }, t.resolution.get = function() { + return this._resolution + }, t.resolution.set = function(t) { + this._autoResolution = !1, this._resolution !== t && (this._resolution = t, this.dirty = !0) + }, Object.defineProperties(Text.prototype, t), Text + }(Xr); + M.UPLOADS_PER_FRAME = 4; + var Jr = function(t) { + this.maxItemsPerFrame = t, this.itemsLeft = 0 + }; + Jr.prototype.beginFrame = function() { + this.itemsLeft = this.maxItemsPerFrame + }, Jr.prototype.allowedToUpload = function() { + return 0 < this.itemsLeft-- + }; + var $r = function(t) { + var e = this; + this.limiter = new Jr(M.UPLOADS_PER_FRAME), this.renderer = t, this.uploadHookHelper = null, this.queue = [], this.addHooks = [], this.uploadHooks = [], this.completes = [], this.ticking = !1, this.delayedTick = function() { + e.queue && e.prepareItems() + }, this.registerFindHook(findText), this.registerFindHook(findTextStyle), this.registerFindHook(findMultipleBaseTextures), this.registerFindHook(findBaseTexture), this.registerFindHook(findTexture), this.registerUploadHook(drawText), this.registerUploadHook(calculateTextStyle) + }; + + function findMultipleBaseTextures(t, e) { + var r = !1; + if (t && t._textures && t._textures.length) + for (var n = 0; n < t._textures.length; n++) + if (t._textures[n] instanceof Ht) { + var i = t._textures[n].baseTexture; - 1 === e.indexOf(i) && (e.push(i), r = !0) + } + return r + } + + function findBaseTexture(t, e) { + return t instanceof Rt && (-1 === e.indexOf(t) && e.push(t), !0) + } + + function findTexture(t, e) { + if (t._texture && t._texture instanceof Ht) { + var r = t._texture.baseTexture; + return -1 === e.indexOf(r) && e.push(r), !0 + } + return !1 + } + + function drawText(t, e) { + return e instanceof Qr && (e.updateText(!0), !0) + } + + function calculateTextStyle(t, e) { + if (e instanceof Hr) { + var r = e.toFontString(); + return Yr.measureFont(r), !0 + } + return !1 + } + + function findText(t, e) { + if (t instanceof Qr) { + -1 === e.indexOf(t.style) && e.push(t.style), -1 === e.indexOf(t) && e.push(t); + var r = t._texture.baseTexture; + return -1 === e.indexOf(r) && e.push(r), !0 + } + return !1 + } + + function findTextStyle(t, e) { + return t instanceof Hr && (-1 === e.indexOf(t) && e.push(t), !0) + } + $r.prototype.upload = function(t, e) { + "function" == typeof t && (e = t, t = null), t && this.add(t), this.queue.length ? (e && this.completes.push(e), this.ticking || (this.ticking = !0, xt.system.addOnce(this.tick, this, _t.UTILITY))) : e && e() + }, $r.prototype.tick = function() { + setTimeout(this.delayedTick, 0) + }, $r.prototype.prepareItems = function() { + for (this.limiter.beginFrame(); this.queue.length && this.limiter.allowedToUpload();) { + var t = this.queue[0], + e = !1; + if (t && !t._destroyed) + for (var r = 0, n = this.uploadHooks.length; r < n; r++) + if (this.uploadHooks[r](this.uploadHookHelper, t)) { + this.queue.shift(), e = !0; + break + } + e || this.queue.shift() + } + if (this.queue.length) xt.system.addOnce(this.tick, this, _t.UTILITY); + else { + this.ticking = !1; + for (var i = this.completes.slice(0), o = this.completes.length = 0, a = i.length; o < a; o++) i[o]() + } + }, $r.prototype.registerFindHook = function(t) { + return t && this.addHooks.push(t), this + }, $r.prototype.registerUploadHook = function(t) { + return t && this.uploadHooks.push(t), this + }, $r.prototype.add = function(t) { + for (var e = 0, r = this.addHooks.length; e < r && !this.addHooks[e](t, this.queue); e++); + if (t instanceof pt) + for (var n = t.children.length - 1; 0 <= n; n--) this.add(t.children[n]); + return this + }, $r.prototype.destroy = function() { + this.ticking && xt.system.remove(this.tick, this), this.ticking = !1, this.addHooks = null, this.uploadHooks = null, this.renderer = null, this.completes = null, this.queue = null, this.limiter = null, this.uploadHookHelper = null + }; + var tn = function(e) { + function Prepare(t) { + e.call(this, t), this.uploadHookHelper = this.renderer, this.registerFindHook(findGraphics), this.registerUploadHook(uploadBaseTextures), this.registerUploadHook(uploadGraphics) + } + return e && (Prepare.__proto__ = e), (Prepare.prototype = Object.create(e && e.prototype)).constructor = Prepare + }($r); + + function uploadBaseTextures(t, e) { + return e instanceof Rt && (e._glTextures[t.CONTEXT_UID] || t.textureManager.updateTexture(e), !0) + } + + function uploadGraphics(t, e) { + return e instanceof Gr && ((e.dirty || e.clearDirty || !e._webGL[t.plugins.graphics.CONTEXT_UID]) && t.plugins.graphics.updateGraphics(e), !0) + } + + function findGraphics(t, e) { + return t instanceof Gr && (e.push(t), !0) + } + var en = function(t) { + this.maxMilliseconds = t, this.frameStart = 0 + }; + en.prototype.beginFrame = function() { + this.frameStart = Date.now() + }, en.prototype.allowedToUpload = function() { + return Date.now() - this.frameStart < this.maxMilliseconds + }; + var rn = function Application(e) { + var r = this; + e = Object.assign({ + forceCanvas: !1 + }, e), this.renderer = autoDetectRenderer(e), this.stage = new pt, Application._plugins.forEach(function(t) { + t.init.call(r, e) + }) + }, + nn = { + view: { + configurable: !0 + }, + screen: { + configurable: !0 + } + }; + rn.registerPlugin = function(t) { + rn._plugins.push(t) + }, rn.prototype.render = function() { + this.renderer.render(this.stage) + }, nn.view.get = function() { + return this.renderer.view + }, nn.screen.get = function() { + return this.renderer.screen + }, rn.prototype.destroy = function(t) { + var e = this, + r = rn._plugins.slice(0); + r.reverse(), r.forEach(function(t) { + t.destroy.call(e) + }), this.stage.destroy(), this.stage = null, this.renderer.destroy(t), this.renderer = null, this._options = null + }, Object.defineProperties(rn.prototype, nn), rn._plugins = []; + var on = function() {}; + on.init = function(t) { + var e = this; + Object.defineProperty(this, "resizeTo", { + set: function(t) { + window.removeEventListener("resize", this.resize), (this._resizeTo = t) && (window.addEventListener("resize", this.resize), this.resize()) + }, + get: function() { + return this._resizeTo + } + }), this.resize = function() { + e._resizeTo && (e._resizeTo === window ? e.renderer.resize(window.innerWidth, window.innerHeight) : e.renderer.resize(e._resizeTo.clientWidth, e._resizeTo.clientHeight)) + }, this._resizeTo = null, this.resizeTo = t.resizeTo || null + }, on.destroy = function() { + this.resizeTo = null, this.resize = null + }, rn.registerPlugin(on); + var an = r(66), + sn = r.n(an); + + function unwrapExports(t) { + return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t + } + + function createCommonjsModule(t, e) { + return t(e = { + exports: {} + }, e.exports), e.exports + } + var un = function(t, e) { + e = e || {}; + for (var n = { + key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"], + q: { + name: "queryKey", + parser: /(?:^|&)([^&=]*)=?([^&]*)/g + }, + parser: { + strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, + loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + } + }, r = n.parser[e.strictMode ? "strict" : "loose"].exec(t), i = {}, o = 14; o--;) i[n.key[o]] = r[o] || ""; + return i[n.q.name] = {}, i[n.key[12]].replace(n.q.parser, function(t, e, r) { + e && (i[n.q.name][e] = r) + }), i + }, + hn = createCommonjsModule(function(t, e) { + Object.defineProperty(e, "__esModule", { + value: !0 + }); + var r = function() { + function defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + return function(t, e, r) { + return e && defineProperties(t.prototype, e), r && defineProperties(t, r), t + } + }(); + + function _classCallCheck(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + } + var n = function() { + function MiniSignalBinding(t, e, r) { + void 0 === e && (e = !1), _classCallCheck(this, MiniSignalBinding), this._fn = t, this._once = e, this._thisArg = r, this._next = this._prev = this._owner = null + } + return r(MiniSignalBinding, [{ + key: "detach", + value: function() { + return null !== this._owner && (this._owner.detach(this), !0) + } + }]), MiniSignalBinding + }(); + + function _addMiniSignalBinding(t, e) { + return t._head ? (t._tail._next = e)._prev = t._tail : t._head = e, (t._tail = e)._owner = t, e + } + var i = function() { + function MiniSignal() { + _classCallCheck(this, MiniSignal), this._head = this._tail = void 0 + } + return r(MiniSignal, [{ + key: "handlers", + value: function() { + var t = !(arguments.length <= 0 || void 0 === arguments[0]) && arguments[0], + e = this._head; + if (t) return !!e; + for (var r = []; e;) r.push(e), e = e._next; + return r + } + }, { + key: "has", + value: function(t) { + if (!(t instanceof n)) throw new Error("MiniSignal#has(): First arg must be a MiniSignalBinding object."); + return t._owner === this + } + }, { + key: "dispatch", + value: function() { + var t = arguments, + e = this._head; + if (!e) return !1; + for (; e;) e._once && this.detach(e), e._fn.apply(e._thisArg, t), e = e._next; + return !0 + } + }, { + key: "add", + value: function(t) { + var e = arguments.length <= 1 || void 0 === arguments[1] ? null : arguments[1]; + if ("function" != typeof t) throw new Error("MiniSignal#add(): First arg must be a Function."); + return _addMiniSignalBinding(this, new n(t, !1, e)) + } + }, { + key: "once", + value: function(t) { + var e = arguments.length <= 1 || void 0 === arguments[1] ? null : arguments[1]; + if ("function" != typeof t) throw new Error("MiniSignal#once(): First arg must be a Function."); + return _addMiniSignalBinding(this, new n(t, !0, e)) + } + }, { + key: "detach", + value: function(t) { + if (!(t instanceof n)) throw new Error("MiniSignal#detach(): First arg must be a MiniSignalBinding object."); + return t._owner !== this || (t._prev && (t._prev._next = t._next), t._next && (t._next._prev = t._prev), t === this._head ? (this._head = t._next, null === t._next && (this._tail = null)) : t === this._tail && (this._tail = t._prev, this._tail._next = null), t._owner = null), this + } + }, { + key: "detachAll", + value: function() { + var t = this._head; + if (!t) return this; + for (this._head = this._tail = null; t;) t._owner = null, t = t._next; + return this + } + }]), MiniSignal + }(); + i.MiniSignalBinding = n, e.default = i, t.exports = e.default + }); + unwrapExports(hn); + var cn = createCommonjsModule(function(t, e) { + e.__esModule = !0, e.Resource = void 0; + var r = function() { + function defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + return function(t, e, r) { + return e && defineProperties(t.prototype, e), r && defineProperties(t, r), t + } + }(), + i = _interopRequireDefault(un), + n = _interopRequireDefault(hn); + + function _interopRequireDefault(t) { + return t && t.__esModule ? t : { + default: t + } + } + var o = !(!window.XDomainRequest || "withCredentials" in new XMLHttpRequest), + a = null; + + function _noop() {} + var s = e.Resource = function() { + function Resource(t, e, r) { + if (function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, Resource), "string" != typeof t || "string" != typeof e) throw new Error("Both name and url are required for constructing a resource."); + r = r || {}, this._flags = 0, this._setFlag(Resource.STATUS_FLAGS.DATA_URL, 0 === e.indexOf("data:")), this.name = t, this.url = e, this.extension = this._getExtension(), this.data = null, this.crossOrigin = !0 === r.crossOrigin ? "anonymous" : r.crossOrigin, this.timeout = r.timeout || 0, this.loadType = r.loadType || this._determineLoadType(), this.xhrType = r.xhrType, this.metadata = r.metadata || {}, this.error = null, this.xhr = null, this.children = [], this.type = Resource.TYPE.UNKNOWN, this.progressChunk = 0, this._dequeue = _noop, this._onLoadBinding = null, this._elementTimer = 0, this._boundComplete = this.complete.bind(this), this._boundOnError = this._onError.bind(this), this._boundOnProgress = this._onProgress.bind(this), this._boundOnTimeout = this._onTimeout.bind(this), this._boundXhrOnError = this._xhrOnError.bind(this), this._boundXhrOnTimeout = this._xhrOnTimeout.bind(this), this._boundXhrOnAbort = this._xhrOnAbort.bind(this), this._boundXhrOnLoad = this._xhrOnLoad.bind(this), this.onStart = new n.default, this.onProgress = new n.default, this.onComplete = new n.default, this.onAfterMiddleware = new n.default + } + return Resource.setExtensionLoadType = function(t, e) { + setExtMap(Resource._loadTypeMap, t, e) + }, Resource.setExtensionXhrType = function(t, e) { + setExtMap(Resource._xhrTypeMap, t, e) + }, Resource.prototype.complete = function() { + this._clearEvents(), this._finish() + }, Resource.prototype.abort = function(t) { + if (!this.error) { + if (this.error = new Error(t), this._clearEvents(), this.xhr) this.xhr.abort(); + else if (this.xdr) this.xdr.abort(); + else if (this.data) + if (this.data.src) this.data.src = Resource.EMPTY_GIF; + else + for (; this.data.firstChild;) this.data.removeChild(this.data.firstChild); + this._finish() + } + }, Resource.prototype.load = function(t) { + var e = this; + if (!this.isLoading) + if (this.isComplete) t && setTimeout(function() { + return t(e) + }, 1); + else switch (t && this.onComplete.once(t), this._setFlag(Resource.STATUS_FLAGS.LOADING, !0), this.onStart.dispatch(this), !1 !== this.crossOrigin && "string" == typeof this.crossOrigin || (this.crossOrigin = this._determineCrossOrigin(this.url)), this.loadType) { + case Resource.LOAD_TYPE.IMAGE: + this.type = Resource.TYPE.IMAGE, this._loadElement("image"); + break; + case Resource.LOAD_TYPE.AUDIO: + this.type = Resource.TYPE.AUDIO, this._loadSourceElement("audio"); + break; + case Resource.LOAD_TYPE.VIDEO: + this.type = Resource.TYPE.VIDEO, this._loadSourceElement("video"); + break; + case Resource.LOAD_TYPE.XHR: + default: + o && this.crossOrigin ? this._loadXdr() : this._loadXhr() + } + }, Resource.prototype._hasFlag = function(t) { + return 0 != (this._flags & t) + }, Resource.prototype._setFlag = function(t, e) { + this._flags = e ? this._flags | t : this._flags & ~t + }, Resource.prototype._clearEvents = function() { + clearTimeout(this._elementTimer), this.data && this.data.removeEventListener && (this.data.removeEventListener("error", this._boundOnError, !1), this.data.removeEventListener("load", this._boundComplete, !1), this.data.removeEventListener("progress", this._boundOnProgress, !1), this.data.removeEventListener("canplaythrough", this._boundComplete, !1)), this.xhr && (this.xhr.removeEventListener ? (this.xhr.removeEventListener("error", this._boundXhrOnError, !1), this.xhr.removeEventListener("timeout", this._boundXhrOnTimeout, !1), this.xhr.removeEventListener("abort", this._boundXhrOnAbort, !1), this.xhr.removeEventListener("progress", this._boundOnProgress, !1), this.xhr.removeEventListener("load", this._boundXhrOnLoad, !1)) : (this.xhr.onerror = null, this.xhr.ontimeout = null, this.xhr.onprogress = null, this.xhr.onload = null)) + }, Resource.prototype._finish = function() { + if (this.isComplete) throw new Error("Complete called again for an already completed resource."); + this._setFlag(Resource.STATUS_FLAGS.COMPLETE, !0), this._setFlag(Resource.STATUS_FLAGS.LOADING, !1), this.onComplete.dispatch(this) + }, Resource.prototype._loadElement = function(t) { + this.metadata.loadElement ? this.data = this.metadata.loadElement : "image" === t && void 0 !== window.Image ? this.data = new Image : this.data = document.createElement(t), this.crossOrigin && (this.data.crossOrigin = this.crossOrigin), this.metadata.skipSource || (this.data.src = this.url), this.data.addEventListener("error", this._boundOnError, !1), this.data.addEventListener("load", this._boundComplete, !1), this.data.addEventListener("progress", this._boundOnProgress, !1), this.timeout && (this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout)) + }, Resource.prototype._loadSourceElement = function(t) { + if (this.metadata.loadElement ? this.data = this.metadata.loadElement : "audio" === t && void 0 !== window.Audio ? this.data = new Audio : this.data = document.createElement(t), null !== this.data) { + if (this.crossOrigin && (this.data.crossOrigin = this.crossOrigin), !this.metadata.skipSource) + if (navigator.isCocoonJS) this.data.src = Array.isArray(this.url) ? this.url[0] : this.url; + else if (Array.isArray(this.url)) + for (var e = this.metadata.mimeType, r = 0; r < this.url.length; ++r) this.data.appendChild(this._createSource(t, this.url[r], Array.isArray(e) ? e[r] : e)); + else { + var n = this.metadata.mimeType; + this.data.appendChild(this._createSource(t, this.url, Array.isArray(n) ? n[0] : n)) + } + this.data.addEventListener("error", this._boundOnError, !1), this.data.addEventListener("load", this._boundComplete, !1), this.data.addEventListener("progress", this._boundOnProgress, !1), this.data.addEventListener("canplaythrough", this._boundComplete, !1), this.data.load(), this.timeout && (this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout)) + } else this.abort("Unsupported element: " + t) + }, Resource.prototype._loadXhr = function() { + "string" != typeof this.xhrType && (this.xhrType = this._determineXhrType()); + var t = this.xhr = new XMLHttpRequest; + t.open("GET", this.url, !0), t.timeout = this.timeout, this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON || this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT ? t.responseType = Resource.XHR_RESPONSE_TYPE.TEXT : t.responseType = this.xhrType, t.addEventListener("error", this._boundXhrOnError, !1), t.addEventListener("timeout", this._boundXhrOnTimeout, !1), t.addEventListener("abort", this._boundXhrOnAbort, !1), t.addEventListener("progress", this._boundOnProgress, !1), t.addEventListener("load", this._boundXhrOnLoad, !1), t.send() + }, Resource.prototype._loadXdr = function() { + "string" != typeof this.xhrType && (this.xhrType = this._determineXhrType()); + var t = this.xhr = new XDomainRequest; + t.timeout = this.timeout || 5e3, t.onerror = this._boundXhrOnError, t.ontimeout = this._boundXhrOnTimeout, t.onprogress = this._boundOnProgress, t.onload = this._boundXhrOnLoad, t.open("GET", this.url, !0), setTimeout(function() { + return t.send() + }, 1) + }, Resource.prototype._createSource = function(t, e, r) { + r || (r = t + "/" + this._getExtension(e)); + var n = document.createElement("source"); + return n.src = e, n.type = r, n + }, Resource.prototype._onError = function(t) { + this.abort("Failed to load element using: " + t.target.nodeName) + }, Resource.prototype._onProgress = function(t) { + t && t.lengthComputable && this.onProgress.dispatch(this, t.loaded / t.total) + }, Resource.prototype._onTimeout = function() { + this.abort("Load timed out.") + }, Resource.prototype._xhrOnError = function() { + var t = this.xhr; + this.abort(reqType(t) + " Request failed. Status: " + t.status + ', text: "' + t.statusText + '"') + }, Resource.prototype._xhrOnTimeout = function() { + var t = this.xhr; + this.abort(reqType(t) + " Request timed out.") + }, Resource.prototype._xhrOnAbort = function() { + var t = this.xhr; + this.abort(reqType(t) + " Request was aborted by the user.") + }, Resource.prototype._xhrOnLoad = function() { + var t = this.xhr, + e = "", + r = void 0 === t.status ? 200 : t.status; + if ("" !== t.responseType && "text" !== t.responseType && void 0 !== t.responseType || (e = t.responseText), 0 === r && (0 < e.length || t.responseType === Resource.XHR_RESPONSE_TYPE.BUFFER) ? r = 200 : 1223 === r && (r = 204), 2 === (r / 100 | 0)) { + if (this.xhrType === Resource.XHR_RESPONSE_TYPE.TEXT) this.data = e, this.type = Resource.TYPE.TEXT; + else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON) try { + this.data = JSON.parse(e), this.type = Resource.TYPE.JSON + } catch (t) { + return void this.abort("Error trying to parse loaded json: " + t) + } else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT) try { + if (window.DOMParser) { + var n = new DOMParser; + this.data = n.parseFromString(e, "text/xml") + } else { + var i = document.createElement("div"); + i.innerHTML = e, this.data = i + } + this.type = Resource.TYPE.XML + } catch (t) { + return void this.abort("Error trying to parse loaded xml: " + t) + } else this.data = t.response || e; + this.complete() + } else this.abort("[" + t.status + "] " + t.statusText + ": " + t.responseURL) + }, Resource.prototype._determineCrossOrigin = function(t, e) { + if (0 === t.indexOf("data:")) return ""; + if (window.origin !== window.location.origin) return "anonymous"; + e = e || window.location, a || (a = document.createElement("a")), a.href = t; + var r = !(t = (0, i.default)(a.href, { + strictMode: !0 + })).port && "" === e.port || t.port === e.port, + n = t.protocol ? t.protocol + ":" : ""; + return t.host === e.hostname && r && n === e.protocol ? "" : "anonymous" + }, Resource.prototype._determineXhrType = function() { + return Resource._xhrTypeMap[this.extension] || Resource.XHR_RESPONSE_TYPE.TEXT + }, Resource.prototype._determineLoadType = function() { + return Resource._loadTypeMap[this.extension] || Resource.LOAD_TYPE.XHR + }, Resource.prototype._getExtension = function() { + var t = this.url, + e = ""; + if (this.isDataUrl) { + var r = t.indexOf("/"); + e = t.substring(r + 1, t.indexOf(";", r)) + } else { + var n = t.indexOf("?"), + i = t.indexOf("#"), + o = Math.min(-1 < n ? n : t.length, -1 < i ? i : t.length); + e = (t = t.substring(0, o)).substring(t.lastIndexOf(".") + 1) + } + return e.toLowerCase() + }, Resource.prototype._getMimeFromXhrType = function(t) { + switch (t) { + case Resource.XHR_RESPONSE_TYPE.BUFFER: + return "application/octet-binary"; + case Resource.XHR_RESPONSE_TYPE.BLOB: + return "application/blob"; + case Resource.XHR_RESPONSE_TYPE.DOCUMENT: + return "application/xml"; + case Resource.XHR_RESPONSE_TYPE.JSON: + return "application/json"; + case Resource.XHR_RESPONSE_TYPE.DEFAULT: + case Resource.XHR_RESPONSE_TYPE.TEXT: + default: + return "text/plain" + } + }, r(Resource, [{ + key: "isDataUrl", + get: function() { + return this._hasFlag(Resource.STATUS_FLAGS.DATA_URL) + } + }, { + key: "isComplete", + get: function() { + return this._hasFlag(Resource.STATUS_FLAGS.COMPLETE) + } + }, { + key: "isLoading", + get: function() { + return this._hasFlag(Resource.STATUS_FLAGS.LOADING) + } + }]), Resource + }(); + + function setExtMap(t, e, r) { + e && 0 === e.indexOf(".") && (e = e.substring(1)), e && (t[e] = r) + } + + function reqType(t) { + return t.toString().replace("object ", "") + } + s.STATUS_FLAGS = { + NONE: 0, + DATA_URL: 1, + COMPLETE: 2, + LOADING: 4 + }, s.TYPE = { + UNKNOWN: 0, + JSON: 1, + XML: 2, + IMAGE: 3, + AUDIO: 4, + VIDEO: 5, + TEXT: 6 + }, s.LOAD_TYPE = { + XHR: 1, + IMAGE: 2, + AUDIO: 3, + VIDEO: 4 + }, s.XHR_RESPONSE_TYPE = { + DEFAULT: "text", + BUFFER: "arraybuffer", + BLOB: "blob", + DOCUMENT: "document", + JSON: "json", + TEXT: "text" + }, s._loadTypeMap = { + gif: s.LOAD_TYPE.IMAGE, + png: s.LOAD_TYPE.IMAGE, + bmp: s.LOAD_TYPE.IMAGE, + jpg: s.LOAD_TYPE.IMAGE, + jpeg: s.LOAD_TYPE.IMAGE, + tif: s.LOAD_TYPE.IMAGE, + tiff: s.LOAD_TYPE.IMAGE, + webp: s.LOAD_TYPE.IMAGE, + tga: s.LOAD_TYPE.IMAGE, + svg: s.LOAD_TYPE.IMAGE, + "svg+xml": s.LOAD_TYPE.IMAGE, + mp3: s.LOAD_TYPE.AUDIO, + ogg: s.LOAD_TYPE.AUDIO, + wav: s.LOAD_TYPE.AUDIO, + mp4: s.LOAD_TYPE.VIDEO, + webm: s.LOAD_TYPE.VIDEO + }, s._xhrTypeMap = { + xhtml: s.XHR_RESPONSE_TYPE.DOCUMENT, + html: s.XHR_RESPONSE_TYPE.DOCUMENT, + htm: s.XHR_RESPONSE_TYPE.DOCUMENT, + xml: s.XHR_RESPONSE_TYPE.DOCUMENT, + tmx: s.XHR_RESPONSE_TYPE.DOCUMENT, + svg: s.XHR_RESPONSE_TYPE.DOCUMENT, + tsx: s.XHR_RESPONSE_TYPE.DOCUMENT, + gif: s.XHR_RESPONSE_TYPE.BLOB, + png: s.XHR_RESPONSE_TYPE.BLOB, + bmp: s.XHR_RESPONSE_TYPE.BLOB, + jpg: s.XHR_RESPONSE_TYPE.BLOB, + jpeg: s.XHR_RESPONSE_TYPE.BLOB, + tif: s.XHR_RESPONSE_TYPE.BLOB, + tiff: s.XHR_RESPONSE_TYPE.BLOB, + webp: s.XHR_RESPONSE_TYPE.BLOB, + tga: s.XHR_RESPONSE_TYPE.BLOB, + json: s.XHR_RESPONSE_TYPE.JSON, + text: s.XHR_RESPONSE_TYPE.TEXT, + txt: s.XHR_RESPONSE_TYPE.TEXT, + ttf: s.XHR_RESPONSE_TYPE.BUFFER, + otf: s.XHR_RESPONSE_TYPE.BUFFER + }, s.EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", t.exports.default = s + }); + unwrapExports(cn); + cn.Resource; + var ln = createCommonjsModule(function(t, e) { + e.__esModule = !0, e.encodeBinary = encodeBinary; + var s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + function encodeBinary(t) { + for (var e = "", r = 0; r < t.length;) { + for (var n = [0, 0, 0], i = [0, 0, 0, 0], o = 0; o < n.length; ++o) r < t.length ? n[o] = 255 & t.charCodeAt(r++) : n[o] = 0; + switch (i[0] = n[0] >> 2, i[1] = (3 & n[0]) << 4 | n[1] >> 4, i[2] = (15 & n[1]) << 2 | n[2] >> 6, i[3] = 63 & n[2], r - (t.length - 1)) { + case 2: + i[3] = 64, i[2] = 64; + break; + case 1: + i[3] = 64 + } + for (var a = 0; a < i.length; ++a) e += s.charAt(i[a]) + } + return e + } + t.exports.default = encodeBinary + }); + unwrapExports(ln); + ln.encodeBinary; + var fn = createCommonjsModule(function(t, e) { + e.__esModule = !0, e.blobMiddlewareFactory = function() { + return function(t, e) { + if (t.data) { + if (t.xhr && t.xhrType === cn.Resource.XHR_RESPONSE_TYPE.BLOB) + if (window.Blob && "string" != typeof t.data) { + if (0 === t.data.type.indexOf("image")) { + var r = i.createObjectURL(t.data); + return t.blob = t.data, t.data = new Image, t.data.src = r, t.type = cn.Resource.TYPE.IMAGE, void(t.data.onload = function() { + i.revokeObjectURL(r), t.data.onload = null, e() + }) + } + } else { + var n = t.xhr.getResponseHeader("content-type"); + if (n && 0 === n.indexOf("image")) return t.data = new Image, t.data.src = "data:" + n + ";base64," + (0, ln.encodeBinary)(t.xhr.responseText), t.type = cn.Resource.TYPE.IMAGE, void(t.data.onload = function() { + t.data.onload = null, e() + }) + } + e() + } else e() + } + }; + var i = window.URL || window.webkitURL + }); + unwrapExports(fn); + var dn = fn.blobMiddlewareFactory, + pn = function() {}; + pn.use = function(t, e) { + t.data && t.type === an.Resource.TYPE.IMAGE && (t.texture = Ht.fromLoader(t.data, t.url, t.name)), e() + }; + var mn = function(s) { + function Loader(t, e) { + var n = this; + s.call(this, t, e), l.a.call(this); + for (var r = 0; r < Loader._plugins.length; ++r) { + var i = Loader._plugins[r], + o = i.pre, + a = i.use; + o && this.pre(o), a && this.use(a) + } + this.onStart.add(function(t) { + return n.emit("start", t) + }), this.onProgress.add(function(t, e) { + return n.emit("progress", t, e) + }), this.onError.add(function(t, e, r) { + return n.emit("error", t, e, r) + }), this.onLoad.add(function(t, e) { + return n.emit("load", t, e) + }), this.onComplete.add(function(t, e) { + return n.emit("complete", t, e) + }), this._protected = !1 + } + s && (Loader.__proto__ = s); + var t = { + shared: { + configurable: !0 + } + }; + return ((Loader.prototype = Object.create(s && s.prototype)).constructor = Loader).prototype.destroy = function() { + this._protected || (this.removeAllListeners(), this.reset()) + }, t.shared.get = function() { + var t = Loader._shared; + return t || ((t = new Loader)._protected = !0, Loader._shared = t), t + }, Object.defineProperties(Loader, t), Loader + }(sn.a); + Object.assign(mn.prototype, l.a.prototype), mn._plugins = [], mn.registerPlugin = function(t) { + return mn._plugins.push(t), t.add && t.add(), mn + }, mn.registerPlugin({ + use: dn() + }), mn.registerPlugin(pn); + var vn = function() {}; + vn.init = function(t) { + t = Object.assign({ + sharedLoader: !1 + }, t), this.loader = t.sharedLoader ? mn.shared : new mn + }, vn.destroy = function() { + this.loader && (this.loader.destroy(), this.loader = null) + }; + var gn = an.Resource, + yn = function(i) { + function ParticleContainer(t, e, r, n) { + void 0 === t && (t = 1500), void 0 === r && (r = 16384), void 0 === n && (n = !1), i.call(this); + 16384 < r && (r = 16384), t < r && (r = t), this._properties = [!1, !0, !1, !1, !1], this._maxSize = t, this._batchSize = r, this._buffers = null, this._bufferUpdateIDs = [], this._updateID = 0, this.interactiveChildren = !1, this.blendMode = y.NORMAL, this.autoResize = n, this.roundPixels = !0, this.baseTexture = null, this.setProperties(e), this._tint = 0, this.tintRgb = new Float32Array(4), this.tint = 16777215 + } + i && (ParticleContainer.__proto__ = i); + var t = { + tint: { + configurable: !0 + } + }; + return ((ParticleContainer.prototype = Object.create(i && i.prototype)).constructor = ParticleContainer).prototype.setProperties = function(t) { + t && (this._properties[0] = "vertices" in t || "scale" in t ? !!t.vertices || !!t.scale : this._properties[0], this._properties[1] = "position" in t ? !!t.position : this._properties[1], this._properties[2] = "rotation" in t ? !!t.rotation : this._properties[2], this._properties[3] = "uvs" in t ? !!t.uvs : this._properties[3], this._properties[4] = "tint" in t || "alpha" in t ? !!t.tint || !!t.alpha : this._properties[4]) + }, ParticleContainer.prototype.updateTransform = function() { + this.displayObjectUpdateTransform() + }, t.tint.get = function() { + return this._tint + }, t.tint.set = function(t) { + hex2rgb(this._tint = t, this.tintRgb) + }, ParticleContainer.prototype.render = function(t) { + var e = this; + this.visible && !(this.worldAlpha <= 0) && this.children.length && this.renderable && (this.baseTexture || (this.baseTexture = this.children[0]._texture.baseTexture, this.baseTexture.valid || this.baseTexture.once("update", function() { + return e.onChildrenChange(0) + })), t.batch.setObjectRenderer(t.plugins.particle), t.plugins.particle.render(this)) + }, ParticleContainer.prototype.onChildrenChange = function(t) { + for (var e = Math.floor(t / this._batchSize); this._bufferUpdateIDs.length < e;) this._bufferUpdateIDs.push(0); + this._bufferUpdateIDs[e] = ++this._updateID + }, ParticleContainer.prototype.dispose = function() { + if (this._buffers) { + for (var t = 0; t < this._buffers.length; ++t) this._buffers[t].destroy(); + this._buffers = null + } + }, ParticleContainer.prototype.destroy = function(t) { + i.prototype.destroy.call(this, t), this.dispose(), this._properties = null, this._buffers = null, this._bufferUpdateIDs = null + }, Object.defineProperties(ParticleContainer.prototype, t), ParticleContainer + }(pt), + _n = function(t, e, r) { + this.geometry = new ee, this.indexBuffer = null, this.size = r, this.dynamicProperties = [], this.staticProperties = []; + for (var n = 0; n < t.length; ++n) { + var i = t[n]; + i = { + attributeName: i.attributeName, + size: i.size, + uploadFunction: i.uploadFunction, + type: i.type || w.FLOAT, + offset: i.offset + }, e[n] ? this.dynamicProperties.push(i) : this.staticProperties.push(i) + } + this.staticStride = 0, this.staticBuffer = null, this.staticData = null, this.staticDataUint32 = null, this.dynamicStride = 0, this.dynamicBuffer = null, this.dynamicData = null, this.dynamicDataUint32 = null, this._updateID = 0, this.initBuffers() + }; + _n.prototype.initBuffers = function() { + var t = this.geometry, + e = 0; + this.indexBuffer = new Zt(createIndicesForQuads(this.size), !0, !0), t.addIndex(this.indexBuffer); + for (var r = this.dynamicStride = 0; r < this.dynamicProperties.length; ++r) { + var n = this.dynamicProperties[r]; + n.offset = e, e += n.size, this.dynamicStride += n.size + } + var i = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4); + this.dynamicData = new Float32Array(i), this.dynamicDataUint32 = new Uint32Array(i), this.dynamicBuffer = new Zt(this.dynamicData, !1, !1); + for (var o = 0, a = this.staticStride = 0; a < this.staticProperties.length; ++a) { + var s = this.staticProperties[a]; + s.offset = o, o += s.size, this.staticStride += s.size + } + var u = new ArrayBuffer(this.size * this.staticStride * 4 * 4); + this.staticData = new Float32Array(u), this.staticDataUint32 = new Uint32Array(u), this.staticBuffer = new Zt(this.staticData, !0, !1); + for (var h = 0; h < this.dynamicProperties.length; ++h) { + var c = this.dynamicProperties[h]; + t.addAttribute(c.attributeName, this.dynamicBuffer, 0, c.type === w.UNSIGNED_BYTE, c.type, 4 * this.dynamicStride, 4 * c.offset) + } + for (var l = 0; l < this.staticProperties.length; ++l) { + var f = this.staticProperties[l]; + t.addAttribute(f.attributeName, this.staticBuffer, 0, f.type === w.UNSIGNED_BYTE, f.type, 4 * this.staticStride, 4 * f.offset) + } + }, _n.prototype.uploadDynamic = function(t, e, r) { + for (var n = 0; n < this.dynamicProperties.length; n++) { + var i = this.dynamicProperties[n]; + i.uploadFunction(t, e, r, i.type === w.UNSIGNED_BYTE ? this.dynamicDataUint32 : this.dynamicData, this.dynamicStride, i.offset) + } + this.dynamicBuffer._updateID++ + }, _n.prototype.uploadStatic = function(t, e, r) { + for (var n = 0; n < this.staticProperties.length; n++) { + var i = this.staticProperties[n]; + i.uploadFunction(t, e, r, i.type === w.UNSIGNED_BYTE ? this.staticDataUint32 : this.staticData, this.staticStride, i.offset) + } + this.staticBuffer._updateID++ + }, _n.prototype.destroy = function() { + this.indexBuffer = null, this.dynamicProperties = null, this.dynamicBuffer = null, this.dynamicData = null, this.dynamicDataUint32 = null, this.staticProperties = null, this.staticBuffer = null, this.staticData = null, this.staticDataUint32 = null, this.geometry.destroy() + }; + var bn = function(e) { + function ParticleRenderer(t) { + e.call(this, t), this.shader = null, this.properties = null, this.tempMatrix = new Y, this.properties = [{ + attributeName: "aVertexPosition", + size: 2, + uploadFunction: this.uploadVertices, + offset: 0 + }, { + attributeName: "aPositionCoord", + size: 2, + uploadFunction: this.uploadPosition, + offset: 0 + }, { + attributeName: "aRotation", + size: 1, + uploadFunction: this.uploadRotation, + offset: 0 + }, { + attributeName: "aTextureCoord", + size: 2, + uploadFunction: this.uploadUvs, + offset: 0 + }, { + attributeName: "aColor", + size: 1, + type: w.UNSIGNED_BYTE, + uploadFunction: this.uploadTint, + offset: 0 + }], this.shader = Ie.from("attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nattribute vec2 aPositionCoord;\nattribute float aRotation;\n\nuniform mat3 translationMatrix;\nuniform vec4 uColor;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nvoid main(void){\n float x = (aVertexPosition.x) * cos(aRotation) - (aVertexPosition.y) * sin(aRotation);\n float y = (aVertexPosition.x) * sin(aRotation) + (aVertexPosition.y) * cos(aRotation);\n\n vec2 v = vec2(x, y);\n v = v + aPositionCoord;\n\n gl_Position = vec4((translationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vColor = aColor * uColor;\n}\n", "varying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n vec4 color = texture2D(uSampler, vTextureCoord) * vColor;\n gl_FragColor = color;\n}", {}) + } + return e && (ParticleRenderer.__proto__ = e), ((ParticleRenderer.prototype = Object.create(e && e.prototype)).constructor = ParticleRenderer).prototype.render = function(t) { + var e = t.children, + r = t._maxSize, + n = t._batchSize, + i = this.renderer, + o = e.length; + if (0 !== o) { + r < o && (o = r); + var a = t._buffers; + a || (a = t._buffers = this.generateBuffers(t)); + var s = e[0]._texture.baseTexture; + this.renderer.state.setBlendMode(correctBlendMode(t.blendMode, s.premultiplyAlpha)); + var u = i.gl, + h = t.worldTransform.copyTo(this.tempMatrix); + h.prepend(i.globalUniforms.uniforms.projectionMatrix), this.shader.uniforms.translationMatrix = h.toArray(!0), this.shader.uniforms.uColor = premultiplyRgba(t.tintRgb, t.worldAlpha, this.shader.uniforms.uColor, s.premultiplyAlpha), this.shader.uniforms.uSampler = s, this.renderer.shader.bind(this.shader); + for (var c = !1, l = 0, f = 0; l < o; l += n, f += 1) { + var d = o - l; + if (n < d && (d = n), f >= a.length) { + if (!t.autoResize) break; + a.push(this._generateOneMoreBuffer(t)) + } + var p = a[f]; + p.uploadDynamic(e, l, d); + var m = t._bufferUpdateIDs[f] || 0; + (c = c || p._updateID < m) && (p._updateID = t._updateID, p.uploadStatic(e, l, d)), i.geometry.bind(p.geometry), u.drawElements(u.TRIANGLES, 6 * d, u.UNSIGNED_SHORT, 0) + } + } + }, ParticleRenderer.prototype.generateBuffers = function(t) { + for (var e = [], r = t._maxSize, n = t._batchSize, i = t._properties, o = 0; o < r; o += n) e.push(new _n(this.properties, i, n)); + return e + }, ParticleRenderer.prototype._generateOneMoreBuffer = function(t) { + var e = t._batchSize, + r = t._properties; + return new _n(this.properties, r, e) + }, ParticleRenderer.prototype.uploadVertices = function(t, e, r, n, i, o) { + for (var a = 0, s = 0, u = 0, h = 0, c = 0; c < r; ++c) { + var l = t[e + c], + f = l._texture, + d = l.scale.x, + p = l.scale.y, + m = f.trim, + v = f.orig; + m ? (a = (s = m.x - l.anchor.x * v.width) + m.width, u = (h = m.y - l.anchor.y * v.height) + m.height) : (a = v.width * (1 - l.anchor.x), s = v.width * -l.anchor.x, u = v.height * (1 - l.anchor.y), h = v.height * -l.anchor.y), n[o] = s * d, n[o + 1] = h * p, n[o + i] = a * d, n[o + i + 1] = h * p, n[o + 2 * i] = a * d, n[o + 2 * i + 1] = u * p, n[o + 3 * i] = s * d, n[o + 3 * i + 1] = u * p, o += 4 * i + } + }, ParticleRenderer.prototype.uploadPosition = function(t, e, r, n, i, o) { + for (var a = 0; a < r; a++) { + var s = t[e + a].position; + n[o] = s.x, n[o + 1] = s.y, n[o + i] = s.x, n[o + i + 1] = s.y, n[o + 2 * i] = s.x, n[o + 2 * i + 1] = s.y, n[o + 3 * i] = s.x, n[o + 3 * i + 1] = s.y, o += 4 * i + } + }, ParticleRenderer.prototype.uploadRotation = function(t, e, r, n, i, o) { + for (var a = 0; a < r; a++) { + var s = t[e + a].rotation; + n[o] = s, n[o + i] = s, n[o + 2 * i] = s, n[o + 3 * i] = s, o += 4 * i + } + }, ParticleRenderer.prototype.uploadUvs = function(t, e, r, n, i, o) { + for (var a = 0; a < r; ++a) { + var s = t[e + a]._texture._uvs; + n[o + 3 * i + 1] = s ? (n[o] = s.x0, n[o + 1] = s.y0, n[o + i] = s.x1, n[o + i + 1] = s.y1, n[o + 2 * i] = s.x2, n[o + 2 * i + 1] = s.y2, n[o + 3 * i] = s.x3, s.y3) : (n[o] = 0, n[o + 1] = 0, n[o + i] = 0, n[o + i + 1] = 0, n[o + 2 * i] = 0, n[o + 2 * i + 1] = 0, n[o + 3 * i] = 0), o += 4 * i + } + }, ParticleRenderer.prototype.uploadTint = function(t, e, r, n, i, o) { + for (var a = 0; a < r; ++a) { + var s = t[e + a], + u = s._texture.baseTexture.premultiplyAlpha, + h = s.alpha, + c = h < 1 && u ? premultiplyTint(s._tintRGB, h) : s._tintRGB + (255 * h << 24); + n[o] = c, n[o + i] = c, n[o + 2 * i] = c, n[o + 3 * i] = c, o += 4 * i + } + }, ParticleRenderer.prototype.destroy = function() { + e.prototype.destroy.call(this), this.shader && (this.shader.destroy(), this.shader = null), this.tempMatrix = null + }, ParticleRenderer + }(he), + xn = function(t, e, r) { + void 0 === r && (r = null), this.baseTexture = t, this.textures = {}, this.animations = {}, this.data = e, this.resolution = this._updateResolution(r || (this.baseTexture.resource ? this.baseTexture.resource.url : null)), this._frames = this.data.frames, this._frameKeys = Object.keys(this._frames), this._batchIndex = 0, this._callback = null + }, + wn = { + BATCH_SIZE: { + configurable: !0 + } + }; + wn.BATCH_SIZE.get = function() { + return 1e3 + }, xn.prototype._updateResolution = function(t) { + var e = this.data.meta.scale, + r = getResolutionOfUrl(t, null); + return null === r && (r = void 0 !== e ? parseFloat(e) : 1), 1 !== r && this.baseTexture.setResolution(r), r + }, xn.prototype.parse = function(t) { + this._batchIndex = 0, this._callback = t, this._frameKeys.length <= xn.BATCH_SIZE ? (this._processFrames(0), this._processAnimations(), this._parseComplete()) : this._nextBatch() + }, xn.prototype._processFrames = function(t) { + for (var e = t, r = xn.BATCH_SIZE; e - t < r && e < this._frameKeys.length;) { + var n = this._frameKeys[e], + i = this._frames[n], + o = i.frame; + if (o) { + var a = null, + s = null, + u = !1 !== i.trimmed && i.sourceSize ? i.sourceSize : i.frame, + h = new ot(0, 0, Math.floor(u.w) / this.resolution, Math.floor(u.h) / this.resolution); + a = i.rotated ? new ot(Math.floor(o.x) / this.resolution, Math.floor(o.y) / this.resolution, Math.floor(o.h) / this.resolution, Math.floor(o.w) / this.resolution) : new ot(Math.floor(o.x) / this.resolution, Math.floor(o.y) / this.resolution, Math.floor(o.w) / this.resolution, Math.floor(o.h) / this.resolution), !1 !== i.trimmed && i.spriteSourceSize && (s = new ot(Math.floor(i.spriteSourceSize.x) / this.resolution, Math.floor(i.spriteSourceSize.y) / this.resolution, Math.floor(o.w) / this.resolution, Math.floor(o.h) / this.resolution)), this.textures[n] = new Ht(this.baseTexture, a, h, s, i.rotated ? 2 : 0, i.anchor), Ht.addToCache(this.textures[n], n) + } + e++ + } + }, xn.prototype._processAnimations = function() { + var t = this.data.animations || {}; + for (var e in t) { + this.animations[e] = []; + for (var r = 0; r < t[e].length; r++) { + var n = t[e][r]; + this.animations[e].push(this.textures[n]) + } + } + }, xn.prototype._parseComplete = function() { + var t = this._callback; + this._callback = null, this._batchIndex = 0, t.call(this, this.textures) + }, xn.prototype._nextBatch = function() { + var t = this; + this._processFrames(this._batchIndex * xn.BATCH_SIZE), this._batchIndex++, setTimeout(function() { + t._batchIndex * xn.BATCH_SIZE < t._frameKeys.length ? t._nextBatch() : (t._processAnimations(), t._parseComplete()) + }, 0) + }, xn.prototype.destroy = function(t) { + for (var e in void 0 === t && (t = !1), this.textures) this.textures[e].destroy(); + this._frames = null, this._frameKeys = null, this.data = null, this.textures = null, t && this.baseTexture.destroy(), this.baseTexture = null + }, Object.defineProperties(xn, wn); + var Tn = function() {}; + Tn.use = function(r, n) { + var t = r.name + "_image"; + if (r.data && r.type === gn.TYPE.JSON && r.data.frames && !this.resources[t]) { + var e = { + crossOrigin: r.crossOrigin, + metadata: r.metadata.imageMetadata, + parentResource: r + }, + i = Tn.getResourcePath(r, this.baseUrl); + this.add(t, i, e, function(t) { + if (t.error) n(t.error); + else { + var e = new xn(t.texture.baseTexture, r.data, r.url); + e.parse(function() { + r.spritesheet = e, r.textures = e.textures, n() + }) + } + }) + } else n() + }, Tn.getResourcePath = function(t, e) { + return t.isDataUrl ? t.data.meta.image : m.a.resolve(t.url.replace(e, ""), t.data.meta.image) + }; + var Sn = new q, + Mn = function(n) { + function TilingSprite(t, e, r) { + void 0 === e && (e = 100), void 0 === r && (r = 100), n.call(this, t), this.tileTransform = new nt, this._width = e, this._height = r, this._canvasPattern = null, this.uvMatrix = t.uvMatrix || new ke(t), this.pluginName = "tilingSprite", this.uvRespectAnchor = !1 + } + n && (TilingSprite.__proto__ = n), (TilingSprite.prototype = Object.create(n && n.prototype)).constructor = TilingSprite; + var t = { + clampMargin: { + configurable: !0 + }, + tileScale: { + configurable: !0 + }, + tilePosition: { + configurable: !0 + }, + width: { + configurable: !0 + }, + height: { + configurable: !0 + } + }; + return t.clampMargin.get = function() { + return this.uvMatrix.clampMargin + }, t.clampMargin.set = function(t) { + this.uvMatrix.clampMargin = t, this.uvMatrix.update(!0) + }, t.tileScale.get = function() { + return this.tileTransform.scale + }, t.tileScale.set = function(t) { + this.tileTransform.scale.copyFrom(t) + }, t.tilePosition.get = function() { + return this.tileTransform.position + }, t.tilePosition.set = function(t) { + this.tileTransform.position.copyFrom(t) + }, TilingSprite.prototype._onTextureUpdate = function() { + this.uvMatrix && (this.uvMatrix.texture = this._texture), this.cachedTint = 16777215 + }, TilingSprite.prototype._render = function(t) { + var e = this._texture; + e && e.valid && (this.tileTransform.updateLocalTransform(), this.uvMatrix.update(), t.batch.setObjectRenderer(t.plugins[this.pluginName]), t.plugins[this.pluginName].render(this)) + }, TilingSprite.prototype._calculateBounds = function() { + var t = this._width * -this._anchor._x, + e = this._height * -this._anchor._y, + r = this._width * (1 - this._anchor._x), + n = this._height * (1 - this._anchor._y); + this._bounds.addFrame(this.transform, t, e, r, n) + }, TilingSprite.prototype.getLocalBounds = function(t) { + return 0 === this.children.length ? (this._bounds.minX = this._width * -this._anchor._x, this._bounds.minY = this._height * -this._anchor._y, this._bounds.maxX = this._width * (1 - this._anchor._x), this._bounds.maxY = this._height * (1 - this._anchor._y), t || (this._localBoundsRect || (this._localBoundsRect = new ot), t = this._localBoundsRect), this._bounds.getRectangle(t)) : n.prototype.getLocalBounds.call(this, t) + }, TilingSprite.prototype.containsPoint = function(t) { + this.worldTransform.applyInverse(t, Sn); + var e = this._width, + r = this._height, + n = -e * this.anchor._x; + if (Sn.x >= n && Sn.x < n + e) { + var i = -r * this.anchor._y; + if (Sn.y >= i && Sn.y < i + r) return !0 + } + return !1 + }, TilingSprite.prototype.destroy = function(t) { + n.prototype.destroy.call(this, t), this.tileTransform = null, this.uvMatrix = null + }, TilingSprite.from = function(t, e, r) { + return new TilingSprite(Ht.from(t), e, r) + }, TilingSprite.fromFrame = function(t, e, r) { + var n = F[t]; + if (!n) throw new Error('The frameId "' + t + '" does not exist in the texture cache ' + this); + return new TilingSprite(n, e, r) + }, TilingSprite.fromImage = function(t, e, r, n) { + return n && "object" != typeof n && (n = { + scaleMode: arguments[4], + resourceOptions: { + crossorigin: arguments[3] + } + }), new TilingSprite(Ht.from(t, n), e, r) + }, t.width.get = function() { + return this._width + }, t.width.set = function(t) { + this._width = t + }, t.height.get = function() { + return this._height + }, t.height.set = function(t) { + this._height = t + }, Object.defineProperties(TilingSprite.prototype, t), TilingSprite + }(Xr), + En = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform mat3 uTransform;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;\n}\n", + An = new Y, + Pn = function(r) { + function TilingSpriteRenderer(t) { + r.call(this, t); + var e = { + globals: this.renderer.globalUniforms + }; + this.shader = Ie.from(En, "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform vec4 uColor;\nuniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;\n\nvoid main(void)\n{\n vec2 coord = mod(vTextureCoord - uClampOffset, vec2(1.0, 1.0)) + uClampOffset;\n coord = (uMapCoord * vec3(coord, 1.0)).xy;\n coord = clamp(coord, uClampFrame.xy, uClampFrame.zw);\n\n vec4 sample = texture2D(uSampler, coord);\n gl_FragColor = sample * uColor;\n}\n", e), this.simpleShader = Ie.from(En, "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform vec4 uColor;\n\nvoid main(void)\n{\n vec4 sample = texture2D(uSampler, vTextureCoord);\n gl_FragColor = sample * uColor;\n}\n", e), this.quad = new ne + } + return r && (TilingSpriteRenderer.__proto__ = r), ((TilingSpriteRenderer.prototype = Object.create(r && r.prototype)).constructor = TilingSpriteRenderer).prototype.render = function(t) { + var e = this.renderer, + r = this.quad, + n = r.vertices; + n[0] = n[6] = t._width * -t.anchor.x, n[1] = n[3] = t._height * -t.anchor.y, n[2] = n[4] = t._width * (1 - t.anchor.x), n[5] = n[7] = t._height * (1 - t.anchor.y), t.uvRespectAnchor && ((n = r.uvs)[0] = n[6] = -t.anchor.x, n[1] = n[3] = -t.anchor.y, n[2] = n[4] = 1 - t.anchor.x, n[5] = n[7] = 1 - t.anchor.y), r.invalidate(); + var i = t._texture, + o = i.baseTexture, + a = t.tileTransform.localTransform, + s = t.uvMatrix, + u = o.isPowerOfTwo && i.frame.width === o.width && i.frame.height === o.height; + u && (o._glTextures[e.CONTEXT_UID] ? u = o.wrapMode !== S.CLAMP : o.wrapMode === S.CLAMP && (o.wrapMode = S.REPEAT)); + var h = u ? this.simpleShader : this.shader, + c = i.width, + l = i.height, + f = t._width, + d = t._height; + An.set(a.a * c / f, a.b * c / d, a.c * l / f, a.d * l / d, a.tx / f, a.ty / d), An.invert(), u ? An.prepend(s.mapCoord) : (h.uniforms.uMapCoord = s.mapCoord.toArray(!0), h.uniforms.uClampFrame = s.uClampFrame, h.uniforms.uClampOffset = s.uClampOffset), h.uniforms.uTransform = An.toArray(!0), h.uniforms.uColor = premultiplyTintToRgba(t.tint, t.worldAlpha, h.uniforms.uColor, o.premultiplyAlpha), h.uniforms.translationMatrix = t.transform.worldTransform.toArray(!0), h.uniforms.uSampler = i, e.shader.bind(h), e.geometry.bind(r), e.state.setBlendMode(correctBlendMode(t.blendMode, o.premultiplyAlpha)), e.geometry.draw(this.renderer.gl.TRIANGLES, 6, 0) + }, TilingSpriteRenderer + }(he), + In = function(n) { + function BitmapText(t, e) { + var r = this; + void 0 === e && (e = {}), n.call(this), this._textWidth = 0, this._textHeight = 0, this._glyphs = [], this._font = { + tint: void 0 !== e.tint ? e.tint : 16777215, + align: e.align || "left", + name: null, + size: 0 + }, this.font = e.font, this._text = t, this._maxWidth = 0, this._maxLineHeight = 0, this._letterSpacing = 0, this._anchor = new j(function() { + r.dirty = !0 + }, this, 0, 0), this.dirty = !1, this.roundPixels = M.ROUND_PIXELS, this.updateText() + } + n && (BitmapText.__proto__ = n); + var t = { + tint: { + configurable: !0 + }, + align: { + configurable: !0 + }, + anchor: { + configurable: !0 + }, + font: { + configurable: !0 + }, + text: { + configurable: !0 + }, + maxWidth: { + configurable: !0 + }, + maxLineHeight: { + configurable: !0 + }, + textWidth: { + configurable: !0 + }, + letterSpacing: { + configurable: !0 + }, + textHeight: { + configurable: !0 + } + }; + return ((BitmapText.prototype = Object.create(n && n.prototype)).constructor = BitmapText).prototype.updateText = function() { + for (var t = BitmapText.fonts[this._font.name], e = this._font.size / t.size, r = new q, n = [], i = [], o = this._text.replace(/(?:\r\n|\r)/g, "\n") || " ", a = o.length, s = this._maxWidth * t.size / this._font.size, u = null, h = 0, c = 0, l = 0, f = -1, d = 0, p = 0, m = 0, v = 0; v < a; v++) { + var g = o.charCodeAt(v), + y = o.charAt(v); + if (/(?:\s)/.test(y) && (f = v, d = h), "\r" !== y && "\n" !== y) { + var _ = t.chars[g]; + _ && (u && _.kerning[u] && (r.x += _.kerning[u]), n.push({ + texture: _.texture, + line: l, + charCode: g, + position: new q(r.x + _.xOffset + this._letterSpacing / 2, r.y + _.yOffset) + }), r.x += _.xAdvance + this._letterSpacing, h = r.x, m = Math.max(m, _.yOffset + _.texture.height), u = g, -1 !== f && 0 < s && r.x > s && (removeItems(n, 1 + f - ++p, 1 + v - f), v = f, f = -1, i.push(d), c = Math.max(c, d), l++, r.x = 0, r.y += t.lineHeight, u = null)) + } else i.push(h), c = Math.max(c, h), ++l, ++p, r.x = 0, r.y += t.lineHeight, u = null + } + var b = o.charAt(o.length - 1); + "\r" !== b && "\n" !== b && (/(?:\s)/.test(b) && (h = d), i.push(h), c = Math.max(c, h)); + for (var x = [], w = 0; w <= l; w++) { + var T = 0; + "right" === this._font.align ? T = c - i[w] : "center" === this._font.align && (T = (c - i[w]) / 2), x.push(T) + } + for (var S = n.length, M = this.tint, E = 0; E < S; E++) { + var A = this._glyphs[E]; + A ? A.texture = n[E].texture : ((A = new Xr(n[E].texture)).roundPixels = this.roundPixels, this._glyphs.push(A)), A.position.x = (n[E].position.x + x[n[E].line]) * e, A.position.y = n[E].position.y * e, A.scale.x = A.scale.y = e, A.tint = M, A.parent || this.addChild(A) + } + for (var P = S; P < this._glyphs.length; ++P) this.removeChild(this._glyphs[P]); + if (this._textWidth = c * e, this._textHeight = (r.y + t.lineHeight) * e, 0 !== this.anchor.x || 0 !== this.anchor.y) + for (var I = 0; I < S; I++) this._glyphs[I].x -= this._textWidth * this.anchor.x, this._glyphs[I].y -= this._textHeight * this.anchor.y; + this._maxLineHeight = m * e + }, BitmapText.prototype.updateTransform = function() { + this.validate(), this.containerUpdateTransform() + }, BitmapText.prototype.getLocalBounds = function() { + return this.validate(), n.prototype.getLocalBounds.call(this) + }, BitmapText.prototype.validate = function() { + this.dirty && (this.updateText(), this.dirty = !1) + }, t.tint.get = function() { + return this._font.tint + }, t.tint.set = function(t) { + this._font.tint = "number" == typeof t && 0 <= t ? t : 16777215, this.dirty = !0 + }, t.align.get = function() { + return this._font.align + }, t.align.set = function(t) { + this._font.align = t || "left", this.dirty = !0 + }, t.anchor.get = function() { + return this._anchor + }, t.anchor.set = function(t) { + "number" == typeof t ? this._anchor.set(t) : this._anchor.copyFrom(t) + }, t.font.get = function() { + return this._font + }, t.font.set = function(t) { + t && (this._font.size = "string" == typeof t ? (t = t.split(" "), this._font.name = 1 === t.length ? t[0] : t.slice(1).join(" "), 2 <= t.length ? parseInt(t[0], 10) : BitmapText.fonts[this._font.name].size) : (this._font.name = t.name, "number" == typeof t.size ? t.size : parseInt(t.size, 10)), this.dirty = !0) + }, t.text.get = function() { + return this._text + }, t.text.set = function(t) { + t = String(null == t ? "" : t), this._text !== t && (this._text = t, this.dirty = !0) + }, t.maxWidth.get = function() { + return this._maxWidth + }, t.maxWidth.set = function(t) { + this._maxWidth !== t && (this._maxWidth = t, this.dirty = !0) + }, t.maxLineHeight.get = function() { + return this.validate(), this._maxLineHeight + }, t.textWidth.get = function() { + return this.validate(), this._textWidth + }, t.letterSpacing.get = function() { + return this._letterSpacing + }, t.letterSpacing.set = function(t) { + this._letterSpacing !== t && (this._letterSpacing = t, this.dirty = !0) + }, t.textHeight.get = function() { + return this.validate(), this._textHeight + }, BitmapText.registerFont = function(t, e) { + var r = {}, + n = t.getElementsByTagName("info")[0], + i = t.getElementsByTagName("common")[0], + o = t.getElementsByTagName("page"), + a = getResolutionOfUrl(o[0].getAttribute("file"), M.RESOLUTION), + s = {}; + r.font = n.getAttribute("face"), r.size = parseInt(n.getAttribute("size"), 10), r.lineHeight = parseInt(i.getAttribute("lineHeight"), 10) / a, r.chars = {}, e instanceof Ht && (e = [e]); + for (var u = 0; u < o.length; u++) { + var h = o[u].getAttribute("id"), + c = o[u].getAttribute("file"); + s[h] = e instanceof Array ? e[u] : e[c] + } + for (var l = t.getElementsByTagName("char"), f = 0; f < l.length; f++) { + var d = l[f], + p = parseInt(d.getAttribute("id"), 10), + m = d.getAttribute("page") || 0, + v = new ot(parseInt(d.getAttribute("x"), 10) / a + s[m].frame.x / a, parseInt(d.getAttribute("y"), 10) / a + s[m].frame.y / a, parseInt(d.getAttribute("width"), 10) / a, parseInt(d.getAttribute("height"), 10) / a); + r.chars[p] = { + xOffset: parseInt(d.getAttribute("xoffset"), 10) / a, + yOffset: parseInt(d.getAttribute("yoffset"), 10) / a, + xAdvance: parseInt(d.getAttribute("xadvance"), 10) / a, + kerning: {}, + texture: new Ht(s[m].baseTexture, v), + page: m + } + } + for (var g = t.getElementsByTagName("kerning"), y = 0; y < g.length; y++) { + var _ = g[y], + b = parseInt(_.getAttribute("first"), 10) / a, + x = parseInt(_.getAttribute("second"), 10) / a, + w = parseInt(_.getAttribute("amount"), 10) / a; + r.chars[x] && (r.chars[x].kerning[b] = w) + } + return BitmapText.fonts[r.font] = r + }, Object.defineProperties(BitmapText.prototype, t), BitmapText + }(pt); + In.fonts = {}; + var On = function() {}; + On.parse = function(t, e) { + t.bitmapFont = In.registerFont(t.data, e) + }, On.add = function() { + gn.setExtensionXhrType("fnt", gn.XHR_RESPONSE_TYPE.DOCUMENT) + }, On.dirname = function(t) { + var e = t.replace(/\/$/, "").replace(/\/[^\/]*$/, ""); + return e === t ? "." : "" === e ? "/" : e + }, On.use = function(e, r) { + if (e.data && e.type === gn.TYPE.XML) + if (0 !== e.data.getElementsByTagName("page").length && 0 !== e.data.getElementsByTagName("info").length && null !== e.data.getElementsByTagName("info")[0].getAttribute("face")) { + var t = e.isDataUrl ? "" : On.dirname(e.url); + e.isDataUrl && ("." === t && (t = ""), this.baseUrl && t && "/" === this.baseUrl.charAt(this.baseUrl.length - 1) && (t += "/")), (t = t.replace(this.baseUrl, "")) && "/" !== t.charAt(t.length - 1) && (t += "/"); + for (var n = e.data.getElementsByTagName("page"), i = {}, o = function(t) { + i[t.metadata.pageFile] = t.texture, Object.keys(i).length === n.length && (On.parse(e, i), r()) + }, a = 0; a < n.length; ++a) { + var s = n[a].getAttribute("file"), + u = t + s, + h = !1; + for (var c in this.resources) { + var l = this.resources[c]; + if (l.url === u) { + l.metadata.pageFile = s, l.texture ? o(l) : l.onAfterMiddleware.add(o), h = !0; + break + } + } + if (!h) { + var f = { + crossOrigin: e.crossOrigin, + loadType: gn.LOAD_TYPE.IMAGE, + metadata: Object.assign({ + pageFile: s + }, e.metadata.imageMetadata), + parentResource: e + }; + this.add(u, f, o) + } + } + } else r(); + else r() + }; + var Cn = function(e) { + function AlphaFilter(t) { + void 0 === t && (t = 1), e.call(this, tr, "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform float uAlpha;\n\nvoid main(void)\n{\n gl_FragColor = texture2D(uSampler, vTextureCoord) * uAlpha;\n}\n", { + uAlpha: 1 + }), this.alpha = t + } + e && (AlphaFilter.__proto__ = e), (AlphaFilter.prototype = Object.create(e && e.prototype)).constructor = AlphaFilter; + var t = { + alpha: { + configurable: !0 + } + }; + return t.alpha.get = function() { + return this.uniforms.uAlpha + }, t.alpha.set = function(t) { + this.uniforms.uAlpha = t + }, Object.defineProperties(AlphaFilter.prototype, t), AlphaFilter + }(De), + Rn = "\n attribute vec2 aVertexPosition;\n\n uniform mat3 projectionMatrix;\n\n uniform float strength;\n\n varying vec2 vBlurTexCoords[%size%];\n\n uniform vec4 inputSize;\n uniform vec4 outputFrame;\n \n vec4 filterVertexPosition( void )\n {\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n \n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n }\n \n vec2 filterTextureCoord( void )\n {\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n }\n\n void main(void)\n {\n gl_Position = filterVertexPosition();\n\n vec2 textureCoord = filterTextureCoord();\n %blur%\n }"; + var Dn = { + 5: [.153388, .221461, .250301], + 7: [.071303, .131514, .189879, .214607], + 9: [.028532, .067234, .124009, .179044, .20236], + 11: [.0093, .028002, .065984, .121703, .175713, .198596], + 13: [.002406, .009255, .027867, .065666, .121117, .174868, .197641], + 15: [489e-6, .002403, .009246, .02784, .065602, .120999, .174697, .197448] + }, + Fn = ["varying vec2 vBlurTexCoords[%size%];", "uniform sampler2D uSampler;", "void main(void)", "{", " gl_FragColor = vec4(0.0);", " %blur%", "}"].join("\n"); + var kn = function(s) { + function BlurFilterPass(t, e, r, n, i) { + var o = function(t, e) { + var r, n = Math.ceil(t / 2), + i = Rn, + o = ""; + r = e ? "vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * strength, 0.0);" : "vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * strength);"; + for (var a = 0; a < t; a++) { + var s = r.replace("%index%", a); + o += s = s.replace("%sampleIndex%", a - (n - 1) + ".0"), o += "\n" + } + return i = (i = i.replace("%blur%", o)).replace("%size%", t) + }(i = i || 5, t), + a = function(t) { + for (var e, r = Dn[t], n = r.length, i = Fn, o = "", a = 0; a < t; a++) { + var s = "gl_FragColor += texture2D(uSampler, vBlurTexCoords[%index%]) * %value%;".replace("%index%", a); + n <= (e = a) && (e = t - a - 1), o += s = s.replace("%value%", r[e]), o += "\n" + } + return i = (i = i.replace("%blur%", o)).replace("%size%", t) + }(i); + s.call(this, o, a), this.horizontal = t, this.resolution = n || M.RESOLUTION, this._quality = 0, this.quality = r || 4, this.blur = e || 8 + } + s && (BlurFilterPass.__proto__ = s); + var t = { + blur: { + configurable: !0 + }, + quality: { + configurable: !0 + } + }; + return ((BlurFilterPass.prototype = Object.create(s && s.prototype)).constructor = BlurFilterPass).prototype.apply = function(t, e, r, n) { + if (r ? this.horizontal ? this.uniforms.strength = 1 / r.width * (r.width / e.width) : this.uniforms.strength = 1 / r.height * (r.height / e.height) : this.horizontal ? this.uniforms.strength = 1 / t.renderer.width * (t.renderer.width / e.width) : this.uniforms.strength = 1 / t.renderer.height * (t.renderer.height / e.height), this.uniforms.strength *= this.strength, this.uniforms.strength /= this.passes, 1 === this.passes) t.applyFilter(this, e, r, n); + else { + var i = t.getFilterTexture(), + o = t.renderer, + a = e, + s = i; + this.state.blend = !1, t.applyFilter(this, a, s, !1); + for (var u = 1; u < this.passes - 1; u++) { + o.renderTexture.bind(a, a.filterFrame); + var h = this.uniforms.uSampler = s; + s = a, a = h, o.shader.bind(this), o.geometry.draw(5) + } + this.state.blend = !0, t.applyFilter(this, s, r, n), t.returnFilterTexture(i) + } + }, t.blur.get = function() { + return this.strength + }, t.blur.set = function(t) { + this.padding = 1 + 2 * Math.abs(t), this.strength = t + }, t.quality.get = function() { + return this._quality + }, t.quality.set = function(t) { + this._quality = t, this.passes = t + }, Object.defineProperties(BlurFilterPass.prototype, t), BlurFilterPass + }(De), + Ln = function(i) { + function BlurFilter(t, e, r, n) { + i.call(this), this.blurXFilter = new kn(!0, t, e, r, n), this.blurYFilter = new kn(!1, t, e, r, n), this.resolution = r || M.RESOLUTION, this.quality = e || 4, this.blur = t || 8, this.repeatEdgePixels = !1 + } + i && (BlurFilter.__proto__ = i); + var t = { + blur: { + configurable: !0 + }, + quality: { + configurable: !0 + }, + blurX: { + configurable: !0 + }, + blurY: { + configurable: !0 + }, + blendMode: { + configurable: !0 + }, + repeatEdgePixels: { + configurable: !0 + } + }; + return ((BlurFilter.prototype = Object.create(i && i.prototype)).constructor = BlurFilter).prototype.apply = function(t, e, r, n) { + var i = Math.abs(this.blurXFilter.strength), + o = Math.abs(this.blurYFilter.strength); + if (i && o) { + var a = t.getFilterTexture(); + this.blurXFilter.apply(t, e, a, !0), this.blurYFilter.apply(t, a, r, n), t.returnFilterTexture(a) + } else o ? this.blurYFilter.apply(t, e, r, n) : this.blurXFilter.apply(t, e, r, n) + }, BlurFilter.prototype.updatePadding = function() { + this._repeatEdgePixels ? this.padding = 0 : this.padding = 2 * Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) + }, t.blur.get = function() { + return this.blurXFilter.blur + }, t.blur.set = function(t) { + this.blurXFilter.blur = this.blurYFilter.blur = t, this.updatePadding() + }, t.quality.get = function() { + return this.blurXFilter.quality + }, t.quality.set = function(t) { + this.blurXFilter.quality = this.blurYFilter.quality = t + }, t.blurX.get = function() { + return this.blurXFilter.blur + }, t.blurX.set = function(t) { + this.blurXFilter.blur = t, this.updatePadding() + }, t.blurY.get = function() { + return this.blurYFilter.blur + }, t.blurY.set = function(t) { + this.blurYFilter.blur = t, this.updatePadding() + }, t.blendMode.get = function() { + return this.blurYFilter.blendMode + }, t.blendMode.set = function(t) { + this.blurYFilter.blendMode = t + }, t.repeatEdgePixels.get = function() { + return this._repeatEdgePixels + }, t.repeatEdgePixels.set = function(t) { + this._repeatEdgePixels = t, this.updatePadding() + }, Object.defineProperties(BlurFilter.prototype, t), BlurFilter + }(De), + Nn = function(e) { + function ColorMatrixFilter() { + var t = { + m: new Float32Array([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), + uAlpha: 1 + }; + e.call(this, er, "varying vec2 vTextureCoord;\nuniform sampler2D uSampler;\nuniform float m[20];\nuniform float uAlpha;\n\nvoid main(void)\n{\n vec4 c = texture2D(uSampler, vTextureCoord);\n\n if (uAlpha == 0.0) {\n gl_FragColor = c;\n return;\n }\n\n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (c.a > 0.0) {\n c.rgb /= c.a;\n }\n\n vec4 result;\n\n result.r = (m[0] * c.r);\n result.r += (m[1] * c.g);\n result.r += (m[2] * c.b);\n result.r += (m[3] * c.a);\n result.r += m[4];\n\n result.g = (m[5] * c.r);\n result.g += (m[6] * c.g);\n result.g += (m[7] * c.b);\n result.g += (m[8] * c.a);\n result.g += m[9];\n\n result.b = (m[10] * c.r);\n result.b += (m[11] * c.g);\n result.b += (m[12] * c.b);\n result.b += (m[13] * c.a);\n result.b += m[14];\n\n result.a = (m[15] * c.r);\n result.a += (m[16] * c.g);\n result.a += (m[17] * c.b);\n result.a += (m[18] * c.a);\n result.a += m[19];\n\n vec3 rgb = mix(c.rgb, result.rgb, uAlpha);\n\n // Premultiply alpha again.\n rgb *= result.a;\n\n gl_FragColor = vec4(rgb, result.a);\n}\n", t), this.alpha = 1 + } + e && (ColorMatrixFilter.__proto__ = e); + var t = { + matrix: { + configurable: !0 + }, + alpha: { + configurable: !0 + } + }; + return ((ColorMatrixFilter.prototype = Object.create(e && e.prototype)).constructor = ColorMatrixFilter).prototype._loadMatrix = function(t, e) { + void 0 === e && (e = !1); + var r = t; + e && (this._multiply(r, this.uniforms.m, t), r = this._colorMatrix(r)), this.uniforms.m = r + }, ColorMatrixFilter.prototype._multiply = function(t, e, r) { + return t[0] = e[0] * r[0] + e[1] * r[5] + e[2] * r[10] + e[3] * r[15], t[1] = e[0] * r[1] + e[1] * r[6] + e[2] * r[11] + e[3] * r[16], t[2] = e[0] * r[2] + e[1] * r[7] + e[2] * r[12] + e[3] * r[17], t[3] = e[0] * r[3] + e[1] * r[8] + e[2] * r[13] + e[3] * r[18], t[4] = e[0] * r[4] + e[1] * r[9] + e[2] * r[14] + e[3] * r[19] + e[4], t[5] = e[5] * r[0] + e[6] * r[5] + e[7] * r[10] + e[8] * r[15], t[6] = e[5] * r[1] + e[6] * r[6] + e[7] * r[11] + e[8] * r[16], t[7] = e[5] * r[2] + e[6] * r[7] + e[7] * r[12] + e[8] * r[17], t[8] = e[5] * r[3] + e[6] * r[8] + e[7] * r[13] + e[8] * r[18], t[9] = e[5] * r[4] + e[6] * r[9] + e[7] * r[14] + e[8] * r[19] + e[9], t[10] = e[10] * r[0] + e[11] * r[5] + e[12] * r[10] + e[13] * r[15], t[11] = e[10] * r[1] + e[11] * r[6] + e[12] * r[11] + e[13] * r[16], t[12] = e[10] * r[2] + e[11] * r[7] + e[12] * r[12] + e[13] * r[17], t[13] = e[10] * r[3] + e[11] * r[8] + e[12] * r[13] + e[13] * r[18], t[14] = e[10] * r[4] + e[11] * r[9] + e[12] * r[14] + e[13] * r[19] + e[14], t[15] = e[15] * r[0] + e[16] * r[5] + e[17] * r[10] + e[18] * r[15], t[16] = e[15] * r[1] + e[16] * r[6] + e[17] * r[11] + e[18] * r[16], t[17] = e[15] * r[2] + e[16] * r[7] + e[17] * r[12] + e[18] * r[17], t[18] = e[15] * r[3] + e[16] * r[8] + e[17] * r[13] + e[18] * r[18], t[19] = e[15] * r[4] + e[16] * r[9] + e[17] * r[14] + e[18] * r[19] + e[19], t + }, ColorMatrixFilter.prototype._colorMatrix = function(t) { + var e = new Float32Array(t); + return e[4] /= 255, e[9] /= 255, e[14] /= 255, e[19] /= 255, e + }, ColorMatrixFilter.prototype.brightness = function(t, e) { + var r = [t, 0, 0, 0, 0, 0, t, 0, 0, 0, 0, 0, t, 0, 0, 0, 0, 0, 1, 0]; + this._loadMatrix(r, e) + }, ColorMatrixFilter.prototype.greyscale = function(t, e) { + var r = [t, t, t, 0, 0, t, t, t, 0, 0, t, t, t, 0, 0, 0, 0, 0, 1, 0]; + this._loadMatrix(r, e) + }, ColorMatrixFilter.prototype.blackAndWhite = function(t) { + this._loadMatrix([.3, .6, .1, 0, 0, .3, .6, .1, 0, 0, .3, .6, .1, 0, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.hue = function(t, e) { + t = (t || 0) / 180 * Math.PI; + var r = Math.cos(t), + n = Math.sin(t), + i = (0, Math.sqrt)(1 / 3), + o = [r + 1 / 3 * (1 - r), 1 / 3 * (1 - r) - i * n, 1 / 3 * (1 - r) + i * n, 0, 0, 1 / 3 * (1 - r) + i * n, r + 1 / 3 * (1 - r), 1 / 3 * (1 - r) - i * n, 0, 0, 1 / 3 * (1 - r) - i * n, 1 / 3 * (1 - r) + i * n, r + 1 / 3 * (1 - r), 0, 0, 0, 0, 0, 1, 0]; + this._loadMatrix(o, e) + }, ColorMatrixFilter.prototype.contrast = function(t, e) { + var r = (t || 0) + 1, + n = -.5 * (r - 1), + i = [r, 0, 0, 0, n, 0, r, 0, 0, n, 0, 0, r, 0, n, 0, 0, 0, 1, 0]; + this._loadMatrix(i, e) + }, ColorMatrixFilter.prototype.saturate = function(t, e) { + void 0 === t && (t = 0); + var r = 2 * t / 3 + 1, + n = -.5 * (r - 1), + i = [r, n, n, 0, 0, n, r, n, 0, 0, n, n, r, 0, 0, 0, 0, 0, 1, 0]; + this._loadMatrix(i, e) + }, ColorMatrixFilter.prototype.desaturate = function() { + this.saturate(-1) + }, ColorMatrixFilter.prototype.negative = function(t) { + this._loadMatrix([-1, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, -1, 1, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.sepia = function(t) { + this._loadMatrix([.393, .7689999, .18899999, 0, 0, .349, .6859999, .16799999, 0, 0, .272, .5339999, .13099999, 0, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.technicolor = function(t) { + this._loadMatrix([1.9125277891456083, -.8545344976951645, -.09155508482755585, 0, 11.793603434377337, -.3087833385928097, 1.7658908555458428, -.10601743074722245, 0, -70.35205161461398, -.231103377548616, -.7501899197440212, 1.847597816108189, 0, 30.950940869491138, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.polaroid = function(t) { + this._loadMatrix([1.438, -.062, -.062, 0, 0, -.122, 1.378, -.122, 0, 0, -.016, -.016, 1.483, 0, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.toBGR = function(t) { + this._loadMatrix([0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.kodachrome = function(t) { + this._loadMatrix([1.1285582396593525, -.3967382283601348, -.03992559172921793, 0, 63.72958762196502, -.16404339962244616, 1.0835251566291304, -.05498805115633132, 0, 24.732407896706203, -.16786010706155763, -.5603416277695248, 1.6014850761964943, 0, 35.62982807460946, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.browni = function(t) { + this._loadMatrix([.5997023498159715, .34553243048391263, -.2708298674538042, 0, 47.43192855600873, -.037703249837783157, .8609577587992641, .15059552388459913, 0, -36.96841498319127, .24113635128153335, -.07441037908422492, .44972182064877153, 0, -7.562075277591283, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.vintage = function(t) { + this._loadMatrix([.6279345635605994, .3202183420819367, -.03965408211312453, 0, 9.651285835294123, .02578397704808868, .6441188644374771, .03259127616149294, 0, 7.462829176470591, .0466055556782719, -.0851232987247891, .5241648018700465, 0, 5.159190588235296, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.colorTone = function(t, e, r, n, i) { + var o = ((r = r || 16770432) >> 16 & 255) / 255, + a = (r >> 8 & 255) / 255, + s = (255 & r) / 255, + u = ((n = n || 3375104) >> 16 & 255) / 255, + h = (n >> 8 & 255) / 255, + c = (255 & n) / 255, + l = [.3, .59, .11, 0, 0, o, a, s, t = t || .2, 0, u, h, c, e = e || .15, 0, o - u, a - h, s - c, 0, 0]; + this._loadMatrix(l, i) + }, ColorMatrixFilter.prototype.night = function(t, e) { + var r = [-2 * (t = t || .1), -t, 0, 0, 0, -t, 0, t, 0, 0, 0, t, 2 * t, 0, 0, 0, 0, 0, 1, 0]; + this._loadMatrix(r, e) + }, ColorMatrixFilter.prototype.predator = function(t, e) { + var r = [11.224130630493164 * t, -4.794486999511719 * t, -2.8746118545532227 * t, 0 * t, .40342438220977783 * t, -3.6330697536468506 * t, 9.193157196044922 * t, -2.951810836791992 * t, 0 * t, -1.316135048866272 * t, -3.2184197902679443 * t, -4.2375030517578125 * t, 7.476448059082031 * t, 0 * t, .8044459223747253 * t, 0, 0, 0, 1, 0]; + this._loadMatrix(r, e) + }, ColorMatrixFilter.prototype.lsd = function(t) { + this._loadMatrix([2, -.4, .5, 0, 0, -.5, 2, -.4, 0, 0, -.4, -.5, 3, 0, 0, 0, 0, 0, 1, 0], t) + }, ColorMatrixFilter.prototype.reset = function() { + this._loadMatrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0], !1) + }, t.matrix.get = function() { + return this.uniforms.m + }, t.matrix.set = function(t) { + this.uniforms.m = t + }, t.alpha.get = function() { + return this.uniforms.uAlpha + }, t.alpha.set = function(t) { + this.uniforms.uAlpha = t + }, Object.defineProperties(ColorMatrixFilter.prototype, t), ColorMatrixFilter + }(De); + Nn.prototype.grayscale = Nn.prototype.greyscale; + var Bn = function(n) { + function DisplacementFilter(t, e) { + var r = new Y; + t.renderable = !1, n.call(this, "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\nuniform mat3 filterMatrix;\n\nvarying vec2 vTextureCoord;\nvarying vec2 vFilterCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n\tgl_Position = filterVertexPosition();\n\tvTextureCoord = filterTextureCoord();\n\tvFilterCoord = ( filterMatrix * vec3( vTextureCoord, 1.0) ).xy;\n}\n", "varying vec2 vFilterCoord;\nvarying vec2 vTextureCoord;\n\nuniform vec2 scale;\nuniform mat2 rotation;\nuniform sampler2D uSampler;\nuniform sampler2D mapSampler;\n\nuniform highp vec4 inputSize;\nuniform vec4 inputClamp;\n\nvoid main(void)\n{\n vec4 map = texture2D(mapSampler, vFilterCoord);\n\n map -= 0.5;\n map.xy = scale * inputSize.zw * (rotation * map.xy);\n\n gl_FragColor = texture2D(uSampler, clamp(vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y), inputClamp.xy, inputClamp.zw));\n}\n", { + mapSampler: t._texture, + filterMatrix: r, + scale: { + x: 1, + y: 1 + }, + rotation: new Float32Array([1, 0, 0, 1]) + }), this.maskSprite = t, this.maskMatrix = r, null == e && (e = 20), this.scale = new q(e, e) + } + n && (DisplacementFilter.__proto__ = n); + var t = { + map: { + configurable: !0 + } + }; + return ((DisplacementFilter.prototype = Object.create(n && n.prototype)).constructor = DisplacementFilter).prototype.apply = function(t, e, r) { + this.uniforms.filterMatrix = t.calculateSpriteMatrix(this.maskMatrix, this.maskSprite), this.uniforms.scale.x = this.scale.x, this.uniforms.scale.y = this.scale.y; + var n = this.maskSprite.transform.worldTransform, + i = Math.sqrt(n.a * n.a + n.b * n.b), + o = Math.sqrt(n.c * n.c + n.d * n.d); + 0 !== i && 0 !== o && (this.uniforms.rotation[0] = n.a / i, this.uniforms.rotation[1] = n.b / i, this.uniforms.rotation[2] = n.c / o, this.uniforms.rotation[3] = n.d / o), t.applyFilter(this, e, r) + }, t.map.get = function() { + return this.uniforms.mapSampler + }, t.map.set = function(t) { + this.uniforms.mapSampler = t + }, Object.defineProperties(DisplacementFilter.prototype, t), DisplacementFilter + }(De), + Un = function(t) { + function FXAAFilter() { + t.call(this, "\nattribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 v_rgbNW;\nvarying vec2 v_rgbNE;\nvarying vec2 v_rgbSW;\nvarying vec2 v_rgbSE;\nvarying vec2 v_rgbM;\n\nvarying vec2 vFragCoord;\n\nuniform vec4 inputPixel;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvoid texcoords(vec2 fragCoord, vec2 inverseVP,\n out vec2 v_rgbNW, out vec2 v_rgbNE,\n out vec2 v_rgbSW, out vec2 v_rgbSE,\n out vec2 v_rgbM) {\n v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP;\n v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP;\n v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP;\n v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP;\n v_rgbM = vec2(fragCoord * inverseVP);\n}\n\nvoid main(void) {\n\n gl_Position = filterVertexPosition();\n\n vFragCoord = aVertexPosition * outputFrame.zw;\n\n texcoords(vFragCoord, inputPixel.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);\n}\n", 'varying vec2 v_rgbNW;\nvarying vec2 v_rgbNE;\nvarying vec2 v_rgbSW;\nvarying vec2 v_rgbSE;\nvarying vec2 v_rgbM;\n\nvarying vec2 vFragCoord;\nuniform sampler2D uSampler;\nuniform highp vec4 inputPixel;\n\n\n/**\n Basic FXAA implementation based on the code on geeks3d.com with the\n modification that the texture2DLod stuff was removed since it\'s\n unsupported by WebGL.\n\n --\n\n From:\n https://github.com/mitsuhiko/webgl-meincraft\n\n Copyright (c) 2011 by Armin Ronacher.\n\n Some rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are\n met:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided\n with the distribution.\n\n * The names of the contributors may not be used to endorse or\n promote products derived from this software without specific\n prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef FXAA_REDUCE_MIN\n#define FXAA_REDUCE_MIN (1.0/ 128.0)\n#endif\n#ifndef FXAA_REDUCE_MUL\n#define FXAA_REDUCE_MUL (1.0 / 8.0)\n#endif\n#ifndef FXAA_SPAN_MAX\n#define FXAA_SPAN_MAX 8.0\n#endif\n\n//optimized version for mobile, where dependent\n//texture reads can be a bottleneck\nvec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 inverseVP,\n vec2 v_rgbNW, vec2 v_rgbNE,\n vec2 v_rgbSW, vec2 v_rgbSE,\n vec2 v_rgbM) {\n vec4 color;\n vec3 rgbNW = texture2D(tex, v_rgbNW).xyz;\n vec3 rgbNE = texture2D(tex, v_rgbNE).xyz;\n vec3 rgbSW = texture2D(tex, v_rgbSW).xyz;\n vec3 rgbSE = texture2D(tex, v_rgbSE).xyz;\n vec4 texColor = texture2D(tex, v_rgbM);\n vec3 rgbM = texColor.xyz;\n vec3 luma = vec3(0.299, 0.587, 0.114);\n float lumaNW = dot(rgbNW, luma);\n float lumaNE = dot(rgbNE, luma);\n float lumaSW = dot(rgbSW, luma);\n float lumaSE = dot(rgbSE, luma);\n float lumaM = dot(rgbM, luma);\n float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\n mediump vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *\n (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\n float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * inverseVP;\n\n vec3 rgbA = 0.5 * (\n texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz +\n texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n vec3 rgbB = rgbA * 0.5 + 0.25 * (\n texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz +\n texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\n float lumaB = dot(rgbB, luma);\n if ((lumaB < lumaMin) || (lumaB > lumaMax))\n color = vec4(rgbA, texColor.a);\n else\n color = vec4(rgbB, texColor.a);\n return color;\n}\n\nvoid main() {\n\n vec4 color;\n\n color = fxaa(uSampler, vFragCoord, inputPixel.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);\n\n gl_FragColor = color;\n}\n') + } + return t && (FXAAFilter.__proto__ = t), (FXAAFilter.prototype = Object.create(t && t.prototype)).constructor = FXAAFilter + }(De), + Gn = function(r) { + function NoiseFilter(t, e) { + void 0 === t && (t = .5), void 0 === e && (e = Math.random()), r.call(this, er, "precision highp float;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform float uNoise;\nuniform float uSeed;\nuniform sampler2D uSampler;\n\nfloat rand(vec2 co)\n{\n return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvoid main()\n{\n vec4 color = texture2D(uSampler, vTextureCoord);\n float randomValue = rand(gl_FragCoord.xy * uSeed);\n float diff = (randomValue - 0.5) * uNoise;\n\n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (color.a > 0.0) {\n color.rgb /= color.a;\n }\n\n color.r += diff;\n color.g += diff;\n color.b += diff;\n\n // Premultiply alpha again.\n color.rgb *= color.a;\n\n gl_FragColor = color;\n}\n", { + uNoise: 0, + uSeed: 0 + }), this.noise = t, this.seed = e + } + r && (NoiseFilter.__proto__ = r), (NoiseFilter.prototype = Object.create(r && r.prototype)).constructor = NoiseFilter; + var t = { + noise: { + configurable: !0 + }, + seed: { + configurable: !0 + } + }; + return t.noise.get = function() { + return this.uniforms.uNoise + }, t.noise.set = function(t) { + this.uniforms.uNoise = t + }, t.seed.get = function() { + return this.uniforms.uSeed + }, t.seed.set = function(t) { + this.uniforms.uSeed = t + }, Object.defineProperties(NoiseFilter.prototype, t), NoiseFilter + }(De), + jn = new Y; + dt.prototype._cacheAsBitmap = !1, dt.prototype._cacheData = !1; + var zn = function() { + this.textureCacheId = null, this.originalRender = null, this.originalRenderCanvas = null, this.originalCalculateBounds = null, this.originalGetLocalBounds = null, this.originalUpdateTransform = null, this.originalHitTest = null, this.originalDestroy = null, this.originalMask = null, this.originalFilterArea = null, this.sprite = null + }; + Object.defineProperties(dt.prototype, { + cacheAsBitmap: { + get: function() { + return this._cacheAsBitmap + }, + set: function(t) { + var e; + this._cacheAsBitmap !== t && ((this._cacheAsBitmap = t) ? (this._cacheData || (this._cacheData = new zn), (e = this._cacheData).originalRender = this.render, e.originalRenderCanvas = this.renderCanvas, e.originalUpdateTransform = this.updateTransform, e.originalCalculateBounds = this.calculateBounds, e.originalGetLocalBounds = this.getLocalBounds, e.originalDestroy = this.destroy, e.originalContainsPoint = this.containsPoint, e.originalMask = this._mask, e.originalFilterArea = this.filterArea, this.render = this._renderCached, this.renderCanvas = this._renderCachedCanvas, this.destroy = this._cacheAsBitmapDestroy) : ((e = this._cacheData).sprite && this._destroyCachedDisplayObject(), this.render = e.originalRender, this.renderCanvas = e.originalRenderCanvas, this.calculateBounds = e.originalCalculateBounds, this.getLocalBounds = e.originalGetLocalBounds, this.destroy = e.originalDestroy, this.updateTransform = e.originalUpdateTransform, this.containsPoint = e.originalContainsPoint, this._mask = e.originalMask, this.filterArea = e.originalFilterArea)) + } + } + }), dt.prototype._renderCached = function(t) { + !this.visible || this.worldAlpha <= 0 || !this.renderable || (this._initCachedDisplayObject(t), this._cacheData.sprite.transform._worldID = this.transform._worldID, this._cacheData.sprite.worldAlpha = this.worldAlpha, this._cacheData.sprite._render(t)) + }, dt.prototype._initCachedDisplayObject = function(t) { + if (!this._cacheData || !this._cacheData.sprite) { + var e = this.alpha; + this.alpha = 1, t.batch.flush(); + var r = this.getLocalBounds().clone(); + if (this.filters) { + var n = this.filters[0].padding; + r.pad(n) + } + r.ceil(M.RESOLUTION); + var i = t._activeRenderTarget, + o = Wt.create(r.width, r.height), + a = "cacheAsBitmap_" + uid(); + this._cacheData.textureCacheId = a, Rt.addToCache(o.baseTexture, a), Ht.addToCache(o, a); + var s = jn; + s.tx = -r.x, s.ty = -r.y, this.transform.worldTransform.identity(), this.render = this._cacheData.originalRender, t.render(this, o, !0, s, !0), t.renderTexture.bind(i), this.render = this._renderCached, this.updateTransform = this.displayObjectUpdateTransform, this.calculateBounds = this._calculateCachedBounds, this.getLocalBounds = this._getCachedLocalBounds, this._mask = null, this.filterArea = null; + var u = new Xr(o); + u.transform.worldTransform = this.transform.worldTransform, u.anchor.x = -r.x / r.width, u.anchor.y = -r.y / r.height, u.alpha = e, u._bounds = this._bounds, this._cacheData.sprite = u, this.transform._parentID = -1, this.parent ? this.updateTransform() : (this.parent = t._tempDisplayObjectParent, this.updateTransform(), this.parent = null), this.containsPoint = u.containsPoint.bind(u) + } + }, dt.prototype._renderCachedCanvas = function(t) { + !this.visible || this.worldAlpha <= 0 || !this.renderable || (this._initCachedDisplayObjectCanvas(t), this._cacheData.sprite.worldAlpha = this.worldAlpha, this._cacheData.sprite._renderCanvas(t)) + }, dt.prototype._initCachedDisplayObjectCanvas = function(t) { + if (!this._cacheData || !this._cacheData.sprite) { + var e = this.getLocalBounds(), + r = this.alpha; + this.alpha = 1; + var n = t.context; + e.ceil(M.RESOLUTION); + var i = Wt.create(e.width, e.height), + o = "cacheAsBitmap_" + uid(); + this._cacheData.textureCacheId = o, Rt.addToCache(i.baseTexture, o), Ht.addToCache(i, o); + var a = jn; + this.transform.localTransform.copyTo(a), a.invert(), a.tx -= e.x, a.ty -= e.y, this.renderCanvas = this._cacheData.originalRenderCanvas, t.render(this, i, !0, a, !1), t.context = n, this.renderCanvas = this._renderCachedCanvas, this.updateTransform = this.displayObjectUpdateTransform, this.calculateBounds = this._calculateCachedBounds, this.getLocalBounds = this._getCachedLocalBounds, this._mask = null, this.filterArea = null; + var s = new Xr(i); + s.transform.worldTransform = this.transform.worldTransform, s.anchor.x = -e.x / e.width, s.anchor.y = -e.y / e.height, s.alpha = r, s._bounds = this._bounds, this._cacheData.sprite = s, this.transform._parentID = -1, this.parent ? this.updateTransform() : (this.parent = t._tempDisplayObjectParent, this.updateTransform(), this.parent = null), this.containsPoint = s.containsPoint.bind(s) + } + }, dt.prototype._calculateCachedBounds = function() { + this._bounds.clear(), this._cacheData.sprite.transform._worldID = this.transform._worldID, this._cacheData.sprite._calculateBounds(), this._lastBoundsID = this._boundsID + }, dt.prototype._getCachedLocalBounds = function() { + return this._cacheData.sprite.getLocalBounds() + }, dt.prototype._destroyCachedDisplayObject = function() { + this._cacheData.sprite._texture.destroy(!0), this._cacheData.sprite = null, Rt.removeFromCache(this._cacheData.textureCacheId), Ht.removeFromCache(this._cacheData.textureCacheId), this._cacheData.textureCacheId = null + }, dt.prototype._cacheAsBitmapDestroy = function(t) { + this.cacheAsBitmap = !1, this.destroy(t) + }, dt.prototype.name = null, pt.prototype.getChildByName = function(t) { + for (var e = 0; e < this.children.length; e++) + if (this.children[e].name === t) return this.children[e]; + return null + }, dt.prototype.getGlobalPosition = function(t, e) { + return void 0 === t && (t = new q), void 0 === e && (e = !1), this.parent ? this.parent.toGlobal(this.position, t, e) : (t.x = this.position.x, t.y = this.position.y), t + }; + var Xn = function(t, e) { + this.uvBuffer = t, this.uvMatrix = e, this.data = null, this._bufferUpdateId = -1, this._textureUpdateId = -1, this._updateID = 0 + }; + Xn.prototype.update = function(t) { + if (t || this._bufferUpdateId !== this.uvBuffer._updateID || this._textureUpdateId !== this.uvMatrix._updateID) { + this._bufferUpdateId = this.uvBuffer._updateID, this._textureUpdateId = this.uvMatrix._updateID; + var e = this.uvBuffer.data; + this.data && this.data.length === e.length || (this.data = new Float32Array(e.length)), this.uvMatrix.multiplyUvs(e, this.data), this._updateID++ + } + }; + var Vn = new q, + qn = new ct, + Hn = function(i) { + function Mesh(t, e, r, n) { + void 0 === n && (n = x.TRIANGLES), i.call(this), (this.geometry = t).refCount++, this.shader = e, this.state = r || Ce.for2d(), this.drawMode = n, this.start = 0, this.size = 0, this.uvs = null, this.indices = null, this.vertexData = new Float32Array(1), this.vertexDirty = 0, this._transformID = -1, this.tint = 16777215, this.blendMode = y.NORMAL, this._roundPixels = M.ROUND_PIXELS, this.batchUvs = null + } + i && (Mesh.__proto__ = i), (Mesh.prototype = Object.create(i && i.prototype)).constructor = Mesh; + var t = { + uvBuffer: { + configurable: !0 + }, + verticesBuffer: { + configurable: !0 + }, + material: { + configurable: !0 + }, + blendMode: { + configurable: !0 + }, + roundPixels: { + configurable: !0 + }, + tint: { + configurable: !0 + }, + texture: { + configurable: !0 + } + }; + return t.uvBuffer.get = function() { + return this.geometry.buffers[1].data + }, t.verticesBuffer.get = function() { + return this.geometry.buffers[0].data + }, t.material.set = function(t) { + this.shader = t + }, t.material.get = function() { + return this.shader + }, t.blendMode.set = function(t) { + this.state.blendMode = t + }, t.blendMode.get = function() { + return this.state.blendMode + }, t.roundPixels.set = function(t) { + this._roundPixels !== t && (this._transformID = -1), this._roundPixels = t + }, t.roundPixels.get = function() { + return this._roundPixels + }, t.tint.get = function() { + return this.shader.tint + }, t.tint.set = function(t) { + this.shader.tint = t + }, t.texture.get = function() { + return this.shader.texture + }, t.texture.set = function(t) { + this.shader.texture = t + }, Mesh.prototype._render = function(t) { + var e = this.geometry.buffers[0].data; + this.shader.batchable && this.drawMode === x.TRIANGLES && e.length < 2 * Mesh.BATCHABLE_SIZE ? this._renderToBatch(t) : this._renderDefault(t) + }, Mesh.prototype._renderDefault = function(t) { + var e = this.shader; + e.alpha = this.worldAlpha, e.update && e.update(), t.batch.flush(), e.program.uniformData.translationMatrix && (e.uniforms.translationMatrix = this.transform.worldTransform.toArray(!0)), t.shader.bind(e), t.state.setState(this.state), t.geometry.bind(this.geometry, e), t.geometry.draw(this.drawMode, this.size, this.start, this.geometry.instanceCount) + }, Mesh.prototype._renderToBatch = function(t) { + var e = this.geometry; + this.shader.uvMatrix && (this.shader.uvMatrix.update(), this.calculateUvs()), this.calculateVertices(), this.indices = e.indexBuffer.data, this._tintRGB = this.shader._tintRGB, this._texture = this.shader.texture; + var r = this.material.pluginName; + t.batch.setObjectRenderer(t.plugins[r]), t.plugins[r].render(this) + }, Mesh.prototype.calculateVertices = function() { + var t = this.geometry, + e = t.buffers[0].data; + if (t.vertexDirtyId !== this.vertexDirty || this._transformID !== this.transform._worldID) { + this._transformID = this.transform._worldID, this.vertexData.length !== e.length && (this.vertexData = new Float32Array(e.length)); + for (var r = this.transform.worldTransform, n = r.a, i = r.b, o = r.c, a = r.d, s = r.tx, u = r.ty, h = this.vertexData, c = 0; c < h.length / 2; c++) { + var l = e[2 * c], + f = e[2 * c + 1]; + h[2 * c] = n * l + o * f + s, h[2 * c + 1] = i * l + a * f + u + } + if (this._roundPixels) + for (var d = 0; d < h.length; d++) h[d] = Math.round(h[d]); + this.vertexDirty = t.vertexDirtyId + } + }, Mesh.prototype.calculateUvs = function() { + var t = this.geometry.buffers[1]; + this.shader.uvMatrix.isSimple ? this.uvs = t.data : (this.batchUvs || (this.batchUvs = new Xn(t, this.shader.uvMatrix)), this.batchUvs.update(), this.uvs = this.batchUvs.data) + }, Mesh.prototype._calculateBounds = function() { + this.calculateVertices(), this._bounds.addVertexData(this.vertexData, 0, this.vertexData.length) + }, Mesh.prototype.containsPoint = function(t) { + if (!this.getBounds().contains(t.x, t.y)) return !1; + this.worldTransform.applyInverse(t, Vn); + for (var e = this.geometry.getAttribute("aVertexPosition").data, r = qn.points, n = this.geometry.getIndex().data, i = n.length, o = 4 === this.drawMode ? 3 : 1, a = 0; a + 2 < i; a += o) { + var s = 2 * n[a], + u = 2 * n[a + 1], + h = 2 * n[a + 2]; + if (r[0] = e[s], r[1] = e[s + 1], r[2] = e[u], r[3] = e[u + 1], r[4] = e[h], r[5] = e[h + 1], qn.contains(Vn.x, Vn.y)) return !0 + } + return !1 + }, Mesh.prototype.destroy = function(t) { + i.prototype.destroy.call(this, t), this.geometry.refCount--, 0 === this.geometry.refCount && this.geometry.dispose(), this.geometry = null, this.shader = null, this.state = null, this.uvs = null, this.indices = null, this.vertexData = null + }, Object.defineProperties(Mesh.prototype, t), Mesh + }(pt); + Hn.BATCHABLE_SIZE = 100; + var Wn = function(n) { + function MeshMaterial(t, e) { + var r = { + uSampler: t, + alpha: 1, + uTextureMatrix: Y.IDENTITY, + uColor: new Float32Array([1, 1, 1, 1]) + }; + (e = Object.assign({ + tint: 16777215, + alpha: 1, + pluginName: "batch" + }, e)).uniforms && Object.assign(r, e.uniforms), n.call(this, e.program || Ae.from("attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform mat3 uTextureMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;\n}\n", "varying vec2 vTextureCoord;\nuniform vec4 uColor;\n\nuniform sampler2D uSampler;\n\nvoid main(void)\n{\n gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor;\n}\n"), r), this._colorDirty = !1, this.uvMatrix = new ke(t), this.batchable = void 0 === e.program, this.pluginName = e.pluginName, this.tint = e.tint, this.alpha = e.alpha + } + n && (MeshMaterial.__proto__ = n), (MeshMaterial.prototype = Object.create(n && n.prototype)).constructor = MeshMaterial; + var t = { + texture: { + configurable: !0 + }, + alpha: { + configurable: !0 + }, + tint: { + configurable: !0 + } + }; + return t.texture.get = function() { + return this.uniforms.uSampler + }, t.texture.set = function(t) { + this.uniforms.uSampler !== t && (this.uniforms.uSampler = t, this.uvMatrix.texture = t) + }, t.alpha.set = function(t) { + t !== this._alpha && (this._alpha = t, this._colorDirty = !0) + }, t.alpha.get = function() { + return this._alpha + }, t.tint.set = function(t) { + t !== this._tint && (this._tint = t, this._tintRGB = (t >> 16) + (65280 & t) + ((255 & t) << 16), this._colorDirty = !0) + }, t.tint.get = function() { + return this._tint + }, MeshMaterial.prototype.update = function() { + if (this._colorDirty) { + this._colorDirty = !1; + var t = this.texture.baseTexture; + premultiplyTintToRgba(this._tint, this._alpha, this.uniforms.uColor, t.premultiplyAlpha) + } + this.uvMatrix.update() && (this.uniforms.uTextureMatrix = this.uvMatrix.mapCoord) + }, Object.defineProperties(MeshMaterial.prototype, t), MeshMaterial + }(Ie), + Yn = function(a) { + function MeshGeometry(t, e, r) { + a.call(this); + var n = new Zt(t), + i = new Zt(e, !0), + o = new Zt(r, !0, !0); + this.addAttribute("aVertexPosition", n, 2, !1, w.FLOAT).addAttribute("aTextureCoord", i, 2, !1, w.FLOAT).addIndex(o), this._updateId = -1 + } + a && (MeshGeometry.__proto__ = a), (MeshGeometry.prototype = Object.create(a && a.prototype)).constructor = MeshGeometry; + var t = { + vertexDirtyId: { + configurable: !0 + } + }; + return t.vertexDirtyId.get = function() { + return this.buffers[0]._updateID + }, Object.defineProperties(MeshGeometry.prototype, t), MeshGeometry + }(ee), + Kn = function(i) { + function PlaneGeometry(t, e, r, n) { + void 0 === t && (t = 100), void 0 === e && (e = 100), void 0 === r && (r = 10), void 0 === n && (n = 10), i.call(this), this.segWidth = r, this.segHeight = n, this.width = t, this.height = e, this.build() + } + return i && (PlaneGeometry.__proto__ = i), ((PlaneGeometry.prototype = Object.create(i && i.prototype)).constructor = PlaneGeometry).prototype.build = function() { + for (var t = this.segWidth * this.segHeight, e = [], r = [], n = [], i = this.segWidth - 1, o = this.segHeight - 1, a = this.width / i, s = this.height / o, u = 0; u < t; u++) { + var h = u % this.segWidth, + c = u / this.segWidth | 0; + e.push(h * a, c * s), r.push(h / i, c / o) + } + for (var l = i * o, f = 0; f < l; f++) { + var d = f % i, + p = f / i | 0, + m = p * this.segWidth + d, + v = p * this.segWidth + d + 1, + g = (p + 1) * this.segWidth + d, + y = (p + 1) * this.segWidth + d + 1; + n.push(m, v, g, v, y, g) + } + this.buffers[0].data = new Float32Array(e), this.buffers[1].data = new Float32Array(r), this.indexBuffer.data = new Uint16Array(n), this.buffers[0].update(), this.buffers[1].update(), this.indexBuffer.update() + }, PlaneGeometry + }(Yn), + Zn = function(r) { + function RopeGeometry(t, e) { + void 0 === t && (t = 200), r.call(this, new Float32Array(4 * e.length), new Float32Array(4 * e.length), new Uint16Array(6 * (e.length - 1))), this.points = e, this.width = t, this.build() + } + return r && (RopeGeometry.__proto__ = r), ((RopeGeometry.prototype = Object.create(r && r.prototype)).constructor = RopeGeometry).prototype.build = function() { + var t = this.points; + if (t) { + var e = this.getAttribute("aVertexPosition"), + r = this.getAttribute("aTextureCoord"), + n = this.getIndex(); + if (!(t.length < 1)) { + e.data.length / 4 !== t.length && (e.data = new Float32Array(4 * t.length), r.data = new Float32Array(4 * t.length), n.data = new Uint16Array(6 * (t.length - 1))); + var i = r.data, + o = n.data; + i[0] = 0, i[1] = 0, i[2] = 0, i[3] = 1; + for (var a = t.length, s = 0; s < a; s++) { + var u = 4 * s, + h = s / (a - 1); + i[u] = h, i[u + 1] = 0, i[u + 2] = h, i[u + 3] = 1 + } + for (var c = 0, l = 0; l < a - 1; l++) { + var f = 2 * l; + o[c++] = f, o[c++] = f + 1, o[c++] = f + 2, o[c++] = f + 2, o[c++] = f + 1, o[c++] = f + 3 + } + r.update(), n.update(), this.updateVertices() + } + } + }, RopeGeometry.prototype.updateVertices = function() { + var t = this.points; + if (!(t.length < 1)) { + for (var e, r = t[0], n = 0, i = 0, o = this.buffers[0].data, a = t.length, s = 0; s < a; s++) { + var u = t[s], + h = 4 * s; + i = -((e = s < t.length - 1 ? t[s + 1] : u).x - r.x), n = e.y - r.y; + var c = Math.sqrt(n * n + i * i), + l = this.width / 2; + n /= c, i /= c, n *= l, i *= l, o[h] = u.x + n, o[h + 1] = u.y + i, o[h + 2] = u.x - n, o[h + 3] = u.y - i, r = u + } + this.buffers[0].update() + } + }, RopeGeometry.prototype.update = function() { + this.updateVertices() + }, RopeGeometry + }(Yn), + Qn = function(i) { + function SimpleRope(t, e) { + var r = new Zn(t.height, e), + n = new Wn(t); + i.call(this, r, n), this.autoUpdate = !0 + } + return i && (SimpleRope.__proto__ = i), ((SimpleRope.prototype = Object.create(i && i.prototype)).constructor = SimpleRope).prototype._render = function(t) { + (this.autoUpdate || this.geometry.width !== this.shader.texture.height) && (this.geometry.width = this.shader.texture.height, this.geometry.update()), i.prototype._render.call(this, t) + }, SimpleRope + }(Hn), + Jn = function(o) { + function SimplePlane(t, e, r) { + var n = new Kn(t.width, t.height, e, r), + i = new Wn(Ht.WHITE); + o.call(this, n, i), this.texture = t + } + o && (SimplePlane.__proto__ = o); + var t = { + texture: { + configurable: !0 + } + }; + return ((SimplePlane.prototype = Object.create(o && o.prototype)).constructor = SimplePlane).prototype.textureUpdated = function() { + this._textureID = this.shader.texture._updateID, this.geometry.width = this.shader.texture.width, this.geometry.height = this.shader.texture.height, this.geometry.build() + }, t.texture.set = function(t) { + this.shader.texture !== t && (this.shader.texture = t, this._textureID = -1, t.baseTexture.valid ? this.textureUpdated() : t.once("update", this.textureUpdated, this)) + }, t.texture.get = function() { + return this.shader.texture + }, SimplePlane.prototype._render = function(t) { + this._textureID !== this.shader.texture._updateID && this.textureUpdated(), o.prototype._render.call(this, t) + }, Object.defineProperties(SimplePlane.prototype, t), SimplePlane + }(Hn), + $n = function(s) { + function SimpleMesh(t, e, r, n, i) { + void 0 === t && (t = Ht.EMPTY); + var o = new Yn(e, r, n); + o.getAttribute("aVertexPosition").static = !1; + var a = new Wn(t); + s.call(this, o, a, null, i), this.autoUpdate = !0 + } + s && (SimpleMesh.__proto__ = s), (SimpleMesh.prototype = Object.create(s && s.prototype)).constructor = SimpleMesh; + var t = { + vertices: { + configurable: !0 + } + }; + return t.vertices.get = function() { + return this.geometry.getAttribute("aVertexPosition").data + }, t.vertices.set = function(t) { + this.geometry.getAttribute("aVertexPosition").data = t + }, SimpleMesh.prototype._render = function(t) { + this.autoUpdate && this.geometry.getAttribute("aVertexPosition").update(), s.prototype._render.call(this, t) + }, Object.defineProperties(SimpleMesh.prototype, t), SimpleMesh + }(Hn), + ti = function(o) { + function NineSlicePlane(t, e, r, n, i) { + o.call(this, Ht.WHITE, 4, 4), this._origWidth = t.orig.width, this._origHeight = t.orig.height, this._width = this._origWidth, this._height = this._origHeight, this._leftWidth = void 0 !== e ? e : 10, this._rightWidth = void 0 !== n ? n : 10, this._topHeight = void 0 !== r ? r : 10, this._bottomHeight = void 0 !== i ? i : 10, this.texture = t + } + o && (NineSlicePlane.__proto__ = o); + var t = { + vertices: { + configurable: !0 + }, + width: { + configurable: !0 + }, + height: { + configurable: !0 + }, + leftWidth: { + configurable: !0 + }, + rightWidth: { + configurable: !0 + }, + topHeight: { + configurable: !0 + }, + bottomHeight: { + configurable: !0 + } + }; + return ((NineSlicePlane.prototype = Object.create(o && o.prototype)).constructor = NineSlicePlane).prototype.textureUpdated = function() { + this._textureID = this.shader.texture._updateID, this._refresh() + }, t.vertices.get = function() { + return this.geometry.getAttribute("aVertexPosition").data + }, t.vertices.set = function(t) { + this.geometry.getAttribute("aVertexPosition").data = t + }, NineSlicePlane.prototype.updateHorizontalVertices = function() { + var t = this.vertices, + e = this._topHeight + this._bottomHeight, + r = this._height > e ? 1 : this._height / e; + t[9] = t[11] = t[13] = t[15] = this._topHeight * r, t[17] = t[19] = t[21] = t[23] = this._height - this._bottomHeight * r, t[25] = t[27] = t[29] = t[31] = this._height + }, NineSlicePlane.prototype.updateVerticalVertices = function() { + var t = this.vertices, + e = this._leftWidth + this._rightWidth, + r = this._width > e ? 1 : this._width / e; + t[2] = t[10] = t[18] = t[26] = this._leftWidth * r, t[4] = t[12] = t[20] = t[28] = this._width - this._rightWidth * r, t[6] = t[14] = t[22] = t[30] = this._width + }, t.width.get = function() { + return this._width + }, t.width.set = function(t) { + this._width = t, this._refresh() + }, t.height.get = function() { + return this._height + }, t.height.set = function(t) { + this._height = t, this._refresh() + }, t.leftWidth.get = function() { + return this._leftWidth + }, t.leftWidth.set = function(t) { + this._leftWidth = t, this._refresh() + }, t.rightWidth.get = function() { + return this._rightWidth + }, t.rightWidth.set = function(t) { + this._rightWidth = t, this._refresh() + }, t.topHeight.get = function() { + return this._topHeight + }, t.topHeight.set = function(t) { + this._topHeight = t, this._refresh() + }, t.bottomHeight.get = function() { + return this._bottomHeight + }, t.bottomHeight.set = function(t) { + this._bottomHeight = t, this._refresh() + }, NineSlicePlane.prototype._refresh = function() { + var t = this.texture, + e = this.geometry.buffers[1].data; + this._origWidth = t.orig.width, this._origHeight = t.orig.height; + var r = 1 / this._origWidth, + n = 1 / this._origHeight; + e[0] = e[8] = e[16] = e[24] = 0, e[1] = e[3] = e[5] = e[7] = 0, e[6] = e[14] = e[22] = e[30] = 1, e[25] = e[27] = e[29] = e[31] = 1, e[2] = e[10] = e[18] = e[26] = r * this._leftWidth, e[4] = e[12] = e[20] = e[28] = 1 - r * this._rightWidth, e[9] = e[11] = e[13] = e[15] = n * this._topHeight, e[17] = e[19] = e[21] = e[23] = 1 - n * this._bottomHeight, this.updateHorizontalVertices(), this.updateVerticalVertices(), this.geometry.buffers[0].update(), this.geometry.buffers[1].update() + }, Object.defineProperties(NineSlicePlane.prototype, t), NineSlicePlane + }(Jn), + ei = function(r) { + function AnimatedSprite(t, e) { + r.call(this, t[0] instanceof Ht ? t[0] : t[0].texture), this._textures = null, this._durations = null, this.textures = t, this._autoUpdate = !1 !== e, this.animationSpeed = 1, this.loop = !0, this.updateAnchor = !1, this.onComplete = null, this.onFrameChange = null, this.onLoop = null, this._currentTime = 0, this.playing = !1 + } + r && (AnimatedSprite.__proto__ = r); + var t = { + totalFrames: { + configurable: !0 + }, + textures: { + configurable: !0 + }, + currentFrame: { + configurable: !0 + } + }; + return ((AnimatedSprite.prototype = Object.create(r && r.prototype)).constructor = AnimatedSprite).prototype.stop = function() { + this.playing && (this.playing = !1, this._autoUpdate && xt.shared.remove(this.update, this)) + }, AnimatedSprite.prototype.play = function() { + this.playing || (this.playing = !0, this._autoUpdate && xt.shared.add(this.update, this, _t.HIGH)) + }, AnimatedSprite.prototype.gotoAndStop = function(t) { + this.stop(); + var e = this.currentFrame; + this._currentTime = t, e !== this.currentFrame && this.updateTexture() + }, AnimatedSprite.prototype.gotoAndPlay = function(t) { + var e = this.currentFrame; + this._currentTime = t, e !== this.currentFrame && this.updateTexture(), this.play() + }, AnimatedSprite.prototype.update = function(t) { + var e = this.animationSpeed * t, + r = this.currentFrame; + if (null !== this._durations) { + var n = this._currentTime % 1 * this._durations[this.currentFrame]; + for (n += e / 60 * 1e3; n < 0;) this._currentTime--, n += this._durations[this.currentFrame]; + var i = Math.sign(this.animationSpeed * t); + for (this._currentTime = Math.floor(this._currentTime); n >= this._durations[this.currentFrame];) n -= this._durations[this.currentFrame] * i, this._currentTime += i; + this._currentTime += n / this._durations[this.currentFrame] + } else this._currentTime += e; + this._currentTime < 0 && !this.loop ? (this.gotoAndStop(0), this.onComplete && this.onComplete()) : this._currentTime >= this._textures.length && !this.loop ? (this.gotoAndStop(this._textures.length - 1), this.onComplete && this.onComplete()) : r !== this.currentFrame && (this.loop && this.onLoop && (0 < this.animationSpeed && this.currentFrame < r ? this.onLoop() : this.animationSpeed < 0 && this.currentFrame > r && this.onLoop()), this.updateTexture()) + }, AnimatedSprite.prototype.updateTexture = function() { + this._texture = this._textures[this.currentFrame], this._textureID = -1, this._textureTrimmedID = -1, this.cachedTint = 16777215, this.uvs = this._texture._uvs.uvsFloat32, this.updateAnchor && this._anchor.copy(this._texture.defaultAnchor), this.onFrameChange && this.onFrameChange(this.currentFrame) + }, AnimatedSprite.prototype.destroy = function(t) { + this.stop(), r.prototype.destroy.call(this, t), this.onComplete = null, this.onFrameChange = null, this.onLoop = null + }, AnimatedSprite.fromFrames = function(t) { + for (var e = [], r = 0; r < t.length; ++r) e.push(Ht.from(t[r])); + return new AnimatedSprite(e) + }, AnimatedSprite.fromImages = function(t) { + for (var e = [], r = 0; r < t.length; ++r) e.push(Ht.from(t[r])); + return new AnimatedSprite(e) + }, t.totalFrames.get = function() { + return this._textures.length + }, t.textures.get = function() { + return this._textures + }, t.textures.set = function(t) { + if (t[0] instanceof Ht) this._textures = t, this._durations = null; + else { + this._textures = [], this._durations = []; + for (var e = 0; e < t.length; e++) this._textures.push(t[e].texture), this._durations.push(t[e].time) + } + this.gotoAndStop(0), this.updateTexture() + }, t.currentFrame.get = function() { + var t = Math.floor(this._currentTime) % this._textures.length; + return t < 0 && (t += this._textures.length), t + }, Object.defineProperties(AnimatedSprite.prototype, t), AnimatedSprite + }(Xr); + r.d(e, "VERSION", function() { + return ri + }), r.d(e, "filters", function() { + return ni + }), r.d(e, "useDeprecated", function() { + return useDeprecated + }), r.d(e, "accessibility", function() { + return i + }), r.d(e, "extract", function() { + return o + }), r.d(e, "interaction", function() { + return a + }), r.d(e, "prepare", function() { + return s + }), r.d(e, "utils", function() { + return n + }), r.d(e, "Application", function() { + return rn + }), r.d(e, "AbstractRenderer", function() { + return Je + }), r.d(e, "Attribute", function() { + return Yt + }), r.d(e, "BaseRenderTexture", function() { + return Xt + }), r.d(e, "BaseTexture", function() { + return Rt + }), r.d(e, "BatchDrawCall", function() { + return ir + }), r.d(e, "BatchGeometry", function() { + return nr + }), r.d(e, "BatchRenderer", function() { + return cr + }), r.d(e, "Buffer", function() { + return Zt + }), r.d(e, "CubeTexture", function() { + return rr + }), r.d(e, "Filter", function() { + return De + }), r.d(e, "Framebuffer", function() { + return jt + }), r.d(e, "GLProgram", function() { + return Xe + }), r.d(e, "GLTexture", function() { + return Rt + }), r.d(e, "Geometry", function() { + return ee + }), r.d(e, "ObjectRenderer", function() { + return he + }), r.d(e, "Program", function() { + return Ae + }), r.d(e, "Quad", function() { + return re + }), r.d(e, "QuadUv", function() { + return ne + }), r.d(e, "RenderTexture", function() { + return Wt + }), r.d(e, "Renderer", function() { + return $e + }), r.d(e, "Shader", function() { + return Ie + }), r.d(e, "SpriteMaskFilter", function() { + return Ne + }), r.d(e, "State", function() { + return Ce + }), r.d(e, "System", function() { + return Ut + }), r.d(e, "Texture", function() { + return Ht + }), r.d(e, "TextureMatrix", function() { + return ke + }), r.d(e, "TextureUvs", function() { + return Vt + }), r.d(e, "UniformGroup", function() { + return oe + }), r.d(e, "autoDetectRenderer", function() { + return autoDetectRenderer + }), r.d(e, "checkMaxIfStatementsInShader", function() { + return checkMaxIfStatementsInShader + }), r.d(e, "defaultFilterVertex", function() { + return er + }), r.d(e, "defaultVertex", function() { + return tr + }), r.d(e, "generateMultiTextureShader", function() { + return generateMultiTextureShader + }), r.d(e, "resources", function() { + return Bt + }), r.d(e, "systems", function() { + return Ze + }), r.d(e, "AppLoaderPlugin", function() { + return vn + }), r.d(e, "Loader", function() { + return mn + }), r.d(e, "LoaderResource", function() { + return gn + }), r.d(e, "TextureLoader", function() { + return pn + }), r.d(e, "ParticleContainer", function() { + return yn + }), r.d(e, "ParticleRenderer", function() { + return bn + }), r.d(e, "Spritesheet", function() { + return xn + }), r.d(e, "SpritesheetLoader", function() { + return Tn + }), r.d(e, "TilingSprite", function() { + return Mn + }), r.d(e, "TilingSpriteRenderer", function() { + return Pn + }), r.d(e, "BitmapFontLoader", function() { + return On + }), r.d(e, "BitmapText", function() { + return In + }), r.d(e, "Ticker", function() { + return xt + }), r.d(e, "TickerPlugin", function() { + return St + }), r.d(e, "UPDATE_PRIORITY", function() { + return _t + }), r.d(e, "BLEND_MODES", function() { + return y + }), r.d(e, "DRAW_MODES", function() { + return x + }), r.d(e, "ENV", function() { + return v + }), r.d(e, "FORMATS", function() { + return _ + }), r.d(e, "GC_MODES", function() { + return A + }), r.d(e, "MIPMAP_MODES", function() { + return E + }), r.d(e, "PRECISION", function() { + return P + }), r.d(e, "RENDERER_TYPE", function() { + return g + }), r.d(e, "SCALE_MODES", function() { + return T + }), r.d(e, "TARGETS", function() { + return b + }), r.d(e, "TYPES", function() { + return w + }), r.d(e, "WRAP_MODES", function() { + return S + }), r.d(e, "Bounds", function() { + return ft + }), r.d(e, "Container", function() { + return pt + }), r.d(e, "DisplayObject", function() { + return dt + }), r.d(e, "GRAPHICS_CURVES", function() { + return xr + }), r.d(e, "Graphics", function() { + return Gr + }), r.d(e, "GraphicsData", function() { + return Tr + }), r.d(e, "GraphicsGeometry", function() { + return Rr + }), r.d(e, "Circle", function() { + return ut + }), r.d(e, "DEG_TO_RAD", function() { + return H + }), r.d(e, "Ellipse", function() { + return ht + }), r.d(e, "GroupD8", function() { + return rt + }), r.d(e, "Matrix", function() { + return Y + }), r.d(e, "ObservablePoint", function() { + return j + }), r.d(e, "PI_2", function() { + return X + }), r.d(e, "Point", function() { + return q + }), r.d(e, "Polygon", function() { + return ct + }), r.d(e, "RAD_TO_DEG", function() { + return V + }), r.d(e, "Rectangle", function() { + return ot + }), r.d(e, "RoundedRectangle", function() { + return lt + }), r.d(e, "SHAPES", function() { + return W + }), r.d(e, "Transform", function() { + return nt + }), r.d(e, "Mesh", function() { + return Hn + }), r.d(e, "MeshBatchUvs", function() { + return Xn + }), r.d(e, "MeshGeometry", function() { + return Yn + }), r.d(e, "MeshMaterial", function() { + return Wn + }), r.d(e, "NineSlicePlane", function() { + return ti + }), r.d(e, "PlaneGeometry", function() { + return Kn + }), r.d(e, "RopeGeometry", function() { + return Zn + }), r.d(e, "SimpleMesh", function() { + return $n + }), r.d(e, "SimplePlane", function() { + return Jn + }), r.d(e, "SimpleRope", function() { + return Qn + }), r.d(e, "Runner", function() { + return gt + }), r.d(e, "Sprite", function() { + return Xr + }), r.d(e, "AnimatedSprite", function() { + return ei + }), r.d(e, "TEXT_GRADIENT", function() { + return Vr + }), r.d(e, "Text", function() { + return Qr + }), r.d(e, "TextMetrics", function() { + return Yr + }), r.d(e, "TextStyle", function() { + return Hr + }), r.d(e, "isMobile", function() { + return h.a + }), r.d(e, "settings", function() { + return M + }); + + function useDeprecated() { + var e = this; + Object.defineProperties(e, { + SVG_SIZE: { + get: function() { + return deprecation(0, "PIXI.utils.SVG_SIZE has moved to PIXI.SVGResource.SVG_SIZE"), e.SVGResource.SVG_SIZE + } + }, + TransformStatic: { + get: function() { + return deprecation(0, "PIXI.TransformStatic has been removed, use PIXI.Transform"), e.Transform + } + }, + TransformBase: { + get: function() { + return deprecation(0, "PIXI.TransformBase has been removed, use PIXI.Transform"), e.Transform + } + }, + TRANSFORM_MODE: { + get: function() { + return deprecation(0, "PIXI.TRANSFORM_MODE has been removed"), { + STATIC: 0, + DYNAMIC: 1 + } + } + }, + WebGLRenderer: { + get: function() { + return deprecation(0, "PIXI.WebGLRenderer has moved to PIXI.Renderer"), e.Renderer + } + }, + CanvasRenderTarget: { + get: function() { + return deprecation(0, "PIXI.CanvasRenderTarget has moved to PIXI.utils.CanvasRenderTarget"), e.utils.CanvasRenderTarget + } + }, + loader: { + get: function() { + return deprecation(0, "PIXI.loader has moved to PIXI.Loader.shared"), e.Loader.shared + } + }, + FilterManager: { + get: function() { + return deprecation(0, "PIXI.FilterManager has moved to PIXI.systems.FilterSystem"), e.systems.FilterManager + } + } + }), e.extras = {}, Object.defineProperties(e.extras, { + TilingSprite: { + get: function() { + return deprecation(0, "PIXI.extras.TilingSprite has moved to PIXI.TilingSprite"), e.TilingSprite + } + }, + TilingSpriteRenderer: { + get: function() { + return deprecation(0, "PIXI.extras.TilingSpriteRenderer has moved to PIXI.TilingSpriteRenderer"), e.TilingSpriteRenderer + } + }, + AnimatedSprite: { + get: function() { + return deprecation(0, "PIXI.extras.AnimatedSprite has moved to PIXI.AnimatedSprite"), e.AnimatedSprite + } + }, + BitmapText: { + get: function() { + return deprecation(0, "PIXI.extras.BitmapText has moved to PIXI.BitmapText"), e.BitmapText + } + } + }), Object.defineProperties(e.utils, { + getSvgSize: { + get: function() { + return deprecation(0, "PIXI.utils.getSvgSize has moved to PIXI.SVGResource.getSize"), e.SVGResource.getSize + } + } + }), e.mesh = {}, Object.defineProperties(e.mesh, { + Mesh: { + get: function() { + return deprecation(0, "PIXI.mesh.Mesh has moved to PIXI.SimpleMesh"), e.SimpleMesh + } + }, + NineSlicePlane: { + get: function() { + return deprecation(0, "PIXI.mesh.NineSlicePlane has moved to PIXI.NineSlicePlane"), e.NineSlicePlane + } + }, + Plane: { + get: function() { + return deprecation(0, "PIXI.mesh.Plane has moved to PIXI.SimplePlane"), e.SimplePlane + } + }, + Rope: { + get: function() { + return deprecation(0, "PIXI.mesh.Rope has moved to PIXI.SimpleRope"), e.SimpleRope + } + }, + RawMesh: { + get: function() { + return deprecation(0, "PIXI.mesh.RawMesh has moved to PIXI.Mesh"), e.Mesh + } + }, + CanvasMeshRenderer: { + get: function() { + return deprecation(0, "PIXI.mesh.CanvasMeshRenderer has moved to PIXI.CanvasMeshRenderer"), e.CanvasMeshRenderer + } + }, + MeshRenderer: { + get: function() { + return deprecation(0, "PIXI.mesh.MeshRenderer has moved to PIXI.MeshRenderer"), e.MeshRenderer + } + } + }), e.particles = {}, Object.defineProperties(e.particles, { + ParticleContainer: { + get: function() { + return deprecation(0, "PIXI.particles.ParticleContainer has moved to PIXI.ParticleContainer"), e.ParticleContainer + } + }, + ParticleRenderer: { + get: function() { + return deprecation(0, "PIXI.particles.ParticleRenderer has moved to PIXI.ParticleRenderer"), e.ParticleRenderer + } + } + }), e.ticker = {}, Object.defineProperties(e.ticker, { + Ticker: { + get: function() { + return deprecation(0, "PIXI.ticker.Ticker has moved to PIXI.Ticker"), e.Ticker + } + }, + shared: { + get: function() { + return deprecation(0, "PIXI.ticker.shared has moved to PIXI.Ticker.shared"), e.Ticker.shared + } + } + }), e.loaders = {}, Object.defineProperties(e.loaders, { + Loader: { + get: function() { + return deprecation(0, "PIXI.loaders.Loader has moved to PIXI.Loader"), e.Loader + } + }, + Resource: { + get: function() { + return deprecation(0, "PIXI.loaders.Resource has moved to PIXI.LoaderResource"), e.LoaderResource + } + }, + bitmapFontParser: { + get: function() { + return deprecation(0, "PIXI.loaders.bitmapFontParser has moved to PIXI.BitmapFontLoader.use"), e.BitmapFontLoader.use + } + }, + parseBitmapFontData: { + get: function() { + return deprecation(0, "PIXI.loaders.parseBitmapFontData has moved to PIXI.BitmapFontLoader.parse"), e.BitmapFontLoader.parse + } + }, + spritesheetParser: { + get: function() { + return deprecation(0, "PIXI.loaders.spritesheetParser has moved to PIXI.SpritesheetLoader.use"), e.SpritesheetLoader.use + } + }, + getResourcePath: { + get: function() { + return deprecation(0, "PIXI.loaders.getResourcePath has moved to PIXI.SpritesheetLoader.getResourcePath"), e.SpritesheetLoader.getResourcePath + } + } + }), e.Loader.addPixiMiddleware = function(t) { + return deprecation(0, "PIXI.loaders.Loader.addPixiMiddleware is deprecated, use PIXI.loaders.Loader.registerPlugin"), e.loaders.Loader.registerPlugin({ + use: t() + }) + }, Object.defineProperty(e.extract, "WebGLExtract", { + get: function() { + return deprecation(0, "PIXI.extract.WebGLExtract has moved to PIXI.extract.Extract"), e.extract.Extract + } + }), Object.defineProperty(e.prepare, "WebGLPrepare", { + get: function() { + return deprecation(0, "PIXI.prepare.WebGLPrepare has moved to PIXI.prepare.Prepare"), e.prepare.Prepare + } + }), e.Container.prototype._renderWebGL = function(t) { + deprecation(0, "PIXI.Container#_renderWebGL has moved to PIXI.Container#_render"), this._render(t) + }, e.Container.prototype.renderWebGL = function(t) { + deprecation(0, "PIXI.Container#renderWebGL has moved to PIXI.Container#render"), this.render(t) + }, e.DisplayObject.prototype.renderWebGL = function(t) { + deprecation(0, "PIXI.DisplayObject#renderWebGL has moved to PIXI.DisplayObject#render"), this.render(t) + }, e.Container.prototype.renderAdvancedWebGL = function(t) { + deprecation(0, "PIXI.Container#renderAdvancedWebGL has moved to PIXI.Container#renderAdvanced"), this.renderAdvanced(t) + }, Object.defineProperties(e.settings, { + TRANSFORM_MODE: { + get: function() { + return deprecation(0, "PIXI.settings.TRANSFORM_MODE has been removed."), 0 + }, + set: function() { + deprecation(0, "PIXI.settings.TRANSFORM_MODE has been removed.") + } + } + }); + var o = e.BaseTexture; + o.fromImage = function(t, e, r, n) { + deprecation(0, "PIXI.BaseTexture.fromImage has been replaced with PIXI.BaseTexture.from"); + var i = { + scale: n, + crossorigin: e + }; + return o.from(t, { + scaleMode: r, + resourceOptions: i + }) + }, o.fromCanvas = function(t, e) { + return deprecation(0, "PIXI.BaseTexture.fromCanvas has been replaced with PIXI.BaseTexture.from"), o.from(t, { + scaleMode: e + }) + }, o.fromSVG = function(t, e, r, n) { + deprecation(0, "PIXI.BaseTexture.fromSVG has been replaced with PIXI.BaseTexture.from"); + var i = { + scale: n, + crossorigin: e + }; + return o.from(t, { + scaleMode: r, + resourceOptions: i + }) + }, e.Point.prototype.copy = function(t) { + return deprecation(0, "PIXI.Point.copy has been replaced with PIXI.Point#copyFrom"), this.copyFrom(t) + }, e.ObservablePoint.prototype.copy = function(t) { + return deprecation(0, "PIXI.ObservablePoint.copy has been replaced with PIXI.ObservablePoint#copyFrom"), this.copyFrom(t) + }, e.Rectangle.prototype.copy = function(t) { + return deprecation(0, "PIXI.Rectangle.copy has been replaced with PIXI.Rectangle#copyFrom"), this.copyFrom(t) + }, e.Matrix.prototype.copy = function(t) { + return deprecation(0, "PIXI.Matrix.copy has been replaced with PIXI.Matrix#copyTo"), this.copyTo(t) + }, Object.assign(e.systems.FilterSystem.prototype, { + getRenderTarget: function(t, e) { + return deprecation(0, "FilterManager#getRenderTarget has been replaced with FilterSystem#getFilterTexture"), this.getFilterTexture(e) + }, + returnRenderTarget: function(t) { + deprecation(0, "FilterManager#returnRenderTarget has been replaced with FilterSystem#returnFilterTexture"), this.returnFilterTexture(t) + } + }), Object.defineProperties(e.RenderTexture.prototype, { + sourceFrame: { + get: function() { + return deprecation(0, "PIXI.RenderTexture#sourceFrame has been removed"), this.filterFrame + } + }, + size: { + get: function() { + return deprecation(0, "PIXI.RenderTexture#size has been removed"), this._frame + } + } + }); + var t = function(i) { + function BlurXFilter(t, e, r, n) { + deprecation(0, "PIXI.filters.BlurXFilter is deprecated, use PIXI.filters.BlurFilterPass"), i.call(this, !0, t, e, r, n) + } + return i && (BlurXFilter.__proto__ = i), (BlurXFilter.prototype = Object.create(i && i.prototype)).constructor = BlurXFilter + }(e.filters.BlurFilterPass), + r = function(i) { + function BlurYFilter(t, e, r, n) { + deprecation(0, "PIXI.filters.BlurYFilter is deprecated, use PIXI.filters.BlurFilterPass"), i.call(this, !1, t, e, r, n) + } + return i && (BlurYFilter.__proto__ = i), (BlurYFilter.prototype = Object.create(i && i.prototype)).constructor = BlurYFilter + }(e.filters.BlurFilterPass); + Object.assign(e.filters, { + BlurXFilter: t, + BlurYFilter: r + }); + var i = e.Sprite, + a = e.Texture, + n = e.Graphics; + + function spriteFrom(t, e, r, n) { + return deprecation(0, "PIXI.Sprite." + t + " is deprecated, use PIXI.Sprite.from"), i.from(e, { + resourceOptions: { + scale: n, + crossorigin: r + } + }) + } + + function textureFrom(t, e, r, n) { + return deprecation(0, "PIXI.Texture." + t + " is deprecated, use PIXI.Texture.from"), a.from(e, { + resourceOptions: { + scale: n, + crossorigin: r + } + }) + } + n.prototype.generateCanvasTexture || (n.prototype.generateCanvasTexture = function() { + deprecation(0, 'PIXI.Graphics#generateCanvasTexture is only available in "pixi.js-legacy"') + }), i.fromImage = spriteFrom.bind(null, "fromImage"), i.fromSVG = spriteFrom.bind(null, "fromSVG"), i.fromCanvas = spriteFrom.bind(null, "fromCanvas"), i.fromVideo = spriteFrom.bind(null, "fromVideo"), i.fromFrame = spriteFrom.bind(null, "fromFrame"), a.fromImage = textureFrom.bind(null, "fromImage"), a.fromSVG = textureFrom.bind(null, "fromSVG"), a.fromCanvas = textureFrom.bind(null, "fromCanvas"), a.fromVideo = textureFrom.bind(null, "fromVideo"), a.fromFrame = textureFrom.bind(null, "fromFrame"), Object.defineProperty(e.AbstractRenderer.prototype, "autoResize", { + get: function() { + return deprecation(0, "PIXI.AbstractRenderer autoResize is deprecated, use autoDensity"), this.autoDensity + }, + set: function(t) { + deprecation(0, "PIXI.AbstractRenderer autoResize is deprecated, use autoDensity"), this.autoDensity = t + } + }), e.utils.mixins = { + mixin: function() { + deprecation(0, "PIXI.utils.mixins.mixin no longer available") + }, + delayMixin: function() { + deprecation(0, "PIXI.utils.mixins.delayMixin no longer available") + }, + performMixins: function() { + deprecation(0, "PIXI.utils.mixins.performMixins no longer available") + } + } + } + $e.registerPlugin("accessibility", vt), $e.registerPlugin("extract", fr), $e.registerPlugin("interaction", br), $e.registerPlugin("particle", bn), $e.registerPlugin("prepare", tn), $e.registerPlugin("batch", cr), $e.registerPlugin("tilingSprite", Pn), mn.registerPlugin(On), mn.registerPlugin(Tn), rn.registerPlugin(St), rn.registerPlugin(vn); + var ri = "5.0.0-rc.3", + ni = { + AlphaFilter: Cn, + BlurFilter: Ln, + BlurFilterPass: kn, + ColorMatrixFilter: Nn, + DisplacementFilter: Bn, + FXAAFilter: Un, + NoiseFilter: Gn + } + }, function(t, e, r) { + var m = r(9), + v = r(32), + g = r(25), + y = r(26), + _ = r(33), + b = "prototype", + x = function(t, e, r) { + var n, i, o, a, s = t & x.F, + u = t & x.G, + h = t & x.S, + c = t & x.P, + l = t & x.B, + f = u ? m : h ? m[e] || (m[e] = {}) : (m[e] || {})[b], + d = u ? v : v[e] || (v[e] = {}), + p = d[b] || (d[b] = {}); + for (n in u && (r = e), r) o = ((i = !s && f && void 0 !== f[n]) ? f : r)[n], a = l && i ? _(o, m) : c && "function" == typeof o ? _(Function.call, o) : o, f && y(f, n, o, t & x.U), d[n] != o && g(d, n, a), c && p[n] != o && (p[n] = o) + }; + m.core = v, x.F = 1, x.G = 2, x.S = 4, x.P = 8, x.B = 16, x.W = 32, x.U = 64, x.R = 128, t.exports = x + }, function(t, e, r) { + "use strict"; + var n = {}; + r.r(n), r.d(n, "DEG2RAD", function() { + return u + }), r.d(n, "RAD2DEG", function() { + return h + }), r.d(n, "generateUUID", function() { + return generateUUID + }), r.d(n, "clamp", function() { + return Math_clamp + }), r.d(n, "euclideanModulo", function() { + return euclideanModulo + }), r.d(n, "mapLinear", function() { + return mapLinear + }), r.d(n, "lerp", function() { + return lerp + }), r.d(n, "smoothstep", function() { + return smoothstep + }), r.d(n, "smootherstep", function() { + return smootherstep + }), r.d(n, "randInt", function() { + return randInt + }), r.d(n, "randFloat", function() { + return randFloat + }), r.d(n, "randFloatSpread", function() { + return randFloatSpread + }), r.d(n, "degToRad", function() { + return degToRad + }), r.d(n, "radToDeg", function() { + return radToDeg + }), r.d(n, "isPowerOfTwo", function() { + return isPowerOfTwo + }), r.d(n, "ceilPowerOfTwo", function() { + return ceilPowerOfTwo + }), r.d(n, "floorPowerOfTwo", function() { + return floorPowerOfTwo + }); + var i = r(90); + + function _inheritsLoose(t, e) { + t.prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e + } + + function _defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + for (var o = function() { + function BufferAttribute(t, e, r) { + if (Array.isArray(t)) throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); + this.name = "", this.array = t, this.itemSize = e, this.count = void 0 !== t ? t.length / e : 0, this.normalized = !0 === r, this.dynamic = !1, this.updateRange = { + offset: 0, + count: -1 + }, this.version = 0, this.isBufferAttribute = !0 + } + var t, e, r, n = BufferAttribute.prototype; + return n.onUploadCallback = function() {}, n.setArray = function(t) { + if (Array.isArray(t)) throw new TypeError("THREE.BufferAttribute: array should be a Typed Array."); + return this.count = void 0 !== t ? t.length / this.itemSize : 0, this.array = t, this + }, n.setDynamic = function(t) { + return this.dynamic = t, this + }, n.copy = function(t) { + return this.name = t.name, this.array = new t.array.constructor(t.array), this.itemSize = t.itemSize, this.count = t.count, this.normalized = t.normalized, this.dynamic = t.dynamic, this + }, n.copyAt = function(t, e, r) { + t *= this.itemSize, r *= e.itemSize; + for (var n = 0, i = this.itemSize; n < i; n++) this.array[t + n] = e.array[r + n]; + return this + }, n.copyArray = function(t) { + return this.array.set(t), this + }, n.copyColorsArray = function(t) { + for (var e = this.array, r = 0, n = 0, i = t.length; n < i; n++) { + var o = t[n]; + void 0 === o && (o = new Color), e[r++] = o.r, e[r++] = o.g, e[r++] = o.b + } + return this + }, n.copyVector2sArray = function(t) { + for (var e = this.array, r = 0, n = 0, i = t.length; n < i; n++) { + var o = t[n]; + void 0 === o && (o = new Vector2), e[r++] = o.x, e[r++] = o.y + } + return this + }, n.copyVector3sArray = function(t) { + for (var e = this.array, r = 0, n = 0, i = t.length; n < i; n++) { + var o = t[n]; + void 0 === o && (o = new Vector3), e[r++] = o.x, e[r++] = o.y, e[r++] = o.z + } + return this + }, n.copyVector4sArray = function(t) { + for (var e = this.array, r = 0, n = 0, i = t.length; n < i; n++) { + var o = t[n]; + void 0 === o && (o = new Vector4), e[r++] = o.x, e[r++] = o.y, e[r++] = o.z, e[r++] = o.w + } + return this + }, n.set = function(t, e) { + return void 0 === e && (e = 0), this.array.set(t, e), this + }, n.getX = function(t) { + return this.array[t * this.itemSize] + }, n.setX = function(t, e) { + return this.array[t * this.itemSize] = e, this + }, n.getY = function(t) { + return this.array[t * this.itemSize + 1] + }, n.setY = function(t, e) { + return this.array[t * this.itemSize + 1] = e, this + }, n.getZ = function(t) { + return this.array[t * this.itemSize + 2] + }, n.setZ = function(t, e) { + return this.array[t * this.itemSize + 2] = e, this + }, n.getW = function(t) { + return this.array[t * this.itemSize + 3] + }, n.setW = function(t, e) { + return this.array[t * this.itemSize + 3] = e, this + }, n.setXY = function(t, e, r) { + return t *= this.itemSize, this.array[t + 0] = e, this.array[t + 1] = r, this + }, n.setXYZ = function(t, e, r, n) { + return t *= this.itemSize, this.array[t + 0] = e, this.array[t + 1] = r, this.array[t + 2] = n, this + }, n.setXYZW = function(t, e, r, n, i) { + return t *= this.itemSize, this.array[t + 0] = e, this.array[t + 1] = r, this.array[t + 2] = n, this.array[t + 3] = i, this + }, n.onUpload = function(t) { + return this.onUploadCallback = t, this + }, n.clone = function() { + return new this.constructor(this.array, this.itemSize).copy(this) + }, t = BufferAttribute, (e = [{ + key: "needsUpdate", + set: function(t) { + !0 === t && this.version++ + } + }]) && _defineProperties(t.prototype, e), r && _defineProperties(t, r), BufferAttribute + }(), a = function(n) { + function Float32BufferAttribute(t, e, r) { + return n.call(this, new Float32Array(t), e, r) || this + } + return _inheritsLoose(Float32BufferAttribute, n), Float32BufferAttribute + }(o), s = function(n) { + function Uint16BufferAttribute(t, e, r) { + return n.call(this, new Uint16Array(t), e, r) || this + } + return _inheritsLoose(Uint16BufferAttribute, n), Uint16BufferAttribute + }(o), u = Math.PI / 180, h = 180 / Math.PI, c = [], l = 0; l < 256; l++) c[l] = (l < 16 ? "0" : "") + l.toString(16); + + function generateUUID() { + var t = 4294967295 * Math.random() | 0, + e = 4294967295 * Math.random() | 0, + r = 4294967295 * Math.random() | 0, + n = 4294967295 * Math.random() | 0; + return (c[255 & t] + c[t >> 8 & 255] + c[t >> 16 & 255] + c[t >> 24 & 255] + "-" + c[255 & e] + c[e >> 8 & 255] + "-" + c[e >> 16 & 15 | 64] + c[e >> 24 & 255] + "-" + c[63 & r | 128] + c[r >> 8 & 255] + "-" + c[r >> 16 & 255] + c[r >> 24 & 255] + c[255 & n] + c[n >> 8 & 255] + c[n >> 16 & 255] + c[n >> 24 & 255]).toUpperCase() + } + + function Math_clamp(t, e, r) { + return Math.max(e, Math.min(r, t)) + } + + function euclideanModulo(t, e) { + return (t % e + e) % e + } + + function mapLinear(t, e, r, n, i) { + return n + (t - e) * (i - n) / (r - e) + } + + function lerp(t, e, r) { + return (1 - r) * t + r * e + } + + function smoothstep(t, e, r) { + return t <= e ? 0 : r <= t ? 1 : (t = (t - e) / (r - e)) * t * (3 - 2 * t) + } + + function smootherstep(t, e, r) { + return t <= e ? 0 : r <= t ? 1 : (t = (t - e) / (r - e)) * t * t * (t * (6 * t - 15) + 10) + } + + function randInt(t, e) { + return t + Math.floor(Math.random() * (e - t + 1)) + } + + function randFloat(t, e) { + return t + Math.random() * (e - t) + } + + function randFloatSpread(t) { + return t * (.5 - Math.random()) + } + + function degToRad(t) { + return t * u + } + + function radToDeg(t) { + return t * h + } + + function isPowerOfTwo(t) { + return 0 == (t & t - 1) && 0 !== t + } + + function ceilPowerOfTwo(t) { + return Math.pow(2, Math.ceil(Math.log(t) / Math.LN2)) + } + + function floorPowerOfTwo(t) { + return Math.pow(2, Math.floor(Math.log(t) / Math.LN2)) + } + var f = { + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }; + + function hue2rgb(t, e, r) { + return r < 0 && (r += 1), 1 < r && (r -= 1), r < 1 / 6 ? t + 6 * (e - t) * r : r < .5 ? e : r < 2 / 3 ? t + 6 * (e - t) * (2 / 3 - r) : t + } + + function SRGBToLinear(t) { + return t < .04045 ? .0773993808 * t : Math.pow(.9478672986 * t + .0521327014, 2.4) + } + + function LinearToSRGB(t) { + return t < .0031308 ? 12.92 * t : 1.055 * Math.pow(t, .41666) - .055 + } + var d = { + h: 0, + s: 0, + l: 0 + }, + p = { + h: 0, + s: 0, + l: 0 + }, + m = function() { + function Color() {} + var t = Color.prototype; + return t.copnstructor = function(t, e, r) { + return this.isColor = !0, this.r = 1, this.g = 1, this.b = 1, void 0 === e && void 0 === r ? this.set(t) : this.setRGB(t, e, r) + }, t.set = function(t) { + return t && t.isColor ? this.copy(t) : "number" == typeof t ? this.setHex(t) : "string" == typeof t && this.setStyle(t), this + }, t.setScalar = function(t) { + return this.r = t, this.g = t, this.b = t, this + }, t.setHex = function(t) { + return t = Math.floor(t), this.r = (t >> 16 & 255) / 255, this.g = (t >> 8 & 255) / 255, this.b = (255 & t) / 255, this + }, t.setRGB = function(t, e, r) { + return this.r = t, this.g = e, this.b = r, this + }, t.setHSL = function(t, e, r) { + if (t = euclideanModulo(t, 1), e = Math_clamp(e, 0, 1), r = Math_clamp(r, 0, 1), 0 === e) this.r = this.g = this.b = r; + else { + var n = r <= .5 ? r * (1 + e) : r + e - r * e, + i = 2 * r - n; + this.r = hue2rgb(i, n, t + 1 / 3), this.g = hue2rgb(i, n, t), this.b = hue2rgb(i, n, t - 1 / 3) + } + return this + }, t.setStyle = function(t) { + function handleAlpha(t) { + void 0 !== t && parseFloat(t) + } + var e; + if (e = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(t)) { + var r, n = e[1], + i = e[2]; + switch (n) { + case "rgb": + case "rgba": + if (r = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(i)) return this.r = Math.min(255, parseInt(r[1], 10)) / 255, this.g = Math.min(255, parseInt(r[2], 10)) / 255, this.b = Math.min(255, parseInt(r[3], 10)) / 255, handleAlpha(r[5]), this; + if (r = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(i)) return this.r = Math.min(100, parseInt(r[1], 10)) / 100, this.g = Math.min(100, parseInt(r[2], 10)) / 100, this.b = Math.min(100, parseInt(r[3], 10)) / 100, handleAlpha(r[5]), this; + break; + case "hsl": + case "hsla": + if (r = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(i)) { + var o = parseFloat(r[1]) / 360, + a = parseInt(r[2], 10) / 100, + s = parseInt(r[3], 10) / 100; + return handleAlpha(r[5]), this.setHSL(o, a, s) + } + } + } else if (e = /^\#([A-Fa-f0-9]+)$/.exec(t)) { + var u = e[1], + h = u.length; + if (3 === h) return this.r = parseInt(u.charAt(0) + u.charAt(0), 16) / 255, this.g = parseInt(u.charAt(1) + u.charAt(1), 16) / 255, this.b = parseInt(u.charAt(2) + u.charAt(2), 16) / 255, this; + if (6 === h) return this.r = parseInt(u.charAt(0) + u.charAt(1), 16) / 255, this.g = parseInt(u.charAt(2) + u.charAt(3), 16) / 255, this.b = parseInt(u.charAt(4) + u.charAt(5), 16) / 255, this + } + if (t && 0 < t.length) { + var c = f[t]; + void 0 !== c && this.setHex(c) + } + return this + }, t.clone = function() { + return new this.constructor(this.r, this.g, this.b) + }, t.copy = function(t) { + return this.r = t.r, this.g = t.g, this.b = t.b, this + }, t.copyGammaToLinear = function(t, e) { + return void 0 === e && (e = 2), this.r = Math.pow(t.r, e), this.g = Math.pow(t.g, e), this.b = Math.pow(t.b, e), this + }, t.copyLinearToGamma = function(t, e) { + void 0 === e && (e = 2); + var r = 0 < e ? 1 / e : 1; + return this.r = Math.pow(t.r, r), this.g = Math.pow(t.g, r), this.b = Math.pow(t.b, r), this + }, t.convertGammaToLinear = function(t) { + return this.copyGammaToLinear(this, t), this + }, t.convertLinearToGamma = function(t) { + return this.copyLinearToGamma(this, t), this + }, t.copySRGBToLinear = function(t) { + return this.r = SRGBToLinear(t.r), this.g = SRGBToLinear(t.g), this.b = SRGBToLinear(t.b), this + }, t.copyLinearToSRGB = function(t) { + this.r = LinearToSRGB(t.r), this.g = LinearToSRGB(t.g), this.b = LinearToSRGB(t.b) + }, t.convertSRGBToLinear = function() { + return this.copySRGBToLinear(this), this + }, t.convertLinearToSRGB = function() { + return this.copyLinearToSRGB(this), this + }, t.getHex = function() { + return 255 * this.r << 16 ^ 255 * this.g << 8 ^ 255 * this.b << 0 + }, t.getHexString = function() { + return ("000000" + this.getHex().toString(16)).slice(-6) + }, t.getHSL = function(t) { + void 0 === t && (t = { + h: 0, + s: 0, + l: 0 + }); + var e, r, n = this.r, + i = this.g, + o = this.b, + a = Math.max(n, i, o), + s = Math.min(n, i, o), + u = (s + a) / 2; + if (s === a) r = e = 0; + else { + var h = a - s; + switch (r = u <= .5 ? h / (a + s) : h / (2 - a - s), a) { + case n: + e = (i - o) / h + (i < o ? 6 : 0); + break; + case i: + e = (o - n) / h + 2; + break; + case o: + e = (n - i) / h + 4 + } + e /= 6 + } + return t.h = e, t.s = r, t.l = u, t + }, t.getStyle = function() { + return "rgb(" + (255 * this.r | 0) + "," + (255 * this.g | 0) + "," + (255 * this.b | 0) + ")" + }, t.offsetHSL = function(t, e, r) { + var n = tempHsl; + return this.getHSL(n), n.h += t, n.s += e, n.l += r, this.setHSL(n.h, n.s, n.l), this + }, t.add = function(t) { + return this.r += t.r, this.g += t.g, this.b += t.b, this + }, t.addColors = function(t, e) { + return this.r = t.r + e.r, this.g = t.g + e.g, this.b = t.b + e.b, this + }, t.addScalar = function(t) { + return this.r += t, this.g += t, this.b += t, this + }, t.sub = function(t) { + return this.r = Math.max(0, this.r - t.r), this.g = Math.max(0, this.g - t.g), this.b = Math.max(0, this.b - t.b), this + }, t.multiply = function(t) { + return this.r *= t.r, this.g *= t.g, this.b *= t.b, this + }, t.multiplyScalar = function(t) { + return this.r *= t, this.g *= t, this.b *= t, this + }, t.lerp = function(t, e) { + return this.r += (t.r - this.r) * e, this.g += (t.g - this.g) * e, this.b += (t.b - this.b) * e, this + }, t.lerpHSL = function(t, e) { + var r = d, + n = p; + this.getHSL(r), t.getHSL(n); + var i = lerp(r.h, n.h, e), + o = lerp(r.s, n.s, e), + a = lerp(r.l, n.l, e); + return this.setHSL(i, o, a), this + }, t.equals = function(t) { + return t.r === this.r && t.g === this.g && t.b === this.b + }, t.fromArray = function(t, e) { + return void 0 === e && (e = 0), this.r = t[e], this.g = t[e + 1], this.b = t[e + 2], this + }, t.toArray = function(t, e) { + return void 0 === t && (t = []), void 0 === e && (e = 0), t[e] = this.r, t[e + 1] = this.g, t[e + 2] = this.b, t + }, t.toJSON = function() { + return this.getHex() + }, Color + }(), + v = function() { + function Vector3(t, e, r) { + this.x = t || 0, this.y = e || 0, this.z = r || 0, this.isVector3 = !0 + } + var t = Vector3.prototype; + return t.set = function(t, e, r) { + return this.x = t, this.y = e, this.z = r, this + }, t.setScalar = function(t) { + return this.x = t, this.y = t, this.z = t, this + }, t.setX = function(t) { + return this.x = t, this + }, t.setY = function(t) { + return this.y = t, this + }, t.setZ = function(t) { + return this.z = t, this + }, t.setComponent = function(t, e) { + switch (t) { + case 0: + this.x = e; + break; + case 1: + this.y = e; + break; + case 2: + this.z = e; + break; + default: + throw new Error("index is out of range: " + t) + } + return this + }, t.getComponent = function(t) { + switch (t) { + case 0: + return this.x; + case 1: + return this.y; + case 2: + return this.z; + default: + throw new Error("index is out of range: " + t) + } + }, t.clone = function() { + return new this.constructor(this.x, this.y, this.z) + }, t.copy = function(t) { + return this.x = t.x, this.y = t.y, this.z = t.z, this + }, t.add = function(t, e) { + return void 0 !== e ? this.addVectors(t, e) : (this.x += t.x, this.y += t.y, this.z += t.z, this) + }, t.addScalar = function(t) { + return this.x += t, this.y += t, this.z += t, this + }, t.addVectors = function(t, e) { + return this.x = t.x + e.x, this.y = t.y + e.y, this.z = t.z + e.z, this + }, t.addScaledVector = function(t, e) { + return this.x += t.x * e, this.y += t.y * e, this.z += t.z * e, this + }, t.sub = function(t, e) { + return void 0 !== e ? this.subVectors(t, e) : (this.x -= t.x, this.y -= t.y, this.z -= t.z, this) + }, t.subScalar = function(t) { + return this.x -= t, this.y -= t, this.z -= t, this + }, t.subVectors = function(t, e) { + return this.x = t.x - e.x, this.y = t.y - e.y, this.z = t.z - e.z, this + }, t.multiply = function(t, e) { + return void 0 !== e ? this.multiplyVectors(t, e) : (this.x *= t.x, this.y *= t.y, this.z *= t.z, this) + }, t.multiplyScalar = function(t) { + return this.x *= t, this.y *= t, this.z *= t, this + }, t.multiplyVectors = function(t, e) { + return this.x = t.x * e.x, this.y = t.y * e.y, this.z = t.z * e.z, this + }, t.applyMatrix3 = function(t) { + var e = this.x, + r = this.y, + n = this.z, + i = t.elements; + return this.x = i[0] * e + i[3] * r + i[6] * n, this.y = i[1] * e + i[4] * r + i[7] * n, this.z = i[2] * e + i[5] * r + i[8] * n, this + }, t.applyMatrix4 = function(t) { + var e = this.x, + r = this.y, + n = this.z, + i = t.elements, + o = 1 / (i[3] * e + i[7] * r + i[11] * n + i[15]); + return this.x = (i[0] * e + i[4] * r + i[8] * n + i[12]) * o, this.y = (i[1] * e + i[5] * r + i[9] * n + i[13]) * o, this.z = (i[2] * e + i[6] * r + i[10] * n + i[14]) * o, this + }, t.applyQuaternion = function(t) { + var e = this.x, + r = this.y, + n = this.z, + i = t.x, + o = t.y, + a = t.z, + s = t.w, + u = s * e + o * n - a * r, + h = s * r + a * e - i * n, + c = s * n + i * r - o * e, + l = -i * e - o * r - a * n; + return this.x = u * s + l * -i + h * -a - c * -o, this.y = h * s + l * -o + c * -i - u * -a, this.z = c * s + l * -a + u * -o - h * -i, this + }, t.transformDirection = function(t) { + var e = this.x, + r = this.y, + n = this.z, + i = t.elements; + return this.x = i[0] * e + i[4] * r + i[8] * n, this.y = i[1] * e + i[5] * r + i[9] * n, this.z = i[2] * e + i[6] * r + i[10] * n, this.normalize() + }, t.divide = function(t) { + return this.x /= t.x, this.y /= t.y, this.z /= t.z, this + }, t.divideScalar = function(t) { + return this.multiplyScalar(1 / t) + }, t.min = function(t) { + return this.x = Math.min(this.x, t.x), this.y = Math.min(this.y, t.y), this.z = Math.min(this.z, t.z), this + }, t.max = function(t) { + return this.x = Math.max(this.x, t.x), this.y = Math.max(this.y, t.y), this.z = Math.max(this.z, t.z), this + }, t.clamp = function(t, e) { + return this.x = Math.max(t.x, Math.min(e.x, this.x)), this.y = Math.max(t.y, Math.min(e.y, this.y)), this.z = Math.max(t.z, Math.min(e.z, this.z)), this + }, t.clampScalar = function(t, e) { + var r = g, + n = y; + return r.set(t, t, t), n.set(e, e, e), this.clamp(r, n) + }, t.clampLength = function(t, e) { + var r = this.length(); + return this.divideScalar(r || 1).multiplyScalar(Math.max(t, Math.min(e, r))) + }, t.floor = function() { + return this.x = Math.floor(this.x), this.y = Math.floor(this.y), this.z = Math.floor(this.z), this + }, t.ceil = function() { + return this.x = Math.ceil(this.x), this.y = Math.ceil(this.y), this.z = Math.ceil(this.z), this + }, t.round = function() { + return this.x = Math.round(this.x), this.y = Math.round(this.y), this.z = Math.round(this.z), this + }, t.roundToZero = function() { + return this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x), this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y), this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z), this + }, t.negate = function() { + return this.x = -this.x, this.y = -this.y, this.z = -this.z, this + }, t.dot = function(t) { + return this.x * t.x + this.y * t.y + this.z * t.z + }, t.lengthSq = function() { + return this.x * this.x + this.y * this.y + this.z * this.z + }, t.length = function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z) + }, t.manhattanLength = function() { + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + }, t.normalize = function() { + return this.divideScalar(this.length() || 1) + }, t.setLength = function(t) { + return this.normalize().multiplyScalar(t) + }, t.lerp = function(t, e) { + return this.x += (t.x - this.x) * e, this.y += (t.y - this.y) * e, this.z += (t.z - this.z) * e, this + }, t.lerpVectors = function(t, e, r) { + return this.subVectors(e, t).multiplyScalar(r).add(t) + }, t.cross = function(t, e) { + return void 0 !== e ? this.crossVectors(t, e) : this.crossVectors(this, t) + }, t.crossVectors = function(t, e) { + var r = t.x, + n = t.y, + i = t.z, + o = e.x, + a = e.y, + s = e.z; + return this.x = n * s - i * a, this.y = i * o - r * s, this.z = r * a - n * o, this + }, t.projectOnVector = function(t) { + var e = t.dot(this) / t.lengthSq(); + return this.copy(t).multiplyScalar(e) + }, t.projectOnPlane = function(t) { + var e = g; + return e.copy(this).projectOnVector(t), this.sub(e) + }, t.reflect = function(t) { + var e = new g; + return this.sub(e.copy(t).multiplyScalar(2 * this.dot(t))) + }, t.angleTo = function(t) { + var e = this.dot(t) / Math.sqrt(this.lengthSq() * t.lengthSq()); + return Math.acos(Math_clamp(e, -1, 1)) + }, t.distanceTo = function(t) { + return Math.sqrt(this.distanceToSquared(t)) + }, t.distanceToSquared = function(t) { + var e = this.x - t.x, + r = this.y - t.y, + n = this.z - t.z; + return e * e + r * r + n * n + }, t.manhattanDistanceTo = function(t) { + return Math.abs(this.x - t.x) + Math.abs(this.y - t.y) + Math.abs(this.z - t.z) + }, t.setFromSpherical = function(t) { + return this.setFromSphericalCoords(t.radius, t.phi, t.theta) + }, t.setFromSphericalCoords = function(t, e, r) { + var n = Math.sin(e) * t; + return this.x = n * Math.sin(r), this.y = Math.cos(e) * t, this.z = n * Math.cos(r), this + }, t.setFromCylindrical = function(t) { + return this.setFromCylindricalCoords(t.radius, t.theta, t.y) + }, t.setFromCylindricalCoords = function(t, e, r) { + return this.x = t * Math.sin(e), this.y = r, this.z = t * Math.cos(e), this + }, t.setFromMatrixPosition = function(t) { + var e = t.elements; + return this.x = e[12], this.y = e[13], this.z = e[14], this + }, t.setFromMatrixScale = function(t) { + var e = this.setFromMatrixColumn(t, 0).length(), + r = this.setFromMatrixColumn(t, 1).length(), + n = this.setFromMatrixColumn(t, 2).length(); + return this.x = e, this.y = r, this.z = n, this + }, t.setFromMatrixColumn = function(t, e) { + return this.fromArray(t.elements, 4 * e) + }, t.equals = function(t) { + return t.x === this.x && t.y === this.y && t.z === this.z + }, t.fromArray = function(t, e) { + return void 0 === e && (e = 0), this.x = t[e], this.y = t[e + 1], this.z = t[e + 2], this + }, t.toArray = function(t, e) { + return void 0 === t && (t = []), void 0 === e && (e = 0), t[e] = this.x, t[e + 1] = this.y, t[e + 2] = this.z, t + }, t.fromBufferAttribute = function(t, e, r) { + return this.x = t.getX(e), this.y = t.getY(e), this.z = t.getZ(e), this + }, Vector3 + }(), + g = new v, + y = new v, + _ = function() { + function Vector4(t, e, r, n) { + this.x = t || 0, this.y = e || 0, this.z = r || 0, this.w = void 0 !== n ? n : 1, this.isVector4 = !0 + } + var t = Vector4.prototype; + return t.set = function(t, e, r, n) { + return this.x = t, this.y = e, this.z = r, this.w = n, this + }, t.setScalar = function(t) { + return this.x = t, this.y = t, this.z = t, this.w = t, this + }, t.setX = function(t) { + return this.x = t, this + }, t.setY = function(t) { + return this.y = t, this + }, t.setZ = function(t) { + return this.z = t, this + }, t.setW = function(t) { + return this.w = t, this + }, t.setComponent = function(t, e) { + switch (t) { + case 0: + this.x = e; + break; + case 1: + this.y = e; + break; + case 2: + this.z = e; + break; + case 3: + this.w = e; + break; + default: + throw new Error("index is out of range: " + t) + } + return this + }, t.getComponent = function(t) { + switch (t) { + case 0: + return this.x; + case 1: + return this.y; + case 2: + return this.z; + case 3: + return this.w; + default: + throw new Error("index is out of range: " + t) + } + }, t.clone = function() { + return new this.constructor(this.x, this.y, this.z, this.w) + }, t.copy = function(t) { + return this.x = t.x, this.y = t.y, this.z = t.z, this.w = void 0 !== t.w ? t.w : 1, this + }, t.add = function(t, e) { + return void 0 !== e ? this.addVectors(t, e) : (this.x += t.x, this.y += t.y, this.z += t.z, this.w += t.w, this) + }, t.addScalar = function(t) { + return this.x += t, this.y += t, this.z += t, this.w += t, this + }, t.addVectors = function(t, e) { + return this.x = t.x + e.x, this.y = t.y + e.y, this.z = t.z + e.z, this.w = t.w + e.w, this + }, t.addScaledVector = function(t, e) { + return this.x += t.x * e, this.y += t.y * e, this.z += t.z * e, this.w += t.w * e, this + }, t.sub = function(t, e) { + return void 0 !== e ? this.subVectors(t, e) : (this.x -= t.x, this.y -= t.y, this.z -= t.z, this.w -= t.w, this) + }, t.subScalar = function(t) { + return this.x -= t, this.y -= t, this.z -= t, this.w -= t, this + }, t.subVectors = function(t, e) { + return this.x = t.x - e.x, this.y = t.y - e.y, this.z = t.z - e.z, this.w = t.w - e.w, this + }, t.multiplyScalar = function(t) { + return this.x *= t, this.y *= t, this.z *= t, this.w *= t, this + }, t.applyMatrix4 = function(t) { + var e = this.x, + r = this.y, + n = this.z, + i = this.w, + o = t.elements; + return this.x = o[0] * e + o[4] * r + o[8] * n + o[12] * i, this.y = o[1] * e + o[5] * r + o[9] * n + o[13] * i, this.z = o[2] * e + o[6] * r + o[10] * n + o[14] * i, this.w = o[3] * e + o[7] * r + o[11] * n + o[15] * i, this + }, t.divideScalar = function(t) { + return this.multiplyScalar(1 / t) + }, t.setAxisAngleFromQuaternion = function(t) { + this.w = 2 * Math.acos(t.w); + var e = Math.sqrt(1 - t.w * t.w); + return this.z = e < 1e-4 ? (this.x = 1, this.y = 0) : (this.x = t.x / e, this.y = t.y / e, t.z / e), this + }, t.setAxisAngleFromRotationMatrix = function(t) { + var e, r, n, i, o = t.elements, + a = o[0], + s = o[4], + u = o[8], + h = o[1], + c = o[5], + l = o[9], + f = o[2], + d = o[6], + p = o[10]; + if (Math.abs(s - h) < .01 && Math.abs(u - f) < .01 && Math.abs(l - d) < .01) { + if (Math.abs(s + h) < .1 && Math.abs(u + f) < .1 && Math.abs(l + d) < .1 && Math.abs(a + c + p - 3) < .1) return this.set(1, 0, 0, 0), this; + e = Math.PI; + var m = (a + 1) / 2, + v = (c + 1) / 2, + g = (p + 1) / 2, + y = (s + h) / 4, + _ = (u + f) / 4, + b = (l + d) / 4; + return v < m && g < m ? i = m < .01 ? (r = 0, n = .707106781) : (n = y / (r = Math.sqrt(m)), _ / r) : g < v ? i = v < .01 ? (n = 0, r = .707106781) : (r = y / (n = Math.sqrt(v)), b / n) : g < .01 ? (n = r = .707106781, i = 0) : (r = _ / (i = Math.sqrt(g)), n = b / i), this.set(r, n, i, e), this + } + var x = Math.sqrt((d - l) * (d - l) + (u - f) * (u - f) + (h - s) * (h - s)); + return Math.abs(x) < .001 && (x = 1), this.x = (d - l) / x, this.y = (u - f) / x, this.z = (h - s) / x, this.w = Math.acos((a + c + p - 1) / 2), this + }, t.min = function(t) { + return this.x = Math.min(this.x, t.x), this.y = Math.min(this.y, t.y), this.z = Math.min(this.z, t.z), this.w = Math.min(this.w, t.w), this + }, t.max = function(t) { + return this.x = Math.max(this.x, t.x), this.y = Math.max(this.y, t.y), this.z = Math.max(this.z, t.z), this.w = Math.max(this.w, t.w), this + }, t.clamp = function(t, e) { + return this.x = Math.max(t.x, Math.min(e.x, this.x)), this.y = Math.max(t.y, Math.min(e.y, this.y)), this.z = Math.max(t.z, Math.min(e.z, this.z)), this.w = Math.max(t.w, Math.min(e.w, this.w)), this + }, t.clampScalar = function(t, e) { + var r = b, + n = x; + return r.set(t, t, t, t), n.set(e, e, e, e), this.clamp(r, n) + }, t.clampLength = function(t, e) { + var r = this.length(); + return this.divideScalar(r || 1).multiplyScalar(Math.max(t, Math.min(e, r))) + }, t.floor = function() { + return this.x = Math.floor(this.x), this.y = Math.floor(this.y), this.z = Math.floor(this.z), this.w = Math.floor(this.w), this + }, t.ceil = function() { + return this.x = Math.ceil(this.x), this.y = Math.ceil(this.y), this.z = Math.ceil(this.z), this.w = Math.ceil(this.w), this + }, t.round = function() { + return this.x = Math.round(this.x), this.y = Math.round(this.y), this.z = Math.round(this.z), this.w = Math.round(this.w), this + }, t.roundToZero = function() { + return this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x), this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y), this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z), this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w), this + }, t.negate = function() { + return this.x = -this.x, this.y = -this.y, this.z = -this.z, this.w = -this.w, this + }, t.dot = function(t) { + return this.x * t.x + this.y * t.y + this.z * t.z + this.w * t.w + }, t.lengthSq = function() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w + }, t.length = function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w) + }, t.manhattanLength = function() { + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w) + }, t.normalize = function() { + return this.divideScalar(this.length() || 1) + }, t.setLength = function(t) { + return this.normalize().multiplyScalar(t) + }, t.lerp = function(t, e) { + return this.x += (t.x - this.x) * e, this.y += (t.y - this.y) * e, this.z += (t.z - this.z) * e, this.w += (t.w - this.w) * e, this + }, t.lerpVectors = function(t, e, r) { + return this.subVectors(e, t).multiplyScalar(r).add(t) + }, t.equals = function(t) { + return t.x === this.x && t.y === this.y && t.z === this.z && t.w === this.w + }, t.fromArray = function(t, e) { + return void 0 === e && (e = 0), this.x = t[e], this.y = t[e + 1], this.z = t[e + 2], this.w = t[e + 3], this + }, t.toArray = function(t, e) { + return void 0 === t && (t = []), void 0 === e && (e = 0), t[e] = this.x, t[e + 1] = this.y, t[e + 2] = this.z, t[e + 3] = this.w, t + }, t.fromBufferAttribute = function(t, e, r) { + return this.x = t.getX(e), this.y = t.getY(e), this.z = t.getZ(e), this.w = t.getW(e), this + }, Vector4 + }(), + b = new _, + x = new _, + w = function() { + function Matrix3() { + this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1], arguments.length, this.isMatrix3 = !0 + } + var t = Matrix3.prototype; + return t.set = function(t, e, r, n, i, o, a, s, u) { + var h = this.elements; + return h[0] = t, h[1] = n, h[2] = a, h[3] = e, h[4] = i, h[5] = s, h[6] = r, h[7] = o, h[8] = u, this + }, t.identity = function() { + return this.set(1, 0, 0, 0, 1, 0, 0, 0, 1), this + }, t.clone = function() { + return (new this.constructor).fromArray(this.elements) + }, t.copy = function(t) { + var e = this.elements, + r = t.elements; + return e[0] = r[0], e[1] = r[1], e[2] = r[2], e[3] = r[3], e[4] = r[4], e[5] = r[5], e[6] = r[6], e[7] = r[7], e[8] = r[8], this + }, t.setFromMatrix4 = function(t) { + var e = t.elements; + return this.set(e[0], e[4], e[8], e[1], e[5], e[9], e[2], e[6], e[10]), this + }, t.applyToBufferAttribute = function(t) { + for (var e = T, r = 0, n = t.count; r < n; r++) e.x = t.getX(r), e.y = t.getY(r), e.z = t.getZ(r), e.applyMatrix3(this), t.setXYZ(r, e.x, e.y, e.z); + return t + }, t.multiply = function(t) { + return this.multiplyMatrices(this, t) + }, t.premultiply = function(t) { + return this.multiplyMatrices(t, this) + }, t.multiplyMatrices = function(t, e) { + var r = t.elements, + n = e.elements, + i = this.elements, + o = r[0], + a = r[3], + s = r[6], + u = r[1], + h = r[4], + c = r[7], + l = r[2], + f = r[5], + d = r[8], + p = n[0], + m = n[3], + v = n[6], + g = n[1], + y = n[4], + _ = n[7], + b = n[2], + x = n[5], + w = n[8]; + return i[0] = o * p + a * g + s * b, i[3] = o * m + a * y + s * x, i[6] = o * v + a * _ + s * w, i[1] = u * p + h * g + c * b, i[4] = u * m + h * y + c * x, i[7] = u * v + h * _ + c * w, i[2] = l * p + f * g + d * b, i[5] = l * m + f * y + d * x, i[8] = l * v + f * _ + d * w, this + }, t.multiplyScalar = function(t) { + var e = this.elements; + return e[0] *= t, e[3] *= t, e[6] *= t, e[1] *= t, e[4] *= t, e[7] *= t, e[2] *= t, e[5] *= t, e[8] *= t, this + }, t.determinant = function() { + var t = this.elements, + e = t[0], + r = t[1], + n = t[2], + i = t[3], + o = t[4], + a = t[5], + s = t[6], + u = t[7], + h = t[8]; + return e * o * h - e * a * u - r * i * h + r * a * s + n * i * u - n * o * s + }, t.getInverse = function(t, e) { + t && t.isMatrix4; + var r = t.elements, + n = this.elements, + i = r[0], + o = r[1], + a = r[2], + s = r[3], + u = r[4], + h = r[5], + c = r[6], + l = r[7], + f = r[8], + d = f * u - h * l, + p = h * c - f * s, + m = l * s - u * c, + v = i * d + o * p + a * m; + if (0 === v) { + var g = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; + if (!0 === e) throw new Error(g); + return this.identity() + } + var y = 1 / v; + return n[0] = d * y, n[1] = (a * l - f * o) * y, n[2] = (h * o - a * u) * y, n[3] = p * y, n[4] = (f * i - a * c) * y, n[5] = (a * s - h * i) * y, n[6] = m * y, n[7] = (o * c - l * i) * y, n[8] = (u * i - o * s) * y, this + }, t.transpose = function() { + var t, e = this.elements; + return t = e[1], e[1] = e[3], e[3] = t, t = e[2], e[2] = e[6], e[6] = t, t = e[5], e[5] = e[7], e[7] = t, this + }, t.getNormalMatrix = function(t) { + return this.setFromMatrix4(t).getInverse(this).transpose() + }, t.transposeIntoArray = function(t) { + var e = this.elements; + return t[0] = e[0], t[1] = e[3], t[2] = e[6], t[3] = e[1], t[4] = e[4], t[5] = e[7], t[6] = e[2], t[7] = e[5], t[8] = e[8], this + }, t.setUvTransform = function(t, e, r, n, i, o, a) { + var s = Math.cos(i), + u = Math.sin(i); + this.set(r * s, r * u, -r * (s * o + u * a) + o + t, -n * u, n * s, -n * (-u * o + s * a) + a + e, 0, 0, 1) + }, t.scale = function(t, e) { + var r = this.elements; + return r[0] *= t, r[3] *= t, r[6] *= t, r[1] *= e, r[4] *= e, r[7] *= e, this + }, t.rotate = function(t) { + var e = Math.cos(t), + r = Math.sin(t), + n = this.elements, + i = n[0], + o = n[3], + a = n[6], + s = n[1], + u = n[4], + h = n[7]; + return n[0] = e * i + r * s, n[3] = e * o + r * u, n[6] = e * a + r * h, n[1] = -r * i + e * s, n[4] = -r * o + e * u, n[7] = -r * a + e * h, this + }, t.translate = function(t, e) { + var r = this.elements; + return r[0] += t * r[2], r[3] += t * r[5], r[6] += t * r[8], r[1] += e * r[2], r[4] += e * r[5], r[7] += e * r[8], this + }, t.equals = function(t) { + for (var e = this.elements, r = t.elements, n = 0; n < 9; n++) + if (e[n] !== r[n]) return !1; + return !0 + }, t.fromArray = function(t, e) { + void 0 === e && (e = 0); + for (var r = 0; r < 9; r++) this.elements[r] = t[r + e]; + return this + }, t.toArray = function(t, e) { + void 0 === t && (t = []), void 0 === e && (e = 0); + var r = this.elements; + return t[e] = r[0], t[e + 1] = r[1], t[e + 2] = r[2], t[e + 3] = r[3], t[e + 4] = r[4], t[e + 5] = r[5], t[e + 6] = r[6], t[e + 7] = r[7], t[e + 8] = r[8], t + }, Matrix3 + }(), + T = new v, + S = function() { + function Matrix4() { + this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], arguments.length, this.isMatrix4 = !0 + } + var t = Matrix4.prototype; + return t.set = function(t, e, r, n, i, o, a, s, u, h, c, l, f, d, p, m) { + var v = this.elements; + return v[0] = t, v[4] = e, v[8] = r, v[12] = n, v[1] = i, v[5] = o, v[9] = a, v[13] = s, v[2] = u, v[6] = h, v[10] = c, v[14] = l, v[3] = f, v[7] = d, v[11] = p, v[15] = m, this + }, t.identity = function() { + return this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), this + }, t.clone = function() { + return (new Matrix4).fromArray(this.elements) + }, t.copy = function(t) { + var e = this.elements, + r = t.elements; + return e[0] = r[0], e[1] = r[1], e[2] = r[2], e[3] = r[3], e[4] = r[4], e[5] = r[5], e[6] = r[6], e[7] = r[7], e[8] = r[8], e[9] = r[9], e[10] = r[10], e[11] = r[11], e[12] = r[12], e[13] = r[13], e[14] = r[14], e[15] = r[15], this + }, t.copyPosition = function(t) { + var e = this.elements, + r = t.elements; + return e[12] = r[12], e[13] = r[13], e[14] = r[14], this + }, t.extractBasis = function(t, e, r) { + return t.setFromMatrixColumn(this, 0), e.setFromMatrixColumn(this, 1), r.setFromMatrixColumn(this, 2), this + }, t.makeBasis = function(t, e, r) { + return this.set(t.x, e.x, r.x, 0, t.y, e.y, r.y, 0, t.z, e.z, r.z, 0, 0, 0, 0, 1), this + }, t.extractRotation = function(t) { + var e = E, + r = this.elements, + n = t.elements, + i = 1 / e.setFromMatrixColumn(t, 0).length(), + o = 1 / e.setFromMatrixColumn(t, 1).length(), + a = 1 / e.setFromMatrixColumn(t, 2).length(); + return r[0] = n[0] * i, r[1] = n[1] * i, r[2] = n[2] * i, r[3] = 0, r[4] = n[4] * o, r[5] = n[5] * o, r[6] = n[6] * o, r[7] = 0, r[8] = n[8] * a, r[9] = n[9] * a, r[10] = n[10] * a, r[11] = 0, r[12] = 0, r[13] = 0, r[14] = 0, r[15] = 1, this + }, t.makeRotationFromEuler = function(t) { + !t || t.isEuler; + var e = this.elements, + r = t.x, + n = t.y, + i = t.z, + o = Math.cos(r), + a = Math.sin(r), + s = Math.cos(n), + u = Math.sin(n), + h = Math.cos(i), + c = Math.sin(i); + if ("XYZ" === t.order) { + var l = o * h, + f = o * c, + d = a * h, + p = a * c; + e[0] = s * h, e[4] = -s * c, e[8] = u, e[1] = f + d * u, e[5] = l - p * u, e[9] = -a * s, e[2] = p - l * u, e[6] = d + f * u, e[10] = o * s + } else if ("YXZ" === t.order) { + var m = s * h, + v = s * c, + g = u * h, + y = u * c; + e[0] = m + y * a, e[4] = g * a - v, e[8] = o * u, e[1] = o * c, e[5] = o * h, e[9] = -a, e[2] = v * a - g, e[6] = y + m * a, e[10] = o * s + } else if ("ZXY" === t.order) { + var _ = s * h, + b = s * c, + x = u * h, + w = u * c; + e[0] = _ - w * a, e[4] = -o * c, e[8] = x + b * a, e[1] = b + x * a, e[5] = o * h, e[9] = w - _ * a, e[2] = -o * u, e[6] = a, e[10] = o * s + } else if ("ZYX" === t.order) { + var T = o * h, + S = o * c, + M = a * h, + E = a * c; + e[0] = s * h, e[4] = M * u - S, e[8] = T * u + E, e[1] = s * c, e[5] = E * u + T, e[9] = S * u - M, e[2] = -u, e[6] = a * s, e[10] = o * s + } else if ("YZX" === t.order) { + var A = o * s, + P = o * u, + I = a * s, + O = a * u; + e[0] = s * h, e[4] = O - A * c, e[8] = I * c + P, e[1] = c, e[5] = o * h, e[9] = -a * h, e[2] = -u * h, e[6] = P * c + I, e[10] = A - O * c + } else if ("XZY" === t.order) { + var C = o * s, + R = o * u, + D = a * s, + F = a * u; + e[0] = s * h, e[4] = -c, e[8] = u * h, e[1] = C * c + F, e[5] = o * h, e[9] = R * c - D, e[2] = D * c - R, e[6] = a * h, e[10] = F * c + C + } + return e[3] = 0, e[7] = 0, e[11] = 0, e[12] = 0, e[13] = 0, e[14] = 0, e[15] = 1, this + }, t.makeRotationFromQuaternion = function(t) { + return this.compose(I, t, O) + }, t.lookAt = function() { + var t = E, + e = A, + r = P, + n = this.elements; + return r.subVectors(eye, target), 0 === r.lengthSq() && (r.z = 1), r.normalize(), t.crossVectors(up, r), 0 === t.lengthSq() && (1 === Math.abs(up.z) ? r.x += 1e-4 : r.z += 1e-4, r.normalize(), t.crossVectors(up, r)), t.normalize(), e.crossVectors(r, t), n[0] = t.x, n[4] = e.x, n[8] = r.x, n[1] = t.y, n[5] = e.y, n[9] = r.y, n[2] = t.z, n[6] = e.z, n[10] = r.z, this + }, t.multiply = function(t, e) { + return void 0 !== e ? this.multiplyMatrices(t, e) : this.multiplyMatrices(this, t) + }, t.premultiply = function(t) { + return this.multiplyMatrices(t, this) + }, t.multiplyMatrices = function(t, e) { + var r = t.elements, + n = e.elements, + i = this.elements, + o = r[0], + a = r[4], + s = r[8], + u = r[12], + h = r[1], + c = r[5], + l = r[9], + f = r[13], + d = r[2], + p = r[6], + m = r[10], + v = r[14], + g = r[3], + y = r[7], + _ = r[11], + b = r[15], + x = n[0], + w = n[4], + T = n[8], + S = n[12], + M = n[1], + E = n[5], + A = n[9], + P = n[13], + I = n[2], + O = n[6], + C = n[10], + R = n[14], + D = n[3], + F = n[7], + k = n[11], + L = n[15]; + return i[0] = o * x + a * M + s * I + u * D, i[4] = o * w + a * E + s * O + u * F, i[8] = o * T + a * A + s * C + u * k, i[12] = o * S + a * P + s * R + u * L, i[1] = h * x + c * M + l * I + f * D, i[5] = h * w + c * E + l * O + f * F, i[9] = h * T + c * A + l * C + f * k, i[13] = h * S + c * P + l * R + f * L, i[2] = d * x + p * M + m * I + v * D, i[6] = d * w + p * E + m * O + v * F, i[10] = d * T + p * A + m * C + v * k, i[14] = d * S + p * P + m * R + v * L, i[3] = g * x + y * M + _ * I + b * D, i[7] = g * w + y * E + _ * O + b * F, i[11] = g * T + y * A + _ * C + b * k, i[15] = g * S + y * P + _ * R + b * L, this + }, t.multiplyScalar = function(t) { + var e = this.elements; + return e[0] *= t, e[4] *= t, e[8] *= t, e[12] *= t, e[1] *= t, e[5] *= t, e[9] *= t, e[13] *= t, e[2] *= t, e[6] *= t, e[10] *= t, e[14] *= t, e[3] *= t, e[7] *= t, e[11] *= t, e[15] *= t, this + }, t.applyToBufferAttribute = function(t) { + for (var e = E, r = 0, n = t.count; r < n; r++) e.x = t.getX(r), e.y = t.getY(r), e.z = t.getZ(r), e.applyMatrix4(this), t.setXYZ(r, e.x, e.y, e.z); + return t + }, t.determinant = function() { + var t = this.elements, + e = t[0], + r = t[4], + n = t[8], + i = t[12], + o = t[1], + a = t[5], + s = t[9], + u = t[13], + h = t[2], + c = t[6], + l = t[10], + f = t[14]; + return t[3] * (+i * s * c - n * u * c - i * a * l + r * u * l + n * a * f - r * s * f) + t[7] * (+e * s * f - e * u * l + i * o * l - n * o * f + n * u * h - i * s * h) + t[11] * (+e * u * c - e * a * f - i * o * c + r * o * f + i * a * h - r * u * h) + t[15] * (-n * a * h - e * s * c + e * a * l + n * o * c - r * o * l + r * s * h) + }, t.transpose = function() { + var t, e = this.elements; + return t = e[1], e[1] = e[4], e[4] = t, t = e[2], e[2] = e[8], e[8] = t, t = e[6], e[6] = e[9], e[9] = t, t = e[3], e[3] = e[12], e[12] = t, t = e[7], e[7] = e[13], e[13] = t, t = e[11], e[11] = e[14], e[14] = t, this + }, t.setPosition = function(t) { + var e = this.elements; + return e[12] = t.x, e[13] = t.y, e[14] = t.z, this + }, t.getInverse = function(t, e) { + var r = this.elements, + n = t.elements, + i = n[0], + o = n[1], + a = n[2], + s = n[3], + u = n[4], + h = n[5], + c = n[6], + l = n[7], + f = n[8], + d = n[9], + p = n[10], + m = n[11], + v = n[12], + g = n[13], + y = n[14], + _ = n[15], + b = d * y * l - g * p * l + g * c * m - h * y * m - d * c * _ + h * p * _, + x = v * p * l - f * y * l - v * c * m + u * y * m + f * c * _ - u * p * _, + w = f * g * l - v * d * l + v * h * m - u * g * m - f * h * _ + u * d * _, + T = v * d * c - f * g * c - v * h * p + u * g * p + f * h * y - u * d * y, + S = i * b + o * x + a * w + s * T; + if (0 === S) { + var M = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; + if (!0 === e) throw new Error(M); + return this.identity() + } + var E = 1 / S; + return r[0] = b * E, r[1] = (g * p * s - d * y * s - g * a * m + o * y * m + d * a * _ - o * p * _) * E, r[2] = (h * y * s - g * c * s + g * a * l - o * y * l - h * a * _ + o * c * _) * E, r[3] = (d * c * s - h * p * s - d * a * l + o * p * l + h * a * m - o * c * m) * E, r[4] = x * E, r[5] = (f * y * s - v * p * s + v * a * m - i * y * m - f * a * _ + i * p * _) * E, r[6] = (v * c * s - u * y * s - v * a * l + i * y * l + u * a * _ - i * c * _) * E, r[7] = (u * p * s - f * c * s + f * a * l - i * p * l - u * a * m + i * c * m) * E, r[8] = w * E, r[9] = (v * d * s - f * g * s - v * o * m + i * g * m + f * o * _ - i * d * _) * E, r[10] = (u * g * s - v * h * s + v * o * l - i * g * l - u * o * _ + i * h * _) * E, r[11] = (f * h * s - u * d * s - f * o * l + i * d * l + u * o * m - i * h * m) * E, r[12] = T * E, r[13] = (f * g * a - v * d * a + v * o * p - i * g * p - f * o * y + i * d * y) * E, r[14] = (v * h * a - u * g * a - v * o * c + i * g * c + u * o * y - i * h * y) * E, r[15] = (u * d * a - f * h * a + f * o * c - i * d * c - u * o * p + i * h * p) * E, this + }, t.scale = function(t) { + var e = this.elements, + r = t.x, + n = t.y, + i = t.z; + return e[0] *= r, e[4] *= n, e[8] *= i, e[1] *= r, e[5] *= n, e[9] *= i, e[2] *= r, e[6] *= n, e[10] *= i, e[3] *= r, e[7] *= n, e[11] *= i, this + }, t.getMaxScaleOnAxis = function() { + var t = this.elements, + e = t[0] * t[0] + t[1] * t[1] + t[2] * t[2], + r = t[4] * t[4] + t[5] * t[5] + t[6] * t[6], + n = t[8] * t[8] + t[9] * t[9] + t[10] * t[10]; + return Math.sqrt(Math.max(e, r, n)) + }, t.makeTranslation = function(t, e, r) { + return this.set(1, 0, 0, t, 0, 1, 0, e, 0, 0, 1, r, 0, 0, 0, 1), this + }, t.makeRotationX = function(t) { + var e = Math.cos(t), + r = Math.sin(t); + return this.set(1, 0, 0, 0, 0, e, -r, 0, 0, r, e, 0, 0, 0, 0, 1), this + }, t.makeRotationY = function(t) { + var e = Math.cos(t), + r = Math.sin(t); + return this.set(e, 0, r, 0, 0, 1, 0, 0, -r, 0, e, 0, 0, 0, 0, 1), this + }, t.makeRotationZ = function(t) { + var e = Math.cos(t), + r = Math.sin(t); + return this.set(e, -r, 0, 0, r, e, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), this + }, t.makeRotationAxis = function(t, e) { + var r = Math.cos(e), + n = Math.sin(e), + i = 1 - r, + o = t.x, + a = t.y, + s = t.z, + u = i * o, + h = i * a; + return this.set(u * o + r, u * a - n * s, u * s + n * a, 0, u * a + n * s, h * a + r, h * s - n * o, 0, u * s - n * a, h * s + n * o, i * s * s + r, 0, 0, 0, 0, 1), this + }, t.makeScale = function(t, e, r) { + return this.set(t, 0, 0, 0, 0, e, 0, 0, 0, 0, r, 0, 0, 0, 0, 1), this + }, t.makeShear = function(t, e, r) { + return this.set(1, e, r, 0, t, 1, r, 0, t, e, 1, 0, 0, 0, 0, 1), this + }, t.compose = function(t, e, r) { + var n = this.elements, + i = e._x, + o = e._y, + a = e._z, + s = e._w, + u = i + i, + h = o + o, + c = a + a, + l = i * u, + f = i * h, + d = i * c, + p = o * h, + m = o * c, + v = a * c, + g = s * u, + y = s * h, + _ = s * c, + b = r.x, + x = r.y, + w = r.z; + return n[0] = (1 - (p + v)) * b, n[1] = (f + _) * b, n[2] = (d - y) * b, n[3] = 0, n[4] = (f - _) * x, n[5] = (1 - (l + v)) * x, n[6] = (m + g) * x, n[7] = 0, n[8] = (d + y) * w, n[9] = (m - g) * w, n[10] = (1 - (l + p)) * w, n[11] = 0, n[12] = t.x, n[13] = t.y, n[14] = t.z, n[15] = 1, this + }, t.decompose = function(t, e, r) { + var n = E, + i = M, + o = this.elements, + a = n.set(o[0], o[1], o[2]).length(), + s = n.set(o[4], o[5], o[6]).length(), + u = n.set(o[8], o[9], o[10]).length(); + this.determinant() < 0 && (a = -a), t.x = o[12], t.y = o[13], t.z = o[14], i.copy(this); + var h = 1 / a, + c = 1 / s, + l = 1 / u; + return i.elements[0] *= h, i.elements[1] *= h, i.elements[2] *= h, i.elements[4] *= c, i.elements[5] *= c, i.elements[6] *= c, i.elements[8] *= l, i.elements[9] *= l, i.elements[10] *= l, e.setFromRotationMatrix(i), r.x = a, r.y = s, r.z = u, this + }, t.makePerspective = function(t, e, r, n, i, o) { + var a = this.elements, + s = 2 * i / (e - t), + u = 2 * i / (r - n), + h = (e + t) / (e - t), + c = (r + n) / (r - n), + l = -(o + i) / (o - i), + f = -2 * o * i / (o - i); + return a[0] = s, a[4] = 0, a[8] = h, a[12] = 0, a[1] = 0, a[5] = u, a[9] = c, a[13] = 0, a[2] = 0, a[6] = 0, a[10] = l, a[14] = f, a[3] = 0, a[7] = 0, a[11] = -1, a[15] = 0, this + }, t.makeOrthographic = function(t, e, r, n, i, o) { + var a = this.elements, + s = 1 / (e - t), + u = 1 / (r - n), + h = 1 / (o - i), + c = (e + t) * s, + l = (r + n) * u, + f = (o + i) * h; + return a[0] = 2 * s, a[4] = 0, a[8] = 0, a[12] = -c, a[1] = 0, a[5] = 2 * u, a[9] = 0, a[13] = -l, a[2] = 0, a[6] = 0, a[10] = -2 * h, a[14] = -f, a[3] = 0, a[7] = 0, a[11] = 0, a[15] = 1, this + }, t.equals = function(t) { + for (var e = this.elements, r = t.elements, n = 0; n < 16; n++) + if (e[n] !== r[n]) return !1; + return !0 + }, t.fromArray = function(t, e) { + void 0 === e && (e = 0); + for (var r = 0; r < 16; r++) this.elements[r] = t[r + e]; + return this + }, t.toArray = function(t, e) { + void 0 === t && (t = []), void 0 === e && (e = 0); + var r = this.elements; + return t[e] = r[0], t[e + 1] = r[1], t[e + 2] = r[2], t[e + 3] = r[3], t[e + 4] = r[4], t[e + 5] = r[5], t[e + 6] = r[6], t[e + 7] = r[7], t[e + 8] = r[8], t[e + 9] = r[9], t[e + 10] = r[10], t[e + 11] = r[11], t[e + 12] = r[12], t[e + 13] = r[13], t[e + 14] = r[14], t[e + 15] = r[15], t + }, Matrix4 + }(), + M = new S, + E = new v, + A = new v, + P = new v, + I = new v(0, 0, 0), + O = new v(1, 1, 1); + + function Quaternion_defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + v.prototype.project = function(t) { + return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix) + }, v.prototype.unproject = function(t) { + var e = M; + return this.applyMatrix4(e.getInverse(t.projectionMatrix)).applyMatrix4(t.matrixWorld) + }; + var C = function() { + function Quaternion(t, e, r, n) { + this._x = t || 0, this._y = e || 0, this._z = r || 0, this._w = void 0 !== n ? n : 1, this.isQuaternion = !0 + } + var t, e, r, n = Quaternion.prototype; + return n.slerp = function(t, e, r, n) { + return r.copy(t).slerp(e, n) + }, Quaternion.slerpFlat = function(t, e, r, n, i, o, a) { + var s = r[n + 0], + u = r[n + 1], + h = r[n + 2], + c = r[n + 3], + l = i[o + 0], + f = i[o + 1], + d = i[o + 2], + p = i[o + 3]; + if (c !== p || s !== l || u !== f || h !== d) { + var m = 1 - a, + v = s * l + u * f + h * d + c * p, + g = 0 <= v ? 1 : -1, + y = 1 - v * v; + if (y > Number.EPSILON) { + var _ = Math.sqrt(y), + b = Math.atan2(_, v * g); + m = Math.sin(m * b) / _, a = Math.sin(a * b) / _ + } + var x = a * g; + if (s = s * m + l * x, u = u * m + f * x, h = h * m + d * x, c = c * m + p * x, m === 1 - a) { + var w = 1 / Math.sqrt(s * s + u * u + h * h + c * c); + s *= w, u *= w, h *= w, c *= w + } + } + t[e] = s, t[e + 1] = u, t[e + 2] = h, t[e + 3] = c + }, n.set = function(t, e, r, n) { + return this._x = t, this._y = e, this._z = r, this._w = n, this.onChangeCallback(), this + }, n.clone = function() { + return new this.constructor(this._x, this._y, this._z, this._w) + }, n.copy = function(t) { + return this._x = t.x, this._y = t.y, this._z = t.z, this._w = t.w, this.onChangeCallback(), this + }, n.setFromEuler = function(t, e) { + if (!t || !t.isEuler) throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); + var r = t._x, + n = t._y, + i = t._z, + o = t.order, + a = Math.cos, + s = Math.sin, + u = a(r / 2), + h = a(n / 2), + c = a(i / 2), + l = s(r / 2), + f = s(n / 2), + d = s(i / 2); + return "XYZ" === o ? (this._x = l * h * c + u * f * d, this._y = u * f * c - l * h * d, this._z = u * h * d + l * f * c, this._w = u * h * c - l * f * d) : "YXZ" === o ? (this._x = l * h * c + u * f * d, this._y = u * f * c - l * h * d, this._z = u * h * d - l * f * c, this._w = u * h * c + l * f * d) : "ZXY" === o ? (this._x = l * h * c - u * f * d, this._y = u * f * c + l * h * d, this._z = u * h * d + l * f * c, this._w = u * h * c - l * f * d) : "ZYX" === o ? (this._x = l * h * c - u * f * d, this._y = u * f * c + l * h * d, this._z = u * h * d - l * f * c, this._w = u * h * c + l * f * d) : "YZX" === o ? (this._x = l * h * c + u * f * d, this._y = u * f * c + l * h * d, this._z = u * h * d - l * f * c, this._w = u * h * c - l * f * d) : "XZY" === o && (this._x = l * h * c - u * f * d, this._y = u * f * c - l * h * d, this._z = u * h * d + l * f * c, this._w = u * h * c + l * f * d), !1 !== e && this.onChangeCallback(), this + }, n.setFromAxisAngle = function(t, e) { + var r = e / 2, + n = Math.sin(r); + return this._x = t.x * n, this._y = t.y * n, this._z = t.z * n, this._w = Math.cos(r), this.onChangeCallback(), this + }, n.setFromRotationMatrix = function(t) { + var e, r = t.elements, + n = r[0], + i = r[4], + o = r[8], + a = r[1], + s = r[5], + u = r[9], + h = r[2], + c = r[6], + l = r[10], + f = n + s + l; + return this._z = 0 < f ? (e = .5 / Math.sqrt(f + 1), this._w = .25 / e, this._x = (c - u) * e, this._y = (o - h) * e, (a - i) * e) : s < n && l < n ? (e = 2 * Math.sqrt(1 + n - s - l), this._w = (c - u) / e, this._x = .25 * e, this._y = (i + a) / e, (o + h) / e) : l < s ? (e = 2 * Math.sqrt(1 + s - n - l), this._w = (o - h) / e, this._x = (i + a) / e, this._y = .25 * e, (u + c) / e) : (e = 2 * Math.sqrt(1 + l - n - s), this._w = (a - i) / e, this._x = (o + h) / e, this._y = (u + c) / e, .25 * e), this.onChangeCallback(), this + }, n.setFromUnitVectors = function(t, e) { + var r, n = R; + return (r = t.dot(e) + 1) < 1e-6 ? (r = 0, Math.abs(t.x) > Math.abs(t.z) ? n.set(-t.y, t.x, 0) : n.set(0, -t.z, t.y)) : n.crossVectors(t, e), this._x = n.x, this._y = n.y, this._z = n.z, this._w = r, this.normalize() + }, n.angleTo = function(t) { + return 2 * Math.acos(Math.abs(Math_clamp(this.dot(t), -1, 1))) + }, n.rotateTowards = function(t, e) { + var r = this.angleTo(t); + if (0 === r) return this; + var n = Math.min(1, e / r); + return this.slerp(t, n), this + }, n.inverse = function() { + return this.conjugate() + }, n.conjugate = function() { + return this._x *= -1, this._y *= -1, this._z *= -1, this.onChangeCallback(), this + }, n.dot = function(t) { + return this._x * t._x + this._y * t._y + this._z * t._z + this._w * t._w + }, n.lengthSq = function() { + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w + }, n.length = function() { + return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w) + }, n.normalize = function() { + var t = this.length(); + return this._w = 0 === t ? (this._x = 0, this._y = 0, this._z = 0, 1) : (t = 1 / t, this._x = this._x * t, this._y = this._y * t, this._z = this._z * t, this._w * t), this.onChangeCallback(), this + }, n.multiply = function(t, e) { + return void 0 !== e ? this.multiplyQuaternions(t, e) : this.multiplyQuaternions(this, t) + }, n.premultiply = function(t) { + return this.multiplyQuaternions(t, this) + }, n.multiplyQuaternions = function(t, e) { + var r = t._x, + n = t._y, + i = t._z, + o = t._w, + a = e._x, + s = e._y, + u = e._z, + h = e._w; + return this._x = r * h + o * a + n * u - i * s, this._y = n * h + o * s + i * a - r * u, this._z = i * h + o * u + r * s - n * a, this._w = o * h - r * a - n * s - i * u, this.onChangeCallback(), this + }, n.slerp = function(t, e) { + if (0 === e) return this; + if (1 === e) return this.copy(t); + var r = this._x, + n = this._y, + i = this._z, + o = this._w, + a = o * t._w + r * t._x + n * t._y + i * t._z; + if (a < 0 ? (this._w = -t._w, this._x = -t._x, this._y = -t._y, this._z = -t._z, a = -a) : this.copy(t), 1 <= a) return this._w = o, this._x = r, this._y = n, this._z = i, this; + var s = 1 - a * a; + if (s <= Number.EPSILON) { + var u = 1 - e; + return this._w = u * o + e * this._w, this._x = u * r + e * this._x, this._y = u * n + e * this._y, this._z = u * i + e * this._z, this.normalize() + } + var h = Math.sqrt(s), + c = Math.atan2(h, a), + l = Math.sin((1 - e) * c) / h, + f = Math.sin(e * c) / h; + return this._w = o * l + this._w * f, this._x = r * l + this._x * f, this._y = n * l + this._y * f, this._z = i * l + this._z * f, this.onChangeCallback(), this + }, n.equals = function(t) { + return t._x === this._x && t._y === this._y && t._z === this._z && t._w === this._w + }, n.fromArray = function(t, e) { + return void 0 === e && (e = 0), this._x = t[e], this._y = t[e + 1], this._z = t[e + 2], this._w = t[e + 3], this.onChangeCallback(), this + }, n.toArray = function(t, e) { + return void 0 === t && (t = []), void 0 === e && (e = 0), t[e] = this._x, t[e + 1] = this._y, t[e + 2] = this._z, t[e + 3] = this._w, t + }, n.onChange = function(t) { + return this.onChangeCallback = t, this + }, n.onChangeCallback = function() {}, t = Quaternion, (e = [{ + key: "x", + get: function() { + return this._z + }, + set: function(t) { + this._x = t, this.onChangeCallback() + } + }, { + key: "y", + get: function() { + return this._y + }, + set: function(t) { + this._y = t, this.onChangeCallback() + } + }, { + key: "z", + set: function(t) { + this._z = t, this.onChangeCallback() + } + }, { + key: "w", + get: function() { + return this._w + }, + set: function(t) { + this._w = t, this.onChangeCallback() + } + }]) && Quaternion_defineProperties(t.prototype, e), r && Quaternion_defineProperties(t, r), Quaternion + }(), + R = new v, + D = new C; + + function Euler_defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + v.prototype.applyEuler = function(t) { + var e = D; + return !t || t.isEuler, this.applyQuaternion(e.setFromEuler(t)) + }, v.prototype.applyAxisAngle = function(t, e) { + var r = D; + return this.applyQuaternion(r.setFromAxisAngle(t, e)) + }; + var F = function() { + function Euler(t, e, r, n) { + this._x = t || 0, this._y = e || 0, this._z = r || 0, this._order = n || Euler.DefaultOrder, this.isEuler = !0 + } + var t, e, r, n = Euler.prototype; + return n.set = function(t) { + this._y = t, this.onChangeCallback() + }, n.set = function(t, e, r, n) { + return this._x = t, this._y = e, this._z = r, this._order = n || this._order, this.onChangeCallback(), this + }, n.clone = function() { + return new this.constructor(this._x, this._y, this._z, this._order) + }, n.copy = function(t) { + return this._x = t._x, this._y = t._y, this._z = t._z, this._order = t._order, this.onChangeCallback(), this + }, n.setFromRotationMatrix = function(t, e, r) { + var n = Math_clamp, + i = t.elements, + o = i[0], + a = i[4], + s = i[8], + u = i[1], + h = i[5], + c = i[9], + l = i[2], + f = i[6], + d = i[10]; + return "XYZ" === (e = e || this._order) ? (this._y = Math.asin(n(s, -1, 1)), Math.abs(s) < .99999 ? (this._x = Math.atan2(-c, d), this._z = Math.atan2(-a, o)) : (this._x = Math.atan2(f, h), this._z = 0)) : "YXZ" === e ? (this._x = Math.asin(-n(c, -1, 1)), Math.abs(c) < .99999 ? (this._y = Math.atan2(s, d), this._z = Math.atan2(u, h)) : (this._y = Math.atan2(-l, o), this._z = 0)) : "ZXY" === e ? (this._x = Math.asin(n(f, -1, 1)), Math.abs(f) < .99999 ? (this._y = Math.atan2(-l, d), this._z = Math.atan2(-a, h)) : (this._y = 0, this._z = Math.atan2(u, o))) : "ZYX" === e ? (this._y = Math.asin(-n(l, -1, 1)), Math.abs(l) < .99999 ? (this._x = Math.atan2(f, d), this._z = Math.atan2(u, o)) : (this._x = 0, this._z = Math.atan2(-a, h))) : "YZX" === e ? (this._z = Math.asin(n(u, -1, 1)), Math.abs(u) < .99999 ? (this._x = Math.atan2(-c, h), this._y = Math.atan2(-l, o)) : (this._x = 0, this._y = Math.atan2(s, d))) : "XZY" === e && (this._z = Math.asin(-n(a, -1, 1)), Math.abs(a) < .99999 ? (this._x = Math.atan2(f, h), this._y = Math.atan2(s, o)) : (this._x = Math.atan2(-c, d), this._y = 0)), this._order = e, !1 !== r && this.onChangeCallback(), this + }, n.setFromQuaternion = function(t, e, r) { + return L.makeRotationFromQuaternion(t), this.setFromRotationMatrix(L, e, r) + }, n.setFromVector3 = function(t, e) { + return this.set(t.x, t.y, t.z, e || this._order) + }, n.reorder = function(t) { + return N.setFromEuler(this), this.setFromQuaternion(N, t) + }, n.equals = function(t) { + return t._x === this._x && t._y === this._y && t._z === this._z && t._order === this._order + }, n.fromArray = function(t) { + return this._x = t[0], this._y = t[1], this._z = t[2], void 0 !== t[3] && (this._order = t[3]), this.onChangeCallback(), this + }, n.toArray = function(t, e) { + return void 0 === t && (t = []), void 0 === e && (e = 0), t[e] = this._x, t[e + 1] = this._y, t[e + 2] = this._z, t[e + 3] = this._order, t + }, n.toVector3 = function(t) { + return t ? t.set(this._x, this._y, this._z) : new v(this._x, this._y, this._z) + }, n.onChange = function(t) { + return this.onChangeCallback = t, this + }, n.onChangeCallback = function() {}, t = Euler, (e = [{ + key: "x", + get: function() { + return this._x + }, + set: function(t) { + this._z = t, this.onChangeCallback() + } + }, { + key: "y", + get: function() { + return this._y + } + }, { + key: "z", + get: function() { + return this._z + } + }, { + key: "order", + get: function() { + return this._order + }, + set: function(t) { + this._order = t, this.onChangeCallback() + } + }]) && Euler_defineProperties(t.prototype, e), r && Euler_defineProperties(t, r), Euler + }(), + L = new S, + N = new C; + F.RotationOrders = ["XYZ", "YZX", "ZXY", "XZY", "YXZ", "ZYX"], F.DefaultOrder = "XYZ"; + var B = r(37), + U = r.n(B), + G = r(45), + j = r.n(G), + z = r(179), + X = r.n(z); + U.a.prototype.setValues = function(t) {}; + var V = function() { + function Interpolant(t, e, r, n) { + this.parameterPositions = t, this._cachedIndex = 0, this.resultBuffer = void 0 !== n ? n : new e.constructor(r), this.sampleValues = e, this.valueSize = r, this.settings = null, this.DefaultSettings_ = {} + } + var t = Interpolant.prototype; + return t.evaluate = function(t) { + var e = this.parameterPositions, + r = this._cachedIndex, + n = e[r], + i = e[r - 1]; + t: { + e: { + var o;r: { + n: if (!(t < n)) { + for (var a = r + 2;;) { + if (void 0 === n) { + if (t < i) break n; + return r = e.length, this._cachedIndex = r, this.afterEnd_(r - 1, t, i) + } + if (r === a) break; + if (i = n, t < (n = e[++r])) break e + } + o = e.length; + break r + }if (i <= t) break t; + var s = e[1];t < s && (r = 2, i = s); + for (a = r - 2;;) { + if (void 0 === i) return this._cachedIndex = 0, this.beforeStart_(0, t, n); + if (r === a) break; + if (n = i, (i = e[--r - 1]) <= t) break e + } + o = r, + r = 0 + } + for (; r < o;) { + var u = r + o >>> 1; + t < e[u] ? o = u : r = u + 1 + } + if (n = e[r], void 0 === (i = e[r - 1])) return this._cachedIndex = 0, this.beforeStart_(0, t, n); + if (void 0 === n) return r = e.length, this._cachedIndex = r, this.afterEnd_(r - 1, i, t) + } + this._cachedIndex = r, + this.intervalChanged_(r, i, n) + } + return this.interpolate_(r, i, t, n) + }, t.getSettings_ = function() { + return this.settings || this.DefaultSettings_ + }, t.copySampleValue_ = function(t) { + for (var e = this.resultBuffer, r = this.sampleValues, n = this.valueSize, i = t * n, o = 0; o !== n; ++o) e[o] = r[i + o]; + return e + }, t.interpolate_ = function() { + throw new Error("call to abstract method") + }, t.intervalChanged_ = function() {}, Interpolant + }(); + Object.assign(V.prototype, { + beforeStart_: V.prototype.copySampleValue_, + afterEnd_: V.prototype.copySampleValue_ + }); + var q = function(o) { + var t, e; + + function CubicInterpolant(t, e, r, n) { + var i; + return (i = o.call(this, t, e, r, n) || this)._weightPrev = -0, i._offsetPrev = -0, i._weightNext = -0, i._offsetNext = -0, i.DefaultSettings_ = { + endingStart: 2400, + endingEnd: 2400 + }, i + } + e = o, (t = CubicInterpolant).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e; + var r = CubicInterpolant.prototype; + return r.intervalChanged = function(t, e, r) { + var n = this.parameterPositions, + i = t - 2, + o = t + 1, + a = n[i], + s = n[o]; + if (void 0 === a) switch (this.getSettings_().endingStart) { + case 2401: + i = t, a = 2 * e - r; + break; + case 2402: + a = e + n[i = n.length - 2] - n[i + 1]; + break; + default: + i = t, a = r + } + if (void 0 === s) switch (this.getSettings_().endingEnd) { + case 2401: + o = t, s = 2 * r - e; + break; + case 2402: + s = r + n[o = 1] - n[0]; + break; + default: + o = t - 1, s = e + } + var u = .5 * (r - e), + h = this.valueSize; + this._weightPrev = u / (e - a), this._weightNext = u / (s - r), this._offsetPrev = i * h, this._offsetNext = o * h + }, r.interpolate_ = function(t, e, r, n) { + for (var i = this.resultBuffer, o = this.sampleValues, a = this.valueSize, s = t * a, u = s - a, h = this._offsetPrev, c = this._offsetNext, l = this._weightPrev, f = this._weightNext, d = (r - e) / (n - e), p = d * d, m = p * d, v = -l * m + 2 * l * p - l * d, g = (1 + l) * m + (-1.5 - 2 * l) * p + (-.5 + l) * d + 1, y = (-1 - f) * m + (1.5 + f) * p + .5 * d, _ = f * m - f * p, b = 0; b !== a; ++b) i[b] = v * o[h + b] + g * o[u + b] + y * o[s + b] + _ * o[c + b]; + return i + }, CubicInterpolant + }(V); + var H = function(i) { + var t, e; + + function LinearInterpolant(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = LinearInterpolant).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, LinearInterpolant.prototype.interpolate_ = function(t, e, r, n) { + for (var i = this.resultBuffer, o = this.sampleValues, a = this.valueSize, s = t * a, u = s - a, h = (r - e) / (n - e), c = 1 - h, l = 0; l !== a; ++l) i[l] = o[u + l] * c + o[s + l] * h; + return i + }, LinearInterpolant + }(V); + var W = function(i) { + var t, e; + + function DiscreteInterpolant(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = DiscreteInterpolant).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, DiscreteInterpolant.prototype.interpolate_ = function(t) { + return this.copySampleValue_(t - 1) + }, DiscreteInterpolant + }(V), + Y = r(18), + K = function() { + function KeyframeTrack(t, e, r, n) { + if (void 0 === t) throw new Error("THREE.KeyframeTrack: track name is undefined"); + if (void 0 === e || 0 === e.length) throw new Error("THREE.KeyframeTrack: no keyframes in track named " + t); + this.name = t, this.times = Y.b(e, this.TimeBufferType), this.values = Y.b(r, this.ValueBufferType), this.setInterpolation(n || this.DefaultInterpolation) + } + var t = KeyframeTrack.prototype; + return t.toJSON = function(t) { + var e, r = t.constructor; + if (void 0 !== r.toJSON) e = r.toJSON(t); + else { + e = { + name: t.name, + times: Y.b(t.times, Array), + values: Y.b(t.values, Array) + }; + var n = t.getInterpolation(); + n !== t.DefaultInterpolation && (e.interpolation = n) + } + return e.type = t.ValueTypeName, e + }, t.InterpolantFactoryMethodDiscrete = function(t) { + return new W(this.times, this.values, this.getValueSize(), t) + }, t.InterpolantFactoryMethodLinear = function(t) { + return new H(this.times, this.values, this.getValueSize(), t) + }, t.InterpolantFactoryMethodSmooth = function(t) { + return new q(this.times, this.values, this.getValueSize(), t) + }, t.setInterpolation = function(t) { + var e; + switch (t) { + case 2300: + e = this.InterpolantFactoryMethodDiscrete; + break; + case 2301: + e = this.InterpolantFactoryMethodLinear; + break; + case 2302: + e = this.InterpolantFactoryMethodSmooth + } + if (void 0 !== e) return this.createInterpolant = e, this; + var r = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; + if (void 0 === this.createInterpolant) { + if (t === this.DefaultInterpolation) throw new Error(r); + this.setInterpolation(this.DefaultInterpolation) + } + return this + }, t.getInterpolation = function() { + switch (this.createInterpolant) { + case this.InterpolantFactoryMethodDiscrete: + return 2300; + case this.InterpolantFactoryMethodLinear: + return 2301; + case this.InterpolantFactoryMethodSmooth: + return 2302 + } + }, t.getValueSize = function() { + return this.values.length / this.times.length + }, t.shift = function(t) { + if (0 !== t) + for (var e = this.times, r = 0, n = e.length; r !== n; ++r) e[r] += t; + return this + }, t.scale = function(t) { + if (1 !== t) + for (var e = this.times, r = 0, n = e.length; r !== n; ++r) e[r] *= t; + return this + }, t.trim = function(t, e) { + for (var r = this.times, n = r.length, i = 0, o = n - 1; i !== n && r[i] < t;) ++i; + for (; - 1 !== o && r[o] > e;) --o; + if (++o, 0 !== i || o !== n) { + o <= i && (i = (o = Math.max(o, 1)) - 1); + var a = this.getValueSize(); + this.times = Y.a(r, i, o), this.values = Y.a(this.values, i * a, o * a) + } + return this + }, t.validate = function() { + var t = !0, + e = this.getValueSize(); + e - Math.floor(e) != 0 && (t = !1); + var r = this.times, + n = this.values, + i = r.length; + 0 === i && (t = !1); + for (var o = null, a = 0; a !== i; a++) { + var s = r[a]; + if ("number" == typeof s && isNaN(s)) { + t = !1; + break + } + if (null !== o && s < o) { + t = !1; + break + } + o = s + } + if (void 0 !== n && Y.e(n)) + for (var u = 0, h = n.length; u !== h; ++u) { + var c = n[u]; + if (isNaN(c)) { + t = !1; + break + } + } + return t + }, t.optimize = function() { + for (var t = this.times, e = this.values, r = this.getValueSize(), n = 2302 === this.getInterpolation(), i = 1, o = t.length - 1, a = 1; a < o; ++a) { + var s = !1, + u = t[a]; + if (u !== t[a + 1] && (1 !== a || u !== u[0])) + if (n) s = !0; + else + for (var h = a * r, c = h - r, l = h + r, f = 0; f !== r; ++f) { + var d = e[h + f]; + if (d !== e[c + f] || d !== e[l + f]) { + s = !0; + break + } + } + if (s) { + if (a !== i) { + t[i] = t[a]; + for (var p = a * r, m = i * r, v = 0; v !== r; ++v) e[m + v] = e[p + v] + }++i + } + } + if (0 < o) { + t[i] = t[o]; + for (var g = o * r, y = i * r, _ = 0; _ !== r; ++_) e[y + _] = e[g + _]; + ++i + } + return i !== t.length && (this.times = Y.a(t, 0, i), this.values = Y.a(e, 0, i * r)), this + }, t.clone = function() { + var t = Y.a(this.times, 0), + e = Y.a(this.values, 0), + r = new this.constructor(this.name, t, e); + return r.createInterpolant = this.createInterpolant, r + }, KeyframeTrack + }(); + Object.assign(K.prototype, { + TimeBufferType: Float32Array, + ValueBufferType: Float32Array, + DefaultInterpolation: 2301 + }); + var Z = function(n) { + var t, e; + + function BooleanKeyframeTrack(t, e, r) { + return n.call(this, t, e, r) || this + } + return e = n, (t = BooleanKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, BooleanKeyframeTrack + }(K); + Object.assign(Z.prototype, { + ValueTypeName: "bool", + ValueBufferType: Array, + DefaultInterpolation: 2300, + InterpolantFactoryMethodLinear: void 0, + InterpolantFactoryMethodSmooth: void 0 + }); + var Q = function(o) { + var t, e; + + function ColorKeyframeTrack(t, e, r, n) { + var i; + return (i = o.call(this, t, e, r, n) || this).ValueTypeName = "color", i.ValueBufferType = Array, i + } + return e = o, (t = ColorKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, ColorKeyframeTrack + }(K); + Object.assign(Q.prototype, { + ValueTypeName: "color", + ValueBufferType: Array + }); + var J = function(i) { + var t, e; + + function QuaternionLinearInterpolant(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = QuaternionLinearInterpolant).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, QuaternionLinearInterpolant.prototype.interpolate_ = function(t, e, r, n) { + for (var i = this.resultBuffer, o = this.sampleValues, a = this.valueSize, s = t * a, u = (r - e) / (n - e), h = s + a; s !== h; s += 4) C.slerpFlat(i, 0, o, s - a, o, s, u); + return i + }, QuaternionLinearInterpolant + }(V); + var $ = function(i) { + var t, e; + + function QuaternionKeyframeTrack(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = QuaternionKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodLinear = function(t) { + return new J(this.times, this.values, this.getValueSize(), t) + }, QuaternionKeyframeTrack + }(K); + Object.assign($.prototype, { + ValueTypeName: "quaternion", + DefaultInterpolation: 2301, + InterpolantFactoryMethodSmooth: void 0 + }); + var tt = function(o) { + var t, e; + + function NumberKeyframeTrack(t, e, r, n) { + var i; + return (i = o.call(this, t, e, r, n) || this).ValueTypeName = "number", i + } + return e = o, (t = NumberKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, NumberKeyframeTrack + }(K); + Object.assign(tt.prototype, { + ValueTypeName: "number" + }); + var et = function(i) { + var t, e; + + function VectorKeyframeTrack(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = VectorKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, VectorKeyframeTrack + }(K); + Object.assign(Z.prototype, { + ValueTypeName: "vector" + }); + var rt = function(i) { + var t, e; + + function StringKeyframeTrack(t, e, r, n) { + return i.call(this, t, e, r, n) || this + } + return e = i, (t = StringKeyframeTrack).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, StringKeyframeTrack + }(K); + Object.assign(rt.prototype, { + ValueTypeName: "string", + ValueBufferType: Array, + DefaultInterpolation: 2300, + InterpolantFactoryMethodLinear: void 0, + InterpolantFactoryMethodSmooth: void 0 + }); + var nt = function() { + function AnimationClip(t, e, r) { + this.name = t, this.tracks = r, this.duration = void 0 !== e ? e : -1, this.uuid = generateUUID(), this.duration < 0 && this.resetDuration() + } + var t = AnimationClip.prototype; + return t.resetDuration = function() { + for (var t = 0, e = 0, r = this.tracks.length; e !== r; ++e) { + var n = this.tracks[e]; + t = Math.max(t, n.times[n.times.length - 1]) + } + return this.duration = t, this + }, t.trim = function() { + for (var t = 0; t < this.tracks.length; t++) this.tracks[t].trim(0, this.duration); + return this + }, t.validate = function() { + for (var t = !0, e = 0; e < this.tracks.length; e++) t = t && this.tracks[e].validate(); + return t + }, t.optimize = function() { + for (var t = 0; t < this.tracks.length; t++) this.tracks[t].optimize(); + return this + }, t.clone = function() { + for (var t = [], e = 0; e < this.tracks.length; e++) t.push(this.tracks[e].clone()); + return new AnimationClip(this.name, this.duration, t) + }, AnimationClip.parse = function(t) { + for (var e = [], r = t.tracks, n = 1 / (t.fps || 1), i = 0, o = r.length; i !== o; ++i) e.push(parseKeyframeTrack(r[i]).scale(n)); + return new AnimationClip(t.name, t.duration, e) + }, AnimationClip.CreateFromMorphTargetSequence = function(t, e, r, n) { + for (var i = e.length, o = [], a = 0; a < i; a++) { + var s = [], + u = []; + s.push((a + i - 1) % i, a, (a + 1) % i), u.push(0, 1, 0); + var h = Y.d(s); + s = Y.f(s, 1, h), u = Y.f(u, 1, h), n || 0 !== s[0] || (s.push(i), u.push(u[0])), o.push(new tt(".morphTargetInfluences[" + e[a].name + "]", s, u).scale(1 / r)) + } + return new AnimationClip(t, -1, o) + }, AnimationClip.findByName = function(t, e) { + var r = t; + if (!Array.isArray(t)) { + var n = t; + r = n.geometry && n.geometry.animations || n.animations + } + for (var i = 0; i < r.length; i++) + if (r[i].name === e) return r[i]; + return null + }, AnimationClip.CreateClipsFromMorphTargetSequences = function(t, e, r) { + for (var n = {}, i = /^([\w-]*?)([\d]+)$/, o = 0, a = t.length; o < a; o++) { + var s = t[o], + u = s.name.match(i); + if (u && 1 < u.length) { + var h = u[1], + c = n[h]; + c || (n[h] = c = []), c.push(s) + } + } + var l = []; + for (var f in n) l.push(AnimationClip.CreateFromMorphTargetSequence(f, n[f], e, r)); + return l + }, AnimationClip.parseAnimation = function(t, e) { + if (!t) return null; + for (var r = function(t, e, r, n, i) { + if (0 !== r.length) { + var o = [], + a = []; + Y.c(r, o, a, n), 0 !== o.length && i.push(new t(e, o, a)) + } + }, n = [], i = t.name || "default", o = t.length || -1, a = t.fps || 30, s = t.hierarchy || [], u = 0; u < s.length; u++) { + var h = s[u].keys; + if (h && 0 !== h.length) + if (h[0].morphTargets) { + for (var c = {}, l = 0; l < h.length; l++) + if (h[l].morphTargets) + for (var f = 0; f < h[l].morphTargets.length; f++) c[h[l].morphTargets[f]] = -1; + for (var d in c) { + for (var p = [], m = [], v = 0; v !== h[k].morphTargets.length; ++v) { + var g = h[k]; + p.push(g.time), m.push(g.morphTarget === d ? 1 : 0) + } + n.push(new tt(".morphTargetInfluence[" + d + "]", p, m)) + } + o = c.length * (a || 1) + } else { + var y = ".bones[" + e[u].name + "]"; + r(et, y + ".position", h, "pos", n), r($, y + ".quaternion", h, "rot", n), r(et, y + ".scale", h, "scl", n) + } + } + return 0 === n.length ? null : new AnimationClip(i, o, n) + }, AnimationClip + }(); + + function parseKeyframeTrack(t) { + if (void 0 === t.type) throw new Error("THREE.KeyframeTrack: track type undefined, can not parse"); + var e = function(t) { + switch (t.toLowerCase()) { + case "scalar": + case "double": + case "float": + case "number": + case "integer": + return tt; + case "vector": + case "vector2": + case "vector3": + case "vector4": + return et; + case "color": + return Q; + case "quaternion": + return $; + case "bool": + case "boolean": + return Z; + case "string": + return rt + } + throw new Error("THREE.KeyframeTrack: Unsupported typeName: " + t) + }(t.type); + if (void 0 === t.times) { + var r = [], + n = []; + Y.c(t.keys, r, n, "value"), t.times = r, t.values = n + } + return void 0 !== e.parse ? e.parse(t) : new e(t.name, t.times, t.values, t.interpolation) + } + var it = function() { + function AnimationAction(t, e, r) { + this._mixer = t, this._clip = e, this._localRoot = r || null; + for (var n = e.tracks, i = n.length, o = new Array(i), a = { + endingStart: 2400, + endingEnd: 2400 + }, s = 0; s !== i; ++s) { + var u = n[s].createInterpolant(null); + (o[s] = u).settings = a + } + this._interpolantSettings = a, this._interpolants = o, this._propertyBindings = new Array(i), this._cacheIndex = null, this._byClipCacheIndex = null, this._timeScaleInterpolant = null, this._weightInterpolant = null, this.loop = 2201, this._loopCount = -1, this._startTime = null, this.time = 0, this.timeScale = 1, this._effectiveTimeScale = 1, this.weight = 1, this._effectiveWeight = 1, this.repetitions = 1 / 0, this.paused = !1, this.enabled = !0, this.clampWhenFinished = !1, this.zeroSlopeAtStart = !0, this.zeroSlopeAtEnd = !0 + } + var t = AnimationAction.prototype; + return t.play = function() { + return this._mixer._activateAction(this), this + }, t.stop = function() { + return this._mixer._deactivateAction(this), this.reset() + }, t.reset = function() { + return this.paused = !1, this.enabled = !0, this.time = 0, this._loopCount = -1, this._startTime = null, this.stopFading().stopWarping() + }, t.isRunning = function() { + return this.enabled && !this.paused && 0 !== this.timeScale && null === this._startTime && this._mixer._isActiveAction(this) + }, t.isScheduled = function() { + return this._mixer._isActiveAction(this) + }, t.startAt = function(t) { + return this._startTime = t, this + }, t.setLoop = function(t, e) { + return this.loop = t, this.repetitions = e, this + }, t.setEffectiveWeight = function(t) { + return this.weight = t, this._effectiveWeight = this.enabled ? t : 0, this.stopFading() + }, t.getEffectiveWeight = function() { + return this._effectiveWeight + }, t.fadeIn = function(t) { + return this._scheduleFading(t, 0, 1) + }, t.fadeOut = function(t) { + return this._scheduleFading(t, 1, 0) + }, t.crossFadeFrom = function(t, e, r) { + if (t.fadeOut(e), this.fadeIn(e), r) { + var n = this._clip.duration, + i = t._clip.duration, + o = i / n, + a = n / i; + t.warp(1, o, e), this.warp(a, 1, e) + } + return this + }, t.crossFadeTo = function(t, e, r) { + return t.crossFadeFrom(this, e, r) + }, t.stopFading = function() { + var t = this._weightInterpolant; + return null !== t && (this._weightInterpolant = null, this._mixer._takeBackControlInterpolant(t)), this + }, t.setEffectiveTimeScale = function(t) { + return this.timeScale = t, this._effectiveTimeScale = this.paused ? 0 : t, this.stopWarping() + }, t.getEffectiveTimeScale = function() { + return this._effectiveTimeScale + }, t.setDuration = function(t) { + return this.timeScale = this._clip.duration / t, this.stopWarping() + }, t.syncWith = function(t) { + return this.time = t.time, this.timeScale = t.timeScale, this.stopWarping() + }, t.halt = function(t) { + return this.warp(this._effectiveTimeScale, 0, t) + }, t.warp = function(t, e, r) { + var n = this._mixer, + i = n.time, + o = this._timeScaleInterpolant, + a = this.timeScale; + null === o && (o = n._lendControlInterpolant(), this._timeScaleInterpolant = o); + var s = o.parameterPositions, + u = o.sampleValues; + return s[0] = i, s[1] = i + r, u[0] = t / a, u[1] = e / a, this + }, t.stopWarping = function() { + var t = this._timeScaleInterpolant; + return null !== t && (this._timeScaleInterpolant = null, this._mixer._takeBackControlInterpolant(t)), this + }, t.getMixer = function() { + return this._mixer + }, t.getClip = function() { + return this._clip + }, t.getRoot = function() { + return this._localRoot || this._mixer._root + }, t._update = function(t, e, r, n) { + if (this.enabled) { + var i = this._startTime; + if (null !== i) { + var o = (t - i) * r; + if (o < 0 || 0 === r) return; + this._startTime = null, e = r * o + } + e *= this._updateTimeScale(t); + var a = this._updateTime(e), + s = this._updateWeight(t); + if (0 < s) + for (var u = this._interpolants, h = this._propertyBindings, c = 0, l = u.length; c !== l; ++c) u[c].evaluate(a), h[c].accumulate(n, s) + } else this._updateWeight(t) + }, t._updateWeight = function(t) { + var e = 0; + if (this.enabled) { + e = this.weight; + var r = this._weightInterpolant; + if (null !== r) { + var n = r.evaluate(t)[0]; + e *= n, t > r.parameterPositions[1] && (this.stopFading(), 0 === n && (this.enabled = !1)) + } + } + return this._effectiveWeight = e + }, t._updateTimeScale = function(t) { + var e = 0; + if (!this.paused) { + e = this.timeScale; + var r = this._timeScaleInterpolant; + if (null !== r) e *= r.evaluate(t)[0], t > r.parameterPositions[1] && (this.stopWarping(), 0 === e ? this.paused = !0 : this.timeScale = e) + } + return this._effectiveTimeScale = e + }, t._updateTime = function(t) { + var e = this.time + t, + r = this._clip.duration, + n = this.loop, + i = this._loopCount, + o = 2202 === n; + if (0 === t) return -1 === i ? e : o && 1 == (1 & i) ? r - e : e; + if (2200 === n) { + -1 === i && (this._loopCount = 0, this._setEndings(!0, !0, !1)); + t: { + if (r <= e) e = r; + else { + if (!(e < 0)) break t; + e = 0 + } + this.clampWhenFinished ? this.paused = !0 : this.enabled = !1, + this._mixer.dispatchEvent({ + type: "finished", + action: this, + direction: t < 0 ? -1 : 1 + }) + } + } else { + if (-1 === i && (0 <= t ? (i = 0, this._setEndings(!0, 0 === this.repetitions, o)) : this._setEndings(0 === this.repetitions, !0, o)), r <= e || e < 0) { + var a = Math.floor(e / r); + e -= r * a, i += Math.abs(a); + var s = this.repetitions - i; + if (s <= 0) this.clampWhenFinished ? this.paused = !0 : this.enabled = !1, e = 0 < t ? r : 0, this._mixer.dispatchEvent({ + type: "finished", + action: this, + direction: 0 < t ? 1 : -1 + }); + else { + if (1 === s) { + var u = t < 0; + this._setEndings(u, !u, o) + } else this._setEndings(!1, !1, o); + this._loopCount = i, this._mixer.dispatchEvent({ + type: "loop", + action: this, + loopDelta: a + }) + } + } + if (o && 1 == (1 & i)) return r - (this.time = e) + } + return this.time = e + }, t._setEndings = function(t, e, r) { + var n = this._interpolantSettings; + n.endingEnd = r ? n.endingStart = 2401 : (n.endingStart = t ? this.zeroSlopeAtStart ? 2401 : 2400 : 2402, e ? this.zeroSlopeAtEnd ? 2401 : 2400 : 2402) + }, t._scheduleFading = function(t, e, r) { + var n = this._mixer, + i = n.time, + o = this._weightInterpolant; + null === o && (o = n._lendControlInterpolant(), this._weightInterpolant = o); + var a = o.parameterPositions, + s = o.sampleValues; + return a[0] = i, s[0] = e, a[1] = i + t, s[1] = r, this + }, AnimationAction + }(), + ot = "\\[\\]\\.:\\/", + at = new RegExp("[" + ot + "]", "g"), + st = "[^" + ot + "]", + ut = "[^" + ot.replace("\\.", "") + "]", + ht = /((?:WC+[\/:])*)/.source.replace("WC", st), + ct = /(WCOD+)?/.source.replace("WCOD", ut), + lt = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC", st), + ft = /\.(WC+)(?:\[(.+)\])?/.source.replace("WC", st), + dt = new RegExp("^" + ht + ct + lt + ft + "$"), + pt = ["material", "materials", "bones"], + mt = function() { + function Composite(t, e, r) { + var n = r || vt.parseTrackName(e); + this._targetGroup = t, this._bindings = t.subscribe_(e, n) + } + var t = Composite.prototype; + return t.getValue = function(t, e) { + this.bind(); + var r = this._targetGroup.nCachedObjects_, + n = this._bindings[r]; + void 0 !== n && n.getValue(t, e) + }, t.setValue = function(t, e) { + for (var r = this._bindings, n = this._targetGroup.nCachedObjects_, i = r.length; n !== i; ++n) r[n].setValue(t, e) + }, t.bind = function() { + for (var t = this._bindings, e = this._targetGroup.nCachedObjects_, r = t.length; e !== r; ++e) t[e].bind() + }, t.unbind = function() { + for (var t = this._bindings, e = this._targetGroup.nCachedObjects_, r = t.length; e !== r; ++e) t[e].unbind() + }, Composite + }(), + vt = function() { + function PropertyBinding(t, e, r) { + this.path = e, this.parsedPath = r || PropertyBinding.parseTrackName(e), this.node = PropertyBinding.findNode(t, this.parsedPath.nodeName) || t, this.rootNode = t + } + PropertyBinding.sanitizeNodeName = function(t) { + return t.replace(/\s/g, "_").replace(at, "") + }, PropertyBinding.create = function(t, e, r) { + return t && t.isAnimationObjectGroup ? new PropertyBinding.Composite(t, e, r) : new PropertyBinding(t, e, r) + }, PropertyBinding.parseTrackName = function(t) { + var e = dt.exec(t); + if (!e) throw new Error("PropertyBinding: Cannot parse trackName: " + t); + var r = { + nodeName: e[2], + objectName: e[3], + objectIndex: e[4], + propertyName: e[5], + propertyIndex: e[6] + }, + n = r.nodeName && r.nodeName.lastIndexOf("."); + if (void 0 !== n && -1 !== n) { + var i = r.nodeName.substring(n + 1); - 1 !== pt.indexOf(i) && (r.nodeName = r.nodeName.substring(0, n), r.objectName = i) + } + if (null === r.propertyName || 0 === r.propertyName.length) throw new Error("PropertyBinding: can not parse propertyName from trackName: " + t); + return r + }, PropertyBinding.findNode = function(t, i) { + if (!i || "" === i || "root" === i || "." === i || -1 === i || i === t.name || i === t.uuid) return t; + if (t.skeleton) { + var e = t.skeleton.getBoneByName(i); + if (void 0 !== e) return e + } + if (t.children) { + var r = function searchNodeSubtree(t) { + for (var e = 0; e < t.length; e++) { + var r = t[e]; + if (r.name === i || r.uuid === i) return r; + var n = searchNodeSubtree(r.children); + if (n) return n + } + return null + }(t.children); + if (r) return r + } + return null + }; + var t = PropertyBinding.prototype; + return t._getValue_unavailable = function() {}, t._setValue_unavailable = function() {}, t.getValue = function(t, e) { + this.bind(), this.getValue(t, e) + }, t.setValue = function(t, e) { + this.bind(), this.setValue(t, e) + }, t.bind = function() { + var t = this.node, + e = this.parsedPath, + r = e.objectName, + n = e.propertyName, + i = e.propertyIndex; + if (t || (t = PropertyBinding.findNode(this.rootNode, e.nodeName) || this.rootNode, this.node = t), this.getValue = this._getValue_unavailable, this.setValue = this._setValue_unavailable, t) { + if (r) { + var o = e.objectIndex; + switch (r) { + case "materials": + if (!t.material) return; + if (!t.material.materials) return; + t = t.material.materials; + break; + case "bones": + if (!t.skeleton) return; + t = t.skeleton.bones; + for (var a = 0; a < t.length; a++) + if (t[a].name === o) { + o = a; + break + } + break; + default: + if (void 0 === t[r]) return; + t = t[r] + } + if (void 0 !== o) { + if (void 0 === t[o]) return; + t = t[o] + } + } + var s = t[n]; + if (void 0 !== s) { + var u = this.Versioning.None; + void 0 !== (this.targetObject = t).needsUpdate ? u = this.Versioning.NeedsUpdate : void 0 !== t.matrixWorldNeedsUpdate && (u = this.Versioning.MatrixWorldNeedsUpdate); + var h = this.BindingType.Direct; + if (void 0 !== i) { + if ("morphTargetInfluences" === n) { + if (!t.geometry) return; + if (t.geometry.isBufferGeometry) { + if (!t.geometry.morphAttributes) return; + for (var c = 0; c < this.node.geometry.morphAttributes.position.length; c++) + if (t.geometry.morphAttributes.position[c].name === i) { + i = c; + break + } + } else { + if (!t.geometry.morphTargets) return; + for (var l = 0; l < this.node.geometry.morphTargets.length; l++) + if (t.geometry.morphTargets[l].name === i) { + i = l; + break + } + } + } + h = this.BindingType.ArrayElement, this.resolvedProperty = s, this.propertyIndex = i + } else void 0 !== s.fromArray && void 0 !== s.toArray ? (h = this.BindingType.HasFromToArray, this.resolvedProperty = s) : Array.isArray(s) ? (h = this.BindingType.EntireArray, this.resolvedProperty = s) : this.propertyName = n; + this.getValue = this.GetterByBindingType[h], this.setValue = this.SetterByBindingTypeAndVersioning[h][u] + } else e.nodeName + } + }, t.unbind = function() { + this.node = null, this.getValue = this._getValue_unbound, this.setValue = this._setValue_unbound + }, PropertyBinding + }(); + Object.assign(vt.prototype, { + BindingType: { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 + }, + Versioning: { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 + }, + GetterByBindingType: [function(t, e) { + t[e] = this.node[this.propertyName] + }, function(t, e) { + for (var r = this.resolvedProperty, n = 0, i = r.length; n !== i; ++n) t[e++] = r[n] + }, function(t, e) { + t[e] = this.resolvedProperty[this.propertyIndex] + }, function(t, e) { + this.resolvedProperty.toArray(t, e) + }], + SetterByBindingTypeAndVersioning: [ + [function(t, e) { + this.targetObject[this.propertyName] = t[e] + }, function(t, e) { + this.targetObject[this.propertyName] = t[e], this.targetObject.needsUpdate = !0 + }, function(t, e) { + this.targetObject[this.propertyName] = t[e], this.targetObject.matrixWorldNeedsUpdate = !0 + }], + [function(t, e) { + for (var r = this.resolvedProperty, n = 0, i = r.length; n !== i; ++n) r[n] = t[e++] + }, function(t, e) { + for (var r = this.resolvedProperty, n = 0, i = r.length; n !== i; ++n) r[n] = t[e++]; + this.targetObject.needsUpdate = !0 + }, function(t, e) { + for (var r = this.resolvedProperty, n = 0, i = r.length; n !== i; ++n) r[n] = t[e++]; + this.targetObject.matrixWorldNeedsUpdate = !0 + }], + [function(t, e) { + this.resolvedProperty[this.propertyIndex] = t[e] + }, function(t, e) { + this.resolvedProperty[this.propertyIndex] = t[e], this.targetObject.needsUpdate = !0 + }, function(t, e) { + this.resolvedProperty[this.propertyIndex] = t[e], this.targetObject.matrixWorldNeedsUpdate = !0 + }], + [function(t, e) { + this.resolvedProperty.fromArray(t, e) + }, function(t, e) { + this.resolvedProperty.fromArray(t, e), this.targetObject.needsUpdate = !0 + }, function(t, e) { + this.resolvedProperty.fromArray(t, e), this.targetObject.matrixWorldNeedsUpdate = !0 + }] + ] + }), Object.assign(vt, { + Composite: mt + }); + var gt = function() { + function PropertyMixer(t, e, r) { + this.binding = t, this.valueSize = r; + var n, i = Float64Array; + switch (e) { + case "quaternion": + n = this._slerp; + break; + case "string": + case "bool": + i = Array, n = this._select; + break; + default: + n = this._lerp + } + this.buffer = new i(4 * r), this._mixBufferRegion = n, this.cumulativeWeight = 0, this.useCount = 0, this.referenceCount = 0 + } + var t = PropertyMixer.prototype; + return t.accumulate = function(t, e) { + var r = this.buffer, + n = this.valueSize, + i = t * n + n, + o = this.cumulativeWeight; + if (0 === o) { + for (var a = 0; a !== n; ++a) r[i + a] = r[a]; + o = e + } else { + var s = e / (o += e); + this._mixBufferRegion(r, i, 0, s, n) + } + this.cumulativeWeight = o + }, t.apply = function(t) { + var e = this.valueSize, + r = this.buffer, + n = t * e + e, + i = this.cumulativeWeight, + o = this.binding; + if (this.cumulativeWeight = 0, i < 1) { + var a = 3 * e; + this._mixBufferRegion(r, n, a, 1 - i, e) + } + for (var s = e, u = e + e; s !== u; ++s) + if (r[s] !== r[s + e]) { + o.setValue(r, n); + break + } + }, t.saveOriginalState = function() { + var t = this.binding, + e = this.buffer, + r = this.valueSize, + n = 3 * r; + t.getValue(e, n); + for (var i = r, o = n; i !== o; ++i) e[i] = e[n + i % r]; + this.cumulativeWeight = 0 + }, t.restoreOriginalState = function() { + var t = 3 * this.valueSize; + this.binding.setValue(this.buffer, t) + }, t._select = function(t, e, r, n, i) { + if (.5 <= n) + for (var o = 0; o !== i; ++o) t[e + o] = t[r + o] + }, t._slerp = function(t, e, r, n) { + C.slerpFlat(t, e, t, e, t, r, n) + }, t._lerp = function(t, e, r, n, i) { + for (var o = 1 - n, a = 0; a !== i; ++a) { + var s = e + a; + t[s] = t[s] * o + t[r + a] * n + } + }, PropertyMixer + }(), + yt = function() { + function AnimationMixer(t) { + this._root = t, this._initMemoryManager(), this._accuIndex = 0, this.time = 0, this.timeScale = 1 + } + var t = AnimationMixer.prototype; + return t._bindAction = function(t, e) { + var r = t._localRoot || this._root, + n = t._clip.tracks, + i = n.length, + o = t._propertyBindings, + a = t._interpolants, + s = r.uuid, + u = this._bindingsByRootAndName, + h = u[s]; + void 0 === h && (h = {}, u[s] = h); + for (var c = 0; c !== i; ++c) { + var l = n[c], + f = l.name, + d = h[f]; + if (void 0 !== d) o[c] = d; + else { + if (void 0 !== (d = o[c])) { + null === d._cacheIndex && (++d.referenceCount, this._addInactiveBinding(d, s, f)); + continue + } + var p = e && e._propertyBindings[c].binding.parsedPath; + ++(d = new gt(vt.create(r, f, p), l.ValueTypeName, l.getValueSize())).referenceCount, this._addInactiveBinding(d, s, f), o[c] = d + } + a[c].resultBuffer = d.buffer + } + }, t._activateAction = function(t) { + if (!this._isActiveAction(t)) { + if (null === t._cacheIndex) { + var e = (t._localRoot || this._root).uuid, + r = t._clip.uuid, + n = this._actionsByClip[r]; + this._bindAction(t, n && n.knownActions[0]), this._addInactiveAction(t, r, e) + } + for (var i = t._propertyBindings, o = 0, a = i.length; o !== a; ++o) { + var s = i[o]; + 0 == s.useCount++ && (this._lendBinding(s), s.saveOriginalState()) + } + this._lendAction(t) + } + }, t._deactivateAction = function(t) { + if (this._isActiveAction(t)) { + for (var e = t._propertyBindings, r = 0, n = e.length; r !== n; ++r) { + var i = e[r]; + 0 == --i.useCount && (i.restoreOriginalState(), this._takeBackBinding(i)) + } + this._takeBackAction(t) + } + }, t._initMemoryManager = function() { + this._actions = [], this._nActiveActions = 0, this._actionsByClip = {}, this._bindings = [], this._nActiveBindings = 0, this._bindingsByRootAndName = {}, this._controlInterpolants = [], this._nActiveControlInterpolants = 0; + var t = this; + this.stats = { + actions: { + get total() { + return t._actions.length + }, + get inUse() { + return t._nActiveActions + } + }, + bindings: { + get total() { + return t._bindings.length + }, + get inUse() { + return t._nActiveBindings + } + }, + controlInterpolants: { + get total() { + return t._controlInterpolants.length + }, + get inUse() { + return t._nActiveControlInterpolants + } + } + } + }, t._isActiveAction = function(t) { + var e = t._cacheIndex; + return null !== e && e < this._nActiveActions + }, t._addInactiveAction = function(t, e, r) { + var n = this._actions, + i = this._actionsByClip, + o = i[e]; + if (void 0 === o) o = { + knownActions: [t], + actionByRoot: {} + }, t._byClipCacheIndex = 0, i[e] = o; + else { + var a = o.knownActions; + t._byClipCacheIndex = a.length, a.push(t) + } + t._cacheIndex = n.length, n.push(t), o.actionByRoot[r] = t + }, t._removeInactiveAction = function(t) { + var e = this._actions, + r = e[e.length - 1], + n = t._cacheIndex; + e[r._cacheIndex = n] = r, e.pop(), t._cacheIndex = null; + var i = t._clip.uuid, + o = this._actionsByClip, + a = o[i], + s = a.knownActions, + u = s[s.length - 1], + h = t._byClipCacheIndex; + s[u._byClipCacheIndex = h] = u, s.pop(), t._byClipCacheIndex = null, delete a.actionByRoot[(t._localRoot || this._root).uuid], 0 === s.length && delete o[i], this._removeInactiveBindingsForAction(t) + }, t._removeInactiveBindingsForAction = function(t) { + for (var e = t._propertyBindings, r = 0, n = e.length; r !== n; ++r) { + var i = e[r]; + 0 == --i.referenceCount && this._removeInactiveBinding(i) + } + }, t._lendAction = function(t) { + var e = this._actions, + r = t._cacheIndex, + n = this._nActiveActions++, + i = e[n]; + e[t._cacheIndex = n] = t, e[i._cacheIndex = r] = i + }, t._takeBackAction = function(t) { + var e = this._actions, + r = t._cacheIndex, + n = --this._nActiveActions, + i = e[n]; + e[t._cacheIndex = n] = t, e[i._cacheIndex = r] = i + }, t._addInactiveBinding = function(t, e, r) { + var n = this._bindingsByRootAndName, + i = n[e], + o = this._bindings; + void 0 === i && (i = {}, n[e] = i), (i[r] = t)._cacheIndex = o.length, o.push(t) + }, t._removeInactiveBinding = function(t) { + var e = this._bindings, + r = t.binding, + n = r.rootNode.uuid, + i = r.path, + o = this._bindingsByRootAndName, + a = o[n], + s = e[e.length - 1], + u = t._cacheIndex; + e[s._cacheIndex = u] = s, e.pop(), delete a[i]; + t: { + for (var h in a) break t;delete o[n] + } + }, t._lendBinding = function(t) { + var e = this._bindings, + r = t._cacheIndex, + n = this._nActiveBindings++, + i = e[n]; + e[t._cacheIndex = n] = t, e[i._cacheIndex = r] = i + }, t._takeBackBinding = function(t) { + var e = this._bindings, + r = t._cacheIndex, + n = --this._nActiveBindings, + i = e[n]; + e[t._cacheIndex = n] = t, e[i._cacheIndex = r] = i + }, t._lendControlInterpolant = function() { + var t = this._controlInterpolants, + e = this._nActiveControlInterpolants++, + r = t[e]; + return void 0 === r && (t[(r = new H(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer)).__cacheIndex = e] = r), r + }, t._takeBackControlInterpolant = function(t) { + var e = this._controlInterpolants, + r = t.__cacheIndex, + n = --this._nActiveControlInterpolants, + i = e[n]; + e[t.__cacheIndex = n] = t, e[i.__cacheIndex = r] = i + }, t.clipAction = function(t, e) { + var r = e || this._root, + n = r.uuid, + i = "string" == typeof t ? nt.findByName(r, t) : t, + o = null !== i ? i.uuid : t, + a = this._actionsByClip[o], + s = null; + if (void 0 !== a) { + var u = a.actionByRoot[n]; + if (void 0 !== u) return u; + s = a.knownActions[0], null === i && (i = s._clip) + } + if (null === i) return null; + var h = new it(this, i, e); + return this._bindAction(h, s), this._addInactiveAction(h, o, n), h + }, t.existingAction = function(t, e) { + var r = e || this._root, + n = r.uuid, + i = "string" == typeof t ? nt.findByName(r, t) : t, + o = i ? i.uuid : t, + a = this._actionsByClip[o]; + return void 0 !== a && a.actionByRoot[n] || null + }, t.stopAllAction = function() { + var t = this._actions, + e = this._nActiveActions, + r = this._bindings, + n = this._nActiveBindings; + this._nActiveActions = 0; + for (var i = this._nActiveBindings = 0; i !== e; ++i) t[i].reset(); + for (var o = 0; o !== n; ++o) r[o].useCount = 0; + return this + }, t.update = function(t) { + t *= this.timeScale; + for (var e = this._actions, r = this._nActiveActions, n = this.time += t, i = Math.sign(t), o = this._accuIndex ^= 1, a = 0; a !== r; ++a) { + e[a]._update(n, t, i, o) + } + for (var s = this._bindings, u = this._nActiveBindings, h = 0; h !== u; ++h) s[h].apply(o); + return this + }, t.getRoot = function() { + return this._root + }, t.uncacheClip = function(t) { + var e = this._actions, + r = t.uuid, + n = this._actionsByClip, + i = n[r]; + if (void 0 !== i) { + for (var o = i.knownActions, a = 0, s = o.length; a !== s; ++a) { + var u = o[a]; + this._deactivateAction(u); + var h = u._cacheIndex, + c = e[e.length - 1]; + u._cacheIndex = null, u._byClipCacheIndex = null, e[c._cacheIndex = h] = c, e.pop(), this._removeInactiveBindingsForAction(u) + } + delete n[r] + } + }, t.uncacheRoot = function(t) { + var e = t.uuid, + r = this._actionsByClip; + for (var n in r) { + var i = r[n].actionByRoot[e]; + void 0 !== i && (this._deactivateAction(i), this._removeInactiveAction(i)) + } + var o = this._bindingsByRootAndName[e]; + if (void 0 !== o) + for (var a in o) { + var s = o[a]; + s.restoreOriginalState(), this._removeInactiveBinding(s) + } + }, t.uncacheAction = function(t, e) { + var r = this.existingAction(t, e); + null !== r && (this._deactivateAction(r), this._removeInactiveAction(r)) + }, t.dispatchEvent = function() {}, AnimationMixer + }(); + Object.assign(yt.prototype, { + _controlInterpolantsResultBuffer: new Float32Array(1) + }); + var _t = 0, + bt = new C, + xt = new S, + wt = new v, + Tt = new v, + St = function() { + function Object3D() { + Object.defineProperty(this, "id", { + value: _t++ + }), this.uuid = generateUUID(), this.name = "", this.type = "Object3D", this.parent = null, this.children = [], this.up = Object3D.DefaultUp.clone(); + var t = new v, + e = new F, + r = new C, + n = new v(1, 1, 1); + e.onChange(function() { + r.setFromEuler(e, !1) + }), r.onChange(function() { + e.setFromQuaternion(r, void 0, !1) + }), this.position = void 0, this.rotation = void 0, this.quaternion = void 0, this.scale = void 0, this.modelViewMatrix = void 0, this.normalMatrix = void 0, Object.defineProperties(this, { + position: { + configurable: !0, + enumerable: !0, + value: t + }, + rotation: { + configurable: !0, + enumerable: !0, + value: e + }, + quaternion: { + configurable: !0, + enumerable: !0, + value: r + }, + scale: { + configurable: !0, + enumerable: !0, + value: n + }, + modelViewMatrix: { + value: new S + }, + normalMatrix: { + value: new w + } + }), this.matrix = new S, this.matrixWorld = new S, this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate, this.matrixWorldNeedsUpdate = !1, this.visible = !0, this.userData = {}, this.isObject3D = !0 + } + var t = Object3D.prototype; + return t.applyMatrix = function(t) { + this.matrix.multiplyMatrices(t, this.matrix), this.matrix.decompose(this.position, this.quaternion, this.scale) + }, t.lookAt = function(t, e, r) { + var n = bt, + i = xt, + o = wt, + a = Tt; + t.isVector3 ? o.copy(t) : o.set(t, e, r); + var s = this.parent; + this.updateWorldMatrix(!0, !1), a.setFromMatrixPosition(this.matrixWorld), this.isCamera || this.isLight ? i.lookAt(a, o, this.up) : i.lookAt(o, a, this.up), this.quaternion.setFromRotationMatrix(i), s && (i.extractRotation(s.matrixWorld), n.setFromRotationMatrix(i), this.quaternion.premultiply(n.inverse())) + }, t.add = function(t) { + if (1 < arguments.length) { + for (var e = 0; e < arguments.length; e++) this.add(arguments[e]); + return this + } + return t === this || t && t.isObject3D && (null !== t.parent && t.parent.remove(t), (t.parent = this).children.push(t)), this + }, t.remove = function(t) { + if (1 < arguments.length) { + for (var e = 0; e < arguments.length; e++) this.remove(arguments[e]); + return this + } + var r = this.children.indexOf(t); + return -1 !== r && (t.parent = null, t.dispatchEvent({ + type: "removed" + }), this.children.splice(r, 1)), this + }, t.getObjectById = function(t) { + return this.getObjectByProperty("id", t) + }, t.getObjectByName = function(t) { + return this.getObjectByProperty("name", t) + }, t.getObjectByProperty = function(t, e) { + if (this[t] === e) return this; + for (var r = 0, n = this.children.length; r < n; r++) { + var i = this.children[r].getObjectByProperty(t, e); + if (void 0 !== i) return i + } + }, t.getWorldPosition = function(t) { + return void 0 === t && (t = new v), this.updateMatrixWorld(!0), t.setFromMatrixPosition(this.matrixWorld) + }, t.getWorldQuaternion = function(t) { + var e = wt, + r = Tt; + return void 0 === t && (t = new C), this.updateMatrixWorld(!0), this.matrixWorld.decompose(e, t, r), t + }, t.getWorldScale = function(t) { + var e = wt, + r = bt; + return void 0 === t && (t = new v), this.updateMatrixWorld(!0), this.matrixWorld.decompose(e, r, t), t + }, t.getWorldDirection = function(t) { + void 0 === t && (t = new v), this.updateMatrixWorld(!0); + var e = this.matrixWorld.elements; + return t.set(e[8], e[9], e[10]).normalize() + }, t.traverse = function(t) { + t(this); + for (var e = this.children, r = 0, n = e.length; r < n; r++) e[r].traverse(t) + }, t.traverseVisible = function(t) { + if (!1 !== this.visible) { + t(this); + for (var e = this.children, r = 0, n = e.length; r < n; r++) e[r].traverseVisible(t) + } + }, t.traverseAncestors = function(t) { + var e = this.parent; + null !== e && (t(e), e.traverseAncestors(t)) + }, t.updateMatrix = function() { + this.matrix.compose(this.position, this.quaternion, this.scale), this.matrixWorldNeedsUpdate = !0 + }, t.updateMatrixWorld = function(t) { + this.matrixAutoUpdate && this.updateMatrix(), (this.matrixWorldNeedsUpdate || t) && (null === this.parent ? this.matrixWorld.copy(this.matrix) : this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix), t = !(this.matrixWorldNeedsUpdate = !1)); + for (var e = this.children, r = 0, n = e.length; r < n; r++) e[r].updateMatrixWorld(t) + }, t.updateWorldMatrix = function(t, e) { + var r = this.parent; + if (!0 === t && null !== r && r.updateWorldMatrix(!0, !1), this.matrixAutoUpdate && this.updateMatrix(), null === this.parent ? this.matrixWorld.copy(this.matrix) : this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix), !0 === e) + for (var n = this.children, i = 0, o = n.length; i < o; i++) n[i].updateWorldMatrix(!1, !0) + }, Object3D + }(); + St.DefaultUp = new v(0, 1, 0), St.DefaultMatrixAutoUpdate = !0; + var Mt = function(e) { + var t, r; + + function Group() { + var t; + return (t = e.call(this) || this).type = "Group", t.isGroup = !0, t + } + return r = e, (t = Group).prototype = Object.create(r.prototype), (t.prototype.constructor = t).__proto__ = r, Group + }(St); + var Et = function(e) { + var t, r; + + function Bone() { + var t; + return (t = e.call(this) || this).type = "Bone", t.isBone = !0, t.root = !1, t + } + return r = e, (t = Bone).prototype = Object.create(r.prototype), (t.prototype.constructor = t).__proto__ = r, Bone + }(St), + At = 1, + Pt = function() { + function BufferGeometry() { + Object.defineProperty(this, "id", { + value: At += 2 + }), this.uuid = generateUUID(), this.name = "", this.type = "BufferGeometry", this.index = null, this.attributes = {}, this.morphAttributes = {}, this.groups = [], this.isBufferGeometry = !0 + } + var t = BufferGeometry.prototype; + return t.getIndex = function() { + return this.index + }, t.setIndex = function(t) { + Array.isArray(t) ? this.index = new(65535 < arrayMax(t) ? Uint32BufferAttribute : Uint16BufferAttribute)(t, 1) : this.index = t + }, t.addAttribute = function(t, e) { + return e && e.isBufferAttribute || e && e.isInterleavedBufferAttribute ? ("index" === t ? this.setIndex(e) : this.attributes[t] = e, this) : this.addAttribute(t, new o(e, arguments[2])) + }, t.getAttribute = function(t) { + return this.attributes[t] + }, t.removeAttribute = function(t) { + return delete this.attributes[t], this + }, t.addGroup = function(t, e, r) { + this.groups.push({ + start: t, + count: e, + materialIndex: void 0 !== r ? r : 0 + }) + }, t.clearGroups = function() { + this.groups = [] + }, t.applyMatrix = function(t) { + var e = this.attributes.position; + void 0 !== e && (t.applyToBufferAttribute(e), e.needsUpdate = !0); + var r = this.attributes.normal; + void 0 !== r && ((new w).getNormalMatrix(t).applyToBufferAttribute(r), r.needsUpdate = !0); + var n = this.attributes.tangent; + void 0 !== n && ((new w).getNormalMatrix(t).applyToBufferAttribute(n), n.needsUpdate = !0); + return this + }, BufferGeometry + }(), + It = 0, + Ot = function Texture(t, e, r, n, i, o, a, s, u, h) { + Object.defineProperty(this, "id", { + value: It++ + }), this.uuid = generateUUID(), this.name = "", this.image = void 0 !== t ? t : Texture.DEFAULT_IMAGE, this.mapping = void 0 !== e ? e : Texture.DEFAULT_MAPPING, this.wrapS = void 0 !== r ? r : 33071, this.wrapT = void 0 !== n ? n : 33071, this.magFilter = void 0 !== i ? i : 1006, this.minFilter = void 0 !== o ? o : 1008, this.anisotropy = void 0 !== u ? u : 1, this.format = void 0 !== a ? a : 1023, this.type = void 0 !== s ? s : 1009, this.encoding = void 0 !== h ? h : 3e3 + }; + Ot.DEFAULT_IMAGE = void 0, Ot.DEFAULT_MAPPING = 300; + var Ct = new S, + Rt = new S, + Dt = function() { + function Skeleton(t, e) { + if (t = t || [], this.bones = t.slice(0), this.boneMatrices = new Float32Array(16 * this.bones.length), void 0 === e) this.calculateInverses(); + else if (this.bones.length === e.length) this.boneInverses = e.slice(0); + else { + this.boneInverses = []; + for (var r = 0, n = this.bones.length; r < n; r++) this.boneInverses.push(new S) + } + } + var t = Skeleton.prototype; + return t.calculateInverses = function() { + this.boneInverses = []; + for (var t = 0, e = this.bones.length; t < e; t++) { + var r = new S; + this.bones[t] && r.getInverse(this.bones[t].matrixWorld), this.boneInverses.push(r) + } + }, t.pose = function() { + var t, e, r; + for (e = 0, r = this.bones.length; e < r; e++)(t = this.bones[e]) && t.matrixWorld.getInverse(this.boneInverses[e]); + for (e = 0, r = this.bones.length; e < r; e++)(t = this.bones[e]) && (t.parent && t.parent.isBone ? (t.matrix.getInverse(t.parent.matrixWorld), t.matrix.multiply(t.matrixWorld)) : t.matrix.copy(t.matrixWorld), t.matrix.decompose(t.position, t.quaternion, t.scale)) + }, t.update = function() { + for (var t = Ct, e = Rt, r = this.bones, n = this.boneInverses, i = this.boneMatrices, o = this.boneTexture, a = 0, s = r.length; a < s; a++) { + var u = r[a] ? r[a].matrixWorld : e; + t.multiplyMatrices(u, n[a]), t.toArray(i, 16 * a) + } + void 0 !== o && (o.needsUpdate = !0) + }, t.clone = function() { + return new Skeleton(this.bones, this.boneInverses) + }, t.getBoneByName = function(t) { + for (var e = 0, r = this.bones.length; e < r; e++) { + var n = this.bones[e]; + if (n.name === t) return n + } + }, Skeleton + }(); + var Ft = function(n) { + var t, e; + + function Mesh(t, e) { + var r; + return (r = n.call(this) || this).type = "Bone", r.isMesh = !0, r.geometry = void 0 !== t ? t : new Pt, r.material = void 0 !== e ? e : new MeshBasicMaterial({ + color: 16777215 * Math.random() + }), r + } + return e = n, (t = Mesh).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e, Mesh + }(St); + var kt = function(n) { + var t, e; + + function SkinnedMesh(t, e) { + var r; + return (r = n.call(this, t, e) || this).type = "SkinnedMesh", r.isSkinnedMesh = !0, r.bindMode = "attached", r.bindMatrix = new S, r.bindMatrixInverse = new S, r + } + e = n, (t = SkinnedMesh).prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e; + var r = SkinnedMesh.prototype; + return r.bind = function(t, e) { + this.skeleton = t, void 0 === e && (this.updateMatrixWorld(!0), this.skeleton.calculateInverses(), e = this.matrixWorld), this.bindMatrix.copy(e), this.bindMatrixInverse.getInverse(e) + }, r.pose = function() { + this.skeleton.pose() + }, r.normalizeSkinWeights = function() { + for (var t = new _, e = this.geometry.attributes.skinWeight, r = 0, n = e.count; r < n; r++) { + t.x = e.getX(r), t.y = e.getY(r), t.z = e.getZ(r), t.w = e.getW(r); + var i = 1 / t.manhattanLength(); + i !== 1 / 0 ? t.multiplyScalar(i) : t.set(1, 0, 0, 0), e.setXYZW(r, t.x, t.y, t.z, t.w) + } + }, r.updateMatrixWorld = function(t) { + n.prototype.updateMatrixWorld.call(this, t), "attached" === this.bindMode ? this.bindMatrixInverse.getInverse(this.matrixWorld) : "detached" === this.bindMode && this.bindMatrixInverse.getInverse(this.bindMatrix) + }, SkinnedMesh + }(Ft); + r.d(e, "e", function() { + return o + }), r.d(e, "l", function() { + return a + }), r.d(e, "O", function() { + return s + }), r.d(e, "t", function() { + return n + }), r.d(e, "h", function() { + return m + }), r.d(e, "P", function() { + return v + }), r.d(e, "Q", function() { + return _ + }), r.d(e, "u", function() { + return w + }), r.d(e, "v", function() { + return S + }), r.d(e, "G", function() { + return C + }), r.d(e, "k", function() { + return F + }), r.d(e, "I", function() { + return 10497 + }), r.d(e, "g", function() { + return 33071 + }), r.d(e, "S", function() { + return 2 + }), r.d(e, "r", function() { + return 2200 + }), r.d(e, "s", function() { + return 2201 + }), r.d(e, "y", function() { + return j.a + }), r.d(e, "x", function() { + return X.a + }), r.d(e, "A", function() { + return tt + }), r.d(e, "H", function() { + return $ + }), r.d(e, "R", function() { + return et + }), r.d(e, "b", function() { + return nt + }), r.d(e, "c", function() { + return yt + }), r.d(e, "F", function() { + return vt + }), r.d(e, "q", function() { + return i + }), r.d(e, "B", function() { + return St + }), r.d(e, "m", function() { + return Mt + }), r.d(e, "d", function() { + return Et + }), r.d(e, "f", function() { + return Pt + }), r.d(e, "N", function() { + return Ot + }), r.d(e, "J", function() { + return Dt + }), r.d(e, "w", function() { + return Ft + }), r.d(e, "K", function() { + return kt + }) + }, function(t, e, r) { + "use strict"; + e.__esModule = !0; + var i = function() { + function defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + return function(t, e, r) { + return e && defineProperties(t.prototype, e), r && defineProperties(t, r), t + } + }(), + o = function(t) { + { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var r in t) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e.default = t, e + } + }(r(0)), + n = _interopRequireDefault(r(418)), + a = _interopRequireDefault(r(423)); + + function _interopRequireDefault(t) { + return t && t.__esModule ? t : { + default: t + } + } + var s = function(n) { + function Entity3d(t) { + ! function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, Entity3d); + var e = function(t, e) { + if (!t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return !e || "object" != typeof e && "function" != typeof e ? t : e + }(this, n.call(this)); + if (t) { + if (!t.state) { + var r = new o.State; + r.depthTest = !0, r.blend = !1, r.culling = !0, t.state = r + } + e.add(a.default, t) + } + return e + } + return function(t, e) { + if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function, not " + typeof e); + t.prototype = Object.create(e && e.prototype, { + constructor: { + value: t, + enumerable: !1, + writable: !0, + configurable: !0 + } + }), e && (Object.setPrototypeOf ? Object.setPrototypeOf(t, e) : t.__proto__ = e) + }(Entity3d, n), i(Entity3d, [{ + key: "x", + set: function(t) { + this.transform.position.x = t + }, + get: function() { + return this.transform.position.x + } + }, { + key: "y", + set: function(t) { + this.transform.position.y = t + }, + get: function() { + return this.transform.position.y + } + }, { + key: "z", + set: function(t) { + this.transform.position.z = t + }, + get: function() { + return this.transform.position.z + } + }, { + key: "sx", + set: function(t) { + this.transform.scale.x = t + }, + get: function() { + return this.transform.scale.x + } + }, { + key: "sy", + set: function(t) { + this.transform.scale.y = t + }, + get: function() { + return this.transform.scale.y + } + }, { + key: "sz", + set: function(t) { + this.transform.scale.z = t + }, + get: function() { + return this.transform.scale.z + } + }, { + key: "rx", + set: function(t) { + this.transform.rotation.x = t + }, + get: function() { + return this.transform.rotation.x + } + }, { + key: "ry", + set: function(t) { + this.transform.rotation.y = t + }, + get: function() { + return this.transform.rotation.y + } + }, { + key: "rz", + set: function(t) { + this.transform.rotation.z = t + }, + get: function() { + return this.transform.rotation.z + } + }, { + key: "position", + get: function() { + return this.transform.position + } + }, { + key: "scale", + get: function() { + return this.transform.scale + } + }, { + key: "rotation", + get: function() { + return this.transform.rotation + } + }]), Entity3d + }(n.default); + e.default = s + }, function(t, e, r) { + "use strict"; + r.r(e); + var n = {}; + r.r(n), r.d(n, "EPSILON", function() { + return F + }), r.d(n, "ARRAY_TYPE", function() { + return g + }), r.d(n, "RANDOM", function() { + return d + }), r.d(n, "setMatrixArrayType", function() { + return setMatrixArrayType + }), r.d(n, "toRadian", function() { + return toRadian + }), r.d(n, "equals", function() { + return equals + }); + var i = {}; + r.r(i), r.d(i, "create", function() { + return create + }), r.d(i, "clone", function() { + return clone + }), r.d(i, "copy", function() { + return copy + }), r.d(i, "identity", function() { + return identity + }), r.d(i, "fromValues", function() { + return fromValues + }), r.d(i, "set", function() { + return set + }), r.d(i, "transpose", function() { + return transpose + }), r.d(i, "invert", function() { + return invert + }), r.d(i, "adjoint", function() { + return adjoint + }), r.d(i, "determinant", function() { + return determinant + }), r.d(i, "multiply", function() { + return multiply + }), r.d(i, "rotate", function() { + return rotate + }), r.d(i, "scale", function() { + return mat2_scale + }), r.d(i, "fromRotation", function() { + return fromRotation + }), r.d(i, "fromScaling", function() { + return fromScaling + }), r.d(i, "str", function() { + return str + }), r.d(i, "frob", function() { + return frob + }), r.d(i, "LDU", function() { + return LDU + }), r.d(i, "add", function() { + return add + }), r.d(i, "subtract", function() { + return subtract + }), r.d(i, "exactEquals", function() { + return exactEquals + }), r.d(i, "equals", function() { + return mat2_equals + }), r.d(i, "multiplyScalar", function() { + return multiplyScalar + }), r.d(i, "multiplyScalarAndAdd", function() { + return multiplyScalarAndAdd + }), r.d(i, "mul", function() { + return m + }), r.d(i, "sub", function() { + return v + }); + var o = {}; + r.r(o), r.d(o, "create", function() { + return mat2d_create + }), r.d(o, "clone", function() { + return mat2d_clone + }), r.d(o, "copy", function() { + return mat2d_copy + }), r.d(o, "identity", function() { + return mat2d_identity + }), r.d(o, "fromValues", function() { + return mat2d_fromValues + }), r.d(o, "set", function() { + return mat2d_set + }), r.d(o, "invert", function() { + return mat2d_invert + }), r.d(o, "determinant", function() { + return mat2d_determinant + }), r.d(o, "multiply", function() { + return mat2d_multiply + }), r.d(o, "rotate", function() { + return mat2d_rotate + }), r.d(o, "scale", function() { + return mat2d_scale + }), r.d(o, "translate", function() { + return translate + }), r.d(o, "fromRotation", function() { + return mat2d_fromRotation + }), r.d(o, "fromScaling", function() { + return mat2d_fromScaling + }), r.d(o, "fromTranslation", function() { + return fromTranslation + }), r.d(o, "str", function() { + return mat2d_str + }), r.d(o, "frob", function() { + return mat2d_frob + }), r.d(o, "add", function() { + return mat2d_add + }), r.d(o, "subtract", function() { + return mat2d_subtract + }), r.d(o, "multiplyScalar", function() { + return mat2d_multiplyScalar + }), r.d(o, "multiplyScalarAndAdd", function() { + return mat2d_multiplyScalarAndAdd + }), r.d(o, "exactEquals", function() { + return mat2d_exactEquals + }), r.d(o, "equals", function() { + return mat2d_equals + }), r.d(o, "mul", function() { + return y + }), r.d(o, "sub", function() { + return _ + }); + var a = {}; + r.r(a), r.d(a, "create", function() { + return mat3_create + }), r.d(a, "fromMat4", function() { + return fromMat4 + }), r.d(a, "clone", function() { + return mat3_clone + }), r.d(a, "copy", function() { + return mat3_copy + }), r.d(a, "fromValues", function() { + return mat3_fromValues + }), r.d(a, "set", function() { + return mat3_set + }), r.d(a, "identity", function() { + return mat3_identity + }), r.d(a, "transpose", function() { + return mat3_transpose + }), r.d(a, "invert", function() { + return mat3_invert + }), r.d(a, "adjoint", function() { + return mat3_adjoint + }), r.d(a, "determinant", function() { + return mat3_determinant + }), r.d(a, "multiply", function() { + return mat3_multiply + }), r.d(a, "translate", function() { + return mat3_translate + }), r.d(a, "rotate", function() { + return mat3_rotate + }), r.d(a, "scale", function() { + return mat3_scale + }), r.d(a, "fromTranslation", function() { + return mat3_fromTranslation + }), r.d(a, "fromRotation", function() { + return mat3_fromRotation + }), r.d(a, "fromScaling", function() { + return mat3_fromScaling + }), r.d(a, "fromMat2d", function() { + return fromMat2d + }), r.d(a, "fromQuat", function() { + return fromQuat + }), r.d(a, "normalFromMat4", function() { + return normalFromMat4 + }), r.d(a, "projection", function() { + return projection + }), r.d(a, "str", function() { + return mat3_str + }), r.d(a, "frob", function() { + return mat3_frob + }), r.d(a, "add", function() { + return mat3_add + }), r.d(a, "subtract", function() { + return mat3_subtract + }), r.d(a, "multiplyScalar", function() { + return mat3_multiplyScalar + }), r.d(a, "multiplyScalarAndAdd", function() { + return mat3_multiplyScalarAndAdd + }), r.d(a, "exactEquals", function() { + return mat3_exactEquals + }), r.d(a, "equals", function() { + return mat3_equals + }), r.d(a, "mul", function() { + return b + }), r.d(a, "sub", function() { + return x + }); + var s = {}; + r.r(s), r.d(s, "create", function() { + return mat4_create + }), r.d(s, "clone", function() { + return mat4_clone + }), r.d(s, "copy", function() { + return mat4_copy + }), r.d(s, "fromValues", function() { + return mat4_fromValues + }), r.d(s, "set", function() { + return mat4_set + }), r.d(s, "identity", function() { + return mat4_identity + }), r.d(s, "transpose", function() { + return mat4_transpose + }), r.d(s, "invert", function() { + return mat4_invert + }), r.d(s, "adjoint", function() { + return mat4_adjoint + }), r.d(s, "determinant", function() { + return mat4_determinant + }), r.d(s, "multiply", function() { + return mat4_multiply + }), r.d(s, "translate", function() { + return mat4_translate + }), r.d(s, "scale", function() { + return mat4_scale + }), r.d(s, "rotate", function() { + return mat4_rotate + }), r.d(s, "rotateX", function() { + return rotateX + }), r.d(s, "rotateY", function() { + return rotateY + }), r.d(s, "rotateZ", function() { + return rotateZ + }), r.d(s, "fromTranslation", function() { + return mat4_fromTranslation + }), r.d(s, "fromScaling", function() { + return mat4_fromScaling + }), r.d(s, "fromRotation", function() { + return mat4_fromRotation + }), r.d(s, "fromXRotation", function() { + return fromXRotation + }), r.d(s, "fromYRotation", function() { + return fromYRotation + }), r.d(s, "fromZRotation", function() { + return fromZRotation + }), r.d(s, "fromRotationTranslation", function() { + return fromRotationTranslation + }), r.d(s, "fromQuat2", function() { + return fromQuat2 + }), r.d(s, "getTranslation", function() { + return getTranslation + }), r.d(s, "getScaling", function() { + return getScaling + }), r.d(s, "getRotation", function() { + return getRotation + }), r.d(s, "fromRotationTranslationScale", function() { + return fromRotationTranslationScale + }), r.d(s, "fromRotationTranslationScaleOrigin", function() { + return fromRotationTranslationScaleOrigin + }), r.d(s, "fromQuat", function() { + return mat4_fromQuat + }), r.d(s, "frustum", function() { + return frustum + }), r.d(s, "perspective", function() { + return perspective + }), r.d(s, "perspectiveFromFieldOfView", function() { + return perspectiveFromFieldOfView + }), r.d(s, "ortho", function() { + return ortho + }), r.d(s, "lookAt", function() { + return lookAt + }), r.d(s, "targetTo", function() { + return targetTo + }), r.d(s, "str", function() { + return mat4_str + }), r.d(s, "frob", function() { + return mat4_frob + }), r.d(s, "add", function() { + return mat4_add + }), r.d(s, "subtract", function() { + return mat4_subtract + }), r.d(s, "multiplyScalar", function() { + return mat4_multiplyScalar + }), r.d(s, "multiplyScalarAndAdd", function() { + return mat4_multiplyScalarAndAdd + }), r.d(s, "exactEquals", function() { + return mat4_exactEquals + }), r.d(s, "equals", function() { + return mat4_equals + }), r.d(s, "mul", function() { + return w + }), r.d(s, "sub", function() { + return T + }); + var u = {}; + r.r(u), r.d(u, "create", function() { + return vec3_create + }), r.d(u, "clone", function() { + return vec3_clone + }), r.d(u, "length", function() { + return vec3_length + }), r.d(u, "fromValues", function() { + return vec3_fromValues + }), r.d(u, "copy", function() { + return vec3_copy + }), r.d(u, "set", function() { + return vec3_set + }), r.d(u, "add", function() { + return vec3_add + }), r.d(u, "subtract", function() { + return vec3_subtract + }), r.d(u, "multiply", function() { + return vec3_multiply + }), r.d(u, "divide", function() { + return divide + }), r.d(u, "ceil", function() { + return ceil + }), r.d(u, "floor", function() { + return floor + }), r.d(u, "min", function() { + return min + }), r.d(u, "max", function() { + return max + }), r.d(u, "round", function() { + return round + }), r.d(u, "scale", function() { + return vec3_scale + }), r.d(u, "scaleAndAdd", function() { + return scaleAndAdd + }), r.d(u, "distance", function() { + return distance + }), r.d(u, "squaredDistance", function() { + return squaredDistance + }), r.d(u, "squaredLength", function() { + return squaredLength + }), r.d(u, "negate", function() { + return negate + }), r.d(u, "inverse", function() { + return inverse + }), r.d(u, "normalize", function() { + return normalize + }), r.d(u, "dot", function() { + return vec3_dot + }), r.d(u, "cross", function() { + return cross + }), r.d(u, "lerp", function() { + return lerp + }), r.d(u, "hermite", function() { + return hermite + }), r.d(u, "bezier", function() { + return bezier + }), r.d(u, "random", function() { + return random + }), r.d(u, "transformMat4", function() { + return transformMat4 + }), r.d(u, "transformMat3", function() { + return transformMat3 + }), r.d(u, "transformQuat", function() { + return transformQuat + }), r.d(u, "rotateX", function() { + return vec3_rotateX + }), r.d(u, "rotateY", function() { + return vec3_rotateY + }), r.d(u, "rotateZ", function() { + return vec3_rotateZ + }), r.d(u, "angle", function() { + return angle + }), r.d(u, "str", function() { + return vec3_str + }), r.d(u, "exactEquals", function() { + return vec3_exactEquals + }), r.d(u, "equals", function() { + return vec3_equals + }), r.d(u, "sub", function() { + return M + }), r.d(u, "mul", function() { + return E + }), r.d(u, "div", function() { + return A + }), r.d(u, "dist", function() { + return P + }), r.d(u, "sqrDist", function() { + return I + }), r.d(u, "len", function() { + return O + }), r.d(u, "sqrLen", function() { + return C + }), r.d(u, "forEach", function() { + return R + }); + var h = {}; + r.r(h), r.d(h, "create", function() { + return vec4_create + }), r.d(h, "clone", function() { + return vec4_clone + }), r.d(h, "fromValues", function() { + return vec4_fromValues + }), r.d(h, "copy", function() { + return vec4_copy + }), r.d(h, "set", function() { + return vec4_set + }), r.d(h, "add", function() { + return vec4_add + }), r.d(h, "subtract", function() { + return vec4_subtract + }), r.d(h, "multiply", function() { + return vec4_multiply + }), r.d(h, "divide", function() { + return vec4_divide + }), r.d(h, "ceil", function() { + return vec4_ceil + }), r.d(h, "floor", function() { + return vec4_floor + }), r.d(h, "min", function() { + return vec4_min + }), r.d(h, "max", function() { + return vec4_max + }), r.d(h, "round", function() { + return vec4_round + }), r.d(h, "scale", function() { + return vec4_scale + }), r.d(h, "scaleAndAdd", function() { + return vec4_scaleAndAdd + }), r.d(h, "distance", function() { + return vec4_distance + }), r.d(h, "squaredDistance", function() { + return vec4_squaredDistance + }), r.d(h, "length", function() { + return vec4_length + }), r.d(h, "squaredLength", function() { + return vec4_squaredLength + }), r.d(h, "negate", function() { + return vec4_negate + }), r.d(h, "inverse", function() { + return vec4_inverse + }), r.d(h, "normalize", function() { + return vec4_normalize + }), r.d(h, "dot", function() { + return vec4_dot + }), r.d(h, "lerp", function() { + return vec4_lerp + }), r.d(h, "random", function() { + return vec4_random + }), r.d(h, "transformMat4", function() { + return vec4_transformMat4 + }), r.d(h, "transformQuat", function() { + return vec4_transformQuat + }), r.d(h, "str", function() { + return vec4_str + }), r.d(h, "exactEquals", function() { + return vec4_exactEquals + }), r.d(h, "equals", function() { + return vec4_equals + }), r.d(h, "sub", function() { + return k + }), r.d(h, "mul", function() { + return L + }), r.d(h, "div", function() { + return N + }), r.d(h, "dist", function() { + return B + }), r.d(h, "sqrDist", function() { + return U + }), r.d(h, "len", function() { + return G + }), r.d(h, "sqrLen", function() { + return j + }), r.d(h, "forEach", function() { + return z + }); + var c = {}; + r.r(c), r.d(c, "create", function() { + return quat_create + }), r.d(c, "identity", function() { + return quat_identity + }), r.d(c, "setAxisAngle", function() { + return setAxisAngle + }), r.d(c, "getAxisAngle", function() { + return getAxisAngle + }), r.d(c, "multiply", function() { + return quat_multiply + }), r.d(c, "rotateX", function() { + return quat_rotateX + }), r.d(c, "rotateY", function() { + return quat_rotateY + }), r.d(c, "rotateZ", function() { + return quat_rotateZ + }), r.d(c, "calculateW", function() { + return calculateW + }), r.d(c, "slerp", function() { + return slerp + }), r.d(c, "random", function() { + return quat_random + }), r.d(c, "invert", function() { + return quat_invert + }), r.d(c, "conjugate", function() { + return conjugate + }), r.d(c, "fromMat3", function() { + return fromMat3 + }), r.d(c, "fromEuler", function() { + return fromEuler + }), r.d(c, "str", function() { + return quat_str + }), r.d(c, "clone", function() { + return K + }), r.d(c, "fromValues", function() { + return Z + }), r.d(c, "copy", function() { + return Q + }), r.d(c, "set", function() { + return J + }), r.d(c, "add", function() { + return $ + }), r.d(c, "mul", function() { + return tt + }), r.d(c, "scale", function() { + return et + }), r.d(c, "dot", function() { + return rt + }), r.d(c, "lerp", function() { + return nt + }), r.d(c, "length", function() { + return it + }), r.d(c, "len", function() { + return ot + }), r.d(c, "squaredLength", function() { + return at + }), r.d(c, "sqrLen", function() { + return st + }), r.d(c, "normalize", function() { + return ut + }), r.d(c, "exactEquals", function() { + return ht + }), r.d(c, "equals", function() { + return ct + }), r.d(c, "rotationTo", function() { + return lt + }), r.d(c, "sqlerp", function() { + return ft + }), r.d(c, "setAxes", function() { + return dt + }); + var l = {}; + r.r(l), r.d(l, "create", function() { + return quat2_create + }), r.d(l, "clone", function() { + return quat2_clone + }), r.d(l, "fromValues", function() { + return quat2_fromValues + }), r.d(l, "fromRotationTranslationValues", function() { + return fromRotationTranslationValues + }), r.d(l, "fromRotationTranslation", function() { + return quat2_fromRotationTranslation + }), r.d(l, "fromTranslation", function() { + return quat2_fromTranslation + }), r.d(l, "fromRotation", function() { + return quat2_fromRotation + }), r.d(l, "fromMat4", function() { + return quat2_fromMat4 + }), r.d(l, "copy", function() { + return quat2_copy + }), r.d(l, "identity", function() { + return quat2_identity + }), r.d(l, "set", function() { + return quat2_set + }), r.d(l, "getReal", function() { + return pt + }), r.d(l, "getDual", function() { + return getDual + }), r.d(l, "setReal", function() { + return mt + }), r.d(l, "setDual", function() { + return setDual + }), r.d(l, "getTranslation", function() { + return quat2_getTranslation + }), r.d(l, "translate", function() { + return quat2_translate + }), r.d(l, "rotateX", function() { + return quat2_rotateX + }), r.d(l, "rotateY", function() { + return quat2_rotateY + }), r.d(l, "rotateZ", function() { + return quat2_rotateZ + }), r.d(l, "rotateByQuatAppend", function() { + return rotateByQuatAppend + }), r.d(l, "rotateByQuatPrepend", function() { + return rotateByQuatPrepend + }), r.d(l, "rotateAroundAxis", function() { + return rotateAroundAxis + }), r.d(l, "add", function() { + return quat2_add + }), r.d(l, "multiply", function() { + return quat2_multiply + }), r.d(l, "mul", function() { + return vt + }), r.d(l, "scale", function() { + return quat2_scale + }), r.d(l, "dot", function() { + return gt + }), r.d(l, "lerp", function() { + return quat2_lerp + }), r.d(l, "invert", function() { + return quat2_invert + }), r.d(l, "conjugate", function() { + return quat2_conjugate + }), r.d(l, "length", function() { + return yt + }), r.d(l, "len", function() { + return _t + }), r.d(l, "squaredLength", function() { + return bt + }), r.d(l, "sqrLen", function() { + return xt + }), r.d(l, "normalize", function() { + return quat2_normalize + }), r.d(l, "str", function() { + return quat2_str + }), r.d(l, "exactEquals", function() { + return quat2_exactEquals + }), r.d(l, "equals", function() { + return quat2_equals + }); + var f = {}; + r.r(f), r.d(f, "create", function() { + return vec2_create + }), r.d(f, "clone", function() { + return vec2_clone + }), r.d(f, "fromValues", function() { + return vec2_fromValues + }), r.d(f, "copy", function() { + return vec2_copy + }), r.d(f, "set", function() { + return vec2_set + }), r.d(f, "add", function() { + return vec2_add + }), r.d(f, "subtract", function() { + return vec2_subtract + }), r.d(f, "multiply", function() { + return vec2_multiply + }), r.d(f, "divide", function() { + return vec2_divide + }), r.d(f, "ceil", function() { + return vec2_ceil + }), r.d(f, "floor", function() { + return vec2_floor + }), r.d(f, "min", function() { + return vec2_min + }), r.d(f, "max", function() { + return vec2_max + }), r.d(f, "round", function() { + return vec2_round + }), r.d(f, "scale", function() { + return vec2_scale + }), r.d(f, "scaleAndAdd", function() { + return vec2_scaleAndAdd + }), r.d(f, "distance", function() { + return vec2_distance + }), r.d(f, "squaredDistance", function() { + return vec2_squaredDistance + }), r.d(f, "length", function() { + return vec2_length + }), r.d(f, "squaredLength", function() { + return vec2_squaredLength + }), r.d(f, "negate", function() { + return vec2_negate + }), r.d(f, "inverse", function() { + return vec2_inverse + }), r.d(f, "normalize", function() { + return vec2_normalize + }), r.d(f, "dot", function() { + return vec2_dot + }), r.d(f, "cross", function() { + return vec2_cross + }), r.d(f, "lerp", function() { + return vec2_lerp + }), r.d(f, "random", function() { + return vec2_random + }), r.d(f, "transformMat2", function() { + return transformMat2 + }), r.d(f, "transformMat2d", function() { + return transformMat2d + }), r.d(f, "transformMat3", function() { + return vec2_transformMat3 + }), r.d(f, "transformMat4", function() { + return vec2_transformMat4 + }), r.d(f, "rotate", function() { + return vec2_rotate + }), r.d(f, "angle", function() { + return vec2_angle + }), r.d(f, "str", function() { + return vec2_str + }), r.d(f, "exactEquals", function() { + return vec2_exactEquals + }), r.d(f, "equals", function() { + return vec2_equals + }), r.d(f, "len", function() { + return Tt + }), r.d(f, "sub", function() { + return St + }), r.d(f, "mul", function() { + return Mt + }), r.d(f, "div", function() { + return Et + }), r.d(f, "dist", function() { + return At + }), r.d(f, "sqrDist", function() { + return Pt + }), r.d(f, "sqrLen", function() { + return It + }), r.d(f, "forEach", function() { + return Ot + }); + var F = 1e-6, + g = "undefined" != typeof Float32Array ? Float32Array : Array, + d = Math.random; + + function setMatrixArrayType(t) { + g = t + } + var p = Math.PI / 180; + + function toRadian(t) { + return t * p + } + + function equals(t, e) { + return Math.abs(t - e) <= F * Math.max(1, Math.abs(t), Math.abs(e)) + } + + function create() { + var t = new g(4); + return g != Float32Array && (t[1] = 0, t[2] = 0), t[0] = 1, t[3] = 1, t + } + + function clone(t) { + var e = new g(4); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e + } + + function copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t + } + + function identity(t) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 1, t + } + + function fromValues(t, e, r, n) { + var i = new g(4); + return i[0] = t, i[1] = e, i[2] = r, i[3] = n, i + } + + function set(t, e, r, n, i) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t + } + + function transpose(t, e) { + if (t === e) { + var r = e[1]; + t[1] = e[2], t[2] = r + } else t[0] = e[0], t[1] = e[2], t[2] = e[1], t[3] = e[3]; + return t + } + + function invert(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = r * o - i * n; + return a ? (a = 1 / a, t[0] = o * a, t[1] = -n * a, t[2] = -i * a, t[3] = r * a, t) : null + } + + function adjoint(t, e) { + var r = e[0]; + return t[0] = e[3], t[1] = -e[1], t[2] = -e[2], t[3] = r, t + } + + function determinant(t) { + return t[0] * t[3] - t[2] * t[1] + } + + function multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = r[0], + u = r[1], + h = r[2], + c = r[3]; + return t[0] = n * s + o * u, t[1] = i * s + a * u, t[2] = n * h + o * c, t[3] = i * h + a * c, t + } + + function rotate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = Math.sin(r), + u = Math.cos(r); + return t[0] = n * u + o * s, t[1] = i * u + a * s, t[2] = n * -s + o * u, t[3] = i * -s + a * u, t + } + + function mat2_scale(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = r[0], + u = r[1]; + return t[0] = n * s, t[1] = i * s, t[2] = o * u, t[3] = a * u, t + } + + function fromRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = n, t[1] = r, t[2] = -r, t[3] = n, t + } + + function fromScaling(t, e) { + return t[0] = e[0], t[1] = 0, t[2] = 0, t[3] = e[1], t + } + + function str(t) { + return "mat2(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ")" + } + + function frob(t) { + return Math.sqrt(Math.pow(t[0], 2) + Math.pow(t[1], 2) + Math.pow(t[2], 2) + Math.pow(t[3], 2)) + } + + function LDU(t, e, r, n) { + return t[2] = n[2] / n[0], r[0] = n[0], r[1] = n[1], r[3] = n[3] - t[2] * r[1], [t, e, r] + } + + function add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t + } + + function subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t[3] = e[3] - r[3], t + } + + function exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] + } + + function mat2_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = e[0], + s = e[1], + u = e[2], + h = e[3]; + return Math.abs(r - a) <= F * Math.max(1, Math.abs(r), Math.abs(a)) && Math.abs(n - s) <= F * Math.max(1, Math.abs(n), Math.abs(s)) && Math.abs(i - u) <= F * Math.max(1, Math.abs(i), Math.abs(u)) && Math.abs(o - h) <= F * Math.max(1, Math.abs(o), Math.abs(h)) + } + + function multiplyScalar(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t + } + + function multiplyScalarAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t[3] = e[3] + r[3] * n, t + } + var m = multiply, + v = subtract; + + function mat2d_create() { + var t = new g(6); + return g != Float32Array && (t[1] = 0, t[2] = 0, t[4] = 0, t[5] = 0), t[0] = 1, t[3] = 1, t + } + + function mat2d_clone(t) { + var e = new g(6); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e[4] = t[4], e[5] = t[5], e + } + + function mat2d_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4], t[5] = e[5], t + } + + function mat2d_identity(t) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 1, t[4] = 0, t[5] = 0, t + } + + function mat2d_fromValues(t, e, r, n, i, o) { + var a = new g(6); + return a[0] = t, a[1] = e, a[2] = r, a[3] = n, a[4] = i, a[5] = o, a + } + + function mat2d_set(t, e, r, n, i, o, a) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t[4] = o, t[5] = a, t + } + + function mat2d_invert(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = r * o - n * i; + return u ? (u = 1 / u, t[0] = o * u, t[1] = -n * u, t[2] = -i * u, t[3] = r * u, t[4] = (i * s - o * a) * u, t[5] = (n * a - r * s) * u, t) : null + } + + function mat2d_determinant(t) { + return t[0] * t[3] - t[1] * t[2] + } + + function mat2d_multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = r[0], + c = r[1], + l = r[2], + f = r[3], + d = r[4], + p = r[5]; + return t[0] = n * h + o * c, t[1] = i * h + a * c, t[2] = n * l + o * f, t[3] = i * l + a * f, t[4] = n * d + o * p + s, t[5] = i * d + a * p + u, t + } + + function mat2d_rotate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = Math.sin(r), + c = Math.cos(r); + return t[0] = n * c + o * h, t[1] = i * c + a * h, t[2] = n * -h + o * c, t[3] = i * -h + a * c, t[4] = s, t[5] = u, t + } + + function mat2d_scale(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = r[0], + c = r[1]; + return t[0] = n * h, t[1] = i * h, t[2] = o * c, t[3] = a * c, t[4] = s, t[5] = u, t + } + + function translate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = r[0], + c = r[1]; + return t[0] = n, t[1] = i, t[2] = o, t[3] = a, t[4] = n * h + o * c + s, t[5] = i * h + a * c + u, t + } + + function mat2d_fromRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = n, t[1] = r, t[2] = -r, t[3] = n, t[4] = 0, t[5] = 0, t + } + + function mat2d_fromScaling(t, e) { + return t[0] = e[0], t[1] = 0, t[2] = 0, t[3] = e[1], t[4] = 0, t[5] = 0, t + } + + function fromTranslation(t, e) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 1, t[4] = e[0], t[5] = e[1], t + } + + function mat2d_str(t) { + return "mat2d(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ", " + t[4] + ", " + t[5] + ")" + } + + function mat2d_frob(t) { + return Math.sqrt(Math.pow(t[0], 2) + Math.pow(t[1], 2) + Math.pow(t[2], 2) + Math.pow(t[3], 2) + Math.pow(t[4], 2) + Math.pow(t[5], 2) + 1) + } + + function mat2d_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t[4] = e[4] + r[4], t[5] = e[5] + r[5], t + } + + function mat2d_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t[3] = e[3] - r[3], t[4] = e[4] - r[4], t[5] = e[5] - r[5], t + } + + function mat2d_multiplyScalar(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t[4] = e[4] * r, t[5] = e[5] * r, t + } + + function mat2d_multiplyScalarAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t[3] = e[3] + r[3] * n, t[4] = e[4] + r[4] * n, t[5] = e[5] + r[5] * n, t + } + + function mat2d_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] && t[4] === e[4] && t[5] === e[5] + } + + function mat2d_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = t[4], + s = t[5], + u = e[0], + h = e[1], + c = e[2], + l = e[3], + f = e[4], + d = e[5]; + return Math.abs(r - u) <= F * Math.max(1, Math.abs(r), Math.abs(u)) && Math.abs(n - h) <= F * Math.max(1, Math.abs(n), Math.abs(h)) && Math.abs(i - c) <= F * Math.max(1, Math.abs(i), Math.abs(c)) && Math.abs(o - l) <= F * Math.max(1, Math.abs(o), Math.abs(l)) && Math.abs(a - f) <= F * Math.max(1, Math.abs(a), Math.abs(f)) && Math.abs(s - d) <= F * Math.max(1, Math.abs(s), Math.abs(d)) + } + var y = mat2d_multiply, + _ = mat2d_subtract; + + function mat3_create() { + var t = new g(9); + return g != Float32Array && (t[1] = 0, t[2] = 0, t[3] = 0, t[5] = 0, t[6] = 0, t[7] = 0), t[0] = 1, t[4] = 1, t[8] = 1, t + } + + function fromMat4(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[4], t[4] = e[5], t[5] = e[6], t[6] = e[8], t[7] = e[9], t[8] = e[10], t + } + + function mat3_clone(t) { + var e = new g(9); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e[4] = t[4], e[5] = t[5], e[6] = t[6], e[7] = t[7], e[8] = t[8], e + } + + function mat3_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4], t[5] = e[5], t[6] = e[6], t[7] = e[7], t[8] = e[8], t + } + + function mat3_fromValues(t, e, r, n, i, o, a, s, u) { + var h = new g(9); + return h[0] = t, h[1] = e, h[2] = r, h[3] = n, h[4] = i, h[5] = o, h[6] = a, h[7] = s, h[8] = u, h + } + + function mat3_set(t, e, r, n, i, o, a, s, u, h) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t[4] = o, t[5] = a, t[6] = s, t[7] = u, t[8] = h, t + } + + function mat3_identity(t) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 1, t[5] = 0, t[6] = 0, t[7] = 0, t[8] = 1, t + } + + function mat3_transpose(t, e) { + if (t === e) { + var r = e[1], + n = e[2], + i = e[5]; + t[1] = e[3], t[2] = e[6], t[3] = r, t[5] = e[7], t[6] = n, t[7] = i + } else t[0] = e[0], t[1] = e[3], t[2] = e[6], t[3] = e[1], t[4] = e[4], t[5] = e[7], t[6] = e[2], t[7] = e[5], t[8] = e[8]; + return t + } + + function mat3_invert(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = e[6], + h = e[7], + c = e[8], + l = c * a - s * h, + f = -c * o + s * u, + d = h * o - a * u, + p = r * l + n * f + i * d; + return p ? (p = 1 / p, t[0] = l * p, t[1] = (-c * n + i * h) * p, t[2] = (s * n - i * a) * p, t[3] = f * p, t[4] = (c * r - i * u) * p, t[5] = (-s * r + i * o) * p, t[6] = d * p, t[7] = (-h * r + n * u) * p, t[8] = (a * r - n * o) * p, t) : null + } + + function mat3_adjoint(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = e[6], + h = e[7], + c = e[8]; + return t[0] = a * c - s * h, t[1] = i * h - n * c, t[2] = n * s - i * a, t[3] = s * u - o * c, t[4] = r * c - i * u, t[5] = i * o - r * s, t[6] = o * h - a * u, t[7] = n * u - r * h, t[8] = r * a - n * o, t + } + + function mat3_determinant(t) { + var e = t[0], + r = t[1], + n = t[2], + i = t[3], + o = t[4], + a = t[5], + s = t[6], + u = t[7], + h = t[8]; + return e * (h * o - a * u) + r * (-h * i + a * s) + n * (u * i - o * s) + } + + function mat3_multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = e[8], + f = r[0], + d = r[1], + p = r[2], + m = r[3], + v = r[4], + g = r[5], + y = r[6], + _ = r[7], + b = r[8]; + return t[0] = f * n + d * a + p * h, t[1] = f * i + d * s + p * c, t[2] = f * o + d * u + p * l, t[3] = m * n + v * a + g * h, t[4] = m * i + v * s + g * c, t[5] = m * o + v * u + g * l, t[6] = y * n + _ * a + b * h, t[7] = y * i + _ * s + b * c, t[8] = y * o + _ * u + b * l, t + } + + function mat3_translate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = e[8], + f = r[0], + d = r[1]; + return t[0] = n, t[1] = i, t[2] = o, t[3] = a, t[4] = s, t[5] = u, t[6] = f * n + d * a + h, t[7] = f * i + d * s + c, t[8] = f * o + d * u + l, t + } + + function mat3_rotate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = e[8], + f = Math.sin(r), + d = Math.cos(r); + return t[0] = d * n + f * a, t[1] = d * i + f * s, t[2] = d * o + f * u, t[3] = d * a - f * n, t[4] = d * s - f * i, t[5] = d * u - f * o, t[6] = h, t[7] = c, t[8] = l, t + } + + function mat3_scale(t, e, r) { + var n = r[0], + i = r[1]; + return t[0] = n * e[0], t[1] = n * e[1], t[2] = n * e[2], t[3] = i * e[3], t[4] = i * e[4], t[5] = i * e[5], t[6] = e[6], t[7] = e[7], t[8] = e[8], t + } + + function mat3_fromTranslation(t, e) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 1, t[5] = 0, t[6] = e[0], t[7] = e[1], t[8] = 1, t + } + + function mat3_fromRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = n, t[1] = r, t[2] = 0, t[3] = -r, t[4] = n, t[5] = 0, t[6] = 0, t[7] = 0, t[8] = 1, t + } + + function mat3_fromScaling(t, e) { + return t[0] = e[0], t[1] = 0, t[2] = 0, t[3] = 0, t[4] = e[1], t[5] = 0, t[6] = 0, t[7] = 0, t[8] = 1, t + } + + function fromMat2d(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = 0, t[3] = e[2], t[4] = e[3], t[5] = 0, t[6] = e[4], t[7] = e[5], t[8] = 1, t + } + + function fromQuat(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = r + r, + s = n + n, + u = i + i, + h = r * a, + c = n * a, + l = n * s, + f = i * a, + d = i * s, + p = i * u, + m = o * a, + v = o * s, + g = o * u; + return t[0] = 1 - l - p, t[3] = c - g, t[6] = f + v, t[1] = c + g, t[4] = 1 - h - p, t[7] = d - m, t[2] = f - v, t[5] = d + m, t[8] = 1 - h - l, t + } + + function normalFromMat4(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = e[6], + h = e[7], + c = e[8], + l = e[9], + f = e[10], + d = e[11], + p = e[12], + m = e[13], + v = e[14], + g = e[15], + y = r * s - n * a, + _ = r * u - i * a, + b = r * h - o * a, + x = n * u - i * s, + w = n * h - o * s, + T = i * h - o * u, + S = c * m - l * p, + M = c * v - f * p, + E = c * g - d * p, + A = l * v - f * m, + P = l * g - d * m, + I = f * g - d * v, + O = y * I - _ * P + b * A + x * E - w * M + T * S; + return O ? (O = 1 / O, t[0] = (s * I - u * P + h * A) * O, t[1] = (u * E - a * I - h * M) * O, t[2] = (a * P - s * E + h * S) * O, t[3] = (i * P - n * I - o * A) * O, t[4] = (r * I - i * E + o * M) * O, t[5] = (n * E - r * P - o * S) * O, t[6] = (m * T - v * w + g * x) * O, t[7] = (v * b - p * T - g * _) * O, t[8] = (p * w - m * b + g * y) * O, t) : null + } + + function projection(t, e, r) { + return t[0] = 2 / e, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = -2 / r, t[5] = 0, t[6] = -1, t[7] = 1, t[8] = 1, t + } + + function mat3_str(t) { + return "mat3(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ", " + t[4] + ", " + t[5] + ", " + t[6] + ", " + t[7] + ", " + t[8] + ")" + } + + function mat3_frob(t) { + return Math.sqrt(Math.pow(t[0], 2) + Math.pow(t[1], 2) + Math.pow(t[2], 2) + Math.pow(t[3], 2) + Math.pow(t[4], 2) + Math.pow(t[5], 2) + Math.pow(t[6], 2) + Math.pow(t[7], 2) + Math.pow(t[8], 2)) + } + + function mat3_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t[4] = e[4] + r[4], t[5] = e[5] + r[5], t[6] = e[6] + r[6], t[7] = e[7] + r[7], t[8] = e[8] + r[8], t + } + + function mat3_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t[3] = e[3] - r[3], t[4] = e[4] - r[4], t[5] = e[5] - r[5], t[6] = e[6] - r[6], t[7] = e[7] - r[7], t[8] = e[8] - r[8], t + } + + function mat3_multiplyScalar(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t[4] = e[4] * r, t[5] = e[5] * r, t[6] = e[6] * r, t[7] = e[7] * r, t[8] = e[8] * r, t + } + + function mat3_multiplyScalarAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t[3] = e[3] + r[3] * n, t[4] = e[4] + r[4] * n, t[5] = e[5] + r[5] * n, t[6] = e[6] + r[6] * n, t[7] = e[7] + r[7] * n, t[8] = e[8] + r[8] * n, t + } + + function mat3_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] && t[4] === e[4] && t[5] === e[5] && t[6] === e[6] && t[7] === e[7] && t[8] === e[8] + } + + function mat3_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = t[4], + s = t[5], + u = t[6], + h = t[7], + c = t[8], + l = e[0], + f = e[1], + d = e[2], + p = e[3], + m = e[4], + v = e[5], + g = e[6], + y = e[7], + _ = e[8]; + return Math.abs(r - l) <= F * Math.max(1, Math.abs(r), Math.abs(l)) && Math.abs(n - f) <= F * Math.max(1, Math.abs(n), Math.abs(f)) && Math.abs(i - d) <= F * Math.max(1, Math.abs(i), Math.abs(d)) && Math.abs(o - p) <= F * Math.max(1, Math.abs(o), Math.abs(p)) && Math.abs(a - m) <= F * Math.max(1, Math.abs(a), Math.abs(m)) && Math.abs(s - v) <= F * Math.max(1, Math.abs(s), Math.abs(v)) && Math.abs(u - g) <= F * Math.max(1, Math.abs(u), Math.abs(g)) && Math.abs(h - y) <= F * Math.max(1, Math.abs(h), Math.abs(y)) && Math.abs(c - _) <= F * Math.max(1, Math.abs(c), Math.abs(_)) + } + var b = mat3_multiply, + x = mat3_subtract; + + function mat4_create() { + var t = new g(16); + return g != Float32Array && (t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0), t[0] = 1, t[5] = 1, t[10] = 1, t[15] = 1, t + } + + function mat4_clone(t) { + var e = new g(16); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e[4] = t[4], e[5] = t[5], e[6] = t[6], e[7] = t[7], e[8] = t[8], e[9] = t[9], e[10] = t[10], e[11] = t[11], e[12] = t[12], e[13] = t[13], e[14] = t[14], e[15] = t[15], e + } + + function mat4_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4], t[5] = e[5], t[6] = e[6], t[7] = e[7], t[8] = e[8], t[9] = e[9], t[10] = e[10], t[11] = e[11], t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15], t + } + + function mat4_fromValues(t, e, r, n, i, o, a, s, u, h, c, l, f, d, p, m) { + var v = new g(16); + return v[0] = t, v[1] = e, v[2] = r, v[3] = n, v[4] = i, v[5] = o, v[6] = a, v[7] = s, v[8] = u, v[9] = h, v[10] = c, v[11] = l, v[12] = f, v[13] = d, v[14] = p, v[15] = m, v + } + + function mat4_set(t, e, r, n, i, o, a, s, u, h, c, l, f, d, p, m, v) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t[4] = o, t[5] = a, t[6] = s, t[7] = u, t[8] = h, t[9] = c, t[10] = l, t[11] = f, t[12] = d, t[13] = p, t[14] = m, t[15] = v, t + } + + function mat4_identity(t) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = 1, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = 1, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function mat4_transpose(t, e) { + if (t === e) { + var r = e[1], + n = e[2], + i = e[3], + o = e[6], + a = e[7], + s = e[11]; + t[1] = e[4], t[2] = e[8], t[3] = e[12], t[4] = r, t[6] = e[9], t[7] = e[13], t[8] = n, t[9] = o, t[11] = e[14], t[12] = i, t[13] = a, t[14] = s + } else t[0] = e[0], t[1] = e[4], t[2] = e[8], t[3] = e[12], t[4] = e[1], t[5] = e[5], t[6] = e[9], t[7] = e[13], t[8] = e[2], t[9] = e[6], t[10] = e[10], t[11] = e[14], t[12] = e[3], t[13] = e[7], t[14] = e[11], t[15] = e[15]; + return t + } + + function mat4_invert(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = e[6], + h = e[7], + c = e[8], + l = e[9], + f = e[10], + d = e[11], + p = e[12], + m = e[13], + v = e[14], + g = e[15], + y = r * s - n * a, + _ = r * u - i * a, + b = r * h - o * a, + x = n * u - i * s, + w = n * h - o * s, + T = i * h - o * u, + S = c * m - l * p, + M = c * v - f * p, + E = c * g - d * p, + A = l * v - f * m, + P = l * g - d * m, + I = f * g - d * v, + O = y * I - _ * P + b * A + x * E - w * M + T * S; + return O ? (O = 1 / O, t[0] = (s * I - u * P + h * A) * O, t[1] = (i * P - n * I - o * A) * O, t[2] = (m * T - v * w + g * x) * O, t[3] = (f * w - l * T - d * x) * O, t[4] = (u * E - a * I - h * M) * O, t[5] = (r * I - i * E + o * M) * O, t[6] = (v * b - p * T - g * _) * O, t[7] = (c * T - f * b + d * _) * O, t[8] = (a * P - s * E + h * S) * O, t[9] = (n * E - r * P - o * S) * O, t[10] = (p * w - m * b + g * y) * O, t[11] = (l * b - c * w - d * y) * O, t[12] = (s * M - a * A - u * S) * O, t[13] = (r * A - n * M + i * S) * O, t[14] = (m * _ - p * x - v * y) * O, t[15] = (c * x - l * _ + f * y) * O, t) : null + } + + function mat4_adjoint(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = e[4], + s = e[5], + u = e[6], + h = e[7], + c = e[8], + l = e[9], + f = e[10], + d = e[11], + p = e[12], + m = e[13], + v = e[14], + g = e[15]; + return t[0] = s * (f * g - d * v) - l * (u * g - h * v) + m * (u * d - h * f), t[1] = -(n * (f * g - d * v) - l * (i * g - o * v) + m * (i * d - o * f)), t[2] = n * (u * g - h * v) - s * (i * g - o * v) + m * (i * h - o * u), t[3] = -(n * (u * d - h * f) - s * (i * d - o * f) + l * (i * h - o * u)), t[4] = -(a * (f * g - d * v) - c * (u * g - h * v) + p * (u * d - h * f)), t[5] = r * (f * g - d * v) - c * (i * g - o * v) + p * (i * d - o * f), t[6] = -(r * (u * g - h * v) - a * (i * g - o * v) + p * (i * h - o * u)), t[7] = r * (u * d - h * f) - a * (i * d - o * f) + c * (i * h - o * u), t[8] = a * (l * g - d * m) - c * (s * g - h * m) + p * (s * d - h * l), t[9] = -(r * (l * g - d * m) - c * (n * g - o * m) + p * (n * d - o * l)), t[10] = r * (s * g - h * m) - a * (n * g - o * m) + p * (n * h - o * s), t[11] = -(r * (s * d - h * l) - a * (n * d - o * l) + c * (n * h - o * s)), t[12] = -(a * (l * v - f * m) - c * (s * v - u * m) + p * (s * f - u * l)), t[13] = r * (l * v - f * m) - c * (n * v - i * m) + p * (n * f - i * l), t[14] = -(r * (s * v - u * m) - a * (n * v - i * m) + p * (n * u - i * s)), t[15] = r * (s * f - u * l) - a * (n * f - i * l) + c * (n * u - i * s), t + } + + function mat4_determinant(t) { + var e = t[0], + r = t[1], + n = t[2], + i = t[3], + o = t[4], + a = t[5], + s = t[6], + u = t[7], + h = t[8], + c = t[9], + l = t[10], + f = t[11], + d = t[12], + p = t[13], + m = t[14], + v = t[15]; + return (e * a - r * o) * (l * v - f * m) - (e * s - n * o) * (c * v - f * p) + (e * u - i * o) * (c * m - l * p) + (r * s - n * a) * (h * v - f * d) - (r * u - i * a) * (h * m - l * d) + (n * u - i * s) * (h * p - c * d) + } + + function mat4_multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = e[8], + f = e[9], + d = e[10], + p = e[11], + m = e[12], + v = e[13], + g = e[14], + y = e[15], + _ = r[0], + b = r[1], + x = r[2], + w = r[3]; + return t[0] = _ * n + b * s + x * l + w * m, t[1] = _ * i + b * u + x * f + w * v, t[2] = _ * o + b * h + x * d + w * g, t[3] = _ * a + b * c + x * p + w * y, _ = r[4], b = r[5], x = r[6], w = r[7], t[4] = _ * n + b * s + x * l + w * m, t[5] = _ * i + b * u + x * f + w * v, t[6] = _ * o + b * h + x * d + w * g, t[7] = _ * a + b * c + x * p + w * y, _ = r[8], b = r[9], x = r[10], w = r[11], t[8] = _ * n + b * s + x * l + w * m, t[9] = _ * i + b * u + x * f + w * v, t[10] = _ * o + b * h + x * d + w * g, t[11] = _ * a + b * c + x * p + w * y, _ = r[12], b = r[13], x = r[14], w = r[15], t[12] = _ * n + b * s + x * l + w * m, t[13] = _ * i + b * u + x * f + w * v, t[14] = _ * o + b * h + x * d + w * g, t[15] = _ * a + b * c + x * p + w * y, t + } + + function mat4_translate(t, e, r) { + var n = r[0], + i = r[1], + o = r[2], + a = void 0, + s = void 0, + u = void 0, + h = void 0, + c = void 0, + l = void 0, + f = void 0, + d = void 0, + p = void 0, + m = void 0, + v = void 0, + g = void 0; + return t[15] = e === t ? (t[12] = e[0] * n + e[4] * i + e[8] * o + e[12], t[13] = e[1] * n + e[5] * i + e[9] * o + e[13], t[14] = e[2] * n + e[6] * i + e[10] * o + e[14], e[3] * n + e[7] * i + e[11] * o + e[15]) : (a = e[0], s = e[1], u = e[2], h = e[3], c = e[4], l = e[5], f = e[6], d = e[7], p = e[8], m = e[9], v = e[10], g = e[11], t[0] = a, t[1] = s, t[2] = u, t[3] = h, t[4] = c, t[5] = l, t[6] = f, t[7] = d, t[8] = p, t[9] = m, t[10] = v, t[11] = g, t[12] = a * n + c * i + p * o + e[12], t[13] = s * n + l * i + m * o + e[13], t[14] = u * n + f * i + v * o + e[14], h * n + d * i + g * o + e[15]), t + } + + function mat4_scale(t, e, r) { + var n = r[0], + i = r[1], + o = r[2]; + return t[0] = e[0] * n, t[1] = e[1] * n, t[2] = e[2] * n, t[3] = e[3] * n, t[4] = e[4] * i, t[5] = e[5] * i, t[6] = e[6] * i, t[7] = e[7] * i, t[8] = e[8] * o, t[9] = e[9] * o, t[10] = e[10] * o, t[11] = e[11] * o, t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15], t + } + + function mat4_rotate(t, e, r, n) { + var i, o, a, s, u, h, c, l, f, d, p, m, v, g, y, _, b, x, w, T, S, M, E, A, P = n[0], + I = n[1], + O = n[2], + C = Math.sqrt(P * P + I * I + O * O); + return C < F ? null : (P *= C = 1 / C, I *= C, O *= C, i = Math.sin(r), a = 1 - (o = Math.cos(r)), s = e[0], u = e[1], h = e[2], c = e[3], l = e[4], f = e[5], d = e[6], p = e[7], m = e[8], v = e[9], g = e[10], y = e[11], _ = P * P * a + o, b = I * P * a + O * i, x = O * P * a - I * i, w = P * I * a - O * i, T = I * I * a + o, S = O * I * a + P * i, M = P * O * a + I * i, E = I * O * a - P * i, A = O * O * a + o, t[0] = s * _ + l * b + m * x, t[1] = u * _ + f * b + v * x, t[2] = h * _ + d * b + g * x, t[3] = c * _ + p * b + y * x, t[4] = s * w + l * T + m * S, t[5] = u * w + f * T + v * S, t[6] = h * w + d * T + g * S, t[7] = c * w + p * T + y * S, t[8] = s * M + l * E + m * A, t[9] = u * M + f * E + v * A, t[10] = h * M + d * E + g * A, t[11] = c * M + p * E + y * A, e !== t && (t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15]), t) + } + + function rotateX(t, e, r) { + var n = Math.sin(r), + i = Math.cos(r), + o = e[4], + a = e[5], + s = e[6], + u = e[7], + h = e[8], + c = e[9], + l = e[10], + f = e[11]; + return e !== t && (t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15]), t[4] = o * i + h * n, t[5] = a * i + c * n, t[6] = s * i + l * n, t[7] = u * i + f * n, t[8] = h * i - o * n, t[9] = c * i - a * n, t[10] = l * i - s * n, t[11] = f * i - u * n, t + } + + function rotateY(t, e, r) { + var n = Math.sin(r), + i = Math.cos(r), + o = e[0], + a = e[1], + s = e[2], + u = e[3], + h = e[8], + c = e[9], + l = e[10], + f = e[11]; + return e !== t && (t[4] = e[4], t[5] = e[5], t[6] = e[6], t[7] = e[7], t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15]), t[0] = o * i - h * n, t[1] = a * i - c * n, t[2] = s * i - l * n, t[3] = u * i - f * n, t[8] = o * n + h * i, t[9] = a * n + c * i, t[10] = s * n + l * i, t[11] = u * n + f * i, t + } + + function rotateZ(t, e, r) { + var n = Math.sin(r), + i = Math.cos(r), + o = e[0], + a = e[1], + s = e[2], + u = e[3], + h = e[4], + c = e[5], + l = e[6], + f = e[7]; + return e !== t && (t[8] = e[8], t[9] = e[9], t[10] = e[10], t[11] = e[11], t[12] = e[12], t[13] = e[13], t[14] = e[14], t[15] = e[15]), t[0] = o * i + h * n, t[1] = a * i + c * n, t[2] = s * i + l * n, t[3] = u * i + f * n, t[4] = h * i - o * n, t[5] = c * i - a * n, t[6] = l * i - s * n, t[7] = f * i - u * n, t + } + + function mat4_fromTranslation(t, e) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = 1, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = 1, t[11] = 0, t[12] = e[0], t[13] = e[1], t[14] = e[2], t[15] = 1, t + } + + function mat4_fromScaling(t, e) { + return t[0] = e[0], t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = e[1], t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = e[2], t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function mat4_fromRotation(t, e, r) { + var n, i, o, a = r[0], + s = r[1], + u = r[2], + h = Math.sqrt(a * a + s * s + u * u); + return h < F ? null : (a *= h = 1 / h, s *= h, u *= h, n = Math.sin(e), o = 1 - (i = Math.cos(e)), t[0] = a * a * o + i, t[1] = s * a * o + u * n, t[2] = u * a * o - s * n, t[3] = 0, t[4] = a * s * o - u * n, t[5] = s * s * o + i, t[6] = u * s * o + a * n, t[7] = 0, t[8] = a * u * o + s * n, t[9] = s * u * o - a * n, t[10] = u * u * o + i, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t) + } + + function fromXRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = n, t[6] = r, t[7] = 0, t[8] = 0, t[9] = -r, t[10] = n, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function fromYRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = n, t[1] = 0, t[2] = -r, t[3] = 0, t[4] = 0, t[5] = 1, t[6] = 0, t[7] = 0, t[8] = r, t[9] = 0, t[10] = n, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function fromZRotation(t, e) { + var r = Math.sin(e), + n = Math.cos(e); + return t[0] = n, t[1] = r, t[2] = 0, t[3] = 0, t[4] = -r, t[5] = n, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = 1, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function fromRotationTranslation(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = n + n, + u = i + i, + h = o + o, + c = n * s, + l = n * u, + f = n * h, + d = i * u, + p = i * h, + m = o * h, + v = a * s, + g = a * u, + y = a * h; + return t[0] = 1 - (d + m), t[1] = l + y, t[2] = f - g, t[3] = 0, t[4] = l - y, t[5] = 1 - (c + m), t[6] = p + v, t[7] = 0, t[8] = f + g, t[9] = p - v, t[10] = 1 - (c + d), t[11] = 0, t[12] = r[0], t[13] = r[1], t[14] = r[2], t[15] = 1, t + } + + function fromQuat2(t, e) { + var r = new g(3), + n = -e[0], + i = -e[1], + o = -e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = n * n + i * i + o * o + a * a; + return r[2] = 0 < l ? (r[0] = 2 * (s * a + c * n + u * o - h * i) / l, r[1] = 2 * (u * a + c * i + h * n - s * o) / l, 2 * (h * a + c * o + s * i - u * n) / l) : (r[0] = 2 * (s * a + c * n + u * o - h * i), r[1] = 2 * (u * a + c * i + h * n - s * o), 2 * (h * a + c * o + s * i - u * n)), fromRotationTranslation(t, e, r), t + } + + function getTranslation(t, e) { + return t[0] = e[12], t[1] = e[13], t[2] = e[14], t + } + + function getScaling(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[4], + a = e[5], + s = e[6], + u = e[8], + h = e[9], + c = e[10]; + return t[0] = Math.sqrt(r * r + n * n + i * i), t[1] = Math.sqrt(o * o + a * a + s * s), t[2] = Math.sqrt(u * u + h * h + c * c), t + } + + function getRotation(t, e) { + var r = e[0] + e[5] + e[10], + n = 0; + return 0 < r ? (n = 2 * Math.sqrt(r + 1), t[3] = .25 * n, t[0] = (e[6] - e[9]) / n, t[1] = (e[8] - e[2]) / n, t[2] = (e[1] - e[4]) / n) : e[0] > e[5] && e[0] > e[10] ? (n = 2 * Math.sqrt(1 + e[0] - e[5] - e[10]), t[3] = (e[6] - e[9]) / n, t[0] = .25 * n, t[1] = (e[1] + e[4]) / n, t[2] = (e[8] + e[2]) / n) : e[5] > e[10] ? (n = 2 * Math.sqrt(1 + e[5] - e[0] - e[10]), t[3] = (e[8] - e[2]) / n, t[0] = (e[1] + e[4]) / n, t[1] = .25 * n, t[2] = (e[6] + e[9]) / n) : (n = 2 * Math.sqrt(1 + e[10] - e[0] - e[5]), t[3] = (e[1] - e[4]) / n, t[0] = (e[8] + e[2]) / n, t[1] = (e[6] + e[9]) / n, t[2] = .25 * n), t + } + + function fromRotationTranslationScale(t, e, r, n) { + var i = e[0], + o = e[1], + a = e[2], + s = e[3], + u = i + i, + h = o + o, + c = a + a, + l = i * u, + f = i * h, + d = i * c, + p = o * h, + m = o * c, + v = a * c, + g = s * u, + y = s * h, + _ = s * c, + b = n[0], + x = n[1], + w = n[2]; + return t[0] = (1 - (p + v)) * b, t[1] = (f + _) * b, t[2] = (d - y) * b, t[3] = 0, t[4] = (f - _) * x, t[5] = (1 - (l + v)) * x, t[6] = (m + g) * x, t[7] = 0, t[8] = (d + y) * w, t[9] = (m - g) * w, t[10] = (1 - (l + p)) * w, t[11] = 0, t[12] = r[0], t[13] = r[1], t[14] = r[2], t[15] = 1, t + } + + function fromRotationTranslationScaleOrigin(t, e, r, n, i) { + var o = e[0], + a = e[1], + s = e[2], + u = e[3], + h = o + o, + c = a + a, + l = s + s, + f = o * h, + d = o * c, + p = o * l, + m = a * c, + v = a * l, + g = s * l, + y = u * h, + _ = u * c, + b = u * l, + x = n[0], + w = n[1], + T = n[2], + S = i[0], + M = i[1], + E = i[2], + A = (1 - (m + g)) * x, + P = (d + b) * x, + I = (p - _) * x, + O = (d - b) * w, + C = (1 - (f + g)) * w, + R = (v + y) * w, + D = (p + _) * T, + F = (v - y) * T, + k = (1 - (f + m)) * T; + return t[0] = A, t[1] = P, t[2] = I, t[3] = 0, t[4] = O, t[5] = C, t[6] = R, t[7] = 0, t[8] = D, t[9] = F, t[10] = k, t[11] = 0, t[12] = r[0] + S - (A * S + O * M + D * E), t[13] = r[1] + M - (P * S + C * M + F * E), t[14] = r[2] + E - (I * S + R * M + k * E), t[15] = 1, t + } + + function mat4_fromQuat(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = r + r, + s = n + n, + u = i + i, + h = r * a, + c = n * a, + l = n * s, + f = i * a, + d = i * s, + p = i * u, + m = o * a, + v = o * s, + g = o * u; + return t[0] = 1 - l - p, t[1] = c + g, t[2] = f - v, t[3] = 0, t[4] = c - g, t[5] = 1 - h - p, t[6] = d + m, t[7] = 0, t[8] = f + v, t[9] = d - m, t[10] = 1 - h - l, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, t + } + + function frustum(t, e, r, n, i, o, a) { + var s = 1 / (r - e), + u = 1 / (i - n), + h = 1 / (o - a); + return t[0] = 2 * o * s, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = 2 * o * u, t[6] = 0, t[7] = 0, t[8] = (r + e) * s, t[9] = (i + n) * u, t[10] = (a + o) * h, t[11] = -1, t[12] = 0, t[13] = 0, t[14] = a * o * 2 * h, t[15] = 0, t + } + + function perspective(t, e, r, n, i) { + var o = 1 / Math.tan(e / 2), + a = void 0; + return t[0] = o / r, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = o, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[11] = -1, t[12] = 0, t[13] = 0, t[15] = 0, t[14] = null != i && i !== 1 / 0 ? (a = 1 / (n - i), t[10] = (i + n) * a, 2 * i * n * a) : (t[10] = -1, -2 * n), t + } + + function perspectiveFromFieldOfView(t, e, r, n) { + var i = Math.tan(e.upDegrees * Math.PI / 180), + o = Math.tan(e.downDegrees * Math.PI / 180), + a = Math.tan(e.leftDegrees * Math.PI / 180), + s = Math.tan(e.rightDegrees * Math.PI / 180), + u = 2 / (a + s), + h = 2 / (i + o); + return t[0] = u, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = h, t[6] = 0, t[7] = 0, t[8] = -(a - s) * u * .5, t[9] = (i - o) * h * .5, t[10] = n / (r - n), t[11] = -1, t[12] = 0, t[13] = 0, t[14] = n * r / (r - n), t[15] = 0, t + } + + function ortho(t, e, r, n, i, o, a) { + var s = 1 / (e - r), + u = 1 / (n - i), + h = 1 / (o - a); + return t[0] = -2 * s, t[1] = 0, t[2] = 0, t[3] = 0, t[4] = 0, t[5] = -2 * u, t[6] = 0, t[7] = 0, t[8] = 0, t[9] = 0, t[10] = 2 * h, t[11] = 0, t[12] = (e + r) * s, t[13] = (i + n) * u, t[14] = (a + o) * h, t[15] = 1, t + } + + function lookAt(t, e, r, n) { + var i = void 0, + o = void 0, + a = void 0, + s = void 0, + u = void 0, + h = void 0, + c = void 0, + l = void 0, + f = void 0, + d = void 0, + p = e[0], + m = e[1], + v = e[2], + g = n[0], + y = n[1], + _ = n[2], + b = r[0], + x = r[1], + w = r[2]; + return Math.abs(p - b) < F && Math.abs(m - x) < F && Math.abs(v - w) < F ? mat4_identity(t) : (c = p - b, l = m - x, f = v - w, i = y * (f *= d = 1 / Math.sqrt(c * c + l * l + f * f)) - _ * (l *= d), o = _ * (c *= d) - g * f, a = g * l - y * c, (d = Math.sqrt(i * i + o * o + a * a)) ? (i *= d = 1 / d, o *= d, a *= d) : a = o = i = 0, s = l * a - f * o, u = f * i - c * a, h = c * o - l * i, (d = Math.sqrt(s * s + u * u + h * h)) ? (s *= d = 1 / d, u *= d, h *= d) : h = u = s = 0, t[0] = i, t[1] = s, t[2] = c, t[3] = 0, t[4] = o, t[5] = u, t[6] = l, t[7] = 0, t[8] = a, t[9] = h, t[10] = f, t[11] = 0, t[12] = -(i * p + o * m + a * v), t[13] = -(s * p + u * m + h * v), t[14] = -(c * p + l * m + f * v), t[15] = 1, t) + } + + function targetTo(t, e, r, n) { + var i = e[0], + o = e[1], + a = e[2], + s = n[0], + u = n[1], + h = n[2], + c = i - r[0], + l = o - r[1], + f = a - r[2], + d = c * c + l * l + f * f; + 0 < d && (c *= d = 1 / Math.sqrt(d), l *= d, f *= d); + var p = u * f - h * l, + m = h * c - s * f, + v = s * l - u * c; + return 0 < (d = p * p + m * m + v * v) && (p *= d = 1 / Math.sqrt(d), m *= d, v *= d), t[0] = p, t[1] = m, t[2] = v, t[3] = 0, t[4] = l * v - f * m, t[5] = f * p - c * v, t[6] = c * m - l * p, t[7] = 0, t[8] = c, t[9] = l, t[10] = f, t[11] = 0, t[12] = i, t[13] = o, t[14] = a, t[15] = 1, t + } + + function mat4_str(t) { + return "mat4(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ", " + t[4] + ", " + t[5] + ", " + t[6] + ", " + t[7] + ", " + t[8] + ", " + t[9] + ", " + t[10] + ", " + t[11] + ", " + t[12] + ", " + t[13] + ", " + t[14] + ", " + t[15] + ")" + } + + function mat4_frob(t) { + return Math.sqrt(Math.pow(t[0], 2) + Math.pow(t[1], 2) + Math.pow(t[2], 2) + Math.pow(t[3], 2) + Math.pow(t[4], 2) + Math.pow(t[5], 2) + Math.pow(t[6], 2) + Math.pow(t[7], 2) + Math.pow(t[8], 2) + Math.pow(t[9], 2) + Math.pow(t[10], 2) + Math.pow(t[11], 2) + Math.pow(t[12], 2) + Math.pow(t[13], 2) + Math.pow(t[14], 2) + Math.pow(t[15], 2)) + } + + function mat4_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t[4] = e[4] + r[4], t[5] = e[5] + r[5], t[6] = e[6] + r[6], t[7] = e[7] + r[7], t[8] = e[8] + r[8], t[9] = e[9] + r[9], t[10] = e[10] + r[10], t[11] = e[11] + r[11], t[12] = e[12] + r[12], t[13] = e[13] + r[13], t[14] = e[14] + r[14], t[15] = e[15] + r[15], t + } + + function mat4_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t[3] = e[3] - r[3], t[4] = e[4] - r[4], t[5] = e[5] - r[5], t[6] = e[6] - r[6], t[7] = e[7] - r[7], t[8] = e[8] - r[8], t[9] = e[9] - r[9], t[10] = e[10] - r[10], t[11] = e[11] - r[11], t[12] = e[12] - r[12], t[13] = e[13] - r[13], t[14] = e[14] - r[14], t[15] = e[15] - r[15], t + } + + function mat4_multiplyScalar(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t[4] = e[4] * r, t[5] = e[5] * r, t[6] = e[6] * r, t[7] = e[7] * r, t[8] = e[8] * r, t[9] = e[9] * r, t[10] = e[10] * r, t[11] = e[11] * r, t[12] = e[12] * r, t[13] = e[13] * r, t[14] = e[14] * r, t[15] = e[15] * r, t + } + + function mat4_multiplyScalarAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t[3] = e[3] + r[3] * n, t[4] = e[4] + r[4] * n, t[5] = e[5] + r[5] * n, t[6] = e[6] + r[6] * n, t[7] = e[7] + r[7] * n, t[8] = e[8] + r[8] * n, t[9] = e[9] + r[9] * n, t[10] = e[10] + r[10] * n, t[11] = e[11] + r[11] * n, t[12] = e[12] + r[12] * n, t[13] = e[13] + r[13] * n, t[14] = e[14] + r[14] * n, t[15] = e[15] + r[15] * n, t + } + + function mat4_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] && t[4] === e[4] && t[5] === e[5] && t[6] === e[6] && t[7] === e[7] && t[8] === e[8] && t[9] === e[9] && t[10] === e[10] && t[11] === e[11] && t[12] === e[12] && t[13] === e[13] && t[14] === e[14] && t[15] === e[15] + } + + function mat4_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = t[4], + s = t[5], + u = t[6], + h = t[7], + c = t[8], + l = t[9], + f = t[10], + d = t[11], + p = t[12], + m = t[13], + v = t[14], + g = t[15], + y = e[0], + _ = e[1], + b = e[2], + x = e[3], + w = e[4], + T = e[5], + S = e[6], + M = e[7], + E = e[8], + A = e[9], + P = e[10], + I = e[11], + O = e[12], + C = e[13], + R = e[14], + D = e[15]; + return Math.abs(r - y) <= F * Math.max(1, Math.abs(r), Math.abs(y)) && Math.abs(n - _) <= F * Math.max(1, Math.abs(n), Math.abs(_)) && Math.abs(i - b) <= F * Math.max(1, Math.abs(i), Math.abs(b)) && Math.abs(o - x) <= F * Math.max(1, Math.abs(o), Math.abs(x)) && Math.abs(a - w) <= F * Math.max(1, Math.abs(a), Math.abs(w)) && Math.abs(s - T) <= F * Math.max(1, Math.abs(s), Math.abs(T)) && Math.abs(u - S) <= F * Math.max(1, Math.abs(u), Math.abs(S)) && Math.abs(h - M) <= F * Math.max(1, Math.abs(h), Math.abs(M)) && Math.abs(c - E) <= F * Math.max(1, Math.abs(c), Math.abs(E)) && Math.abs(l - A) <= F * Math.max(1, Math.abs(l), Math.abs(A)) && Math.abs(f - P) <= F * Math.max(1, Math.abs(f), Math.abs(P)) && Math.abs(d - I) <= F * Math.max(1, Math.abs(d), Math.abs(I)) && Math.abs(p - O) <= F * Math.max(1, Math.abs(p), Math.abs(O)) && Math.abs(m - C) <= F * Math.max(1, Math.abs(m), Math.abs(C)) && Math.abs(v - R) <= F * Math.max(1, Math.abs(v), Math.abs(R)) && Math.abs(g - D) <= F * Math.max(1, Math.abs(g), Math.abs(D)) + } + var w = mat4_multiply, + T = mat4_subtract; + + function vec3_create() { + var t = new g(3); + return g != Float32Array && (t[0] = 0, t[1] = 0, t[2] = 0), t + } + + function vec3_clone(t) { + var e = new g(3); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e + } + + function vec3_length(t) { + var e = t[0], + r = t[1], + n = t[2]; + return Math.sqrt(e * e + r * r + n * n) + } + + function vec3_fromValues(t, e, r) { + var n = new g(3); + return n[0] = t, n[1] = e, n[2] = r, n + } + + function vec3_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t + } + + function vec3_set(t, e, r, n) { + return t[0] = e, t[1] = r, t[2] = n, t + } + + function vec3_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t + } + + function vec3_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t + } + + function vec3_multiply(t, e, r) { + return t[0] = e[0] * r[0], t[1] = e[1] * r[1], t[2] = e[2] * r[2], t + } + + function divide(t, e, r) { + return t[0] = e[0] / r[0], t[1] = e[1] / r[1], t[2] = e[2] / r[2], t + } + + function ceil(t, e) { + return t[0] = Math.ceil(e[0]), t[1] = Math.ceil(e[1]), t[2] = Math.ceil(e[2]), t + } + + function floor(t, e) { + return t[0] = Math.floor(e[0]), t[1] = Math.floor(e[1]), t[2] = Math.floor(e[2]), t + } + + function min(t, e, r) { + return t[0] = Math.min(e[0], r[0]), t[1] = Math.min(e[1], r[1]), t[2] = Math.min(e[2], r[2]), t + } + + function max(t, e, r) { + return t[0] = Math.max(e[0], r[0]), t[1] = Math.max(e[1], r[1]), t[2] = Math.max(e[2], r[2]), t + } + + function round(t, e) { + return t[0] = Math.round(e[0]), t[1] = Math.round(e[1]), t[2] = Math.round(e[2]), t + } + + function vec3_scale(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t + } + + function scaleAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t + } + + function distance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1], + i = e[2] - t[2]; + return Math.sqrt(r * r + n * n + i * i) + } + + function squaredDistance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1], + i = e[2] - t[2]; + return r * r + n * n + i * i + } + + function squaredLength(t) { + var e = t[0], + r = t[1], + n = t[2]; + return e * e + r * r + n * n + } + + function negate(t, e) { + return t[0] = -e[0], t[1] = -e[1], t[2] = -e[2], t + } + + function inverse(t, e) { + return t[0] = 1 / e[0], t[1] = 1 / e[1], t[2] = 1 / e[2], t + } + + function normalize(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = r * r + n * n + i * i; + return 0 < o && (o = 1 / Math.sqrt(o), t[0] = e[0] * o, t[1] = e[1] * o, t[2] = e[2] * o), t + } + + function vec3_dot(t, e) { + return t[0] * e[0] + t[1] * e[1] + t[2] * e[2] + } + + function cross(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = r[0], + s = r[1], + u = r[2]; + return t[0] = i * u - o * s, t[1] = o * a - n * u, t[2] = n * s - i * a, t + } + + function lerp(t, e, r, n) { + var i = e[0], + o = e[1], + a = e[2]; + return t[0] = i + n * (r[0] - i), t[1] = o + n * (r[1] - o), t[2] = a + n * (r[2] - a), t + } + + function hermite(t, e, r, n, i, o) { + var a = o * o, + s = a * (2 * o - 3) + 1, + u = a * (o - 2) + o, + h = a * (o - 1), + c = a * (3 - 2 * o); + return t[0] = e[0] * s + r[0] * u + n[0] * h + i[0] * c, t[1] = e[1] * s + r[1] * u + n[1] * h + i[1] * c, t[2] = e[2] * s + r[2] * u + n[2] * h + i[2] * c, t + } + + function bezier(t, e, r, n, i, o) { + var a = 1 - o, + s = a * a, + u = o * o, + h = s * a, + c = 3 * o * s, + l = 3 * u * a, + f = u * o; + return t[0] = e[0] * h + r[0] * c + n[0] * l + i[0] * f, t[1] = e[1] * h + r[1] * c + n[1] * l + i[1] * f, t[2] = e[2] * h + r[2] * c + n[2] * l + i[2] * f, t + } + + function random(t, e) { + e = e || 1; + var r = 2 * d() * Math.PI, + n = 2 * d() - 1, + i = Math.sqrt(1 - n * n) * e; + return t[0] = Math.cos(r) * i, t[1] = Math.sin(r) * i, t[2] = n * e, t + } + + function transformMat4(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = r[3] * n + r[7] * i + r[11] * o + r[15]; + return a = a || 1, t[0] = (r[0] * n + r[4] * i + r[8] * o + r[12]) / a, t[1] = (r[1] * n + r[5] * i + r[9] * o + r[13]) / a, t[2] = (r[2] * n + r[6] * i + r[10] * o + r[14]) / a, t + } + + function transformMat3(t, e, r) { + var n = e[0], + i = e[1], + o = e[2]; + return t[0] = n * r[0] + i * r[3] + o * r[6], t[1] = n * r[1] + i * r[4] + o * r[7], t[2] = n * r[2] + i * r[5] + o * r[8], t + } + + function transformQuat(t, e, r) { + var n = r[0], + i = r[1], + o = r[2], + a = r[3], + s = e[0], + u = e[1], + h = e[2], + c = i * h - o * u, + l = o * s - n * h, + f = n * u - i * s, + d = i * f - o * l, + p = o * c - n * f, + m = n * l - i * c, + v = 2 * a; + return c *= v, l *= v, f *= v, d *= 2, p *= 2, m *= 2, t[0] = s + c + d, t[1] = u + l + p, t[2] = h + f + m, t + } + + function vec3_rotateX(t, e, r, n) { + var i = [], + o = []; + return i[0] = e[0] - r[0], i[1] = e[1] - r[1], i[2] = e[2] - r[2], o[0] = i[0], o[1] = i[1] * Math.cos(n) - i[2] * Math.sin(n), o[2] = i[1] * Math.sin(n) + i[2] * Math.cos(n), t[0] = o[0] + r[0], t[1] = o[1] + r[1], t[2] = o[2] + r[2], t + } + + function vec3_rotateY(t, e, r, n) { + var i = [], + o = []; + return i[0] = e[0] - r[0], i[1] = e[1] - r[1], i[2] = e[2] - r[2], o[0] = i[2] * Math.sin(n) + i[0] * Math.cos(n), o[1] = i[1], o[2] = i[2] * Math.cos(n) - i[0] * Math.sin(n), t[0] = o[0] + r[0], t[1] = o[1] + r[1], t[2] = o[2] + r[2], t + } + + function vec3_rotateZ(t, e, r, n) { + var i = [], + o = []; + return i[0] = e[0] - r[0], i[1] = e[1] - r[1], i[2] = e[2] - r[2], o[0] = i[0] * Math.cos(n) - i[1] * Math.sin(n), o[1] = i[0] * Math.sin(n) + i[1] * Math.cos(n), o[2] = i[2], t[0] = o[0] + r[0], t[1] = o[1] + r[1], t[2] = o[2] + r[2], t + } + + function angle(t, e) { + var r = vec3_fromValues(t[0], t[1], t[2]), + n = vec3_fromValues(e[0], e[1], e[2]); + normalize(r, r), normalize(n, n); + var i = vec3_dot(r, n); + return 1 < i ? 0 : i < -1 ? Math.PI : Math.acos(i) + } + + function vec3_str(t) { + return "vec3(" + t[0] + ", " + t[1] + ", " + t[2] + ")" + } + + function vec3_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] + } + + function vec3_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = e[0], + a = e[1], + s = e[2]; + return Math.abs(r - o) <= F * Math.max(1, Math.abs(r), Math.abs(o)) && Math.abs(n - a) <= F * Math.max(1, Math.abs(n), Math.abs(a)) && Math.abs(i - s) <= F * Math.max(1, Math.abs(i), Math.abs(s)) + } + var S, M = vec3_subtract, + E = vec3_multiply, + A = divide, + P = distance, + I = squaredDistance, + O = vec3_length, + C = squaredLength, + R = (S = vec3_create(), function(t, e, r, n, i, o) { + var a = void 0, + s = void 0; + for (e || (e = 3), r || (r = 0), s = n ? Math.min(n * e + r, t.length) : t.length, a = r; a < s; a += e) S[0] = t[a], S[1] = t[a + 1], S[2] = t[a + 2], i(S, S, o), t[a] = S[0], t[a + 1] = S[1], t[a + 2] = S[2]; + return t + }); + + function vec4_create() { + var t = new g(4); + return g != Float32Array && (t[0] = 0, t[1] = 0, t[2] = 0, t[3] = 0), t + } + + function vec4_clone(t) { + var e = new g(4); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e + } + + function vec4_fromValues(t, e, r, n) { + var i = new g(4); + return i[0] = t, i[1] = e, i[2] = r, i[3] = n, i + } + + function vec4_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t + } + + function vec4_set(t, e, r, n, i) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t + } + + function vec4_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t + } + + function vec4_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t[2] = e[2] - r[2], t[3] = e[3] - r[3], t + } + + function vec4_multiply(t, e, r) { + return t[0] = e[0] * r[0], t[1] = e[1] * r[1], t[2] = e[2] * r[2], t[3] = e[3] * r[3], t + } + + function vec4_divide(t, e, r) { + return t[0] = e[0] / r[0], t[1] = e[1] / r[1], t[2] = e[2] / r[2], t[3] = e[3] / r[3], t + } + + function vec4_ceil(t, e) { + return t[0] = Math.ceil(e[0]), t[1] = Math.ceil(e[1]), t[2] = Math.ceil(e[2]), t[3] = Math.ceil(e[3]), t + } + + function vec4_floor(t, e) { + return t[0] = Math.floor(e[0]), t[1] = Math.floor(e[1]), t[2] = Math.floor(e[2]), t[3] = Math.floor(e[3]), t + } + + function vec4_min(t, e, r) { + return t[0] = Math.min(e[0], r[0]), t[1] = Math.min(e[1], r[1]), t[2] = Math.min(e[2], r[2]), t[3] = Math.min(e[3], r[3]), t + } + + function vec4_max(t, e, r) { + return t[0] = Math.max(e[0], r[0]), t[1] = Math.max(e[1], r[1]), t[2] = Math.max(e[2], r[2]), t[3] = Math.max(e[3], r[3]), t + } + + function vec4_round(t, e) { + return t[0] = Math.round(e[0]), t[1] = Math.round(e[1]), t[2] = Math.round(e[2]), t[3] = Math.round(e[3]), t + } + + function vec4_scale(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t + } + + function vec4_scaleAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t[2] = e[2] + r[2] * n, t[3] = e[3] + r[3] * n, t + } + + function vec4_distance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1], + i = e[2] - t[2], + o = e[3] - t[3]; + return Math.sqrt(r * r + n * n + i * i + o * o) + } + + function vec4_squaredDistance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1], + i = e[2] - t[2], + o = e[3] - t[3]; + return r * r + n * n + i * i + o * o + } + + function vec4_length(t) { + var e = t[0], + r = t[1], + n = t[2], + i = t[3]; + return Math.sqrt(e * e + r * r + n * n + i * i) + } + + function vec4_squaredLength(t) { + var e = t[0], + r = t[1], + n = t[2], + i = t[3]; + return e * e + r * r + n * n + i * i + } + + function vec4_negate(t, e) { + return t[0] = -e[0], t[1] = -e[1], t[2] = -e[2], t[3] = -e[3], t + } + + function vec4_inverse(t, e) { + return t[0] = 1 / e[0], t[1] = 1 / e[1], t[2] = 1 / e[2], t[3] = 1 / e[3], t + } + + function vec4_normalize(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = r * r + n * n + i * i + o * o; + return 0 < a && (a = 1 / Math.sqrt(a), t[0] = r * a, t[1] = n * a, t[2] = i * a, t[3] = o * a), t + } + + function vec4_dot(t, e) { + return t[0] * e[0] + t[1] * e[1] + t[2] * e[2] + t[3] * e[3] + } + + function vec4_lerp(t, e, r, n) { + var i = e[0], + o = e[1], + a = e[2], + s = e[3]; + return t[0] = i + n * (r[0] - i), t[1] = o + n * (r[1] - o), t[2] = a + n * (r[2] - a), t[3] = s + n * (r[3] - s), t + } + + function vec4_random(t, e) { + var r, n, i, o, a, s; + for (e = e || 1; 1 <= (a = (r = 2 * d() - 1) * r + (n = 2 * d() - 1) * n);); + for (; 1 <= (s = (i = 2 * d() - 1) * i + (o = 2 * d() - 1) * o);); + var u = Math.sqrt((1 - a) / s); + return t[0] = e * r, t[1] = e * n, t[2] = e * i * u, t[3] = e * o * u, t + } + + function vec4_transformMat4(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3]; + return t[0] = r[0] * n + r[4] * i + r[8] * o + r[12] * a, t[1] = r[1] * n + r[5] * i + r[9] * o + r[13] * a, t[2] = r[2] * n + r[6] * i + r[10] * o + r[14] * a, t[3] = r[3] * n + r[7] * i + r[11] * o + r[15] * a, t + } + + function vec4_transformQuat(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = r[0], + s = r[1], + u = r[2], + h = r[3], + c = h * n + s * o - u * i, + l = h * i + u * n - a * o, + f = h * o + a * i - s * n, + d = -a * n - s * i - u * o; + return t[0] = c * h + d * -a + l * -u - f * -s, t[1] = l * h + d * -s + f * -a - c * -u, t[2] = f * h + d * -u + c * -s - l * -a, t[3] = e[3], t + } + + function vec4_str(t) { + return "vec4(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ")" + } + + function vec4_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] + } + + function vec4_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = e[0], + s = e[1], + u = e[2], + h = e[3]; + return Math.abs(r - a) <= F * Math.max(1, Math.abs(r), Math.abs(a)) && Math.abs(n - s) <= F * Math.max(1, Math.abs(n), Math.abs(s)) && Math.abs(i - u) <= F * Math.max(1, Math.abs(i), Math.abs(u)) && Math.abs(o - h) <= F * Math.max(1, Math.abs(o), Math.abs(h)) + } + var D, k = vec4_subtract, + L = vec4_multiply, + N = vec4_divide, + B = vec4_distance, + U = vec4_squaredDistance, + G = vec4_length, + j = vec4_squaredLength, + z = (D = vec4_create(), function(t, e, r, n, i, o) { + var a = void 0, + s = void 0; + for (e || (e = 4), r || (r = 0), s = n ? Math.min(n * e + r, t.length) : t.length, a = r; a < s; a += e) D[0] = t[a], D[1] = t[a + 1], D[2] = t[a + 2], D[3] = t[a + 3], i(D, D, o), t[a] = D[0], t[a + 1] = D[1], t[a + 2] = D[2], t[a + 3] = D[3]; + return t + }); + + function quat_create() { + var t = new g(4); + return g != Float32Array && (t[0] = 0, t[1] = 0, t[2] = 0), t[3] = 1, t + } + + function quat_identity(t) { + return t[0] = 0, t[1] = 0, t[2] = 0, t[3] = 1, t + } + + function setAxisAngle(t, e, r) { + r *= .5; + var n = Math.sin(r); + return t[0] = n * e[0], t[1] = n * e[1], t[2] = n * e[2], t[3] = Math.cos(r), t + } + + function getAxisAngle(t, e) { + var r = 2 * Math.acos(e[3]), + n = Math.sin(r / 2); + return t[2] = F < n ? (t[0] = e[0] / n, t[1] = e[1] / n, e[2] / n) : (t[0] = 1, t[1] = 0), r + } + + function quat_multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = r[0], + u = r[1], + h = r[2], + c = r[3]; + return t[0] = n * c + a * s + i * h - o * u, t[1] = i * c + a * u + o * s - n * h, t[2] = o * c + a * h + n * u - i * s, t[3] = a * c - n * s - i * u - o * h, t + } + + function quat_rotateX(t, e, r) { + r *= .5; + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = Math.sin(r), + u = Math.cos(r); + return t[0] = n * u + a * s, t[1] = i * u + o * s, t[2] = o * u - i * s, t[3] = a * u - n * s, t + } + + function quat_rotateY(t, e, r) { + r *= .5; + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = Math.sin(r), + u = Math.cos(r); + return t[0] = n * u - o * s, t[1] = i * u + a * s, t[2] = o * u + n * s, t[3] = a * u - i * s, t + } + + function quat_rotateZ(t, e, r) { + r *= .5; + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = Math.sin(r), + u = Math.cos(r); + return t[0] = n * u + i * s, t[1] = i * u - n * s, t[2] = o * u + a * s, t[3] = a * u - o * s, t + } + + function calculateW(t, e) { + var r = e[0], + n = e[1], + i = e[2]; + return t[0] = r, t[1] = n, t[2] = i, t[3] = Math.sqrt(Math.abs(1 - r * r - n * n - i * i)), t + } + + function slerp(t, e, r, n) { + var i = e[0], + o = e[1], + a = e[2], + s = e[3], + u = r[0], + h = r[1], + c = r[2], + l = r[3], + f = void 0, + d = void 0, + p = void 0, + m = void 0, + v = void 0; + return (d = i * u + o * h + a * c + s * l) < 0 && (d = -d, u = -u, h = -h, c = -c, l = -l), v = F < 1 - d ? (f = Math.acos(d), p = Math.sin(f), m = Math.sin((1 - n) * f) / p, Math.sin(n * f) / p) : (m = 1 - n, n), t[0] = m * i + v * u, t[1] = m * o + v * h, t[2] = m * a + v * c, t[3] = m * s + v * l, t + } + + function quat_random(t) { + var e = d(), + r = d(), + n = d(), + i = Math.sqrt(1 - e), + o = Math.sqrt(e); + return t[0] = i * Math.sin(2 * Math.PI * r), t[1] = i * Math.cos(2 * Math.PI * r), t[2] = o * Math.sin(2 * Math.PI * n), t[3] = o * Math.cos(2 * Math.PI * n), t + } + + function quat_invert(t, e) { + var r = e[0], + n = e[1], + i = e[2], + o = e[3], + a = r * r + n * n + i * i + o * o, + s = a ? 1 / a : 0; + return t[0] = -r * s, t[1] = -n * s, t[2] = -i * s, t[3] = o * s, t + } + + function conjugate(t, e) { + return t[0] = -e[0], t[1] = -e[1], t[2] = -e[2], t[3] = e[3], t + } + + function fromMat3(t, e) { + var r = e[0] + e[4] + e[8], + n = void 0; + if (0 < r) n = Math.sqrt(r + 1), t[3] = .5 * n, n = .5 / n, t[0] = (e[5] - e[7]) * n, t[1] = (e[6] - e[2]) * n, t[2] = (e[1] - e[3]) * n; + else { + var i = 0; + e[4] > e[0] && (i = 1), e[8] > e[3 * i + i] && (i = 2); + var o = (i + 1) % 3, + a = (i + 2) % 3; + n = Math.sqrt(e[3 * i + i] - e[3 * o + o] - e[3 * a + a] + 1), t[i] = .5 * n, n = .5 / n, t[3] = (e[3 * o + a] - e[3 * a + o]) * n, t[o] = (e[3 * o + i] + e[3 * i + o]) * n, t[a] = (e[3 * a + i] + e[3 * i + a]) * n + } + return t + } + + function fromEuler(t, e, r, n) { + var i = .5 * Math.PI / 180; + e *= i, r *= i, n *= i; + var o = Math.sin(e), + a = Math.cos(e), + s = Math.sin(r), + u = Math.cos(r), + h = Math.sin(n), + c = Math.cos(n); + return t[0] = o * u * c - a * s * h, t[1] = a * s * c + o * u * h, t[2] = a * u * h - o * s * c, t[3] = a * u * c + o * s * h, t + } + + function quat_str(t) { + return "quat(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ")" + } + var X, V, q, H, W, Y, K = vec4_clone, + Z = vec4_fromValues, + Q = vec4_copy, + J = vec4_set, + $ = vec4_add, + tt = quat_multiply, + et = vec4_scale, + rt = vec4_dot, + nt = vec4_lerp, + it = vec4_length, + ot = it, + at = vec4_squaredLength, + st = at, + ut = vec4_normalize, + ht = vec4_exactEquals, + ct = vec4_equals, + lt = (X = vec3_create(), V = vec3_fromValues(1, 0, 0), q = vec3_fromValues(0, 1, 0), function(t, e, r) { + var n = vec3_dot(e, r); + return n < -.999999 ? (cross(X, V, e), O(X) < 1e-6 && cross(X, q, e), normalize(X, X), setAxisAngle(t, X, Math.PI), t) : .999999 < n ? (t[0] = 0, t[1] = 0, t[2] = 0, t[3] = 1, t) : (cross(X, e, r), t[0] = X[0], t[1] = X[1], t[2] = X[2], t[3] = 1 + n, ut(t, t)) + }), + ft = (H = quat_create(), W = quat_create(), function(t, e, r, n, i, o) { + return slerp(H, e, i, o), slerp(W, r, n, o), slerp(t, H, W, 2 * o * (1 - o)), t + }), + dt = (Y = mat3_create(), function(t, e, r, n) { + return Y[0] = r[0], Y[3] = r[1], Y[6] = r[2], Y[1] = n[0], Y[4] = n[1], Y[7] = n[2], Y[2] = -e[0], Y[5] = -e[1], Y[8] = -e[2], ut(t, fromMat3(t, Y)) + }); + + function quat2_create() { + var t = new g(8); + return g != Float32Array && (t[0] = 0, t[1] = 0, t[2] = 0, t[4] = 0, t[5] = 0, t[6] = 0, t[7] = 0), t[3] = 1, t + } + + function quat2_clone(t) { + var e = new g(8); + return e[0] = t[0], e[1] = t[1], e[2] = t[2], e[3] = t[3], e[4] = t[4], e[5] = t[5], e[6] = t[6], e[7] = t[7], e + } + + function quat2_fromValues(t, e, r, n, i, o, a, s) { + var u = new g(8); + return u[0] = t, u[1] = e, u[2] = r, u[3] = n, u[4] = i, u[5] = o, u[6] = a, u[7] = s, u + } + + function fromRotationTranslationValues(t, e, r, n, i, o, a) { + var s = new g(8); + s[0] = t, s[1] = e, s[2] = r, s[3] = n; + var u = .5 * i, + h = .5 * o, + c = .5 * a; + return s[4] = u * n + h * r - c * e, s[5] = h * n + c * t - u * r, s[6] = c * n + u * e - h * t, s[7] = -u * t - h * e - c * r, s + } + + function quat2_fromRotationTranslation(t, e, r) { + var n = .5 * r[0], + i = .5 * r[1], + o = .5 * r[2], + a = e[0], + s = e[1], + u = e[2], + h = e[3]; + return t[0] = a, t[1] = s, t[2] = u, t[3] = h, t[4] = n * h + i * u - o * s, t[5] = i * h + o * a - n * u, t[6] = o * h + n * s - i * a, t[7] = -n * a - i * s - o * u, t + } + + function quat2_fromTranslation(t, e) { + return t[0] = 0, t[1] = 0, t[2] = 0, t[3] = 1, t[4] = .5 * e[0], t[5] = .5 * e[1], t[6] = .5 * e[2], t[7] = 0, t + } + + function quat2_fromRotation(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = 0, t[5] = 0, t[6] = 0, t[7] = 0, t + } + + function quat2_fromMat4(t, e) { + var r = quat_create(); + getRotation(r, e); + var n = new g(3); + return getTranslation(n, e), quat2_fromRotationTranslation(t, r, n), t + } + + function quat2_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4], t[5] = e[5], t[6] = e[6], t[7] = e[7], t + } + + function quat2_identity(t) { + return t[0] = 0, t[1] = 0, t[2] = 0, t[3] = 1, t[4] = 0, t[5] = 0, t[6] = 0, t[7] = 0, t + } + + function quat2_set(t, e, r, n, i, o, a, s, u) { + return t[0] = e, t[1] = r, t[2] = n, t[3] = i, t[4] = o, t[5] = a, t[6] = s, t[7] = u, t + } + var pt = Q; + + function getDual(t, e) { + return t[0] = e[4], t[1] = e[5], t[2] = e[6], t[3] = e[7], t + } + var mt = Q; + + function setDual(t, e) { + return t[4] = e[0], t[5] = e[1], t[6] = e[2], t[7] = e[3], t + } + + function quat2_getTranslation(t, e) { + var r = e[4], + n = e[5], + i = e[6], + o = e[7], + a = -e[0], + s = -e[1], + u = -e[2], + h = e[3]; + return t[0] = 2 * (r * h + o * a + n * u - i * s), t[1] = 2 * (n * h + o * s + i * a - r * u), t[2] = 2 * (i * h + o * u + r * s - n * a), t + } + + function quat2_translate(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = .5 * r[0], + u = .5 * r[1], + h = .5 * r[2], + c = e[4], + l = e[5], + f = e[6], + d = e[7]; + return t[0] = n, t[1] = i, t[2] = o, t[3] = a, t[4] = a * s + i * h - o * u + c, t[5] = a * u + o * s - n * h + l, t[6] = a * h + n * u - i * s + f, t[7] = -n * s - i * u - o * h + d, t + } + + function quat2_rotateX(t, e, r) { + var n = -e[0], + i = -e[1], + o = -e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = s * a + c * n + u * o - h * i, + f = u * a + c * i + h * n - s * o, + d = h * a + c * o + s * i - u * n, + p = c * a - s * n - u * i - h * o; + return quat_rotateX(t, e, r), n = t[0], i = t[1], o = t[2], a = t[3], t[4] = l * a + p * n + f * o - d * i, t[5] = f * a + p * i + d * n - l * o, t[6] = d * a + p * o + l * i - f * n, t[7] = p * a - l * n - f * i - d * o, t + } + + function quat2_rotateY(t, e, r) { + var n = -e[0], + i = -e[1], + o = -e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = s * a + c * n + u * o - h * i, + f = u * a + c * i + h * n - s * o, + d = h * a + c * o + s * i - u * n, + p = c * a - s * n - u * i - h * o; + return quat_rotateY(t, e, r), n = t[0], i = t[1], o = t[2], a = t[3], t[4] = l * a + p * n + f * o - d * i, t[5] = f * a + p * i + d * n - l * o, t[6] = d * a + p * o + l * i - f * n, t[7] = p * a - l * n - f * i - d * o, t + } + + function quat2_rotateZ(t, e, r) { + var n = -e[0], + i = -e[1], + o = -e[2], + a = e[3], + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = s * a + c * n + u * o - h * i, + f = u * a + c * i + h * n - s * o, + d = h * a + c * o + s * i - u * n, + p = c * a - s * n - u * i - h * o; + return quat_rotateZ(t, e, r), n = t[0], i = t[1], o = t[2], a = t[3], t[4] = l * a + p * n + f * o - d * i, t[5] = f * a + p * i + d * n - l * o, t[6] = d * a + p * o + l * i - f * n, t[7] = p * a - l * n - f * i - d * o, t + } + + function rotateByQuatAppend(t, e, r) { + var n = r[0], + i = r[1], + o = r[2], + a = r[3], + s = e[0], + u = e[1], + h = e[2], + c = e[3]; + return t[0] = s * a + c * n + u * o - h * i, t[1] = u * a + c * i + h * n - s * o, t[2] = h * a + c * o + s * i - u * n, t[3] = c * a - s * n - u * i - h * o, s = e[4], u = e[5], h = e[6], c = e[7], t[4] = s * a + c * n + u * o - h * i, t[5] = u * a + c * i + h * n - s * o, t[6] = h * a + c * o + s * i - u * n, t[7] = c * a - s * n - u * i - h * o, t + } + + function rotateByQuatPrepend(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = r[0], + u = r[1], + h = r[2], + c = r[3]; + return t[0] = n * c + a * s + i * h - o * u, t[1] = i * c + a * u + o * s - n * h, t[2] = o * c + a * h + n * u - i * s, t[3] = a * c - n * s - i * u - o * h, s = r[4], u = r[5], h = r[6], c = r[7], t[4] = n * c + a * s + i * h - o * u, t[5] = i * c + a * u + o * s - n * h, t[6] = o * c + a * h + n * u - i * s, t[7] = a * c - n * s - i * u - o * h, t + } + + function rotateAroundAxis(t, e, r, n) { + if (Math.abs(n) < F) return quat2_copy(t, e); + var i = Math.sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]); + n *= .5; + var o = Math.sin(n), + a = o * r[0] / i, + s = o * r[1] / i, + u = o * r[2] / i, + h = Math.cos(n), + c = e[0], + l = e[1], + f = e[2], + d = e[3]; + t[0] = c * h + d * a + l * u - f * s, t[1] = l * h + d * s + f * a - c * u, t[2] = f * h + d * u + c * s - l * a, t[3] = d * h - c * a - l * s - f * u; + var p = e[4], + m = e[5], + v = e[6], + g = e[7]; + return t[4] = p * h + g * a + m * u - v * s, t[5] = m * h + g * s + v * a - p * u, t[6] = v * h + g * u + p * s - m * a, t[7] = g * h - p * a - m * s - v * u, t + } + + function quat2_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t[2] = e[2] + r[2], t[3] = e[3] + r[3], t[4] = e[4] + r[4], t[5] = e[5] + r[5], t[6] = e[6] + r[6], t[7] = e[7] + r[7], t + } + + function quat2_multiply(t, e, r) { + var n = e[0], + i = e[1], + o = e[2], + a = e[3], + s = r[4], + u = r[5], + h = r[6], + c = r[7], + l = e[4], + f = e[5], + d = e[6], + p = e[7], + m = r[0], + v = r[1], + g = r[2], + y = r[3]; + return t[0] = n * y + a * m + i * g - o * v, t[1] = i * y + a * v + o * m - n * g, t[2] = o * y + a * g + n * v - i * m, t[3] = a * y - n * m - i * v - o * g, t[4] = n * c + a * s + i * h - o * u + l * y + p * m + f * g - d * v, t[5] = i * c + a * u + o * s - n * h + f * y + p * v + d * m - l * g, t[6] = o * c + a * h + n * u - i * s + d * y + p * g + l * v - f * m, t[7] = a * c - n * s - i * u - o * h + p * y - l * m - f * v - d * g, t + } + var vt = quat2_multiply; + + function quat2_scale(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t[2] = e[2] * r, t[3] = e[3] * r, t[4] = e[4] * r, t[5] = e[5] * r, t[6] = e[6] * r, t[7] = e[7] * r, t + } + var gt = rt; + + function quat2_lerp(t, e, r, n) { + var i = 1 - n; + return gt(e, r) < 0 && (n = -n), t[0] = e[0] * i + r[0] * n, t[1] = e[1] * i + r[1] * n, t[2] = e[2] * i + r[2] * n, t[3] = e[3] * i + r[3] * n, t[4] = e[4] * i + r[4] * n, t[5] = e[5] * i + r[5] * n, t[6] = e[6] * i + r[6] * n, t[7] = e[7] * i + r[7] * n, t + } + + function quat2_invert(t, e) { + var r = bt(e); + return t[0] = -e[0] / r, t[1] = -e[1] / r, t[2] = -e[2] / r, t[3] = e[3] / r, t[4] = -e[4] / r, t[5] = -e[5] / r, t[6] = -e[6] / r, t[7] = e[7] / r, t + } + + function quat2_conjugate(t, e) { + return t[0] = -e[0], t[1] = -e[1], t[2] = -e[2], t[3] = e[3], t[4] = -e[4], t[5] = -e[5], t[6] = -e[6], t[7] = e[7], t + } + var yt = it, + _t = yt, + bt = at, + xt = bt; + + function quat2_normalize(t, e) { + var r = bt(e); + if (0 < r) { + r = Math.sqrt(r); + var n = e[0] / r, + i = e[1] / r, + o = e[2] / r, + a = e[3] / r, + s = e[4], + u = e[5], + h = e[6], + c = e[7], + l = n * s + i * u + o * h + a * c; + t[0] = n, t[1] = i, t[2] = o, t[3] = a, t[4] = (s - n * l) / r, t[5] = (u - i * l) / r, t[6] = (h - o * l) / r, t[7] = (c - a * l) / r + } + return t + } + + function quat2_str(t) { + return "quat2(" + t[0] + ", " + t[1] + ", " + t[2] + ", " + t[3] + ", " + t[4] + ", " + t[5] + ", " + t[6] + ", " + t[7] + ")" + } + + function quat2_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] && t[2] === e[2] && t[3] === e[3] && t[4] === e[4] && t[5] === e[5] && t[6] === e[6] && t[7] === e[7] + } + + function quat2_equals(t, e) { + var r = t[0], + n = t[1], + i = t[2], + o = t[3], + a = t[4], + s = t[5], + u = t[6], + h = t[7], + c = e[0], + l = e[1], + f = e[2], + d = e[3], + p = e[4], + m = e[5], + v = e[6], + g = e[7]; + return Math.abs(r - c) <= F * Math.max(1, Math.abs(r), Math.abs(c)) && Math.abs(n - l) <= F * Math.max(1, Math.abs(n), Math.abs(l)) && Math.abs(i - f) <= F * Math.max(1, Math.abs(i), Math.abs(f)) && Math.abs(o - d) <= F * Math.max(1, Math.abs(o), Math.abs(d)) && Math.abs(a - p) <= F * Math.max(1, Math.abs(a), Math.abs(p)) && Math.abs(s - m) <= F * Math.max(1, Math.abs(s), Math.abs(m)) && Math.abs(u - v) <= F * Math.max(1, Math.abs(u), Math.abs(v)) && Math.abs(h - g) <= F * Math.max(1, Math.abs(h), Math.abs(g)) + } + + function vec2_create() { + var t = new g(2); + return g != Float32Array && (t[0] = 0, t[1] = 0), t + } + + function vec2_clone(t) { + var e = new g(2); + return e[0] = t[0], e[1] = t[1], e + } + + function vec2_fromValues(t, e) { + var r = new g(2); + return r[0] = t, r[1] = e, r + } + + function vec2_copy(t, e) { + return t[0] = e[0], t[1] = e[1], t + } + + function vec2_set(t, e, r) { + return t[0] = e, t[1] = r, t + } + + function vec2_add(t, e, r) { + return t[0] = e[0] + r[0], t[1] = e[1] + r[1], t + } + + function vec2_subtract(t, e, r) { + return t[0] = e[0] - r[0], t[1] = e[1] - r[1], t + } + + function vec2_multiply(t, e, r) { + return t[0] = e[0] * r[0], t[1] = e[1] * r[1], t + } + + function vec2_divide(t, e, r) { + return t[0] = e[0] / r[0], t[1] = e[1] / r[1], t + } + + function vec2_ceil(t, e) { + return t[0] = Math.ceil(e[0]), t[1] = Math.ceil(e[1]), t + } + + function vec2_floor(t, e) { + return t[0] = Math.floor(e[0]), t[1] = Math.floor(e[1]), t + } + + function vec2_min(t, e, r) { + return t[0] = Math.min(e[0], r[0]), t[1] = Math.min(e[1], r[1]), t + } + + function vec2_max(t, e, r) { + return t[0] = Math.max(e[0], r[0]), t[1] = Math.max(e[1], r[1]), t + } + + function vec2_round(t, e) { + return t[0] = Math.round(e[0]), t[1] = Math.round(e[1]), t + } + + function vec2_scale(t, e, r) { + return t[0] = e[0] * r, t[1] = e[1] * r, t + } + + function vec2_scaleAndAdd(t, e, r, n) { + return t[0] = e[0] + r[0] * n, t[1] = e[1] + r[1] * n, t + } + + function vec2_distance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1]; + return Math.sqrt(r * r + n * n) + } + + function vec2_squaredDistance(t, e) { + var r = e[0] - t[0], + n = e[1] - t[1]; + return r * r + n * n + } + + function vec2_length(t) { + var e = t[0], + r = t[1]; + return Math.sqrt(e * e + r * r) + } + + function vec2_squaredLength(t) { + var e = t[0], + r = t[1]; + return e * e + r * r + } + + function vec2_negate(t, e) { + return t[0] = -e[0], t[1] = -e[1], t + } + + function vec2_inverse(t, e) { + return t[0] = 1 / e[0], t[1] = 1 / e[1], t + } + + function vec2_normalize(t, e) { + var r = e[0], + n = e[1], + i = r * r + n * n; + return 0 < i && (i = 1 / Math.sqrt(i), t[0] = e[0] * i, t[1] = e[1] * i), t + } + + function vec2_dot(t, e) { + return t[0] * e[0] + t[1] * e[1] + } + + function vec2_cross(t, e, r) { + var n = e[0] * r[1] - e[1] * r[0]; + return t[0] = t[1] = 0, t[2] = n, t + } + + function vec2_lerp(t, e, r, n) { + var i = e[0], + o = e[1]; + return t[0] = i + n * (r[0] - i), t[1] = o + n * (r[1] - o), t + } + + function vec2_random(t, e) { + e = e || 1; + var r = 2 * d() * Math.PI; + return t[0] = Math.cos(r) * e, t[1] = Math.sin(r) * e, t + } + + function transformMat2(t, e, r) { + var n = e[0], + i = e[1]; + return t[0] = r[0] * n + r[2] * i, t[1] = r[1] * n + r[3] * i, t + } + + function transformMat2d(t, e, r) { + var n = e[0], + i = e[1]; + return t[0] = r[0] * n + r[2] * i + r[4], t[1] = r[1] * n + r[3] * i + r[5], t + } + + function vec2_transformMat3(t, e, r) { + var n = e[0], + i = e[1]; + return t[0] = r[0] * n + r[3] * i + r[6], t[1] = r[1] * n + r[4] * i + r[7], t + } + + function vec2_transformMat4(t, e, r) { + var n = e[0], + i = e[1]; + return t[0] = r[0] * n + r[4] * i + r[12], t[1] = r[1] * n + r[5] * i + r[13], t + } + + function vec2_rotate(t, e, r, n) { + var i = e[0] - r[0], + o = e[1] - r[1], + a = Math.sin(n), + s = Math.cos(n); + return t[0] = i * s - o * a + r[0], t[1] = i * a + o * s + r[1], t + } + + function vec2_angle(t, e) { + var r = t[0], + n = t[1], + i = e[0], + o = e[1], + a = r * r + n * n; + 0 < a && (a = 1 / Math.sqrt(a)); + var s = i * i + o * o; + 0 < s && (s = 1 / Math.sqrt(s)); + var u = (r * i + n * o) * a * s; + return 1 < u ? 0 : u < -1 ? Math.PI : Math.acos(u) + } + + function vec2_str(t) { + return "vec2(" + t[0] + ", " + t[1] + ")" + } + + function vec2_exactEquals(t, e) { + return t[0] === e[0] && t[1] === e[1] + } + + function vec2_equals(t, e) { + var r = t[0], + n = t[1], + i = e[0], + o = e[1]; + return Math.abs(r - i) <= F * Math.max(1, Math.abs(r), Math.abs(i)) && Math.abs(n - o) <= F * Math.max(1, Math.abs(n), Math.abs(o)) + } + var wt, Tt = vec2_length, + St = vec2_subtract, + Mt = vec2_multiply, + Et = vec2_divide, + At = vec2_distance, + Pt = vec2_squaredDistance, + It = vec2_squaredLength, + Ot = (wt = vec2_create(), function(t, e, r, n, i, o) { + var a = void 0, + s = void 0; + for (e || (e = 2), r || (r = 0), s = n ? Math.min(n * e + r, t.length) : t.length, a = r; a < s; a += e) wt[0] = t[a], wt[1] = t[a + 1], i(wt, wt, o), t[a] = wt[0], t[a + 1] = wt[1]; + return t + }); + r.d(e, "glMatrix", function() { + return n + }), r.d(e, "mat2", function() { + return i + }), r.d(e, "mat2d", function() { + return o + }), r.d(e, "mat3", function() { + return a + }), r.d(e, "mat4", function() { + return s + }), r.d(e, "quat", function() { + return c + }), r.d(e, "quat2", function() { + return l + }), r.d(e, "vec2", function() { + return f + }), r.d(e, "vec3", function() { + return u + }), r.d(e, "vec4", function() { + return h + }) + }, function(t, e, r) { + "use strict"; + e.__esModule = !0; + var n = function() { + function Component(t, e) { + if (function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, Component), !t) throw new Error("component requires an entity"); + this.entity = t, this.data = e, this.name = "", this.game = null, this.active = !1 + } + return Component.create = function(t, r) { + if (Component.mapHash[t]) return Component.mapHash[t]; + + function com(t, e) { + this.entity = t, this.data = e, r.constructor && r.constructor.call(this) + } + for (var e in com.prototype = Object.create(Component.prototype), com.id = t, r) com.prototype[e] = r[e]; + return Component.mapHash[t] = com + }, Component + }(); + (e.default = n).mapHash = {} + }, function(t, e, r) { + var n = r(11); + t.exports = function(t) { + if (!n(t)) throw TypeError(t + " is not an object!"); + return t + } + }, function(t, e, r) { + "use strict"; + + function _defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + + function mockPromise() { + for (var t = arguments.length, e = new Array(t), r = 0; r < t; r++) e[r] = arguments[r]; + return { + then: function(t) { + return t.apply(void 0, e) + } + } + } + r.d(e, "a", function() { + return s + }); + var n = function() { + function PokiSDKMockup() {} + var t = PokiSDKMockup.prototype; + return t.init = function() { + return mockPromise() + }, t.gameLoadingStart = function() { + return mockPromise() + }, t.gameLoadingFinished = function() { + return mockPromise() + }, t.gameplayStart = function() { + return mockPromise() + }, t.gameplayStop = function() { + return mockPromise() + }, t.commercialBreak = function() { + return mockPromise() + }, t.happyTime = function(t) { + return mockPromise() + }, t.rewardedBreak = function() { + return mockPromise(!0) + }, PokiSDKMockup + }(), + i = function() { + function PokiSDKHangoutMockup() {} + var t = PokiSDKHangoutMockup.prototype; + return t.setDebug = function() { + return mockPromise() + }, t.getLobbyId = function() { + return mockPromise() + }, t.getLeaderboard = function(t, e, r) { + return mockPromise([{ + name: "Yutani", + image: "icon_07_yutani", + score: 999999 + }, { + name: "Spike", + image: "icon_08_spike", + score: 810312 + }, { + name: "Fresh", + image: "icon_09_fresh", + score: 555555 + }, { + name: "Tricky", + image: "icon_11_tricky", + score: 345678 + }, { + name: "Lucy", + image: "icon_04_lucy", + score: 171110 + }, { + name: "Ninja", + image: "icon_03_ninja", + score: 123456 + }, { + name: "Brody", + image: "icon_00_brody", + score: 65900 + }, { + name: "Tagbot", + image: "icon_01_tagbot", + score: 15505 + }, { + name: "Tasha", + image: "icon_02_tasha", + score: 1e3 + }, { + name: "King", + image: "icon_05_king", + score: 500 + }, { + name: "ME", + image: "icon_friend", + score: r + }].sort(function(t, e) { + return e.score - t.score + })) + }, t.initHighscoreSession = function() { + return mockPromise() + }, t.saveHighscore = function() { + return mockPromise() + }, PokiSDKHangoutMockup + }(), + o = new n, + a = new i, + s = function() { + function Tabouzigt() {} + var t, e, r; + return Tabouzigt.sendCustomMessage = function(t, e, r) { + window.parent && window.parent.postMessage({ + type: "pokiMessageEvent", + content: { + event: "pokiTrackingCustom", + data: { + eventNoun: t, + eventVerb: e, + eventData: r + } + } + }, "*") + }, t = Tabouzigt, r = [{ + key: "SDK", + get: function() { + var t = window.Tabouzigtsdk || o; + return t && !t.sendCustomMessage && (t.sendCustomMessage = this.sendCustomMessage), t + } + }, { + key: "hangout", + get: function() { + var t = window.Tabouzigtsdk || o; + return t && t.hangout ? t.hangout : a + } + }], (e = null) && _defineProperties(t.prototype, e), r && _defineProperties(t, r), Tabouzigt + }() + }, function(t, e, r) { + "use strict"; + e.__esModule = !0; + var n = function() { + function defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + return function(t, e, r) { + return e && defineProperties(t.prototype, e), r && defineProperties(t, r), t + } + }(); + var i = function() { + function Runner(t) { + var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : 0; + ! function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, Runner), this.items = [], this._name = t, this.running = !1, this.dispatch = Runner.generateRun(t, e), this.run = this.dispatch, this.emit = this.dispatch, this.dummy = {}, this.dummy[t] = function() {} + } + return Runner.prototype.add = function(t) { + t[this._name] && (this.remove(t), this.items.push(t), this.toRemove = [], this.needsTidy = !1) + }, Runner.prototype.tidy = function() { + for (var t = 0; t < this.toRemove.length; t++) { + var e = this.toRemove[t]; - 1 !== e && this.items.splice(e, 1) + } + }, Runner.prototype.remove = function(t) { + var e = this.items.indexOf(t); + this.running ? (this.needsTidy = !0, this.toRemove.push(e), this.items[e] = this.dummy) : -1 !== e && this.items.splice(e, 1) + }, Runner.prototype.contains = function(t) { + return -1 !== this.items.indexOf(t) + }, Runner.prototype.removeAll = function() { + this.items.length = 0 + }, Runner.generateRun = function(t, e) { + var r = t + "|" + e, + n = Runner.hash[r]; + if (!n) { + if (0 < e) { + for (var i = "arg0", o = 1; o < e; o++) i += ",arg" + o; + n = new Function(i, "this.running = true; \n var items = this.items; \n for(var i=0;i= e.length && e === convertArrayBufferToString(t, 0, e.length) + } + + function isFbxFormatASCII(t) { + var e, r, n = ["K", "a", "y", "d", "a", "r", "a", "\\", "F", "B", "X", "\\", "B", "i", "n", "a", "r", "y", "\\", "\\"], + i = 0; + for (var o = 0; o < n.length; ++o) { + if ((void 0, r = t[(e = 1) - 1], t = t.slice(i + e), i++, r) === n[o]) return !1 + } + return !0 + } + + function getFbxVersion(t) { + var e = t.match(/FBXVersion: (\d+)/); + if (e) return parseInt(e[1]); + throw new Error("THREE.FBXLoader: Cannot find the version number for the file given.") + } + + function convertFBXTimeToSeconds(t) { + return t / 46186158e3 + } + var s = []; + + function getData(t, e, r, n) { + var i; + switch (n.mappingType) { + case "ByPolygonVertex": + i = t; + break; + case "ByPolygon": + i = e; + break; + case "ByVertice": + i = r; + break; + case "AllSame": + i = n.indices[0] + } + "IndexToDirect" === n.referenceType && (i = n.indices[i]); + var o = i * n.dataSize, + a = o + n.dataSize; + return function(t, e, r, n) { + for (var i = r, o = 0; i < n; i++, o++) t[o] = e[i]; + return t + }(s, n.buffer, o, a) + } + var I = new P.k, + O = new P.P; + + function generateTransform(t) { + var e = new P.v, + r = new P.v, + n = new P.v, + i = new P.v, + o = new P.v, + a = new P.v, + s = new P.v, + u = new P.v, + h = new P.v, + c = new P.v, + l = new P.v, + f = t.inheritType ? t.inheritType : 0; + if (t.translation && e.setPosition(O.fromArray(t.translation)), t.preRotation) { + var d = t.preRotation.map(P.t.degToRad); + d.push(t.eulerOrder), r.makeRotationFromEuler(I.fromArray(d)) + } + if (t.rotation) { + var p = t.rotation.map(P.t.degToRad); + p.push(t.eulerOrder), n.makeRotationFromEuler(I.fromArray(p)) + } + if (t.postRotation) { + var m = t.postRotation.map(P.t.degToRad); + m.push(t.eulerOrder), i.makeRotationFromEuler(I.fromArray(m)) + } + t.scale && o.scale(O.fromArray(t.scale)), t.scalingOffset && s.setPosition(O.fromArray(t.scalingOffset)), t.scalingPivot && a.setPosition(O.fromArray(t.scalingPivot)), t.rotationOffset && u.setPosition(O.fromArray(t.rotationOffset)), t.rotationPivot && h.setPosition(O.fromArray(t.rotationPivot)), t.parentMatrixWorld && (c = t.parentMatrixWorld); + var v = r.multiply(n).multiply(i), + g = new P.v; + c.extractRotation(g); + var y, _, b, x, w = new P.v; + if (w.copyPosition(c), b = w.getInverse(w).multiply(c), _ = g.getInverse(g).multiply(b), y = o, 0 === f) x = g.multiply(v).multiply(_).multiply(y); + else if (1 === f) x = g.multiply(_).multiply(v).multiply(y); + else { + var T = (new P.v).copy(o), + S = _.multiply(T.getInverse(T)); + x = g.multiply(v).multiply(S).multiply(y) + } + var M = e.multiply(u).multiply(h).multiply(r).multiply(n).multiply(i).multiply(h.getInverse(h)).multiply(s).multiply(a).multiply(o).multiply(a.getInverse(a)), + E = (new P.v).copyPosition(M), + A = c.multiply(E); + return l.copyPosition(A), M = l.multiply(x) + } + + function getEulerOrder(t) { + var e = ["ZYX", "YZX", "XZY", "ZXY", "YXZ", "XYZ"]; + return 6 === (t = t || 0) ? e[0] : e[t] + } + + function parseNumberArray(t) { + return t.split(",").map(function(t) { + return parseFloat(t) + }) + } + + function convertArrayBufferToString(t, e, r) { + return void 0 === e && (e = 0), void 0 === r && (r = t.byteLength), P.q.decodeText(new Uint8Array(t, e, r)) + } + + function append(t, e) { + for (var r = 0, n = t.length, i = e.length; r < i; r++, n++) t[n] = e[r] + } + + function inject(t, e, r) { + return t.slice(0, e).concat(r).concat(t.slice(e)) + } + }, function(t, e, r) { + var n = r(35), + i = Math.min; + t.exports = function(t) { + return 0 < t ? i(n(t), 9007199254740991) : 0 + } + }, function(t, e, r) { + "use strict"; + + function arraySlice(t, e, r) { + return isTypedArray(t) ? new t.constructor(t.subarray(e, void 0 !== r ? r : t.length)) : t.slice(e, r) + } + + function convertArray(t, e, r) { + return !t || !r && t.constructor === e ? t : "number" == typeof e.BYTES_PER_ELEMENT ? new e(t) : Array.prototype.slice.call(t) + } + + function isTypedArray(t) { + return ArrayBuffer.isView(t) && !(t instanceof DataView) + } + + function getKeyframeOrder(r) { + for (var t = r.length, e = new Array(t), n = 0; n !== t; ++n) e[n] = n; + return e.sort(function(t, e) { + return r[t] - r[e] + }), e + } + + function sortedArray(t, e, r) { + for (var n = t.length, i = new t.constructor(n), o = 0, a = 0; a !== n; ++o) + for (var s = r[o] * e, u = 0; u !== e; ++u) i[a++] = t[s + u]; + return i + } + + function flattenJSON(t, e, r, n) { + for (var i = 1, o = t[0]; void 0 !== o && void 0 === o[n];) o = t[i++]; + if (void 0 !== o) { + var a = o[n]; + if (void 0 !== a) + if (Array.isArray(a)) + for (; void 0 !== (a = o[n]) && (e.push(o.time), r.push.apply(r, a)), void 0 !== (o = t[i++]);); + else if (void 0 !== a.toArray) + for (; void 0 !== (a = o[n]) && (e.push(o.time), a.toArray(r, r.length)), void 0 !== (o = t[i++]);); + else + for (; void 0 !== (a = o[n]) && (e.push(o.time), r.push(a)), void 0 !== (o = t[i++]);); + } + } + + function subclip(t, e, r, n, i) { + i = i || 30; + var o = t.clone(); + o.name = e; + for (var a = [], s = 0; s < o.tracks.length; ++s) { + for (var u = o.tracks[s], h = u.getValueSize(), c = [], l = [], f = 0; f < u.times.length; ++f) { + var d = u.times[f] * i; + if (!(d < r || n <= d)) { + c.push(u.times[f]); + for (var p = 0; p < h; ++p) l.push(u.values[f * h + p]) + } + } + 0 !== c.length && (u.times = convertArray(c, u.times.constructor), u.values = convertArray(l, u.values.constructor), a.push(u)) + } + o.tracks = a; + var m = 1 / 0; + for (s = 0; s < o.tracks.length; ++s) m > o.tracks[s].times[0] && (m = o.tracks[s].times[0]); + for (s = 0; s < o.tracks.length; ++s) o.tracks[s].shift(-1 * m); + return o.resetDuration(), o + } + r.d(e, "a", function() { + return arraySlice + }), r.d(e, "b", function() { + return convertArray + }), r.d(e, "e", function() { + return isTypedArray + }), r.d(e, "d", function() { + return getKeyframeOrder + }), r.d(e, "f", function() { + return sortedArray + }), r.d(e, "c", function() { + return flattenJSON + }), r.d(e, "g", function() { + return subclip + }) + }, function(t, e, r) { + t.exports = !r(10)(function() { + return 7 != Object.defineProperty({}, "a", { + get: function() { + return 7 + } + }).a + }) + }, function(t, e, r) { + var n = r(6), + i = r(134), + o = r(38), + a = Object.defineProperty; + e.f = r(19) ? Object.defineProperty : function(t, e, r) { + if (n(t), e = o(e, !0), n(r), i) try { + return a(t, e, r) + } catch (t) {} + if ("get" in r || "set" in r) throw TypeError("Accessors not supported!"); + return "value" in r && (t[e] = r.value), t + } + }, function(t, e, r) { + "use strict"; + + function _defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + var n = function() { + function Device() { + this.arora = !1, this.chrome = !1, this.epiphany = !1, this.firefox = !1, this.mobileSafari = !1, this.ie = !1, this.ieVersion = 0, this.trident = !1, this.tridentVersion = 0, this.midori = !1, this.opera = !1, this.safari = !1, this.silk = !1, this.webApp = !1, this.cocoonJS = !1, this.android = !1, this.chromeOS = !1, this.iOS = !1, this.linux = !1, this.macOS = !1, this.windows = !1, this.desktop = !1, this.pixelRatio = 0, this.iPhone = !1, this.iPhone4 = !1, this.iPad = !1, this.blob = !1, this.canvas = !1, this.localStorage = !1, this.file = !1, this.fileSystem = !1, this.webGL = !1, this.worker = !1, this.audioData = !1, this.webAudio = !1, this.ogg = !1, this.opus = !1, this.mp3 = !1, this.wav = !1, this.m4a = !1, this.webm = !1; + var t = navigator.userAgent; + this._checkBrowser(t), this._checkOS(t), this._checkDevice(t), this._checkAudio(), this._checkFeatures(), this._checkIsMobile(), this._checkIsTouch() + } + var t, e, r, n = Device.prototype; + return n._checkBrowser = function(t) { + /Arora/.test(t) ? this.arora = !0 : /Opera|OPR|op/.test(t) ? (this.opera = !0, this.chrome = !1) : /Chrome/.test(t) ? this.chrome = !0 : /Epiphany/.test(t) ? this.epiphany = !0 : /Firefox/.test(t) ? this.firefox = !0 : /Mobile Safari/.test(t) ? this.mobileSafari = !0 : /MSIE (\d+\.\d+);/.test(t) || navigator.userAgent.match(/Trident.*rv[ :]*11\./) ? (this.ie = !0, this.ieVersion = parseInt(RegExp.$1, 10)) : /Midori/.test(t) ? this.midori = !0 : /Safari/.test(t) ? this.safari = !0 : /\bSilk\b/.test(t) ? this.silk = !0 : /Trident\/(\d+\.\d+)(.*)rv:(\d+\.\d+)/.test(t) && (this.ie = !0, this.trident = !0, this.tridentVersion = parseInt(RegExp.$1, 10), this.ieVersion = parseInt(RegExp.$3, 10)), navigator.standalone && (this.webApp = !0), navigator.isCocoonJS && (this.cocoonJS = !0) + }, n._checkOS = function(t) { + if (/Android/.test(t)) this.android = !0; + else if (/CrOS/.test(t)) this.chromeOS = !0; + else if (/iP[ao]d|iPhone/i.test(t)) { + this.iOS = !0; + var e = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); + this.iOS_version = [parseInt(e[1], 10), parseInt(e[2], 10), parseInt(e[3] || 0, 10)] + } else /Linux/.test(t) ? this.linux = !0 : /Mac OS/.test(t) ? this.macOS = !0 : /Windows/.test(t) && (this.windows = !0); + (this.windows || this.macOS || this.linux || this.chromeOS) && (this.desktop = !0) + }, n._checkDevice = function() { + this.pixelRatio = window.devicePixelRatio || 1, this.iPhone = -1 !== navigator.userAgent.toLowerCase().indexOf("iphone"), this.iPhone4 = 2 === this.pixelRatio && this.iPhone, this.iPhone4 && (this.iPhone4 = 480 == window.screen.height && 320 == window.screen.width || 480 == window.screen.width && 320 == window.screen.height), this.iPad = -1 !== navigator.userAgent.toLowerCase().indexOf("ipad") + }, n._checkFeatures = function() { + void 0 !== window.Blob && (this.blob = !0), this.canvas = !!window.CanvasRenderingContext2D; + try { + this.localStorage = !!localStorage.getItem + } catch (t) { + this.localStorage = !1 + } + this.file = !!(window.File && window.FileReader && window.FileList && window.Blob), this.fileSystem = !!window.requestFileSystem, this.webGL = function() { + try { + var t = document.createElement("canvas"); + return !!window.WebGLRenderingContext && (t.getContext("webgl") || t.getContext("experimental-webgl")) + } catch (t) { + return !1 + } + }(), (this.android || this.ie) && (this.webGL = !1), this.worker = !!window.Worker, ("ontouchstart" in document.documentElement || window.navigator.msPointerEnabled) && (this.touch = !0) + }, n._checkAudio = function() { + this.audioData = !!window.Audio, this.webaudio = !(!window.AudioContext && !window.webkitAudioContext); + var t = document.createElement("audio"); + try { + !1 == !!t.canPlayType && (t.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, "") && (this.ogg = !0), t.canPlayType("audio/mpeg;").replace(/^no$/, "") && (this.mp3 = !0), t.canPlayType('audio/wav; codecs="1"').replace(/^no$/, "") && (this.wav = !0), (t.canPlayType("audio/x-m4a;") || t.canPlayType("audio/aac;").replace(/^no$/, "")) && (this.m4a = !0)) + } catch (t) {} + }, n._checkIsMobile = function() { + var t, e = !1; + t = navigator.userAgent || navigator.vendor || window.opera, (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(t) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(t.substr(0, 4))) && (e = !0), this.isMobile = e, this.mobile = e + }, n._checkIsTouch = function() { + this.isTouch = "ontouchstart" in window || 0 < navigator.MaxTouchPoints || 0 < navigator.msMaxTouchPoints + }, n.is_touch_device = function() {}, n.getInfo = function() { + var t = "DEVICE OUTPUT\n\n"; + return t += "---\n", t += "Browser Info :: \n", t += "Arora : " + this.arora + "\n", t += "Chrome : " + this.chrome + "\n", t += "Epiphany : " + this.epiphany + "\n", t += "Firefox : " + this.firefox + "\n", t += "Mobile Safari : " + this.mobileSafari + "\n", t += "IE : " + this.ie, this.ie ? t += " (Version " + this.ieVersion + ")\n" : t += "\n", t += "Midori : " + this.midori + "\n", t += "Opera : " + this.opera + "\n", t += "Safari : " + this.safari + "\n", t += "Web App : " + this.webApp + "\n", t += "CocoonJS : " + this.cocoonJS + "\n", t += "Android : " + this.android + "\n", t += "---\n", t += "Operating System :: \n", t += "Chrome OS : " + this.chromeOS + "\n", t += "iOS : " + this.iOS + "\n", t += "Linux : " + this.linux + "\n", t += "Mac OS : " + this.macOS + "\n", t += "Windows : " + this.windows + "\n", t += "Desktop : " + this.desktop + "\n", t += "---\n", t += "Device Type : \n", t += "Pixel Ratio : " + this.pixelRatio + "\n", t += "iPhone : " + this.iPhone + "\n", t += "iPhone 4 : " + this.iPhone4 + "\n", t += "iPad : " + this.iPad + "\n", t += "---\n", t += "Features :: \n", t += "Blob : " + this.blob + "\n", t += "Canvas : " + this.canvas + "\n", t += "LocalStorage : " + this.localStorage + "\n", t += "File : " + this.file + "\n", t += "File System : " + this.fileSystem + "\n", t += "WebGL : " + this.webGL + "\n", t += "Workers : " + this.worker + "\n", t += "---\n", t += "Audio :: \n", t += "AudioData : " + this.audioData + "\n", t += "WebAudio : " + this.webAudio + "\n", t += "Supports .ogg : " + this.ogg + "\n", t += "Supports Opus : " + this.opus + "\n", t += "Supports .mp3 : " + this.mp3 + "\n", t += "Supports .wav : " + this.wav + "\n", t += "Supports .m4a : " + this.m4a + "\n", t += "Supports .webm : " + this.webm + }, t = Device, (e = [{ + key: "ie9", + get: function() { + return this.ie && 9 === this.ieVersion + } + }, { + key: "useSM2", + get: function() { + return this.ie || this.opera + } + }]) && _defineProperties(t.prototype, e), r && _defineProperties(t, r), Device + }(); + n.instance = new n, e.a = n + }, function(t, e, r) { + var n = r(39); + t.exports = function(t) { + return Object(n(t)) + } + }, function(i, f, t) { + (function(r) { + var n; + ! function() { + "use strict"; + var t = function() { + this.init() + }; + t.prototype = { + init: function() { + var t = this || b; + return t._counter = 1e3, t._html5AudioPool = [], t.html5PoolSize = 10, t._codecs = {}, t._howls = [], t._muted = !1, t._volume = 1, t._canPlayEvent = "canplaythrough", t._navigator = "undefined" != typeof window && window.navigator ? window.navigator : null, t.masterGain = null, t.noAudio = !1, t.usingWebAudio = !0, t.autoSuspend = !0, t.ctx = null, t.autoUnlock = !0, t._setup(), t + }, + volume: function(t) { + var e = this || b; + if (t = parseFloat(t), e.ctx || l(), void 0 !== t && 0 <= t && t <= 1) { + if (e._volume = t, e._muted) return e; + e.usingWebAudio && e.masterGain.gain.setValueAtTime(t, b.ctx.currentTime); + for (var r = 0; r < e._howls.length; r++) + if (!e._howls[r]._webAudio) + for (var n = e._howls[r]._getSoundIds(), i = 0; i < n.length; i++) { + var o = e._howls[r]._soundById(n[i]); + o && o._node && (o._node.volume = o._volume * t) + } + return e + } + return e._volume + }, + mute: function(t) { + var e = this || b; + e.ctx || l(), e._muted = t, e.usingWebAudio && e.masterGain.gain.setValueAtTime(t ? 0 : e._volume, b.ctx.currentTime); + for (var r = 0; r < e._howls.length; r++) + if (!e._howls[r]._webAudio) + for (var n = e._howls[r]._getSoundIds(), i = 0; i < n.length; i++) { + var o = e._howls[r]._soundById(n[i]); + o && o._node && (o._node.muted = !!t || o._muted) + } + return e + }, + unload: function() { + for (var t = this || b, e = t._howls.length - 1; 0 <= e; e--) t._howls[e].unload(); + return t.usingWebAudio && t.ctx && void 0 !== t.ctx.close && (t.ctx.close(), t.ctx = null, l()), t + }, + codecs: function(t) { + return (this || b)._codecs[t.replace(/^x-/, "")] + }, + _setup: function() { + var e = this || b; + if (e.state = e.ctx && e.ctx.state || "suspended", e._autoSuspend(), !e.usingWebAudio) + if ("undefined" != typeof Audio) try { + void 0 === (new Audio).oncanplaythrough && (e._canPlayEvent = "canplay") + } catch (t) { + e.noAudio = !0 + } else e.noAudio = !0; + try { + (new Audio).muted && (e.noAudio = !0) + } catch (t) {} + return e.noAudio || e._setupCodecs(), e + }, + _setupCodecs: function() { + var e = this || b, + t = null; + try { + t = "undefined" != typeof Audio ? new Audio : null + } catch (t) { + return e + } + if (!t || "function" != typeof t.canPlayType) return e; + var r = t.canPlayType("audio/mpeg;").replace(/^no$/, ""), + n = e._navigator && e._navigator.userAgent.match(/OPR\/([0-6].)/g), + i = n && parseInt(n[0].split("/")[1], 10) < 33; + return e._codecs = { + mp3: !(i || !r && !t.canPlayType("audio/mp3;").replace(/^no$/, "")), + mpeg: !!r, + opus: !!t.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ""), + ogg: !!t.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), + oga: !!t.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), + wav: !!t.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ""), + aac: !!t.canPlayType("audio/aac;").replace(/^no$/, ""), + caf: !!t.canPlayType("audio/x-caf;").replace(/^no$/, ""), + m4a: !!(t.canPlayType("audio/x-m4a;") || t.canPlayType("audio/m4a;") || t.canPlayType("audio/aac;")).replace(/^no$/, ""), + mp4: !!(t.canPlayType("audio/x-mp4;") || t.canPlayType("audio/mp4;") || t.canPlayType("audio/aac;")).replace(/^no$/, ""), + weba: !!t.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ""), + webm: !!t.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ""), + dolby: !!t.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, ""), + flac: !!(t.canPlayType("audio/x-flac;") || t.canPlayType("audio/flac;")).replace(/^no$/, "") + }, e + }, + _unlockAudio: function() { + var s = this || b, + t = /iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi|Chrome|Safari/i.test(s._navigator && s._navigator.userAgent); + if (!s._audioUnlocked && s.ctx && t) { + s._audioUnlocked = !1, s.autoUnlock = !1, s._mobileUnloaded || 44100 === s.ctx.sampleRate || (s._mobileUnloaded = !0, s.unload()), s._scratchBuffer = s.ctx.createBuffer(1, 1, 22050); + var u = function(t) { + for (var e = 0; e < s.html5PoolSize; e++) { + var r = new Audio; + r._unlocked = !0, s._releaseHtml5Audio(r) + } + for (e = 0; e < s._howls.length; e++) + if (!s._howls[e]._webAudio) + for (var n = s._howls[e]._getSoundIds(), i = 0; i < n.length; i++) { + var o = s._howls[e]._soundById(n[i]); + o && o._node && !o._node._unlocked && (o._node._unlocked = !0, o._node.load()) + } + s._autoResume(); + var a = s.ctx.createBufferSource(); + a.buffer = s._scratchBuffer, a.connect(s.ctx.destination), void 0 === a.start ? a.noteOn(0) : a.start(0), "function" == typeof s.ctx.resume && s.ctx.resume(), a.onended = function() { + a.disconnect(0), s._audioUnlocked = !0, document.removeEventListener("touchstart", u, !0), document.removeEventListener("touchend", u, !0), document.removeEventListener("click", u, !0); + for (var t = 0; t < s._howls.length; t++) s._howls[t]._emit("unlock") + } + }; + return document.addEventListener("touchstart", u, !0), document.addEventListener("touchend", u, !0), document.addEventListener("click", u, !0), s + } + }, + _obtainHtml5Audio: function() { + var t = this || b; + if (t._html5AudioPool.length) return t._html5AudioPool.pop(); + var e = (new Audio).play(); + return e && "undefined" != typeof Promise && (e instanceof Promise || "function" == typeof e.then) && e.catch(function() {}), new Audio + }, + _releaseHtml5Audio: function(t) { + var e = this || b; + return t._unlocked && e._html5AudioPool.push(t), e + }, + _autoSuspend: function() { + var t = this; + if (t.autoSuspend && t.ctx && void 0 !== t.ctx.suspend && b.usingWebAudio) { + for (var e = 0; e < t._howls.length; e++) + if (t._howls[e]._webAudio) + for (var r = 0; r < t._howls[e]._sounds.length; r++) + if (!t._howls[e]._sounds[r]._paused) return t; + return t._suspendTimer && clearTimeout(t._suspendTimer), t._suspendTimer = setTimeout(function() { + t.autoSuspend && (t._suspendTimer = null, t.state = "suspending", t.ctx.suspend().then(function() { + t.state = "suspended", t._resumeAfterSuspend && (delete t._resumeAfterSuspend, t._autoResume()) + })) + }, 3e4), t + } + }, + _autoResume: function() { + var e = this; + if (e.ctx && void 0 !== e.ctx.resume && b.usingWebAudio) return "running" === e.state && e._suspendTimer ? (clearTimeout(e._suspendTimer), e._suspendTimer = null) : "suspended" === e.state ? (e.ctx.resume().then(function() { + e.state = "running"; + for (var t = 0; t < e._howls.length; t++) e._howls[t]._emit("resume") + }), e._suspendTimer && (clearTimeout(e._suspendTimer), e._suspendTimer = null)) : "suspending" === e.state && (e._resumeAfterSuspend = !0), e + } + }; + var b = new t, + e = function(t) { + t.src && 0 !== t.src.length && this.init(t) + }; + e.prototype = { + init: function(t) { + var e = this; + return b.ctx || l(), e._autoplay = t.autoplay || !1, e._format = "string" != typeof t.format ? t.format : [t.format], e._html5 = t.html5 || !1, e._muted = t.mute || !1, e._loop = t.loop || !1, e._pool = t.pool || 5, e._preload = "boolean" != typeof t.preload || t.preload, e._rate = t.rate || 1, e._sprite = t.sprite || {}, e._src = "string" != typeof t.src ? t.src : [t.src], e._volume = void 0 !== t.volume ? t.volume : 1, e._xhrWithCredentials = t.xhrWithCredentials || !1, e._duration = 0, e._state = "unloaded", e._sounds = [], e._endTimers = {}, e._queue = [], e._playLock = !1, e._onend = t.onend ? [{ + fn: t.onend + }] : [], e._onfade = t.onfade ? [{ + fn: t.onfade + }] : [], e._onload = t.onload ? [{ + fn: t.onload + }] : [], e._onloaderror = t.onloaderror ? [{ + fn: t.onloaderror + }] : [], e._onplayerror = t.onplayerror ? [{ + fn: t.onplayerror + }] : [], e._onpause = t.onpause ? [{ + fn: t.onpause + }] : [], e._onplay = t.onplay ? [{ + fn: t.onplay + }] : [], e._onstop = t.onstop ? [{ + fn: t.onstop + }] : [], e._onmute = t.onmute ? [{ + fn: t.onmute + }] : [], e._onvolume = t.onvolume ? [{ + fn: t.onvolume + }] : [], e._onrate = t.onrate ? [{ + fn: t.onrate + }] : [], e._onseek = t.onseek ? [{ + fn: t.onseek + }] : [], e._onunlock = t.onunlock ? [{ + fn: t.onunlock + }] : [], e._onresume = [], e._webAudio = b.usingWebAudio && !e._html5, void 0 !== b.ctx && b.ctx && b.autoUnlock && b._unlockAudio(), b._howls.push(e), e._autoplay && e._queue.push({ + event: "play", + action: function() { + e.play() + } + }), e._preload && e.load(), e + }, + load: function() { + var t = this, + e = null; + if (b.noAudio) t._emit("loaderror", null, "No audio support."); + else { + "string" == typeof t._src && (t._src = [t._src]); + for (var r = 0; r < t._src.length; r++) { + var n, i; + if (t._format && t._format[r]) n = t._format[r]; + else { + if ("string" != typeof(i = t._src[r])) { + t._emit("loaderror", null, "Non-string found in selected audio sources - ignoring."); + continue + }(n = /^data:audio\/([^;,]+);/i.exec(i)) || (n = /\.([^.]+)$/.exec(i.split("?", 1)[0])), n && (n = n[1].toLowerCase()) + } + if (n && b.codecs(n)) { + e = t._src[r]; + break + } + } + if (e) return t._src = e, t._state = "loading", "https:" === window.location.protocol && "http:" === e.slice(0, 5) && (t._html5 = !0, t._webAudio = !1), new o(t), t._webAudio && s(t), t; + t._emit("loaderror", null, "No codec support for selected audio sources.") + } + }, + play: function(e, r) { + var n = this, + t = null; + if ("number" == typeof e) t = e, e = null; + else { + if ("string" == typeof e && "loaded" === n._state && !n._sprite[e]) return null; + if (void 0 === e && (e = "__default", !n._playLock)) { + for (var i = 0, o = 0; o < n._sounds.length; o++) n._sounds[o]._paused && !n._sounds[o]._ended && (i++, t = n._sounds[o]._id); + 1 === i ? e = null : t = null + } + } + var a = t ? n._soundById(t) : n._inactiveSound(); + if (!a) return null; + if (t && !e && (e = a._sprite || "__default"), "loaded" !== n._state) { + a._sprite = e, a._ended = !1; + var s = a._id; + return n._queue.push({ + event: "play", + action: function() { + n.play(s) + } + }), s + } + if (t && !a._paused) return r || n._loadQueue("play"), a._id; + n._webAudio && b._autoResume(); + var u = Math.max(0, 0 < a._seek ? a._seek : n._sprite[e][0] / 1e3), + h = Math.max(0, (n._sprite[e][0] + n._sprite[e][1]) / 1e3 - u), + c = 1e3 * h / Math.abs(a._rate), + l = n._sprite[e][0] / 1e3, + f = (n._sprite[e][0] + n._sprite[e][1]) / 1e3, + d = !(!a._loop && !n._sprite[e][2]); + a._sprite = e, a._ended = !1; + var p = function() { + a._paused = !1, a._seek = u, a._start = l, a._stop = f, a._loop = d + }; + if (!(f <= u)) { + var m = a._node; + if (n._webAudio) { + var v = function() { + n._playLock = !1, p(), n._refreshBuffer(a); + var t = a._muted || n._muted ? 0 : a._volume; + m.gain.setValueAtTime(t, b.ctx.currentTime), a._playStart = b.ctx.currentTime, void 0 === m.bufferSource.start ? a._loop ? m.bufferSource.noteGrainOn(0, u, 86400) : m.bufferSource.noteGrainOn(0, u, h) : a._loop ? m.bufferSource.start(0, u, 86400) : m.bufferSource.start(0, u, h), c !== 1 / 0 && (n._endTimers[a._id] = setTimeout(n._ended.bind(n, a), c)), r || setTimeout(function() { + n._emit("play", a._id), n._loadQueue() + }, 0) + }; + "running" === b.state ? v() : (n._playLock = !0, n.once("resume", v), n._clearTimer(a._id)) + } else { + var g = function() { + m.currentTime = u, m.muted = a._muted || n._muted || b._muted || m.muted, m.volume = a._volume * b.volume(), m.playbackRate = a._rate; + try { + var t = m.play(); + if (t && "undefined" != typeof Promise && (t instanceof Promise || "function" == typeof t.then) ? (n._playLock = !0, p(), t.then(function() { + n._playLock = !1, m._unlocked = !0, r || (n._emit("play", a._id), n._loadQueue()) + }).catch(function() { + n._playLock = !1, n._emit("playerror", a._id, "Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."), a._ended = !0, a._paused = !0 + })) : r || (n._playLock = !1, p(), n._emit("play", a._id), n._loadQueue()), m.playbackRate = a._rate, m.paused) return void n._emit("playerror", a._id, "Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."); + "__default" !== e || a._loop ? n._endTimers[a._id] = setTimeout(n._ended.bind(n, a), c) : (n._endTimers[a._id] = function() { + n._ended(a), m.removeEventListener("ended", n._endTimers[a._id], !1) + }, m.addEventListener("ended", n._endTimers[a._id], !1)) + } catch (t) { + n._emit("playerror", a._id, t) + } + }, + y = window && window.ejecta || !m.readyState && b._navigator.isCocoonJS; + if (3 <= m.readyState || y) g(); + else { + n._playLock = !0; + var _ = function() { + g(), m.removeEventListener(b._canPlayEvent, _, !1) + }; + m.addEventListener(b._canPlayEvent, _, !1), n._clearTimer(a._id) + } + } + return a._id + } + n._ended(a) + }, + pause: function(t) { + var e = this; + if ("loaded" !== e._state || e._playLock) return e._queue.push({ + event: "pause", + action: function() { + e.pause(t) + } + }), e; + for (var r = e._getSoundIds(t), n = 0; n < r.length; n++) { + e._clearTimer(r[n]); + var i = e._soundById(r[n]); + if (i && !i._paused && (i._seek = e.seek(r[n]), i._rateSeek = 0, i._paused = !0, e._stopFade(r[n]), i._node)) + if (e._webAudio) { + if (!i._node.bufferSource) continue; + void 0 === i._node.bufferSource.stop ? i._node.bufferSource.noteOff(0) : i._node.bufferSource.stop(0), e._cleanBuffer(i._node) + } else isNaN(i._node.duration) && i._node.duration !== 1 / 0 || i._node.pause(); + arguments[1] || e._emit("pause", i ? i._id : null) + } + return e + }, + stop: function(t, e) { + var r = this; + if ("loaded" !== r._state || r._playLock) return r._queue.push({ + event: "stop", + action: function() { + r.stop(t) + } + }), r; + for (var n = r._getSoundIds(t), i = 0; i < n.length; i++) { + r._clearTimer(n[i]); + var o = r._soundById(n[i]); + o && (o._seek = o._start || 0, o._rateSeek = 0, o._paused = !0, o._ended = !0, r._stopFade(n[i]), o._node && (r._webAudio ? o._node.bufferSource && (void 0 === o._node.bufferSource.stop ? o._node.bufferSource.noteOff(0) : o._node.bufferSource.stop(0), r._cleanBuffer(o._node)) : isNaN(o._node.duration) && o._node.duration !== 1 / 0 || (o._node.currentTime = o._start || 0, o._node.pause())), e || r._emit("stop", o._id)) + } + return r + }, + mute: function(t, e) { + var r = this; + if ("loaded" !== r._state || r._playLock) return r._queue.push({ + event: "mute", + action: function() { + r.mute(t, e) + } + }), r; + if (void 0 === e) { + if ("boolean" != typeof t) return r._muted; + r._muted = t + } + for (var n = r._getSoundIds(e), i = 0; i < n.length; i++) { + var o = r._soundById(n[i]); + o && (o._muted = t, o._interval && r._stopFade(o._id), r._webAudio && o._node ? o._node.gain.setValueAtTime(t ? 0 : o._volume, b.ctx.currentTime) : o._node && (o._node.muted = !!b._muted || t), r._emit("mute", o._id)) + } + return r + }, + volume: function() { + var t, e, r, n = this, + i = arguments; + if (0 === i.length) return n._volume; + if (1 === i.length || 2 === i.length && void 0 === i[1] ? 0 <= n._getSoundIds().indexOf(i[0]) ? e = parseInt(i[0], 10) : t = parseFloat(i[0]) : 2 <= i.length && (t = parseFloat(i[0]), e = parseInt(i[1], 10)), !(void 0 !== t && 0 <= t && t <= 1)) return (r = e ? n._soundById(e) : n._sounds[0]) ? r._volume : 0; + if ("loaded" !== n._state || n._playLock) return n._queue.push({ + event: "volume", + action: function() { + n.volume.apply(n, i) + } + }), n; + void 0 === e && (n._volume = t), e = n._getSoundIds(e); + for (var o = 0; o < e.length; o++)(r = n._soundById(e[o])) && (r._volume = t, i[2] || n._stopFade(e[o]), n._webAudio && r._node && !r._muted ? r._node.gain.setValueAtTime(t, b.ctx.currentTime) : r._node && !r._muted && (r._node.volume = t * b.volume()), n._emit("volume", r._id)); + return n + }, + fade: function(t, e, r, n) { + var i = this; + if ("loaded" !== i._state || i._playLock) return i._queue.push({ + event: "fade", + action: function() { + i.fade(t, e, r, n) + } + }), i; + t = parseFloat(t), e = parseFloat(e), r = parseFloat(r), i.volume(t, n); + for (var o = i._getSoundIds(n), a = 0; a < o.length; a++) { + var s = i._soundById(o[a]); + if (s) { + if (n || i._stopFade(o[a]), i._webAudio && !s._muted) { + var u = b.ctx.currentTime, + h = u + r / 1e3; + s._volume = t, s._node.gain.setValueAtTime(t, u), s._node.gain.linearRampToValueAtTime(e, h) + } + i._startFadeInterval(s, t, e, r, o[a], void 0 === n) + } + } + return i + }, + _startFadeInterval: function(e, r, n, i, t, o) { + var a = this, + s = r, + u = n - r, + h = Math.abs(u / .01), + c = Math.max(4, 0 < h ? i / h : i), + l = Date.now(); + e._fadeTo = n, e._interval = setInterval(function() { + var t = (Date.now() - l) / i; + l = Date.now(), s += u * t, s = Math.max(0, s), s = Math.min(1, s), s = Math.round(100 * s) / 100, a._webAudio ? e._volume = s : a.volume(s, e._id, !0), o && (a._volume = s), (n < r && s <= n || r < n && n <= s) && (clearInterval(e._interval), e._interval = null, e._fadeTo = null, a.volume(n, e._id), a._emit("fade", e._id)) + }, c) + }, + _stopFade: function(t) { + var e = this._soundById(t); + return e && e._interval && (this._webAudio && e._node.gain.cancelScheduledValues(b.ctx.currentTime), clearInterval(e._interval), e._interval = null, this.volume(e._fadeTo, t), e._fadeTo = null, this._emit("fade", t)), this + }, + loop: function() { + var t, e, r, n = arguments; + if (0 === n.length) return this._loop; + if (1 === n.length) { + if ("boolean" != typeof n[0]) return !!(r = this._soundById(parseInt(n[0], 10))) && r._loop; + t = n[0], this._loop = t + } else 2 === n.length && (t = n[0], e = parseInt(n[1], 10)); + for (var i = this._getSoundIds(e), o = 0; o < i.length; o++)(r = this._soundById(i[o])) && (r._loop = t, this._webAudio && r._node && r._node.bufferSource && (r._node.bufferSource.loop = t) && (r._node.bufferSource.loopStart = r._start || 0, r._node.bufferSource.loopEnd = r._stop)); + return this + }, + rate: function() { + var t, e, r, n = this, + i = arguments; + if (0 === i.length) e = n._sounds[0]._id; + else if (1 === i.length) { + 0 <= n._getSoundIds().indexOf(i[0]) ? e = parseInt(i[0], 10) : t = parseFloat(i[0]) + } else 2 === i.length && (t = parseFloat(i[0]), e = parseInt(i[1], 10)); + if ("number" != typeof t) return (r = n._soundById(e)) ? r._rate : n._rate; + if ("loaded" !== n._state || n._playLock) return n._queue.push({ + event: "rate", + action: function() { + n.rate.apply(n, i) + } + }), n; + void 0 === e && (n._rate = t), e = n._getSoundIds(e); + for (var o = 0; o < e.length; o++) + if (r = n._soundById(e[o])) { + n.playing(e[o]) && (r._rateSeek = n.seek(e[o]), r._playStart = n._webAudio ? b.ctx.currentTime : r._playStart), r._rate = t, n._webAudio && r._node && r._node.bufferSource ? r._node.bufferSource.playbackRate.setValueAtTime(t, b.ctx.currentTime) : r._node && (r._node.playbackRate = t); + var a = n.seek(e[o]), + s = 1e3 * ((n._sprite[r._sprite][0] + n._sprite[r._sprite][1]) / 1e3 - a) / Math.abs(r._rate); + !n._endTimers[e[o]] && r._paused || (n._clearTimer(e[o]), n._endTimers[e[o]] = setTimeout(n._ended.bind(n, r), s)), n._emit("rate", r._id) + } + return n + }, + seek: function() { + var t, e, r = this, + n = arguments; + if (0 === n.length) e = r._sounds[0]._id; + else if (1 === n.length) { + 0 <= r._getSoundIds().indexOf(n[0]) ? e = parseInt(n[0], 10) : r._sounds.length && (e = r._sounds[0]._id, t = parseFloat(n[0])) + } else 2 === n.length && (t = parseFloat(n[0]), e = parseInt(n[1], 10)); + if (void 0 === e) return r; + if ("loaded" !== r._state || r._playLock) return r._queue.push({ + event: "seek", + action: function() { + r.seek.apply(r, n) + } + }), r; + var i = r._soundById(e); + if (i) { + if (!("number" == typeof t && 0 <= t)) { + if (r._webAudio) { + var o = r.playing(e) ? b.ctx.currentTime - i._playStart : 0, + a = i._rateSeek ? i._rateSeek - i._seek : 0; + return i._seek + (a + o * Math.abs(i._rate)) + } + return i._node.currentTime + } + var s = r.playing(e); + s && r.pause(e, !0), i._seek = t, i._ended = !1, r._clearTimer(e), r._webAudio || !i._node || isNaN(i._node.duration) || (i._node.currentTime = t); + var u = function() { + r._emit("seek", e), s && r.play(e, !0) + }; + if (s && !r._webAudio) { + var h = function() { + r._playLock ? setTimeout(h, 0) : u() + }; + setTimeout(h, 0) + } else u() + } + return r + }, + playing: function(t) { + if ("number" == typeof t) { + var e = this._soundById(t); + return !!e && !e._paused + } + for (var r = 0; r < this._sounds.length; r++) + if (!this._sounds[r]._paused) return !0; + return !1 + }, + duration: function(t) { + var e = this._duration, + r = this._soundById(t); + return r && (e = this._sprite[r._sprite][1] / 1e3), e + }, + state: function() { + return this._state + }, + unload: function() { + for (var t = this, e = t._sounds, r = 0; r < e.length; r++) { + if (e[r]._paused || t.stop(e[r]._id), !t._webAudio) /MSIE |Trident\//.test(b._navigator && b._navigator.userAgent) || (e[r]._node.src = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"), e[r]._node.removeEventListener("error", e[r]._errorFn, !1), e[r]._node.removeEventListener(b._canPlayEvent, e[r]._loadFn, !1), b._releaseHtml5Audio(e[r]._node); + delete e[r]._node, t._clearTimer(e[r]._id) + } + var n = b._howls.indexOf(t); + 0 <= n && b._howls.splice(n, 1); + var i = !0; + for (r = 0; r < b._howls.length; r++) + if (b._howls[r]._src === t._src || 0 <= t._src.indexOf(b._howls[r]._src)) { + i = !1; + break + } + return a && i && delete a[t._src], b.noAudio = !1, t._state = "unloaded", t._sounds = [], t = null + }, + on: function(t, e, r, n) { + var i = this["_on" + t]; + return "function" == typeof e && i.push(n ? { + id: r, + fn: e, + once: n + } : { + id: r, + fn: e + }), this + }, + off: function(t, e, r) { + var n = this["_on" + t], + i = 0; + if ("number" == typeof e && (r = e, e = null), e || r) + for (i = 0; i < n.length; i++) { + var o = r === n[i].id; + if (e === n[i].fn && o || !e && o) { + n.splice(i, 1); + break + } + } else if (t) this["_on" + t] = []; + else { + var a = Object.keys(this); + for (i = 0; i < a.length; i++) 0 === a[i].indexOf("_on") && Array.isArray(this[a[i]]) && (this[a[i]] = []) + } + return this + }, + once: function(t, e, r) { + return this.on(t, e, r, 1), this + }, + _emit: function(t, e, r) { + for (var n = this["_on" + t], i = n.length - 1; 0 <= i; i--) n[i].id && n[i].id !== e && "load" !== t || (setTimeout(function(t) { + t.call(this, e, r) + }.bind(this, n[i].fn), 0), n[i].once && this.off(t, n[i].fn, n[i].id)); + return this._loadQueue(t), this + }, + _loadQueue: function(t) { + if (0 < this._queue.length) { + var e = this._queue[0]; + e.event === t && (this._queue.shift(), this._loadQueue()), t || e.action() + } + return this + }, + _ended: function(t) { + var e = this, + r = t._sprite; + if (!e._webAudio && t._node && !t._node.paused && !t._node.ended && t._node.currentTime < t._stop) return setTimeout(e._ended.bind(e, t), 100), e; + var n = !(!t._loop && !e._sprite[r][2]); + if (e._emit("end", t._id), !e._webAudio && n && e.stop(t._id, !0).play(t._id), e._webAudio && n) { + e._emit("play", t._id), t._seek = t._start || 0, t._rateSeek = 0, t._playStart = b.ctx.currentTime; + var i = 1e3 * (t._stop - t._start) / Math.abs(t._rate); + e._endTimers[t._id] = setTimeout(e._ended.bind(e, t), i) + } + return e._webAudio && !n && (t._paused = !0, t._ended = !0, t._seek = t._start || 0, t._rateSeek = 0, e._clearTimer(t._id), e._cleanBuffer(t._node), b._autoSuspend()), e._webAudio || n || e.stop(t._id, !0), e + }, + _clearTimer: function(t) { + if (this._endTimers[t]) { + if ("function" != typeof this._endTimers[t]) clearTimeout(this._endTimers[t]); + else { + var e = this._soundById(t); + e && e._node && e._node.removeEventListener("ended", this._endTimers[t], !1) + } + delete this._endTimers[t] + } + return this + }, + _soundById: function(t) { + for (var e = 0; e < this._sounds.length; e++) + if (t === this._sounds[e]._id) return this._sounds[e]; + return null + }, + _inactiveSound: function() { + this._drain(); + for (var t = 0; t < this._sounds.length; t++) + if (this._sounds[t]._ended) return this._sounds[t].reset(); + return new o(this) + }, + _drain: function() { + var t = this, + e = t._pool, + r = 0, + n = 0; + if (!(t._sounds.length < e)) { + for (n = 0; n < t._sounds.length; n++) t._sounds[n]._ended && r++; + for (n = t._sounds.length - 1; 0 <= n; n--) { + if (r <= e) return; + t._sounds[n]._ended && (t._webAudio && t._sounds[n]._node && t._sounds[n]._node.disconnect(0), t._sounds.splice(n, 1), r--) + } + } + }, + _getSoundIds: function(t) { + if (void 0 !== t) return [t]; + for (var e = [], r = 0; r < this._sounds.length; r++) e.push(this._sounds[r]._id); + return e + }, + _refreshBuffer: function(t) { + return t._node.bufferSource = b.ctx.createBufferSource(), t._node.bufferSource.buffer = a[this._src], t._panner ? t._node.bufferSource.connect(t._panner) : t._node.bufferSource.connect(t._node), t._node.bufferSource.loop = t._loop, t._loop && (t._node.bufferSource.loopStart = t._start || 0, t._node.bufferSource.loopEnd = t._stop || 0), t._node.bufferSource.playbackRate.setValueAtTime(t._rate, b.ctx.currentTime), this + }, + _cleanBuffer: function(t) { + var e = b._navigator && 0 <= b._navigator.vendor.indexOf("Apple"); + if (b._scratchBuffer && t.bufferSource && (t.bufferSource.onended = null, t.bufferSource.disconnect(0), e)) try { + t.bufferSource.buffer = b._scratchBuffer + } catch (t) {} + return t.bufferSource = null, this + } + }; + var o = function(t) { + this._parent = t, this.init() + }; + o.prototype = { + init: function() { + var t = this, + e = t._parent; + return t._muted = e._muted, t._loop = e._loop, t._volume = e._volume, t._rate = e._rate, t._seek = 0, t._paused = !0, t._ended = !0, t._sprite = "__default", t._id = ++b._counter, e._sounds.push(t), t.create(), t + }, + create: function() { + var t = this, + e = t._parent, + r = b._muted || t._muted || t._parent._muted ? 0 : t._volume; + return e._webAudio ? (t._node = void 0 === b.ctx.createGain ? b.ctx.createGainNode() : b.ctx.createGain(), t._node.gain.setValueAtTime(r, b.ctx.currentTime), t._node.paused = !0, t._node.connect(b.masterGain)) : (t._node = b._obtainHtml5Audio(), t._errorFn = t._errorListener.bind(t), t._node.addEventListener("error", t._errorFn, !1), t._loadFn = t._loadListener.bind(t), t._node.addEventListener(b._canPlayEvent, t._loadFn, !1), t._node.src = e._src, t._node.preload = "auto", t._node.volume = r * b.volume(), t._node.load()), t + }, + reset: function() { + var t = this, + e = t._parent; + return t._muted = e._muted, t._loop = e._loop, t._volume = e._volume, t._rate = e._rate, t._seek = 0, t._rateSeek = 0, t._paused = !0, t._ended = !0, t._sprite = "__default", t._id = ++b._counter, t + }, + _errorListener: function() { + this._parent._emit("loaderror", this._id, this._node.error ? this._node.error.code : 0), this._node.removeEventListener("error", this._errorFn, !1) + }, + _loadListener: function() { + var t = this._parent; + t._duration = Math.ceil(10 * this._node.duration) / 10, 0 === Object.keys(t._sprite).length && (t._sprite = { + __default: [0, 1e3 * t._duration] + }), "loaded" !== t._state && (t._state = "loaded", t._emit("load"), t._loadQueue()), this._node.removeEventListener(b._canPlayEvent, this._loadFn, !1) + } + }; + var a = {}, + s = function(e) { + var t = e._src; + if (a[t]) return e._duration = a[t].duration, void c(e); + if (/^data:[^;]+;base64,/.test(t)) { + for (var r = atob(t.split(",")[1]), n = new Uint8Array(r.length), i = 0; i < r.length; ++i) n[i] = r.charCodeAt(i); + h(n.buffer, e) + } else { + var o = new XMLHttpRequest; + o.open("GET", t, !0), o.withCredentials = e._xhrWithCredentials, o.responseType = "arraybuffer", o.onload = function() { + var t = (o.status + "")[0]; + "0" === t || "2" === t || "3" === t ? h(o.response, e) : e._emit("loaderror", null, "Failed loading audio file with status: " + o.status + ".") + }, o.onerror = function() { + e._webAudio && (e._html5 = !0, e._webAudio = !1, e._sounds = [], delete a[t], e.load()) + }, u(o) + } + }, + u = function(e) { + try { + e.send() + } catch (t) { + e.onerror() + } + }, + h = function(t, e) { + var r = function() { + e._emit("loaderror", null, "Decoding audio data failed.") + }, + n = function(t) { + t && 0 < e._sounds.length ? (a[e._src] = t, c(e, t)) : r() + }; + "undefined" != typeof Promise && 1 === b.ctx.decodeAudioData.length ? b.ctx.decodeAudioData(t).then(n).catch(r) : b.ctx.decodeAudioData(t, n, r) + }, + c = function(t, e) { + e && !t._duration && (t._duration = e.duration), 0 === Object.keys(t._sprite).length && (t._sprite = { + __default: [0, 1e3 * t._duration] + }), "loaded" !== t._state && (t._state = "loaded", t._emit("load"), t._loadQueue()) + }, + l = function() { + if (b.usingWebAudio) { + try { + "undefined" != typeof AudioContext ? b.ctx = new AudioContext : "undefined" != typeof webkitAudioContext ? b.ctx = new webkitAudioContext : b.usingWebAudio = !1 + } catch (t) { + b.usingWebAudio = !1 + } + b.ctx || (b.usingWebAudio = !1); + var t = /iP(hone|od|ad)/.test(b._navigator && b._navigator.platform), + e = b._navigator && b._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/), + r = e ? parseInt(e[1], 10) : null; + if (t && r && r < 9) { + var n = /safari/.test(b._navigator && b._navigator.userAgent.toLowerCase()); + (b._navigator && b._navigator.standalone && !n || b._navigator && !b._navigator.standalone && !n) && (b.usingWebAudio = !1) + } + b.usingWebAudio && (b.masterGain = void 0 === b.ctx.createGain ? b.ctx.createGainNode() : b.ctx.createGain(), b.masterGain.gain.setValueAtTime(b._muted ? 0 : 1, b.ctx.currentTime), b.masterGain.connect(b.ctx.destination)), b._setup() + } + }; + void 0 === (n = function() { + return { + Howler: b, + Howl: e + } + }.apply(f, [])) || (i.exports = n), f.Howler = b, f.Howl = e, "undefined" != typeof window ? (window.HowlerGlobal = t, window.Howler = b, window.Howl = e, window.Sound = o) : void 0 !== r && (r.HowlerGlobal = t, r.Howler = b, r.Howl = e, r.Sound = o) + }(), + function() { + "use strict"; + var e, r, n; + HowlerGlobal.prototype._pos = [0, 0, 0], HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0], HowlerGlobal.prototype.stereo = function(t) { + if (!this.ctx || !this.ctx.listener) return this; + for (var e = this._howls.length - 1; 0 <= e; e--) this._howls[e].stereo(t); + return this + }, HowlerGlobal.prototype.pos = function(t, e, r) { + var n = this; + return n.ctx && n.ctx.listener ? (e = "number" != typeof e ? n._pos[1] : e, r = "number" != typeof r ? n._pos[2] : r, "number" != typeof t ? n._pos : (n._pos = [t, e, r], void 0 !== n.ctx.listener.positionX ? (n.ctx.listener.positionX.setTargetAtTime(n._pos[0], Howler.ctx.currentTime, .1), n.ctx.listener.positionY.setTargetAtTime(n._pos[1], Howler.ctx.currentTime, .1), n.ctx.listener.positionZ.setTargetAtTime(n._pos[2], Howler.ctx.currentTime, .1)) : n.ctx.listener.setPosition(n._pos[0], n._pos[1], n._pos[2]), n)) : n + }, HowlerGlobal.prototype.orientation = function(t, e, r, n, i, o) { + var a = this; + if (!a.ctx || !a.ctx.listener) return a; + var s = a._orientation; + return e = "number" != typeof e ? s[1] : e, r = "number" != typeof r ? s[2] : r, n = "number" != typeof n ? s[3] : n, i = "number" != typeof i ? s[4] : i, o = "number" != typeof o ? s[5] : o, "number" != typeof t ? s : (a._orientation = [t, e, r, n, i, o], void 0 !== a.ctx.listener.forwardX ? (a.ctx.listener.forwardX.setTargetAtTime(t, Howler.ctx.currentTime, .1), a.ctx.listener.forwardY.setTargetAtTime(e, Howler.ctx.currentTime, .1), a.ctx.listener.forwardZ.setTargetAtTime(r, Howler.ctx.currentTime, .1), a.ctx.listener.upX.setTargetAtTime(t, Howler.ctx.currentTime, .1), a.ctx.listener.upY.setTargetAtTime(e, Howler.ctx.currentTime, .1), a.ctx.listener.upZ.setTargetAtTime(r, Howler.ctx.currentTime, .1)) : a.ctx.listener.setOrientation(t, e, r, n, i, o), a) + }, Howl.prototype.init = (e = Howl.prototype.init, function(t) { + return this._orientation = t.orientation || [1, 0, 0], this._stereo = t.stereo || null, this._pos = t.pos || null, this._pannerAttr = { + coneInnerAngle: void 0 !== t.coneInnerAngle ? t.coneInnerAngle : 360, + coneOuterAngle: void 0 !== t.coneOuterAngle ? t.coneOuterAngle : 360, + coneOuterGain: void 0 !== t.coneOuterGain ? t.coneOuterGain : 0, + distanceModel: void 0 !== t.distanceModel ? t.distanceModel : "inverse", + maxDistance: void 0 !== t.maxDistance ? t.maxDistance : 1e4, + panningModel: void 0 !== t.panningModel ? t.panningModel : "HRTF", + refDistance: void 0 !== t.refDistance ? t.refDistance : 1, + rolloffFactor: void 0 !== t.rolloffFactor ? t.rolloffFactor : 1 + }, this._onstereo = t.onstereo ? [{ + fn: t.onstereo + }] : [], this._onpos = t.onpos ? [{ + fn: t.onpos + }] : [], this._onorientation = t.onorientation ? [{ + fn: t.onorientation + }] : [], e.call(this, t) + }), Howl.prototype.stereo = function(t, e) { + var r = this; + if (!r._webAudio) return r; + if ("loaded" !== r._state) return r._queue.push({ + event: "stereo", + action: function() { + r.stereo(t, e) + } + }), r; + var n = void 0 === Howler.ctx.createStereoPanner ? "spatial" : "stereo"; + if (void 0 === e) { + if ("number" != typeof t) return r._stereo; + r._stereo = t, r._pos = [t, 0, 0] + } + for (var i = r._getSoundIds(e), o = 0; o < i.length; o++) { + var a = r._soundById(i[o]); + if (a) { + if ("number" != typeof t) return a._stereo; + a._stereo = t, a._pos = [t, 0, 0], a._node && (a._pannerAttr.panningModel = "equalpower", a._panner && a._panner.pan || h(a, n), "spatial" === n ? void 0 !== a._panner.positionX ? (a._panner.positionX.setValueAtTime(t, Howler.ctx.currentTime), a._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime), a._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime)) : a._panner.setPosition(t, 0, 0) : a._panner.pan.setValueAtTime(t, Howler.ctx.currentTime)), r._emit("stereo", a._id) + } + } + return r + }, Howl.prototype.pos = function(t, e, r, n) { + var i = this; + if (!i._webAudio) return i; + if ("loaded" !== i._state) return i._queue.push({ + event: "pos", + action: function() { + i.pos(t, e, r, n) + } + }), i; + if (e = "number" != typeof e ? 0 : e, r = "number" != typeof r ? -.5 : r, void 0 === n) { + if ("number" != typeof t) return i._pos; + i._pos = [t, e, r] + } + for (var o = i._getSoundIds(n), a = 0; a < o.length; a++) { + var s = i._soundById(o[a]); + if (s) { + if ("number" != typeof t) return s._pos; + s._pos = [t, e, r], s._node && (s._panner && !s._panner.pan || h(s, "spatial"), void 0 !== s._panner.positionX ? (s._panner.positionX.setValueAtTime(t, Howler.ctx.currentTime), s._panner.positionY.setValueAtTime(e, Howler.ctx.currentTime), s._panner.positionZ.setValueAtTime(r, Howler.ctx.currentTime)) : s._panner.setPosition(t, e, r)), i._emit("pos", s._id) + } + } + return i + }, Howl.prototype.orientation = function(t, e, r, n) { + var i = this; + if (!i._webAudio) return i; + if ("loaded" !== i._state) return i._queue.push({ + event: "orientation", + action: function() { + i.orientation(t, e, r, n) + } + }), i; + if (e = "number" != typeof e ? i._orientation[1] : e, r = "number" != typeof r ? i._orientation[2] : r, void 0 === n) { + if ("number" != typeof t) return i._orientation; + i._orientation = [t, e, r] + } + for (var o = i._getSoundIds(n), a = 0; a < o.length; a++) { + var s = i._soundById(o[a]); + if (s) { + if ("number" != typeof t) return s._orientation; + s._orientation = [t, e, r], s._node && (s._panner || (s._pos || (s._pos = i._pos || [0, 0, -.5]), h(s, "spatial")), void 0 !== s._panner.orientationX ? (s._panner.orientationX.setValueAtTime(t, Howler.ctx.currentTime), s._panner.orientationY.setValueAtTime(e, Howler.ctx.currentTime), s._panner.orientationZ.setValueAtTime(r, Howler.ctx.currentTime)) : s._panner.setOrientation(t, e, r)), i._emit("orientation", s._id) + } + } + return i + }, Howl.prototype.pannerAttr = function() { + var t, e, r, n = this, + i = arguments; + if (!n._webAudio) return n; + if (0 === i.length) return n._pannerAttr; + if (1 === i.length) { + if ("object" != typeof i[0]) return (r = n._soundById(parseInt(i[0], 10))) ? r._pannerAttr : n._pannerAttr; + t = i[0], void 0 === e && (t.pannerAttr || (t.pannerAttr = { + coneInnerAngle: t.coneInnerAngle, + coneOuterAngle: t.coneOuterAngle, + coneOuterGain: t.coneOuterGain, + distanceModel: t.distanceModel, + maxDistance: t.maxDistance, + refDistance: t.refDistance, + rolloffFactor: t.rolloffFactor, + panningModel: t.panningModel + }), n._pannerAttr = { + coneInnerAngle: void 0 !== t.pannerAttr.coneInnerAngle ? t.pannerAttr.coneInnerAngle : n._coneInnerAngle, + coneOuterAngle: void 0 !== t.pannerAttr.coneOuterAngle ? t.pannerAttr.coneOuterAngle : n._coneOuterAngle, + coneOuterGain: void 0 !== t.pannerAttr.coneOuterGain ? t.pannerAttr.coneOuterGain : n._coneOuterGain, + distanceModel: void 0 !== t.pannerAttr.distanceModel ? t.pannerAttr.distanceModel : n._distanceModel, + maxDistance: void 0 !== t.pannerAttr.maxDistance ? t.pannerAttr.maxDistance : n._maxDistance, + refDistance: void 0 !== t.pannerAttr.refDistance ? t.pannerAttr.refDistance : n._refDistance, + rolloffFactor: void 0 !== t.pannerAttr.rolloffFactor ? t.pannerAttr.rolloffFactor : n._rolloffFactor, + panningModel: void 0 !== t.pannerAttr.panningModel ? t.pannerAttr.panningModel : n._panningModel + }) + } else 2 === i.length && (t = i[0], e = parseInt(i[1], 10)); + for (var o = n._getSoundIds(e), a = 0; a < o.length; a++) + if (r = n._soundById(o[a])) { + var s = r._pannerAttr; + s = { + coneInnerAngle: void 0 !== t.coneInnerAngle ? t.coneInnerAngle : s.coneInnerAngle, + coneOuterAngle: void 0 !== t.coneOuterAngle ? t.coneOuterAngle : s.coneOuterAngle, + coneOuterGain: void 0 !== t.coneOuterGain ? t.coneOuterGain : s.coneOuterGain, + distanceModel: void 0 !== t.distanceModel ? t.distanceModel : s.distanceModel, + maxDistance: void 0 !== t.maxDistance ? t.maxDistance : s.maxDistance, + refDistance: void 0 !== t.refDistance ? t.refDistance : s.refDistance, + rolloffFactor: void 0 !== t.rolloffFactor ? t.rolloffFactor : s.rolloffFactor, + panningModel: void 0 !== t.panningModel ? t.panningModel : s.panningModel + }; + var u = r._panner; + u ? (u.coneInnerAngle = s.coneInnerAngle, u.coneOuterAngle = s.coneOuterAngle, u.coneOuterGain = s.coneOuterGain, u.distanceModel = s.distanceModel, u.maxDistance = s.maxDistance, u.refDistance = s.refDistance, u.rolloffFactor = s.rolloffFactor, u.panningModel = s.panningModel) : (r._pos || (r._pos = n._pos || [0, 0, -.5]), h(r, "spatial")) + } + return n + }, Sound.prototype.init = (r = Sound.prototype.init, function() { + var t = this, + e = t._parent; + t._orientation = e._orientation, t._stereo = e._stereo, t._pos = e._pos, t._pannerAttr = e._pannerAttr, r.call(this), t._stereo ? e.stereo(t._stereo) : t._pos && e.pos(t._pos[0], t._pos[1], t._pos[2], t._id) + }), Sound.prototype.reset = (n = Sound.prototype.reset, function() { + var t = this, + e = t._parent; + return t._orientation = e._orientation, t._stereo = e._stereo, t._pos = e._pos, t._pannerAttr = e._pannerAttr, t._stereo ? e.stereo(t._stereo) : t._pos ? e.pos(t._pos[0], t._pos[1], t._pos[2], t._id) : t._panner && (t._panner.disconnect(0), t._panner = void 0, e._refreshBuffer(t)), n.call(this) + }); + var h = function(t, e) { + "spatial" === (e = e || "spatial") ? (t._panner = Howler.ctx.createPanner(), t._panner.coneInnerAngle = t._pannerAttr.coneInnerAngle, t._panner.coneOuterAngle = t._pannerAttr.coneOuterAngle, t._panner.coneOuterGain = t._pannerAttr.coneOuterGain, t._panner.distanceModel = t._pannerAttr.distanceModel, t._panner.maxDistance = t._pannerAttr.maxDistance, t._panner.refDistance = t._pannerAttr.refDistance, t._panner.rolloffFactor = t._pannerAttr.rolloffFactor, t._panner.panningModel = t._pannerAttr.panningModel, void 0 !== t._panner.positionX ? (t._panner.positionX.setValueAtTime(t._pos[0], Howler.ctx.currentTime), t._panner.positionY.setValueAtTime(t._pos[1], Howler.ctx.currentTime), t._panner.positionZ.setValueAtTime(t._pos[2], Howler.ctx.currentTime)) : t._panner.setPosition(t._pos[0], t._pos[1], t._pos[2]), void 0 !== t._panner.orientationX ? (t._panner.orientationX.setValueAtTime(t._orientation[0], Howler.ctx.currentTime), t._panner.orientationY.setValueAtTime(t._orientation[1], Howler.ctx.currentTime), t._panner.orientationZ.setValueAtTime(t._orientation[2], Howler.ctx.currentTime)) : t._panner.setOrientation(t._orientation[0], t._orientation[1], t._orientation[2])) : (t._panner = Howler.ctx.createStereoPanner(), t._panner.pan.setValueAtTime(t._stereo, Howler.ctx.currentTime)), t._panner.connect(t._node), t._paused || t._parent.pause(t._id, !0).play(t._id, !0) + } + }() + }).call(this, t(42)) + }, function(t, e) { + t.exports = function(t) { + if ("function" != typeof t) throw TypeError(t + " is not a function!"); + return t + } + }, function(t, e, r) { + var n = r(20), + i = r(51); + t.exports = r(19) ? function(t, e, r) { + return n.f(t, e, i(1, r)) + } : function(t, e, r) { + return t[e] = r, t + } + }, function(t, e, r) { + var o = r(9), + a = r(25), + s = r(28), + u = r(52)("src"), + n = "toString", + i = Function[n], + h = ("" + i).split(n); + r(32).inspectSource = function(t) { + return i.call(t) + }, (t.exports = function(t, e, r, n) { + var i = "function" == typeof r; + i && (s(r, "name") || a(r, "name", e)), t[e] !== r && (i && (s(r, u) || a(r, u, t[e] ? "" + t[e] : h.join(String(e)))), t === o ? t[e] = r : n ? t[e] ? t[e] = r : a(t, e, r) : (delete t[e], a(t, e, r))) + })(Function.prototype, n, function() { + return "function" == typeof this && this[u] || i.call(this) + }) + }, function(t, e, r) { + var n = r(1), + i = r(10), + a = r(39), + s = /"/g, + o = function(t, e, r, n) { + var i = String(a(t)), + o = "<" + e; + return "" !== r && (o += " " + r + '="' + String(n).replace(s, """) + '"'), o + ">" + i + "" + }; + t.exports = function(e, t) { + var r = {}; + r[e] = t(o), n(n.P + n.F * i(function() { + var t = "" [e]('"'); + return t !== t.toLowerCase() || 3 < t.split('"').length + }), "String", r) + } + }, function(t, e) { + var r = {}.hasOwnProperty; + t.exports = function(t, e) { + return r.call(t, e) + } + }, function(t, e, r) { + var n = r(69), + i = r(39); + t.exports = function(t) { + return n(i(t)) + } + }, function(t, e, r) { + var n = r(70), + i = r(51), + o = r(29), + a = r(38), + s = r(28), + u = r(134), + h = Object.getOwnPropertyDescriptor; + e.f = r(19) ? h : function(t, e) { + if (t = o(t), e = a(e, !0), u) try { + return h(t, e) + } catch (t) {} + if (s(t, e)) return i(!n.f.call(t, e), t[e]) + } + }, function(t, e, r) { + var n = r(28), + i = r(22), + o = r(100)("IE_PROTO"), + a = Object.prototype; + t.exports = Object.getPrototypeOf || function(t) { + return t = i(t), n(t, o) ? t[o] : "function" == typeof t.constructor && t instanceof t.constructor ? t.constructor.prototype : t instanceof Object ? a : null + } + }, function(t, e) { + var r = t.exports = { + version: "2.6.3" + }; + "number" == typeof __e && (__e = r) + }, function(t, e, r) { + var o = r(24); + t.exports = function(n, i, t) { + if (o(n), void 0 === i) return n; + switch (t) { + case 1: + return function(t) { + return n.call(i, t) + }; + case 2: + return function(t, e) { + return n.call(i, t, e) + }; + case 3: + return function(t, e, r) { + return n.call(i, t, e, r) + } + } + return function() { + return n.apply(i, arguments) + } + } + }, function(t, e) { + var r = {}.toString; + t.exports = function(t) { + return r.call(t).slice(8, -1) + } + }, function(t, e) { + var r = Math.ceil, + n = Math.floor; + t.exports = function(t) { + return isNaN(t = +t) ? 0 : (0 < t ? n : r)(t) + } + }, function(t, e, r) { + "use strict"; + var n = r(10); + t.exports = function(t, e) { + return !!t && n(function() { + e ? t.call(null, function() {}, 1) : t.call(null) + }) + } + }, function(t, e, r) { + "use strict"; + e.__esModule = !0; + var n = function() { + function defineProperties(t, e) { + for (var r = 0; r < e.length; r++) { + var n = e[r]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n) + } + } + return function(t, e, r) { + return e && defineProperties(t.prototype, e), r && defineProperties(t, r), t + } + }(), + u = function(t) { + { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var r in t) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e.default = t, e + } + }(r(0)), + h = r(4); + + function filterEmptyLine(t) { + return "" !== t + } + var c = { + uniformData: { + id: -1 + } + }, + l = 0, + i = {}, + f = {}, + o = function(s) { + function BaseMaterial() { + var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}, + e = arguments[1], + r = arguments[2], + n = arguments[3], + i = 4 < arguments.length && void 0 !== arguments[4] ? arguments[4] : "base"; + ! function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, BaseMaterial); + var o = { + uShininess: t.shininess || 32, + uOpacity: void 0 === t.opacity ? 1 : t.opacity, + uSpecular: new Float32Array([0, 0, 0]), + uColor: new Float32Array([1, 1, 1]), + normal: h.mat3.create() + }, + a = function(t, e) { + if (!t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return !e || "object" != typeof e && "function" != typeof e ? t : e + }(this, s.call(this, c, o)); + return a.type = i, a.name = t.name || a.type + "-shader", a.options = {}, a.needsUpdate = !0, a.id = l++, a.skinning = !!t.skinning, a.skinning && (o.bones = new Float32Array(960)), a.specularColor = t.specular || 0, a.color = t.color || 16777215, a.instancing = !!t.instancing, a.emissive = !1, a.instancing && (a.name += "-instanced"), t.uniforms && Object.assign(o, t.uniforms), t.map && (o.uMap = t.map, o.uMapFrame = new u.Rectangle(0, 0, 1, 1)), t.enviromentMap && (o.uEnviromentMap = t.enviromentMap), t.shininessMap && (o.uShininessMap = t.shininessMap), t.occlusionMap && (o.uOcclusionMap = t.occlusionMap), a.emissive = !!(t.emissive || t.emissiveColor || t.emissiveMap), a.emissive && (void 0 !== t.emissive && (o.uEmissive = t.emissive), t.emissiveColor && (o.uEmissiveColor = new Float32Array([1, 1, 1]), a.emissiveColor = t.emissiveColor), t.emissiveMap && (o.uEmissiveMap = t.emissiveMap)), a.vertexSource = e, a.fragmentSource = r, a.fragmentInjection = t.fragment || {}, a.vertexInjection = t.vertex || {}, a.sig = a.getSignature(t), a.program = c, a.rebuildId = 0, a.defines = n || [], a + } + return function(t, e) { + if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function, not " + typeof e); + t.prototype = Object.create(e && e.prototype, { + constructor: { + value: t, + enumerable: !1, + writable: !0, + configurable: !0 + } + }), e && (Object.setPrototypeOf ? Object.setPrototypeOf(t, e) : t.__proto__ = e) + }(BaseMaterial, s), BaseMaterial.prototype.injectLights = function(t, e) { + var r = f[e.sig]; + if (!r) { + var n = [], + i = []; + e.lights.forEach(function(t) { + var e = t.light._lightId; + 0 === t.light.type ? (n.push("uniform vec3 uLightColor" + e + ";"), n.push("uniform vec3 uLightDirection" + e + ";"), i.push("\n\n attenuation = 1.;\n\n attenuation *= diffuseLighting(worldNormal,uLightDirection" + e + ");\n\n lighting += attenuation * uLightColor" + e + ";\n \n attenuation *= specularLighting(worldNormal, eyeToSurfaceDir,uLightDirection" + e + ");\n\n specular += attenuation * uSpecular * uLightColor" + e + ";\n \n ")) : 1 === t.light.type ? (n.push("uniform vec3 uLightColor" + e + ";"), n.push("uniform vec3 uLightDirection" + e + ";"), n.push("uniform vec3 uLightPosition" + e + ";"), n.push("uniform float uLightDistance" + e + ";"), i.push("\n \n lightDirection = vPosition - uLightPosition" + e + ";\n \n lightDirectionNormal = normalize(lightDirection);\n \n distancFromLight = length(lightDirection);\n \n attenuation = pow( saturate(-distancFromLight / uLightDistance" + e + " + 1.0) , 1. );\n \n if(attenuation > 0.0001)\n {\n attenuation *= diffuseLighting(worldNormal,lightDirectionNormal);\n\n lighting += attenuation * uLightColor" + e + ";\n \n attenuation *= specularLighting(worldNormal,eyeToSurfaceDir,lightDirectionNormal);\n\n specular += attenuation * uSpecular * uLightColor" + e + ";\n \n }")) : 2 === t.light.type && (n.push("uniform vec3 uLightColor" + e + ";"), n.push("uniform vec3 uLightDirection" + e + ";"), n.push("uniform vec3 uLightPosition" + e + ";"), n.push("uniform float uLightDistance" + e + ";"), i.push("\n \n lightDirection = uLightPosition" + e + " - vPosition;\n float dist = length(lightDirection);\n \n L = normalize(lightDirection);\n\n float attenuation = pow( saturate(-dist / uLightDistance" + e + " + 1.0) , 1. );\n \n float dotFromDirection = dot(L, -uLightDirection" + e + ");\n \n float limit = cos(30. * (3.14/180.) );\n\n if(dotFromDirection >= 0.000001)\n {\n if(attenuation > 0.001)\n {\n lighting += diffuseLighting(N,L,uLightColor" + e + ") * attenuation;\n lighting += specularLighting(N,L,V) * attenuation;\n lighting *= reflection;\n \n }\n }")) + }), f[e.sig] = r = { + uniforms: n.join("\n"), + code: i.join("\n") + } + } + var o = t.fragment, + a = t.vertex; + o = (o = o.replace(/#HOOK_LIGHT_UNIFROMS/g, r.uniforms)).replace(/#HOOK_LIGHT/g, r.code), a = (a = a.replace(/#HOOK_LIGHT_UNIFROMS/g, r.uniforms)).replace(/#HOOK_LIGHT/g, r.code), t.fragment = o, t.vertex = a + }, BaseMaterial.prototype.injectDefines = function(t, e) { + var r = this.uniforms, + n = [r.uColor ? "#define COLOR" : "", this.skinning ? "#define SKINNING" : "", r.uMap ? "#define MAP" : "", r.uOcclusionMap ? "#define OCCLUSION_MAP" : "", r.uShininess ? "#define SHININESS_FLOAT" : "", r.uShininessMap ? "#define SHININESS_MAP" : "", this.emissive ? "#define EMISSIVE" : "", void 0 !== r.uEmissive ? "#define EMISSIVE_FLOAT" : "", r.uEmissiveColor ? "#define EMISSIVE_COLOR" : "", r.uEmissiveMap ? "#define EMISSIVE_MAP" : "", r.uEnviromentMap ? "#define ENV_REFLECT" : "", this.instancing ? "#define INSTANCING" : "", e ? "#define FOG" : ""].concat(this.defines).filter(filterEmptyLine).join("\n"); + t.vertex = n + "\n" + t.vertex, t.fragment = n + "\n" + t.fragment + }, BaseMaterial.prototype.injectHooks = function(t) { + var e = this.vertexInjection, + r = t.vertex; + r = (r = (r = r.replace(/#HOOK_VERTEX_START/g, e.start || "")).replace(/#HOOK_VERTEX_MAIN/g, e.main || "")).replace(/#HOOK_VERTEX_END/g, e.end || ""); + var n = this.fragmentInjection, + i = t.fragment; + i = (i = (i = i.replace(/#HOOK_FRAGMENT_START/g, n.start || "")).replace(/#HOOK_FRAGMENT_MAIN/g, n.main || "")).replace(/#HOOK_FRAGMENT_END/g, n.end || ""), t.fragment = i, t.vertex = r + }, BaseMaterial.prototype.build = function(t, e) { + if (i[this.sig + t.sig]) this.program = i[this.sig + t.sig]; + else { + var r = { + vertex: this.vertexSource, + fragment: this.fragmentSource + }; + this.injectDefines(r, e), this.injectHooks(r), this.injectLights(r, t), this.program = u.Program.from(r.vertex, r.fragment, this.name), i[this.sig + t.sig] = this.program + } + }, BaseMaterial.prototype.getSignature = function(t) { + var e = [this.type]; + for (var r in t) e.push(r); + return e.sort(), t.vertex && e.push(t.vertex.start || "", t.vertex.main || "", t.vertex.end || ""), t.fragment && e.push(t.fragment.start || "", t.fragment.main || "", t.fragment.end || ""), e.join("-") + }, n(BaseMaterial, [{ + key: "opacity", + set: function(t) { + this.uniforms.uOpacity = t + }, + get: function() { + return this.uniforms.uOpacity + } + }, { + key: "map", + set: function(t) { + this.uniforms.uMap = t + }, + get: function() { + return this.uniforms.uMap + } + }, { + key: "occlusionMap", + set: function(t) { + this.uniforms.uOcclusionMap = t + }, + get: function() { + return this.uniforms.uOcclusionMap + } + }, { + key: "shininessMap", + set: function(t) { + this.uniforms.uShininessMap = t + }, + get: function() { + return this.uniforms.uShininessMap + } + }, { + key: "shininess", + set: function(t) { + this.uniforms.uShininess = t + }, + get: function() { + return this.uniforms.uShininess + } + }, { + key: "emissive", + set: function(t) { + this.uniforms.uEmissive = t + }, + get: function() { + return this.uniforms.uEmissive + } + }, { + key: "emissiveMap", + set: function(t) { + this.uniforms.uEmissiveMap = t + }, + get: function() { + return this.uniforms.uEmissiveMap + } + }, { + key: "enviromentMap", + set: function(t) { + this.uniforms.uEnviromentMap = t + }, + get: function() { + return this.uniforms.uEnviromentMap + } + }, { + key: "emissiveColor", + set: function(t) { + this._hexEmissiveColor = t, u.utils.hex2rgb(t, this.uniforms.uEmissiveColor) + }, + get: function() { + return this._hexEmissiveColor + } + }, { + key: "color", + set: function(t) { + this._hexColor = t, u.utils.hex2rgb(t, this.uniforms.uColor) + }, + get: function() { + return this._hexColor + } + }, { + key: "specularColor", + set: function(t) { + this._hexSpecularColor = t, u.utils.hex2rgb(t, this.uniforms.uSpecular) + }, + get: function() { + return this._hexSpecularColor + } + }]), BaseMaterial + }(u.Shader); + e.default = o + }, function(t, e, r) { + var i = r(11); + t.exports = function(t, e) { + if (!i(t)) return t; + var r, n; + if (e && "function" == typeof(r = t.toString) && !i(n = r.call(t))) return n; + if ("function" == typeof(r = t.valueOf) && !i(n = r.call(t))) return n; + if (!e && "function" == typeof(r = t.toString) && !i(n = r.call(t))) return n; + throw TypeError("Can't convert object to primitive value") + } + }, function(t, e) { + t.exports = function(t) { + if (null == t) throw TypeError("Can't call method on " + t); + return t + } + }, function(t, e, r) { + var i = r(1), + o = r(32), + a = r(10); + t.exports = function(t, e) { + var r = (o.Object || {})[t] || Object[t], + n = {}; + n[t] = e(r), i(i.S + i.F * a(function() { + r(1) + }), "Object", n) + } + }, function(t, e, r) { + var _ = r(33), + b = r(69), + x = r(22), + w = r(17), + n = r(116); + t.exports = function(l, t) { + var f = 1 == l, + d = 2 == l, + p = 3 == l, + m = 4 == l, + v = 6 == l, + g = 5 == l || v, + y = t || n; + return function(t, e, r) { + for (var n, i, o = x(t), a = b(o), s = _(e, r, 3), u = w(a.length), h = 0, c = f ? y(t, u) : d ? y(t, 0) : void 0; h < u; h++) + if ((g || h in a) && (i = s(n = a[h], h, o), l)) + if (f) c[h] = i; + else if (i) switch (l) { + case 3: + return !0; + case 5: + return n; + case 6: + return h; + case 2: + c.push(n) + } else if (m) return !1; + return v ? -1 : p || m ? m : c + } + } + }, function(t, e) { + var r; + r = function() { + return this + }(); + try { + r = r || new Function("return this")() + } catch (t) { + "object" == typeof window && (r = window) + } + t.exports = r + }, function(t, e, r) { + "use strict"; + if (r(19)) { + var g = r(49), + y = r(9), + _ = r(10), + b = r(1), + x = r(85), + n = r(124), + f = r(33), + w = r(58), + i = r(51), + T = r(25), + o = r(60), + a = r(35), + S = r(17), + M = r(162), + s = r(54), + u = r(38), + h = r(28), + E = r(62), + A = r(11), + d = r(22), + p = r(113), + P = r(55), + I = r(31), + O = r(56).f, + m = r(115), + c = r(52), + l = r(15), + v = r(41), + C = r(75), + R = r(72), + D = r(118), + F = r(64), + k = r(80), + L = r(57), + N = r(117), + B = r(151), + U = r(20), + G = r(30), + j = U.f, + z = G.f, + X = y.RangeError, + V = y.TypeError, + q = y.Uint8Array, + H = "ArrayBuffer", + W = "Shared" + H, + Y = "BYTES_PER_ELEMENT", + K = "prototype", + Z = Array[K], + Q = n.ArrayBuffer, + J = n.DataView, + $ = v(0), + tt = v(2), + et = v(3), + rt = v(4), + nt = v(5), + it = v(6), + ot = C(!0), + at = C(!1), + st = D.values, + ut = D.keys, + ht = D.entries, + ct = Z.lastIndexOf, + lt = Z.reduce, + ft = Z.reduceRight, + dt = Z.join, + pt = Z.sort, + mt = Z.slice, + vt = Z.toString, + gt = Z.toLocaleString, + yt = l("iterator"), + _t = l("toStringTag"), + bt = c("typed_constructor"), + xt = c("def_constructor"), + wt = x.CONSTR, + Tt = x.TYPED, + St = x.VIEW, + Mt = "Wrong length!", + Et = v(1, function(t, e) { + return Ct(R(t, t[xt]), e) + }), + At = _(function() { + return 1 === new q(new Uint16Array([1]).buffer)[0] + }), + Pt = !!q && !!q[K].set && _(function() { + new q(1).set({}) + }), + It = function(t, e) { + var r = a(t); + if (r < 0 || r % e) throw X("Wrong offset!"); + return r + }, + Ot = function(t) { + if (A(t) && Tt in t) return t; + throw V(t + " is not a typed array!") + }, + Ct = function(t, e) { + if (!(A(t) && bt in t)) throw V("It is not a typed array constructor!"); + return new t(e) + }, + Rt = function(t, e) { + return Dt(R(t, t[xt]), e) + }, + Dt = function(t, e) { + for (var r = 0, n = e.length, i = Ct(t, n); r < n;) i[r] = e[r++]; + return i + }, + Ft = function(t, e, r) { + j(t, e, { + get: function() { + return this._d[r] + } + }) + }, + kt = function(t) { + var e, r, n, i, o, a, s = d(t), + u = arguments.length, + h = 1 < u ? arguments[1] : void 0, + c = void 0 !== h, + l = m(s); + if (null != l && !p(l)) { + for (a = l.call(s), n = [], e = 0; !(o = a.next()).done; e++) n.push(o.value); + s = n + } + for (c && 2 < u && (h = f(h, arguments[2], 2)), e = 0, r = S(s.length), i = Ct(this, r); e < r; e++) i[e] = c ? h(s[e], e) : s[e]; + return i + }, + Lt = function() { + for (var t = 0, e = arguments.length, r = Ct(this, e); t < e;) r[t] = arguments[t++]; + return r + }, + Nt = !!q && _(function() { + gt.call(new q(1)) + }), + Bt = function() { + return gt.apply(Nt ? mt.call(Ot(this)) : Ot(this), arguments) + }, + Ut = { + copyWithin: function(t, e) { + return B.call(Ot(this), t, e, 2 < arguments.length ? arguments[2] : void 0) + }, + every: function(t) { + return rt(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + fill: function(t) { + return N.apply(Ot(this), arguments) + }, + filter: function(t) { + return Rt(this, tt(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0)) + }, + find: function(t) { + return nt(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + findIndex: function(t) { + return it(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + forEach: function(t) { + $(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + indexOf: function(t) { + return at(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + includes: function(t) { + return ot(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + join: function(t) { + return dt.apply(Ot(this), arguments) + }, + lastIndexOf: function(t) { + return ct.apply(Ot(this), arguments) + }, + map: function(t) { + return Et(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + reduce: function(t) { + return lt.apply(Ot(this), arguments) + }, + reduceRight: function(t) { + return ft.apply(Ot(this), arguments) + }, + reverse: function() { + for (var t, e = Ot(this).length, r = Math.floor(e / 2), n = 0; n < r;) t = this[n], this[n++] = this[--e], this[e] = t; + return this + }, + some: function(t) { + return et(Ot(this), t, 1 < arguments.length ? arguments[1] : void 0) + }, + sort: function(t) { + return pt.call(Ot(this), t) + }, + subarray: function(t, e) { + var r = Ot(this), + n = r.length, + i = s(t, n); + return new(R(r, r[xt]))(r.buffer, r.byteOffset + i * r.BYTES_PER_ELEMENT, S((void 0 === e ? n : s(e, n)) - i)) + } + }, + Gt = function(t, e) { + return Rt(this, mt.call(Ot(this), t, e)) + }, + jt = function(t) { + Ot(this); + var e = It(arguments[1], 1), + r = this.length, + n = d(t), + i = S(n.length), + o = 0; + if (r < i + e) throw X(Mt); + for (; o < i;) this[e + o] = n[o++] + }, + zt = { + entries: function() { + return ht.call(Ot(this)) + }, + keys: function() { + return ut.call(Ot(this)) + }, + values: function() { + return st.call(Ot(this)) + } + }, + Xt = function(t, e) { + return A(t) && t[Tt] && "symbol" != typeof e && e in t && String(+e) == String(e) + }, + Vt = function(t, e) { + return Xt(t, e = u(e, !0)) ? i(2, t[e]) : z(t, e) + }, + qt = function(t, e, r) { + return !(Xt(t, e = u(e, !0)) && A(r) && h(r, "value")) || h(r, "get") || h(r, "set") || r.configurable || h(r, "writable") && !r.writable || h(r, "enumerable") && !r.enumerable ? j(t, e, r) : (t[e] = r.value, t) + }; + wt || (G.f = Vt, U.f = qt), b(b.S + b.F * !wt, "Object", { + getOwnPropertyDescriptor: Vt, + defineProperty: qt + }), _(function() { + vt.call({}) + }) && (vt = gt = function() { + return dt.call(this) + }); + var Ht = o({}, Ut); + o(Ht, zt), T(Ht, yt, zt.values), o(Ht, { + slice: Gt, + set: jt, + constructor: function() {}, + toString: vt, + toLocaleString: Bt + }), Ft(Ht, "buffer", "b"), Ft(Ht, "byteOffset", "o"), Ft(Ht, "byteLength", "l"), Ft(Ht, "length", "e"), j(Ht, _t, { + get: function() { + return this[Tt] + } + }), t.exports = function(t, l, e, o) { + var f = t + ((o = !!o) ? "Clamped" : "") + "Array", + r = "get" + t, + a = "set" + t, + d = y[f], + s = d || {}, + n = d && I(d), + i = !d || !x.ABV, + u = {}, + h = d && d[K], + p = function(t, i) { + j(t, i, { + get: function() { + return t = i, (e = this._d).v[r](t * l + e.o, At); + var t, e + }, + set: function(t) { + return e = i, r = t, n = this._d, o && (r = (r = Math.round(r)) < 0 ? 0 : 255 < r ? 255 : 255 & r), void n.v[a](e * l + n.o, r, At); + var e, r, n + }, + enumerable: !0 + }) + }; + i ? (d = e(function(t, e, r, n) { + w(t, d, f, "_d"); + var i, o, a, s, u = 0, + h = 0; + if (A(e)) { + if (!(e instanceof Q || (s = E(e)) == H || s == W)) return Tt in e ? Dt(d, e) : kt.call(d, e); + i = e, h = It(r, l); + var c = e.byteLength; + if (void 0 === n) { + if (c % l) throw X(Mt); + if ((o = c - h) < 0) throw X(Mt) + } else if (c < (o = S(n) * l) + h) throw X(Mt); + a = o / l + } else a = M(e), i = new Q(o = a * l); + for (T(t, "_d", { + b: i, + o: h, + l: o, + e: a, + v: new J(i) + }); u < a;) p(t, u++) + }), h = d[K] = P(Ht), T(h, "constructor", d)) : _(function() { + d(1) + }) && _(function() { + new d(-1) + }) && k(function(t) { + new d, new d(null), new d(1.5), new d(t) + }, !0) || (d = e(function(t, e, r, n) { + var i; + return w(t, d, f), A(e) ? e instanceof Q || (i = E(e)) == H || i == W ? void 0 !== n ? new s(e, It(r, l), n) : void 0 !== r ? new s(e, It(r, l)) : new s(e) : Tt in e ? Dt(d, e) : kt.call(d, e) : new s(M(e)) + }), $(n !== Function.prototype ? O(s).concat(O(n)) : O(s), function(t) { + t in d || T(d, t, s[t]) + }), d[K] = h, g || (h.constructor = d)); + var c = h[yt], + m = !!c && ("values" == c.name || null == c.name), + v = zt.values; + T(d, bt, !0), T(h, Tt, f), T(h, St, !0), T(h, xt, d), (o ? new d(1)[_t] == f : _t in h) || j(h, _t, { + get: function() { + return f + } + }), u[f] = d, b(b.G + b.W + b.F * (d != s), u), b(b.S, f, { + BYTES_PER_ELEMENT: l + }), b(b.S + b.F * _(function() { + s.of.call(d, 1) + }), f, { + from: kt, + of: Lt + }), Y in h || T(h, Y, l), b(b.P, f, Ut), L(f), b(b.P + b.F * Pt, f, { + set: jt + }), b(b.P + b.F * !m, f, zt), g || h.toString == vt || (h.toString = vt), b(b.P + b.F * _(function() { + new d(1).slice() + }), f, { + slice: Gt + }), b(b.P + b.F * (_(function() { + return [1, 2].toLocaleString() != new d([1, 2]).toLocaleString() + }) || !_(function() { + h.toLocaleString.call([1, 2]) + })), f, { + toLocaleString: Bt + }), F[f] = m ? c : v, g || m || T(h, yt, v) + } + } else t.exports = function() {} + }, function(t, e, r) { + var o = r(157), + n = r(1), + i = r(74)("metadata"), + a = i.store || (i.store = new(r(160))), + s = function(t, e, r) { + var n = a.get(t); + if (!n) { + if (!r) return; + a.set(t, n = new o) + } + var i = n.get(e); + if (!i) { + if (!r) return; + n.set(e, i = new o) + } + return i + }; + t.exports = { + store: a, + map: s, + has: function(t, e, r) { + var n = s(e, r, !1); + return void 0 !== n && n.has(t) + }, + get: function(t, e, r) { + var n = s(e, r, !1); + return void 0 === n ? void 0 : n.get(t) + }, + set: function(t, e, r, n) { + s(r, n, !0).set(t, e) + }, + keys: function(t, e) { + var r = s(t, e, !1), + n = []; + return r && r.forEach(function(t, e) { + n.push(e) + }), n + }, + key: function(t) { + return void 0 === t || "symbol" == typeof t ? t : String(t) + }, + exp: function(t) { + n(n.S, "Reflect", t) + } + } + }, function(t, e, r) { + "use strict"; + e.__esModule = !0; + ! function(t) { + { + if (t && t.__esModule) return; + var e = {}; + if (null != t) + for (var r in t) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + e.default = t + } + }(r(0)); + var n = _interopRequireDefault(r(37)), + i = _interopRequireDefault(r(428)), + o = _interopRequireDefault(r(429)); + + function _interopRequireDefault(t) { + return t && t.__esModule ? t : { + default: t + } + } + var a = function(e) { + function PhongMaterial() { + var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; + return function(t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") + }(this, PhongMaterial), + function(t, e) { + if (!t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return !e || "object" != typeof e && "function" != typeof e ? t : e + }(this, e.call(this, t, i.default, o.default, [], "phong")) + } + return function(t, e) { + if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function, not " + typeof e); + t.prototype = Object.create(e && e.prototype, { + constructor: { + value: t, + enumerable: !1, + writable: !0, + configurable: !0 + } + }), e && (Object.setPrototypeOf ? Object.setPrototypeOf(t, e) : t.__proto__ = e) + }(PhongMaterial, e), PhongMaterial + }(n.default); + e.default = a + }, function(e, r, t) { + var n, b, x; + ! function(t) { + var o = /iPhone/i, + a = /iPod/i, + s = /iPad/i, + u = /\bAndroid(?:.+)Mobile\b/i, + h = /Android/i, + c = /\bAndroid(?:.+)SD4930UR\b/i, + l = /\bAndroid(?:.+)(?:KF[A-Z]{2,4})\b/i, + f = /Windows Phone/i, + d = /\bWindows(?:.+)ARM\b/i, + p = /BlackBerry/i, + v = /BB10/i, + g = /Opera Mini/i, + y = /\b(CriOS|Chrome)(?:.+)Mobile/i, + _ = /\Mobile(?:.+)Firefox\b/i; + + function m(t, e) { + return t.test(e) + } + + function i(t) { + var e = t || ("undefined" != typeof navigator ? navigator.userAgent : ""), + r = e.split("[FBAN"); + void 0 !== r[1] && (e = r[0]), void 0 !== (r = e.split("Twitter"))[1] && (e = r[0]); + var n = { + apple: { + phone: m(o, e) && !m(f, e), + ipod: m(a, e), + tablet: !m(o, e) && m(s, e) && !m(f, e), + device: (m(o, e) || m(a, e) || m(s, e)) && !m(f, e) + }, + amazon: { + phone: m(c, e), + tablet: !m(c, e) && m(l, e), + device: m(c, e) || m(l, e) + }, + android: { + phone: !m(f, e) && m(c, e) || !m(f, e) && m(u, e), + tablet: !m(f, e) && !m(c, e) && !m(u, e) && (m(l, e) || m(h, e)), + device: !m(f, e) && (m(c, e) || m(l, e) || m(u, e) || m(h, e)) + }, + windows: { + phone: m(f, e), + tablet: m(d, e), + device: m(f, e) || m(d, e) + }, + other: { + blackberry: m(p, e), + blackberry10: m(v, e), + opera: m(g, e), + firefox: m(_, e), + chrome: m(y, e), + device: m(p, e) || m(v, e) || m(g, e) || m(_, e) || m(y, e) + } + }; + return n.any = n.apple.device || n.android.device || n.windows.device || n.other.device, n.phone = n.apple.phone || n.android.phone || n.windows.phone, n.tablet = n.apple.tablet || n.android.tablet || n.windows.tablet, n + } + e.exports && "undefined" == typeof window ? e.exports = i : e.exports && "undefined" != typeof window ? e.exports = i() : (b = [], n = t.isMobile = i(), void 0 === (x = "function" == typeof n ? n.apply(r, b) : n) || (e.exports = x)) + }(this) + }, function(t, e, r) { + "use strict"; + var n = r(12), + i = r.n(n), + o = r(68), + c = r(23), + a = function() { + function SoundManagerHowler() { + this.disabled = !1, (window.smm = this).preload = !0, this.currentSound = null, this.sounds = {}, this.groups = {}, this.globalVolume = 1, this.isMuted = !1, this._visibilityActive = !0, this.preload = !0, window.onfocus = this._onFocus.bind(this), this.onMuteToggle = new i.a, o.a.onHide.add(function() { + this.onVisibilityHide() + }.bind(this)), o.a.onShow.add(function() { + this.onVisibilityShow() + }.bind(this)) + } + var t = SoundManagerHowler.prototype; + return t.enableVisibility = function() { + this._visibilityActive = !0 + }, t.disableVisibility = function() { + this._visibilityActive = !1 + }, t.onVisibilityHide = function() { + this._visibilityActive && (c.Howler.mute(!0), c.Howler.volume(0)) + }, t.onVisibilityShow = function() { + this._visibilityActive && this._onFocus() + }, t._onFocus = function() { + this.isMuted ? this.mute() : this.unmute() + }, t.addSound = function(t, e, r) { + if (!this.disabled) { + for (var n, i = "", o = 1, a = null, s = 0; s < e.length; s++) "/" === e[s] && (a = s); + for (null !== a && (i = e.slice(a + 1, e.length)), e = i; this.exists(i);) i = e + "-" + o, o++; + r = r || {}; + var u = t.split(".").pop(); + n = "ogg" === u || "mp3" === u || "wav" === u ? [t] : [t + ".ogg", t + ".mp3"]; + var h = new c.Howl({ + src: n, + preload: this.preload, + autoplay: r.autoplay || !1, + loop: r.loop || !1, + volume: r.volume + }); + r.autoplay && (this.preload || h.load(), h.play()), h.realVolume = r.volume || 1, this.sounds[i] = h + } + }, t.addSounds = function(t, e) { + for (var r = 0; r < t.length; r++) { + var n = t[r]; + this.addSound(e + n, n) + } + }, t.addGroup = function(t, e) { + if (!this.disabled && !this.groups[e]) { + var r = { + index: 0, + type: 0, + sounds: t + }; + this.groups[e] = r + } + }, t.getSoundByAlias = function(t) { + return this.sounds[t] + }, t.getSound = function(t) { + return this.currentSound + }, t.exists = function(t) { + return this.sounds[t] + }, t.play = function(t, e) { + if (void 0 === e && (e = {}), !this.disabled && this.sounds[t]) return e.volume && (this.sounds[t].volume = e.volume * this.globalVolume), e.time && (this.sounds[t].currentTime = e.time), this.preload || this.sounds[t].load(), this.sounds[t].play(), this.currentSound = this.sounds[t], this.currentSound; + this.sounds[t] + }, t.fadeTo = function(t, e, r) { + if (void 0 === r && (r = 2.5), void 0 !== e) { + var n = this.sounds[t]; + n && TweenLite.to(n, r, { + _volume: e, + onUpdate: this.actuallyChangeVolume.bind(this), + onComplete: this.stopAndReset.bind(this), + onCompleteParams: [t], + onUpdateParams: ["{self}", t] + }) + } + }, t.actuallyChangeVolume = function(t, e) { + this.setVolume(e, t.target._volume) + }, t.setVolume = function(t, e) { + if (!this.disabled) { + var r = this.sounds[t]; + r.realVolume = e, r.volume(e * this.globalVolume) + } + }, t.stopAndReset = function(t) { + this.stop(t), this.setVolume(t, 1), this.stop(t) + }, t.isPlaying = function(t) { + if (!this.disabled) return !this.sounds[t]._audioNode[0].paused + }, t.playGroup = function(t) { + if (!this.disabled && this.groups[t]) { + var e = this.groups[t], + r = Math.random() * e.sounds.length | 0; + this.preload || this.sounds[e.sounds[r]].load(), this.sounds[e.sounds[r]].play(), this.currentSound = this.sounds[e.sounds[r]] + } + }, t.setVolume = function(t, e) { + if (!this.disabled) { + var r = this.sounds[t]; + r.realVolume = e, r.volume = e * this.globalVolume + } + }, t.stop = function(t) { + !this.disabled && this.sounds[t] && this.sounds[t].stop() + }, t.pause = function(t) { + !this.disabled && this.sounds[t] && this.sounds[t].pause() + }, t.stopAll = function() { + if (!this.disabled) + for (var t in this.sounds) this.sounds[t].stop() + }, t.setPlaybackSpeed = function(t, e) { + if (!this.disabled) { + var r = this.sounds[t]; + r._playbackSpeed = e; + var n = "music" == t ? 0 : 1; + r._webAudio && c.Howler._howls[n]._audioNode[0] && (c.Howler._howls[n]._audioNode[0].bufferSource.playbackRate.value = e) + } + }, t.getPlaybackSpeed = function(t) { + if (!this.disabled) return this.sounds[t]._playbackSpeed || 1 + }, t.setGlobalVolume = function(t) { + for (var e in this.globalVolume = t, this.sounds) { + var r = this.sounds[e]; + r.volume = r.realVolume * t + } + }, t.mute = function() { + for (var t in this.isMuted = !0, c.Howler.mute(!0), c.Howler.volume(0), this.sounds) this.sounds[t].mute(!0); + this.onMuteToggle.dispatch(!0) + }, t.unmute = function() { + for (var t in this.isMuted = !1, c.Howler.mute(!1), c.Howler.volume(1), this.sounds) this.sounds[t].mute(!1); + this.onMuteToggle.dispatch(!1) + }, t.check = function() { + this.lastSeen = Date.now(); + ! function loop() { + lastSeen = Date.now(), setTimeout(loop, 50) + }(), document.getElementById("music").addEventListener("timeupdate", function() { + 100 < Date.now() - exports.lastSeen && this.pause() + }, !1) + }, SoundManagerHowler + }(); + e.a = new a + }, function(t, e, r) { + var n = r(52)("meta"), + i = r(11), + o = r(28), + a = r(20).f, + s = 0, + u = Object.isExtensible || function() { + return !0 + }, + h = !r(10)(function() { + return u(Object.preventExtensions({})) + }), + c = function(t) { + a(t, n, { + value: { + i: "O" + ++s, + w: {} + } + }) + }, + l = t.exports = { + KEY: n, + NEED: !1, + fastKey: function(t, e) { + if (!i(t)) return "symbol" == typeof t ? t : ("string" == typeof t ? "S" : "P") + t; + if (!o(t, n)) { + if (!u(t)) return "F"; + if (!e) return "E"; + c(t) + } + return t[n].i + }, + getWeak: function(t, e) { + if (!o(t, n)) { + if (!u(t)) return !0; + if (!e) return !1; + c(t) + } + return t[n].w + }, + onFreeze: function(t) { + return h && l.NEED && u(t) && !o(t, n) && c(t), t + } + } + }, function(t, e) { + t.exports = !1 + }, function(t, e, r) { + var n = r(15)("unscopables"), + i = Array.prototype; + null == i[n] && r(25)(i, n, {}), t.exports = function(t) { + i[n][t] = !0 + } + }, function(t, e) { + t.exports = function(t, e) { + return { + enumerable: !(1 & t), + configurable: !(2 & t), + writable: !(4 & t), + value: e + } + } + }, function(t, e) { + var r = 0, + n = Math.random(); + t.exports = function(t) { + return "Symbol(".concat(void 0 === t ? "" : t, ")_", (++r + n).toString(36)) + } + }, function(t, e, r) { + var n = r(136), + i = r(101); + t.exports = Object.keys || function(t) { + return n(t, i) + } + }, function(t, e, r) { + var n = r(35), + i = Math.max, + o = Math.min; + t.exports = function(t, e) { + return (t = n(t)) < 0 ? i(t + e, 0) : o(t, e) + } + }, function(t, e, n) { + var i = n(6), + o = n(137), + a = n(101), + s = n(100)("IE_PROTO"), + u = function() {}, + h = "prototype", + c = function() { + var t, e = n(98)("iframe"), + r = a.length; + for (e.style.display = "none", n(102).appendChild(e), e.src = "javascript:", (t = e.contentWindow.document).open(), t.write(" - - + - - - - - + + - Selenite + Support | Selenite - + - - + + - -
- -
-

Thank you for clicking!

-

I'm just a single developer who works on this website for free in my free time. I currently don't make anything from this website, and so any donation would be very appreciated. Click on one of the links below to help support me!

- Patreon - + + + diff --git a/impossiblegame/image.jpg b/theimpossiblegame/image.jpg similarity index 100% rename from impossiblegame/image.jpg rename to theimpossiblegame/image.jpg diff --git a/impossiblegame/index.html b/theimpossiblegame/index.html similarity index 100% rename from impossiblegame/index.html rename to theimpossiblegame/index.html diff --git a/impossiblegame/theimpossiblegame.swf b/theimpossiblegame/theimpossiblegame.swf similarity index 100% rename from impossiblegame/theimpossiblegame.swf rename to theimpossiblegame/theimpossiblegame.swf diff --git a/themes.css b/themes.css new file mode 100644 index 00000000..9b9e28d6 --- /dev/null +++ b/themes.css @@ -0,0 +1,42 @@ +body { + --inputbg: #000000; + --inputborder: #000000; + --uibg: #000000; + --textcolor: #000; + --bg: #000000; +} +body[theme=main] { + --inputbg: #3c096c; + --inputborder: #5a189a; + --uibg: #240046; + --textcolor: #fff; + --bg: #10002b; +} +body[theme=light] { + --inputbg: #bbbbbb; + --inputborder: #e6e6e6; + --uibg: #b3b3b3; + --textcolor: #1a1a1a; + --bg: #c5c5c5; +} +body[theme=dark] { + --inputbg: #333333; + --inputborder: #444444; + --uibg: #242424; + --textcolor: #fff; + --bg: #0c0c0c; +} +body[theme=egamepass] { + --inputbg: #4f6ed1; + --inputborder: #5586e0; + --uibg: #185494; + --textcolor: #a7d3ff; + --bg: #16416f; +} +body[theme=cools1te] { + --inputbg: #a134dc; + --inputborder: #b153e3; + --uibg: #ab28cf; + --textcolor: #d3a4fa; + --bg: #8e14af; +} \ No newline at end of file diff --git a/thirtydollarwebsite/assets/action_bg.png b/thirtydollarwebsite/assets/action_bg.png new file mode 100644 index 00000000..9735ee80 Binary files /dev/null and b/thirtydollarwebsite/assets/action_bg.png differ diff --git a/thirtydollarwebsite/assets/action_combine.png b/thirtydollarwebsite/assets/action_combine.png new file mode 100644 index 00000000..4ecf64d5 Binary files /dev/null and b/thirtydollarwebsite/assets/action_combine.png differ diff --git a/thirtydollarwebsite/assets/action_cut.png b/thirtydollarwebsite/assets/action_cut.png new file mode 100644 index 00000000..0a9df78c Binary files /dev/null and b/thirtydollarwebsite/assets/action_cut.png differ diff --git a/thirtydollarwebsite/assets/action_divider.png b/thirtydollarwebsite/assets/action_divider.png new file mode 100644 index 00000000..29627ac9 Binary files /dev/null and b/thirtydollarwebsite/assets/action_divider.png differ diff --git a/thirtydollarwebsite/assets/action_flash.png b/thirtydollarwebsite/assets/action_flash.png new file mode 100644 index 00000000..d6c99510 Binary files /dev/null and b/thirtydollarwebsite/assets/action_flash.png differ diff --git a/thirtydollarwebsite/assets/action_jump.png b/thirtydollarwebsite/assets/action_jump.png new file mode 100644 index 00000000..bb198491 Binary files /dev/null and b/thirtydollarwebsite/assets/action_jump.png differ diff --git a/thirtydollarwebsite/assets/action_loop.png b/thirtydollarwebsite/assets/action_loop.png new file mode 100644 index 00000000..4e745b0b Binary files /dev/null and b/thirtydollarwebsite/assets/action_loop.png differ diff --git a/thirtydollarwebsite/assets/action_loopmany.png b/thirtydollarwebsite/assets/action_loopmany.png new file mode 100644 index 00000000..8305ebdc Binary files /dev/null and b/thirtydollarwebsite/assets/action_loopmany.png differ diff --git a/thirtydollarwebsite/assets/action_looptarget.png b/thirtydollarwebsite/assets/action_looptarget.png new file mode 100644 index 00000000..ac64797b Binary files /dev/null and b/thirtydollarwebsite/assets/action_looptarget.png differ diff --git a/thirtydollarwebsite/assets/action_pulse.png b/thirtydollarwebsite/assets/action_pulse.png new file mode 100644 index 00000000..f49f8146 Binary files /dev/null and b/thirtydollarwebsite/assets/action_pulse.png differ diff --git a/thirtydollarwebsite/assets/action_speed.png b/thirtydollarwebsite/assets/action_speed.png new file mode 100644 index 00000000..364b8ee4 Binary files /dev/null and b/thirtydollarwebsite/assets/action_speed.png differ diff --git a/thirtydollarwebsite/assets/action_startpos.png b/thirtydollarwebsite/assets/action_startpos.png new file mode 100644 index 00000000..532c7f20 Binary files /dev/null and b/thirtydollarwebsite/assets/action_startpos.png differ diff --git a/thirtydollarwebsite/assets/action_stop.png b/thirtydollarwebsite/assets/action_stop.png new file mode 100644 index 00000000..8505fad1 Binary files /dev/null and b/thirtydollarwebsite/assets/action_stop.png differ diff --git a/thirtydollarwebsite/assets/action_target.png b/thirtydollarwebsite/assets/action_target.png new file mode 100644 index 00000000..5cf6523c Binary files /dev/null and b/thirtydollarwebsite/assets/action_target.png differ diff --git a/thirtydollarwebsite/assets/action_transpose.png b/thirtydollarwebsite/assets/action_transpose.png new file mode 100644 index 00000000..dc373248 Binary files /dev/null and b/thirtydollarwebsite/assets/action_transpose.png differ diff --git a/thirtydollarwebsite/assets/action_volume.png b/thirtydollarwebsite/assets/action_volume.png new file mode 100644 index 00000000..d59bb53f Binary files /dev/null and b/thirtydollarwebsite/assets/action_volume.png differ diff --git a/thirtydollarwebsite/assets/check_off.svg b/thirtydollarwebsite/assets/check_off.svg new file mode 100644 index 00000000..72504499 --- /dev/null +++ b/thirtydollarwebsite/assets/check_off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/dont_you_lecture_me.wav b/thirtydollarwebsite/assets/dont_you_lecture_me.wav new file mode 100644 index 00000000..a5baaba8 Binary files /dev/null and b/thirtydollarwebsite/assets/dont_you_lecture_me.wav differ diff --git a/thirtydollarwebsite/assets/download.svg b/thirtydollarwebsite/assets/download.svg new file mode 100644 index 00000000..91b227c4 --- /dev/null +++ b/thirtydollarwebsite/assets/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/load.svg b/thirtydollarwebsite/assets/load.svg new file mode 100644 index 00000000..e57e30c1 --- /dev/null +++ b/thirtydollarwebsite/assets/load.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/play.png b/thirtydollarwebsite/assets/play.png new file mode 100644 index 00000000..fc026965 Binary files /dev/null and b/thirtydollarwebsite/assets/play.png differ diff --git a/thirtydollarwebsite/assets/reset.png b/thirtydollarwebsite/assets/reset.png new file mode 100644 index 00000000..8dff0172 Binary files /dev/null and b/thirtydollarwebsite/assets/reset.png differ diff --git a/thirtydollarwebsite/assets/save.svg b/thirtydollarwebsite/assets/save.svg new file mode 100644 index 00000000..a5d27eb7 --- /dev/null +++ b/thirtydollarwebsite/assets/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/section_deselect.png b/thirtydollarwebsite/assets/section_deselect.png new file mode 100644 index 00000000..c6c12913 Binary files /dev/null and b/thirtydollarwebsite/assets/section_deselect.png differ diff --git a/thirtydollarwebsite/assets/section_hide.png b/thirtydollarwebsite/assets/section_hide.png new file mode 100644 index 00000000..8642097d Binary files /dev/null and b/thirtydollarwebsite/assets/section_hide.png differ diff --git a/thirtydollarwebsite/assets/section_next.png b/thirtydollarwebsite/assets/section_next.png new file mode 100644 index 00000000..36606bc9 Binary files /dev/null and b/thirtydollarwebsite/assets/section_next.png differ diff --git a/thirtydollarwebsite/assets/section_previous.png b/thirtydollarwebsite/assets/section_previous.png new file mode 100644 index 00000000..cac00d7f Binary files /dev/null and b/thirtydollarwebsite/assets/section_previous.png differ diff --git a/thirtydollarwebsite/assets/section_show.png b/thirtydollarwebsite/assets/section_show.png new file mode 100644 index 00000000..45ab5768 Binary files /dev/null and b/thirtydollarwebsite/assets/section_show.png differ diff --git a/thirtydollarwebsite/assets/social_tiktok.svg b/thirtydollarwebsite/assets/social_tiktok.svg new file mode 100644 index 00000000..3da85e64 --- /dev/null +++ b/thirtydollarwebsite/assets/social_tiktok.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/social_twitter.svg b/thirtydollarwebsite/assets/social_twitter.svg new file mode 100644 index 00000000..d16b6a7d --- /dev/null +++ b/thirtydollarwebsite/assets/social_twitter.svg @@ -0,0 +1,16 @@ + + + diff --git a/thirtydollarwebsite/assets/social_youtube.svg b/thirtydollarwebsite/assets/social_youtube.svg new file mode 100644 index 00000000..552c295f --- /dev/null +++ b/thirtydollarwebsite/assets/social_youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/stop.png b/thirtydollarwebsite/assets/stop.png new file mode 100644 index 00000000..192a0fab Binary files /dev/null and b/thirtydollarwebsite/assets/stop.png differ diff --git a/thirtydollarwebsite/assets/tab_actions.svg b/thirtydollarwebsite/assets/tab_actions.svg new file mode 100644 index 00000000..a49fdf92 --- /dev/null +++ b/thirtydollarwebsite/assets/tab_actions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/tab_all.svg b/thirtydollarwebsite/assets/tab_all.svg new file mode 100644 index 00000000..00c4a0c2 --- /dev/null +++ b/thirtydollarwebsite/assets/tab_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/tab_notes.svg b/thirtydollarwebsite/assets/tab_notes.svg new file mode 100644 index 00000000..d224ce01 --- /dev/null +++ b/thirtydollarwebsite/assets/tab_notes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/tab_percussion.svg b/thirtydollarwebsite/assets/tab_percussion.svg new file mode 100644 index 00000000..748d8a7b --- /dev/null +++ b/thirtydollarwebsite/assets/tab_percussion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/tab_recent.svg b/thirtydollarwebsite/assets/tab_recent.svg new file mode 100644 index 00000000..5638d69d --- /dev/null +++ b/thirtydollarwebsite/assets/tab_recent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirtydollarwebsite/assets/white_people_be_like.wav b/thirtydollarwebsite/assets/white_people_be_like.wav new file mode 100644 index 00000000..7e0dbc78 Binary files /dev/null and b/thirtydollarwebsite/assets/white_people_be_like.wav differ diff --git a/thirtydollarwebsite/assets/you_deadass_built_like_a.wav b/thirtydollarwebsite/assets/you_deadass_built_like_a.wav new file mode 100644 index 00000000..f948b264 Binary files /dev/null and b/thirtydollarwebsite/assets/you_deadass_built_like_a.wav differ diff --git a/thirtydollarwebsite/assets/🗿.png b/thirtydollarwebsite/assets/🗿.png new file mode 100644 index 00000000..910c14dc Binary files /dev/null and b/thirtydollarwebsite/assets/🗿.png differ diff --git a/thirtydollarwebsite/icons/21.png b/thirtydollarwebsite/icons/21.png new file mode 100644 index 00000000..d6769a30 Binary files /dev/null and b/thirtydollarwebsite/icons/21.png differ diff --git a/thirtydollarwebsite/icons/BABA.png b/thirtydollarwebsite/icons/BABA.png new file mode 100644 index 00000000..5426f05b Binary files /dev/null and b/thirtydollarwebsite/icons/BABA.png differ diff --git a/thirtydollarwebsite/icons/DEFEAT.png b/thirtydollarwebsite/icons/DEFEAT.png new file mode 100644 index 00000000..75acd28e Binary files /dev/null and b/thirtydollarwebsite/icons/DEFEAT.png differ diff --git a/thirtydollarwebsite/icons/SLAM.png b/thirtydollarwebsite/icons/SLAM.png new file mode 100644 index 00000000..436dcb06 Binary files /dev/null and b/thirtydollarwebsite/icons/SLAM.png differ diff --git a/thirtydollarwebsite/icons/YOU.png b/thirtydollarwebsite/icons/YOU.png new file mode 100644 index 00000000..c8356fb0 Binary files /dev/null and b/thirtydollarwebsite/icons/YOU.png differ diff --git a/thirtydollarwebsite/icons/adofai_fire.png b/thirtydollarwebsite/icons/adofai_fire.png new file mode 100644 index 00000000..89705878 Binary files /dev/null and b/thirtydollarwebsite/icons/adofai_fire.png differ diff --git a/thirtydollarwebsite/icons/adofai_ice.png b/thirtydollarwebsite/icons/adofai_ice.png new file mode 100644 index 00000000..592cab02 Binary files /dev/null and b/thirtydollarwebsite/icons/adofai_ice.png differ diff --git a/thirtydollarwebsite/icons/adofaicymbal.png b/thirtydollarwebsite/icons/adofaicymbal.png new file mode 100644 index 00000000..bc449a22 Binary files /dev/null and b/thirtydollarwebsite/icons/adofaicymbal.png differ diff --git a/thirtydollarwebsite/icons/adofaikick.png b/thirtydollarwebsite/icons/adofaikick.png new file mode 100644 index 00000000..889653c0 Binary files /dev/null and b/thirtydollarwebsite/icons/adofaikick.png differ diff --git a/thirtydollarwebsite/icons/airhorn.png b/thirtydollarwebsite/icons/airhorn.png new file mode 100644 index 00000000..7ddca353 Binary files /dev/null and b/thirtydollarwebsite/icons/airhorn.png differ diff --git a/thirtydollarwebsite/icons/americano.png b/thirtydollarwebsite/icons/americano.png new file mode 100644 index 00000000..a618f733 Binary files /dev/null and b/thirtydollarwebsite/icons/americano.png differ diff --git a/thirtydollarwebsite/icons/amogus.png b/thirtydollarwebsite/icons/amogus.png new file mode 100644 index 00000000..808e5b94 Binary files /dev/null and b/thirtydollarwebsite/icons/amogus.png differ diff --git a/thirtydollarwebsite/icons/amogus_emergency.png b/thirtydollarwebsite/icons/amogus_emergency.png new file mode 100644 index 00000000..9d5a0cf1 Binary files /dev/null and b/thirtydollarwebsite/icons/amogus_emergency.png differ diff --git a/thirtydollarwebsite/icons/amogus_kill.png b/thirtydollarwebsite/icons/amogus_kill.png new file mode 100644 index 00000000..e68a6c3d Binary files /dev/null and b/thirtydollarwebsite/icons/amogus_kill.png differ diff --git a/thirtydollarwebsite/icons/amongdrip.png b/thirtydollarwebsite/icons/amongdrip.png new file mode 100644 index 00000000..cf20047d Binary files /dev/null and b/thirtydollarwebsite/icons/amongdrip.png differ diff --git a/thirtydollarwebsite/icons/amongus.png b/thirtydollarwebsite/icons/amongus.png new file mode 100644 index 00000000..eba95be1 Binary files /dev/null and b/thirtydollarwebsite/icons/amongus.png differ diff --git a/thirtydollarwebsite/icons/boing.png b/thirtydollarwebsite/icons/boing.png new file mode 100644 index 00000000..e0c4ae2d Binary files /dev/null and b/thirtydollarwebsite/icons/boing.png differ diff --git a/thirtydollarwebsite/icons/bong.png b/thirtydollarwebsite/icons/bong.png new file mode 100644 index 00000000..f84f6c9d Binary files /dev/null and b/thirtydollarwebsite/icons/bong.png differ diff --git a/thirtydollarwebsite/icons/boom.png b/thirtydollarwebsite/icons/boom.png new file mode 100644 index 00000000..910c14dc Binary files /dev/null and b/thirtydollarwebsite/icons/boom.png differ diff --git a/thirtydollarwebsite/icons/boowomp.png b/thirtydollarwebsite/icons/boowomp.png new file mode 100644 index 00000000..dcc6a85f Binary files /dev/null and b/thirtydollarwebsite/icons/boowomp.png differ diff --git a/thirtydollarwebsite/icons/bruh.png b/thirtydollarwebsite/icons/bruh.png new file mode 100644 index 00000000..3eead237 Binary files /dev/null and b/thirtydollarwebsite/icons/bruh.png differ diff --git a/thirtydollarwebsite/icons/builttoscale.png b/thirtydollarwebsite/icons/builttoscale.png new file mode 100644 index 00000000..a7e5d565 Binary files /dev/null and b/thirtydollarwebsite/icons/builttoscale.png differ diff --git a/thirtydollarwebsite/icons/bup.png b/thirtydollarwebsite/icons/bup.png new file mode 100644 index 00000000..368cb36a Binary files /dev/null and b/thirtydollarwebsite/icons/bup.png differ diff --git a/thirtydollarwebsite/icons/buttonpop.png b/thirtydollarwebsite/icons/buttonpop.png new file mode 100644 index 00000000..a9930f2b Binary files /dev/null and b/thirtydollarwebsite/icons/buttonpop.png differ diff --git a/thirtydollarwebsite/icons/buzzer.png b/thirtydollarwebsite/icons/buzzer.png new file mode 100644 index 00000000..47f2b8da Binary files /dev/null and b/thirtydollarwebsite/icons/buzzer.png differ diff --git a/thirtydollarwebsite/icons/bwomp.png b/thirtydollarwebsite/icons/bwomp.png new file mode 100644 index 00000000..57b39e97 Binary files /dev/null and b/thirtydollarwebsite/icons/bwomp.png differ diff --git a/thirtydollarwebsite/icons/celeste_dash.png b/thirtydollarwebsite/icons/celeste_dash.png new file mode 100644 index 00000000..85786e7a Binary files /dev/null and b/thirtydollarwebsite/icons/celeste_dash.png differ diff --git a/thirtydollarwebsite/icons/celeste_death.png b/thirtydollarwebsite/icons/celeste_death.png new file mode 100644 index 00000000..7c583c12 Binary files /dev/null and b/thirtydollarwebsite/icons/celeste_death.png differ diff --git a/thirtydollarwebsite/icons/celeste_diamond.png b/thirtydollarwebsite/icons/celeste_diamond.png new file mode 100644 index 00000000..7d71fa6e Binary files /dev/null and b/thirtydollarwebsite/icons/celeste_diamond.png differ diff --git a/thirtydollarwebsite/icons/celeste_spring.png b/thirtydollarwebsite/icons/celeste_spring.png new file mode 100644 index 00000000..5e02de77 Binary files /dev/null and b/thirtydollarwebsite/icons/celeste_spring.png differ diff --git a/thirtydollarwebsite/icons/choruskid.png b/thirtydollarwebsite/icons/choruskid.png new file mode 100644 index 00000000..d0efbc3b Binary files /dev/null and b/thirtydollarwebsite/icons/choruskid.png differ diff --git a/thirtydollarwebsite/icons/cowbell.png b/thirtydollarwebsite/icons/cowbell.png new file mode 100644 index 00000000..c4265ea4 Binary files /dev/null and b/thirtydollarwebsite/icons/cowbell.png differ diff --git a/thirtydollarwebsite/icons/dimrainsynth.png b/thirtydollarwebsite/icons/dimrainsynth.png new file mode 100644 index 00000000..38966032 Binary files /dev/null and b/thirtydollarwebsite/icons/dimrainsynth.png differ diff --git a/thirtydollarwebsite/icons/dodgeball.png b/thirtydollarwebsite/icons/dodgeball.png new file mode 100644 index 00000000..95ba87ef Binary files /dev/null and b/thirtydollarwebsite/icons/dodgeball.png differ diff --git a/thirtydollarwebsite/icons/e.png b/thirtydollarwebsite/icons/e.png new file mode 100644 index 00000000..92be0fdf Binary files /dev/null and b/thirtydollarwebsite/icons/e.png differ diff --git a/thirtydollarwebsite/icons/eight.png b/thirtydollarwebsite/icons/eight.png new file mode 100644 index 00000000..eaed8862 Binary files /dev/null and b/thirtydollarwebsite/icons/eight.png differ diff --git a/thirtydollarwebsite/icons/empty.png b/thirtydollarwebsite/icons/empty.png new file mode 100644 index 00000000..855346ad Binary files /dev/null and b/thirtydollarwebsite/icons/empty.png differ diff --git a/thirtydollarwebsite/icons/explosion.png b/thirtydollarwebsite/icons/explosion.png new file mode 100644 index 00000000..e8682d25 Binary files /dev/null and b/thirtydollarwebsite/icons/explosion.png differ diff --git a/thirtydollarwebsite/icons/familyguy.png b/thirtydollarwebsite/icons/familyguy.png new file mode 100644 index 00000000..1b8c2153 Binary files /dev/null and b/thirtydollarwebsite/icons/familyguy.png differ diff --git a/thirtydollarwebsite/icons/flipnote.png b/thirtydollarwebsite/icons/flipnote.png new file mode 100644 index 00000000..d016796b Binary files /dev/null and b/thirtydollarwebsite/icons/flipnote.png differ diff --git a/thirtydollarwebsite/icons/fnf_death.png b/thirtydollarwebsite/icons/fnf_death.png new file mode 100644 index 00000000..b96565e0 Binary files /dev/null and b/thirtydollarwebsite/icons/fnf_death.png differ diff --git a/thirtydollarwebsite/icons/fnf_down.png b/thirtydollarwebsite/icons/fnf_down.png new file mode 100644 index 00000000..cfe07bee Binary files /dev/null and b/thirtydollarwebsite/icons/fnf_down.png differ diff --git a/thirtydollarwebsite/icons/fnf_left.png b/thirtydollarwebsite/icons/fnf_left.png new file mode 100644 index 00000000..0e9c57ba Binary files /dev/null and b/thirtydollarwebsite/icons/fnf_left.png differ diff --git a/thirtydollarwebsite/icons/fnf_right.png b/thirtydollarwebsite/icons/fnf_right.png new file mode 100644 index 00000000..11725547 Binary files /dev/null and b/thirtydollarwebsite/icons/fnf_right.png differ diff --git a/thirtydollarwebsite/icons/fnf_up.png b/thirtydollarwebsite/icons/fnf_up.png new file mode 100644 index 00000000..208f0c17 Binary files /dev/null and b/thirtydollarwebsite/icons/fnf_up.png differ diff --git a/thirtydollarwebsite/icons/gaster.png b/thirtydollarwebsite/icons/gaster.png new file mode 100644 index 00000000..b2863474 Binary files /dev/null and b/thirtydollarwebsite/icons/gaster.png differ diff --git a/thirtydollarwebsite/icons/gd_coin.png b/thirtydollarwebsite/icons/gd_coin.png new file mode 100644 index 00000000..c6ab23fe Binary files /dev/null and b/thirtydollarwebsite/icons/gd_coin.png differ diff --git a/thirtydollarwebsite/icons/gd_diamonds.png b/thirtydollarwebsite/icons/gd_diamonds.png new file mode 100644 index 00000000..10b8a598 Binary files /dev/null and b/thirtydollarwebsite/icons/gd_diamonds.png differ diff --git a/thirtydollarwebsite/icons/gd_orbs.png b/thirtydollarwebsite/icons/gd_orbs.png new file mode 100644 index 00000000..44f6e835 Binary files /dev/null and b/thirtydollarwebsite/icons/gd_orbs.png differ diff --git a/thirtydollarwebsite/icons/gd_quit.png b/thirtydollarwebsite/icons/gd_quit.png new file mode 100644 index 00000000..47726ebf Binary files /dev/null and b/thirtydollarwebsite/icons/gd_quit.png differ diff --git a/thirtydollarwebsite/icons/gdcrash.png b/thirtydollarwebsite/icons/gdcrash.png new file mode 100644 index 00000000..e173d4b7 Binary files /dev/null and b/thirtydollarwebsite/icons/gdcrash.png differ diff --git a/thirtydollarwebsite/icons/gdcrash_orbs.png b/thirtydollarwebsite/icons/gdcrash_orbs.png new file mode 100644 index 00000000..9045afed Binary files /dev/null and b/thirtydollarwebsite/icons/gdcrash_orbs.png differ diff --git a/thirtydollarwebsite/icons/gnome.png b/thirtydollarwebsite/icons/gnome.png new file mode 100644 index 00000000..2b491f1d Binary files /dev/null and b/thirtydollarwebsite/icons/gnome.png differ diff --git a/thirtydollarwebsite/icons/granddad.png b/thirtydollarwebsite/icons/granddad.png new file mode 100644 index 00000000..1c19f80f Binary files /dev/null and b/thirtydollarwebsite/icons/granddad.png differ diff --git a/thirtydollarwebsite/icons/gun.png b/thirtydollarwebsite/icons/gun.png new file mode 100644 index 00000000..30091071 Binary files /dev/null and b/thirtydollarwebsite/icons/gun.png differ diff --git a/thirtydollarwebsite/icons/hammer.png b/thirtydollarwebsite/icons/hammer.png new file mode 100644 index 00000000..5e26d734 Binary files /dev/null and b/thirtydollarwebsite/icons/hammer.png differ diff --git a/thirtydollarwebsite/icons/hehehehaw.png b/thirtydollarwebsite/icons/hehehehaw.png new file mode 100644 index 00000000..30bf42c5 Binary files /dev/null and b/thirtydollarwebsite/icons/hehehehaw.png differ diff --git a/thirtydollarwebsite/icons/hitmarker.png b/thirtydollarwebsite/icons/hitmarker.png new file mode 100644 index 00000000..d7764e4d Binary files /dev/null and b/thirtydollarwebsite/icons/hitmarker.png differ diff --git a/thirtydollarwebsite/icons/hoenn.png b/thirtydollarwebsite/icons/hoenn.png new file mode 100644 index 00000000..5f2d8ed1 Binary files /dev/null and b/thirtydollarwebsite/icons/hoenn.png differ diff --git a/thirtydollarwebsite/icons/isaac_dead.png b/thirtydollarwebsite/icons/isaac_dead.png new file mode 100644 index 00000000..531a96d9 Binary files /dev/null and b/thirtydollarwebsite/icons/isaac_dead.png differ diff --git a/thirtydollarwebsite/icons/isaac_hurt.png b/thirtydollarwebsite/icons/isaac_hurt.png new file mode 100644 index 00000000..4e89a39c Binary files /dev/null and b/thirtydollarwebsite/icons/isaac_hurt.png differ diff --git a/thirtydollarwebsite/icons/isaac_mantle.png b/thirtydollarwebsite/icons/isaac_mantle.png new file mode 100644 index 00000000..fa6b9040 Binary files /dev/null and b/thirtydollarwebsite/icons/isaac_mantle.png differ diff --git a/thirtydollarwebsite/icons/karateman_bulb.png b/thirtydollarwebsite/icons/karateman_bulb.png new file mode 100644 index 00000000..e63876bf Binary files /dev/null and b/thirtydollarwebsite/icons/karateman_bulb.png differ diff --git a/thirtydollarwebsite/icons/karateman_hit.png b/thirtydollarwebsite/icons/karateman_hit.png new file mode 100644 index 00000000..37aea1a9 Binary files /dev/null and b/thirtydollarwebsite/icons/karateman_hit.png differ diff --git a/thirtydollarwebsite/icons/karateman_offbeat.png b/thirtydollarwebsite/icons/karateman_offbeat.png new file mode 100644 index 00000000..c449278d Binary files /dev/null and b/thirtydollarwebsite/icons/karateman_offbeat.png differ diff --git a/thirtydollarwebsite/icons/karateman_throw.png b/thirtydollarwebsite/icons/karateman_throw.png new file mode 100644 index 00000000..ec538786 Binary files /dev/null and b/thirtydollarwebsite/icons/karateman_throw.png differ diff --git a/thirtydollarwebsite/icons/lancersplat.png b/thirtydollarwebsite/icons/lancersplat.png new file mode 100644 index 00000000..ed796cb7 Binary files /dev/null and b/thirtydollarwebsite/icons/lancersplat.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_baby.png b/thirtydollarwebsite/icons/mariopaint_baby.png new file mode 100644 index 00000000..d9161d47 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_baby.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_car.png b/thirtydollarwebsite/icons/mariopaint_car.png new file mode 100644 index 00000000..68c564e4 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_car.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_cat.png b/thirtydollarwebsite/icons/mariopaint_cat.png new file mode 100644 index 00000000..61a5eb3a Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_cat.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_dog.png b/thirtydollarwebsite/icons/mariopaint_dog.png new file mode 100644 index 00000000..3322b916 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_dog.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_flower.png b/thirtydollarwebsite/icons/mariopaint_flower.png new file mode 100644 index 00000000..f39d1db1 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_flower.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_gameboy.png b/thirtydollarwebsite/icons/mariopaint_gameboy.png new file mode 100644 index 00000000..14977d8a Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_gameboy.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_luigi.png b/thirtydollarwebsite/icons/mariopaint_luigi.png new file mode 100644 index 00000000..aa80d8db Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_luigi.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_mario.png b/thirtydollarwebsite/icons/mariopaint_mario.png new file mode 100644 index 00000000..246abdb7 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_mario.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_plane.png b/thirtydollarwebsite/icons/mariopaint_plane.png new file mode 100644 index 00000000..7ebce5cc Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_plane.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_star.png b/thirtydollarwebsite/icons/mariopaint_star.png new file mode 100644 index 00000000..68d0e612 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_star.png differ diff --git a/thirtydollarwebsite/icons/mariopaint_swan.png b/thirtydollarwebsite/icons/mariopaint_swan.png new file mode 100644 index 00000000..a382a742 Binary files /dev/null and b/thirtydollarwebsite/icons/mariopaint_swan.png differ diff --git a/thirtydollarwebsite/icons/megalovania.png b/thirtydollarwebsite/icons/megalovania.png new file mode 100644 index 00000000..30d3b017 Binary files /dev/null and b/thirtydollarwebsite/icons/megalovania.png differ diff --git a/thirtydollarwebsite/icons/metalpipe.png b/thirtydollarwebsite/icons/metalpipe.png new file mode 100644 index 00000000..d39d0c00 Binary files /dev/null and b/thirtydollarwebsite/icons/metalpipe.png differ diff --git a/thirtydollarwebsite/icons/midspin.png b/thirtydollarwebsite/icons/midspin.png new file mode 100644 index 00000000..b3afdaca Binary files /dev/null and b/thirtydollarwebsite/icons/midspin.png differ diff --git a/thirtydollarwebsite/icons/minecraft_anvil.png b/thirtydollarwebsite/icons/minecraft_anvil.png new file mode 100644 index 00000000..656b4ef9 Binary files /dev/null and b/thirtydollarwebsite/icons/minecraft_anvil.png differ diff --git a/thirtydollarwebsite/icons/minecraft_bell.png b/thirtydollarwebsite/icons/minecraft_bell.png new file mode 100644 index 00000000..2ea39119 Binary files /dev/null and b/thirtydollarwebsite/icons/minecraft_bell.png differ diff --git a/thirtydollarwebsite/icons/minecraft_explosion.png b/thirtydollarwebsite/icons/minecraft_explosion.png new file mode 100644 index 00000000..2d32ee73 Binary files /dev/null and b/thirtydollarwebsite/icons/minecraft_explosion.png differ diff --git a/thirtydollarwebsite/icons/morshu.png b/thirtydollarwebsite/icons/morshu.png new file mode 100644 index 00000000..95faf80d Binary files /dev/null and b/thirtydollarwebsite/icons/morshu.png differ diff --git a/thirtydollarwebsite/icons/mrbeast.png b/thirtydollarwebsite/icons/mrbeast.png new file mode 100644 index 00000000..36d6916b Binary files /dev/null and b/thirtydollarwebsite/icons/mrbeast.png differ diff --git a/thirtydollarwebsite/icons/necoarc.png b/thirtydollarwebsite/icons/necoarc.png new file mode 100644 index 00000000..9c98e739 Binary files /dev/null and b/thirtydollarwebsite/icons/necoarc.png differ diff --git a/thirtydollarwebsite/icons/nope.png b/thirtydollarwebsite/icons/nope.png new file mode 100644 index 00000000..6b95174d Binary files /dev/null and b/thirtydollarwebsite/icons/nope.png differ diff --git a/thirtydollarwebsite/icons/noteblock_banjo.png b/thirtydollarwebsite/icons/noteblock_banjo.png new file mode 100644 index 00000000..d2d7eb2f Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_banjo.png differ diff --git a/thirtydollarwebsite/icons/noteblock_bass.png b/thirtydollarwebsite/icons/noteblock_bass.png new file mode 100644 index 00000000..1b99d79e Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_bass.png differ diff --git a/thirtydollarwebsite/icons/noteblock_bell.png b/thirtydollarwebsite/icons/noteblock_bell.png new file mode 100644 index 00000000..1dd03296 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_bell.png differ diff --git a/thirtydollarwebsite/icons/noteblock_bit.png b/thirtydollarwebsite/icons/noteblock_bit.png new file mode 100644 index 00000000..00e04c51 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_bit.png differ diff --git a/thirtydollarwebsite/icons/noteblock_chime.png b/thirtydollarwebsite/icons/noteblock_chime.png new file mode 100644 index 00000000..61a7c354 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_chime.png differ diff --git a/thirtydollarwebsite/icons/noteblock_click.png b/thirtydollarwebsite/icons/noteblock_click.png new file mode 100644 index 00000000..458d306b Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_click.png differ diff --git a/thirtydollarwebsite/icons/noteblock_flute.png b/thirtydollarwebsite/icons/noteblock_flute.png new file mode 100644 index 00000000..4583079d Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_flute.png differ diff --git a/thirtydollarwebsite/icons/noteblock_guitar.png b/thirtydollarwebsite/icons/noteblock_guitar.png new file mode 100644 index 00000000..7efa463d Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_guitar.png differ diff --git a/thirtydollarwebsite/icons/noteblock_harp.png b/thirtydollarwebsite/icons/noteblock_harp.png new file mode 100644 index 00000000..557536c0 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_harp.png differ diff --git a/thirtydollarwebsite/icons/noteblock_pling.png b/thirtydollarwebsite/icons/noteblock_pling.png new file mode 100644 index 00000000..32c76808 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_pling.png differ diff --git a/thirtydollarwebsite/icons/noteblock_snare.png b/thirtydollarwebsite/icons/noteblock_snare.png new file mode 100644 index 00000000..1b638d70 Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_snare.png differ diff --git a/thirtydollarwebsite/icons/noteblock_xylophone.png b/thirtydollarwebsite/icons/noteblock_xylophone.png new file mode 100644 index 00000000..f9885cda Binary files /dev/null and b/thirtydollarwebsite/icons/noteblock_xylophone.png differ diff --git a/thirtydollarwebsite/icons/obama.png b/thirtydollarwebsite/icons/obama.png new file mode 100644 index 00000000..69949ded Binary files /dev/null and b/thirtydollarwebsite/icons/obama.png differ diff --git a/thirtydollarwebsite/icons/oof.png b/thirtydollarwebsite/icons/oof.png new file mode 100644 index 00000000..58d44ddd Binary files /dev/null and b/thirtydollarwebsite/icons/oof.png differ diff --git a/thirtydollarwebsite/icons/ook.png b/thirtydollarwebsite/icons/ook.png new file mode 100644 index 00000000..efbab026 Binary files /dev/null and b/thirtydollarwebsite/icons/ook.png differ diff --git a/thirtydollarwebsite/icons/op.png b/thirtydollarwebsite/icons/op.png new file mode 100644 index 00000000..a71fd4c2 Binary files /dev/null and b/thirtydollarwebsite/icons/op.png differ diff --git a/thirtydollarwebsite/icons/otto_happy.png b/thirtydollarwebsite/icons/otto_happy.png new file mode 100644 index 00000000..66e129ac Binary files /dev/null and b/thirtydollarwebsite/icons/otto_happy.png differ diff --git a/thirtydollarwebsite/icons/otto_off.png b/thirtydollarwebsite/icons/otto_off.png new file mode 100644 index 00000000..ecf0c544 Binary files /dev/null and b/thirtydollarwebsite/icons/otto_off.png differ diff --git a/thirtydollarwebsite/icons/otto_on.png b/thirtydollarwebsite/icons/otto_on.png new file mode 100644 index 00000000..5c30b7a7 Binary files /dev/null and b/thirtydollarwebsite/icons/otto_on.png differ diff --git a/thirtydollarwebsite/icons/otto_stress.png b/thirtydollarwebsite/icons/otto_stress.png new file mode 100644 index 00000000..86effb87 Binary files /dev/null and b/thirtydollarwebsite/icons/otto_stress.png differ diff --git a/thirtydollarwebsite/icons/pan.png b/thirtydollarwebsite/icons/pan.png new file mode 100644 index 00000000..3fd6ae11 Binary files /dev/null and b/thirtydollarwebsite/icons/pan.png differ diff --git a/thirtydollarwebsite/icons/perfectfail.png b/thirtydollarwebsite/icons/perfectfail.png new file mode 100644 index 00000000..0ff7deba Binary files /dev/null and b/thirtydollarwebsite/icons/perfectfail.png differ diff --git a/thirtydollarwebsite/icons/pingas.png b/thirtydollarwebsite/icons/pingas.png new file mode 100644 index 00000000..6e9a103e Binary files /dev/null and b/thirtydollarwebsite/icons/pingas.png differ diff --git a/thirtydollarwebsite/icons/preecho.png b/thirtydollarwebsite/icons/preecho.png new file mode 100644 index 00000000..7f7aa899 Binary files /dev/null and b/thirtydollarwebsite/icons/preecho.png differ diff --git a/thirtydollarwebsite/icons/puyo.png b/thirtydollarwebsite/icons/puyo.png new file mode 100644 index 00000000..cc8a0446 Binary files /dev/null and b/thirtydollarwebsite/icons/puyo.png differ diff --git a/thirtydollarwebsite/icons/rdclap.png b/thirtydollarwebsite/icons/rdclap.png new file mode 100644 index 00000000..e5da4938 Binary files /dev/null and b/thirtydollarwebsite/icons/rdclap.png differ diff --git a/thirtydollarwebsite/icons/rdmistake.png b/thirtydollarwebsite/icons/rdmistake.png new file mode 100644 index 00000000..8821a62c Binary files /dev/null and b/thirtydollarwebsite/icons/rdmistake.png differ diff --git a/thirtydollarwebsite/icons/ride2.png b/thirtydollarwebsite/icons/ride2.png new file mode 100644 index 00000000..c7f43517 Binary files /dev/null and b/thirtydollarwebsite/icons/ride2.png differ diff --git a/thirtydollarwebsite/icons/robtopphone.png b/thirtydollarwebsite/icons/robtopphone.png new file mode 100644 index 00000000..82285353 Binary files /dev/null and b/thirtydollarwebsite/icons/robtopphone.png differ diff --git a/thirtydollarwebsite/icons/samurai.png b/thirtydollarwebsite/icons/samurai.png new file mode 100644 index 00000000..6203f04f Binary files /dev/null and b/thirtydollarwebsite/icons/samurai.png differ diff --git a/thirtydollarwebsite/icons/sans_voice.png b/thirtydollarwebsite/icons/sans_voice.png new file mode 100644 index 00000000..24410b45 Binary files /dev/null and b/thirtydollarwebsite/icons/sans_voice.png differ diff --git a/thirtydollarwebsite/icons/shaker.png b/thirtydollarwebsite/icons/shaker.png new file mode 100644 index 00000000..f8d8f135 Binary files /dev/null and b/thirtydollarwebsite/icons/shaker.png differ diff --git a/thirtydollarwebsite/icons/shatter.png b/thirtydollarwebsite/icons/shatter.png new file mode 100644 index 00000000..45754909 Binary files /dev/null and b/thirtydollarwebsite/icons/shatter.png differ diff --git a/thirtydollarwebsite/icons/sidestick.png b/thirtydollarwebsite/icons/sidestick.png new file mode 100644 index 00000000..9e365a81 Binary files /dev/null and b/thirtydollarwebsite/icons/sidestick.png differ diff --git a/thirtydollarwebsite/icons/skipshot.png b/thirtydollarwebsite/icons/skipshot.png new file mode 100644 index 00000000..54395090 Binary files /dev/null and b/thirtydollarwebsite/icons/skipshot.png differ diff --git a/thirtydollarwebsite/icons/slip.png b/thirtydollarwebsite/icons/slip.png new file mode 100644 index 00000000..7ca53de4 Binary files /dev/null and b/thirtydollarwebsite/icons/slip.png differ diff --git a/thirtydollarwebsite/icons/sm64_hurt.png b/thirtydollarwebsite/icons/sm64_hurt.png new file mode 100644 index 00000000..5bac2ed1 Binary files /dev/null and b/thirtydollarwebsite/icons/sm64_hurt.png differ diff --git a/thirtydollarwebsite/icons/sm64_painting.png b/thirtydollarwebsite/icons/sm64_painting.png new file mode 100644 index 00000000..20a08dee Binary files /dev/null and b/thirtydollarwebsite/icons/sm64_painting.png differ diff --git a/thirtydollarwebsite/icons/smm_scream.png b/thirtydollarwebsite/icons/smm_scream.png new file mode 100644 index 00000000..1cd36c54 Binary files /dev/null and b/thirtydollarwebsite/icons/smm_scream.png differ diff --git a/thirtydollarwebsite/icons/smw_1up.png b/thirtydollarwebsite/icons/smw_1up.png new file mode 100644 index 00000000..c5dab611 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_1up.png differ diff --git a/thirtydollarwebsite/icons/smw_coin.png b/thirtydollarwebsite/icons/smw_coin.png new file mode 100644 index 00000000..8163b0b9 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_coin.png differ diff --git a/thirtydollarwebsite/icons/smw_kick.png b/thirtydollarwebsite/icons/smw_kick.png new file mode 100644 index 00000000..c8ccf380 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_kick.png differ diff --git a/thirtydollarwebsite/icons/smw_spinjump.png b/thirtydollarwebsite/icons/smw_spinjump.png new file mode 100644 index 00000000..c5c8a10d Binary files /dev/null and b/thirtydollarwebsite/icons/smw_spinjump.png differ diff --git a/thirtydollarwebsite/icons/smw_stomp.png b/thirtydollarwebsite/icons/smw_stomp.png new file mode 100644 index 00000000..c5c0b2e5 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_stomp.png differ diff --git a/thirtydollarwebsite/icons/smw_stomp2.png b/thirtydollarwebsite/icons/smw_stomp2.png new file mode 100644 index 00000000..8fe0ee24 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_stomp2.png differ diff --git a/thirtydollarwebsite/icons/smw_yoshi.png b/thirtydollarwebsite/icons/smw_yoshi.png new file mode 100644 index 00000000..69241cb2 Binary files /dev/null and b/thirtydollarwebsite/icons/smw_yoshi.png differ diff --git a/thirtydollarwebsite/icons/steve_oof.png b/thirtydollarwebsite/icons/steve_oof.png new file mode 100644 index 00000000..66ae0301 Binary files /dev/null and b/thirtydollarwebsite/icons/steve_oof.png differ diff --git a/thirtydollarwebsite/icons/stopposting.png b/thirtydollarwebsite/icons/stopposting.png new file mode 100644 index 00000000..e3eb70e4 Binary files /dev/null and b/thirtydollarwebsite/icons/stopposting.png differ diff --git a/thirtydollarwebsite/icons/subaluwa.png b/thirtydollarwebsite/icons/subaluwa.png new file mode 100644 index 00000000..dec707b6 Binary files /dev/null and b/thirtydollarwebsite/icons/subaluwa.png differ diff --git a/thirtydollarwebsite/icons/tab_actions.png b/thirtydollarwebsite/icons/tab_actions.png new file mode 100644 index 00000000..4d286cc6 Binary files /dev/null and b/thirtydollarwebsite/icons/tab_actions.png differ diff --git a/thirtydollarwebsite/icons/tab_decorations.png b/thirtydollarwebsite/icons/tab_decorations.png new file mode 100644 index 00000000..335f68bc Binary files /dev/null and b/thirtydollarwebsite/icons/tab_decorations.png differ diff --git a/thirtydollarwebsite/icons/tab_rooms.png b/thirtydollarwebsite/icons/tab_rooms.png new file mode 100644 index 00000000..74cf4268 Binary files /dev/null and b/thirtydollarwebsite/icons/tab_rooms.png differ diff --git a/thirtydollarwebsite/icons/tab_rows.png b/thirtydollarwebsite/icons/tab_rows.png new file mode 100644 index 00000000..4187c802 Binary files /dev/null and b/thirtydollarwebsite/icons/tab_rows.png differ diff --git a/thirtydollarwebsite/icons/tab_sounds.png b/thirtydollarwebsite/icons/tab_sounds.png new file mode 100644 index 00000000..25ecacdd Binary files /dev/null and b/thirtydollarwebsite/icons/tab_sounds.png differ diff --git a/thirtydollarwebsite/icons/taiko_don.png b/thirtydollarwebsite/icons/taiko_don.png new file mode 100644 index 00000000..7752eb3e Binary files /dev/null and b/thirtydollarwebsite/icons/taiko_don.png differ diff --git a/thirtydollarwebsite/icons/taiko_ka.png b/thirtydollarwebsite/icons/taiko_ka.png new file mode 100644 index 00000000..d6543b36 Binary files /dev/null and b/thirtydollarwebsite/icons/taiko_ka.png differ diff --git a/thirtydollarwebsite/icons/taunt.png b/thirtydollarwebsite/icons/taunt.png new file mode 100644 index 00000000..29692a53 Binary files /dev/null and b/thirtydollarwebsite/icons/taunt.png differ diff --git a/thirtydollarwebsite/icons/terraria_axe.png b/thirtydollarwebsite/icons/terraria_axe.png new file mode 100644 index 00000000..92ad4e14 Binary files /dev/null and b/thirtydollarwebsite/icons/terraria_axe.png differ diff --git a/thirtydollarwebsite/icons/terraria_guitar.png b/thirtydollarwebsite/icons/terraria_guitar.png new file mode 100644 index 00000000..1d21dd87 Binary files /dev/null and b/thirtydollarwebsite/icons/terraria_guitar.png differ diff --git a/thirtydollarwebsite/icons/terraria_pot.png b/thirtydollarwebsite/icons/terraria_pot.png new file mode 100644 index 00000000..20a0de6c Binary files /dev/null and b/thirtydollarwebsite/icons/terraria_pot.png differ diff --git a/thirtydollarwebsite/icons/terraria_reforge.png b/thirtydollarwebsite/icons/terraria_reforge.png new file mode 100644 index 00000000..7d56412b Binary files /dev/null and b/thirtydollarwebsite/icons/terraria_reforge.png differ diff --git a/thirtydollarwebsite/icons/terraria_star.png b/thirtydollarwebsite/icons/terraria_star.png new file mode 100644 index 00000000..41d468b3 Binary files /dev/null and b/thirtydollarwebsite/icons/terraria_star.png differ diff --git a/thirtydollarwebsite/icons/tf2_crit.png b/thirtydollarwebsite/icons/tf2_crit.png new file mode 100644 index 00000000..2e2fc267 Binary files /dev/null and b/thirtydollarwebsite/icons/tf2_crit.png differ diff --git a/thirtydollarwebsite/icons/thwomp.png b/thirtydollarwebsite/icons/thwomp.png new file mode 100644 index 00000000..5a53a001 Binary files /dev/null and b/thirtydollarwebsite/icons/thwomp.png differ diff --git a/thirtydollarwebsite/icons/toby.png b/thirtydollarwebsite/icons/toby.png new file mode 100644 index 00000000..056a9cfa Binary files /dev/null and b/thirtydollarwebsite/icons/toby.png differ diff --git a/thirtydollarwebsite/icons/tonk.png b/thirtydollarwebsite/icons/tonk.png new file mode 100644 index 00000000..a121f53d Binary files /dev/null and b/thirtydollarwebsite/icons/tonk.png differ diff --git a/thirtydollarwebsite/icons/ultrainstinct.png b/thirtydollarwebsite/icons/ultrainstinct.png new file mode 100644 index 00000000..336a6e4a Binary files /dev/null and b/thirtydollarwebsite/icons/ultrainstinct.png differ diff --git a/thirtydollarwebsite/icons/undertale_crack.png b/thirtydollarwebsite/icons/undertale_crack.png new file mode 100644 index 00000000..efe5bea3 Binary files /dev/null and b/thirtydollarwebsite/icons/undertale_crack.png differ diff --git a/thirtydollarwebsite/icons/undertale_encounter.png b/thirtydollarwebsite/icons/undertale_encounter.png new file mode 100644 index 00000000..11f8758e Binary files /dev/null and b/thirtydollarwebsite/icons/undertale_encounter.png differ diff --git a/thirtydollarwebsite/icons/undertale_hit.png b/thirtydollarwebsite/icons/undertale_hit.png new file mode 100644 index 00000000..e6cb1ffe Binary files /dev/null and b/thirtydollarwebsite/icons/undertale_hit.png differ diff --git a/thirtydollarwebsite/icons/vvvvvv_checkpoint.png b/thirtydollarwebsite/icons/vvvvvv_checkpoint.png new file mode 100644 index 00000000..00bd1be7 Binary files /dev/null and b/thirtydollarwebsite/icons/vvvvvv_checkpoint.png differ diff --git a/thirtydollarwebsite/icons/vvvvvv_flash.png b/thirtydollarwebsite/icons/vvvvvv_flash.png new file mode 100644 index 00000000..c6193512 Binary files /dev/null and b/thirtydollarwebsite/icons/vvvvvv_flash.png differ diff --git a/thirtydollarwebsite/icons/vvvvvv_flip.png b/thirtydollarwebsite/icons/vvvvvv_flip.png new file mode 100644 index 00000000..c17e50e3 Binary files /dev/null and b/thirtydollarwebsite/icons/vvvvvv_flip.png differ diff --git a/thirtydollarwebsite/icons/vvvvvv_hurt.png b/thirtydollarwebsite/icons/vvvvvv_hurt.png new file mode 100644 index 00000000..bcf8f34f Binary files /dev/null and b/thirtydollarwebsite/icons/vvvvvv_hurt.png differ diff --git a/thirtydollarwebsite/icons/whatsapp.png b/thirtydollarwebsite/icons/whatsapp.png new file mode 100644 index 00000000..4c0744ce Binary files /dev/null and b/thirtydollarwebsite/icons/whatsapp.png differ diff --git a/thirtydollarwebsite/icons/whipcrack.png b/thirtydollarwebsite/icons/whipcrack.png new file mode 100644 index 00000000..0d0bdc13 Binary files /dev/null and b/thirtydollarwebsite/icons/whipcrack.png differ diff --git a/thirtydollarwebsite/icons/yahoo.png b/thirtydollarwebsite/icons/yahoo.png new file mode 100644 index 00000000..3f46c755 Binary files /dev/null and b/thirtydollarwebsite/icons/yahoo.png differ diff --git a/thirtydollarwebsite/icons/yoda.png b/thirtydollarwebsite/icons/yoda.png new file mode 100644 index 00000000..7b109efb Binary files /dev/null and b/thirtydollarwebsite/icons/yoda.png differ diff --git a/thirtydollarwebsite/index.html b/thirtydollarwebsite/index.html new file mode 100644 index 00000000..99f529d0 --- /dev/null +++ b/thirtydollarwebsite/index.html @@ -0,0 +1,401 @@ + + + + + DON'T YOU LECTURE ME WITH YOUR THIRTY DOLLAR WEBSITE + + + + + + + + + + + + + + + +
+ +

DON'T YOU LECTURE ME WITH YOUR THIRTY DOLLAR WEBSITE

+ +
+ +
+
+
+

Sounds

+

+ + +

+

Left click to add sound

Right click to preview

+
+
+
+

Loading...

+

+

+
+
+ +
+
+

Actions

+

+ + +

+

Left click to add action

+
+
+ +
+ + + +

Right click to skip intro

+
+ +

Play

+
+ +
+ +

Clear

+
+ +
+
+
+ +
+
+

Sequence

+ +

Left click to remove

+

Right click to preview

+

Shift click to clone

+

Scroll to change pitch

+

Ctrl+scroll to change volume

+ +
+
+
+
+ + + + + + + +
+ +

.🗿

+
+ + + +
+

+ NEW: 12 new sounds for all your particle accelerator needs +

Check out my other silly projects over at gdcolon.com

+
+ +
+

Created by Colon :

+

I own pretty much nothing on this site please don't kill me

+
+ +
+ + +
+ +
+
+ +

PRO MODE

+
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/thirtydollarwebsite/sounds.json b/thirtydollarwebsite/sounds.json new file mode 100644 index 00000000..352a16d4 --- /dev/null +++ b/thirtydollarwebsite/sounds.json @@ -0,0 +1,222 @@ +[ + { "id": "_pause", "img": "empty", "name": "Silence", "source": "", "tags": ["note", "percussion"] }, + { "id": "boom", "name": "Vine Boom", "source": "", "tags": ["percussion"] }, + { "id": "bruh", "name": "Bruh Sound Effect #2", "source": "" }, + { "id": "bong", "name": "Bong", "source": "Taco Bell", "tags": ["note"] }, + { "id": "skeleton", "emoji": "💀", "name": "Skeleton", "source": "Minecraft" }, + { "id": "clap", "emoji": "👏", "name": "Reverb Clap", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "dog", "emoji": "🐶", "name": "what the dog doin", "source": "TonyBakerComedy" }, + { "id": "cave", "emoji": "👽", "name": "Cave Noise", "source": "Minecraft" }, + { "id": "bell", "emoji": "🔔", "name": "Bell", "source": "", "tags": ["note"] }, + { "id": "boink", "emoji": "💢", "name": "Boink", "source": "Rhythm Heaven" }, + { "id": "fart", "emoji": "💨", "name": "Reverb Fart", "source": "", "tags": ["percussion"] }, + { "id": "error", "emoji": "🚫", "name": "Error", "source": "Windows XP", "tags": ["note"] }, + + { "id": "kaching", "emoji": "💰", "name": "Kaching", "source": "" }, + { "id": "bonk", "emoji": "🏏", "name": "Bonk", "source": "", "tags": ["percussion"] }, + { "id": "bleep", "emoji": "🤬", "name": "Bleep", "source": "", "tags": ["note"] }, + { "id": "alarm", "emoji": "🚨", "name": "Alarm", "source": "" }, + { "id": "buzzer", "name": "Incorrect Buzzer", "source": "", "tags": ["note"] }, + { "id": "aah", "emoji": "🅰", "name": "aah", "source": "Zaytoven" }, + { "id": "e", "name": "E", "source": "Unknown" }, + { "id": "eight", "name": "8", "source": "The Stanley Parable" }, + { "id": "pizza", "emoji": "🍕", "name": "ayo the pizza here", "source": "CalebCity" }, + { "id": "augh", "emoji": "🐡", "name": "Pufferfish Augh", "source": "Yummyboy" }, + { "id": "quack", "emoji": "🦆", "name": "Quack", "source": "Mac OS" }, + { "id": "honk", "emoji": "🦢", "name": "Honk", "source": "Untitled Goose Game" }, + + { "id": "samsung", "emoji": "📲", "name": "Skyline Notification", "source": "Samsung" }, + { "id": "morningflower", "emoji": "🌄", "name": "Morning Flower", "source": "Samsung" }, + { "id": "shatter", "name": "Glass Shatter", "source": "", "tags": ["percussion"] }, + { "id": "noice", "emoji": "👌", "name": "Noice Click", "source": "Michael Rosen", "tags": ["percussion"] }, + { "id": "slap", "emoji": "🖐", "name": "Slap", "source": "", "tags": ["percussion"] }, + { "id": "krabs", "emoji": "🦀", "name": "Mr. Krabs Walking", "source": "SpongeBob SquarePants" }, + { "id": "wowowow", "emoji": "🚬", "name": "wowowowow", "source": "Kill Bill" }, + { "id": "whatsapp", "name": "Whistle Notification", "source": "Samsung" }, + { "id": "recordscratch", "emoji": "💿", "name": "Record Scratch", "source": "" }, + { "id": "pianoriff", "emoji": "🎹", "name": "Piano Riff", "source": "iPhone" }, + { "id": "alert", "emoji": "❗", "name": "Alert", "source": "Metal Gear Solid" }, + { "id": "slip", "name": "Slip", "source": "" }, + + { "id": "tada", "emoji": "🎉", "name": "Tada", "source": "Windows 3.1" }, + { "id": "pan", "name": "Frying Pan", "source": "Scott the Woz", "tags": ["percussion"] }, + { "id": "gun", "name": "Gun Reload", "source": "" }, + { "id": "hitmarker", "name": "Hitmarker", "source": "Call of Duty", "tags": ["percussion"] }, + { "id": "suspense", "emoji": "😱", "name": "Suspense", "source": "" }, + { "id": "yawn", "emoji": "🥱", "name": "Goofy Yawn", "source": ".mlkey(?)" }, + { "id": "nerdhorn", "emoji": "🤓", "name": "Old Car Horn", "source": "iPhone" }, + { "id": "babylaugh", "emoji": "👶", "name": "Baby Laugh", "source": "" }, + { "id": "airhorn", "name": "Airhorn", "source": "", "tags": ["note"] }, + { "id": "metalpipe", "name": "Metal Pipe", "source": "" }, + { "id": "boing", "name": "Boing", "source": "" }, + { "id": "explosion", "name": "Explosion", "source": "DELTARUNE" }, + + { "id": "dodgeball", "name": "Dodgeball", "source": "", "tags": ["percussion"] }, + { "id": "whipcrack", "name": "Whip Crack", "source": "", "tags": ["percussion"] }, + { "id": "boowomp", "name": "Boowomp", "source": "SpongeBob SquarePants" }, + { "id": "taiko_don", "name": "Don", "source": "Taiko no Tatsujin", "tags": ["percussion"] }, + { "id": "taiko_ka", "name": "Ka", "source": "Taiko no Tatsujin", "tags": ["percussion"] }, + { "id": "puyo", "name": "Puyo", "source": "Puyo Puyo", "tags": ["note"] }, + { "id": "robtopphone", "name": "Xylophone", "source": "", "tags": ["note"] }, + { "id": "waterphone", "emoji": "🎻", "name": "Waterphone", "source": "Hell's Kitchen" }, + { "id": "slapbass", "emoji": "🎸", "name": "Slap Bass", "source": "", "tags": ["note"] }, + { "id": "dimrainsynth", "name": "Synth", "source": "", "tags": ["note"] }, + { "id": "hoenn", "name": "Hoenn Trumpet", "source": "Pokémon", "tags": ["note"] }, + { "id": "zunpet", "emoji": "🎺", "name": "ZUNpet", "source": "Touhou", "tags": ["note"] }, + + { "id": "oof", "name": "Oof", "source": "Roblox" }, + { "id": "steve_oof", "name": "Oof", "source": "Minecraft" }, + { "id": "gnome", "name": "Gnome", "source": "You've Been Gnomed" }, + { "id": "nope", "name": "nope.avi", "source": "Team Fortress 2" }, + { "id": "tf2_crit", "name": "Critical Hit", "source": "Team Fortress 2", "tags": ["percussion"] }, + { "id": "mrbeast", "name": "MR BEAST", "source": "Freshy Kanal" }, + { "id": "obama", "name": "BARACK OBAMA", "source": "ERB", "tags": ["note"] }, + { "id": "op", "name": "op", "source": "Psy" }, + { "id": "SLAM", "name": "SLAM!", "source": "Space Jam" }, + { "id": "stopposting", "name": "Ding", "source": "biggayrapper" }, + { "id": "21", "name": "21", "source": "DREHYPEMSU" }, + { "id": "americano", "name": "dog dancing we no speak americano", "source": "Unknown" }, + + { "id": "taunt", "name": "Taunt", "source": "Pizza Tower", "tags": ["note"] }, + { "id": "subaluwa", "name": "Subaluwa", "source": "Ed, Edd n Eddy" }, + { "id": "necoarc", "name": "Neco-Arc", "source": "Carnival Phantasm" }, + { "id": "samurai", "name": "Samurai Techno", "source": "Rhythm Doctor", "tags": ["note"] }, + { "id": "flipnote", "name": "Frog", "source": "Flipnote Studio" }, + { "id": "familyguy", "name": "Family Guy Intro", "source": "Family Guy", "tags": ["note"] }, + { "id": "pingas", "name": "PINGAS", "source": "AoStH" }, + { "id": "yoda", "name": "Lego Yoda Death", "source": "LEGO Star Wars" }, + { "id": "hehehehaw", "name": "HE HE HE HAW", "source": "Clash Royale" }, + { "id": "ultrainstinct", "name": "Ultra Instinct", "source": "Dragon Ball Z", "tags": ["note"] }, + { "id": "granddad", "name": "GRAND DAD", "source": "Vargskelethor Joel" }, + { "id": "morshu", "name": "Morshu MMMMMMM", "source": "Link: The Faces of Evil", "tags": ["note"] }, + + { "id": "smw_coin", "name": "Coin", "source": "Super Mario World" }, + { "id": "smw_1up", "name": "1-Up", "source": "Super Mario World" }, + { "id": "smw_spinjump", "name": "Spin Jump", "source": "Super Mario World" }, + { "id": "smw_stomp2", "name": "Stomp", "source": "Super Mario World", "tags": ["percussion"] }, + { "id": "smw_kick", "name": "Kick", "source": "Super Mario World" }, + { "id": "smw_stomp", "name": "Boss Stomp", "source": "Super Mario World" }, + { "id": "yahoo", "name": "Long Jump", "source": "Super Mario 64" }, + { "id": "sm64_hurt", "name": "Damage", "source": "Super Mario 64" }, + { "id": "thwomp", "name": "Thwomp", "source": "Super Mario 64" }, + { "id": "bup", "name": "Bup", "source": "Mario Kart 64", "tags": ["note"] }, + { "id": "sm64_painting", "name": "Painting", "source": "Super Mario 64", "tags": ["percussion"] }, + { "id": "smm_scream", "name": "Scream", "source": "Super Mario Maker" }, + + { "id": "mariopaint_mario", "name": "Mario", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_luigi", "name": "Steel Drum", "source": "Super Mario World", "tags": ["note"] }, + { "id": "smw_yoshi", "name": "Yoshi", "source": "Mario Paint" }, + { "id": "mariopaint_star", "name": "Star", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_flower", "name": "Flower", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_gameboy", "name": "Gameboy", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_dog", "name": "Dog", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_cat", "name": "Cat", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_swan", "name": "Swan", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_baby", "name": "Baby", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_plane", "name": "Plane", "source": "Mario Paint", "tags": ["note"] }, + { "id": "mariopaint_car", "name": "Car", "source": "Mario Paint", "tags": ["note"] }, + + { "id": "shaker", "name": "Shaker", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "kick", "emoji": "🥁", "name": "Kick", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "hammer", "name": "Hammer", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "tom", "emoji": "🪘", "name": "Tom Drum", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "sidestick", "name": "Sidestick", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "ride2", "name": "Ride", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "buttonpop", "name": "Pop", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "skipshot", "name": "Skipshot", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "otto_on", "name": "Otto - Enabled", "source": "Rhythm Doctor" }, + { "id": "otto_off", "name": "Otto - Disabled", "source": "Rhythm Doctor" }, + { "id": "otto_happy", "name": "Otto - Happy", "source": "Rhythm Doctor" }, + { "id": "otto_stress", "name": "Otto - Stressed", "source": "A Dance of Fire And Ice" }, + + { "id": "tab_sounds", "name": "Sounds Tab", "source": "Rhythm Doctor", "tags": ["note"] }, + { "id": "tab_rows", "name": "Rows Tab", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "tab_actions", "name": "Actions Tab", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "tab_decorations", "name": "Decorations Tab", "source": "Rhythm Doctor" }, + { "id": "tab_rooms", "name": "Rooms Tab", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "preecho", "name": "Pre-echo Clap", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "tonk", "name": "Tonk", "source": "Rhythm Doctor" }, + { "id": "rdclap", "name": "Clap Hit", "source": "Rhythm Doctor", "tags": ["percussion"] }, + { "id": "rdmistake", "name": "Mistake", "source": "Rhythm Doctor" }, + { "id": "midspin", "name": "Midspin Clack", "source": "A Dance of Fire and Ice", "tags": ["percussion"] }, + { "id": "adofai_fire", "name": "Fire Midspin", "source": "A Dance of Fire and Ice", "tags": ["percussion"] }, + { "id": "adofai_ice", "name": "Ice Midspin", "source": "A Dance of Fire and Ice", "tags": ["percussion"] }, + + { "id": "adofaikick", "name": "Kick", "source": "A Dance of Fire and Ice", "tags": ["percussion"] }, + { "id": "adofaicymbal", "name": "Level Complete", "source": "A Dance of Fire and Ice", "tags": ["percussion"] }, + { "id": "cowbell", "name": "Cowbell", "source": "Rhythm Heaven", "tags": ["percussion"] }, + { "id": "karateman_throw", "name": "Karate Man - Toss", "source": "Rhythm Heaven", "tags": ["percussion"] }, + { "id": "karateman_offbeat", "name": "Karate Man - Offbeat Toss", "source": "Rhythm Heaven", "tags": ["percussion"] }, + { "id": "karateman_hit", "name": "Karate Man - Hit", "source": "Rhythm Heaven", "tags": ["percussion"] }, + { "id": "karateman_bulb", "name": "Karate Man - Bulb", "source": "Rhythm Heaven", "tags": ["percussion"] }, + { "id": "ook", "name": "Ook", "source": "Rhythm Heaven" }, + { "id": "choruskid", "name": "Chorus Kid", "source": "Rhythm Heaven", "tags": ["note"] }, + { "id": "builttoscale", "name": "Widget", "source": "Rhythm Heaven", "tags": ["note"] }, + { "id": "perfectfail", "name": "Perfect Failed", "source": "Rhythm Heaven" }, + { "id": "skillstar", "emoji": "🌟", "name": "Skill Star", "source": "Rhythm Heaven" }, + + { "id": "fnf_left", "name": "Left", "source": "Friday Night Funkin'", "tags": ["note"] }, + { "id": "fnf_down", "name": "Down", "source": "Friday Night Funkin'", "tags": ["note"] }, + { "id": "fnf_up", "name": "Up", "source": "Friday Night Funkin'", "tags": ["note"] }, + { "id": "fnf_right", "name": "Right", "source": "Friday Night Funkin'", "tags": ["note"] }, + { "id": "fnf_death", "name": "Fail", "source": "Friday Night Funkin'" }, + { "id": "gdcrash", "name": "Crash", "source": "Geometry Dash" }, + { "id": "gdcrash_orbs", "name": "Crash + Orbs", "source": "Geometry Dash" }, + { "id": "gd_coin", "name": "Secret Coin", "source": "Geometry Dash" }, + { "id": "gd_orbs", "name": "Mana Orbs", "source": "Geometry Dash" }, + { "id": "gd_diamonds", "name": "Diamond", "source": "Geometry Dash" }, + { "id": "gd_quit", "name": "Quit", "source": "Geometry Dash", "tags": ["note"] }, + { "id": "bwomp", "name": "Blast Processing", "source": "Geometry Dash", "tags": ["note"] }, + + { "id": "undertale_hit", "name": "Damage", "source": "UNDERTALE", "tags": ["percussion"] }, + { "id": "undertale_crack", "name": "Game Over", "source": "UNDERTALE", "tags": ["percussion"] }, + { "id": "sans_voice", "name": "sans", "source": "UNDERTALE" }, + { "id": "megalovania", "name": "Megalovania", "source": "UNDERTALE" }, + { "id": "megalovania_note", "emoji": "🦴", "name": "Megalovania Note", "source": "UNDERTALE", "tags": ["note"] }, + { "id": "undertale_encounter", "name": "Encounter", "source": "UNDERTALE" }, + { "id": "toby", "name": "Bark", "source": "UNDERTALE" }, + { "id": "gaster", "name": "Vanish", "source": "UNDERTALE" }, + { "id": "lancersplat", "name": "Lancer Splat", "source": "DELTARUNE", "tags": ["percussion"] }, + { "id": "isaac_hurt", "name": "Damage", "source": "The Binding of Isaac: Rebirth" }, + { "id": "isaac_dead", "name": "Death", "source": "The Binding of Isaac: Rebirth" }, + { "id": "isaac_mantle", "name": "Holy Mantle", "source": "The Binding of Isaac: Repentance", "tags": ["percussion"] }, + + { "id": "BABA", "name": "MOVE", "source": "Baba Is You" }, + { "id": "YOU", "name": "RULE", "source": "Baba Is You", "tags": ["note"] }, + { "id": "DEFEAT", "name": "DEFEAT", "source": "Baba Is You", "tags": ["percussion"] }, + { "id": "vvvvvv_flip", "name": "Flip", "source": "VVVVVV" }, + { "id": "vvvvvv_hurt", "name": "Sad", "source": "VVVVVV" }, + { "id": "vvvvvv_checkpoint", "name": "Checkpoint", "source": "VVVVVV" }, + { "id": "vvvvvv_flash", "name": "Flash", "source": "VVVVVV", "tags": ["percussion"] }, + { "id": "terraria_star", "name": "Magic", "source": "Terraria" }, + { "id": "terraria_pot", "name": "Shatter", "source": "Terraria" }, + { "id": "terraria_reforge", "name": "Reforge", "source": "Terraria" }, + { "id": "terraria_guitar", "name": "Stellar Tune", "source": "Terraria", "tags": ["note"] }, + { "id": "terraria_axe", "name": "The Axe", "source": "Terraria", "tags": ["note"] }, + + { "id": "celeste_dash", "name": "Dash", "source": "Celeste", "tags": ["percussion"] }, + { "id": "celeste_death", "name": "Death", "source": "Celeste", "tags": ["percussion"] }, + { "id": "celeste_spring", "name": "Spring", "source": "Celeste", "tags": ["percussion"] }, + { "id": "celeste_diamond", "name": "Dash Gem", "source": "Celeste", "tags": ["percussion"] }, + { "id": "amogus_emergency", "name": "Emergency Meeting", "source": "Among Us", "tags": ["note"] }, + { "id": "amogus_kill", "name": "Kill", "source": "Among Us", "tags": ["percussion"] }, + { "id": "amongus", "name": "AMONG US", "source": "Unknown" }, + { "id": "amongdrip", "name": "Among Drip", "source": "Leonz", "tags": ["note"] }, + { "id": "amogus", "name": "Amogus", "source": "Unknown" }, + { "id": "minecraft_explosion", "name": "Explosion", "source": "Minecraft" }, + { "id": "minecraft_anvil", "name": "Anvil", "source": "Minecraft" }, + { "id": "minecraft_bell", "name": "Bell", "source": "Minecraft", "tags": ["note"] }, + + { "id": "noteblock_harp", "name": "Note Block - Harp", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_bass", "name": "Note Block - Bass", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_snare", "name": "Note Block - Snare", "source": "Minecraft", "tags": ["percussion"] }, + { "id": "noteblock_click", "name": "Note Block - Hat", "source": "Minecraft", "tags": ["percussion"] }, + { "id": "noteblock_bell", "name": "Note Block - Bell", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_banjo", "name": "Note Block - Banjo", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_bit", "name": "Note Block - Bit", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_chime", "name": "Note Block - Chime", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_xylophone", "name": "Note Block - Xylophone", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_guitar", "name": "Note Block - Guitar", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_flute", "name": "Note Block - Flute", "source": "Minecraft", "tags": ["note"] }, + { "id": "noteblock_pling", "name": "Note Block - Pling", "source": "Minecraft", "tags": ["note"] } +] \ No newline at end of file diff --git a/thirtydollarwebsite/sounds/21.wav b/thirtydollarwebsite/sounds/21.wav new file mode 100644 index 00000000..a607f239 Binary files /dev/null and b/thirtydollarwebsite/sounds/21.wav differ diff --git a/thirtydollarwebsite/sounds/BABA.wav b/thirtydollarwebsite/sounds/BABA.wav new file mode 100644 index 00000000..54d6bb3c Binary files /dev/null and b/thirtydollarwebsite/sounds/BABA.wav differ diff --git a/thirtydollarwebsite/sounds/DEFEAT.wav b/thirtydollarwebsite/sounds/DEFEAT.wav new file mode 100644 index 00000000..3286e4a3 Binary files /dev/null and b/thirtydollarwebsite/sounds/DEFEAT.wav differ diff --git a/thirtydollarwebsite/sounds/SLAM.wav b/thirtydollarwebsite/sounds/SLAM.wav new file mode 100644 index 00000000..b30fcdad Binary files /dev/null and b/thirtydollarwebsite/sounds/SLAM.wav differ diff --git a/thirtydollarwebsite/sounds/YOU.wav b/thirtydollarwebsite/sounds/YOU.wav new file mode 100644 index 00000000..e33dff3f Binary files /dev/null and b/thirtydollarwebsite/sounds/YOU.wav differ diff --git a/thirtydollarwebsite/sounds/_pause.wav b/thirtydollarwebsite/sounds/_pause.wav new file mode 100644 index 00000000..5c55217d Binary files /dev/null and b/thirtydollarwebsite/sounds/_pause.wav differ diff --git a/thirtydollarwebsite/sounds/aah.wav b/thirtydollarwebsite/sounds/aah.wav new file mode 100644 index 00000000..ecc5903e Binary files /dev/null and b/thirtydollarwebsite/sounds/aah.wav differ diff --git a/thirtydollarwebsite/sounds/adofai_fire.wav b/thirtydollarwebsite/sounds/adofai_fire.wav new file mode 100644 index 00000000..af9746cd Binary files /dev/null and b/thirtydollarwebsite/sounds/adofai_fire.wav differ diff --git a/thirtydollarwebsite/sounds/adofai_ice.wav b/thirtydollarwebsite/sounds/adofai_ice.wav new file mode 100644 index 00000000..8e0cbff1 Binary files /dev/null and b/thirtydollarwebsite/sounds/adofai_ice.wav differ diff --git a/thirtydollarwebsite/sounds/adofaicymbal.wav b/thirtydollarwebsite/sounds/adofaicymbal.wav new file mode 100644 index 00000000..66261ad5 Binary files /dev/null and b/thirtydollarwebsite/sounds/adofaicymbal.wav differ diff --git a/thirtydollarwebsite/sounds/adofaikick.wav b/thirtydollarwebsite/sounds/adofaikick.wav new file mode 100644 index 00000000..d5ad3577 Binary files /dev/null and b/thirtydollarwebsite/sounds/adofaikick.wav differ diff --git a/thirtydollarwebsite/sounds/airhorn.wav b/thirtydollarwebsite/sounds/airhorn.wav new file mode 100644 index 00000000..d01b1753 Binary files /dev/null and b/thirtydollarwebsite/sounds/airhorn.wav differ diff --git a/thirtydollarwebsite/sounds/alarm.wav b/thirtydollarwebsite/sounds/alarm.wav new file mode 100644 index 00000000..cbad48a5 Binary files /dev/null and b/thirtydollarwebsite/sounds/alarm.wav differ diff --git a/thirtydollarwebsite/sounds/alert.wav b/thirtydollarwebsite/sounds/alert.wav new file mode 100644 index 00000000..c6c98892 Binary files /dev/null and b/thirtydollarwebsite/sounds/alert.wav differ diff --git a/thirtydollarwebsite/sounds/americano.wav b/thirtydollarwebsite/sounds/americano.wav new file mode 100644 index 00000000..acd08537 Binary files /dev/null and b/thirtydollarwebsite/sounds/americano.wav differ diff --git a/thirtydollarwebsite/sounds/amogus.wav b/thirtydollarwebsite/sounds/amogus.wav new file mode 100644 index 00000000..5fa96b2e Binary files /dev/null and b/thirtydollarwebsite/sounds/amogus.wav differ diff --git a/thirtydollarwebsite/sounds/amogus_emergency.wav b/thirtydollarwebsite/sounds/amogus_emergency.wav new file mode 100644 index 00000000..8fce2bf5 Binary files /dev/null and b/thirtydollarwebsite/sounds/amogus_emergency.wav differ diff --git a/thirtydollarwebsite/sounds/amogus_kill.wav b/thirtydollarwebsite/sounds/amogus_kill.wav new file mode 100644 index 00000000..7cf3e0f7 Binary files /dev/null and b/thirtydollarwebsite/sounds/amogus_kill.wav differ diff --git a/thirtydollarwebsite/sounds/amongdrip.wav b/thirtydollarwebsite/sounds/amongdrip.wav new file mode 100644 index 00000000..40f12e27 Binary files /dev/null and b/thirtydollarwebsite/sounds/amongdrip.wav differ diff --git a/thirtydollarwebsite/sounds/amongus.wav b/thirtydollarwebsite/sounds/amongus.wav new file mode 100644 index 00000000..82de2ce5 Binary files /dev/null and b/thirtydollarwebsite/sounds/amongus.wav differ diff --git a/thirtydollarwebsite/sounds/augh.wav b/thirtydollarwebsite/sounds/augh.wav new file mode 100644 index 00000000..4f75da8b Binary files /dev/null and b/thirtydollarwebsite/sounds/augh.wav differ diff --git a/thirtydollarwebsite/sounds/babylaugh.wav b/thirtydollarwebsite/sounds/babylaugh.wav new file mode 100644 index 00000000..6eb35a70 Binary files /dev/null and b/thirtydollarwebsite/sounds/babylaugh.wav differ diff --git a/thirtydollarwebsite/sounds/bell.wav b/thirtydollarwebsite/sounds/bell.wav new file mode 100644 index 00000000..dcd0a1db Binary files /dev/null and b/thirtydollarwebsite/sounds/bell.wav differ diff --git a/thirtydollarwebsite/sounds/bleep.wav b/thirtydollarwebsite/sounds/bleep.wav new file mode 100644 index 00000000..1ca535fc Binary files /dev/null and b/thirtydollarwebsite/sounds/bleep.wav differ diff --git a/thirtydollarwebsite/sounds/boing.wav b/thirtydollarwebsite/sounds/boing.wav new file mode 100644 index 00000000..2d3e1984 Binary files /dev/null and b/thirtydollarwebsite/sounds/boing.wav differ diff --git a/thirtydollarwebsite/sounds/boink(1).wav b/thirtydollarwebsite/sounds/boink(1).wav new file mode 100644 index 00000000..59d56749 Binary files /dev/null and b/thirtydollarwebsite/sounds/boink(1).wav differ diff --git a/thirtydollarwebsite/sounds/boink.wav b/thirtydollarwebsite/sounds/boink.wav new file mode 100644 index 00000000..59d56749 Binary files /dev/null and b/thirtydollarwebsite/sounds/boink.wav differ diff --git a/thirtydollarwebsite/sounds/bong.wav b/thirtydollarwebsite/sounds/bong.wav new file mode 100644 index 00000000..1b6ce4c8 Binary files /dev/null and b/thirtydollarwebsite/sounds/bong.wav differ diff --git a/thirtydollarwebsite/sounds/bonk.wav b/thirtydollarwebsite/sounds/bonk.wav new file mode 100644 index 00000000..3df64fd0 Binary files /dev/null and b/thirtydollarwebsite/sounds/bonk.wav differ diff --git a/thirtydollarwebsite/sounds/boom.wav b/thirtydollarwebsite/sounds/boom.wav new file mode 100644 index 00000000..7dfebb12 Binary files /dev/null and b/thirtydollarwebsite/sounds/boom.wav differ diff --git a/thirtydollarwebsite/sounds/boowomp.wav b/thirtydollarwebsite/sounds/boowomp.wav new file mode 100644 index 00000000..90060173 Binary files /dev/null and b/thirtydollarwebsite/sounds/boowomp.wav differ diff --git a/thirtydollarwebsite/sounds/bruh.wav b/thirtydollarwebsite/sounds/bruh.wav new file mode 100644 index 00000000..45542f13 Binary files /dev/null and b/thirtydollarwebsite/sounds/bruh.wav differ diff --git a/thirtydollarwebsite/sounds/builttoscale.wav b/thirtydollarwebsite/sounds/builttoscale.wav new file mode 100644 index 00000000..5d5f4133 Binary files /dev/null and b/thirtydollarwebsite/sounds/builttoscale.wav differ diff --git a/thirtydollarwebsite/sounds/bup.wav b/thirtydollarwebsite/sounds/bup.wav new file mode 100644 index 00000000..258fa95e Binary files /dev/null and b/thirtydollarwebsite/sounds/bup.wav differ diff --git a/thirtydollarwebsite/sounds/buttonpop.wav b/thirtydollarwebsite/sounds/buttonpop.wav new file mode 100644 index 00000000..2cd78271 Binary files /dev/null and b/thirtydollarwebsite/sounds/buttonpop.wav differ diff --git a/thirtydollarwebsite/sounds/buzzer.wav b/thirtydollarwebsite/sounds/buzzer.wav new file mode 100644 index 00000000..2e29cdc8 Binary files /dev/null and b/thirtydollarwebsite/sounds/buzzer.wav differ diff --git a/thirtydollarwebsite/sounds/bwomp.wav b/thirtydollarwebsite/sounds/bwomp.wav new file mode 100644 index 00000000..911decde Binary files /dev/null and b/thirtydollarwebsite/sounds/bwomp.wav differ diff --git a/thirtydollarwebsite/sounds/cave.wav b/thirtydollarwebsite/sounds/cave.wav new file mode 100644 index 00000000..c43be6b7 Binary files /dev/null and b/thirtydollarwebsite/sounds/cave.wav differ diff --git a/thirtydollarwebsite/sounds/celeste_dash.wav b/thirtydollarwebsite/sounds/celeste_dash.wav new file mode 100644 index 00000000..4dbad3f5 Binary files /dev/null and b/thirtydollarwebsite/sounds/celeste_dash.wav differ diff --git a/thirtydollarwebsite/sounds/celeste_death.wav b/thirtydollarwebsite/sounds/celeste_death.wav new file mode 100644 index 00000000..7faefa0d Binary files /dev/null and b/thirtydollarwebsite/sounds/celeste_death.wav differ diff --git a/thirtydollarwebsite/sounds/celeste_diamond.wav b/thirtydollarwebsite/sounds/celeste_diamond.wav new file mode 100644 index 00000000..99b5a1d5 Binary files /dev/null and b/thirtydollarwebsite/sounds/celeste_diamond.wav differ diff --git a/thirtydollarwebsite/sounds/celeste_spring.wav b/thirtydollarwebsite/sounds/celeste_spring.wav new file mode 100644 index 00000000..a5e2b6d2 Binary files /dev/null and b/thirtydollarwebsite/sounds/celeste_spring.wav differ diff --git a/thirtydollarwebsite/sounds/choruskid.wav b/thirtydollarwebsite/sounds/choruskid.wav new file mode 100644 index 00000000..7d6c0165 Binary files /dev/null and b/thirtydollarwebsite/sounds/choruskid.wav differ diff --git a/thirtydollarwebsite/sounds/clap.wav b/thirtydollarwebsite/sounds/clap.wav new file mode 100644 index 00000000..bc30e645 Binary files /dev/null and b/thirtydollarwebsite/sounds/clap.wav differ diff --git a/thirtydollarwebsite/sounds/cowbell.wav b/thirtydollarwebsite/sounds/cowbell.wav new file mode 100644 index 00000000..39a6aee2 Binary files /dev/null and b/thirtydollarwebsite/sounds/cowbell.wav differ diff --git a/thirtydollarwebsite/sounds/dimrainsynth.wav b/thirtydollarwebsite/sounds/dimrainsynth.wav new file mode 100644 index 00000000..36f37de2 Binary files /dev/null and b/thirtydollarwebsite/sounds/dimrainsynth.wav differ diff --git a/thirtydollarwebsite/sounds/dodgeball.wav b/thirtydollarwebsite/sounds/dodgeball.wav new file mode 100644 index 00000000..ec950e34 Binary files /dev/null and b/thirtydollarwebsite/sounds/dodgeball.wav differ diff --git a/thirtydollarwebsite/sounds/dog.wav b/thirtydollarwebsite/sounds/dog.wav new file mode 100644 index 00000000..489cb29c Binary files /dev/null and b/thirtydollarwebsite/sounds/dog.wav differ diff --git a/thirtydollarwebsite/sounds/e.wav b/thirtydollarwebsite/sounds/e.wav new file mode 100644 index 00000000..b2ec2529 Binary files /dev/null and b/thirtydollarwebsite/sounds/e.wav differ diff --git a/thirtydollarwebsite/sounds/eight.wav b/thirtydollarwebsite/sounds/eight.wav new file mode 100644 index 00000000..19f51831 Binary files /dev/null and b/thirtydollarwebsite/sounds/eight.wav differ diff --git a/thirtydollarwebsite/sounds/error.wav b/thirtydollarwebsite/sounds/error.wav new file mode 100644 index 00000000..df684959 Binary files /dev/null and b/thirtydollarwebsite/sounds/error.wav differ diff --git a/thirtydollarwebsite/sounds/explosion.wav b/thirtydollarwebsite/sounds/explosion.wav new file mode 100644 index 00000000..ca3310dd Binary files /dev/null and b/thirtydollarwebsite/sounds/explosion.wav differ diff --git a/thirtydollarwebsite/sounds/familyguy.wav b/thirtydollarwebsite/sounds/familyguy.wav new file mode 100644 index 00000000..430f7032 Binary files /dev/null and b/thirtydollarwebsite/sounds/familyguy.wav differ diff --git a/thirtydollarwebsite/sounds/fart.wav b/thirtydollarwebsite/sounds/fart.wav new file mode 100644 index 00000000..0947e59c Binary files /dev/null and b/thirtydollarwebsite/sounds/fart.wav differ diff --git a/thirtydollarwebsite/sounds/flipnote.wav b/thirtydollarwebsite/sounds/flipnote.wav new file mode 100644 index 00000000..0126707c Binary files /dev/null and b/thirtydollarwebsite/sounds/flipnote.wav differ diff --git a/thirtydollarwebsite/sounds/fnf_death.wav b/thirtydollarwebsite/sounds/fnf_death.wav new file mode 100644 index 00000000..89312eda Binary files /dev/null and b/thirtydollarwebsite/sounds/fnf_death.wav differ diff --git a/thirtydollarwebsite/sounds/fnf_down.wav b/thirtydollarwebsite/sounds/fnf_down.wav new file mode 100644 index 00000000..0ecaea89 Binary files /dev/null and b/thirtydollarwebsite/sounds/fnf_down.wav differ diff --git a/thirtydollarwebsite/sounds/fnf_left.wav b/thirtydollarwebsite/sounds/fnf_left.wav new file mode 100644 index 00000000..e867244e Binary files /dev/null and b/thirtydollarwebsite/sounds/fnf_left.wav differ diff --git a/thirtydollarwebsite/sounds/fnf_right.wav b/thirtydollarwebsite/sounds/fnf_right.wav new file mode 100644 index 00000000..6221e12a Binary files /dev/null and b/thirtydollarwebsite/sounds/fnf_right.wav differ diff --git a/thirtydollarwebsite/sounds/fnf_up.wav b/thirtydollarwebsite/sounds/fnf_up.wav new file mode 100644 index 00000000..15e90d42 Binary files /dev/null and b/thirtydollarwebsite/sounds/fnf_up.wav differ diff --git a/thirtydollarwebsite/sounds/gaster.wav b/thirtydollarwebsite/sounds/gaster.wav new file mode 100644 index 00000000..6f9b5341 Binary files /dev/null and b/thirtydollarwebsite/sounds/gaster.wav differ diff --git a/thirtydollarwebsite/sounds/gd_coin.wav b/thirtydollarwebsite/sounds/gd_coin.wav new file mode 100644 index 00000000..90a1b91c Binary files /dev/null and b/thirtydollarwebsite/sounds/gd_coin.wav differ diff --git a/thirtydollarwebsite/sounds/gd_diamonds.wav b/thirtydollarwebsite/sounds/gd_diamonds.wav new file mode 100644 index 00000000..64eef3f5 Binary files /dev/null and b/thirtydollarwebsite/sounds/gd_diamonds.wav differ diff --git a/thirtydollarwebsite/sounds/gd_orbs.wav b/thirtydollarwebsite/sounds/gd_orbs.wav new file mode 100644 index 00000000..f08bb5ee Binary files /dev/null and b/thirtydollarwebsite/sounds/gd_orbs.wav differ diff --git a/thirtydollarwebsite/sounds/gd_quit.wav b/thirtydollarwebsite/sounds/gd_quit.wav new file mode 100644 index 00000000..dcda0deb Binary files /dev/null and b/thirtydollarwebsite/sounds/gd_quit.wav differ diff --git a/thirtydollarwebsite/sounds/gdcrash.wav b/thirtydollarwebsite/sounds/gdcrash.wav new file mode 100644 index 00000000..671389da Binary files /dev/null and b/thirtydollarwebsite/sounds/gdcrash.wav differ diff --git a/thirtydollarwebsite/sounds/gdcrash_orbs.wav b/thirtydollarwebsite/sounds/gdcrash_orbs.wav new file mode 100644 index 00000000..8e095bce Binary files /dev/null and b/thirtydollarwebsite/sounds/gdcrash_orbs.wav differ diff --git a/thirtydollarwebsite/sounds/gnome.wav b/thirtydollarwebsite/sounds/gnome.wav new file mode 100644 index 00000000..5a83cb07 Binary files /dev/null and b/thirtydollarwebsite/sounds/gnome.wav differ diff --git a/thirtydollarwebsite/sounds/granddad.wav b/thirtydollarwebsite/sounds/granddad.wav new file mode 100644 index 00000000..4d13e261 Binary files /dev/null and b/thirtydollarwebsite/sounds/granddad.wav differ diff --git a/thirtydollarwebsite/sounds/gun.wav b/thirtydollarwebsite/sounds/gun.wav new file mode 100644 index 00000000..fe2f5bd9 Binary files /dev/null and b/thirtydollarwebsite/sounds/gun.wav differ diff --git a/thirtydollarwebsite/sounds/hammer.wav b/thirtydollarwebsite/sounds/hammer.wav new file mode 100644 index 00000000..b4bfae45 Binary files /dev/null and b/thirtydollarwebsite/sounds/hammer.wav differ diff --git a/thirtydollarwebsite/sounds/hehehehaw.wav b/thirtydollarwebsite/sounds/hehehehaw.wav new file mode 100644 index 00000000..684fe0af Binary files /dev/null and b/thirtydollarwebsite/sounds/hehehehaw.wav differ diff --git a/thirtydollarwebsite/sounds/hitmarker.wav b/thirtydollarwebsite/sounds/hitmarker.wav new file mode 100644 index 00000000..2f0f9e29 Binary files /dev/null and b/thirtydollarwebsite/sounds/hitmarker.wav differ diff --git a/thirtydollarwebsite/sounds/hoenn.wav b/thirtydollarwebsite/sounds/hoenn.wav new file mode 100644 index 00000000..48017af6 Binary files /dev/null and b/thirtydollarwebsite/sounds/hoenn.wav differ diff --git a/thirtydollarwebsite/sounds/honk.wav b/thirtydollarwebsite/sounds/honk.wav new file mode 100644 index 00000000..31450ca5 Binary files /dev/null and b/thirtydollarwebsite/sounds/honk.wav differ diff --git a/thirtydollarwebsite/sounds/isaac_dead.wav b/thirtydollarwebsite/sounds/isaac_dead.wav new file mode 100644 index 00000000..5c49067b Binary files /dev/null and b/thirtydollarwebsite/sounds/isaac_dead.wav differ diff --git a/thirtydollarwebsite/sounds/isaac_hurt.wav b/thirtydollarwebsite/sounds/isaac_hurt.wav new file mode 100644 index 00000000..c423784f Binary files /dev/null and b/thirtydollarwebsite/sounds/isaac_hurt.wav differ diff --git a/thirtydollarwebsite/sounds/isaac_mantle.wav b/thirtydollarwebsite/sounds/isaac_mantle.wav new file mode 100644 index 00000000..b8dccf79 Binary files /dev/null and b/thirtydollarwebsite/sounds/isaac_mantle.wav differ diff --git a/thirtydollarwebsite/sounds/kaching.wav b/thirtydollarwebsite/sounds/kaching.wav new file mode 100644 index 00000000..7a96ca20 Binary files /dev/null and b/thirtydollarwebsite/sounds/kaching.wav differ diff --git a/thirtydollarwebsite/sounds/karateman_bulb.wav b/thirtydollarwebsite/sounds/karateman_bulb.wav new file mode 100644 index 00000000..e357c83d Binary files /dev/null and b/thirtydollarwebsite/sounds/karateman_bulb.wav differ diff --git a/thirtydollarwebsite/sounds/karateman_hit.wav b/thirtydollarwebsite/sounds/karateman_hit.wav new file mode 100644 index 00000000..3ad28a39 Binary files /dev/null and b/thirtydollarwebsite/sounds/karateman_hit.wav differ diff --git a/thirtydollarwebsite/sounds/karateman_offbeat.wav b/thirtydollarwebsite/sounds/karateman_offbeat.wav new file mode 100644 index 00000000..38c03691 Binary files /dev/null and b/thirtydollarwebsite/sounds/karateman_offbeat.wav differ diff --git a/thirtydollarwebsite/sounds/karateman_throw.wav b/thirtydollarwebsite/sounds/karateman_throw.wav new file mode 100644 index 00000000..5334c896 Binary files /dev/null and b/thirtydollarwebsite/sounds/karateman_throw.wav differ diff --git a/thirtydollarwebsite/sounds/kick.wav b/thirtydollarwebsite/sounds/kick.wav new file mode 100644 index 00000000..ad11cf5f Binary files /dev/null and b/thirtydollarwebsite/sounds/kick.wav differ diff --git a/thirtydollarwebsite/sounds/krabs.wav b/thirtydollarwebsite/sounds/krabs.wav new file mode 100644 index 00000000..c37eb645 Binary files /dev/null and b/thirtydollarwebsite/sounds/krabs.wav differ diff --git a/thirtydollarwebsite/sounds/lancersplat.wav b/thirtydollarwebsite/sounds/lancersplat.wav new file mode 100644 index 00000000..58b152ad Binary files /dev/null and b/thirtydollarwebsite/sounds/lancersplat.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_baby.wav b/thirtydollarwebsite/sounds/mariopaint_baby.wav new file mode 100644 index 00000000..02701b1a Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_baby.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_car.wav b/thirtydollarwebsite/sounds/mariopaint_car.wav new file mode 100644 index 00000000..64c1186a Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_car.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_cat.wav b/thirtydollarwebsite/sounds/mariopaint_cat.wav new file mode 100644 index 00000000..25100328 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_cat.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_dog.wav b/thirtydollarwebsite/sounds/mariopaint_dog.wav new file mode 100644 index 00000000..199902cb Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_dog.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_flower.wav b/thirtydollarwebsite/sounds/mariopaint_flower.wav new file mode 100644 index 00000000..d87bad97 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_flower.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_gameboy.wav b/thirtydollarwebsite/sounds/mariopaint_gameboy.wav new file mode 100644 index 00000000..1b76958e Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_gameboy.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_luigi.wav b/thirtydollarwebsite/sounds/mariopaint_luigi.wav new file mode 100644 index 00000000..069b7894 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_luigi.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_mario.wav b/thirtydollarwebsite/sounds/mariopaint_mario.wav new file mode 100644 index 00000000..3f24780b Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_mario.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_plane.wav b/thirtydollarwebsite/sounds/mariopaint_plane.wav new file mode 100644 index 00000000..dc7f3229 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_plane.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_star.wav b/thirtydollarwebsite/sounds/mariopaint_star.wav new file mode 100644 index 00000000..7a828cd4 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_star.wav differ diff --git a/thirtydollarwebsite/sounds/mariopaint_swan.wav b/thirtydollarwebsite/sounds/mariopaint_swan.wav new file mode 100644 index 00000000..6f793c20 Binary files /dev/null and b/thirtydollarwebsite/sounds/mariopaint_swan.wav differ diff --git a/thirtydollarwebsite/sounds/megalovania.wav b/thirtydollarwebsite/sounds/megalovania.wav new file mode 100644 index 00000000..21d27f11 Binary files /dev/null and b/thirtydollarwebsite/sounds/megalovania.wav differ diff --git a/thirtydollarwebsite/sounds/megalovania_note.wav b/thirtydollarwebsite/sounds/megalovania_note.wav new file mode 100644 index 00000000..cad7af1f Binary files /dev/null and b/thirtydollarwebsite/sounds/megalovania_note.wav differ diff --git a/thirtydollarwebsite/sounds/metalpipe.wav b/thirtydollarwebsite/sounds/metalpipe.wav new file mode 100644 index 00000000..d6ff0a12 Binary files /dev/null and b/thirtydollarwebsite/sounds/metalpipe.wav differ diff --git a/thirtydollarwebsite/sounds/midspin.wav b/thirtydollarwebsite/sounds/midspin.wav new file mode 100644 index 00000000..ceca2162 Binary files /dev/null and b/thirtydollarwebsite/sounds/midspin.wav differ diff --git a/thirtydollarwebsite/sounds/minecraft_anvil.wav b/thirtydollarwebsite/sounds/minecraft_anvil.wav new file mode 100644 index 00000000..9cdbde43 Binary files /dev/null and b/thirtydollarwebsite/sounds/minecraft_anvil.wav differ diff --git a/thirtydollarwebsite/sounds/minecraft_bell.wav b/thirtydollarwebsite/sounds/minecraft_bell.wav new file mode 100644 index 00000000..52813103 Binary files /dev/null and b/thirtydollarwebsite/sounds/minecraft_bell.wav differ diff --git a/thirtydollarwebsite/sounds/minecraft_explosion.wav b/thirtydollarwebsite/sounds/minecraft_explosion.wav new file mode 100644 index 00000000..1f2c8344 Binary files /dev/null and b/thirtydollarwebsite/sounds/minecraft_explosion.wav differ diff --git a/thirtydollarwebsite/sounds/morningflower.wav b/thirtydollarwebsite/sounds/morningflower.wav new file mode 100644 index 00000000..69d8fdaa Binary files /dev/null and b/thirtydollarwebsite/sounds/morningflower.wav differ diff --git a/thirtydollarwebsite/sounds/morshu.wav b/thirtydollarwebsite/sounds/morshu.wav new file mode 100644 index 00000000..3205864b Binary files /dev/null and b/thirtydollarwebsite/sounds/morshu.wav differ diff --git a/thirtydollarwebsite/sounds/mrbeast.wav b/thirtydollarwebsite/sounds/mrbeast.wav new file mode 100644 index 00000000..c249de12 Binary files /dev/null and b/thirtydollarwebsite/sounds/mrbeast.wav differ diff --git a/thirtydollarwebsite/sounds/necoarc.wav b/thirtydollarwebsite/sounds/necoarc.wav new file mode 100644 index 00000000..e2b72bd9 Binary files /dev/null and b/thirtydollarwebsite/sounds/necoarc.wav differ diff --git a/thirtydollarwebsite/sounds/nerdhorn.wav b/thirtydollarwebsite/sounds/nerdhorn.wav new file mode 100644 index 00000000..9d36aa6b Binary files /dev/null and b/thirtydollarwebsite/sounds/nerdhorn.wav differ diff --git a/thirtydollarwebsite/sounds/noice.wav b/thirtydollarwebsite/sounds/noice.wav new file mode 100644 index 00000000..7d7dfacf Binary files /dev/null and b/thirtydollarwebsite/sounds/noice.wav differ diff --git a/thirtydollarwebsite/sounds/nope.wav b/thirtydollarwebsite/sounds/nope.wav new file mode 100644 index 00000000..aa69491c Binary files /dev/null and b/thirtydollarwebsite/sounds/nope.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_banjo.wav b/thirtydollarwebsite/sounds/noteblock_banjo.wav new file mode 100644 index 00000000..3309337b Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_banjo.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_bass.wav b/thirtydollarwebsite/sounds/noteblock_bass.wav new file mode 100644 index 00000000..0680f510 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_bass.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_bell.wav b/thirtydollarwebsite/sounds/noteblock_bell.wav new file mode 100644 index 00000000..f8086f94 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_bell.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_bit.wav b/thirtydollarwebsite/sounds/noteblock_bit.wav new file mode 100644 index 00000000..f190414c Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_bit.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_chime.wav b/thirtydollarwebsite/sounds/noteblock_chime.wav new file mode 100644 index 00000000..1453a634 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_chime.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_click.wav b/thirtydollarwebsite/sounds/noteblock_click.wav new file mode 100644 index 00000000..5d8e05a6 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_click.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_flute.wav b/thirtydollarwebsite/sounds/noteblock_flute.wav new file mode 100644 index 00000000..30f94959 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_flute.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_guitar.wav b/thirtydollarwebsite/sounds/noteblock_guitar.wav new file mode 100644 index 00000000..277545ca Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_guitar.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_harp.wav b/thirtydollarwebsite/sounds/noteblock_harp.wav new file mode 100644 index 00000000..32eebc74 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_harp.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_pling.wav b/thirtydollarwebsite/sounds/noteblock_pling.wav new file mode 100644 index 00000000..0cae501f Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_pling.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_snare.wav b/thirtydollarwebsite/sounds/noteblock_snare.wav new file mode 100644 index 00000000..438af4f0 Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_snare.wav differ diff --git a/thirtydollarwebsite/sounds/noteblock_xylophone.wav b/thirtydollarwebsite/sounds/noteblock_xylophone.wav new file mode 100644 index 00000000..0f0e9cee Binary files /dev/null and b/thirtydollarwebsite/sounds/noteblock_xylophone.wav differ diff --git a/thirtydollarwebsite/sounds/obama.wav b/thirtydollarwebsite/sounds/obama.wav new file mode 100644 index 00000000..a44cfb4f Binary files /dev/null and b/thirtydollarwebsite/sounds/obama.wav differ diff --git a/thirtydollarwebsite/sounds/oof.wav b/thirtydollarwebsite/sounds/oof.wav new file mode 100644 index 00000000..60b9f8f3 Binary files /dev/null and b/thirtydollarwebsite/sounds/oof.wav differ diff --git a/thirtydollarwebsite/sounds/ook.wav b/thirtydollarwebsite/sounds/ook.wav new file mode 100644 index 00000000..e0415cd1 Binary files /dev/null and b/thirtydollarwebsite/sounds/ook.wav differ diff --git a/thirtydollarwebsite/sounds/op.wav b/thirtydollarwebsite/sounds/op.wav new file mode 100644 index 00000000..d8a70808 Binary files /dev/null and b/thirtydollarwebsite/sounds/op.wav differ diff --git a/thirtydollarwebsite/sounds/otto_happy.wav b/thirtydollarwebsite/sounds/otto_happy.wav new file mode 100644 index 00000000..d45099c7 Binary files /dev/null and b/thirtydollarwebsite/sounds/otto_happy.wav differ diff --git a/thirtydollarwebsite/sounds/otto_off.wav b/thirtydollarwebsite/sounds/otto_off.wav new file mode 100644 index 00000000..0a60738c Binary files /dev/null and b/thirtydollarwebsite/sounds/otto_off.wav differ diff --git a/thirtydollarwebsite/sounds/otto_on.wav b/thirtydollarwebsite/sounds/otto_on.wav new file mode 100644 index 00000000..21079136 Binary files /dev/null and b/thirtydollarwebsite/sounds/otto_on.wav differ diff --git a/thirtydollarwebsite/sounds/otto_stress.wav b/thirtydollarwebsite/sounds/otto_stress.wav new file mode 100644 index 00000000..94d9a9a3 Binary files /dev/null and b/thirtydollarwebsite/sounds/otto_stress.wav differ diff --git a/thirtydollarwebsite/sounds/pan.wav b/thirtydollarwebsite/sounds/pan.wav new file mode 100644 index 00000000..f08cfd8c Binary files /dev/null and b/thirtydollarwebsite/sounds/pan.wav differ diff --git a/thirtydollarwebsite/sounds/perfectfail.wav b/thirtydollarwebsite/sounds/perfectfail.wav new file mode 100644 index 00000000..9d7a28ad Binary files /dev/null and b/thirtydollarwebsite/sounds/perfectfail.wav differ diff --git a/thirtydollarwebsite/sounds/pianoriff.wav b/thirtydollarwebsite/sounds/pianoriff.wav new file mode 100644 index 00000000..21679d3a Binary files /dev/null and b/thirtydollarwebsite/sounds/pianoriff.wav differ diff --git a/thirtydollarwebsite/sounds/pingas.wav b/thirtydollarwebsite/sounds/pingas.wav new file mode 100644 index 00000000..069b98e6 Binary files /dev/null and b/thirtydollarwebsite/sounds/pingas.wav differ diff --git a/thirtydollarwebsite/sounds/pizza.wav b/thirtydollarwebsite/sounds/pizza.wav new file mode 100644 index 00000000..b5b5523b Binary files /dev/null and b/thirtydollarwebsite/sounds/pizza.wav differ diff --git a/thirtydollarwebsite/sounds/preecho.wav b/thirtydollarwebsite/sounds/preecho.wav new file mode 100644 index 00000000..8d6e553e Binary files /dev/null and b/thirtydollarwebsite/sounds/preecho.wav differ diff --git a/thirtydollarwebsite/sounds/puyo.wav b/thirtydollarwebsite/sounds/puyo.wav new file mode 100644 index 00000000..2082f7a7 Binary files /dev/null and b/thirtydollarwebsite/sounds/puyo.wav differ diff --git a/thirtydollarwebsite/sounds/quack.wav b/thirtydollarwebsite/sounds/quack.wav new file mode 100644 index 00000000..0b92f5b3 Binary files /dev/null and b/thirtydollarwebsite/sounds/quack.wav differ diff --git a/thirtydollarwebsite/sounds/rdclap.wav b/thirtydollarwebsite/sounds/rdclap.wav new file mode 100644 index 00000000..f6700534 Binary files /dev/null and b/thirtydollarwebsite/sounds/rdclap.wav differ diff --git a/thirtydollarwebsite/sounds/rdmistake.wav b/thirtydollarwebsite/sounds/rdmistake.wav new file mode 100644 index 00000000..65eaeab7 Binary files /dev/null and b/thirtydollarwebsite/sounds/rdmistake.wav differ diff --git a/thirtydollarwebsite/sounds/recordscratch.wav b/thirtydollarwebsite/sounds/recordscratch.wav new file mode 100644 index 00000000..48ddbaa1 Binary files /dev/null and b/thirtydollarwebsite/sounds/recordscratch.wav differ diff --git a/thirtydollarwebsite/sounds/ride2.wav b/thirtydollarwebsite/sounds/ride2.wav new file mode 100644 index 00000000..572de4e8 Binary files /dev/null and b/thirtydollarwebsite/sounds/ride2.wav differ diff --git a/thirtydollarwebsite/sounds/robtopphone.wav b/thirtydollarwebsite/sounds/robtopphone.wav new file mode 100644 index 00000000..85c5f2f5 Binary files /dev/null and b/thirtydollarwebsite/sounds/robtopphone.wav differ diff --git a/thirtydollarwebsite/sounds/samsung.wav b/thirtydollarwebsite/sounds/samsung.wav new file mode 100644 index 00000000..43e5b42e Binary files /dev/null and b/thirtydollarwebsite/sounds/samsung.wav differ diff --git a/thirtydollarwebsite/sounds/samurai.wav b/thirtydollarwebsite/sounds/samurai.wav new file mode 100644 index 00000000..2360235a Binary files /dev/null and b/thirtydollarwebsite/sounds/samurai.wav differ diff --git a/thirtydollarwebsite/sounds/sans_voice.wav b/thirtydollarwebsite/sounds/sans_voice.wav new file mode 100644 index 00000000..542f1e8b Binary files /dev/null and b/thirtydollarwebsite/sounds/sans_voice.wav differ diff --git a/thirtydollarwebsite/sounds/shaker.wav b/thirtydollarwebsite/sounds/shaker.wav new file mode 100644 index 00000000..e113227d Binary files /dev/null and b/thirtydollarwebsite/sounds/shaker.wav differ diff --git a/thirtydollarwebsite/sounds/shatter.wav b/thirtydollarwebsite/sounds/shatter.wav new file mode 100644 index 00000000..aa92b874 Binary files /dev/null and b/thirtydollarwebsite/sounds/shatter.wav differ diff --git a/thirtydollarwebsite/sounds/sidestick.wav b/thirtydollarwebsite/sounds/sidestick.wav new file mode 100644 index 00000000..0f3af78a Binary files /dev/null and b/thirtydollarwebsite/sounds/sidestick.wav differ diff --git a/thirtydollarwebsite/sounds/skeleton.wav b/thirtydollarwebsite/sounds/skeleton.wav new file mode 100644 index 00000000..8c17b9a3 Binary files /dev/null and b/thirtydollarwebsite/sounds/skeleton.wav differ diff --git a/thirtydollarwebsite/sounds/skillstar.wav b/thirtydollarwebsite/sounds/skillstar.wav new file mode 100644 index 00000000..c5059b67 Binary files /dev/null and b/thirtydollarwebsite/sounds/skillstar.wav differ diff --git a/thirtydollarwebsite/sounds/skipshot.wav b/thirtydollarwebsite/sounds/skipshot.wav new file mode 100644 index 00000000..0b413217 Binary files /dev/null and b/thirtydollarwebsite/sounds/skipshot.wav differ diff --git a/thirtydollarwebsite/sounds/slap.wav b/thirtydollarwebsite/sounds/slap.wav new file mode 100644 index 00000000..e8baa068 Binary files /dev/null and b/thirtydollarwebsite/sounds/slap.wav differ diff --git a/thirtydollarwebsite/sounds/slapbass.wav b/thirtydollarwebsite/sounds/slapbass.wav new file mode 100644 index 00000000..b8084e05 Binary files /dev/null and b/thirtydollarwebsite/sounds/slapbass.wav differ diff --git a/thirtydollarwebsite/sounds/slip.wav b/thirtydollarwebsite/sounds/slip.wav new file mode 100644 index 00000000..1c7f2638 Binary files /dev/null and b/thirtydollarwebsite/sounds/slip.wav differ diff --git a/thirtydollarwebsite/sounds/sm64_hurt.wav b/thirtydollarwebsite/sounds/sm64_hurt.wav new file mode 100644 index 00000000..6017bbe1 Binary files /dev/null and b/thirtydollarwebsite/sounds/sm64_hurt.wav differ diff --git a/thirtydollarwebsite/sounds/sm64_painting.wav b/thirtydollarwebsite/sounds/sm64_painting.wav new file mode 100644 index 00000000..11812352 Binary files /dev/null and b/thirtydollarwebsite/sounds/sm64_painting.wav differ diff --git a/thirtydollarwebsite/sounds/smm_scream.wav b/thirtydollarwebsite/sounds/smm_scream.wav new file mode 100644 index 00000000..cb22d72d Binary files /dev/null and b/thirtydollarwebsite/sounds/smm_scream.wav differ diff --git a/thirtydollarwebsite/sounds/smw_1up.wav b/thirtydollarwebsite/sounds/smw_1up.wav new file mode 100644 index 00000000..1fb8e1b9 Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_1up.wav differ diff --git a/thirtydollarwebsite/sounds/smw_coin.wav b/thirtydollarwebsite/sounds/smw_coin.wav new file mode 100644 index 00000000..58b3df33 Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_coin.wav differ diff --git a/thirtydollarwebsite/sounds/smw_kick.wav b/thirtydollarwebsite/sounds/smw_kick.wav new file mode 100644 index 00000000..2784180b Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_kick.wav differ diff --git a/thirtydollarwebsite/sounds/smw_spinjump.wav b/thirtydollarwebsite/sounds/smw_spinjump.wav new file mode 100644 index 00000000..5c1e3462 Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_spinjump.wav differ diff --git a/thirtydollarwebsite/sounds/smw_stomp.wav b/thirtydollarwebsite/sounds/smw_stomp.wav new file mode 100644 index 00000000..2609a31b Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_stomp.wav differ diff --git a/thirtydollarwebsite/sounds/smw_stomp2.wav b/thirtydollarwebsite/sounds/smw_stomp2.wav new file mode 100644 index 00000000..fcbf9b1a Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_stomp2.wav differ diff --git a/thirtydollarwebsite/sounds/smw_yoshi.wav b/thirtydollarwebsite/sounds/smw_yoshi.wav new file mode 100644 index 00000000..d4b5029b Binary files /dev/null and b/thirtydollarwebsite/sounds/smw_yoshi.wav differ diff --git a/thirtydollarwebsite/sounds/steve_oof.wav b/thirtydollarwebsite/sounds/steve_oof.wav new file mode 100644 index 00000000..97bb2546 Binary files /dev/null and b/thirtydollarwebsite/sounds/steve_oof.wav differ diff --git a/thirtydollarwebsite/sounds/stopposting.wav b/thirtydollarwebsite/sounds/stopposting.wav new file mode 100644 index 00000000..b1c54bf5 Binary files /dev/null and b/thirtydollarwebsite/sounds/stopposting.wav differ diff --git a/thirtydollarwebsite/sounds/subaluwa.wav b/thirtydollarwebsite/sounds/subaluwa.wav new file mode 100644 index 00000000..96772a80 Binary files /dev/null and b/thirtydollarwebsite/sounds/subaluwa.wav differ diff --git a/thirtydollarwebsite/sounds/suspense.wav b/thirtydollarwebsite/sounds/suspense.wav new file mode 100644 index 00000000..d909f09f Binary files /dev/null and b/thirtydollarwebsite/sounds/suspense.wav differ diff --git a/thirtydollarwebsite/sounds/tab_actions.wav b/thirtydollarwebsite/sounds/tab_actions.wav new file mode 100644 index 00000000..01254c7f Binary files /dev/null and b/thirtydollarwebsite/sounds/tab_actions.wav differ diff --git a/thirtydollarwebsite/sounds/tab_decorations.wav b/thirtydollarwebsite/sounds/tab_decorations.wav new file mode 100644 index 00000000..aa4c18e7 Binary files /dev/null and b/thirtydollarwebsite/sounds/tab_decorations.wav differ diff --git a/thirtydollarwebsite/sounds/tab_rooms.wav b/thirtydollarwebsite/sounds/tab_rooms.wav new file mode 100644 index 00000000..d620e065 Binary files /dev/null and b/thirtydollarwebsite/sounds/tab_rooms.wav differ diff --git a/thirtydollarwebsite/sounds/tab_rows.wav b/thirtydollarwebsite/sounds/tab_rows.wav new file mode 100644 index 00000000..be1b8bc6 Binary files /dev/null and b/thirtydollarwebsite/sounds/tab_rows.wav differ diff --git a/thirtydollarwebsite/sounds/tab_sounds.wav b/thirtydollarwebsite/sounds/tab_sounds.wav new file mode 100644 index 00000000..816acfd5 Binary files /dev/null and b/thirtydollarwebsite/sounds/tab_sounds.wav differ diff --git a/thirtydollarwebsite/sounds/tada.wav b/thirtydollarwebsite/sounds/tada.wav new file mode 100644 index 00000000..ebab47ec Binary files /dev/null and b/thirtydollarwebsite/sounds/tada.wav differ diff --git a/thirtydollarwebsite/sounds/taiko_don.wav b/thirtydollarwebsite/sounds/taiko_don.wav new file mode 100644 index 00000000..889f6f72 Binary files /dev/null and b/thirtydollarwebsite/sounds/taiko_don.wav differ diff --git a/thirtydollarwebsite/sounds/taiko_ka.wav b/thirtydollarwebsite/sounds/taiko_ka.wav new file mode 100644 index 00000000..38788c93 Binary files /dev/null and b/thirtydollarwebsite/sounds/taiko_ka.wav differ diff --git a/thirtydollarwebsite/sounds/taunt.wav b/thirtydollarwebsite/sounds/taunt.wav new file mode 100644 index 00000000..38a562ab Binary files /dev/null and b/thirtydollarwebsite/sounds/taunt.wav differ diff --git a/thirtydollarwebsite/sounds/terraria_axe.wav b/thirtydollarwebsite/sounds/terraria_axe.wav new file mode 100644 index 00000000..da4ca268 Binary files /dev/null and b/thirtydollarwebsite/sounds/terraria_axe.wav differ diff --git a/thirtydollarwebsite/sounds/terraria_guitar.wav b/thirtydollarwebsite/sounds/terraria_guitar.wav new file mode 100644 index 00000000..4a942c56 Binary files /dev/null and b/thirtydollarwebsite/sounds/terraria_guitar.wav differ diff --git a/thirtydollarwebsite/sounds/terraria_pot.wav b/thirtydollarwebsite/sounds/terraria_pot.wav new file mode 100644 index 00000000..cba9232e Binary files /dev/null and b/thirtydollarwebsite/sounds/terraria_pot.wav differ diff --git a/thirtydollarwebsite/sounds/terraria_reforge.wav b/thirtydollarwebsite/sounds/terraria_reforge.wav new file mode 100644 index 00000000..49aa5ba5 Binary files /dev/null and b/thirtydollarwebsite/sounds/terraria_reforge.wav differ diff --git a/thirtydollarwebsite/sounds/terraria_star.wav b/thirtydollarwebsite/sounds/terraria_star.wav new file mode 100644 index 00000000..ab56844a Binary files /dev/null and b/thirtydollarwebsite/sounds/terraria_star.wav differ diff --git a/thirtydollarwebsite/sounds/tf2_crit.wav b/thirtydollarwebsite/sounds/tf2_crit.wav new file mode 100644 index 00000000..b81bafda Binary files /dev/null and b/thirtydollarwebsite/sounds/tf2_crit.wav differ diff --git a/thirtydollarwebsite/sounds/thwomp.wav b/thirtydollarwebsite/sounds/thwomp.wav new file mode 100644 index 00000000..339402a8 Binary files /dev/null and b/thirtydollarwebsite/sounds/thwomp.wav differ diff --git a/thirtydollarwebsite/sounds/toby.wav b/thirtydollarwebsite/sounds/toby.wav new file mode 100644 index 00000000..13e54cc9 Binary files /dev/null and b/thirtydollarwebsite/sounds/toby.wav differ diff --git a/thirtydollarwebsite/sounds/tom.wav b/thirtydollarwebsite/sounds/tom.wav new file mode 100644 index 00000000..709f3c7b Binary files /dev/null and b/thirtydollarwebsite/sounds/tom.wav differ diff --git a/thirtydollarwebsite/sounds/tonk.wav b/thirtydollarwebsite/sounds/tonk.wav new file mode 100644 index 00000000..37c5bb39 Binary files /dev/null and b/thirtydollarwebsite/sounds/tonk.wav differ diff --git a/thirtydollarwebsite/sounds/ultrainstinct.wav b/thirtydollarwebsite/sounds/ultrainstinct.wav new file mode 100644 index 00000000..edf2546b Binary files /dev/null and b/thirtydollarwebsite/sounds/ultrainstinct.wav differ diff --git a/thirtydollarwebsite/sounds/undertale_crack.wav b/thirtydollarwebsite/sounds/undertale_crack.wav new file mode 100644 index 00000000..7d0b6a64 Binary files /dev/null and b/thirtydollarwebsite/sounds/undertale_crack.wav differ diff --git a/thirtydollarwebsite/sounds/undertale_encounter.wav b/thirtydollarwebsite/sounds/undertale_encounter.wav new file mode 100644 index 00000000..333d51d7 Binary files /dev/null and b/thirtydollarwebsite/sounds/undertale_encounter.wav differ diff --git a/thirtydollarwebsite/sounds/undertale_hit.wav b/thirtydollarwebsite/sounds/undertale_hit.wav new file mode 100644 index 00000000..b08409de Binary files /dev/null and b/thirtydollarwebsite/sounds/undertale_hit.wav differ diff --git a/thirtydollarwebsite/sounds/vvvvvv_checkpoint.wav b/thirtydollarwebsite/sounds/vvvvvv_checkpoint.wav new file mode 100644 index 00000000..9af06846 Binary files /dev/null and b/thirtydollarwebsite/sounds/vvvvvv_checkpoint.wav differ diff --git a/thirtydollarwebsite/sounds/vvvvvv_flash.wav b/thirtydollarwebsite/sounds/vvvvvv_flash.wav new file mode 100644 index 00000000..0ebb2778 Binary files /dev/null and b/thirtydollarwebsite/sounds/vvvvvv_flash.wav differ diff --git a/thirtydollarwebsite/sounds/vvvvvv_flip.wav b/thirtydollarwebsite/sounds/vvvvvv_flip.wav new file mode 100644 index 00000000..899eaf14 Binary files /dev/null and b/thirtydollarwebsite/sounds/vvvvvv_flip.wav differ diff --git a/thirtydollarwebsite/sounds/vvvvvv_hurt.wav b/thirtydollarwebsite/sounds/vvvvvv_hurt.wav new file mode 100644 index 00000000..fc4e7cb9 Binary files /dev/null and b/thirtydollarwebsite/sounds/vvvvvv_hurt.wav differ diff --git a/thirtydollarwebsite/sounds/waterphone.wav b/thirtydollarwebsite/sounds/waterphone.wav new file mode 100644 index 00000000..d5281909 Binary files /dev/null and b/thirtydollarwebsite/sounds/waterphone.wav differ diff --git a/thirtydollarwebsite/sounds/whatsapp.wav b/thirtydollarwebsite/sounds/whatsapp.wav new file mode 100644 index 00000000..03b90bae Binary files /dev/null and b/thirtydollarwebsite/sounds/whatsapp.wav differ diff --git a/thirtydollarwebsite/sounds/whipcrack.wav b/thirtydollarwebsite/sounds/whipcrack.wav new file mode 100644 index 00000000..0443bc66 Binary files /dev/null and b/thirtydollarwebsite/sounds/whipcrack.wav differ diff --git a/thirtydollarwebsite/sounds/wowowow.wav b/thirtydollarwebsite/sounds/wowowow.wav new file mode 100644 index 00000000..9faa67c6 Binary files /dev/null and b/thirtydollarwebsite/sounds/wowowow.wav differ diff --git a/thirtydollarwebsite/sounds/yahoo.wav b/thirtydollarwebsite/sounds/yahoo.wav new file mode 100644 index 00000000..f62d8ec0 Binary files /dev/null and b/thirtydollarwebsite/sounds/yahoo.wav differ diff --git a/thirtydollarwebsite/sounds/yawn.wav b/thirtydollarwebsite/sounds/yawn.wav new file mode 100644 index 00000000..63353dc6 Binary files /dev/null and b/thirtydollarwebsite/sounds/yawn.wav differ diff --git a/thirtydollarwebsite/sounds/yoda.wav b/thirtydollarwebsite/sounds/yoda.wav new file mode 100644 index 00000000..77d8e07e Binary files /dev/null and b/thirtydollarwebsite/sounds/yoda.wav differ diff --git a/thirtydollarwebsite/sounds/zunpet.wav b/thirtydollarwebsite/sounds/zunpet.wav new file mode 100644 index 00000000..2cf42d0f Binary files /dev/null and b/thirtydollarwebsite/sounds/zunpet.wav differ diff --git a/thirtydollarwebsite/▶.js b/thirtydollarwebsite/▶.js new file mode 100644 index 00000000..d1aa5147 --- /dev/null +++ b/thirtydollarwebsite/▶.js @@ -0,0 +1,421 @@ +// hi hello this is the code for actually preloading and playing sequences + +let MAX_BPM_LIMIT = 20_000 // wow this seems suspiciously easy to redefine + +// convert sequence to json +function getSequenceData() { + let sequenceData = $('#sequence div').map(function (index) { + return { + index, + element: $(this), + sound: $(this).attr("sound"), + pitch: $(this).attr("pitch"), + volume: $(this).attr("vol"), + action: $(this).attr("action"), + icon: $(this).attr("img"), + group: +$(this).parent().attr("group") || 0, + amount: $(this).attr("amount"), + operator: $(this).attr("num"), + dualVal: [$(this).attr("val1"), $(this).attr("val2")] + } + }).toArray() + return sequenceData +} + +function beatLength(bpm) { + return 60 / bpm * 1000 +} + +function modifyNumber(num, newNum, operator) { + switch (operator) { + case "add": return num + newNum + case "multiply": return num * newNum + default: return newNum + } +} + +// fetches any uncached sounds in the sequence to prevent lag spikes +async function fetchRequiredSounds(sequence=getSequenceData()) { + // for loops are better for async stuff + let soundKeys = Object.keys(sounds) + for (let i=0; i < sequence.length; i++) { + let x = sequence[i] + if (x.sound && x.sound != "sounds/_pause.wav" && !soundKeys.includes(x.sound)) { + await fetchSound(x.sound) + soundKeys.push(x.sound) + } + } +} + +function preloadSequence(sequence=getSequenceData()) { + let order = [] + + let bpm = defaultTempo + let volume = defaultVolume + let loopTarget = 0 + let transposition = 0 + + let index = 0 + let timer = 0 + let scrubPos = 0 + let pulseID = 0 + + let startPositions = sequence.filter(x => x.action == "startpos" && (selectedDivider >= 0 ? x.group == selectedDivider : true)) + if (startPositions.length) scrubPos = startPositions[startPositions.length - 1].index + else if (selectedDivider >= 0) scrubPos = (sequence.find(x => x.group == selectedDivider) || {}).index || 0 + + let scrubbing = scrubPos > 0 + + function untrigger(idx, except=[]) { + let untriggered = [] + sequence.slice(idx + 1).map((x, y) => { + if (!except.includes(x.action)) { + if (x.triggered) { x.triggered = false; untriggered.push(idx + 1 + y) } + if (x.remaining <= 0) { delete x.remaining; untriggered.push(idx + 1 + y) } + } + return x; + }) + return untriggered + } + + while (index < sequence.length) { + let x = sequence[index] + let incrementTimer = false + + if (scrubbing && index == scrubPos) scrubbing = false + + // if the event is a sound... + if (x.sound) { + let vol = !isNaN(+x.volume) ? +x.volume : 100 // dead certain theres a browser that wont support ?? + // construct the sound data + let soundObj = { + index, + sound: x.sound, + volume: volume * clamp((vol) / 100, 0, 4), + pitch: clamp((+x.pitch || 0) + transposition, -72, 72), + time: timer / 1000, + element: x.element, + img: x.element.find("img") + } + + let nextSound = sequence[index + 1] || {} + if (nextSound.action != "combine") incrementTimer = true + if (!scrubbing) order.push(soundObj) + } + + // if the event is an action... + else if (x.action) { + + let val = Number(x.amount) || 0 + + let actionObj = { + index, + action: x.action, + pulse: true, + trigger: true, + scrub: scrubbing, + time: timer / 1000, + element: x.element, + img: x.element.find("img") + } + + // do something different for each action + switch (x.action) { + + // ⏩ change BPM + case "speed": + bpm = modifyNumber(bpm, val, x.operator) + bpm = Number(clamp(bpm, 5, MAX_BPM_LIMIT).toFixed(4)) + actionObj.bpm = bpm + break; + + // 🔊 change volume + case "volume": + volume = modifyNumber(volume, val, x.operator) + volume = Number(clamp(volume, 0, 600).toFixed(4)) + actionObj.volume = volume + break; + + // ⏸ pause for duration + case "stop": + let beatsRemaining = isNaN(x.remaining) ? val : x.remaining // i could have used ?? but i'm sure theres a browser out there that doesnt support it. fuck webdev + if (!scrubbing && beatsRemaining > 0) { + let timeToRemove = Math.min(1, beatsRemaining) + timer += beatLength(bpm) * timeToRemove + + beatsRemaining -= 1 + if (beatsRemaining < 0) beatsRemaining = 0 + + actionObj.remaining = beatsRemaining + sequence[index].remaining = beatsRemaining + index-- + actionObj.trigger = false + } + else { + actionObj.finished = true + actionObj.duration = val + } + break; + + // 🔁 multiple loops + case "loopmany": + let loopsRemaining = isNaN(x.remaining) ? val : x.remaining + if (!scrubbing && loopsRemaining > 0) { + loopsRemaining-- + actionObj.remaining = loopsRemaining + sequence[index].remaining = loopsRemaining + index = loopTarget - 1 + if (loopsRemaining < 1) actionObj.pulse = false + else actionObj.trigger = false + + // untrigger: single loops, targets + actionObj.untriggered = untrigger(index, ["loopmany"]) + } + else actionObj.skip = true; + break; + + // 🔂 single loop + case "loop": + if (!x.triggered) { + sequence[index].triggered = true + index = loopTarget - 1 + + // untrigger: targets + actionObj.untriggered = untrigger(index, ["loop", "loopmany"]) + } + else actionObj.skip = true; + break; + + // ◇ loop target + case "looptarget": + loopTarget = index + break; + + // ❎ stop sounds + case "cut": + break; // nothing, actually + + // 📍 startpos + case "startpos": + break; // handled earlier + + // ↔ combine + case "combine": + if (scrubbing) actionObj.skip = true + break; // combining is checked for sounds + + // ⏺ go to target + case "jump": + if (!x.triggered) { + let foundTarget = sequence.findIndex(e => e.action == "target" && !e.triggered && e.amount == x.amount) + if (foundTarget >= 0) { + sequence[index].triggered = true + // sequence[foundTarget].triggered = true ??? + actionObj.target = foundTarget + index = foundTarget + } + + actionObj.untriggered = untrigger(index, ["loop", "loopmany", "jump", "target"]) + } + else actionObj.skip = true; + break; + + // ⭕ target + case "target": + actionObj.trigger = false + actionObj.pulse = false + break; // handled by jump action + + // 🔼 raise or lower pitch of all future sounds + case "transpose": + transposition = modifyNumber(transposition, val, x.operator) + transposition = Number(clamp(transposition, -60, 60).toFixed(4)) + break; + + // ⚡ flash + case "flash": + break; // nothing here, go figure + + // ⛶ pulse screen + case "pulse": + if (scrubbing) actionObj.skip = true // disable pulse scrubbing, will fix eventually + else { + actionObj.count = Math.floor(clamp(+x.dualVal[0], 0, 1000)) + actionObj.frequency = clamp(Number(x.dualVal[1]).toFixed(4), 0, 1000) + actionObj.trigger = false + actionObj.pulseID = pulseID + if (!actionObj.frequency) actionObj.skip = true + + if (actionObj.count > 1 && actionObj.frequency > 0) { + for (let i=1; i < actionObj.count; i++) { + let pulseTime = timer + ((beatLength(bpm) * actionObj.frequency * i)) + if (!order.find(e => e.action == x.action && e.time == pulseTime)) order.push({ + index, + action: x.action, + pulse: true, + pulseID, + trigger: i == actionObj.count - 1, + time: pulseTime / 1000, + element: x.element, img: actionObj.img + }) + } + pulseID++ + } + else actionObj.trigger = true + if (actionObj.count < 1) actionObj.stopPulses = true + } + break; + + // 🎨 background color + case "bg": + actionObj.bgColor = x.dualVal[0].match(colorRegex) ? x.dualVal[0] : defaultBG + actionObj.fadeTime = scrubbing ? 0.1 : clamp(Number(x.dualVal[1]).toFixed(4), 0, 200) + break; + } + + if (!actionObj.skip) order.push(actionObj) + + } + + index++ + if (!scrubbing && incrementTimer) timer += beatLength(bpm) + } + return order.sort((a, b) => a.time - b.time) +} + +let lastPos = -200 // last scroll position +let lastPulse = 0 // last pulse action +let startTime = 0 // time when sequence started + +let playingSequence = []; +let nextSoundToQueue = 0; +let nextAction = 0; + +function playSequence(sequence) { + updateTempo(defaultTempo) + setVolume(defaultVolume) + lastPulse = 0 + startTime = soundcloud.currentTime + + playingSequence = sequence; + nextSoundToQueue = 0; + nextAction = 0; + queueSounds(); + checkActions(); +} + +// how many seconds of sound to queue ahead. +const queueAhead = 5; + +function queueSounds() { + for ("tempooptimizer wtf is this for loop -colon"; nextSoundToQueue < playingSequence.length; nextSoundToQueue++) { + let x = playingSequence[nextSoundToQueue]; + if (startTime + x.time > soundcloud.currentTime + queueAhead) break; + + // this is either a sound or a cut + if (x.action === "cut") cutSounds(startTime + x.time); + else if (x.sound !== undefined) { + let pitch = semitonesToPercent(clamp(x.pitch, -72, 72)) + playSound(x.sound, { pitch, playAt: startTime + x.time, index: x.index, volume: x.volume / 200 }) + } + } + if (nextSoundToQueue < playingSequence.length) { + // use setTimeout for background playback, requestAnimationFrame for less lag spikes -TempoOptimizer + // works for me -Colon + setTimeout(queueSounds, 1000); + } +} + +function checkActions() { + if (!active) return; + + // run for each action where time > current time + for ("ksdjfsdf8sdkfjsdkfjak"; nextAction < playingSequence.length; nextAction++) { + let x = playingSequence[nextAction]; + if (startTime + x.time > soundcloud.currentTime) break; // stops the loop + if (x.triggered) continue; // does not stop the loop + + x.triggered = true + let icon = x.element + let img = x.img + + // sounds just bounce! + if (x.sound) { + icon.runAnimation('bounce') + } + + else if (x.action && !settings.noAnimations) { + + if (x.pulse) icon.runAnimation('pulse'); + if (x.trigger) img.runAnimation('triggered'); + + if (x.untriggered && x.untriggered.length) { + x.untriggered.forEach(n => { + let foundUntrigger = $('#sequence div').eq(n) + foundUntrigger.find('img').removeClass('triggered') + }) + } + + // here we go again! + switch (x.action) { + + case "speed": + updateTempo(x.bpm) + break; + + case "volume": + setVolume(x.volume); + break; + + case "stop": + let beatsLeft = icon.find('p') + beatsLeft.attr("triggeredCountdown", true) + if (x.finished) beatsLeft.text(x.duration) + else beatsLeft.text(x.remaining + 1) + break; + + case "loopmany": + let loopsLeft = icon.find('p') + loopsLeft.attr("triggeredCountdown", true) + if (x.remaining <= 0) loopsLeft.text("") + else loopsLeft.text(x.remaining) + break; + + case "jump": + let jumpTarget = $('#sequence div').eq(x.target) + jumpTarget.runAnimation('pulse') + jumpTarget.find('img').runAnimation('triggered') + break; + + case "flash": + if (!x.scrub) $('#everything').runAnimation('screenflash'); + break; + + case "pulse": + if (x.pulseID > lastPulse) lastPulse = x.pulseID + if (!x.scrub && !x.stopPulses && (lastPulse == x.pulseID)) $('body').runAnimation('screenpulse'); + break; + + case "bg": + if (!x.ignore) $('html').css('transition-duration', x.fadeTime + "s").css('background-color', x.bgColor) + } + + } + + if (settings.autoScroll) { + let pixelsBeforeScroll = 600 + let newPos = icon.prop("offsetTop") - 150 + if (Math.abs(newPos - lastPos) > pixelsBeforeScroll) { + $('#everything').stop().animate({ scrollTop: newPos }, 350) + lastPos = newPos + } + if (x.action == "divider") lastPos = -1000 // dividers always trigger autoscroll + } + } + + if (nextAction < playingSequence.length) { + requestAnimationFrame(checkActions); + } else { + cancel({ keepAnimations: true }); + } +} diff --git a/thirtydollarwebsite/🎨.css b/thirtydollarwebsite/🎨.css new file mode 100644 index 00000000..511d63c8 --- /dev/null +++ b/thirtydollarwebsite/🎨.css @@ -0,0 +1,753 @@ +@import url('https://fonts.googleapis.com/css?family=Lato'); + +html { + --bg: #36393c; + --emojired: #DD2E44; + --emojiyellow: #F4900C; + --emojigreen: #77B255; + --emojiblue: #3B88C3; + --emojipurple: #9266CC; + + --yellowfont: #ccff55; + --greenfont: #00FF64; + --bluefont: #00A2FF; + + background-color: var(--bg); + height: 100%; + + overflow: hidden; + transition-duration: 0.2s; + transition-property: background-color; + + --mainhotbarwidth: 855px; + --mobilehotbarwidth: 90%; + --largehotbarwidth: calc(var(--mainhotbarwidth) * 2) +} + +body { + margin: 0 0; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} + +body.dragOver { + background-color: rgba(255, 255, 255, 0.2); + transition-duration: 0.2s; +} + +#everything { + position: fixed; + width: 100%; + height: 100%; + overflow: auto; +} + +p, h1, vol { + font-family: Lato, Helvetica, Arial, sans-serif; + font-size: 16px; + color: white; + margin-bottom: 10px; + -webkit-text-size-adjust: none +} + +h1 { + font-size: 24px; +} + +vol { + display: block; + margin: 0px 0px; +} + +a { + color: aqua !important; +} + +img { + -webkit-user-drag: none; +} + +.iconbox, .infobox, .title { + margin: auto; +} + +.iconbox.loadingIcons { + flex-wrap: nowrap; + justify-content: center; +} + +.multiline { + line-height: 27px; +} + +.title { + font-size: 40px; + margin-top: 15px; + margin-bottom: 10px; + white-space: nowrap; + cursor: pointer; +} + +#main { + display: flex; + flex-direction: column; + margin: auto; + padding: 0px 30px; +} + +.sideboxes { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.infobox { + display: flex; + justify-content: space-between; + align-items: flex-end; +} + +.infobox span { + display: flex; + flex-wrap: wrap; + margin-bottom: 10px; +} + +.infobox span p { + margin: 0px 0px; + display: block; +} + +.infobox span p:after { content: "•"; margin: 0px 7px; font-size: 14px; } +.infobox span p:last-child:after { content: none; } + +.iconbox { + display: flex; + flex-wrap: wrap; + align-content: flex-start; + align-items: center; + border-radius: 8px; + background-color: rgba(0, 0, 0, 0.25); + min-height: 80px; + user-select: none; + padding: 20px 10px; + margin-bottom: 20px; + scrollbar-width: thin; + touch-action: manipulation; +} + +.iconbox img, .hotbar img { + cursor: pointer; + border-radius: 8px; + color: white; + object-fit: contain; + width: 56px; + height: 56px; + padding: 5px 5px; + display: block; + font-family: Lato, Helvetica, Arial, sans-serif; + color: rgba(255, 255, 255, 0.7); +} + +.iconbox div, .hotbar div { + border-radius: 8px; + position: relative; +} + +.iconbox div:hover img, .hotbar div:hover img { + background-color: rgba(255, 255, 255, 0.15); +} + +#sequence .holdingShift:hover img { + background-color: rgba(64, 255, 64, 0.33); +} + +.iconbox .group { + width: 1000px; +} + +#icons, #actions, .hotbar { + overflow-y: auto; +} + +#everything::-webkit-scrollbar, .iconbox::-webkit-scrollbar, .fancyScroll::-webkit-scrollbar { + width: 12px; + background: rgba(0, 0, 0, 0.15); + border-radius: 10px; +} + +#everything::-webkit-scrollbar-thumb, .iconbox::-webkit-scrollbar-thumb, .fancyScroll::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.33); + border-radius: 10px; +} + +#everything::-webkit-scrollbar-corner, .iconbox::-webkit-scrollbar-corner, .fancyScroll::-webkit-scrollbar-corner { + background: rgba(0, 0, 0, 0); +} + +#sequence { + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + padding: 20px 7.5px 20px; + margin-bottom: 0px; +} + +#sequence div { + height: 70px; + overflow-y: scroll; + scrollbar-width: none; +} + +#sequence img { + width: 58px; + height: 58px; + padding: 6.5px 6px; + margin-bottom: 200px; + margin-top: 200px; +} + +#sequence div::-webkit-scrollbar { + width: 0px; + background: rgba(0, 0, 0, 0); +} + +#sequence section { + display: flex; + flex-wrap: wrap; + border: 2.5px solid rgba(0, 0, 0, 0); + border-radius: 8px; + min-height: 75px; + min-width: 75px; +} + +section:not(:empty) + section:empty { + background-color: rgba(255, 255, 255, 0.05); +} + +#sequence section:not(:last-child) { + margin-bottom: 40px; +} + +#sequence section.holdingCtrl:not(.removedDivider) { + border-color: aqua; + background-color: rgba(255, 255, 255, 0.1); + cursor: pointer; +} + +#sequence section.selectedDivider { + border-color: lime !important; +} + +#sequence section.sectionHidden:not(.selectedDivider) { + height: 40px; + min-height: 40px; + max-height: 40px; + border-color: rgba(255, 255, 255, 0.25); + overflow: hidden; +} + +#sequence section.sectionHidden { + opacity: 33%; +} + +#sequence section.sectionHidden:not(.selectedDivider) div { + pointer-events: none; +} + +.iconbox p, .iconbox vol { + width: 100%; + font-weight: bold; + text-shadow: 0px 0px 4px black; + position: absolute; +} + +#sequence p { + top: 235px; + white-space: nowrap; + overflow-x: auto; +} + +#sequence vol { + top: 201px; + text-align: right; + white-space: nowrap; + overflow-x: auto; + font-size: 13px; + color: #cccccc; +} + +#actions p { + right: 25px; + top: 16px; + font-size: 26px; +} + +.playbuttons { + display: flex; + justify-content: left; + flex-direction: column; + user-select: none; +} + +.playbuttons div { + display: flex; + align-items: center; + margin-bottom: 18px; + border-radius: 8px; + padding-left: 15px; + transition-duration: 0.1s; + cursor: pointer; +} + +.playbuttons p { + font-size: 16px; + margin: 0px 0px 7px 0px; + text-align: left; +} + +.playbuttons h1 { + margin: 0px 0px 2px 0px; +} + +.playbuttons img { + margin: 12px 16px 12px 0px; + width: 36px; + height: 36px; +} + +.playbuttons div:hover { transform: scale(1.02) } +.playbuttons div:active { transform: scale(1.05) } + +.sectionControls { + display: flex; +} + +.sectionControls img { + height: 40px; + margin-right: 15px; + cursor: pointer; + user-select: none; +} + +.sectionControls img.cantSelect { + opacity: 33%; + pointer-events: none; +} + +.sectionControls img:hover { transform: scale(1.02) } +.sectionControls img:active { transform: scale(1.05) } + +#sectionSettings.pinnedSettings { + position: fixed; + background-color: #262626; + border-top-right-radius: 8px; + border-top: 3px solid #202020; + border-right: 3px solid #202020; + bottom: 0px; + left: 0px; + z-index: 2; + padding: 12px 0px 12px 12px; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-property: transform; +} + +#sectionSettings.pinnedSettings .noPin { + display: none; +} + +#sectionSettings.pinnedSettings.pinnedHidden { + transform: translateY(250%); +} + +.saveButtons div { + width: 180px; + background-color: var(--emojipurple); + margin: 8px 16px 16px 16px; +} + +.alreadySaved { + pointer-events: none; + opacity: 50%; + /* background-color: rgba(120, 100, 140, 0.5) !important; */ +} + +.credits { + display: flex; + justify-content: center; +} + +.credits p { + color: rgba(255, 255, 255, 0.33) !important; + margin: 10px 20px; +} +.credits a { color: rgba(255, 255, 255, 0.66) !important } + +.socialLinks { + display: flex; + justify-content: center; +} + +.socialTag { + display: flex; + justify-content: center; + align-items: center; + margin-left: 15px; + width: 105px; +} + +.socialTag a { + display: flex; + justify-content: center; + align-items: center; + text-decoration: underline white; +} + +.socialTag p { + font-size: 20px; + margin: 0px 0px 0px 2px; +} + +.socialTag img { + height: 20px; + margin-right: 5px; +} + +.sortPlaceholder { + opacity: 15%; +} + +.shiftPlaceholder { + display: block !important; +} + +.popup { + position: fixed; + display: flex; + align-content: center; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + top: 0; left: 0; right: 0; bottom: 0; + background-color: rgba(0,0,0,0.66); + z-index: 3; + text-align: center; +} + +.popupbox { + background-color: var(--bg); + border-radius: 5px; + width: 500px; + padding: 10px 5px 30px 5px; +} + +.popupdesc { + margin-top: 0px; + width: 400px; + margin: auto; + line-height: 24px; +} + +.popupinput { + margin-top: 20px; + display: flex; + justify-content: center; + align-items: center; +} + +.popupinput p { + margin: 0px 0px 0px 10px; + font-size: 18px; +} + +.extraSetting { + display: flex; + align-items: center; + margin: 5px 00px; + width: 200px; + cursor: pointer; +} + +.extraSetting p { + margin: 0px 5px; + text-align: left; +} + +.extraSetting:hover { + text-decoration: underline; + text-decoration-color: white; + text-decoration-style: dotted; +} + +.settingList { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: 3px 45px; + margin-top: 10px; +} + +.shortcutList { + display: flex; + flex-wrap: wrap; + text-align: left; + height: 515px; + overflow: auto; + justify-content: space-between; + margin: 0px 15px 30px 15px; + padding-left: 30px; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 10px; +} + +.shortcutList .groupName { + text-decoration: underline; +} + +.shortcutSection { + width: 320px; + margin-bottom: 20px; +} + +button, input { + font-family: Lato, Helvetica, Arial, sans-serif; + font-size: 18px; + text-align: center; + color: white; + height: 40px; + border-radius: 5px; + outline: none; + border: none; + background-color: rgba(0, 0, 0, 0.5); +} + +input[type=number] { + font-weight: bold; + width: 90px; + -moz-appearance: textfield; +} + +input[type=checkbox] { + cursor: pointer; + width: 24px; + height: 24px; +} + +input[type=checkbox]:before { + width: 24px; + height: 24px; + display: inline-block; + content: ""; + background-image: url(../assets/check_off.svg); +} + +input[type=checkbox]:checked:before { + background-image: url(../assets/check_on.svg); +} + +input[type=color] { + visibility: hidden; + pointer-events: none; +} + +.colorPreview { + border-radius: 5px; + border: 2px solid black; + width: 38px; + height: 38px; + margin-left: 12px; + cursor: pointer; +} + +button { + width: 100px; + background-color: rgba(255, 255, 255, 0.2); + cursor: pointer; + transition-duration: 0.1s; + margin: 0px 5px; +} + +#proHotbar { + position: fixed; + bottom: 30px; + z-index: 1; + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + pointer-events: none; + --hotbarwidth: var(--mainhotbarwidth); + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-property: transform; +} + +#proHotbar.playing { + transform: translateY(250%); +} + +.hotbar { + pointer-events: all; + border: 3px solid black; + width: var(--hotbarwidth); + height: 140px; + border-radius: 8px; + background-color: #1d1d1d; + display: flex; + flex-wrap: wrap; + align-content: flex-start; + justify-content: flex-start; + align-items: center; + user-select: none; + touch-action: manipulation; + scrollbar-width: thin; + padding: 6px 6px; + overflow: auto; +} + +.hotbar img { + width: 48px; + height: 48px; + padding: 5px 11px; +} + +.hotbar p { + position: absolute; + right: 48px; + top: 8px; + font-size: 24px; +} + +.hotbarLabel { + margin-bottom: 5px; + margin-left: 10px; + text-shadow: 1px 1px 3px black, -1px -1px 3px black, 1px -1px 3px black, -1px 1px 3px black; +} + +.hotbarTabs { + width: var(--hotbarwidth); + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: flex-end; +} + +.hotbarTab { + pointer-events: all; + width: 70px; + height: 36px; + border: 3px solid black; + border-bottom: 0px; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + margin-right: 5px; + background-color: #1d1d1d; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition-duration: 0.1s; +} + +.hotbarTab:first-child { + margin-left: 5px +} + +.hotbarTab img { + user-select: none; + height: 65%; + opacity: 50%; + transition-duration: 0.1s; +} + +.hotbarTab.selectedTab { + transform: translateY(3px); + height: 38px; + cursor: default; +} + +.hotbarTab.selectedTab img { + opacity: 100%; +} + +.hotbarTab:hover img { + opacity: 75%; +} + +#hotbarHovertext { + max-width: 445px; +} + +button:hover, .imgButton:hover { transform: scale(1.05); } +button:active, .imgButton:active { transform: scale(1.1); } + +input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0 } + +@media screen and (max-width: 1000px) { + #icons_container { width: 428px !important; } + #main { width: 750px !important; } + .title { width: 720px !important; white-space: normal !important; } + .infobox { flex-direction: column; align-items: flex-start; } + .infobox h1 { margin-bottom: 3px; } + .infobox span p { text-align: right; margin: 4px 0px 0px 0px; } + .infobox span p:after { content: "•"; margin: 0px 4px; font-size: 12px; } + .hotbar, .hotbarTabs { width: var(--mobilehotbarwidth) } + #proHotbar { --hotbarwidth: --mobilehotbarwidth; bottom: 50px; } + #hotbarNotes { height: 250px; } + #hotbarHovertext { max-width: 250px; } + #extraPadding { height: 400px !important } +} +@media screen and (min-width: 1000px) { + .mobileOnly { display: none !important; } +} +@media screen and (min-width: 2500px) { + #hotbarNotes { height: 15vh; } +} +@media screen and (min-width: 3250px) { + #proHotbar { --hotbarwidth: var(--largehotbarwidth) } +} + +.placed { animation: smallbounce 0.3s } +.bounce { animation: bounce 0.4s } +.pulse { animation: pulse 0.4s } +.screenflash { animation: screenflash 0.66s } +.screenpulse { animation: screenpulse 0.33s } +.triggered { animation: greyout 0.2s; animation-fill-mode: forwards; } + +@keyframes bounce { + 0% { transform: translateY(0); animation-timing-function: ease-out; } + 40% { transform: translateY(-15px); animation-timing-function: ease-in; } + 0% { transform: translateY(0); } +} + +@keyframes smallbounce { + 0% { transform: translateY(0); animation-timing-function: ease-out; } + 40% { transform: translateY(-5px); animation-timing-function: ease-in; } + 0% { transform: translateY(0); } +} + +@keyframes pulse { + 0% { transform: scale(1); animation-timing-function: ease-out; } + 50% { transform: scale(1.1); animation-timing-function: ease-in; } + 0% { transform: scale(1); } +} + +@keyframes greyout { + 0% { opacity: 100%; filter: grayscale(0); animation-timing-function: ease-out; } + 100% { opacity: 50%; filter: grayscale(0.15); } +} + +@keyframes screenflash { + 0% { background-color: rgba(255, 255, 255, 0.66); } + 100% { background-color: rgba(255, 255, 255, 0); } +} + +@keyframes screenpulse { + 40% { transform: scale(1.05); animation-timing-function: ease-out; } + 100% { transform: scale(1); animation-timing-function: ease-in; } +} \ No newline at end of file diff --git a/thirtydollarwebsite/💾.js b/thirtydollarwebsite/💾.js new file mode 100644 index 00000000..d75deed6 --- /dev/null +++ b/thirtydollarwebsite/💾.js @@ -0,0 +1,266 @@ +let filename = "sequence" +let extension = ".🗿" + +function enableNewSaving() { + sexySaving = true + $('#saveAsText').text("Save As") + $('#saveBtn').show() +} + +function disableNewSaving() { + sexySaving = false + $('#saveAsText').text("Save") + $('#saveBtn').hide() +} + +let sexySavingSupported = typeof window.showSaveFilePicker === "function" // very few browsers support this awesome api +let sexySaving = !settings.oldSaving && sexySavingSupported +if (!sexySaving) disableNewSaving() +else enableNewSaving() + +$('#saveOptions').show() +if (sexySavingSupported) $('.requiresNewSaving').show() + +// read placed sounds and create sequence file +function generateSequenceFile() { + let startingTime = Date.now() + console.log(`Exporting sequence... (${$('#sequence div').length} icons)`) + let sequenceList = [] + let toAdd = "" + $('#sequence div').each(function() { + let x = $(this) + if (x.attr('sound')) { + let snd = x.attr('str') + let pitch = x.attr('pitch') + let vol = x.attr('vol') + let foundSnd = soundList.find(x => x.id == snd) + let soundID = foundSnd ? (foundSnd.emoji || foundSnd.id) : (snd || "_pause") + let hasPitch = (pitch && pitch != 0) + toAdd = (hasPitch ? `${soundID}@${pitch}` : soundID) + if (vol && vol != 100) toAdd += `${!hasPitch ? "@0" : ""}%${vol}` + } + else if (x.attr('action')) { + let action = x.attr('action') + let actionStr = "!" + action + if (action == "divider") actionStr += "\n" + else if (x.attr('advanced')) { + actionStr += `@${x.attr("val1")},${x.attr("val2")}` + } + else if (x.attr('amount')) { + let num = x.attr('num') + actionStr += ("@" + Number(x.attr('amount'))) + actionStr += (num == "add" ? "@+" : num == "multiply" ? "@x" : "") + } + toAdd = (actionStr) + } + if (sequenceList.length && sequenceList[sequenceList.length - 1][0] == toAdd) sequenceList[sequenceList.length - 1][1]++ + else sequenceList.push([toAdd, 1]) + }) + let finalString = sequenceList.map(x => `${x[0]}${x[1] > 1 ? "=" + x[1] : ""}`).join("|") + console.log(`Exporting took ${+((Date.now() - startingTime) / 1000).toFixed(2)}s`) + return finalString +} + +// upon clicking 'save as' (or just save for old browsers) +$('#downloadBtn').click(function() { + let saveData = generateSequenceFile() + if (!saveData.length) return + let blob = new Blob([saveData], {type: 'text/txt;charset=UTF-8'}); + if (ctrlHeld && altHeld) return openSequencePreview(blob) + return sexySaving ? modernSave(blob) : classicSave(blob) +}) + +// saving through element, for browsers that don't support the new file reading stuff +function classicSave(data) { + let saveData = generateSequenceFile() + if (!saveData.length) return + let downloader = document.createElement('a'); + downloader.href = URL.createObjectURL(data) + downloader.dataset.downloadurl = ['text/txt', downloader.download, downloader.href].join(':'); + downloader.style.display = "none"; downloader.download = filename + extension + downloader.target = "_blank"; document.body.appendChild(downloader); + downloader.click(); document.body.removeChild(downloader); + setUnsavedChanges(false) + $('#saveBtn').addClass('alreadySaved') +} + +// saving through file api, for cool browsers +function modernSave(data) { + window.showSaveFilePicker({suggestedName: filename + extension}) + .then(selectedFile => { + selectedFile.createWritable().then(writable => { + setSaveLocation(selectedFile) + setUnsavedChanges(false) + writable.write(data).then(() => writable.close()).catch(console.error) + }).catch(console.error) + }).catch(console.error) +} + +// moai-ify the filename +function setFilename(name) { + filename = name + if (filename.endsWith(extension)) filename = filename.slice(0, extension.length * -1) + $('#saveName').val(filename) +} + +// set location for quick saving +let saveLocation = null +function setSaveLocation(file) { + setFilename(file.name) + saveLocation = file +} + +// save to last opened/saved file +function quickSave() { + if (!saveLocation || saveLocation.name != filename + extension) return $('#downloadBtn').trigger('click') // save as + let saveData = generateSequenceFile() + if (!saveData.length) return + if (ctrlHeld && altHeld) return openSequencePreview(new Blob([saveData], {type: 'text/txt;charset=UTF-8'})) + saveLocation.createWritable().then(async writable => { + writable.write(saveData).then(() => { + setUnsavedChanges(false) + writable.close() + }).catch((e) => alert(e)) + }).catch(() => {}) +} + +// open sequence preview in new tab +function openSequencePreview(blob) { + let blobURL = URL.createObjectURL(blob) + window.open(blobURL) +} + +// on file input +let reader = new FileReader(); +$("#loadFile").on('change', function() { + readLoadedFile(this.files[0]) +}) + +function readLoadedFile(file) { + if (!file) return + $('#sequence').html("") + $('#loadFile').val("") + reader.readAsText(file) + reader.onload = (function(f) { + loadSequence(f.target.result) + saveLocation = null + setFilename(file.name) + }); +} + +// load/read/parse sequence file +function loadSequence(data) { + let startingTime = Date.now() + console.log(`Loading sequence... (size: ${+(data.length / 1000).toFixed(2)} KB)`) + try { + let sequenceData = ""; + (data || "").replace(/\s/g, "").split("|").forEach(x => { + let [data, count] = x.split("=") + let [main, pitch, num] = data.split("@") + if (!main || !data) return + let isAction = main.startsWith("!") + let element = null + if (isAction) { + main = main.slice(1) // remove ! + element = $(`.action[action=${main}]`).first().clone() + if (!element.length) return + let actionData = actions.find(x => x.name == main) + if (actionData.twoValues) { + if (!element.find("p").length) element.append("

") + for (let i=0; i<(count || 1); i++) sequenceData += addAdvancedAction(actionData.name, pitch.split(","), element, true).prop('outerHTML') + return + } + else if (actionData.amount || actionData.isTarget) { + if (actionData.isTarget && !pitch) pitch = 1 + if (!element.find("p").length) element.append("

") + if (num == "x") num = "multiply" + else if (num == "+") num = "add" + for (let i=0; i<(count || 1); i++) sequenceData += addAction(main, +pitch, num, element, true).prop('outerHTML'); + return + } + } + else { + if (main.includes("%")) { [main, pitch] = main.split("%"); pitch = "%" + pitch } + let [pitchVal, volVal] = (pitch || "").split("%").map(x => +x) + let foundSound = soundList.find(x => x.id != "_pause" && (x.id == main || x.emoji == main)) + element = $(`.sound[str=${foundSound ? foundSound.id : "_pause"}]`).first().clone() + if (foundSound) { + if (pitchVal) { + element.attr('pitch', pitchVal) + element.append(`

${pitchVal > 0 ? "+" : ""}${pitchVal}

`) + } + + if (volVal || volVal === 0 + ) { + element.attr('vol', volVal) + element.append(`${volVal}%`) + } + } + element.removeAttr("soundorigin") + element.removeAttr("soundname") + } + if (element) for (let i=0; i<(count || 1); i++) sequenceData += (element[0].outerHTML) + }) + $('#sequence')[0].innerHTML = sequenceData + deselectSection() + syncSections() + fetchRequiredSounds() + setUnsavedChanges(false) + cancel() + console.log(`Loading took ${+((Date.now() - startingTime) / 1000).toFixed(2)}s`) + } + catch(e) { alert("That file couldn't be loaded!"); console.warn(e) } +} + +// remove unsafe filename characters +function safeFilename(str) { return str.replace(/[/\\:*?"<>|]/g, "") } + +$('#saveName').on('input keydown keyup blur', function() { + let safeName = safeFilename($(this).val()) + filename = safeName || "sequence" + $(this).val(safeName) +}) + +// dragging +$('body').on('dragover dragenter', function(e) { + e.preventDefault(); + e.stopPropagation(); + if (!active && e.originalEvent.dataTransfer.types.includes("Files")) $('body').addClass('dragOver') + return false +}) + +$('body').on('dragleave dragend drop', function(e) { + e.preventDefault(); + e.stopPropagation(); + $('body').removeClass('dragOver') + return false +}) + +$('body').on('drop', function(e){ + if (active) return false + if (e.originalEvent.dataTransfer){ + if (e.originalEvent.dataTransfer.files.length) { + e.preventDefault(); + e.stopPropagation(); + let hasUnsaved = unsavedChanges && $('#sequence').children().length + if (hasUnsaved && !confirm("Are you sure you want to load this file? Any unsaved changes will be lost.")) return + readLoadedFile(e.originalEvent.dataTransfer.files[0]) + } + } +}); + +function setUnsavedChanges(unsaved) { + unsavedChanges = !!unsaved + if (unsavedChanges) $('#saveBtn').removeClass('alreadySaved') + else $('#saveBtn').addClass('alreadySaved') +} + +// exit confirmation +let unsavedChanges = false +$('#sequence').on('DOMSubtreeModified', function() { setUnsavedChanges(true) }); +window.onbeforeunload = function(e) { + if (unsavedChanges && $('#sequence').children().length && settings.exitConfirmation) { + e.returnValue = "🗿"; + return cancel() + } +}; \ No newline at end of file diff --git a/thirtydollarwebsite/🗿.js b/thirtydollarwebsite/🗿.js new file mode 100644 index 00000000..882cda6e --- /dev/null +++ b/thirtydollarwebsite/🗿.js @@ -0,0 +1,1011 @@ +let sounds = {} +let soundcloud = new AudioContext(); +let recent = new Set() + +const mobile = ( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) +if (mobile) $('p[mobile]').each(function() { $(this).text($(this).attr('mobile')); $('.nomobile').hide() }) + +const intros = [ + {name: $('#caption').text(), path: "assets/dont_you_lecture_me.wav" }, + {name: "how you gonna talk behind my back when you deadass built like a", path: "assets/you_deadass_built_like_a.wav" }, + {name: "white people be like", path: "assets/white_people_be_like.wav" } +] + +const actions = [ + { shortcut: "t", action: "Set tempo", amount: true, name: "speed", image: "assets/action_speed.png", default: 300, set: [10, 10000], add: [-10000, 10000], multiply: [0.01, 1000, 0.1] }, + { shortcut: "v", action: "Set volume", amount: true, name: "volume", image: "assets/action_volume.png", default: 100, set: [0, 600, 1, "%"], add: [-600, 600, 1, "%"], multiply: [0.01, 1000, 0.1] }, + { shortcut: "p", action: "Pause for duration", amount: true, name: "stop", image: "assets/action_stop.png", default: 4, set: [0, 1000] }, + { shortcut: "m", action: "Transpose", amount: true, name: "transpose", image: "assets/action_transpose.png", default: 1, set: [-60, 60], add: [-60, 60], multiply: [0.01, 100, 0.1] }, + + { shortcut: "l", action: "Loop", amount: true, name: "loopmany", image: "assets/action_loopmany.png", default: 4, set: [1, 1000] }, + { shortcut: "r", action: "Loop once", name: "loop", image: "assets/action_loop.png" }, + { shortcut: "s", action: "Set loop start point", name: "looptarget", image: "assets/action_looptarget.png" }, + { shortcut: "c", action: "Combine sounds", name: "combine", image: "assets/action_combine.png" }, + + { shortcut: "g", action: "Go to target", isTarget: true, name: "jump", image: "assets/action_jump.png", set: [1, 9999] }, + { shortcut: "a", action: "Target", isTarget: true, name: "target", image: "assets/action_target.png", set: [1, 9999] }, + { shortcut: "x", action: "Stop all sounds", name: "cut", stopSounds: true, image: "assets/action_cut.png" }, + { shortcut: "o", action: "Set start position", name: "startpos", image: "assets/action_startpos.png" }, + + { shortcut: "d", action: "Add divider", name: "divider", image: "assets/action_divider.png" }, + { shortcut: "f", action: "Flash screen", name: "flash", image: "assets/action_flash.png" }, + { shortcut: "u", action: "Pulse screen", amount: true, twoValues: [[0, 1000], [0.1, 128]], scroll: [0, 1], default: [1, 2], name: "pulse", image: "assets/action_pulse.png" }, + { shortcut: "b", action: "Set background color", amount: true, colorMode: true, twoValues: [["color"], [0, 128]], scroll: [1, 0.25], default: ["X", 1], name: "bg", image: "assets/action_bg.png" } +] +actions.forEach(x => { $('#actions').append(`
${x.action}${x.amount ? "

+

" : ""}
`) }) + +let soundList = [] +fetch("./sounds.json").then(x => x.json()).then(list => { + soundList = list + $('#iconboxLoading').hide() + $('#icons').removeClass('loadingIcons') + list.forEach(x => { + let imageLink = (!x.emoji && x.id.match(/[a-z0-9]/i)) ? `icons/${x.img || x.id}.png` : `https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/${(x.emoji || x.id).codePointAt(0).toString(16)}.svg` + $('#icons').append(`
"tag_" + x).join(" ")}>${x.name}
`) + lastGroup = x.group + + if (settings.proMode) { + $('.hotbarTab').first().trigger('click') + $('#proHotbar').show() + $('#extraPadding').show() + buildHotbar() + } + }) +}).catch((e) => { + console.error(e) + $('#icons img').hide() + $('#icons').addClass('loadingIcons') + $('#iconboxLoading').show() + $('#loadingText').text("Something went wrong!") + $('#errorInfo').text(e.message) +}) + +let hotbar = $('#hotbarNotes') +function buildHotbar(filter="sounds") { + $('#hotbarNotes .placed').removeClass('placed') + hotbar.html("") // clear hotbar + + if (filter == "recent") { + let recentNotes = [...recent].slice(-48).reverse() + recentNotes.forEach(x => { + if (x.startsWith(".")) hotbar.append($(`#actions div[action=${x.slice(1)}]`).prop('outerHTML') || "") // action + else hotbar.append($(`#icons div[str=${x}]`).prop('outerHTML') || "") // sound + }) + return + } + + // whether to pull from sounds, actions, or both + let pool = (filter == "actions" ? "#actions div" : ["recent", "favorite"].includes(filter) ? "#icons div, #actions div" : "#icons div") + + $(pool).each(function() { + let x = $(this) + if (filter == "notes" && !x.is('[tag_note]')) return + else if (filter == "percussion" && !x.is('[tag_percussion]')) return + hotbar.append(x.prop('outerHTML')) + }) +} + +function updateRecent(id) { + recent.delete(id); recent.add(id) +} + +let defaultBG = "#36393c" +let colorRegex = /^#[a-f0-9]{6}$/i + +let cloneSort = false + +$('#sequence').sortable({ + tolerance: "pointer", + helper: "clone", + containment: "#sequence", + placeholder: "sortPlaceholder", + items: "div", + cursor: "move", + delay: mobile ? 1500 : 0, + forcePlaceholderSize: true, forceHelperSize: true, + start: function(event, ui) { + cloneSort = shiftHeld || altHeld + if (cloneSort) ui.item.addClass('shiftPlaceholder') + ui.placeholder.html(ui.item.html()) + }, + stop: function(event, ui) { + if (cloneSort) { + ui.item.clone().insertAfter(ui.item); + $(this).sortable('cancel'); + ui.item.clone() + } + cloneSort = false + $('.shiftPlaceholder').removeClass('shiftPlaceholder') + $('.placed').removeClass('placed') + cancel() + if ($("#sequence section").length > 1) { + if (ui.item.attr("action") == "divider") deselectSection() + syncSections() + } + } +}) + +$(document).on('mouseover', '#icons div', function () { + let soundCredit = $(this).attr('soundName') + let soundOrigin = $(this).attr('soundOrigin') + $('#soundText').hide() + $('#soundName').text(soundCredit) + $('#soundOrigin').text(soundOrigin ? ` (${soundOrigin})` : "") + $('#soundInfo').show() +}) + +$(document).on('mouseleave', '#icons div', function () { + $('#soundInfo').hide() + $('#soundText').show() +}) + +$(document).on('mouseover', '#actions div', function () { + $('#actionText').hide() + $('#actionInfo').show() + $('#actionName').text($(this).attr('info')) + if (!mobile && !settings.noActionShortcuts) $('#actionKey').text("(" + $(this).attr('key') + ")").show() + else $('#actionKey').hide() +}) + +$(document).on('mouseleave', '#actions div', function () { + $('#actionInfo').hide() + $('#actionText').show() +}) + +$(document).on('mouseover', '#sequence div', function () { + if (shiftHeld || altHeld) $(this).addClass('holdingShift') +}) + +$(document).on('mouseleave', '#sequence div', function () { + $('.holdingShift').removeClass('holdingShift') +}) + +$(document).on('mouseover', '#sequence section', function () { + if (ctrlHeld && hasDividers()) $(this).addClass('holdingCtrl') +}) + +$(document).on('mouseleave', '#sequence section', function () { + $('.holdingCtrl').removeClass('holdingCtrl') +}) + +$(document).on('mouseleave', '#sequence .removedDivider', function (e) { + if (!e.isTrigger) $(this).removeClass('removedDivider') +}) + +$(document).on('mouseover', '.hotbarTab', function() { + $('#hotbarText').css('color', 'var(--yellowfont)').text($(this).attr('desc')).show() + $('#hotbarContext').css('color', 'var(--yellowfont)').text("(" + ($(this).index() + 1) + ")").show() +}) + +$(document).on('mouseover', '#hotbarNotes div.sound', function() { + let origin = $(this).attr("soundOrigin") + $('#hotbarText').css('color', 'var(--greenfont)').text($(this).attr('soundName')).show() + if (!mobile) $('#hotbarContext').css('color', 'var(--greenfont)').text(origin ? `(${origin})` : "").show() +}) + +$(document).on('mouseover', '#hotbarNotes div.action', function() { + $('#hotbarText').css('color', 'var(--bluefont)').text($(this).attr('info')).show() + if (!mobile && !settings.noActionShortcuts) $('#hotbarContext').css('color', 'var(--bluefont)').text("(" + $(this).attr('key') + ")").show() +}) + +$(document).on('mouseleave', '#hotbarNotes div, .hotbarTab', function (e) { + $('#hotbarText, #hotbarContext').text("").hide() +}) + +// prevents right click menu from showing +$(document).on('click contextmenu', '.iconbox, #proHotbar', function () { return false }) + +$(document).on('click contextmenu', '#icons div, #hotbarNotes div.sound', function (event) { + cancel() + let sound = $(this).attr('sound') + let soundID = $(this).attr('str') + let added = $(this.outerHTML) + let pitch = null + if (event.type == "contextmenu") { + if (!settings.noAnimations) $(this).runAnimation('placed') + playSound(sound) + return false + } + if (pitch) added.append(`

${pitch >= 0 ? "+" : ""}${pitch}

`) // ??? + if (!sound.startsWith("_")) { + if (!settings.noAnimations) added.runAnimation('placed') + playSound(sound, { pitch: semitonesToPercent(pitch)} ) + } + added.removeAttr("soundorigin") + added.removeAttr("soundname") + updateRecent(soundID); + addToSequence(added) +}) + +$(document).on('click contextmenu', '#actions div, #hotbarNotes div.action', function (event) { + cancel() + let actionName = $(this).attr('action') + let action = actions.find(x => x.name == actionName) + if (!action) return + + let added = $(this.outerHTML) + added.removeAttr("info").removeAttr("key") + + if (action.amount) { + stash = added + if (event.type == "contextmenu" && action.default) { + if (action.twoValues) return addAdvancedAction(actionName, action.default) + else return addAction(actionName, action.default) + } + else { + if (shiftHeld) stash.attr("addToStart", true) + let actionPopup = $(`#action_${actionName}`) + actionPopup.css('display', 'flex') + + // action editing - fill values + if (replaceAction && replaceAction.attr("action") == actionName) { + if (action.twoValues) { + $(actionPopup.find('input').first().val(replaceAction.attr("val1"))).trigger('input') + $(actionPopup.find('input').last().val(replaceAction.attr("val2"))).trigger('input') + } + else $(actionPopup.find('input').first().val(replaceAction.attr("amount"))).trigger('input') + } + + return + } + } + else if (action.isTarget) { + let nextFree = 1 + while ($(`.action[action=jump][amount=${nextFree}]`).length && $(`.action[action=target][amount=${nextFree}]`).length) nextFree++ + added.attr("amount", nextFree).append(`

${nextFree}

`) + } + else if (action.set) { + if (action.showPlus) added.attr("num", "plus") + added.attr("amount", action.default).append(`

${getPrefix(added.attr("num"), action.default)}${action.default}

`) + } + if (action.stopSounds) stopSounds() + if (!settings.noAnimations) added.addClass('placed') + if (action.name == "divider") { + if (!shiftHeld && selectedDivider >= 0) selectedDivider++ + } + + updateRecent("." + actionName) + addToSequence(added) +}) + +function getPrefix(num, amt) { + return num == "plus" ? (amt >= 0 ? "+" : "") : num == "add" ? "+" : num == "multiply" ? "⨯" : "" +} + +function addToSequence(element, noPrepend, copyGroup) { + let startAttr = element.attr("addToStart") + if (startAttr) element.removeAttr("addToStart") + let prependMode = (shiftHeld && !noPrepend) || startAttr + let container = $(`#sequence section[group="${selectedDivider}"]`) + if (copyGroup >= 0 && selectedDivider != copyGroup) container = [] + if (!container.length) container = prependMode ? $(`#sequence section`).first() : $(`#sequence section`).last() + if (!container.length) { // jquery isn't exactly good at 'or', whatever + $("#sequence").append(`
`) + return addToSequence(element) + } + prependMode ? container.prepend(element) : appendToSection(container, element) + if (element.attr("action") == "divider") syncSections() +} + +// add to end, or second last if it ends with a divider +function appendToSection(container, element) { + let lastChild = container.children().last() + let hasDivider = (lastChild.attr("action") == "divider") + if (hasDivider) element.insertBefore(lastChild) + else container.append(element) +} + +function addAction(action, input, num="set", element=stash, dontAppend=false) { + if (typeof input == "string" && input.startsWith("#")) input = $(input).val() + if (!element || isNaN(input)) return + let amount = Number(Number(input).toFixed(3)) // tofixed converts to string lmao + let foundAction = actions.find(x => x.name == action) + let actionData = foundAction[num] + + amount = clamp(amount, actionData[0], actionData[1]) + let prefix = getPrefix(num, amount) + let amountStr = prefix + String(amount) + (actionData[3] || "") + //element.attr("min", actionData[0]).attr("max", actionData[1]) + if (num != "set") element.attr("num", num) + if (!isNaN(actionData[2])) element.attr("step", actionData[2]) + if (actionData[3]) element.attr("suffix", actionData[3]) + if (!settings.noAnimations) element.addClass('placed') + element.attr("amount", amount).find('p').text(amountStr) + + if (replaceAction) editAction(element) + else if (!dontAppend) addToSequence(element) + else return element + updateRecent("." + action) + if (stash) stash = null + $('.popup').hide() +} + +// eh i'm just gonna make a new function for this +function addAdvancedAction(action, inputs, element=stash, dontAppend=false) { + let foundAction = actions.find(x => x.name == action) + if (!foundAction || !Array.isArray(inputs)) return + let cleanInputs = inputs.map((x, y) => { + let bounds = foundAction.twoValues[y] + if (bounds == "color") x = x.match(colorRegex) ? x : defaultBG + else if (Array.isArray(bounds)) x = clamp(x, bounds[0], bounds[1]) + return x + }) + + element.attr("advanced", true).attr("val1", cleanInputs[0]).attr("val2", cleanInputs[1]) + + if (foundAction.colorMode) element.find('p').html(` ${cleanInputs[1]}`) + else element.find('p').text(`${cleanInputs[0]}, ${cleanInputs[1]}`) + + if (replaceAction) editAction(element) + else if (!dontAppend) addToSequence(element) + else return element + if (stash) stash = null + $('.popup').hide() +} + +function editAction(element) { + replaceAction.replaceWith(element) + replaceAction.runAnimation('placed') + replaceAction = null +} + +function syncSections() { + let noteGroups = [""] + let dividerIndex = 0 + let collapsedSections = [] + $('.placed').removeClass('placed') + $('.selectedDivider').removeClass('selectedDivider') + $('#sequence div').each(function() { + let isDivider = $(this).attr("action") == "divider" + if (isDivider) { + let oldSection = Number($(this).attr("section")) + if (oldSection >= 0 && $(this).parent().hasClass("sectionHidden")) collapsedSections.push(dividerIndex) + $(this).attr("section", dividerIndex) + } + + noteGroups[dividerIndex] += $(this).prop("outerHTML") + + if (isDivider) { + dividerIndex++ + noteGroups.push("") + } + }) + $('#sequence').html(noteGroups.map((x, y) => `
${x}
`).join("")) + if (dividerIndex > 0) $('#sectionSettings').show() + else $('#sectionSettings').hide() +} + +function hasDividers() { + return $("#sequence section").length > 1 +} + +function deselectSection() { + selectedDivider = -1 + if (ctrlHeld) $('.selectedDivider').addClass('removedDivider') + displaySection() +} + +function changeSection(change, scroll) { + let totalDividers = $('#sequence section').length + if (totalDividers <= 1) return selectedDivider = -1 + + if (selectedDivider == -1 && change < 0) selectedDivider = totalDividers - 1 + else if (selectedDivider == -1 && change > 0) selectedDivider = 0 + else selectedDivider = selectedDivider + change + + if (selectedDivider < 0) selectedDivider = totalDividers - 1 + else if (selectedDivider >= totalDividers) selectedDivider = 0 + + displaySection(scroll) +} + +function displaySection(scroll) { + $(".selectedDivider").removeClass("selectedDivider") + if (selectedDivider >= 0) { + let foundDivider = $(`#sequence section[group="${selectedDivider}"]`) + foundDivider.addClass("selectedDivider") + $('.requiresSelected').removeClass('cantSelect') + $('#selectedSection').text(selectedDivider + 1) + if (foundDivider.hasClass("sectionHidden")) { $('#hideSection').hide(); $('#showSection').show() } + else { $('#hideSection').show(); $('#showSection').hide() } + if (!active && scroll) $('#everything').stop().animate({ scrollTop: foundDivider.prop("offsetTop") - 150 }, 100) + } + else { + $('.requiresSelected').addClass('cantSelect') + $('#selectedSection').text("None") + $('#hideSection').show(); + $('#showSection').hide() + } +} + +function toggleSectionVisibility(section=selectedDivider) { + if (section < 0) return + $(`#sequence section[group="${section}"]`).toggleClass('sectionHidden') + displaySection() +} + +$(document).on('click contextmenu', '#sequence section', function (e) { + if (!ctrlHeld) return + e.stopPropagation() + if (!hasDividers()) return + let dividerGroup = parseInt($(this).attr("group")) + if (isNaN(dividerGroup)) return + + if (e.type == "contextmenu") { + toggleSectionVisibility(dividerGroup) + return false + } + + if ($(this).hasClass("selectedDivider")) return deselectSection() + + else { + selectedDivider = dividerGroup + if (isNaN(selectedDivider)) return selectedDivider = -1 + displaySection() + } +}) + +$(document).on('click', '#sequence div', function () { + if (ctrlHeld) return + cancel() + if (shiftHeld || altHeld) { + let copy = $(this).clone() + if (!settings.noAnimations) copy.addClass('placed') + copy.insertAfter($(this)) + playSound($(this).attr("sound"), { pitch: getPitch($(this)), volume: getVolume($(this)), stopPrevious: true }) + } + else $(this).remove() + if ($(this).attr("action") == "divider") { + deselectSection() + syncSections() + } +}) + +$(document).on('contextmenu', '#sequence div', function () { + if (ctrlHeld || active) return false + + let snd = $(this).attr("sound") + let acn = $(this).attr("action") + if (shiftHeld || altHeld) { // clone and append to end + let copy = $(this).clone() + if (!settings.noAnimations) copy.addClass('placed') + addToSequence(copy, true, Number($(this).parent().attr("group"))) + playSound(snd, { pitch: getPitch($(this)), volume: getVolume($(this)), stopPrevious: true}) + return false + } + + else if ($(this).hasClass("action") && acn) { + let foundAction = actions.find(x => x.name == acn) + if (!foundAction || !foundAction.amount) return false + replaceAction = $(this) + let actionBtn = $(`#actions div[action="${acn}"]`) + if (!actionBtn.length) return + actionBtn.trigger("click") + return false + } + + else if (!active && snd) { + if (!settings.noAnimations) $(this).runAnimation('placed') + playSound(snd, { pitch: getPitch($(this)), volume: getVolume($(this)), stopPrevious: true}) + return false + } +}) + +// hotbar tabs +$(document).on('click', '.hotbarTab:not(.selectedTab)', function () { + $('.selectedTab').removeClass('selectedTab') + $(this).addClass('selectedTab') + buildHotbar($(this).attr("tab")) +}) + +$("#everything").scroll(function(){ + if (settings.dontFadeProBar || mobile) return + let normal = 30 + let top = 200 / 1.5 + let dist = Math.min(normal, ($(this).scrollTop() - 200) / 1.5) + let percent = Math.max(0, ((dist + top) / (normal + top)) * 100) + + $("#proHotbar").css("bottom", dist + "px") + .css("opacity", percent + "%") + .css("visibility", percent < 2 ? "hidden" : "visible") +}); + +// no idea what this is for but probably some sorting bug +function whatthefuck(el, index) { + $('#sequence').sortable('cancel') + el.remove().insertAfter('#sequence div')[index] +} + +let lastY = null +let mobileCooldown = false +$(document).on('wheel touchmove', '#sequence div', function(event) { + let el = $(this) + if (event.type == "touchmove") { + if (mobileCooldown || $('.ui-sortable-helper').length) return + let clientY = event.originalEvent.touches[0].clientY; + let sensitivity = 50 + if (clientY > (lastY + sensitivity)) event.arrowDelta = 21 + else if (clientY < (lastY - sensitivity)) event.arrowDelta = -21 + else return + + lastY = clientY; + mobileCooldown = true + setTimeout(() => { mobileCooldown = false; $('#sequence div').scrollTop(200) }, 25); + $('#sequence div').scrollTop(200) + } + if (active || $(this).hasClass('ui-sortable-helper')) return + let isVolume = (ctrlHeld && el.attr("sound")) + let downward = (event.arrowDelta || event.originalEvent.deltaY) > 0 + let foundAction = actions.find(x => x.name == el.attr("action")) + let foundText = el.attr("amount") || el.attr("pitch") || el.find("p").text() + let shift = Number(foundText) || 0 + if (isVolume) { + shift = Number(el.attr("vol")) + if (isNaN(shift) || shift < 0) shift = 100 + } + + if (el.attr("sound") && !el.attr("str").startsWith("_")) { + let shiftChange = ((downward ? -1 : 1) * (shiftHeld ? (isVolume ? 10 : 6) : altHeld ? 0.2 : 1)) + + if (!isVolume) { + shift += shiftChange + shift = Number(clamp(shift, -60, 60).toFixed(2)) + let prefix = shift > 0 ? "+" : "" + if (foundText && shift == 0) el.find("p").remove() + else if (!el.find("p").length) el.append(`

${prefix + shift}

`) + else el.find("p").text(prefix + shift) + el.attr("pitch", shift) + playSound(el.attr("sound"), { pitch: semitonesToPercent(shift), volume: getVolume(el), stopPrevious: true }) + } + + else { + if (shiftChange == 1 || shiftChange == -1) shiftChange *= 2 + shift += shiftChange + shift = Number(clamp(shift, 0, 400).toFixed(2)) + if (shift == 100) el.find("vol").remove() + else if (!el.find("vol").length) el.append(`${shift}%`) + else el.find("vol").text(shift + "%") + el.attr("vol", shift) + playSound(el.attr("sound"), { pitch: getPitch(el), volume: shift / 100 / 2, stopPrevious: true }) + } + } + else if (el.attr("action") && el.attr("amount")) { + let bounds = foundAction[el.attr("num") || "set"] + let step = bounds[2] || 1 + if (shiftHeld) step *= 10 + else if (altHeld) step /= 10 + shift += downward ? step * -1 : step + shift = Number(shift.toFixed(4)) + shift = clamp(shift, bounds[0], bounds[1] || 999) + el.attr("amount", shift) + el.find("p").text(getPrefix(el.attr("num"), shift) + shift + (bounds[3] || "")) + } + else if (foundAction && el.attr("advanced")) { + let scrollInfo = foundAction.scroll + let valStr = scrollInfo[0] == 0 ? "val1" : "val2" + let scrollVal = +el.attr(valStr) + let twoBounds = foundAction.twoValues[scrollInfo[0]] + let step = +scrollInfo[1] || 1 + if (shiftHeld) step *= 10 + else if (altHeld) step /= 10 + scrollVal += downward ? step * -1 : step + scrollVal = clamp(scrollVal.toFixed(4), twoBounds[0], twoBounds[1]) + el.attr(valStr, scrollVal) + if (foundAction.colorMode) el.find("p").children().last().text(el.attr("val2")) + else el.find("p").text(`${el.attr("val1")}, ${el.attr("val2")}`) + } +}) + +// prevent ctrl+zoom +$('.iconbox, #proHotbar').bind('mousewheel DOMMouseScroll', function(e) { if (e.ctrlKey) e.preventDefault(); }); + +let intro = 0 +let lecture = null +let cachedIntros = [] +let mainNode = new GainNode(soundcloud) + +$('#caption').click(function() { + intro++ + if (intro >= intros.length) intro = 0 + $('#caption').text(intros[intro].name).runAnimation('placed') + stopIntro() + loadIntro() + cancel() +}) + +function loadIntro() { + let introPath = intros[intro].path + if (cachedIntros[introPath]) lecture = cachedIntros[introPath] + else { + fetch(introPath) + .then(res => res.arrayBuffer()) + .then(buffer => soundcloud.decodeAudioData(buffer)) + .then(data => { + cachedIntros[introPath] = data + lecture = data + }) + } +} +loadIntro() + +let activeIntro = null +function playIntro() { + stopIntro() + let introSound = new AudioBufferSourceNode(soundcloud, { buffer: lecture }) + mainNode.connect(soundcloud.destination) + introSound.connect(mainNode) + introSound.start() + activeIntro = introSound + introSound.addEventListener("ended", () => { + if (introSound.dead) return + beginSequence() + stopIntro() + }) +} + +function stopIntro() { + if (!activeIntro) return + activeIntro.dead = true + activeIntro.disconnect() + activeIntro = null +} + +document.addEventListener('scroll', function (event) { if ($(event.target).is("#sequence div")) $(event.target).scrollTop(200) }, true); +$('#sequence').on('DOMSubtreeModified', function(event) { $('#sequence div').scrollTop(200) }); + +// https://stackoverflow.com/a/45036752 +$.fn.runAnimation = function(className) { + if (settings.noAnimations) return + let el = $(this)[0] + el.style.animation = "none" + el.offsetHeight + el.style.animation = null + $(this).addClass(className) +} + +let stash = null +let replaceAction = null +let defaultTempo = 300 +let defaultVolume = 100 + +let preloaded = null +let active = false +let scrubbing = false +let ctrlHeld = false +let shiftHeld = false +let altHeld = false +let onCooldown = false +let selectedDivider = -1 + +let volume = 0 +updateTempo(defaultTempo) +setVolume(defaultVolume) + +function clamp(num, min, max) { return Math.min(Math.max(num , min), max) } +function percentToSemitones(percent) { return Math.log(percent, 2) * 12 } +function semitonesToPercent(semitones) { return Math.pow(2, Number(semitones) / 12) } +function getPitch(element) { return semitonesToPercent(element.find('p').text() || "0") } +function getVolume(element) { return Number(element.find('vol').text().slice(0, -1) || 100) / 200 } + +function cancel(cancelOptions={}) { + if ((!cancelOptions.keepAnimations && active) || cancelOptions.stopSounds) stopSounds() + active = false + preloaded = null + updateTempo(defaultTempo) + setVolume(100) + if (!cancelOptions.keepAnimations) { + $('.bounce').removeClass('bounce') + $('.pulse').removeClass('pulse') + } + $('.playInfo').show() + $('.stopInfo').hide() + $('.pinnedHidden').removeClass("pinnedHidden") + $('#proHotbar').removeClass("playing") + $('.triggered').removeClass('triggered') + $('#sequence p[triggeredCountdown]').each(function() { resetAmount($(this)) }) + $('html').removeAttr("style") + stopIntro() +} + +function resetAmount(el) { + let parent = el.parent() + el.text(getPrefix(parent.attr('num'), +parent.attr('amount')) + parent.attr('amount') + (parent.attr('suffix') || "")) + el.removeAttr("triggeredcountdown") +} + +// on play button +function startSequence(instant=false) { + if (onCooldown || active || $('#sequence div').length < 1) return + onCooldown = true + setTimeout(() => { onCooldown = false }, 250); + stopSounds() + cancel() + active = true + $('#sectionSettings.pinnedSettings').addClass("pinnedHidden") + $('#proHotbar').addClass("playing") + $('.playInfo').hide() + $('.stopInfo').show() + + preloaded = preloadSequence() + + if (!instant && selectedDivider < 0) playIntro() + else beginSequence() +} + +// after "don't you lecture me" +function beginSequence() { + if (!preloaded) return + $('.placed').removeClass('placed') + playSequence(preloaded) +} + +function updateTempo(BPM) { + $('#BPM').text(+BPM.toFixed(4)) +} + +function setVolume(percent) { + volume = Number(clamp(percent / 200, 0, 3).toFixed(4)) + $('#volume').text(+(volume * 200).toFixed(4)) +} + +// currently playing sounds are stored here +let activeSounds = []; + +// remove any finished sounds from the active list +function clearPlayedSounds() { + activeSounds = activeSounds.filter(x => !x.sound.finished) +} + +// destroy sound +function killSound(snd, vol, clearList=true) { + snd.finished = true + snd.disconnect() + vol.disconnect() + if (clearList) clearPlayedSounds() +} + +// stop all sounds +function stopSounds() { + activeSounds.forEach(x => killSound(x.sound, x.gainNode, false)) + nextSoundToQueue = playingSequence.length; + clearPlayedSounds() +} + +// fetches and prepares sound +let currentlyFetching = {} // prevent earrape when trying to load the same sound multiple times +async function fetchSound(name) { + if (!sounds[name] && name != "sounds/pause.wav") { + currentlyFetching[name] = true + let newSound = await fetch(name).then(res => res.arrayBuffer()).then(buffer => soundcloud.decodeAudioData(buffer)) + sounds[name] = newSound + delete currentlyFetching[name] + } +} + +async function playSound(name, soundSettings={}) { + if (!name || name == "sounds/_pause.wav") return + if (currentlyFetching[name] && !soundSettings.playAt) return + if (!sounds[name]) await fetchSound(name) + let snd = new AudioBufferSourceNode(soundcloud, { buffer: sounds[name], playbackRate: soundSettings.pitch || 1 }) + let vol = new GainNode(soundcloud, { gain: !isNaN(soundSettings.volume) ? soundSettings.volume : 0.5 }) + vol.connect(soundcloud.destination) + snd.connect(vol) + + if (soundSettings.stopPrevious) { // clear all sounds with the same name + let foundSameSounds = activeSounds.filter(x => x.name == name) + foundSameSounds.forEach(x => killSound(x.sound, x.gainNode, false)) + clearPlayedSounds() + } + + let start = soundSettings.playAt || 0; + activeSounds.push({sound: snd, gainNode: vol, index: soundSettings.index || 0, name, pitch: soundSettings.pitch || 1, volume, cut: false, start}) + snd.start(start) + + snd.addEventListener("ended", () => { + killSound(snd, vol, !active) + }) +} + +function cutSounds(time) { + activeSounds.forEach(x => { + if (!x.cut && x.start <= time) { + x.cut = true; + x.sound.stop(time); + } + }) +} + +$('.skippableAction').each(function() { + $(this).html('(right click to skip this popup)') + $(this).attr("mobile", "(hold to skip this popup)") +}) + +$('#clearsounds').click(function() { + cancel({ stopSounds: true }); + $('#sequence').html(''); + $('#saveName').val(''); + $('#sectionSettings').hide() + $('.popup').hide(); + filename = "sequence" + saveLocation = null +}) + +$(document).on('keydown', function(e) { + if (e.originalEvent.repeat || e.target.nodeName == "INPUT" || mobile) return + let popupVisible = $('.popup').is(":visible") + + if (e.which == 191) { // slash key (toggle menus) + if (shiftHeld && !$('#shortcutMenu').is(":visible")) $('#settingsMenu').toggle() + else if (ctrlHeld && !$('#settingsMenu').is(":visible")) $('#shortcutMenu').toggle() + } + + else if (!popupVisible) { + if ([13, 32].includes(e.which)) { // enter, space + if (!active) $('#playBtn').triggerHandler('contextmenu') + else $('#stopBtn').click() + e.preventDefault() + } + + else if ([38, 40].includes(e.which)) { // up, down + + if (ctrlHeld) { + let change = e.which == 40 ? 1 : -1 + changeSection(change, true) + } + + else { + $('#sequence div:hover').first().trigger({type: "wheel", arrowDelta: e.which == 40 ? 69 : -69}) + e.preventDefault() + } + + } + + else if (settings.proMode && e.which >= 48 && e.which <= 57) { // number keys + let num = e.which - 48 + if (num <= 0) return + $('.hotbarTab').eq(num - 1).trigger('click') + } + + else if (e.which == 83 && e.ctrlKey) { // ctrl + s + $((sexySaving && !e.shiftKey) ? '#saveBtn' : '#downloadBtn').trigger('click') + e.preventDefault() + } + + else if (e.which == 79 && e.ctrlKey) { // ctrl + o + $("#loadBtn").trigger('click') + e.preventDefault() + } + + else if (e.which == 68 && e.ctrlKey) { // ctrl + d + deselectSection() + e.preventDefault() + } + + else if (e.which == 80 && e.ctrlKey) { + $('#toggleProMode').trigger('click') + e.preventDefault() + } + + else if (!ctrlHeld && !altHeld && !settings.noActionShortcuts) { // actions + let foundAction = actions.find(x => x.shortcut == e.key.toLowerCase()) + if (foundAction) $(`#actions .action[action="${foundAction.name}"]`).trigger("click") + } + } + + // popups + else { + if (e.which == 27) { // esc + $('.popup:not(.importantPopup)').hide() + } + + else if (e.which == 13) { // enter + $('button[actionConfirm=true]:visible').first().click() + } + } + +}); + +function updateKeys(e) { + if (e.repeat || e.ctrlKey === undefined) return + if (e.key === 'Alt') { e.preventDefault() } + shiftHeld = e.shiftKey; + altHeld = e.altKey; + ctrlHeld = e.ctrlKey; + $('#sequence div:hover').trigger((shiftHeld || altHeld) ? 'mouseover' : 'mouseleave') + $('#sequence section:not(.ctrlHeld):hover').trigger(ctrlHeld ? 'mouseover' : 'mouseleave') +} + +$(document).on('keyup keydown click wheel touchmove', updateKeys) +$(window).on('blur focus', updateKeys) + +$(document).on('click', '.popup:not(.importantPopup)', function(e) { + if ($(e.target).is('.popup')) { + $('.popup').hide(); + stash = null + replaceAction = null + } +}); + +$('.colorPreview').click(function() { + $(`.colorSelector[colorgroup=${$(this).attr("colorgroup")}`).trigger('click') +}) + +$('.colorSelector').on('input', function() { + $(`.colorPreview[colorgroup=${$(this).attr("colorgroup")}`).css("background-color", $(this).val()) + $(`.colorTextbox[colorgroup=${$(this).attr("colorgroup")}`).val($(this).val().slice(1)) +}) + +$('.colorTextbox').on('input', function() { + let cleanVal = $(this).val().toLowerCase().replace(/[^a-f0-9]/g, "").slice(0, 6) + $(this).val(cleanVal) + let colorVal = "#" + cleanVal + if (!colorVal.match(colorRegex)) return + else $('.colorSelector').val(colorVal).trigger('input') +}) + +// settings + +let settings = {} +try { + settings = localStorage["🗿"] ? JSON.parse(localStorage["🗿"]) : {} + $('.settingBox[setting]').each(function() { + let setting = $(this).attr('setting') + let foundSetting = settings[setting] + if ($(this).attr('inverted')) foundSetting = !foundSetting + if (foundSetting) $(this).prop('checked', foundSetting) + }) +} +catch(e) { console.error(e) } + +if (settings.pinSectionButtons) $('#sectionSettings').addClass("pinnedSettings") +if (!settings.dontFadeProBar && settings.proMode) $('#everything').trigger('scroll') + +$(document).on('change click', '.settingBox', function() { + let settingName = $(this).attr('setting') + let val = $(this).prop('checked') + if ($(this).attr('inverted')) val = !val + if (!val) delete settings[settingName] + else settings[settingName] = val + localStorage["🗿"] = JSON.stringify(settings) + + if (settingName == "oldSaving") { + if (val) disableNewSaving() + else enableNewSaving() + } + + else if (settingName == "pinSectionButtons") { + if (settings.pinSectionButtons) { + if (active) $('#sectionSettings').addClass("pinnedHidden") + $('#sectionSettings').addClass("pinnedSettings") + } + else $('#sectionSettings').removeClass("pinnedSettings") + } + + else if (settingName == "proMode") { + if (settings.proMode) { + $('.hotbarTab').first().trigger('click') + $('#proHotbar').show() + $('#extraPadding').show() + let pageHeight = $('#everything').prop("scrollHeight") + if (!ctrlHeld) $('#everything').animate({ scrollTop: pageHeight + 1000 }) + } + else { + $('#proHotbar').hide() + $('#extraPadding').hide() + } + } +}); + +$('.extraSetting').hover(function() { + $('#settingInfo').text($(this).attr("title")) + $('#settingInfo').show() + $('#settingHelp').hide() +}, function() { + $('#settingInfo').hide() + $('#settingHelp').show() +}) + +$('.extraSetting').click(function(e) { + if (mobile || e.isTrigger || e.target.nodeName == "INPUT") return + $(this).find('input').trigger('click') +}) diff --git a/transfer.html b/transfer.html index 1b055c76..bd0d61cb 100644 --- a/transfer.html +++ b/transfer.html @@ -1,81 +1,74 @@ - - + - - - - - + - + - Selenite + Settings | Selenite - + - - + + - -
- - + +
+ Home + Bookmarklets + Games + Settings + Support + +
-

Welcome!

+
+

Transfer

Thank you for considering Selenite! Scroll down to view how to transfer game data from some of your favorite websites!


3kh0

Use the "Download Save" tool available on 3kh0, and click on the button below to import your data.


-

byvolp

-

Download your data by visiting the Doge Miner game, clicking Settings, and importing this save (make sure to Export Save on Doge Miner if you have any data first), then go to Stats and click Refresh. This will download a file, which you can then import using the Selenite Import Utility or clicking Upload Save on the main page of Selenite.

- Video Tutorial +

byvolp / any website with Doge Miner

+

Download your data by visiting the Doge Miner game, clicking Settings, and importing this save (make sure to Export Save on Doge Miner if you have any data first), then go to Stats and click Refresh. This will download a file, which you can then import using the Selenite Import Utility or clicking Upload Save on the main page of Selenite.

+ Video Tutorial

Other Websites

-

Use our Download Save Utility to download your save from any website, as long as you can use bookmarklets!

- -
+

Use our Download Save Utility to download your save from any website, as long as you can use bookmarklets!

+ + + - + \ No newline at end of file diff --git a/hardestgame/hardestgame.swf b/worldshardestgame/hardestgame.swf similarity index 100% rename from hardestgame/hardestgame.swf rename to worldshardestgame/hardestgame.swf diff --git a/hardestgame/icon.png b/worldshardestgame/icon.png similarity index 100% rename from hardestgame/icon.png rename to worldshardestgame/icon.png diff --git a/hardestgame/index.html b/worldshardestgame/index.html similarity index 100% rename from hardestgame/index.html rename to worldshardestgame/index.html