diff --git a/cookieclicker/main.js b/cookieclicker/main.js index 9cb4b401..f43c5e59 100644 --- a/cookieclicker/main.js +++ b/cookieclicker/main.js @@ -16029,12 +16029,10 @@ Game.Launch=function() Game.ModMenu=function() { - Game.modURL = ["https://klattmose.github.io/CookieClicker/KlattmoseUtilities.js?v=2.16"] + 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','mod',loc("Mod Menu"),loc("Mod Menu"),'Game.LoadMod(Game.modURL[0]);')+'
'+ - Game.WritePrefButton('mod1','mod1',loc("Mod Menu"),loc("Mod Menu"),'Game.ModMenu();')+'
'+ - Game.WritePrefButton('mod2','mod2',loc("Mod Menu"),loc("Mod Menu"),'Game.ModMenu();')+'
'+ - Game.WritePrefButton('mod3','mod3',loc("Mod Menu"),loc("Mod Menu"),'Game.ModMenu();')+'
' + 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)); } 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;e + 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/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(" + + + + Subway Surfers | 3kh0 + + + + + + + + + + diff --git a/subway-surfers/master-loader.js b/subway-surfers/master-loader.js new file mode 100644 index 00000000..edba896e --- /dev/null +++ b/subway-surfers/master-loader.js @@ -0,0 +1,29 @@ +"use strict"; +var scripts = document.getElementsByTagName("script"), + scriptUrl = scripts[scripts.length - 1].src, + root = scriptUrl.split("master-loader.js")[0], + loaders = { + unity: "unity.js", + "unity-beta": "unity-beta.js", + "unity-2020": "unity-2020.js" + }; +if (0 <= window.location.href.indexOf("pokiForceLocalLoader") && (loaders.unity = "/unity/dist/unity.js", loaders["unity-beta"] = "/unity-beta/dist/unity-beta.js", loaders["unity-2020"] = "/unity-2020/dist/unity-2020.js", root = "/loaders"), !window.config) throw Error("window.config not found"); +var loader = loaders[window.config.loader]; +if (!loader) throw Error('Loader "' + window.config.loader + '" not found'); +if (!window.config.unityWebglLoaderUrl) { + var versionSplit = window.config.unityVersion ? window.config.unityVersion.split(".") : [], + year = versionSplit[0], + minor = versionSplit[1]; + switch (year) { + case "2019": + window.config.unityWebglLoaderUrl = 1 === minor ? "UnityLoader.2019.1.js" : "UnityLoader.2019.2.js"; + break; + default: + window.config.unityWebglLoaderUrl = "UnityLoader.js" + } +} +var sdkScript = document.createElement("script"); +sdkScript.src = "./poki-sdk.js", sdkScript.onload = function() { + var i = document.createElement("script"); + i.src = root + loader, document.body.appendChild(i) +}, document.body.appendChild(sdkScript); \ No newline at end of file diff --git a/subway-surfers/poki-sdk-core-v2.234.2.js b/subway-surfers/poki-sdk-core-v2.234.2.js new file mode 100644 index 00000000..a2419d1c --- /dev/null +++ b/subway-surfers/poki-sdk-core-v2.234.2.js @@ -0,0 +1,5253 @@ +(() => { + var e = { + 564: (e, t, i) => { + var n, r, a; + !(function (i, o) { + if (i) { + var s = {}, + d = i.TraceKit, + A = [].slice, + c = "?", + l = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; + (s.noConflict = function () { + return (i.TraceKit = d), s; + }), + (s.wrap = function (e) { + return function () { + try { + return e.apply(this, arguments); + } catch (e) { + throw (s.report(e), e); + } + }; + }), + (s.report = (function () { + var e, + t, + n, + r, + a = [], + o = null, + d = null; + + function A(e, t, i) { + var n = null; + if (!t || s.collectWindowErrors) { + for (var r in a) + if (p(a, r)) + try { + a[r](e, t, i); + } catch (e) { + n = e; + } + if (n) throw n; + } + } + + function c(t, i, n, r, a) { + if (d) s.computeStackTrace.augmentStackTraceWithInitialElement(d, i, n, t), h(); + else if (a) A(s.computeStackTrace(a), !0, a); + else { + var o, + c = { + url: i, + line: n, + column: r, + }, + p = t; + if ("[object String]" === {}.toString.call(t)) { + var u = t.match(l); + u && ((o = u[1]), (p = u[2])); + } + (c.func = s.computeStackTrace.guessFunctionName(c.url, c.line)), + (c.context = s.computeStackTrace.gatherContext(c.url, c.line)), + A( + { + name: o, + message: p, + mode: "onerror", + stack: [c], + }, + !0, + null + ); + } + return !!e && e.apply(this, arguments); + } + + function u(e) { + A(s.computeStackTrace(e.reason), !0, e.reason); + } + + function h() { + var e = d, + t = o; + (d = null), (o = null), A(e, !1, t); + } + + function m(e) { + if (d) { + if (o === e) return; + h(); + } + var t = s.computeStackTrace(e); + throw ( + ((d = t), + (o = e), + setTimeout( + function () { + o === e && h(); + }, + t.incomplete ? 2e3 : 0 + ), + e) + ); + } + return ( + (m.subscribe = function (o) { + !(function () { + if (!0 === t) return; + (e = i.onerror), (i.onerror = c), (t = !0); + })(), + (function () { + if (!0 === r) return; + (n = i.onunhandledrejection), (i.onunhandledrejection = u), (r = !0); + })(), + a.push(o); + }), + (m.unsubscribe = function (o) { + for (var s = a.length - 1; s >= 0; --s) a[s] === o && a.splice(s, 1); + 0 === a.length && (t && ((i.onerror = e), (t = !1)), r && ((i.onunhandledrejection = n), (r = !1))); + }), + m + ); + })()), + (s.computeStackTrace = (function () { + var e = !1, + t = {}; + + function n(e) { + if ("string" != typeof e) return []; + if (!p(t, e)) { + var n = "", + r = ""; + try { + r = i.document.domain; + } catch (e) {} + var a = /(.*)\:\/\/([^:\/]+)([:\d]*)\/{0,1}([\s\S]*)/.exec(e); + a && + a[2] === r && + (n = (function (e) { + if (!s.remoteFetching) return ""; + try { + var t = (function () { + try { + return new i.XMLHttpRequest(); + } catch (e) { + return new i.ActiveXObject("Microsoft.XMLHTTP"); + } + })(); + return t.open("GET", e, !1), t.send(""), t.responseText; + } catch (e) { + return ""; + } + })(e)), + (t[e] = n ? n.split("\n") : []); + } + return t[e]; + } + + function r(e, t) { + var i, + r = /function ([^(]*)\(([^)]*)\)/, + a = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/, + o = "", + s = n(e); + if (!s.length) return c; + for (var d = 0; d < 10; ++d) + if (!u((o = s[t - d] + o))) { + if ((i = a.exec(o))) return i[1]; + if ((i = r.exec(o))) return i[1]; + } + return c; + } + + function a(e, t) { + var i = n(e); + if (!i.length) return null; + var r = [], + a = Math.floor(s.linesOfContext / 2), + o = a + (s.linesOfContext % 2), + d = Math.max(0, t - a - 1), + A = Math.min(i.length, t + o - 1); + t -= 1; + for (var c = d; c < A; ++c) u(i[c]) || r.push(i[c]); + return r.length > 0 ? r : null; + } + + function o(e) { + return e.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, "\\$&"); + } + + function d(e) { + return o(e).replace("<", "(?:<|<)").replace(">", "(?:>|>)").replace("&", "(?:&|&)").replace('"', '(?:"|")').replace(/\s+/g, "\\s+"); + } + + function A(e, t) { + for (var i, r, a = 0, o = t.length; a < o; ++a) + if ((i = n(t[a])).length && ((i = i.join("\n")), (r = e.exec(i)))) + return { + url: t[a], + line: i.substring(0, r.index).split("\n").length, + column: r.index - i.lastIndexOf("\n", r.index) - 1, + }; + return null; + } + + function l(e, t, i) { + var r, + a = n(t), + s = new RegExp("\\b" + o(e) + "\\b"); + return (i -= 1), a && a.length > i && (r = s.exec(a[i])) ? r.index : null; + } + + function h(e) { + if (!u(i && i.document)) { + for (var t, n, r, a, s = [i.location.href], c = i.document.getElementsByTagName("script"), l = "" + e, p = 0; p < c.length; ++p) { + var h = c[p]; + h.src && s.push(h.src); + } + if ((r = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/.exec(l))) { + var m = r[1] ? "\\s+" + r[1] : "", + g = r[2].split(",").join("\\s*,\\s*"); + (t = o(r[3]).replace(/;$/, ";?")), (n = new RegExp("function" + m + "\\s*\\(\\s*" + g + "\\s*\\)\\s*{\\s*" + t + "\\s*}")); + } else n = new RegExp(o(l).replace(/\s+/g, "\\s+")); + if ((a = A(n, s))) return a; + if ((r = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/.exec(l))) { + var v = r[1]; + if (((t = d(r[2])), (a = A((n = new RegExp("on" + v + "=[\\'\"]\\s*" + t + "\\s*[\\'\"]", "i")), s[0])))) return a; + if ((a = A((n = new RegExp(t)), s))) return a; + } + return null; + } + } + + function m(e) { + if (!e.stack) return null; + for ( + var t, + i, + n, + o = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, + s = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, + d = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + A = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, + p = /\((\S*)(?::(\d+))(?::(\d+))\)/, + h = e.stack.split("\n"), + m = [], + g = /^(.*) is undefined$/.exec(e.message), + v = 0, + f = h.length; + v < f; + ++v + ) { + if ((i = o.exec(h[v]))) { + var b = i[2] && 0 === i[2].indexOf("native"); + i[2] && 0 === i[2].indexOf("eval") && (t = p.exec(i[2])) && ((i[2] = t[1]), (i[3] = t[2]), (i[4] = t[3])), + (n = { + url: b ? null : i[2], + func: i[1] || c, + args: b ? [i[2]] : [], + line: i[3] ? +i[3] : null, + column: i[4] ? +i[4] : null, + }); + } else if ((i = d.exec(h[v]))) + n = { + url: i[2], + func: i[1] || c, + args: [], + line: +i[3], + column: i[4] ? +i[4] : null, + }; + else { + if (!(i = s.exec(h[v]))) continue; + i[3] && i[3].indexOf(" > eval") > -1 && (t = A.exec(i[3])) ? ((i[3] = t[1]), (i[4] = t[2]), (i[5] = null)) : 0 !== v || i[5] || u(e.columnNumber) || (m[0].column = e.columnNumber + 1), + (n = { + url: i[3], + func: i[1] || c, + args: i[2] ? i[2].split(",") : [], + line: i[4] ? +i[4] : null, + column: i[5] ? +i[5] : null, + }); + } + !n.func && n.line && (n.func = r(n.url, n.line)), (n.context = n.line ? a(n.url, n.line) : null), m.push(n); + } + return m.length + ? (m[0] && m[0].line && !m[0].column && g && (m[0].column = l(g[1], m[0].url, m[0].line)), + { + mode: "stack", + name: e.name, + message: e.message, + stack: m, + }) + : null; + } + + function g(e, t, i, n) { + var o = { + url: t, + line: i, + }; + if (o.url && o.line) { + (e.incomplete = !1), o.func || (o.func = r(o.url, o.line)), o.context || (o.context = a(o.url, o.line)); + var s = / '([^']+)' /.exec(n); + if ((s && (o.column = l(s[1], o.url, o.line)), e.stack.length > 0 && e.stack[0].url === o.url)) { + if (e.stack[0].line === o.line) return !1; + if (!e.stack[0].line && e.stack[0].func === o.func) return (e.stack[0].line = o.line), (e.stack[0].context = o.context), !1; + } + return e.stack.unshift(o), (e.partial = !0), !0; + } + return (e.incomplete = !0), !1; + } + + function v(e, t) { + for (var i, n, a, o = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i, d = [], A = {}, p = !1, u = v.caller; u && !p; u = u.caller) + if (u !== f && u !== s.report) { + if ( + ((n = { + url: null, + func: c, + args: [], + line: null, + column: null, + }), + u.name ? (n.func = u.name) : (i = o.exec(u.toString())) && (n.func = i[1]), + void 0 === n.func) + ) + try { + n.func = i.input.substring(0, i.input.indexOf("{")); + } catch (e) {} + if ((a = h(u))) { + (n.url = a.url), (n.line = a.line), n.func === c && (n.func = r(n.url, n.line)); + var m = / '([^']+)' /.exec(e.message || e.description); + m && (n.column = l(m[1], a.url, a.line)); + } + A["" + u] ? (p = !0) : (A["" + u] = !0), d.push(n); + } + t && d.splice(0, t); + var b = { + mode: "callers", + name: e.name, + message: e.message, + stack: d, + }; + return g(b, e.sourceURL || e.fileName, e.line || e.lineNumber, e.message || e.description), b; + } + + function f(t, o) { + var s = null; + o = null == o ? 0 : +o; + try { + if ( + ((s = (function (e) { + var t = e.stacktrace; + if (t) { + for ( + var i, + n = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i, + o = / line (\d+), column (\d+)\s*(?:in (?:]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i, + s = t.split("\n"), + d = [], + A = 0; + A < s.length; + A += 2 + ) { + var c = null; + if ( + ((i = n.exec(s[A])) + ? (c = { + url: i[2], + line: +i[1], + column: null, + func: i[3], + args: [], + }) + : (i = o.exec(s[A])) && + (c = { + url: i[6], + line: +i[1], + column: +i[2], + func: i[3] || i[4], + args: i[5] ? i[5].split(",") : [], + }), + c) + ) { + if ((!c.func && c.line && (c.func = r(c.url, c.line)), c.line)) + try { + c.context = a(c.url, c.line); + } catch (e) {} + c.context || (c.context = [s[A + 1]]), d.push(c); + } + } + return d.length + ? { + mode: "stacktrace", + name: e.name, + message: e.message, + stack: d, + } + : null; + } + })(t)), + s) + ) + return s; + } catch (t) { + e; + } + try { + if ((s = m(t))) return s; + } catch (t) { + e; + } + try { + if ( + ((s = (function (e) { + var t = e.message.split("\n"); + if (t.length < 4) return null; + var o, + s = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, + c = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, + l = /^\s*Line (\d+) of function script\s*$/i, + u = [], + h = i && i.document && i.document.getElementsByTagName("script"), + m = []; + for (var g in h) p(h, g) && !h[g].src && m.push(h[g]); + for (var v = 2; v < t.length; v += 2) { + var f = null; + if ((o = s.exec(t[v]))) + f = { + url: o[2], + func: o[3], + args: [], + line: +o[1], + column: null, + }; + else if ((o = c.exec(t[v]))) { + f = { + url: o[3], + func: o[4], + args: [], + line: +o[1], + column: null, + }; + var b = +o[1], + y = m[o[2] - 1]; + if (y) { + var k = n(f.url); + if (k) { + var w = (k = k.join("\n")).indexOf(y.innerText); + w >= 0 && (f.line = b + k.substring(0, w).split("\n").length); + } + } + } else if ((o = l.exec(t[v]))) { + var I = i.location.href.replace(/#.*$/, ""), + S = A(new RegExp(d(t[v + 1])), [I]); + f = { + url: I, + func: "", + args: [], + line: S ? S.line : o[1], + column: null, + }; + } + if (f) { + f.func || (f.func = r(f.url, f.line)); + var E = a(f.url, f.line), + _ = E ? E[Math.floor(E.length / 2)] : null; + E && _.replace(/^\s*/, "") === t[v + 1].replace(/^\s*/, "") ? (f.context = E) : (f.context = [t[v + 1]]), u.push(f); + } + } + return u.length + ? { + mode: "multiline", + name: e.name, + message: t[0], + stack: u, + } + : null; + })(t)), + s) + ) + return s; + } catch (t) { + e; + } + try { + if ((s = v(t, o + 1))) return s; + } catch (t) { + e; + } + return { + name: t.name, + message: t.message, + mode: "failed", + }; + } + return ( + (f.augmentStackTraceWithInitialElement = g), + (f.computeStackTraceFromStackProp = m), + (f.guessFunctionName = r), + (f.gatherContext = a), + (f.ofCaller = function (e) { + e = 1 + (null == e ? 0 : +e); + try { + throw new Error(); + } catch (t) { + return f(t, e + 1); + } + }), + (f.getSource = n), + f + ); + })()), + (s.extendToAsynchronousCallbacks = function () { + var e = function (e) { + var t = i[e]; + i[e] = function () { + var e = A.call(arguments), + i = e[0]; + return "function" == typeof i && (e[0] = s.wrap(i)), t.apply ? t.apply(this, e) : t(e[0], e[1]); + }; + }; + e("setTimeout"), e("setInterval"); + }), + s.remoteFetching || (s.remoteFetching = !0), + s.collectWindowErrors || (s.collectWindowErrors = !0), + (!s.linesOfContext || s.linesOfContext < 1) && (s.linesOfContext = 11), + (r = []), + void 0 === (a = "function" == typeof (n = s) ? n.apply(t, r) : n) || (e.exports = a); + } + + function p(e, t) { + return Object.prototype.hasOwnProperty.call(e, t); + } + + function u(e) { + return void 0 === e; + } + })("undefined" != typeof window ? window : i.g); + }, + }, + t = {}; + + function i(n) { + if (t[n]) return t[n].exports; + var r = (t[n] = { + exports: {}, + }); + return e[n](r, r.exports, i), r.exports; + } + (i.n = (e) => { + var t = e && e.__esModule ? () => e.default : () => e; + return ( + i.d(t, { + a: t, + }), + t + ); + }), + (i.d = (e, t) => { + for (var n in t) + i.o(t, n) && + !i.o(e, n) && + Object.defineProperty(e, n, { + enumerable: !0, + get: t[n], + }); + }), + (i.g = (function () { + if ("object" == typeof globalThis) return globalThis; + try { + return this || new Function("return this")(); + } catch (e) { + if ("object" == typeof window) return window; + } + })()), + (i.o = (e, t) => Object.prototype.hasOwnProperty.call(e, t)), + (() => { + "use strict"; + var e = i(564), + t = i.n(e); + const n = { + ready: "pokiAppReady", + adblocked: "pokiAppAdblocked", + ads: { + completed: "pokiAdsCompleted", + error: "pokiAdsError", + impression: "pokiAdsImpression", + durationChange: "pokiAdsDurationChange", + limit: "pokiAdsLimit", + ready: "pokiAdsReady", + requested: "pokiAdsRequested", + prebidRequested: "pokiAdsPrebidRequested", + skipped: "pokiAdsSkipped", + started: "pokiAdsStarted", + stopped: "pokiAdsStopped", + busy: "pokiAdsBusy", + position: { + preroll: "PP", + midroll: "PM", + rewarded: "PR", + display: "DP", + }, + video: { + clicked: "pokiVideoAdsClicked", + firstQuartile: "pokiVideoAdsFirstQuartile", + midPoint: "pokiVideoAdsMidPoint", + thirdQuartile: "pokiVideoAdsThirdQuartile", + error: "pokiVideoAdsError", + loaderError: "pokiVideoAdsLoaderError", + paused: "pokiVideoAdsPauseTriggered", + resumed: "pokiVideoAdsResumedTriggered", + progress: "pokiVideoAdsProgress", + buffering: "pokiVideoAdsBuffering", + }, + }, + info: { + messages: { + timeLimit: "The ad-request was not processed, because of a time constraint", + prerollLimit: "The ad-request was cancelled, because we're not allowed to show a preroll", + }, + }, + message: { + event: "pokiMessageEvent", + sdkDetails: "pokiMessageSdkDetails", + toggleProgrammaticAds: "pokiMessageToggleProgrammaticAds", + }, + tracking: { + custom: "pokiTrackingCustom", + togglePlayerAdvertisingConsent: "pokiTrackingTogglePlayerAdvertisingConsent", + debugTrueInProduction: "pokiMessageDebugTrueProduction", + screen: { + gameplayStart: "pokiTrackingScreenGameplayStart", + gameplayStop: "pokiTrackingScreenGameplayStop", + gameLoadingStarted: "pokiTrackingScreenGameLoadingStarted", + gameLoadingProgress: "pokiTrackingScreenGameLoadingProgress", + gameLoadingFinished: "pokiTrackingScreenGameLoadingFinished", + commercialBreak: "pokiTrackingScreenCommercialBreak", + rewardedBreak: "pokiTrackingScreenRewardedBreak", + happyTime: "pokiTrackingScreenHappyTime", + firstRound: "pokiTrackingScreenFirstRound", + roundStart: "pokiTrackingScreenRoundStart", + roundEnd: "pokiTrackingScreenRoundEnd", + gameInteractive: "pokiTrackingScreenGameInteractive", + displayAd: "pokiTrackingScreenDisplayAdRequest", + destroyAd: "pokiTrackingScreenDisplayAdDestroy", + }, + sdk: { + status: { + initialized: "pokiTrackingSdkStatusInitialized", + failed: "pokiTrackingSdkStatusFailed", + }, + }, + ads: { + status: { + busy: "pokiTrackingAdsStatusBusy", + completed: "pokiTrackingAdsStatusCompleted", + error: "pokiTrackingAdsStatusError", + displayError: "pokiTrackingAdsStatusDisplayError", + impression: "pokiTrackingAdsStatusImpression", + limit: "pokiTrackingAdsStatusLimit", + ready: "pokiTrackingAdsStatusReady", + requested: "pokiTrackingAdsStatusRequested", + prebidRequested: "pokiTrackingAdsStatusPrebidRequested", + skipped: "pokiTrackingAdsStatusSkipped", + started: "pokiTrackingAdsStatusStarted", + buffering: "pokiTrackingAdsStatusBuffering", + }, + video: { + clicked: "pokiTrackingAdsVideoClicked", + error: "pokiTrackingAdsVideoError", + loaderError: "pokiTrackingAdsVideoLoaderError", + progress: "pokiTrackingAdsVideoProgress", + paused: "pokiTrackingAdsVideoPaused", + resumed: "pokiTrackingAdsVideoResumed", + }, + display: { + requested: "pokiTrackingScreenDisplayAdRequested", + impression: "pokiTrackingScreenDisplayAdImpression", + viewable: "pokiTrackingScreenDisplayAdViewable", + }, + }, + }, + }; + const r = (function () { + function e() {} + return (e.debug = !1), (e.log = !1), e; + })(); + var a = function () { + return ( + (a = + Object.assign || + function (e) { + for (var t, i = 1, n = arguments.length; i < n; i++) for (var r in (t = arguments[i])) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e; + }), + a.apply(this, arguments) + ); + }; + const o = (function () { + function e() {} + return ( + (e.clearEventListeners = function () { + this.listeners = {}; + }), + (e.removeEventListener = function (e, t) { + if (Object.prototype.hasOwnProperty.call(this.listeners, e)) { + var i = this.listeners[e].indexOf(t); + -1 !== i && this.listeners[e].splice(i, 1); + } + }), + (e.addEventListener = function (e, t, i) { + var n = this; + if ((void 0 === i && (i = !1), (i = !!i), Object.prototype.hasOwnProperty.call(this.listeners, e) || (this.listeners[e] = []), i)) { + var r = function (i) { + n.removeEventListener.bind(n)(e, r), t(i); + }; + this.listeners[e].push(r); + } else this.listeners[e].push(t); + }), + (e.dispatchEvent = function (e, t) { + void 0 === t && (t = {}), !r.debug || (window.process && window.process.env && "test" === window.process.env.NODE_ENV) || console.info(e, t); + for (var i = Object.keys(this.listeners), n = 0; n < i.length; n++) { + var o = i[n]; + if (e === o) for (var s = this.listeners[o], d = 0; d < s.length; d++) s[d](a(a({}, this.dataAnnotations), t)); + } + }), + (e.setDataAnnotations = function (e) { + this.dataAnnotations = a(a({}, this.dataAnnotations), e); + }), + (e.getDataAnnotations = function () { + return this.dataAnnotations; + }), + (e.clearAnnotations = function () { + this.dataAnnotations = {}; + }), + (e.listeners = {}), + (e.dataAnnotations = {}), + e + ); + })(); + const s = function (e, t) { + var i = !1; + return ( + Object.keys(t).forEach(function (n) { + t[n] === e && (i = !0); + }), + i + ); + }; + + function d() { + var e, + t = (null === (e = null === window || void 0 === window ? void 0 : window.location) || void 0 === e ? void 0 : e.hostname) || ""; + return "game-cdn.poki.com" === t || t.endsWith(".poki-gdn.com"), !1; + } + var A = { + adTagUrl: "", + adTiming: { + preroll: !1, + timeBetweenAds: 12e4, + timePerTry: 7e3, + startAdsAfter: 12e4, + }, + waterfallRetries: d() ? 1 : 2, + }, + c = { + AE: { + video: { + amazon: 0.7, + appnexus: 0.76, + }, + }, + AL: { + video: { + amazon: 0.75, + }, + }, + AT: { + video: { + appnexus: 0.6, + onetag: 0.81, + pubmatic: 0.82, + }, + }, + AU: { + video: { + pubmatic: 0.43, + rubicon: 0.38, + }, + }, + AZ: { + video: { + amazon: 0.72, + onetag: 0.84, + }, + }, + BE: { + video: { + amazon: 0.83, + appnexus: 0.79, + }, + }, + BG: { + video: { + appnexus: 0.72, + }, + }, + BO: { + video: { + ix: 0.84, + }, + }, + CA: { + video: { + amazon: 0.84, + pubmatic: 0.81, + }, + }, + CL: { + video: { + amazon: 0.78, + }, + }, + CO: { + video: { + amazon: 0.85, + }, + }, + CZ: { + video: { + appnexus: 0.77, + }, + }, + DE: { + video: { + appnexus: 0.21, + onetag: 0.81, + }, + }, + DO: { + video: { + onetag: 0.84, + }, + }, + DZ: { + video: { + amazon: 0.41, + }, + }, + EG: { + video: { + amazon: 0.15, + rubicon: 0.63, + }, + }, + ES: { + video: { + appnexus: 0.66, + ix: 0.84, + onetag: 0.82, + openx: 0.84, + pubmatic: 0.79, + }, + }, + FI: { + video: { + appnexus: 0.43, + }, + }, + FR: { + video: { + appnexus: 0.26, + ix: 0.53, + onetag: 0.81, + }, + }, + GB: { + video: { + districtm: 0.83, + onetag: 0.8, + }, + }, + GT: { + video: { + amazon: 0.74, + }, + }, + HU: { + video: { + amazon: 0.55, + appnexus: 0.65, + }, + }, + IE: { + video: { + onetag: 0.66, + }, + }, + IN: { + video: { + appnexus: 0.72, + }, + }, + IT: { + video: { + appnexus: 0.46, + onetag: 0.46, + }, + }, + JP: { + video: { + onetag: 0.84, + }, + }, + KE: { + video: { + onetag: 0.84, + }, + }, + KW: { + video: { + onetag: 0.83, + }, + }, + KZ: { + video: { + onetag: 0.8, + }, + }, + LU: { + video: { + onetag: 0.82, + }, + }, + MA: { + video: { + amazon: 0.68, + }, + }, + MD: { + video: { + onetag: 0.7, + }, + }, + MX: { + video: { + amazon: 0.47, + pubmatic: 0.75, + }, + }, + NL: { + video: { + ix: 0.39, + onetag: 0.81, + openx: 0.71, + richaudience: 0.72, + }, + }, + NZ: { + video: { + amazon: 0.76, + openx: 0.83, + }, + }, + OM: { + video: { + onetag: 0.84, + }, + }, + PE: { + video: { + amazon: 0.82, + }, + }, + PL: { + video: { + appnexus: 0.84, + districtm: 0.71, + onetag: 0.73, + }, + }, + PR: { + video: { + amazon: 0.83, + }, + }, + RU: { + video: { + amazon: 0.78, + onetag: 0.84, + }, + }, + SA: { + video: { + amazon: 0.83, + appnexus: 0.74, + }, + }, + SK: { + video: { + appnexus: 0.79, + }, + }, + TN: { + video: { + amazon: 0.61, + }, + }, + UA: { + video: { + onetag: 0.8, + }, + }, + US: { + video: { + pubmatic: 0.01, + }, + }, + VE: { + video: { + onetag: 0.8, + }, + }, + VN: { + video: { + appnexus: 0.81, + }, + }, + }; + const l = A; + const p = function (e) { + return e instanceof Array ? e : [e]; + }; + const u = (function () { + function e(e) { + void 0 === e && (e = {}), + this.setTimings(e), + (this.timingIdx = { + timePerTry: 0, + }), + (this.timers = { + timePerTry: void 0, + timeBetweenAds: void 0, + startAdsAfter: void 0, + }), + o.addEventListener(n.ads.requested, this.startTimeBetweenAdsTimer.bind(this)), + o.addEventListener(n.ads.completed, this.startTimeBetweenAdsTimer.bind(this)), + o.addEventListener(n.ads.stopped, this.startTimeBetweenAdsTimer.bind(this)); + } + return ( + (e.prototype.setTimings = function (e) { + var t = l.adTiming, + i = e.preroll, + n = void 0 === i ? t.preroll : i, + r = e.timePerTry, + a = void 0 === r ? t.timePerTry : r, + o = e.timeBetweenAds, + s = void 0 === o ? t.timeBetweenAds : o, + d = e.startAdsAfter, + A = void 0 === d ? t.startAdsAfter : d; + this.timings = { + preroll: !1 !== n, + timePerTry: p(a), + timeBetweenAds: s, + startAdsAfter: A, + }; + }), + (e.prototype.startTimeBetweenAdsTimer = function () { + this.startTimer("timeBetweenAds"); + }), + (e.prototype.startStartAdsAfterTimer = function () { + this.startTimer("startAdsAfter"); + }), + (e.prototype.requestPossible = function () { + return !this.timers.timeBetweenAds && !this.timers.startAdsAfter; + }), + (e.prototype.startWaterfallTimer = function (e) { + this.startTimer("timePerTry", e); + }), + (e.prototype.stopWaterfallTimer = function () { + this.stopTimer("timePerTry"); + }), + (e.prototype.nextWaterfallTimer = function () { + this.nextTiming("timePerTry"); + }), + (e.prototype.resetWaterfallTimerIdx = function () { + this.resetTimingIdx("timePerTry"); + }), + (e.prototype.stopTimer = function (e) { + this.timers[e] && (clearTimeout(this.timers[e]), (this.timers[e] = void 0)); + }), + (e.prototype.startTimer = function (e, t) { + var i = this; + void 0 === t && (t = function () {}), + this.getTiming(e) <= 0 + ? t() + : (this.timers[e] && clearTimeout(this.timers[e]), + (this.timers[e] = window.setTimeout(function () { + i.stopTimer(e), t(); + }, this.getTiming(e)))); + }), + (e.prototype.getTiming = function (e) { + var t = this.timings[e]; + return t instanceof Array ? t[this.timingIdx[e]] : t; + }), + (e.prototype.nextTiming = function (e) { + if (void 0 === this.timingIdx[e]) throw new Error("AdTimings Error: " + e + " does not have multiple timers"); + this.timingIdx[e] = (this.timingIdx[e] + 1) % this.timings[e].length; + }), + (e.prototype.resetTimingIdx = function (e) { + if (void 0 === this.timingIdx[e]) throw new Error("AdTimings Error: " + e + " does not have multiple timers"); + this.timingIdx[e] = 0; + }), + (e.prototype.prerollPossible = function () { + return this.timings.preroll; + }), + e + ); + })(); + var h = document.location.hostname; + + function m(e) { + var t = new RegExp(e + "=([^;]+)(?:;|$)").exec(document.cookie); + return t ? t[1] : ""; + } + + function g(e, t) { + document.cookie = e + "=" + t + "; path=/; samesite=none; secure; max-age=15552000; domain=" + h; + } + h.endsWith("poki-gdn.com") && (h = "poki-gdn.com"); + var v = function (e, t, i, n) { + return new (i || (i = Promise))(function (r, a) { + function o(e) { + try { + d(n.next(e)); + } catch (e) { + a(e); + } + } + + function s(e) { + try { + d(n.throw(e)); + } catch (e) { + a(e); + } + } + + function d(e) { + var t; + e.done + ? r(e.value) + : ((t = e.value), + t instanceof i + ? t + : new i(function (e) { + e(t); + })).then(o, s); + } + d((n = n.apply(e, t || [])).next()); + }); + }, + f = function (e, t) { + var i, + n, + r, + a, + o = { + label: 0, + sent: function () { + if (1 & r[0]) throw r[1]; + return r[1]; + }, + trys: [], + ops: [], + }; + return ( + (a = { + next: s(0), + throw: s(1), + return: s(2), + }), + "function" == typeof Symbol && + (a[Symbol.iterator] = function () { + return this; + }), + a + ); + + function s(a) { + return function (s) { + return (function (a) { + if (i) throw new TypeError("Generator is already executing."); + for (; o; ) + try { + if (((i = 1), n && (r = 2 & a[0] ? n.return : a[0] ? n.throw || ((r = n.return) && r.call(n), 0) : n.next) && !(r = r.call(n, a[1])).done)) return r; + switch (((n = 0), r && (a = [2 & a[0], r.value]), a[0])) { + case 0: + case 1: + r = a; + break; + case 4: + return ( + o.label++, + { + value: a[1], + done: !1, + } + ); + case 5: + o.label++, (n = a[1]), (a = [0]); + continue; + case 7: + (a = o.ops.pop()), o.trys.pop(); + continue; + default: + if (!((r = o.trys), (r = r.length > 0 && r[r.length - 1]) || (6 !== a[0] && 2 !== a[0]))) { + o = 0; + continue; + } + if (3 === a[0] && (!r || (a[1] > r[0] && a[1] < r[3]))) { + o.label = a[1]; + break; + } + if (6 === a[0] && o.label < r[1]) { + (o.label = r[1]), (r = a); + break; + } + if (r && o.label < r[2]) { + (o.label = r[2]), o.ops.push(a); + break; + } + r[2] && o.ops.pop(), o.trys.pop(); + continue; + } + a = t.call(e, o); + } catch (e) { + (a = [6, e]), (n = 0); + } finally { + i = r = 0; + } + if (5 & a[0]) throw a[1]; + return { + value: a[0] ? a[1] : void 0, + done: !0, + }; + })([a, s]); + }; + } + }, + b = function (e, t, i) { + if (i || 2 === arguments.length) for (var n, r = 0, a = t.length; r < a; r++) (!n && r in t) || (n || (n = Array.prototype.slice.call(t, 0, r)), (n[r] = t[r])); + return e.concat(n || Array.prototype.slice.call(t)); + }, + y = "poki_gcuid", + k = m(y); + const w = (function () { + function e() {} + return ( + (e.collectAndLog = function () { + return v(this, void 0, void 0, function () { + var e, t, i, n, r; + return f(this, function (a) { + switch (a.label) { + case 0: + return a.trys.push([0, 5, , 6]), [4, window.cookieStore.getAll()]; + case 1: + return (e = a.sent()), window.indexedDB.databases ? [4, window.indexedDB.databases()] : [3, 3]; + case 2: + return (i = a.sent()), [3, 4]; + case 3: + (i = []), (a.label = 4); + case 4: + return ( + (t = i), + (n = b( + b( + b( + [], + e.map(function (e) { + return { + name: e.name, + expire_seconds: Math.round((e.expires - Date.now()) / 1e3), + type: "cookie", + }; + }), + !0 + ), + Object.keys(window.localStorage).map(function (e) { + return { + name: e, + expire_seconds: 15552e3, + type: "localStorage", + }; + }), + !0 + ), + t.map(function (e) { + return { + name: e.name, + expire_seconds: 0, + type: "idb", + }; + }), + !0 + )), + (r = { + cookies: n, + p4d_game_id: tt.gameId, + user_id: k, + }), + window + .fetch("https://t.poki.io/game-cookies", { + method: "post", + body: JSON.stringify(r), + }) + .catch(), + [3, 6] + ); + case 5: + return a.sent(), [3, 6]; + case 6: + return [2]; + } + }); + }); + }), + (e.trackSavegames = function () { + window.cookieStore && window.cookieStore.getAll && tt.gameId && (Math.random() > 0.01 || (k || ((k = Math.random().toString(36).substr(2, 9)), g(y, k)), e.collectAndLog(), setInterval(e.collectAndLog, 12e4))); + }), + e + ); + })(), + I = function () { + return window.location.href; + }, + S = function () { + return "undefined" != typeof navigator && /(?:phone|windows\s+phone|ipod|blackberry|(?:android|bb\d+|meego|silk|googlebot) .+? mobile|palm|windows\s+ce|opera\smini|avantgo|mobilesafari|docomo)/i.test(navigator.userAgent); + }, + E = function () { + return "undefined" != typeof navigator && /(?:ipad|playbook|(?:android|bb\d+|meego|silk)(?! .+? mobile))/i.test(navigator.userAgent); + }, + _ = function (e, t) { + var i; + if ("undefined" == typeof window && !t) return ""; + e = e.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var n = new RegExp("(?:[\\?&]|^)" + e + "=([^&#]*)").exec(t || (null === (i = null === window || void 0 === window ? void 0 : window.location) || void 0 === i ? void 0 : i.search) || ""); + return null === n ? "" : decodeURIComponent(n[1].replace(/\+/g, " ")); + }, + x = function () { + return "undefined" != typeof navigator && /MSIE \\d|Trident.*rv:/i.test(navigator.userAgent); + }; + var C = { + 1: "eNjDw1AVTr", + 2: "HkuQJaWnBa", + 3: "AfRKClvdYk", + 4: "Db7uYbsnlW", + 5: "UprdYKe74r", + 6: "tBCJC9E6Y4", + 7: "AfRKClvdYk", + 8: "tJ44vpLpuM", + 9: "mF5ASaga4A", + 10: "rKV8rMwiwk", + 11: "SvK8BH5qS5", + 12: "SpfIMxnWTS", + 13: "ysxIcmt3tW", + 14: "gLmtGS4aUq", + 15: "RU6ebIFLw9", + 16: "r9G4tVMYw7", + 17: "SgcDa5B8s1", + 18: "AfRKClvdYk", + 19: "DNZX8XdJXV", + 20: "39o4YUyZTX", + 21: "5sb2HFpz5a", + 22: "pgXzCJZipE", + 23: "Oani8EAGI9", + 24: "IzCeh7d7vW", + 25: "I5vRNtjoMr", + 26: "KpySvG7luq", + 27: "dK42J4rI14", + 28: "HuYorw3fRg", + 29: "mf84cGYc1h", + 30: "9ALgxEyGXU", + 31: "lBzSdVGY8F", + 32: "hKYgk9Wb8q", + 33: "xPBr8E54eE", + 34: "ZvIK2WKC7G", + 35: "7kiYi3zlIX", + 36: "VpygYMTDgm", + 37: "mis9Mt4np4", + 38: "AfRKClvdYk", + 41: "Fqmjp9Hit3", + 42: "lS2XGg058L", + 43: "AfRKClvdYk", + 46: "AfRKClvdYk", + 47: "21OybbiIdc", + 48: "AfRKClvdYk", + 49: "CMVoMvvEmu", + 50: "IoQrhRb3wU", + 52: "AfRKClvdYk", + 53: "AfRKClvdYk", + }; + const T = function (e) { + return 15 === e ? "MP_gIE1VDieUi" : C[e] || ""; + }; + var B = ["AU", "CA", "IE", "NZ", "US", "GB"], + P = ["AT", "BE", "DK", "FI", "FR", "DE", "JA", "NO", "NL", "SA", "ES", "SE", "CH", "AE", "IT"], + D = ["BR", "CL", "CZ", "HU", "PL", "PT", "RU", "SK", "TH"], + M = ["AR", "BG", "CO", "EC", "GR", "IN", "MX", "PE", "PH", "RO", "TR", "UY"]; + + function R(e) { + return B.includes(e) ? 0.13 : P.includes(e) ? 0.07 : D.includes(e) ? 0.04 : 0.02; + } + + function L(e) { + return "US" === e ? 1.5 : B.includes(e) ? 0.5 : P.includes(e) ? 0.15 : D.includes(e) ? 0.08 : M.includes(e) ? 0.03 : 0.02; + } + const z = function (e) { + r.debug + ? console.log(e) + : fetch("https://t.poki.io/adserver", { + method: "POST", + mode: "no-cors", + body: JSON.stringify(e), + }); + }; + var O = [ + "https://a.poki.com/houseads/duo_survival_3.xml", + "https://a.poki.com/houseads/like_a_king.xml", + "https://a.poki.com/houseads/merge_cyber_racers.xml", + "https://a.poki.com/houseads/subway_surfers.xml", + "https://a.poki.com/houseads/tower_crush_compressed.xml", + ]; + + function G() { + return (e = O)[Math.floor(Math.random() * e.length)]; + var e; + } + var j = { + v_k0treo: 2.5, + v_qr1wxs: 7.5, + v_9diccg: 19, + v_13q0xkw: 0.25, + v_dn33ls: 1, + v_z07u2o: 1.5, + v_1400iyo: 2.25, + v_9w8kxs: 3, + v_ufej9c: 3.5, + v_10960ao: 4.25, + v_1ksbym8: 4.75, + v_1ag9340: 5.25, + v_1tbhh4w: 5.75, + v_jjcgzk: 6.5, + v_brnu9s: 7, + v_1wscef4: 7.75, + v_q22xhc: 8.5, + v_f8irk0: 9, + v_1rik45c: 9.75, + v_lxhyww: 10.5, + v_a9z0u8: 11, + v_1yhiww0: 11.75, + v_10mwg74: 12.25, + v_1ji4u80: 12.75, + v_wm2c5c: 13.5, + v_2na6tc: 14, + v_1myzri8: 14.75, + v_3pzm68: 6, + v_16kerr4: 6.25, + v_1mdrmkg: 6.75, + v_1ga0k5c: 7.25, + v_5iwz5s: 8, + v_12tk934: 8.25, + v_1hsybr4: 8.75, + v_1cj61hc: 9.25, + v_y3r5kw: 9.5, + v_94ow0: 10, + v_15woqgw: 10.25, + v_1orx4hs: 10.75, + v_1d4e6f4: 11.25, + v_t57ev4: 11.5, + v_783hmo: 12, + v_m7hkao: 12.5, + v_hmo9hc: 13, + v_19djnr4: 13.25, + v_1twpm2o: 13.75, + v_17zlou8: 14.25, + v_ign1mo: 14.5, + v_ccvz7k: 15, + v_1f7b4sg: 15.25, + v_snq4g0: 15.5, + v_5wnf28: 16, + v_137aozk: 16.25, + v_1j0njsw: 16.75, + v_1b8yx34: 17.25, + v_yhhlhc: 17.5, + v_25swe8: 18, + v_15081z4: 18.25, + v_1pje0ao: 18.75, + v_1eptudc: 19.25, + v_1xl28e8: 19.75, + v_gfliio: 21, + v_3y3sao: 22, + v_ixhuyo: 22.5, + v_ro52io: 23.5, + v_qa73ls: 24.5, + v_emo5j4: 25, + v_yq5fk: 26, + v_aobxts: 27, + v_6shmgw: 28, + v_natgqo: 28.5, + v_x0f94w: 29.5, + v_d2hfr4: 31, + v_dch14w: 33, + v_1jyadc: 34, + v_8p5tz4: 36, + v_fwv9xc: 37, + v_c60r9c: 39, + v_58awow: 40, + v_bbcow: 42, + v_a0x534: 43, + v_hdmdq8: 45, + v_2e8b28: 46, + v_5nljb4: 48, + v_1wr0n4: 50, + v_pam1og: 0.5, + v_1ipf08w: 0.75, + v_1axqdj4: 1.25, + v_1qr38cg: 1.75, + v_15ldds: 2, + v_1q248w0: 2.75, + v_1eelatc: 3.25, + v_1x9tou8: 3.75, + v_8iam0w: 4, + v_nhooow: 4.5, + v_fq01z4: 5, + v_w0u77k: 5.5, + v_1vi5a0w: 15.75, + v_orvt34: 16.5, + v_dybn5s: 17, + v_1q8czr4: 17.75, + v_l11af4: 18.5, + v_uqn2tc: 19.5, + v_7zkdfk: 20, + v_o7a58g: 20.5, + v_vezl6o: 21.5, + v_b5t88w: 23, + v_4x2d4w: 24, + v_xhwjk0: 25.5, + v_lhw3r4: 26.5, + v_tjkbuo: 27.5, + v_h72ebk: 29, + v_31n3sw: 30, + v_64rl6o: 32, + v_9lmigw: 35, + v_3fdjpc: 38, + v_fapfcw: 41, + v_7o0lc0: 44, + v_clbdvk: 47, + v_ee8qv4: 49, + }; + var U = function () { + return ( + (U = + Object.assign || + function (e) { + for (var t, i = 1, n = arguments.length; i < n; i++) for (var r in (t = arguments[i])) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e; + }), + U.apply(this, arguments) + ); + }, + N = function (e, t, i) { + if (i || 2 === arguments.length) for (var n, r = 0, a = t.length; r < a; r++) (!n && r in t) || (n || (n = Array.prototype.slice.call(t, 0, r)), (n[r] = t[r])); + return e.concat(n || Array.prototype.slice.call(t)); + }, + Q = parseInt(_("site_id"), 10) || 0, + F = "desktop"; + S() && (F = "mobile"), E() && (F = "tablet"); + var X = "rewarded", + Z = "video", + K = { + "728x90": "/21682198607/" + F + "_ingame_728x90/" + Q + "_" + F + "_ingame_728x90", + "300x250": "/21682198607/" + F + "_ingame_300x250/" + Q + "_" + F + "_ingame_300x250", + "970x250": "/21682198607/" + F + "_ingame_970x250/" + Q + "_" + F + "_ingame_970x250", + "160x600": "/21682198607/" + F + "_ingame_160x600/" + Q + "_" + F + "_ingame_160x600", + "320x50": "/21682198607/" + F + "_ingame_320x50/" + Q + "_" + F + "_ingame_320x50", + "728x90_external": "/21682198607/external_" + F + "_display_ingame/external_" + F + "_ingame_728x90", + "300x250_external": "/21682198607/external_" + F + "_display_ingame/external_" + F + "_ingame_300x250", + "970x250_external": "/21682198607/external_" + F + "_display_ingame/external_" + F + "_ingame_970x250", + "160x600_external": "/21682198607/external_" + F + "_display_ingame/external_" + F + "_ingame_160x600", + "320x50_external": "/21682198607/external_" + F + "_display_ingame/external_" + F + "_ingame_320x50", + }, + H = parseInt(_("site_id"), 10) || 0, + W = function (e) { + var t = x() || S() || E() ? ["video/mp4", "application/javascript"] : ["video/mp4", "video/webm", "video/ogg", "application/javascript"], + i = U( + U( + { + mimes: t, + minduration: 0, + maxduration: 15, + protocols: [2, 3, 5, 6, 7, 8], + w: 640, + h: 480, + placement: 1, + linearity: 1, + }, + e + ? {} + : { + skip: 1, + skipafter: 5, + } + ), + { + boxingallowed: 1, + pos: 1, + api: [2], + } + ); + return { + bids: [ + { + bidder: "appnexus", + params: { + placementId: 13184250, + supplyType: "web", + }, + }, + { + bidder: "openx", + params: { + delDomain: "poki-d.openx.net", + unit: "540105196", + }, + }, + { + bidder: "spotx", + params: { + channel_id: "265590", + ad_unit: "instream", + secure: !0, + hide_skin: !0, + }, + }, + { + bidder: "ix", + params: { + siteId: "436284", + video: U({}, i), + }, + }, + { + bidder: "richaudience", + params: { + pid: T(H), + supplyType: "site", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + ext: { + override_maxduration: 3600, + enforce_skip: !0, + }, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "266914", + zoneId: "1322034", + position: "atf", + video: { + size_id: 204, + }, + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3607869@640x360", + }, + }, + { + bidder: "districtm", + params: { + placementId: 12906789, + }, + }, + ], + mediaTypes: { + video: U( + { + context: "instream", + playerSize: [640, 480], + }, + i + ), + }, + }; + }, + V = W(!0), + q = W(!1), + J = [ + { + code: Z, + mediaTypes: q.mediaTypes, + bids: N([], q.bids, !0), + }, + { + code: X, + mediaTypes: V.mediaTypes, + bids: N([], V.bids, !0), + }, + { + code: K["728x90"], + mediaTypes: { + banner: { + sizes: [[728, 90]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "12940427", + }, + }, + { + bidder: "openx", + params: { + unit: "539859872", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "268177", + size: [728, 90], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "1374895@728x90", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "80117", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "204596", + zoneId: "1008080", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "1V6a2fgLvX", + supplyType: "site", + }, + }, + ], + }, + { + code: K["300x250"], + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "12935252", + }, + }, + { + bidder: "openx", + params: { + unit: "539859873", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "268178", + size: [300, 250], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "1374896@300x250", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "80118", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "204596", + zoneId: "1008080", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "pKqNt5LyvF", + supplyType: "site", + }, + }, + ], + }, + { + code: K["970x250"], + mediaTypes: { + banner: { + sizes: [[970, 250]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20595278", + }, + }, + { + bidder: "openx", + params: { + unit: "543540497", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "597527", + size: [970, 250], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3344351@970x250", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "123738", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + secure: 1, + position: 1, + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "yYyae7vnIh", + supplyType: "site", + }, + }, + ], + }, + { + code: K["160x600"], + mediaTypes: { + banner: { + sizes: [[160, 600]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "12940425", + }, + }, + { + bidder: "openx", + params: { + unit: "539859871", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "268175", + size: [160, 600], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "1374893@160x600", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "80119", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "204596", + zoneId: "1008080", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "rAEnPimPzC", + supplyType: "site", + }, + }, + ], + }, + { + code: K["320x50"], + mediaTypes: { + banner: { + sizes: [[320, 50]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20595224", + }, + }, + { + bidder: "openx", + params: { + unit: "543540495", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "597529", + size: [320, 50], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3344350@320x50", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "204596", + zoneId: "1008080", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "123737", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "1DP5EtcOip", + supplyType: "site", + }, + }, + ], + }, + { + code: K["728x90_external"], + mediaTypes: { + banner: { + sizes: [[728, 90]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20973406", + }, + }, + { + bidder: "openx", + params: { + unit: "543885656", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "268177", + placementId: "625562", + size: [728, 90], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3457872", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "132973", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + tag_id: "96373699", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "362566", + zoneId: "1962680-2", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "1V6a2fgLvX", + supplyType: "site", + }, + }, + ], + }, + { + code: K["300x250_external"], + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20973408", + }, + }, + { + bidder: "openx", + params: { + unit: "543885657", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "625564", + size: [300, 250], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3457874", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "132975", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + tag_id: "94f55c24", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "362566", + zoneId: "1962680-15", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "pKqNt5LyvF", + supplyType: "site", + }, + }, + ], + }, + { + code: K["970x250_external"], + mediaTypes: { + banner: { + sizes: [[970, 250]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20973415", + }, + }, + { + bidder: "openx", + params: { + unit: "543885650", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "625560", + size: [970, 250], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3457879", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "132979", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + tag_id: "62235ccb", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "362566", + zoneId: "1962680-57", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "yYyae7vnIh", + supplyType: "site", + }, + }, + ], + }, + { + code: K["160x600_external"], + mediaTypes: { + banner: { + sizes: [[160, 600]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20973407", + }, + }, + { + bidder: "openx", + params: { + unit: "543885653", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "625563", + size: [160, 600], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3457877", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "132974", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + tag_id: "9960183e", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "362566", + zoneId: "1962680-9", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "rAEnPimPzC", + supplyType: "site", + }, + }, + ], + }, + { + code: K["320x50_external"], + mediaTypes: { + banner: { + sizes: [[320, 50]], + }, + }, + bids: [ + { + bidder: "districtm", + params: { + placementId: "12906789", + }, + }, + { + bidder: "appnexus", + params: { + placementId: "20973413", + }, + }, + { + bidder: "openx", + params: { + unit: "543885649", + delDomain: "poki-d.openx.net", + }, + }, + { + bidder: "ix", + params: { + siteId: "625559", + size: [320, 50], + }, + }, + { + bidder: "pubmatic", + params: { + publisherId: "156838", + adSlot: "3457875", + }, + }, + { + bidder: "conversant", + params: { + site_id: "117477", + tag_id: "402db827", + secure: 1, + position: 1, + }, + }, + { + bidder: "rubicon", + params: { + accountId: "18608", + siteId: "362566", + zoneId: "1962680-43", + }, + }, + { + bidder: "emx_digital", + params: { + tagid: "132978", + }, + }, + { + bidder: "onetag", + params: { + pubId: "6da09f566a9dc06", + }, + }, + { + bidder: "richaudience", + params: { + pid: 15 === H ? "MP_gIE1VDieUi" : "1DP5EtcOip", + supplyType: "site", + }, + }, + ], + }, + ], + Y = { + debug: !1, + enableSendAllBids: !0, + usePrebidCache: !0, + bidderTimeout: 1500, + priceGranularity: { + buckets: [ + { + precision: 2, + min: 0.01, + max: 3, + increment: 0.01, + }, + { + precision: 2, + min: 3, + max: 8, + increment: 0.05, + }, + { + precision: 2, + min: 8, + max: 20, + increment: 0.5, + }, + { + precision: 2, + min: 20, + max: 45, + increment: 1, + }, + ], + }, + currency: { + adServerCurrency: "EUR", + defaultRates: { + EUR: { + EUR: 1, + GBP: 0.86408, + USD: 1.2212, + }, + GBP: { + EUR: 1.157300249976854, + GBP: 1, + USD: 1.4132950652717342, + }, + USD: { + EUR: 0.8188666885031116, + GBP: 0.7075663282017687, + USD: 1, + }, + }, + }, + cache: { + url: "https://prebid.adnxs.com/pbc/v1/cache", + }, + targetingControls: { + allowTargetingKeys: ["BIDDER", "AD_ID", "PRICE_BUCKET", "SIZE", "DEAL", "SOURCE", "FORMAT", "UUID", "CACHE_ID", "CACHE_HOST", "ADOMAIN"], + allowSendAllBidsTargetingKeys: ["BIDDER", "AD_ID", "PRICE_BUCKET", "SIZE", "DEAL", "SOURCE", "FORMAT", "UUID", "CACHE_ID", "CACHE_HOST", "ADOMAIN"], + }, + userSync: { + filterSettings: { + all: { + bidders: "*", + filter: "include", + }, + }, + syncsPerBidder: 1e3, + syncDelay: 100, + userIds: [ + { + name: "pubCommonId", + storage: { + type: "cookie", + name: "poki_pubcid", + expires: 180, + }, + }, + ], + }, + }, + $ = function (e, t) { + (window.pbjs = window.pbjs || {}), + (window.pbjs.que = window.pbjs.que || []), + window.pbjs.que.push(function () { + window.pbjs.aliasBidder("appnexus", "districtm"); + var i = U( + U( + { + floors: { + data: { + currency: "EUR", + schema: { + fields: ["mediaType"], + }, + values: { + banner: R(t), + video: L(t), + }, + }, + }, + }, + Y + ), + e.config + ); + window.pbjs.addAdUnits( + (function (e, t, i) { + var n, + r, + a = t[(i = i.toUpperCase())]; + if (!a) return e; + for (var o = Math.random(), s = 0; s <= e.length; s++) + for ( + var d = e[s], + A = a[(null === (n = null == d ? void 0 : d.mediaTypes) || void 0 === n ? void 0 : n.video) ? "video" : "display"] || {}, + c = (null === (r = null == d ? void 0 : d.bids) || void 0 === r ? void 0 : r.length) - 1; + c >= 0; + c-- + ) { + var l = d.bids[c]; + A[l.bidder] && o > A[l.bidder] && e[s].bids.splice(c, 1); + } + return e; + })(e.adUnits || J, c, t) + ), + window.pbjs.setConfig(i), + (window.pbjs.bidderSettings = { + districtm: { + bidCpmAdjustment: function (e) { + return 0.85 * e; + }, + }, + }); + }); + }, + ee = !1, + te = function (e, t, i) { + (function (e, t) { + var i, + n, + r = null === (n = null === (i = e[(t = t.toUpperCase())]) || void 0 === i ? void 0 : i.video) || void 0 === n ? void 0 : n.amazon; + return !!r && Math.random() > r; + })(c, t) || + (window.apstag && + window.apstag.init( + e.settings || + U( + { + pubID: "e32f1423-28bc-43ed-8ab0-5ae6b4449cf8", + adServer: "", + videoAdServer: "GAM", + }, + i + ? { + gdpr: { + cmpTimeout: 1e4, + }, + } + : {} + ), + function () { + (ee = !0), e.callback && e.callback(); + } + )); + }, + ie = function () { + !(function () { + if (!window.__tcfapi) { + var e = window.top, + t = {}; + (window.__tcfapi = function (i, n, r, a) { + var o = "" + Math.random(), + s = { + __tcfapiCall: { + command: i, + parameter: a, + version: n, + callId: o, + }, + }; + (t[o] = r), e.postMessage(s, "*"); + }), + window.addEventListener( + "message", + function (e) { + var i = {}; + try { + i = "string" == typeof e.data ? JSON.parse(e.data) : e.data; + } catch (e) {} + var n = i.__tcfapiReturn; + n && "function" == typeof t[n.callId] && (t[n.callId](n.returnValue, n.success), (t[n.callId] = null)); + }, + !1 + ); + } + })(), + window.pbjs.que.push(function () { + window.pbjs.setConfig({ + consentManagement: { + gdpr: { + cmpApi: "iab", + timeout: 8e3, + defaultGdprScope: !0, + }, + }, + }); + }); + }, + ne = function () { + !(function () { + if (!window.__uspapi) { + var e = window.top, + t = {}; + (window.__uspapi = function (i, n, r) { + var a = "" + Math.random(), + o = { + __uspapiCall: { + command: i, + version: n, + callId: a, + }, + }; + (t[a] = r), e.postMessage(o, "*"); + }), + window.addEventListener( + "message", + function (e) { + var i = e && e.data && e.data.__uspapiReturn; + i && i.callId && "function" == typeof t[i.callId] && (t[i.callId](i.returnValue, i.success), (t[i.callId] = null)); + }, + !1 + ); + } + })(), + window.pbjs.que.push(function () { + window.pbjs.setConfig({ + consentManagement: { + usp: { + cmpApi: "iab", + timeout: 8e3, + }, + }, + }); + }); + }; + + function re(e, t, i, r, a) { + var s = d(), + A = s ? "nope" : t; + if (window.pbjs && window.pbjs.que && window.pbjs.getConfig) { + var c, + l = I().split("?"), + p = encodeURIComponent(l[0]), + u = r ? X : Z, + h = o.getDataAnnotations(), + m = 1, + g = function () { + if (!(--m > 0)) + try { + o.dispatchEvent(n.ads.prebidRequested); + var d = window.pbjs.adUnits.filter(function (e) { + return e.code === u; + })[0]; + if ("undefined" === d) return console.error("Video-ad-unit not found, did you give it the adunit.code='video' value?"), void e.requestAd(A); + var l = window.pbjs.adServers.dfp.buildVideoUrl({ + adUnit: d, + params: { + iu: _("iu", t), + sz: "640x360|640x480", + output: "vast", + cust_params: i, + description_url: p, + }, + }), + g = window.pbjs.getHighestCpmBids(u); + window.pbjs.markWinningBidAsUsed({ + adUnitCode: u, + }), + c && (l = l.replace("cust_params=", "cust_params=" + c + "%26")); + var v = !1; + if (s) { + var f = void 0; + if ((g.length > 0 && (f = g[0]), c)) { + var b = (function (e) { + var t = decodeURIComponent(e), + i = _("amznbid", t); + if (!i) return null; + var n = j[i]; + return n + ? { + bid: n, + vast: "https://aax.amazon-adsystem.com/e/dtb/vast?b=" + _("amzniid", t) + "&rnd=" + Math.round(1e10 * Math.random()) + "&pp=" + i, + } + : null; + })(c); + b && + (!f || !f.videoCacheKey || f.cpm < b.bid) && + (f = { + cpm: b.bid, + vast: b.vast, + bidder: "amazon", + videoCacheKey: "amazon", + }); + } + if ((!f || !f.videoCacheKey || f.cpm < L(a)) && (r || Math.random() > 0.5)) { + var y = void 0; + (y = (function (e) { + return "US" === e ? 6.1 : B.includes(e) ? 0.5 : P.includes(e) ? 0.15 : D.includes(e) ? 0.08 : M.includes(e) ? 0.03 : 0.02; + })(a)), + (f = { + cpm: 2 * y, + vast: G(), + bidder: "poki", + videoCacheKey: "poki", + }); + } + if (!f || !f.videoCacheKey) return void o.dispatchEvent(n.ads.completed); + switch (f.bidder) { + case "onetag": + l = "https://onetag-sys.com/invocation/?key=" + f.videoCacheKey; + break; + case "rubicon": + l = "https://prebid-server.rubiconproject.com/cache?uuid=" + f.videoCacheKey; + break; + case "spotx": + l = "https://search.spotxchange.com/ad/vast.html?key=" + f.videoCacheKey; + break; + case "amazon": + case "poki": + l = f.vast; + break; + default: + l = "https://prebid.adnxs.com/pbc/v1/cache?uuid=" + f.videoCacheKey; + } + z({ + event: "video-ready", + size: "640x360v", + opportunityId: null == h ? void 0 : h.opportunityId, + adUnitPath: null == h ? void 0 : h.adUnitPath, + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + bidder: null == f ? void 0 : f.bidder, + bid: null == f ? void 0 : f.cpm, + }), + (v = !0), + o.setDataAnnotations({ + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + bidder: null == f ? void 0 : f.bidder, + bid: null == f ? void 0 : f.cpm, + }); + } + if ( + (o.setDataAnnotations({ + vhbOnlyMode: v, + adTagUrl: l, + }), + g.length > 0) + ) { + f = g[0]; + o.setDataAnnotations({ + prebidBidder: null == f ? void 0 : f.bidder, + prebidBid: null == f ? void 0 : f.cpm, + }); + } else + o.setDataAnnotations({ + prebidBidder: void 0, + prebidBid: void 0, + }); + e.requestAd(l); + } catch (t) { + e.requestAd(A); + } + }; + ee && + (m++, + window.apstag.fetchBids( + { + slots: [ + { + slotID: r ? "Rewarded" : "Midroll", + mediaType: "video", + }, + ], + timeout: Y.bidderTimeout, + }, + function (e) { + e.length > 0 && (c = e[0].encodedQsParams), g(); + } + )), + s && + z({ + event: "video-request", + size: "640x360v", + opportunityId: null == h ? void 0 : h.opportunityId, + adUnitPath: null == h ? void 0 : h.adUnitPath, + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + }), + window.pbjs.que.push(function () { + window.pbjs.requestBids({ + adUnitCodes: [u], + bidsBackHandler: function () { + g(); + }, + }); + }); + } else e.requestAd(A); + } + var ae = (function () { + function e(e, t) { + void 0 === t && (t = {}), + (this.retries = 0), + (this.running = !1), + (this.ima = e), + (this.siteID = t.siteID || 3), + (this.country = t.country || "ZZ"), + (this.totalRetries = t.totalRetries || l.waterfallRetries || 1), + (this.timing = t.timing || new u(l.adTiming)), + d() && (this.totalRetries = 1), + o.addEventListener(n.ads.video.error, this.moveThroughWaterfall.bind(this)), + o.addEventListener(n.ads.video.loaderError, this.moveThroughWaterfall.bind(this)), + o.addEventListener(n.ads.ready, this.timing.stopWaterfallTimer.bind(this.timing)), + o.addEventListener(n.ads.started, this.stopWaterfall.bind(this)); + } + return ( + (e.prototype.moveThroughWaterfall = function () { + if (!1 !== this.running) { + if ((this.timing.stopWaterfallTimer(), this.retries < this.totalRetries)) return this.timing.nextWaterfallTimer(), void this.requestAd(); + (this.running = !1), + this.timing.resetWaterfallTimerIdx(), + o.dispatchEvent(n.ads.error, { + message: "No ads", + }); + } + }), + (e.prototype.cutOffWaterfall = function () { + this.ima.tearDown(), this.moveThroughWaterfall(); + }), + (e.prototype.buildAdUnitPaths = function (e) { + if (r.debug) { + var t = "/21682198607/debug-video/"; + return e === n.ads.position.rewarded ? [t + "debug-video-rewarded"] : e === n.ads.position.preroll ? [t + "debug-video-preroll"] : [t + "debug-video-midroll"]; + } + var i = "desktop", + a = "midroll"; + S() ? (i = "mobile") : E() && (i = "tablet"), e === n.ads.position.rewarded && (a = "rewarded"); + var o = "/21682198607/"; + return nt.GetIsPokiIFrame() + ? ["" + o + i + "_ingame_" + a + "_1/" + this.siteID + "_" + i + "_ingame_" + a + "_1", "" + o + i + "_ingame_" + a + "_2/" + this.siteID + "_" + i + "_ingame_" + a + "_2"] + : [o + "external_" + i + "_video_1/external_" + i + "_ingame_" + a + "_1", o + "external_" + i + "_video_2/external_" + i + "_ingame_" + a + "_2"]; + }), + (e.prototype.start = function (e, t) { + void 0 === e && (e = {}), + (this.running = !0), + (this.retries = 0), + (this.criteria = e), + this.timing.resetWaterfallTimerIdx(), + (this.rewarded = t === n.ads.position.rewarded), + (this.adUnitPaths = this.buildAdUnitPaths(t)), + this.requestAd(); + }), + (e.prototype.requestAd = function () { + this.timing.startWaterfallTimer(this.cutOffWaterfall.bind(this)), this.retries++, (this.criteria.waterfall = this.retries); + var e = (this.retries - 1) % this.adUnitPaths.length, + t = this.adUnitPaths[e], + i = "https://securepubads.g.doubleclick.net/gampad/ads?sz=640x360|640x480&iu=" + t + "&ciu_szs&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url={url}&description_url={descriptionUrl}&correlator={timestamp}"; + nt.consentString && nt.consentString.length > 0 && (this.criteria.consent_string = nt.consentString); + var r = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) > 970; + this.criteria.billboards_fit = r ? "yes" : "no"; + var a, + s, + A = + (function (e) { + var t = I().split("?"), + i = encodeURIComponent(t[0]); + return (e = e.split("{descriptionUrl}").join(i)).split("{timestamp}").join(new Date().getTime().toString()); + })(i) + + ((a = this.criteria), + (s = ""), + Object.keys(a).forEach(function (e) { + if (Object.prototype.hasOwnProperty.call(a, e)) { + var t = a[e]; + Array.isArray(t) && (t = t.join()), (s += e + "=" + t + "&"); + } + }), + "&cust_params=" + (s = encodeURIComponent(s)) + "&"); + nt.childDirected && (A += "&tfcd=1"), + nt.nonPersonalized && (A += "&npa=1"), + o.setDataAnnotations({ + adUnitPath: t, + adTagUrl: A, + waterfall: this.retries, + }), + o.dispatchEvent(n.ads.requested), + d() || (1 === this.retries && !S() && !E()) + ? (console.debug("adRequest started with Prebid Video enabled"), re(this.ima, A, this.criteria, this.rewarded, this.country)) + : (console.debug("adRequest started in plain mode"), this.ima.requestAd(A)); + }), + (e.prototype.isRunning = function () { + return this.running; + }), + (e.prototype.stopWaterfall = function () { + (this.running = !1), this.timing.stopWaterfallTimer(), this.timing.resetWaterfallTimerIdx(); + }), + e + ); + })(); + const oe = ae; + var se = "pokiSdkContainer", + de = "pokiSdkFixed", + Ae = "pokiSdkOverlay", + ce = "pokiSdkHidden", + le = "pokiSdkInsideContainer", + pe = "pokiSdkPauseButton", + ue = "pokiSdkPauseButtonBG", + he = "pokiSdkStartAdButton", + me = "pokiSdkProgressBar", + ge = "pokiSdkProgressContainer", + ve = "pokiSdkSpinnerContainer", + fe = "pokiSdkVideoContainer", + be = "pokiSdkVisible", + ye = "pokiSDKAdContainer"; + var ke = function (e, t, i) { + if (i || 2 === arguments.length) for (var n, r = 0, a = t.length; r < a; r++) (!n && r in t) || (n || (n = Array.prototype.slice.call(t, 0, r)), (n[r] = t[r])); + return e.concat(n || Array.prototype.slice.call(t)); + }; + const we = (function () { + function e(e) { + var t = this; + if ( + ((this.hideElement = function (e) { + e.classList.add(ce), e.classList.remove(be); + }), + (this.showElement = function (e) { + e.classList.add(be), e.classList.remove(ce); + }), + (this.wrapper = e.wrapper), + (this.progressFaker = new Ie(function (e) { + return t.updateProgressBar(e); + })), + this.progressFaker.queueFakeProgress(10, 1e3, n.ads.prebidRequested), + this.progressFaker.queueFakeProgress(20, 2e3, n.ads.started), + this.wrapper instanceof HTMLElement || (console.error("POKI-SDK: wrapper is not a HTMLElement, falling back to document.body"), (this.wrapper = document.body)), + this.createElements(), + "undefined" != typeof window && document) + ) { + var i = document.createElement("style"); + (i.innerHTML = + "\n.pokiSdkContainer {\n\toverflow: hidden;\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\tz-index: 1000;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\n\n.pokiSdkContainer.pokiSdkFixed {\n\tposition: fixed;\n}\n\n.pokiSdkContainer.pokiSdkVisible {\n\tdisplay: block;\n}\n\n.pokiSdkContainer.pokiSdkHidden,\n.pokiSdkSpinnerContainer.pokiSdkHidden {\n\tdisplay: none;\n}\n\n.pokiSdkContainer.pokiSdkHidden,\n.pokiSdkSpinnerContainer {\n\tpointer-events: none;\n}\n\n.pokiSdkSpinnerContainer {\n\tz-index: 10;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: url('https://a.poki.com/images/thumb_anim_2x.gif') 50% 50% no-repeat;\n\tuser-select: none;\n}\n\n.pokiSdkInsideContainer {\n\tbackground: #000;\n\tposition: relative;\n\tz-index: 1;\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\tflex-direction: column;\n\n\topacity: 0;\n\t-webkit-transition: opacity 0.5s ease-in-out;\n\t-moz-transition: opacity 0.5s ease-in-out;\n\t-ms-transition: opacity 0.5s ease-in-out;\n\t-o-transition: opacity 0.5s ease-in-out;\n\ttransition: opacity 0.5s ease-in-out;\n}\n\n.pokiSdkContainer.pokiSdkVisible .pokiSdkInsideContainer {\n\topacity: 1;\n}\n\n.pokiSDKAdContainer, .pokiSdkVideoContainer {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n}\n\n.pokiSdkStartAdButton {\n\tposition: absolute;\n\tz-index: 9999;\n\ttop: 0;\n\n\tpadding-top: 10%;\n\twidth: 100%;\n\theight: 100%;\n\ttext-align: center;\n\tcolor: #FFF;\n\n\tfont: 700 15pt 'Arial', sans-serif;\n\tfont-weight: bold;\n\tletter-spacing: 1px;\n\ttransition: 0.1s ease-in-out;\n\tline-height: 1em;\n}\n\n.pokiSdkPauseButton {\n\tcursor:pointer;\n position: absolute;\n top: 50%;\n left: 50%;\n z-index: 1;\n}\n\n.pokiSdkPauseButton:before {\n\tcontent: '';\n\tposition: absolute;\n\twidth: 100px;\n\theight: 100px;\n\tdisplay: block;\n\tborder: 2px solid #fff;\n\tborder-radius: 50%;\n\tuser-select: none;\n\tbackground-color: rgba(0, 0, 0, 0.6);\n\ttransition: background-color 0.5s ease;\n\tanimation: 1s linear infinite pulse;\n}\n\n.pokiSdkPauseButton:after {\n\tcontent: '';\n\tposition: absolute;\n\tdisplay: block;\n\tbox-sizing: border-box;\n\tborder-color: transparent transparent transparent #fff;\n\tborder-style: solid;\n\tborder-width: 26px 0 26px 40px;\n\tpointer-events: none;\n\tanimation: 1s linear infinite pulse;\n\tleft: 6px;\n}\n.pokiSdkPauseButtonBG {\n position: fixed;\n top: 0;\n left: 0;\n display: block;\n content: '';\n background: rgba(0, 43, 80, 0.5);\n width: 100%;\n height: 100%;\n}\n\n.pokiSdkPauseButtonBG:hover{\n\tbackground: rgba(0, 43, 80, 0.7);\n}\n\n@keyframes pulse {\n\t0% {\n\t\ttransform: translate(-50%, -50%) scale(0.95);\n\t}\n\t70% {\n\t\ttransform: translate(-50%, -50%) scale(1.1);\n\t}\n\t100% {\n\t\ttransform: translate(-50%, -50%) scale(0.95);\n\t}\n}\n\n.pokiSdkProgressContainer {\n\tbackground: #B8C7DD;\n\twidth: 100%;\n\theight: 5px;\n\tposition: absolute;\n\tbottom: 0;\n\tz-index: 9999;\n}\n\n.pokiSdkProgressBar {\n\tposition:relative;\n\tbottom:0px;\n\tbackground: #FFDC00;\n\theight: 100%;\n\twidth: 0%;\n\ttransition: width 0.5s;\n\ttransition-timing-function: linear;\n}\n\n.pokiSdkProgressBar.pokiSdkVisible, .pokiSdkPauseButton.pokiSdkVisible, .pokiSdkStartAdButton.pokiSdkVisible {\n\tdisplay: block;\n\tpointer-events: auto;\n}\n\n.pokiSdkProgressBar.pokiSdkHidden, .pokiSdkPauseButton.pokiSdkHidden, .pokiSdkStartAdButton.pokiSdkHidden {\n\tdisplay: none;\n\tpointer-events: none;\n}\n"), + document.head.appendChild(i); + } + } + return ( + (e.prototype.updateProgressBar = function (e) { + this.progressBar.style.width = e + "%"; + }), + (e.prototype.setupEvents = function (e) { + this.internalSDK = e; + }), + (e.prototype.hide = function () { + this.hideElement(this.containerDiv), + this.hideElement(this.progressContainer), + this.hidePauseButton(), + this.hideElement(this.startAdButton), + this.containerDiv.classList.remove(Ae), + (this.progressBar.style.width = "0%"), + this.progressFaker.reset(); + }), + (e.prototype.hideSpinner = function () { + this.hideElement(this.spinnerContainer); + }), + (e.prototype.show = function () { + this.containerDiv.classList.add(Ae), this.showElement(this.containerDiv), this.showElement(this.spinnerContainer), this.showElement(this.progressContainer), this.progressFaker.start(); + }), + (e.prototype.getVideoBounds = function () { + return this.adContainer.getBoundingClientRect(); + }), + (e.prototype.getAdContainer = function () { + return this.adContainer; + }), + (e.prototype.getVideoContainer = function () { + return this.videoContainer; + }), + (e.prototype.showPauseButton = function () { + this.showElement(this.pauseButton), this.internalSDK && this.pauseButton.addEventListener("click", this.internalSDK.resumeAd.bind(this.internalSDK)); + }), + (e.prototype.hidePauseButton = function () { + this.hideElement(this.pauseButton), this.internalSDK && this.pauseButton.removeEventListener("click", this.internalSDK.resumeAd.bind(this.internalSDK)); + }), + (e.prototype.showStartAdButton = function () { + this.showElement(this.startAdButton), this.internalSDK && this.startAdButton.addEventListener("click", this.internalSDK.startAdClicked.bind(this.internalSDK)); + }), + (e.prototype.hideStartAdButton = function () { + this.hideElement(this.startAdButton), this.internalSDK && this.startAdButton.removeEventListener("click", this.internalSDK.startAdClicked.bind(this.internalSDK)); + }), + (e.prototype.createElements = function () { + if ( + ((this.containerDiv = document.createElement("div")), + (this.insideContainer = document.createElement("div")), + (this.pauseButton = document.createElement("div")), + (this.pauseButtonBG = document.createElement("div")), + (this.startAdButton = document.createElement("div")), + (this.progressBar = document.createElement("div")), + (this.progressContainer = document.createElement("div")), + (this.spinnerContainer = document.createElement("div")), + (this.adContainer = document.createElement("div")), + (this.videoContainer = document.createElement("video")), + (this.adContainer.id = "pokiSDKAdContainer"), + (this.videoContainer.id = "pokiSDKVideoContainer"), + (this.containerDiv.className = se), + (this.insideContainer.className = le), + (this.pauseButton.className = pe), + (this.pauseButtonBG.className = ue), + this.pauseButton.appendChild(this.pauseButtonBG), + (this.startAdButton.className = he), + (this.startAdButton.innerHTML = "Tap anywhere to play ad"), + (this.progressBar.className = me), + (this.progressContainer.className = ge), + (this.spinnerContainer.className = ve), + (this.adContainer.className = ye), + (this.videoContainer.className = fe), + this.hide(), + this.videoContainer.setAttribute("playsinline", "playsinline"), + this.videoContainer.setAttribute("muted", "muted"), + this.containerDiv.appendChild(this.insideContainer), + this.containerDiv.appendChild(this.spinnerContainer), + this.insideContainer.appendChild(this.progressContainer), + this.insideContainer.appendChild(this.videoContainer), + this.insideContainer.appendChild(this.adContainer), + this.containerDiv.appendChild(this.pauseButton), + this.containerDiv.appendChild(this.startAdButton), + this.progressContainer.appendChild(this.progressBar), + this.wrapper.appendChild(this.containerDiv), + this.wrapper === document.body) + ) + this.containerDiv.classList.add(de); + else { + var e = window.getComputedStyle(this.wrapper).position; + (e && -1 !== ["absolute", "fixed", "relative"].indexOf(e)) || (this.wrapper.style.position = "relative"); + } + }), + e + ); + })(); + var Ie = (function () { + function e(e) { + var t = this; + (this.storedQueue = []), + (this.progressCallback = e), + this.reset(), + o.addEventListener(n.ads.video.progress, function (e) { + var i = 100 - t.currentProgress, + n = (e.currentTime / e.duration) * i; + n < i && t.progressCallback(t.currentProgress + n); + }), + this.initializeNoProgressFix(); + } + return ( + (e.prototype.queueFakeProgress = function (e, t, i) { + var n = this; + this.storedQueue.push({ + progressToFake: e, + duration: t, + stopEvent: i, + }), + o.addEventListener(i, function () { + (n.eventWatcher[i] = !0), (n.currentProgress = n.startProgress + e), (n.startProgress = n.currentProgress), n.progressCallback(n.currentProgress), n.activeQueue.shift(), n.activeQueue.length > 0 ? n.continue() : n.pause(); + }); + }), + (e.prototype.fakeProgress = function (e, t, i) { + this.activeQueue.push({ + progressToFake: e, + duration: t, + stopEvent: i, + }), + (this.fakeProgressEvents = !0), + this.continue(); + }), + (e.prototype.start = function () { + this.activeQueue.length > 0 || ((this.activeQueue = ke([], this.storedQueue, !0)), (this.active = !0), this.continue()); + }), + (e.prototype.continue = function () { + if (this.activeQueue.length > 0 && !this.tickInterval) { + this.startTime = Date.now(); + (this.tickInterval = window.setInterval(this.tick.bind(this), 50)), (this.active = !0); + } + }), + (e.prototype.pause = function () { + this.clearInterval(); + }), + (e.prototype.tick = function () { + var e = this.activeQueue[0], + t = Date.now() - this.startTime, + i = Math.min(t / e.duration, 1); + (this.currentProgress = this.startProgress + e.progressToFake * i), + this.fakeProgressEvents && + o.dispatchEvent(n.ads.video.progress, { + duration: e.duration / 1e3, + currentTime: t / 1e3, + }), + this.progressCallback(this.currentProgress), + (this.eventWatcher[e.stopEvent] || 1 === i) && this.pause(); + }), + (e.prototype.clearInterval = function () { + this.tickInterval && (clearInterval(this.tickInterval), (this.tickInterval = 0)); + }), + (e.prototype.initializeNoProgressFix = function () { + var e = this; + o.addEventListener(n.ads.started, function (t) { + e.progressWatcherTimeout = window.setTimeout(function () { + if (e.active) { + var i = 100 - e.currentProgress, + r = 1e3 * t.duration - 1e3; + e.fakeProgress(i, r, n.ads.completed); + } + }, 1e3); + }), + o.addEventListener(n.ads.video.progress, function () { + e.progressWatcherTimeout && (clearTimeout(e.progressWatcherTimeout), (e.progressWatcherTimeout = 0)); + }); + }), + (e.prototype.reset = function () { + (this.eventWatcher = {}), (this.startProgress = 0), (this.startTime = 0), (this.currentProgress = 0), (this.activeQueue = []), (this.active = !1), (this.fakeProgressEvents = !1), this.clearInterval(); + }), + e + ); + })(), + Se = !0, + Ee = {}; + + function _e() { + if (document.body && document.body.appendChild) { + var e = document.createElement("iframe"); + if ( + ((e.style.display = "none"), + document.body.appendChild(e), + e.contentWindow && + ((window.pokiKeysChanged = new Map()), + e.contentWindow.document.open(), + e.contentWindow.document.write( + "" + ), + e.contentWindow.document.close(), + !window.location.hostname.endsWith("poki-gdn.com") && tt.gameId)) + ) { + var t = document.createElement("iframe"); + (t.style.display = "none"), + (t.src = "https://" + tt.gameId + ".poki-gdn.com/poki-savegame-store.html"), + (t.onload = function () { + if ( + (setInterval(function () { + var e = []; + window.pokiKeysChanged.forEach(function (t, i) { + "set" === t ? e.push([t, i, localStorage.getItem(i)]) : e.push([t, i]); + }), + e.length > 0 && + (t.contentWindow && + t.contentWindow.postMessage( + { + type: "store", + data: e, + }, + "*" + ), + window.pokiKeysChanged.clear()); + }, 1e3), + !localStorage.getItem("pokiMigrated")) + ) { + for (var e = [], i = 0; i < localStorage.length; i++) { + var n = localStorage.key(i); + e.push(["set", n, localStorage.getItem(n)]); + } + e.length > 0 && + t.contentWindow && + t.contentWindow.postMessage( + { + type: "store", + data: e, + }, + "*" + ), + localStorage.setItem("pokiMigrated", "1"); + } + }), + document.body.appendChild(t); + } + } else document.addEventListener("DOMContentLoaded", _e); + } + var xe = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE", "IS", "LI", "NO"], + Ce = ["US"], + Te = ["ZZ"]; + + function Be(e) { + return xe.includes(e); + } + + function Pe(e) { + return Te.includes(e); + } + var De = function (e, t, i, n) { + return new (i || (i = Promise))(function (r, a) { + function o(e) { + try { + d(n.next(e)); + } catch (e) { + a(e); + } + } + + function s(e) { + try { + d(n.throw(e)); + } catch (e) { + a(e); + } + } + + function d(e) { + var t; + e.done + ? r(e.value) + : ((t = e.value), + t instanceof i + ? t + : new i(function (e) { + e(t); + })).then(o, s); + } + d((n = n.apply(e, t || [])).next()); + }); + }, + Me = function (e, t) { + var i, + n, + r, + a, + o = { + label: 0, + sent: function () { + if (1 & r[0]) throw r[1]; + return r[1]; + }, + trys: [], + ops: [], + }; + return ( + (a = { + next: s(0), + throw: s(1), + return: s(2), + }), + "function" == typeof Symbol && + (a[Symbol.iterator] = function () { + return this; + }), + a + ); + + function s(a) { + return function (s) { + return (function (a) { + if (i) throw new TypeError("Generator is already executing."); + for (; o; ) + try { + if (((i = 1), n && (r = 2 & a[0] ? n.return : a[0] ? n.throw || ((r = n.return) && r.call(n), 0) : n.next) && !(r = r.call(n, a[1])).done)) return r; + switch (((n = 0), r && (a = [2 & a[0], r.value]), a[0])) { + case 0: + case 1: + r = a; + break; + case 4: + return ( + o.label++, + { + value: a[1], + done: !1, + } + ); + case 5: + o.label++, (n = a[1]), (a = [0]); + continue; + case 7: + (a = o.ops.pop()), o.trys.pop(); + continue; + default: + if (!((r = o.trys), (r = r.length > 0 && r[r.length - 1]) || (6 !== a[0] && 2 !== a[0]))) { + o = 0; + continue; + } + if (3 === a[0] && (!r || (a[1] > r[0] && a[1] < r[3]))) { + o.label = a[1]; + break; + } + if (6 === a[0] && o.label < r[1]) { + (o.label = r[1]), (r = a); + break; + } + if (r && o.label < r[2]) { + (o.label = r[2]), o.ops.push(a); + break; + } + r[2] && o.ops.pop(), o.trys.pop(); + continue; + } + a = t.call(e, o); + } catch (e) { + (a = [6, e]), (n = 0); + } finally { + i = r = 0; + } + if (5 & a[0]) throw a[1]; + return { + value: a[0] ? a[1] : void 0, + done: !0, + }; + })([a, s]); + }; + } + }; + const Re = (function () { + function e(e) { + var t = this; + (this.bannerTimeout = null), + (this.allowedToPlayAd = !1), + (this.runningAd = !1), + (this.currentWidth = 640), + (this.currentHeight = 480), + (this.currentRequestIsMuted = !1), + (this.volume = 1), + (this.canWeAutoPlayWithSound = function () { + return De(t, void 0, void 0, function () { + return Me(this, function (e) { + switch (e.label) { + case 0: + if (!this.blankVideo) return [2, !1]; + e.label = 1; + case 1: + return e.trys.push([1, 3, , 4]), [4, this.blankVideo.play()]; + case 2: + return e.sent(), [2, !0]; + case 3: + return e.sent(), [2, !1]; + case 4: + return [2]; + } + }); + }); + }), + (this.videoElement = document.getElementById("pokiSDKVideoContainer")), + (this.adsManager = null), + (this.volume = e), + this.initAdDisplayContainer(), + this.initBlankVideo(), + this.initAdsLoader(); + } + return ( + (e.prototype.initAdDisplayContainer = function () {}), + (e.prototype.initBlankVideo = function () { + (this.blankVideo = document.createElement("video")), this.blankVideo.setAttribute("playsinline", "playsinline"); + var e = document.createElement("source"); + (e.src = + "data:video/mp4;base64, AAAAHGZ0eXBNNFYgAAACAGlzb21pc28yYXZjMQAAAAhmcmVlAAAGF21kYXTeBAAAbGliZmFhYyAxLjI4AABCAJMgBDIARwAAArEGBf//rdxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNDIgcjIgOTU2YzhkOCAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMTQgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0zIGRlYmxvY2s9MTowOjAgYW5hbHlzZT0weDE6MHgxMTEgbWU9aGV4IHN1Ym1lPTcgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MSBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTEgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz02IGxvb2thaGVhZF90aHJlYWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCB2YnZfbWF4cmF0ZT03NjggdmJ2X2J1ZnNpemU9MzAwMCBjcmZfbWF4PTAuMCBuYWxfaHJkPW5vbmUgZmlsbGVyPTAgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAFZliIQL8mKAAKvMnJycnJycnJycnXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXiEASZACGQAjgCEASZACGQAjgAAAAAdBmjgX4GSAIQBJkAIZACOAAAAAB0GaVAX4GSAhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGagC/AySEASZACGQAjgAAAAAZBmqAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZrAL8DJIQBJkAIZACOAAAAABkGa4C/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmwAvwMkhAEmQAhkAI4AAAAAGQZsgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGbQC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm2AvwMkhAEmQAhkAI4AAAAAGQZuAL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGboC/AySEASZACGQAjgAAAAAZBm8AvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZvgL8DJIQBJkAIZACOAAAAABkGaAC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmiAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpAL8DJIQBJkAIZACOAAAAABkGaYC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmoAvwMkhAEmQAhkAI4AAAAAGQZqgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGawC/AySEASZACGQAjgAAAAAZBmuAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZsAL8DJIQBJkAIZACOAAAAABkGbIC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm0AvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZtgL8DJIQBJkAIZACOAAAAABkGbgCvAySEASZACGQAjgCEASZACGQAjgAAAAAZBm6AnwMkhAEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AAAAhubW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAABDcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAzB0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAA+kAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAALAAAACQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAPpAAAAAAABAAAAAAKobWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAB1MAAAdU5VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAACU21pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAhNzdGJsAAAAr3N0c2QAAAAAAAAAAQAAAJ9hdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAALAAkABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAALWF2Y0MBQsAN/+EAFWdCwA3ZAsTsBEAAAPpAADqYA8UKkgEABWjLg8sgAAAAHHV1aWRraEDyXyRPxbo5pRvPAyPzAAAAAAAAABhzdHRzAAAAAAAAAAEAAAAeAAAD6QAAABRzdHNzAAAAAAAAAAEAAAABAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAAIxzdHN6AAAAAAAAAAAAAAAeAAADDwAAAAsAAAALAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAiHN0Y28AAAAAAAAAHgAAAEYAAANnAAADewAAA5gAAAO0AAADxwAAA+MAAAP2AAAEEgAABCUAAARBAAAEXQAABHAAAASMAAAEnwAABLsAAATOAAAE6gAABQYAAAUZAAAFNQAABUgAAAVkAAAFdwAABZMAAAWmAAAFwgAABd4AAAXxAAAGDQAABGh0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAACAAAAAAAABDcAAAAAAAAAAAAAAAEBAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAQkAAADcAABAAAAAAPgbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAC7gAAAykBVxAAAAAAALWhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAABTb3VuZEhhbmRsZXIAAAADi21pbmYAAAAQc21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAADT3N0YmwAAABnc3RzZAAAAAAAAAABAAAAV21wNGEAAAAAAAAAAQAAAAAAAAAAAAIAEAAAAAC7gAAAAAAAM2VzZHMAAAAAA4CAgCIAAgAEgICAFEAVBbjYAAu4AAAADcoFgICAAhGQBoCAgAECAAAAIHN0dHMAAAAAAAAAAgAAADIAAAQAAAAAAQAAAkAAAAFUc3RzYwAAAAAAAAAbAAAAAQAAAAEAAAABAAAAAgAAAAIAAAABAAAAAwAAAAEAAAABAAAABAAAAAIAAAABAAAABgAAAAEAAAABAAAABwAAAAIAAAABAAAACAAAAAEAAAABAAAACQAAAAIAAAABAAAACgAAAAEAAAABAAAACwAAAAIAAAABAAAADQAAAAEAAAABAAAADgAAAAIAAAABAAAADwAAAAEAAAABAAAAEAAAAAIAAAABAAAAEQAAAAEAAAABAAAAEgAAAAIAAAABAAAAFAAAAAEAAAABAAAAFQAAAAIAAAABAAAAFgAAAAEAAAABAAAAFwAAAAIAAAABAAAAGAAAAAEAAAABAAAAGQAAAAIAAAABAAAAGgAAAAEAAAABAAAAGwAAAAIAAAABAAAAHQAAAAEAAAABAAAAHgAAAAIAAAABAAAAHwAAAAQAAAABAAAA4HN0c3oAAAAAAAAAAAAAADMAAAAaAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAACMc3RjbwAAAAAAAAAfAAAALAAAA1UAAANyAAADhgAAA6IAAAO+AAAD0QAAA+0AAAQAAAAEHAAABC8AAARLAAAEZwAABHoAAASWAAAEqQAABMUAAATYAAAE9AAABRAAAAUjAAAFPwAABVIAAAVuAAAFgQAABZ0AAAWwAAAFzAAABegAAAX7AAAGFwAAAGJ1ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWlsc3QAAAAlqXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNTUuMzMuMTAw"), + this.blankVideo.appendChild(e); + }), + (e.prototype.initAdsLoader = function () { + var e = this; + this.adsLoader || + (window.google && + ((this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer)), + this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.INSECURE), + this.adsLoader.getSettings().setDisableCustomPlaybackForIOS10Plus(!0), + this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded, !1, this), + this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdLoaderError, !1, this), + this.videoElement.addEventListener("onended", function () { + return e.adsLoader.contentComplete(); + }))); + }), + (e.prototype.requestAd = function (e) { + return De(this, void 0, void 0, function () { + var t; + return Me(this, function (i) { + switch (i.label) { + case 0: + return this.runningAd + ? [2] + : ((this.runningAd = !0), + this.adDisplayContainer.initialize(), + (this.videoElement.src = ""), + ((t = new google.ima.AdsRequest()).adTagUrl = e), + (t.linearAdSlotWidth = this.currentWidth), + (t.linearAdSlotHeight = this.currentHeight), + (t.nonLinearAdSlotWidth = this.currentWidth), + (t.nonLinearAdSlotHeight = this.currentHeight), + (t.forceNonLinearFullSlot = !0), + [4, this.canWeAutoPlayWithSound()]); + case 1: + return i.sent() ? (t.setAdWillPlayMuted(!1), (this.currentRequestIsMuted = !1)) : (t.setAdWillPlayMuted(!0), (this.currentRequestIsMuted = !0)), (this.allowedToPlayAd = !0), this.adsLoader.requestAds(t), [2]; + } + }); + }); + }), + (e.prototype.resize = function (e, t, i) { + void 0 === i && (i = google.ima.ViewMode.NORMAL), (this.currentWidth = e), (this.currentHeight = t), this.adsManager && this.adsManager.resize(e, t, i); + }), + (e.prototype.onAdsManagerLoaded = function (e) { + var t = new google.ima.AdsRenderingSettings(); + (t.enablePreloading = !0), + (t.restoreCustomPlaybackStateOnAdBreakComplete = !0), + (t.mimeTypes = x() || S() || E() ? ["video/mp4"] : ["video/mp4", "video/webm", "video/ogg"]), + (t.loadVideoTimeout = 8e3), + (this.adsManager = e.getAdsManager(this.videoElement, t)), + this.adsManager.setVolume(Math.max(0, Math.min(1, this.volume))), + this.currentRequestIsMuted && this.adsManager.setVolume(0), + this.allowedToPlayAd ? (this.attachAdEvents(), o.dispatchEvent(n.ads.ready)) : this.tearDown(); + }), + (e.prototype.setVolume = function (e) { + (this.volume = e), this.adsManager && this.adsManager.setVolume(Math.max(0, Math.min(1, this.volume))); + }), + (e.prototype.startPlayback = function () { + try { + this.adsManager.init(this.currentWidth, this.currentHeight, google.ima.ViewMode.NORMAL), this.adsManager.start(); + } catch (e) { + this.videoElement.play(); + } + }), + (e.prototype.startIOSPlayback = function () { + this.adsManager.start(); + }), + (e.prototype.stopPlayback = function () { + o.dispatchEvent(n.ads.stopped), this.tearDown(); + }), + (e.prototype.resumeAd = function () { + o.dispatchEvent(n.ads.video.resumed), this.adsManager && this.adsManager.resume(); + }), + (e.prototype.tearDown = function () { + this.adsManager && (this.adsManager.stop(), this.adsManager.destroy(), (this.adsManager = null)), + null !== this.bannerTimeout && (clearTimeout(this.bannerTimeout), (this.bannerTimeout = null)), + this.adsLoader && (this.adsLoader.contentComplete(), this.adsLoader.destroy(), (this.adsLoader = null), this.initAdsLoader()), + (this.runningAd = !1); + }), + (e.prototype.attachAdEvents = function () { + var e = this, + t = google.ima.AdEvent.Type; + this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError, !1, this), + [t.AD_PROGRESS, t.ALL_ADS_COMPLETED, t.CLICK, t.COMPLETE, t.IMPRESSION, t.PAUSED, t.SKIPPED, t.STARTED, t.USER_CLOSE, t.AD_BUFFERING].forEach(function (t) { + e.adsManager.addEventListener(t, e.onAdEvent, !1, e); + }); + }), + (e.prototype.onAdEvent = function (e) { + var t = this, + i = e.getAd(); + switch (e.type) { + case google.ima.AdEvent.Type.AD_PROGRESS: + o.dispatchEvent(n.ads.video.progress, e.getAdData()); + break; + case google.ima.AdEvent.Type.STARTED: + (e.remainingTime = this.adsManager.getRemainingTime()), + e.remainingTime <= 0 && (e.remainingTime = 15), + i.isLinear() || + (this.bannerTimeout = window.setTimeout(function () { + o.dispatchEvent(n.ads.completed, { + rewardAllowed: !!e.rewardAllowed, + }), + t.tearDown(); + }, 1e3 * (e.remainingTime + 1))), + o.setDataAnnotations({ + creativeId: i.getCreativeId(), + }), + o.dispatchEvent(n.ads.started, { + duration: i.getDuration(), + }); + break; + case google.ima.AdEvent.Type.COMPLETE: + o.dispatchEvent(n.ads.completed, { + rewardAllowed: !0, + }), + this.tearDown(); + break; + case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: + case google.ima.AdEvent.Type.USER_CLOSE: + this.tearDown(); + break; + case google.ima.AdEvent.Type.PAUSED: + this.adsManager.pause(), o.dispatchEvent(n.ads.video.paused); + break; + case google.ima.AdEvent.Type.AD_BUFFERING: + o.dispatchEvent(n.ads.video.buffering); + break; + case google.ima.AdEvent.Type.CLICK: + o.dispatchEvent(n.ads.video.clicked); + break; + case google.ima.AdEvent.Type.SKIPPED: + o.dispatchEvent(n.ads.skipped), + o.dispatchEvent(n.ads.completed, { + rewardAllowed: !0, + }), + document.activeElement && document.activeElement.blur(); + break; + case google.ima.AdEvent.Type.IMPRESSION: + o.dispatchEvent(n.ads.impression); + } + }), + (e.prototype.onAdLoaderError = function (e) { + this.tearDown(); + var t = (e.getError && e.getError().toString()) || "Unknown"; + o.dispatchEvent(n.ads.video.loaderError, { + message: t, + }); + }), + (e.prototype.onAdError = function (e) { + this.tearDown(); + var t = (e.getError && e.getError().toString()) || "Unknown"; + o.dispatchEvent(n.ads.video.error, { + message: t, + }); + }), + (e.prototype.muteAd = function () { + void 0 !== this.adsManager && null != this.adsManager && this.adsManager.setVolume(0); + }), + (e.prototype.isAdRunning = function () { + return this.runningAd; + }), + e + ); + })(); + const Le = function (e) { + return new Promise(function (t, i) { + var n = document.createElement("script"); + (n.type = "text/javascript"), (n.async = !0), (n.src = e); + var r = function () { + (n.readyState && "loaded" !== n.readyState && "complete" !== n.readyState) || (t(), (n.onload = null), (n.onreadystatechange = null)); + }; + (n.onload = r), (n.onreadystatechange = r), (n.onerror = i), document.head.appendChild(n); + }); + }; + var ze = function (e, t, i, n) { + return new (i || (i = Promise))(function (r, a) { + function o(e) { + try { + d(n.next(e)); + } catch (e) { + a(e); + } + } + + function s(e) { + try { + d(n.throw(e)); + } catch (e) { + a(e); + } + } + + function d(e) { + var t; + e.done + ? r(e.value) + : ((t = e.value), + t instanceof i + ? t + : new i(function (e) { + e(t); + })).then(o, s); + } + d((n = n.apply(e, t || [])).next()); + }); + }, + Oe = function (e, t) { + var i, + n, + r, + a, + o = { + label: 0, + sent: function () { + if (1 & r[0]) throw r[1]; + return r[1]; + }, + trys: [], + ops: [], + }; + return ( + (a = { + next: s(0), + throw: s(1), + return: s(2), + }), + "function" == typeof Symbol && + (a[Symbol.iterator] = function () { + return this; + }), + a + ); + + function s(a) { + return function (s) { + return (function (a) { + if (i) throw new TypeError("Generator is already executing."); + for (; o; ) + try { + if (((i = 1), n && (r = 2 & a[0] ? n.return : a[0] ? n.throw || ((r = n.return) && r.call(n), 0) : n.next) && !(r = r.call(n, a[1])).done)) return r; + switch (((n = 0), r && (a = [2 & a[0], r.value]), a[0])) { + case 0: + case 1: + r = a; + break; + case 4: + return ( + o.label++, + { + value: a[1], + done: !1, + } + ); + case 5: + o.label++, (n = a[1]), (a = [0]); + continue; + case 7: + (a = o.ops.pop()), o.trys.pop(); + continue; + default: + if (!((r = o.trys), (r = r.length > 0 && r[r.length - 1]) || (6 !== a[0] && 2 !== a[0]))) { + o = 0; + continue; + } + if (3 === a[0] && (!r || (a[1] > r[0] && a[1] < r[3]))) { + o.label = a[1]; + break; + } + if (6 === a[0] && o.label < r[1]) { + (o.label = r[1]), (r = a); + break; + } + if (r && o.label < r[2]) { + (o.label = r[2]), o.ops.push(a); + break; + } + r[2] && o.ops.pop(), o.trys.pop(); + continue; + } + a = t.call(e, o); + } catch (e) { + (a = [6, e]), (n = 0); + } finally { + i = r = 0; + } + if (5 & a[0]) throw a[1]; + return { + value: a[0] ? a[1] : void 0, + done: !0, + }; + })([a, s]); + }; + } + }; + const Ge = function () {}; + var je = function (e, t, i, n) { + return new (i || (i = Promise))(function (r, a) { + function o(e) { + try { + d(n.next(e)); + } catch (e) { + a(e); + } + } + + function s(e) { + try { + d(n.throw(e)); + } catch (e) { + a(e); + } + } + + function d(e) { + var t; + e.done + ? r(e.value) + : ((t = e.value), + t instanceof i + ? t + : new i(function (e) { + e(t); + })).then(o, s); + } + d((n = n.apply(e, t || [])).next()); + }); + }, + Ue = function (e, t) { + var i, + n, + r, + a, + o = { + label: 0, + sent: function () { + if (1 & r[0]) throw r[1]; + return r[1]; + }, + trys: [], + ops: [], + }; + return ( + (a = { + next: s(0), + throw: s(1), + return: s(2), + }), + "function" == typeof Symbol && + (a[Symbol.iterator] = function () { + return this; + }), + a + ); + + function s(a) { + return function (s) { + return (function (a) { + if (i) throw new TypeError("Generator is already executing."); + for (; o; ) + try { + if (((i = 1), n && (r = 2 & a[0] ? n.return : a[0] ? n.throw || ((r = n.return) && r.call(n), 0) : n.next) && !(r = r.call(n, a[1])).done)) return r; + switch (((n = 0), r && (a = [2 & a[0], r.value]), a[0])) { + case 0: + case 1: + r = a; + break; + case 4: + return ( + o.label++, + { + value: a[1], + done: !1, + } + ); + case 5: + o.label++, (n = a[1]), (a = [0]); + continue; + case 7: + (a = o.ops.pop()), o.trys.pop(); + continue; + default: + if (!((r = o.trys), (r = r.length > 0 && r[r.length - 1]) || (6 !== a[0] && 2 !== a[0]))) { + o = 0; + continue; + } + if (3 === a[0] && (!r || (a[1] > r[0] && a[1] < r[3]))) { + o.label = a[1]; + break; + } + if (6 === a[0] && o.label < r[1]) { + (o.label = r[1]), (r = a); + break; + } + if (r && o.label < r[2]) { + (o.label = r[2]), o.ops.push(a); + break; + } + r[2] && o.ops.pop(), o.trys.pop(); + continue; + } + a = t.call(e, o); + } catch (e) { + (a = [6, e]), (n = 0); + } finally { + i = r = 0; + } + if (5 & a[0]) throw a[1]; + return { + value: a[0] ? a[1] : void 0, + done: !0, + }; + })([a, s]); + }; + } + }; + + function Ne() {} + var Qe = function (e, t, i, n) { + return new (i || (i = Promise))(function (r, a) { + function o(e) { + try { + d(n.next(e)); + } catch (e) { + a(e); + } + } + + function s(e) { + try { + d(n.throw(e)); + } catch (e) { + a(e); + } + } + + function d(e) { + var t; + e.done + ? r(e.value) + : ((t = e.value), + t instanceof i + ? t + : new i(function (e) { + e(t); + })).then(o, s); + } + d((n = n.apply(e, t || [])).next()); + }); + }, + Fe = function (e, t) { + var i, + n, + r, + a, + o = { + label: 0, + sent: function () { + if (1 & r[0]) throw r[1]; + return r[1]; + }, + trys: [], + ops: [], + }; + return ( + (a = { + next: s(0), + throw: s(1), + return: s(2), + }), + "function" == typeof Symbol && + (a[Symbol.iterator] = function () { + return this; + }), + a + ); + + function s(a) { + return function (s) { + return (function (a) { + if (i) throw new TypeError("Generator is already executing."); + for (; o; ) + try { + if (((i = 1), n && (r = 2 & a[0] ? n.return : a[0] ? n.throw || ((r = n.return) && r.call(n), 0) : n.next) && !(r = r.call(n, a[1])).done)) return r; + switch (((n = 0), r && (a = [2 & a[0], r.value]), a[0])) { + case 0: + case 1: + r = a; + break; + case 4: + return ( + o.label++, + { + value: a[1], + done: !1, + } + ); + case 5: + o.label++, (n = a[1]), (a = [0]); + continue; + case 7: + (a = o.ops.pop()), o.trys.pop(); + continue; + default: + if (!((r = o.trys), (r = r.length > 0 && r[r.length - 1]) || (6 !== a[0] && 2 !== a[0]))) { + o = 0; + continue; + } + if (3 === a[0] && (!r || (a[1] > r[0] && a[1] < r[3]))) { + o.label = a[1]; + break; + } + if (6 === a[0] && o.label < r[1]) { + (o.label = r[1]), (r = a); + break; + } + if (r && o.label < r[2]) { + (o.label = r[2]), o.ops.push(a); + break; + } + r[2] && o.ops.pop(), o.trys.pop(); + continue; + } + a = t.call(e, o); + } catch (e) { + (a = [6, e]), (n = 0); + } finally { + i = r = 0; + } + if (5 & a[0]) throw a[1]; + return { + value: a[0] ? a[1] : void 0, + done: !0, + }; + })([a, s]); + }; + } + }, + Xe = !1, + Ze = function () { + return Qe(void 0, void 0, void 0, function () { + var e, t, i; + return Fe(this, function (n) { + switch (n.label) { + case 0: + if (Xe) return [2]; + n.label = 1; + case 1: + return n.trys.push([1, 4, , 5]), [4, fetch("./touchControllerConfig.json")]; + case 2: + return [4, n.sent().json()]; + case 3: + return ( + (e = n.sent()) && + (((t = document.createElement("script")).src = "//game-cdn.poki.com/scripts/touchOverlayController.js"), + (t.onload = function () { + new window.OverlayController(document.body, e); + }), + document.head.appendChild(t), + (Xe = !0)), + [3, 5] + ); + case 4: + return (i = n.sent()), console.log(i), [3, 5]; + case 5: + return [2]; + } + }); + }); + }; + const Ke = function () { + for (var e = Math.floor(Date.now() / 1e3), t = "", i = 0; i < 4; i++) (t = String.fromCharCode(255 & e) + t), (e >>= 8); + if (window.crypto && crypto.getRandomValues && Uint32Array) { + var n = new Uint32Array(12); + crypto.getRandomValues(n); + for (i = 0; i < 12; i++) t += String.fromCharCode(255 & n[i]); + } else for (i = 0; i < 12; i++) t += String.fromCharCode(Math.floor(256 * Math.random())); + return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + }; + var He = (function () { + function e() { + this.slotMap = new Map(); + } + return ( + (e.prototype.waitUntilReady = function (e) { + window.pbjs.que.push(function () { + e(); + }); + }), + (e.prototype.setupSlotRenderEndedListener = function () { + var e = this; + this.waitUntilReady(function () { + window.googletag.pubads().addEventListener("slotRenderEnded", function (t) {}), window.googletag.pubads().addEventListener("impressionViewable", function (t) {}); + }); + }), + (e.prototype.validateDisplaySettings = function (e) { + return !!(S() || E() || ["970x250", "300x250", "728x90", "160x600", "320x50"].includes(e)) && !((S() || E()) && !["320x50"].includes(e)); + }), + (e.prototype.getDisplaySlotConfig = function (e) { + var t = e.split("x").map(function (e) { + return parseInt(e, 10); + }), + i = "/21682198607/debug-display/debug-display-" + e, + n = "desktop"; + S() && (n = "mobile"), E() && (n = "tablet"); + var a = parseInt(_("site_id"), 10) || 0; + return ( + r.debug || (i = nt.GetIsPokiIFrame() ? "/21682198607/" + n + "_ingame_" + e + "/" + a + "_" + n + "_ingame_" + e : "/21682198607/external_" + n + "_display_ingame/external_" + n + "_ingame_" + e), + { + id: "poki-" + Ke(), + adUnitPath: i, + size: e, + width: t[0], + height: t[1], + refresh: !1, + } + ); + }), + (e.prototype.renderIGDAd = function (e, t, i, n, r) { + var a = this, + o = this.getIGDSlotID(e); + o && this.slotMap.get(o) && (console.error("displayAd called with a container that already contains an ad"), this.clearIGDAd(e)); + var s = this.getDisplaySlotConfig(t); + this.slotMap.set(s.id, s), (s.opportunityId = n), (s.duringGameplayFn = r); + var d = document.createElement("div"); + (d.style.width = s.width + "px"), + (d.style.height = s.height + "px"), + e.appendChild(d), + e.setAttribute("data-poki-ad-id", s.id), + (s.intersectionObserver = new window.IntersectionObserver( + function (t) { + var r; + t[0].isIntersecting && + (null === (r = s.intersectionObserver) || void 0 === r || r.disconnect(), + a.waitUntilReady(function () { + var t = a.slotMap.get(s.id); + if (t && t.opportunityId === n) { + (d.style.background = "#CCC"), + (d.innerHTML = "

ADVERTISEMENT

"), + (d.style.display = "flex"), + (d.style.justifyContent = "center"), + (d.style.alignItems = "center"), + (d.style.position = "absolute"), + (d.style.zIndex = "1"), + (d.style.fontFamily = "Helvetica"), + (d.style.fontSize = "16px"), + (d.style.color = "#888"), + (d.style.pointerEvents = "none"); + var r = document.createElement("div"); + (r.id = s.id), + (r.className = "poki-ad-slot"), + (r.style.width = s.width + "px"), + (r.style.height = s.height + "px"), + (r.style.position = "relative"), + (r.style.zIndex = "2"), + r.setAttribute("data-poki-ad-size", s.size), + e.appendChild(r), + a.setupGPT(s, i), + a.requestAd(s); + } + })); + }, + { + threshold: 1, + } + )), + s.intersectionObserver.observe(d); + }), + (e.prototype.setupGPT = function (e, t) { + var i; + (e.gptSlot = window.googletag.defineSlot(e.adUnitPath, [e.width, e.height], e.id).addService(window.googletag.pubads())), + window.googletag.enableServices(), + null === (i = e.gptSlot) || void 0 === i || i.clearTargeting(), + Object.keys(t).forEach(function (i) { + var n; + null === (n = e.gptSlot) || void 0 === n || n.setTargeting(i, t[i]); + }); + }), + (e.prototype.requestAd = function (e) { + var t, + i = this; + dt.track(n.tracking.ads.display.requested, { + size: e.size, + opportunityId: e.opportunityId, + adUnitPath: e.adUnitPath, + refresh: e.refresh, + duringGameplay: null === (t = e.duringGameplayFn) || void 0 === t ? void 0 : t.call(e), + }), + window.pbjs.requestBids({ + adUnitCodes: [e.adUnitPath], + bidsBackHandler: function () { + i.bidsBackHandler(e.id); + }, + }); + }), + (e.prototype.clearIGDAd = function (e) { + var t, + i = this.getIGDSlotID(e); + if (i) { + var r = this.slotMap.get(i) || null; + if (r) { + for ( + dt.track(n.tracking.screen.destroyAd, { + opportunityId: r.opportunityId, + }), + null === (t = r.intersectionObserver) || void 0 === t || t.disconnect(), + r.gptSlot && googletag.destroySlots([r.gptSlot]); + e.lastChild; + + ) + e.removeChild(e.lastChild); + e.removeAttribute("data-poki-ad-id"), this.slotMap.delete(r.id); + } + } else console.error("destroyAd called on a container without ad"); + }), + (e.prototype.getIGDSlotID = function (e) { + if (!e) return null; + var t = e.getAttribute("data-poki-ad-id"); + return t || null; + }), + (e.prototype.bidsBackHandler = function (e) { + var t = this.slotMap.get(e); + t && t.gptSlot && (window.pbjs.setTargetingForGPTAsync([t.adUnitPath]), (t.adserverTargeting = window.pbjs.getAdserverTargetingForAdUnitCode([t.adUnitPath])), window.googletag.display(t.id)); + }), + e + ); + })(); + const We = He; + var Ve, + qe = + ((Ve = function (e, t) { + return ( + (Ve = + Object.setPrototypeOf || + ({ + __proto__: [], + } instanceof Array && + function (e, t) { + e.__proto__ = t; + }) || + function (e, t) { + for (var i in t) Object.prototype.hasOwnProperty.call(t, i) && (e[i] = t[i]); + }), + Ve(e, t) + ); + }), + function (e, t) { + if ("function" != typeof t && null !== t) throw new TypeError("Class extends value " + String(t) + " is not a constructor or null"); + + function i() { + this.constructor = e; + } + Ve(e, t), (e.prototype = null === t ? Object.create(t) : ((i.prototype = t.prototype), new i())); + }), + Je = (function (e) { + function t() { + return (null !== e && e.apply(this, arguments)) || this; + } + return ( + qe(t, e), + (t.prototype.waitUntilReady = function (e) { + window.pbjs.que.push(function () { + e(); + }); + }), + (t.prototype.requestAd = function (t) { + z({ + event: "request", + size: t.size, + opportunityId: t.opportunityId, + adUnitPath: t.adUnitPath, + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + }), + e.prototype.requestAd.call(this, t); + }), + (t.prototype.bidsBackHandler = function (e) { + var t, + i, + r = this.slotMap.get(e); + if (r) { + var a = document.createElement("iframe"); + a.setAttribute("frameborder", "0"), + a.setAttribute("scrolling", "no"), + a.setAttribute("marginheight", "0"), + a.setAttribute("marginwidth", "0"), + a.setAttribute("topmargin", "0"), + a.setAttribute("leftmargin", "0"), + a.setAttribute("allowtransparency", "true"), + a.setAttribute("width", "" + r.width), + a.setAttribute("height", "" + r.height); + var o = document.getElementById(r.id); + if (o) { + o.appendChild(a); + var s = null === (t = null == a ? void 0 : a.contentWindow) || void 0 === t ? void 0 : t.document; + if (!s) return console.error("IGD error - iframe injection for ad failed", e), void this.clearIGDAd(o.parentNode); + if (((r.adserverTargeting = window.pbjs.getAdserverTargetingForAdUnitCode([r.adUnitPath])), !r.adserverTargeting.hb_adid)) + return console.error("IGD info - nothing to render", e, r.adserverTargeting), void this.clearIGDAd(o.parentNode); + var d = r.adserverTargeting.hb_bidder, + A = parseFloat(r.adserverTargeting.hb_pb); + isNaN(A) && (A = 0), + window.pbjs.renderAd(s, r.adserverTargeting.hb_adid), + dt.track(n.tracking.ads.display.impression, { + size: r.size, + opportunityId: r.opportunityId, + duringGameplay: null === (i = r.duringGameplayFn) || void 0 === i ? void 0 : i.call(r), + adUnitPath: r.adUnitPath, + prebidBid: A, + prebidBidder: d, + preBidWon: !0, + dfpIsBackfill: !1, + dfpLineItemId: void 0, + dfpCampaignId: void 0, + }), + z({ + event: "impression", + size: r.size, + opportunityId: r.opportunityId, + adUnitPath: r.adUnitPath, + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + bidder: d, + bid: A, + }), + (r.intersectionObserver = new IntersectionObserver( + function (e) { + e.forEach(function (e) { + e.isIntersecting + ? r.intersectingTimer || + (r.intersectingTimer = setTimeout(function () { + var t, i; + null === (t = r.intersectionObserver) || void 0 === t || t.unobserve(e.target), + dt.track(n.tracking.ads.display.viewable, { + size: r.size, + opportunityId: r.opportunityId, + duringGameplay: null === (i = r.duringGameplayFn) || void 0 === i ? void 0 : i.call(r), + adUnitPath: r.adUnitPath, + }), + z({ + event: "viewable", + size: r.size, + opportunityId: r.opportunityId, + adUnitPath: r.adUnitPath, + p4d_game_id: tt.gameId, + p4d_version_id: tt.versionId, + bidder: d, + bid: A, + }); + }, 1e3)) + : r.intersectingTimer && (clearTimeout(r.intersectingTimer), (r.intersectingTimer = void 0)); + }); + }, + { + threshold: 0.5, + } + )), + r.intersectionObserver.observe(o); + } else console.error("IGD error - container not found", e); + } + }), + (t.prototype.setupGPT = function (e, t) {}), + (t.prototype.setupSlotRenderEndedListener = function () {}), + t + ); + })(We); + const Ye = Je; + var $e = function () { + return ( + ($e = + Object.assign || + function (e) { + for (var t, i = 1, n = arguments.length; i < n; i++) for (var r in (t = arguments[i])) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e; + }), + $e.apply(this, arguments) + ); + }, + et = function (e, t, i) { + if (i || 2 === arguments.length) for (var n, r = 0, a = t.length; r < a; r++) (!n && r in t) || (n || (n = Array.prototype.slice.call(t, 0, r)), (n[r] = t[r])); + return e.concat(n || Array.prototype.slice.call(t)); + }, + tt = { + gameId: _("game_id"), + versionId: _("game_version_id"), + }, + it = (function () { + function e() { + (this.autoStartOnReady = !1), + (this.criteria = {}), + (this.debugIsOverwritten = !1), + (this.handlers = {}), + (this.initializingPromise = null), + (this.isInitialized = !1), + (this.programmaticAdsEnabled = !0), + (this.sdkBooted = !1), + (this.sdkImaError = !1), + (this.startAdEnabled = !1), + (this.startStartAdsAfterTimerOnInit = !1), + (this.initOptions = {}), + (this.forceDisableCommercialBreak = !1), + (this.installedTCFv2 = !1), + (this.installedUSP = !1), + (this.isBot = !1), + (this.adReady = !1), + (this.debugTouchOverlayController = !1), + (this.setPlayerAge = function (e) { + e && + (function (e, t) { + if (Se) + try { + localStorage.setItem(e, t); + } catch (i) { + (Se = !1), (Ee[e] = t); + } + else Ee[e] = t; + })("playerAge", e); + }), + (this.toggleNonPersonalized = function (t) { + e.nonPersonalized = t; + }), + (this.setConsentString = function (t) { + e.consentString = t; + }), + (this.setLogging = function (e) { + r.log = e; + }), + (this.sdkNotBootedButCalled = function () { + console.error("The Poki SDK has not yet been initialized"); + }), + d() ? (this.IGD = new Ye()) : (this.IGD = new We()); + var t = _("pokiDebug"); + "" !== t && (this.setDebug("true" === t), (this.debugIsOverwritten = !0)), "" !== _("pokiLogging") && this.setLogging(!0); + } + return ( + (e.prototype.init = function (e) { + if ((void 0 === e && (e = {}), "undefined" != typeof window)) { + var t = e.onReady, + i = void 0 === t ? null : t, + n = e.onAdblocked, + r = void 0 === n ? null : n; + return ( + (this.initOptions = e), + i && this.registerHandler("onReady", i), + r && this.registerHandler("onAdblocked", r), + this.isInitialized ? console.error("Poki SDK has already been initialized") : (this.initializingPromise || (this.initializingPromise = this.lazyLoadInit()), this.initializingPromise) + ); + } + }), + (e.prototype.lazyLoadInit = function () { + var t = this, + i = this.initOptions, + a = i.debug, + s = void 0 !== a && a, + d = i.prebid, + A = void 0 === d ? {} : d, + c = i.a9, + p = void 0 === c ? {} : c, + u = i.volume, + h = void 0 === u ? 1 : u, + m = i.waterfallRetries, + g = i.wrapper, + v = void 0 === g ? document.body : g, + f = parseInt(_("site_id"), 10) || 0; + (this.isBot = "1" === _("bot") || {}), this.setupDefaultEvents(), dt.setupDefaultEvents(), e.GetIsPokiIFrame() && _e(), setTimeout(w.trackSavegames, 1e4); + var b = $e({}, l), + y = Ge; + r.debug && + (y = function () { + return Promise.resolve(); + }); + var k = Ne, + I = _("ccpaApplies"), + x = this.initOptions.country || _("country"), + C = void 0 !== this.initOptions.isCCPA ? this.initOptions.isCCPA : "" !== I ? "1" === I : void 0; + x && + void 0 !== C && + (k = function () { + return Promise.resolve({ + ISO: x, + ccpaApplies: C, + }); + }); + var T = !1; + x && ($(A, x), (T = !0)), + window.addEventListener("resize", this.resize.bind(this), !1), + window.addEventListener("message", this.onMessage.bind(this), !1), + this.debugIsOverwritten || this.setDebug(r.debug || s), + this.debugTouchOverlayController && (S() || E()) && Ze(); + var B = [y(), k()], + P = et([], B, !0); + this.isBot || P.push(Le("")); + var D = function (e, i, a) { + if ((void 0 === a && (a = !0), (t.country = x || (null == i ? void 0 : i.ISO) || "zz"), (t.isCCPA = void 0 === C ? (null == i ? void 0 : i.ccpaApplies) || !1 : C), e)) { + tt.gameId || (tt.gameId = e.gameId); + [""].includes(tt.gameId) && (S() || E()) && Ze(), + (b.adTiming = e.adTiming), + (b.customCriteria = $e($e({}, b.customCriteria), { + p4d_game_id: tt.gameId, + })); + } + Be(t.country) && + !r.debug && + (ie(), + console.debug("GDPR - waiting for __tcfapi callback"), + window.__tcfapi("ping", 2, function () { + console.debug("GDPR - __tcfapi callback received"), (t.installedTCFv2 = !0), a && o.dispatchEvent(n.ready); + }), + setTimeout(function () { + t.installedTCFv2 || (console.error("GDPR - No __tcfapi callback after 2s, verify implementation!"), a && o.dispatchEvent(n.ready)); + }, 2e3)), + t.isCCPA && + !r.debug && + (ne(), + console.debug("USPrivacy - waiting for __uspapi callback"), + window.__uspapi("uspPing", 1, function () { + console.debug("USPrivacy - __uspapi callback received"), (t.installedUSP = !0), a && o.dispatchEvent(n.ready); + }), + setTimeout(function () { + t.installedUSP || (console.error("USPrivacy - No __uspapi callback after 2s, verify implementation!"), a && o.dispatchEvent(n.ready)); + }, 2e3)); + }; + return Promise.all(P) + .catch(function () { + Promise.all(B).then(function (e) { + var t = e[0], + i = e[1]; + D(t, i, !1); + }), + o.dispatchEvent(n.adblocked); + }) + .then(function (e) { + if (void 0 !== e) { + var i = e[0], + a = e[1]; + D(i, a), + T || $(A, t.country), + r.debug && (b.adTiming.startAdsAfter = 0), + t.enableSettings(b), + (t.playerSkin = new we({ + wrapper: v, + })), + (t.ima = new Re(h)), + t.playerSkin.setupEvents(t), + t.startStartAdsAfterTimerOnInit && t.adTimings.startStartAdsAfterTimer(), + (t.waterfall = new oe(t.ima, { + timing: t.adTimings, + totalRetries: m, + siteID: f, + country: t.country, + })), + t.IGD.setupSlotRenderEndedListener(); + var s = Be(t.country); + te(p, t.country, s), (t.isInitialized = !0), t.isCCPA || s || o.dispatchEvent(n.ready), r.debug && o.dispatchEvent(n.ready); + } + }); + }), + (e.prototype.requestAd = function (e) { + void 0 === e && (e = {}); + var t = e.autoStart, + i = void 0 === t || t, + a = e.onFinish, + d = void 0 === a ? null : a, + A = e.onStart, + c = void 0 === A ? null : A, + l = e.position, + p = void 0 === l ? null : l; + if (((this.autoStartOnReady = !1 !== i), d && this.registerHandler("onFinish", d), c && this.registerHandler("onStart", c), this.forceDisableCommercialBreak && [n.ads.position.midroll, n.ads.position.preroll].includes(p))) + d && d(); + else if (this.isBot) d && d({}); + else { + if (!this.sdkBooted) + return ( + o.dispatchEvent(n.ads.error, { + message: "Requesting ad on unbooted SDK", + }), + void this.sdkNotBootedButCalled() + ); + else if ((!S() && !E()) || p === n.ads.position.rewarded) + if (null !== p && s(p, n.ads.position)) if (!Be(this.country) || this.installedTCFv2 || r.debug) if (!this.isCCPA || this.installedUSP) if (this.adReady) o.dispatchEvent(n.ads.ready); + + if (p === n.ads.position.rewarded) { + } else + o.dispatchEvent(n.ads.limit, { + reason: n.info.messages.timeLimit, + }); + } + }), + (e.prototype.displayAd = function (e, t, i, a) { + if (!this.isBot) { + var s = n.ads.position.display; + if (!Be(this.country) || this.installedTCFv2 || r.debug) + if (!this.isCCPA || window.__uspapi) + if (t) { + if (!this.sdkBooted) + return ( + o.dispatchEvent(n.ads.error, { + message: "Requesting ad on unbooted SDK", + position: s, + }), + void this.sdkNotBootedButCalled() + ); + if (e) + if (this.sdkImaError) + o.dispatchEvent(n.ads.error, { + message: "Adblocker has been detected", + position: s, + }); + else { + if (!this.IGD.validateDisplaySettings(t)) + return o.dispatchEvent(n.ads.error, { + reason: "Display size " + t + " is not supported on this device", + position: s, + }); + var d = $e($e({}, this.genericCriteria()), this.criteria); + this.IGD.renderIGDAd(e, t, d, i, a); + } + else + o.dispatchEvent(n.ads.error, { + message: "Provided container does not exist", + position: s, + }); + } else + o.dispatchEvent(n.ads.error, { + message: "No ad size given, usage: displayAd(, )", + position: s, + }); + else + o.dispatchEvent(n.ads.error, { + message: "No USP detected, please contact developersupport@poki.com for more information", + position: s, + }); + else + o.dispatchEvent(n.ads.error, { + message: "No TCFv2 CMP detected, please contact developersupport@poki.com for more information", + position: s, + }); + } + }), + (e.prototype.destroyAd = function (e) { + if (!this.sdkBooted) return o.dispatchEvent(), void this.sdkNotBootedButCalled(); + this.sdkImaError ? o.dispatchEvent() : ((e = e || document.body), this.IGD.clearIGDAd(e)); + }), + (e.prototype.startStartAdsAfterTimer = function () {}), + (e.prototype.enableSettings = function (e) { + (this.criteria = $e({}, e.customCriteria)), (this.adTimings = new u(e.adTiming)); + }), + (e.prototype.togglePlayerAdvertisingConsent = function (e) { + if (e) { + var t, + i = + parseInt( + (function (e) { + if (!Se) return Ee[e]; + try { + return localStorage.getItem(e); + } catch (t) { + return Ee[e]; + } + })("playerAge"), + 10 + ) || 0, + n = this.country, + r = Be(n), + a = ((t = n), Ce.includes(t)), + o = Pe(n); + (r || a || Pe) && ((r && i <= 12) || (a && i <= 16) || (o && i <= 16)) ? this.disableProgrammatic() : this.enableProgrammatic(); + } else this.disableProgrammatic(); + }), + (e.prototype.disableProgrammatic = function () { + (e.childDirected = !0), (this.programmaticAdsEnabled = !1); + }), + (e.prototype.enableProgrammatic = function () { + (e.childDirected = !1), (this.programmaticAdsEnabled = !0); + }), + (e.prototype.getProgrammaticAdsEnabled = function () { + return this.programmaticAdsEnabled; + }), + (e.prototype.setDebug = function (e) { + this.debugIsOverwritten ? e && dt.track(n.tracking.debugTrueInProduction) : (r.debug = e); + }), + (e.prototype.resize = function () { + var e = this; + if (!this.sdkBooted) return this.sdkNotBootedButCalled(); + if (!this.sdkImaError) { + var t = this.playerSkin.getVideoBounds(); + 0 !== t.width && 0 !== t.height + ? this.ima.resize(t.width, t.height) + : setTimeout(function () { + e.resize(); + }, 100); + } + }), + (e.prototype.onMessage = function (e) { + if ("string" == typeof e.data.type) + switch (e.data.type) { + case "toggleNonPersonalized": + this.toggleNonPersonalized(!(!e.data.content || !e.data.content.nonPersonalized)); + break; + case "setPersonalizedADConsent": + this.toggleNonPersonalized(!(e.data.content && e.data.content.consent)), this.setConsentString(e.data.content ? e.data.content.consentString : ""); + break; + case "forceDisableCommercialBreak": + this.forceDisableCommercialBreak = !0; + } + }), + (e.prototype.startAd = function () { + if (!this.sdkBooted) return this.sdkNotBootedButCalled(); + this.sdkImaError || + (this.adReady + ? (this.resize(), this.ima.startPlayback()) + : o.dispatchEvent(n.ads.error, { + message: "No ads ready to start", + })); + }), + (e.prototype.startAdClicked = function () { + "undefined" != typeof navigator && /(iPad|iPhone|iPod)/gi.test(navigator.userAgent) && this.startAdEnabled && ((this.startAdEnabled = !1), this.playerSkin.hideStartAdButton(), this.ima.startIOSPlayback()); + }), + (e.prototype.stopAd = function () { + if (!this.sdkBooted) return this.sdkNotBootedButCalled(); + this.sdkImaError || (this.waterfall.stopWaterfall(), this.ima.stopPlayback(), this.playerSkin.hide()); + }), + (e.prototype.resumeAd = function () { + if (!this.sdkBooted) return this.sdkNotBootedButCalled(); + this.sdkImaError || (this.playerSkin.hidePauseButton(), this.ima.resumeAd()); + }), + (e.prototype.skipAd = function () { + this.stopAd(), + this.callHandler("onFinish", { + type: n.ads.completed, + rewardAllowed: !0, + }); + }), + (e.prototype.muteAd = function () { + if (!this.sdkBooted) return this.sdkNotBootedButCalled(); + this.sdkImaError || this.ima.muteAd(); + }), + (e.prototype.registerHandler = function (e, t) { + this.handlers[e] = t; + }), + (e.prototype.callHandler = function (e) { + for (var t = [], i = 1; i < arguments.length; i++) t[i - 1] = arguments[i]; + "function" == typeof this.handlers[e] && this.handlers[e](t); + }), + (e.prototype.setupDefaultEvents = function () { + var e = this; + o.addEventListener(n.ready, function () { + (e.sdkBooted = !0), e.callHandler("onReady"); + }), + o.addEventListener(n.adblocked, function () { + (e.sdkBooted = !0), e.callHandler("onReady"); + }), + o.addEventListener(n.ads.ready, function () { + (e.adReady = !0), e.callHandler("onReady"); + }), + o.addEventListener(n.ads.started, function () { + e.playerSkin.hideSpinner(), + e.callHandler("onStart", { + type: n.ads.limit, + }); + }), + o.addEventListener(n.ads.video.paused, function () { + e.playerSkin.showPauseButton(); + }), + o.addEventListener(n.ads.limit, function () { + e.callHandler("onFinish", { + type: n.ads.limit, + rewardAllowed: !1, + }); + }), + o.addEventListener(n.ads.stopped, function () { + e.callHandler("onFinish", { + type: n.ads.stopped, + rewardAllowed: !1, + }); + }), + o.addEventListener(n.ads.error, function () { + e.callHandler("onFinish", { + type: n.ads.error, + rewardAllowed: !1, + }); + }), + o.addEventListener(n.ads.busy, function () { + e.callHandler("onFinish", { + type: n.ads.busy, + rewardAllowed: !1, + }); + }), + o.addEventListener(n.ads.completed, function (t) { + e.callHandler("onFinish", { + type: n.ads.completed, + rewardAllowed: !!t.rewardAllowed, + }); + }), + [n.ads.limit, n.ads.stopped, n.ads.error, n.ads.busy, n.ads.completed].forEach(function (t) { + o.addEventListener(t, function () { + e.playerSkin && e.playerSkin.hide(), (e.adReady = !1); + }); + }); + }), + (e.prototype.genericCriteria = function () { + var e = {}, + t = encodeURIComponent(_("tag") || ""), + i = encodeURIComponent(_("site_id") || ""), + n = encodeURIComponent(_("experiment") || ""), + r = encodeURIComponent(_("categories") || ""); + return (e.tag = t), (e.tag_site = t + "|" + i), (e.site_id = i), (e.experiment = n), (e.categories = r), this.programmaticAdsEnabled || (e.disable_programmatic = 1), e; + }), + (e.prototype.setVolume = function (e) { + this.ima && this.ima.setVolume(e); + }), + (e.GetIsPokiIFrame = function () { + return (parseInt(_("site_id"), 10) || 0) > 0; + }), + (e.childDirected = !1), + (e.nonPersonalized = !1), + (e.consentString = ""), + e + ); + })(); + const nt = it; + const rt = (function () { + function e() {} + return ( + (e.sendMessage = function (e, t) { + var i = window.parent; + if (!s(e, n.message)) { + var r = Object.keys(n.message).map(function (e) { + return "poki.message." + e; + }); + throw new TypeError("Argument 'type' must be one of " + r.join(", ")); + } + var a = t || {}; + tt.gameId && + tt.versionId && + (a.pokifordevs = { + game_id: tt.gameId, + game_version_id: tt.versionId, + }), + i.postMessage( + { + type: e, + content: a, + }, + "*" + ); + }), + e + ); + })(); + var at = function (e) { + var t = new Array(); + return ( + Object.keys(e).forEach(function (i) { + "object" == typeof e[i] ? (t = t.concat(at(e[i]))) : t.push(e[i]); + }), + t + ); + }; + var ot = function () { + return ( + (ot = + Object.assign || + function (e) { + for (var t, i = 1, n = arguments.length; i < n; i++) for (var r in (t = arguments[i])) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e; + }), + ot.apply(this, arguments) + ); + }, + st = at(n.tracking); + const dt = (function () { + function e() {} + return ( + (e.track = function (e, t) { + if ((void 0 === t && (t = {}), -1 === st.indexOf(e))) throw new TypeError("Invalid 'event', must be one of " + st.join(", ")); + if ("object" != typeof t) throw new TypeError("Invalid data, must be an object"); + var i = o.getDataAnnotations(); + if (null == i ? void 0 : i.vhbOnlyMode) + switch (e) { + case n.tracking.ads.status.impression: + z( + ot( + { + event: "video-impression", + size: "640x360v", + }, + i + ) + ); + break; + case n.tracking.ads.video.error: + z( + ot( + { + event: "video-error", + size: "640x360v", + }, + i + ) + ); + break; + case n.tracking.ads.video.loaderError: + z( + ot( + { + event: "video-adsloader-error", + size: "640x360v", + }, + i + ) + ); + break; + case n.tracking.ads.status.completed: + z( + ot( + { + event: "video-complete", + size: "640x360v", + }, + i + ) + ); + } + if (r.debug || r.log) { + if (window.process && window.process.env && "test" === window.process.env.NODE_ENV) return; + Object.keys(t).length ? console.info("%cPOKI_TRACKER: %cTracked event '" + e + "' with data:", "font-weight: bold", "", t) : console.info("%cPOKI_TRACKER: %cTracked event '" + e + "'", "font-weight: bold", ""); + } + rt.sendMessage(n.message.event, { + event: e, + data: t, + }); + }), + (e.setupDefaultEvents = function () { + var t, + i = + (((t = {})[n.ready] = n.tracking.sdk.status.initialized), + (t[n.adblocked] = console.log(n.tracking.sdk.status.failed)), + (t[n.ads.busy] = n.tracking.ads.status.busy), + (t[n.ads.completed] = n.tracking.ads.status.completed), + (t[n.ads.error] = console.log(n.tracking.ads.status.error)), + (t[n.ads.displayError] = console.log(n.tracking.ads.status.displayError)), + (t[n.ads.impression] = n.tracking.ads.status.impression), + (t[n.ads.limit] = n.tracking.ads.status.limit), + (t[n.ads.ready] = n.tracking.ads.status.ready), + (t[n.ads.requested] = n.tracking.ads.status.requested), + (t[n.ads.prebidRequested] = n.tracking.ads.status.prebidRequested), + (t[n.ads.skipped] = n.tracking.ads.status.skipped), + (t[n.ads.started] = n.tracking.ads.status.started), + (t[n.ads.video.clicked] = n.tracking.ads.video.clicked), + (t[n.ads.video.error] = n.tracking.ads.video.error), + (t[n.ads.video.loaderError] = n.tracking.ads.video.loaderError), + (t[n.ads.video.buffering] = n.tracking.ads.status.buffering), + (t[n.ads.video.progress] = n.tracking.ads.video.progress), + (t[n.ads.video.paused] = n.tracking.ads.video.paused), + (t[n.ads.video.resumed] = n.tracking.ads.video.resumed), + (t[n.tracking.screen.gameplayStart] = n.tracking.screen.gameplayStart), + (t[n.tracking.screen.gameplayStop] = n.tracking.screen.gameplayStop), + (t[n.tracking.screen.loadingProgress] = n.tracking.screen.loadingProgress), + (t[n.tracking.screen.commercialBreak] = n.tracking.screen.commercialBreak), + (t[n.tracking.screen.rewardedBreak] = n.tracking.screen.rewardedBreak), + (t[n.tracking.screen.happyTime] = n.tracking.screen.happyTime), + t); + Object.keys(i).forEach(function (t) { + o.addEventListener(t, function (n) { + e.track(i[t], n); + }); + }); + }), + e + ); + })(); + + function At(e) { + switch (Object.prototype.toString.call(e)) { + case "[object Error]": + case "[object Exception]": + case "[object DOMException]": + return !0; + default: + return e instanceof Error; + } + } + var ct = "poki_erruid", + lt = Date.now(), + pt = m(ct); + + function ut(e) { + if (tt.gameId && tt.versionId) { + if (!(Date.now() < lt)) { + pt || ((pt = Math.random().toString(36).substr(2, 9)), g(ct, pt)); + try { + var t = JSON.stringify({ + gid: tt.gameId, + vid: tt.versionId, + ve: 7, + n: e.name, + m: e.message, + s: JSON.stringify(e.stack), + ui: pt, + }), + i = "https://t.poki.io/ge"; + if (navigator.sendBeacon) navigator.sendBeacon(i, t); + else { + var n = new XMLHttpRequest(); + n.open("POST", i, !0), n.send(t); + } + lt = Date.now() + 100; + } catch (e) { + console.error(e); + } + } + } else console.log(e); + } + "undefined" != typeof window && + ((t().remoteFetching = !1), + t().report.subscribe(function (e) { + if ("Script error." === e.message && window.pokiLastCatch) { + var i = window.pokiLastCatch; + (window.pokiLastCatch = null), t().report(i); + } else ut(e); + }), + (window.onunhandledrejection = function (e) { + At(e.reason) + ? t().report(e.reason) + : ut({ + name: "unhandledrejection", + message: JSON.stringify(e.reason), + }); + })); + var ht = function () { + return ( + (ht = + Object.assign || + function (e) { + for (var t, i = 1, n = arguments.length; i < n; i++) for (var r in (t = arguments[i])) Object.prototype.hasOwnProperty.call(t, r) && (e[r] = t[r]); + return e; + }), + ht.apply(this, arguments) + ); + }, + mt = (function () { + function t() { + var t = this; + (this.gameStarted = !1), + (this.SDK = new nt()), + (this.gameplayStartCounter = 0), + (this.gameplayStopCounter = 0), + (this.duringGameplay = !1), + (this.init = function (e) { + return ( + void 0 === e && (e = {}), + new Promise(function (i, r) { + t.SDK.init( + ht( + { + onReady: function () { + if (_("preroll")) { + var e = t.SDK.adTimings.prerollPossible; + (t.SDK.adTimings.prerollPossible = function () { + return !0; + }), + t.commercialBreak(), + (t.SDK.adTimings.prerollPossible = e); + } + i(); + }, + onAdblocked: r, + }, + e + ) + ), + rt.sendMessage(n.message.sdkDetails, { + version: "2.234.2", + }); + }) + ); + }), + (this.initWithVideoHB = function () { + return t.init(); + }), + (this.gameLoadingProgress = function (e) { + var t = {}; + void 0 !== e.percentageDone && (t.percentageDone = Number(e.percentageDone)), + void 0 !== e.kbLoaded && (t.kbLoaded = Number(e.kbLoaded)), + void 0 !== e.kbTotal && (t.kbTotal = Number(e.kbTotal)), + void 0 !== e.fileNameLoaded && (t.fileNameLoaded = String(e.fileNameLoaded)), + void 0 !== e.filesLoaded && (t.filesLoaded = Number(e.filesLoaded)), + void 0 !== e.filesTotal && (t.filesTotal = Number(e.filesTotal)), + dt.track(n.tracking.screen.gameLoadingProgress, t); + }), + (this.gameLoadingStart = function () { + var e, t; + dt.track(n.tracking.screen.gameLoadingStarted, { + now: Math.round(null === (t = null === (e = window.performance) || void 0 === e ? void 0 : e.now) || void 0 === t ? void 0 : t.call(e)) || void 0, + }); + }), + (this.gameLoadingFinished = function () { + var e, t, i, r, a; + try { + (i = performance + .getEntriesByType("resource") + .map(function (e) { + return e.transferSize; + }) + .reduce(function (e, t) { + return e + t; + })), + (i += performance.getEntriesByType("navigation")[0].transferSize); + } catch (e) {} + dt.track(n.tracking.screen.gameLoadingFinished, { + transferSize: i, + trackers: ((r = window), (a = []), "function" != typeof r.ga, r.mixpanel && "function" == typeof r.mixpanel.track && a.push("mixpanel"), r.Countly && a.push("countly"), r.amplitude && a.push("amplitude"), a).join(","), + now: Math.round(null === (t = null === (e = window.performance) || void 0 === e ? void 0 : e.now) || void 0 === t ? void 0 : t.call(e)) || void 0, + }); + }), + (this.gameplayStart = function (e) { + t.gameplayStartCounter++, + (t.duringGameplay = !0), + t.gameStarted || ((t.gameStarted = !0), dt.track(n.tracking.screen.firstRound), t.SDK.startStartAdsAfterTimer()), + dt.track( + n.tracking.screen.gameplayStart, + ht(ht({}, e), { + playId: t.gameplayStartCounter, + }) + ); + }), + (this.gameInteractive = function () { + dt.track(n.tracking.screen.gameInteractive); + }), + (this.gameplayStop = function (e) { + t.gameplayStopCounter++, + (t.duringGameplay = !1), + dt.track( + n.tracking.screen.gameplayStop, + ht(ht({}, e), { + playId: t.gameplayStartCounter, + stopId: t.gameplayStopCounter, + }) + ); + }), + (this.roundStart = function (e) { + void 0 === e && (e = ""), + (e = String(e)), + dt.track(n.tracking.screen.roundStart, { + identifier: e, + }); + }), + (this.roundEnd = function (e) { + void 0 === e && (e = ""), + (e = String(e)), + dt.track(n.tracking.screen.roundEnd, { + identifier: e, + }); + }), + (this.customEvent = function (e, i, r) { + void 0 === r && (r = {}), + e && i + ? ((e = String(e)), + (i = String(i)), + (r = ht({}, r)), + dt.track(n.tracking.custom, { + eventNoun: e, + eventVerb: i, + eventData: r, + })) + : t.error("customEvent", "customEvent needs at least a noun and a verb"); + }), + (this.commercialBreak = function (e) { + return new Promise(function (i) { + var r = t.gameStarted ? n.ads.position.midroll : n.ads.position.preroll; + o.clearAnnotations(), + o.setDataAnnotations({ + opportunityId: Ke(), + position: r, + }), + dt.track(n.tracking.screen.commercialBreak), + t.SDK.requestAd({ + position: r, + onFinish: i, + onStart: e, + }); + }); + }), + (this.rewardedBreak = function (e) { + return new Promise(function (i) { + var r = n.ads.position.rewarded; + o.clearAnnotations(), + o.setDataAnnotations({ + opportunityId: Ke(), + position: r, + }), + dt.track(n.tracking.screen.rewardedBreak), + t.SDK.requestAd({ + position: r, + onFinish: function (e) { + e.length > 0 ? i(e[0].rewardAllowed) : i(!1); + }, + onStart: e, + }); + }); + }), + (this.happyTime = function (e) { + void 0 === e && (e = 1), + ((e = Number(e)) < 0 || e > 1) && ((e = Math.max(0, Math.min(1, e))), t.warning("happyTime", "Intensity should be a value between 0 and 1, adjusted to " + e)), + dt.track(n.tracking.screen.happyTime, { + intensity: e, + }); + }), + (this.muteAd = function () { + t.SDK.muteAd(); + }), + (this.setPlayerAge = function (e) { + e && t.SDK.setPlayerAge(e); + }), + (this.togglePlayerAdvertisingConsent = function (e) { + dt.track(n.tracking.togglePlayerAdvertisingConsent, { + didConsent: e, + }), + t.SDK.togglePlayerAdvertisingConsent(e), + rt.sendMessage(n.message.toggleProgrammaticAds, { + enabled: t.SDK.getProgrammaticAdsEnabled(), + }); + }), + (this.displayAd = function (e, i) { + o.clearAnnotations(); + var r = Ke(); + dt.track(n.tracking.screen.displayAd, { + size: i, + opportunityId: r, + duringGameplay: t.duringGameplay, + }), + t.SDK.displayAd(e, i, r, function () { + return t.duringGameplay; + }); + }), + (this.logError = function (t) { + At(t) + ? e.report(t) + : ut({ + name: "logError", + message: JSON.stringify(t), + }); + }), + (this.sendHighscore = function () {}), + (this.setDebugTouchOverlayController = function (e) { + t.SDK.debugTouchOverlayController = e; + }), + (this.getLeaderboard = function () { + return Promise.resolve([]); + }), + (this.getIsoLanguage = function () { + return _("iso_lang"); + }), + (this.warning = function (e, t) { + console.warn("PokiSDK." + e + ": " + t); + }), + (this.error = function (e, t) { + console.error("PokiSDK." + e + ": " + t); + }); + } + return ( + (t.prototype.setDebug = function (e) { + void 0 === e && (e = !0), this.SDK.setDebug(e); + }), + (t.prototype.disableProgrammatic = function () { + this.SDK.disableProgrammatic(); + }), + (t.prototype.toggleNonPersonalized = function (e) { + void 0 === e && (e = !1), this.SDK.toggleNonPersonalized(e); + }), + (t.prototype.setConsentString = function (e) { + this.SDK.setConsentString(e); + }), + (t.prototype.destroyAd = function (e) { + this.SDK.destroyAd(e); + }), + (t.prototype.setVolume = function (e) { + this.SDK.setVolume(e); + }), + t + ); + })(); + var gt = new mt(); + for (var vt in gt) window.PokiSDK[vt] = gt[vt]; + })(); +})(); \ No newline at end of file diff --git a/subway-surfers/poki-sdk.js b/subway-surfers/poki-sdk.js new file mode 100644 index 00000000..ac32c7e0 --- /dev/null +++ b/subway-surfers/poki-sdk.js @@ -0,0 +1,113 @@ +(() => { + var e = function (e) { + var n = RegExp("[?&]" + e + "=([^&]*)").exec(window.location.search); + return n && decodeURIComponent(n[1].replace(/\+/g, " ")); + }, + n = "kids" === e("tag"), + o = new ((function () { + function e() { + var e = this; + (this.queue = []), + (this.init = function (n) { + return ( + void 0 === n && (n = {}), + new Promise(function (o, t) { + e.enqueue("init", n, o, t); + }) + ); + }), + (this.rewardedBreak = function () { + return new Promise(function (e) { + e(!1); + }); + }), + (this.noArguments = function (n) { + return function () { + e.enqueue(n); + }; + }), + (this.oneArgument = function (n) { + return function (o) { + e.enqueue(n, o); + }; + }), + (this.handleAutoResolvePromise = function () { + return new Promise(function (e) { + e(); + }); + }), + (this.handleAutoResolvePromiseObj = function () { + return new Promise(function (e) { + e(); + }); + }), + (this.throwNotLoaded = function () { + console.debug("PokiSDK is not loaded yet. Not all methods are available."); + }); + } + return ( + (e.prototype.enqueue = function (e, o, t, i) { + var r = { fn: e, options: o, resolveFn: t, rejectFn: i }; + n ? i && i() : this.queue.push(r); + }), + (e.prototype.dequeue = function () { + for ( + var e = function () { + var e = n.queue.shift(), + o = e, + t = o.fn, + i = o.options; + "function" == typeof window.PokiSDK[t] + ? (null == e ? void 0 : e.resolveFn) || (null == e ? void 0 : e.rejectFn) + ? window.PokiSDK[t](i) + .then(function () { + for (var n = [], o = 0; o < arguments.length; o++) n[o] = arguments[o]; + "function" == typeof e.resolveFn && e.resolveFn.apply(e, n); + }) + .catch(function () { + for (var n = [], o = 0; o < arguments.length; o++) n[o] = arguments[o]; + "function" == typeof e.rejectFn && e.rejectFn.apply(e, n); + }) + : void 0 !== (null == e ? void 0 : e.fn) && window.PokiSDK[t](i) + : console.error("Cannot execute " + e.fn); + }, + n = this; + this.queue.length > 0; + + ) + e(); + }), + e + ); + })())(); + (window.PokiSDK = { init: o.init, initWithVideoHB: o.init, customEvent: o.throwNotLoaded, destroyAd: o.throwNotLoaded, getLeaderboard: o.handleAutoResolvePromiseObj }), + ["disableProgrammatic", "gameLoadingStart", "gameLoadingFinished", "gameInteractive", "roundStart", "roundEnd", "muteAd"].forEach(function (e) { + window.PokiSDK[e] = o.noArguments(e); + }), + [ + "setDebug", + "gameplayStart", + "gameplayStop", + "gameLoadingProgress", + "happyTime", + "setPlayerAge", + "togglePlayerAdvertisingConsent", + "toggleNonPersonalized", + "setConsentString", + "logError", + "sendHighscore", + "setDebugTouchOverlayController", + ].forEach(function (e) { + window.PokiSDK[e] = o.oneArgument(e); + }); + var t, + i = ((t = window.pokiSDKVersion) || (t = e("ab") || "v2.234.2"), "poki-sdk-" + (n ? "kids" : "core") + "-" + t + ".js"), + r = document.createElement("script"); + r.setAttribute("src", i), + r.setAttribute("type", "text/javascript"), + r.setAttribute("crossOrigin", "anonymous"), + (r.onload = function () { + return o.dequeue(); + }), + document.head.appendChild(r); +})(); diff --git a/subway-surfers/unity.js b/subway-surfers/unity.js new file mode 100644 index 00000000..4f768592 --- /dev/null +++ b/subway-surfers/unity.js @@ -0,0 +1,914 @@ +! function(n) { + function e(o) { + if (t[o]) return t[o].exports; + var i = t[o] = { + i: o, + l: !1, + exports: {} + }; + return n[o].call(i.exports, i, i.exports, e), i.l = !0, i.exports + } + var t = {}; + e.m = n, e.c = t, e.d = function(n, t, o) { + e.o(n, t) || Object.defineProperty(n, t, { + configurable: !1, + enumerable: !0, + get: o + }) + }, e.n = function(n) { + var t = n && n.__esModule ? function() { + return n.default + } : function() { + return n + }; + return e.d(t, "a", t), t + }, e.o = function(n, e) { + return Object.prototype.hasOwnProperty.call(n, e) + }, e.p = "", e(e.s = 2) +}([function(n, e, t) { + "use strict"; + var o = { + loader: "unity", + maxRatio: 16 / 9, + minRatio: 9 / 16, + thumbnail: "", + numScreenshots: 0, + commentChangeTime: 5e3, + spinnerRemoveDelay: 1e3, + fullImageMaxWidth: .6, + fullImageMaxHeight: .7, + smallImageSizeOfFullImage: .8, + animationTargetSizeOfSmallImage: .5, + transitionDuration: .5, + slideshowInterval: 5 + }; + window.config.title || console.error(new Error("No title on window.config")); + var i = Object.assign(o, window.config); + e.a = i +}, function(n, e, t) { + "use strict"; + + function o(n, e, t, o, i, r, a) { + try { + var s = n[r](a), + c = s.value + } catch (n) { + return void t(n) + } + s.done ? e(c) : Promise.resolve(c).then(o, i) + } + + function i(n) { + return function() { + var e = this, + t = arguments; + return new Promise(function(i, r) { + function a(n) { + o(c, i, r, a, s, "next", n) + } + + function s(n) { + o(c, i, r, a, s, "throw", n) + } + var c = n.apply(e, t); + a(void 0) + }) + } + } + + function r() { + return a.apply(this, arguments) + } + + function a() { + return a = i(x.a.mark(function n() { + var e, t, o, i, r, a, c; + return x.a.wrap(function(n) { + for (;;) switch (n.prev = n.next) { + case 0: + return m = document.getElementById("slideshow"), p = document.getElementById("slideshow-top"), g = document.getElementById("slideshow-nav"), w = document.getElementById("slideshow-images"), p.className = "active", n.prev = 5, n.next = 8, f("".concat(B, "screenshots/1-small.jpg").concat(I)); + case 8: + e = n.sent, n.next = 16; + break; + case 11: + return n.prev = 11, n.t0 = n.catch(5), n.next = 15, f("".concat(B, "screenshots/1.jpg").concat(I)); + case 15: + e = n.sent; + case 16: + for (t = h(), t.className = "".concat(L, " middle"), t.setAttribute("fullImageLoaded", !0), t.setAttribute("data-idx", 0), t.appendChild(e), w.appendChild(t), m.className = "active", v = e.width / e.height, y = document.createElement("style"), u(), document.body.appendChild(y), window.addEventListener("resize", u), o = 0; o <= S.a.numScreenshots - 1; o++) i = document.createElement("div"), i.className = "bullet".concat(0 === o ? " active" : ""), i.setAttribute("data-idx", o), g.appendChild(i); + return n.next = 31, f("".concat(B, "screenshots/1.jpg").concat(I)); + case 31: + for (r = n.sent, t.querySelector("img").src = r.src, a = function(n) { + var e = h(), + t = new Image; + t.src = "".concat(B, "screenshots/").concat(n + 1, "-small.jpg").concat(I), e.appendChild(t), e.setAttribute("data-idx", n), 1 === n ? e.className = "".concat(L, " right") : n === S.a.numScreenshots - 1 ? e.className = "".concat(L, " left") : e.className = "".concat(L, " inactive"), w.appendChild(e) + }, c = 1; c <= S.a.numScreenshots - 1; c++) a(c); + s(); + case 36: + case "end": + return n.stop() + } + }, n, null, [ + [5, 11] + ]) + })), a.apply(this, arguments) + } + + function s() { + return c.apply(this, arguments) + } + + function c() { + return c = i(x.a.mark(function n() { + var e, t, o, i, r, a; + return x.a.wrap(function(n) { + for (;;) switch (n.prev = n.next) { + case 0: + if (e = 1e3 * S.a.slideshowInterval, t = w.querySelector("#slideshow-images .right"), o = t.getAttribute("data-idx") << 0, t.getAttribute("fullImageLoaded")) { + n.next = 16; + break + } + return i = Date.now(), n.next = 7, f("".concat(B, "screenshots/").concat(o + 1, ".jpg").concat(I)); + case 7: + r = n.sent, t.querySelector("img").src = r.src, t.setAttribute("fullImageLoaded", !0), clearTimeout(window.slideShowMoveTransitionID), clearTimeout(window.slideShowTimeoutID), a = Date.now() - i, a > e ? l() : window.slideShowTimeoutID = window.setTimeout(l, e - a), n.next = 19; + break; + case 16: + clearTimeout(window.slideShowMoveTransitionID), clearTimeout(window.slideShowTimeoutID), window.slideShowTimeoutID = window.setTimeout(l, e); + case 19: + case "end": + return n.stop() + } + }, n) + })), c.apply(this, arguments) + } + + function l() { + if (!E) { + var n = k + 1; + n > S.a.numScreenshots - 1 && (n = 0), d(n) + } + } + + function d(n) { + k = n << 0; + var e = k > 0 ? k - 1 : S.a.numScreenshots - 1, + t = k < S.a.numScreenshots - 1 ? k + 1 : 0; + w.querySelectorAll(".image").forEach(function(n) { + n.className === "".concat(L, " left") && (n.className = "".concat(L, " fromLeft")), n.className === "".concat(L, " right") && (n.className = "".concat(L, " fromRight")), -1 === n.className.indexOf("inactive") && (n.className += " inactive") + }), w.querySelector('[data-idx="'.concat(k, '"]')).className = "".concat(L, " middle"), w.querySelector('[data-idx="'.concat(e, '"]')).className = "".concat(L, " left"), w.querySelector('[data-idx="'.concat(t, '"]')).className = "".concat(L, " right"), g.querySelectorAll(".bullet").forEach(function(n, e) { + n.className = "bullet", e === k && (n.className += " active") + }), window.slideShowMoveTransitionID = window.setTimeout(function() { + w.querySelectorAll(".inactive").forEach(function(n) { + n.className = "".concat(L, " inactive fromRight") + }) + }, 1e3 * S.a.transitionDuration), s() + } + + function u() { + var n = window.innerWidth / window.innerHeight, + e = S.a.fullImageMaxWidth / v * n, + t = S.a.fullImageMaxWidth; + e > S.a.fullImageMaxHeight && (e = S.a.fullImageMaxHeight, t = e * v / n); + var o = t * S.a.smallImageSizeOfFullImage, + i = .5 - t / 2, + r = t * S.a.animationTargetSizeOfSmallImage, + a = -2 * r, + s = 1 + r, + c = (1 - t) / 4 - t / 2, + l = .5 - .5 * t - (o + t) / 2, + d = 1 - (1 - t) / 4 - t / 2, + u = .5 + .5 * o, + f = Math.min(c, l), + h = Math.max(d, u); + y.innerHTML = "" + } + + function f(n) { + return new Promise(function(e, t) { + var o = new Image; + o.addEventListener("load", function() { + return e(o) + }), o.addEventListener("error", function(n) { + o.src.indexOf(".jpg") > 0 ? o.src = o.src.replace(".jpg", ".png") : t(n) + }), o.src = n + }) + } + + function h() { + var n = document.createElement("div"); + return n.className = L, n + } + e.a = r; + var m, p, g, w, v, y, b = t(10), + x = t.n(b), + S = t(0), + L = "image", + k = 0, + E = !1, + I = S.a.screenshotsVersion ? "?v".concat(S.a.screenshotsVersion) : "", + C = window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/")), + T = window.location.hostname.endsWith("game-cdn.poki.com") || window.location.hostname.endsWith(".poki-gdn.com"), + B = T ? "/cdn-cgi/image/f=auto,quality=78".concat(C, "/") : ""; + window.navigateNext = l, window.removeSlideshowEventListeners = function() { + E = !0 + } +}, function(n, e, t) { + n.exports = t(3) +}, function(n, e, t) { + "use strict"; + + function o(n) { + var e = document.createElement("div"); + return e.id = n, e + } + Object.defineProperty(e, "__esModule", { + value: !0 + }); + var i = t(4), + r = (t.n(i), t(0)), + a = (t(9), t(1), t(11)), + s = (t.n(a), o("loader")), + c = o("slideshow"), + l = o("slideshow-top"), + d = document.createElement("img"); + var u = o("slideshow-top-container"), + f = o("game-title"); + f.innerText = r.a.title; + var h = o("progress-spinner"); + h.innerHTML = '
', h.setAttribute("class", "spinner"); + var m = o("progress-container"), + p = o("progress-bar"), + g = o("progress-fill"); + g.style.width = "0%"; + var w = o("progress-amount"); + w.innerText = "0%"; + var v = o("progress-comment"); + v.innerText = "Loading"; + var y = o("slideshow-images"), + b = o("slideshow-nav"), + x = o("game-container"), + S = o("game"); + s.appendChild(c), c.appendChild(l), c.appendChild(y), c.appendChild(b), l.appendChild(d), l.appendChild(u), u.appendChild(f), u.appendChild(h), u.appendChild(m), m.appendChild(p), m.appendChild(w), u.appendChild(v), p.appendChild(g), x.appendChild(S), document.body.appendChild(s), document.body.appendChild(x) +}, function(n, e, t) { + var o = t(5); + "string" == typeof o && (o = [ + [n.i, o, ""] + ]); + var i = { + hmr: !0 + }; + i.transform = void 0, i.insertInto = void 0; + t(7)(o, i); + o.locals && (n.exports = o.locals) +}, function(n, e, t) { + e = n.exports = t(6)(!1), e.push([n.i, "* {\n margin: 0;\n padding: 0;\n}\n\nhtml,\nbody {\n width: 100vw;\n height: 100vh;\n overflow: hidden;\n background: #002B50;\n font-family: Torus, Arial, Helvetica, sans-serif;\n color: #fff;\n}\n\n#game-container {\n position: absolute !important;\n left: 50%;\n top: 50%;\n display: none;\n}\n\n#game,\n#game canvas {\n width: 100%;\n height: 100%;\n}\n\n#loader {\n width: 100%;\n height: 100%;\n}\n\n/**\n * Slideshow\n */\n\n#slideshow {\n width: 100%;\n height: 100%;\n flex-direction: column;\n align-items: center;\n justify-content: space-evenly;\n display: flex;\n user-select: none;\n}\n\n@font-face {\n font-family: Torus;\n src:\n url('./fonts/torus-bold-webfont.woff2') format('woff2'),\n url('//a.poki.com/fonts/torus-bold-webfont.woff') format('woff');\n font-style: bold;\n font-weight: 700;\n}\n\n/**\n * Slideshow - Top section\n */\n#progress-spinner{\n margin-left: 0;\n margin-top: 0;\n left: 0px;\n display:none;\n transform: translate(100%, -50%);\n width:10vh;\n}\n#progress-spinner >div{\n width:2vh;\n height:2vh;\n}\n\n\n#slideshow-top {\n display: flex;\n margin: 2.5vh 0;\n}\n\n#slideshow-top-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n flex-grow: 1;\n}\n\n#game-title, #progress-comment {\n display: flex;\n flex-grow: 1;\n align-items: center;\n font-size:2vh;\n}\n\n#progress-container {\n display: flex;\n align-items: center;\n flex-grow: 1;\n transition: 0.2s ease-out all;\n}\n\n#progress-container.done {\n opacity: 0;\n}\n\n#progress-bar {\n background: #fff;\n width: 100%;\n overflow: hidden;\n}\n\n#progress-fill {\n background: #3CF7DC;\n height: 100%;\n transition: 0.2s ease-out all;\n animation-name: fillColor;\n animation-duration: 3.5s;\n animation-iteration-count: infinite;\n animation-fill-mode: both;\n}\n\n@keyframes fillColor {\n 0% {\n background-color: #3CF7DC;\n }\n\n 25% {\n background-color: #FFA9BE;\n }\n\n 50% {\n background-color: #FFDC00;\n }\n\n 75% {\n background-color: #E0AEF5;\n }\n\n 100% {\n background-color: #3CF7DC;\n }\n}\n\n@media (orientation: portrait) {\n \n\n #game-title h1 {\n font-size: 2vh;\n }\n\n #slideshow-top {\n width: 70vw;\n }\n\n #progress-bar {\n height: 1vh;\n border-radius: 0.5vh;\n }\n\n #progress-fill {\n border-radius: 0.5vh;\n }\n\n \n\n #progress-amount {\n font-size: 2vh;\n margin-left: 1.5vh;\n width: 3vh;\n }\n}\n\n@media (orientation: landscape) {\n \n\n #game-title h1 {\n font-size: 3vh;\n }\n\n #slideshow-top {\n width: 50vw;\n }\n\n #progress-bar {\n height: 1.2vh;\n border-radius: 0.6vh;\n }\n\n #progress-fill {\n border-radius: 0.6vh;\n }\n\n \n\n #progress-amount {\n font-size: 2.5vh;\n margin-left: 1.875vh;\n width: 3.75vh;\n }\n}\n\n/**\n * Slideshow - Images section\n */\n\n#slideshow-images {\n width: 100vw;\n display: flex;\n justify-content: center;\n}\n\n#slideshow-images .image {\n position: absolute;\n box-shadow: 0 2.4vh 3.6vh rgba(0, 0, 0, 0.4);\n transition-property: transform;\n transition-timing-function: ease-in-out;\n perspective: 1000px;\n left: 0;\n overflow: hidden;\n /* border: 1vh solid; */\n}\n\n#slideshow-images .image img {\n width: 100%;\n height: 100%;\n}\n\n#slideshow-images .image:nth-of-type(1n) {\n border-color: #3BE8B0;\n}\n\n#slideshow-images .image:nth-of-type(2n) {\n border-color: #FF6D92;\n}\n\n#slideshow-images .image:nth-of-type(3n) {\n border-color: #A177FF;\n}\n\n#slideshow-images .image:nth-of-type(4n) {\n border-color: #FFD200;\n}\n\n#slideshow-images .left {\n z-index: 2;\n}\n#slideshow-images .right {\n z-index: 1;\n}\n\n#slideshow-images .middle {\n z-index: 3;\n}\n\n#slideshow-images .left img,\n#slideshow-images .right img {\n transform: scale(1.05);\n}\n\n#slideshow-images .left img,\n#slideshow-images .right img,\n#slideshow-images .fromLeft img,\n#slideshow-images .fromRight img {\n filter: blur(1vh);\n}\n\n#slideshow-images .inactive {\n display: none;\n}\n\n#slideshow-images .inactive.fromLeft,\n#slideshow-images .inactive.fromRight {\n display: block;\n}\n\n/**\n * Slideshow - Navigation section\n */\n\n#slideshow-nav {\n display: flex;\n justify-content: center;\n margin: 2.5vh 0;\n}\n\n#slideshow-nav .bullet {\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n#slideshow-nav .bullet:after {\n content: '';\n background: #fff;\n border-radius: 0.4vh;\n width: 0.8vh;\n height: 0.8vh;\n}\n\n#slideshow-nav .bullet.active:after {\n background: #009CFF;\n}\n\n#slideshow-nav .bullet {\n width: 2.5vh;\n height: 2.5vh;\n}\n\n#slideshow-nav .bullet:after {\n border-radius: 50%;\n width: 50%;\n height: 50%;\n}\n\n/**\n * Pop-in animation\n */\n\n#slideshow-nav,\n#slideshow-images {\n opacity: 0;\n transition: 0.4s all ease-out;\n transform: translateY(2vh);\n perspective: 1000px;\n transition-delay: 400ms;\n}\n\n#slideshow-nav {\n transition-delay: 600ms;\n}\n\n#slideshow.active #slideshow-images,\n#slideshow.active #slideshow-nav {\n opacity: 1;\n transform: translateY(0);\n}\n\n@keyframes bounceInDown {\n\n from,\n 60%,\n 75%,\n 90%,\n to {\n animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);\n }\n\n 0% {\n transform: translate3d(0, -100vh, 0);\n }\n\n 40% {\n transform: translate3d(0, 0.5vh, 0);\n }\n\n 65% {\n transform: translate3d(0, -0.2vh, 0);\n }\n\n 80% {\n transform: translate3d(0, 0.1vh, 0);\n }\n\n to {\n transform: translate3d(0, 0, 0);\n }\n}\n\n#slideshow-top {\n transform: translate3d(0, -20vh, 0);\n opacity: 0;\n}\n\n#slideshow-top.active {\n opacity: 1;\n transform: translate3d(0, 0, 0);\n animation-name: bounceInDown;\n animation-duration: 0.5s;\n}\n\n/**\n * Loading dots\n */\n.spinner {\n position: relative;\n left: -9999px;\n animation: dot-pulse 1.5s infinite linear;\n animation-delay: .25s;\n transform:translate(50vw, 50vh) translate(-130%, -130%);\n }\n\n .spinner:before, .spinner:after {\n content: '';\n display: inline-block;\n position: absolute;\n top: 0;\n width: 10px;\n height: 10px;\n border-radius: 5px;\n }\n .spinner, .spinner:before, .spinner:after{\n width: 10px;\n height: 10px;\n border-radius: 5px;\n }\n\n .spinner:before {\n animation: dot-pulse 1.5s infinite linear;\n animation-delay: 0s;\n left:-20px;\n }\n\n .spinner:after {\n animation: dot-pulse 1.5s infinite linear;\n animation-delay: .5s;\n left:20px;\n }\n\n @keyframes dot-pulse {\n 0% {\n box-shadow: 9999px 0 0 -5px #FFF;\n }\n 30% {\n box-shadow: 9999px 0 0 2px #FFF;\n }\n 60%,\n 100% {\n box-shadow: 9999px 0 0 -5px #FFF;\n }\n }\n", ""]) +}, function(n, e, t) { + "use strict"; + + function o(n, e) { + var t = n[1] || "", + o = n[3]; + if (!o) return t; + if (e && "function" == typeof btoa) { + var r = i(o); + return [t].concat(o.sources.map(function(n) { + return "/*# sourceURL=" + o.sourceRoot + n + " */" + })).concat([r]).join("\n") + } + return [t].join("\n") + } + + function i(n) { + return "/*# sourceMappingURL=data:application/json;charset=utf-8;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(n)))) + " */" + } + n.exports = function(n) { + var e = []; + return e.toString = function() { + return this.map(function(e) { + var t = o(e, n); + return e[2] ? "@media " + e[2] + "{" + t + "}" : t + }).join("") + }, e.i = function(n, t) { + "string" == typeof n && (n = [ + [null, n, ""] + ]); + for (var o = {}, i = 0; i < this.length; i++) { + var r = this[i][0]; + null != r && (o[r] = !0) + } + for (i = 0; i < n.length; i++) { + var a = n[i]; + null != a[0] && o[a[0]] || (t && !a[2] ? a[2] = t : t && (a[2] = "(" + a[2] + ") and (" + t + ")"), e.push(a)) + } + }, e + } +}, function(n, e, t) { + function o(n, e) { + for (var t = 0; t < n.length; t++) { + var o = n[t], + i = p[o.id]; + if (i) { + i.refs++; + for (var r = 0; r < i.parts.length; r++) i.parts[r](o.parts[r]); + for (; r < o.parts.length; r++) i.parts.push(u(o.parts[r], e)) + } else { + for (var a = [], r = 0; r < o.parts.length; r++) a.push(u(o.parts[r], e)); + p[o.id] = { + id: o.id, + refs: 1, + parts: a + } + } + } + } + + function i(n, e) { + for (var t = [], o = {}, i = 0; i < n.length; i++) { + var r = n[i], + a = e.base ? r[0] + e.base : r[0], + s = r[1], + c = r[2], + l = r[3], + d = { + css: s, + media: c, + sourceMap: l + }; + o[a] ? o[a].parts.push(d) : t.push(o[a] = { + id: a, + parts: [d] + }) + } + return t + } + + function r(n, e) { + var t = v(n.insertInto); + if (!t) throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid."); + var o = x[x.length - 1]; + if ("top" === n.insertAt) o ? o.nextSibling ? t.insertBefore(e, o.nextSibling) : t.appendChild(e) : t.insertBefore(e, t.firstChild), x.push(e); + else if ("bottom" === n.insertAt) t.appendChild(e); + else { + if ("object" != typeof n.insertAt || !n.insertAt.before) throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n"); + var i = v(n.insertAt.before, t); + t.insertBefore(e, i) + } + } + + function a(n) { + if (null === n.parentNode) return !1; + n.parentNode.removeChild(n); + var e = x.indexOf(n); + e >= 0 && x.splice(e, 1) + } + + function s(n) { + var e = document.createElement("style"); + if (void 0 === n.attrs.type && (n.attrs.type = "text/css"), void 0 === n.attrs.nonce) { + var t = d(); + t && (n.attrs.nonce = t) + } + return l(e, n.attrs), r(n, e), e + } + + function c(n) { + var e = document.createElement("link"); + return void 0 === n.attrs.type && (n.attrs.type = "text/css"), n.attrs.rel = "stylesheet", l(e, n.attrs), r(n, e), e + } + + function l(n, e) { + Object.keys(e).forEach(function(t) { + n.setAttribute(t, e[t]) + }) + } + + function d() { + return t.nc + } + + function u(n, e) { + var t, o, i, r; + if (e.transform && n.css) { + if (!(r = "function" == typeof e.transform ? e.transform(n.css) : e.transform.default(n.css))) return function() {}; + n.css = r + } + if (e.singleton) { + var l = b++; + t = y || (y = s(e)), o = f.bind(null, t, l, !1), i = f.bind(null, t, l, !0) + } else n.sourceMap && "function" == typeof URL && "function" == typeof URL.createObjectURL && "function" == typeof URL.revokeObjectURL && "function" == typeof Blob && "function" == typeof btoa ? (t = c(e), o = m.bind(null, t, e), i = function() { + a(t), t.href && URL.revokeObjectURL(t.href) + }) : (t = s(e), o = h.bind(null, t), i = function() { + a(t) + }); + return o(n), + function(e) { + if (e) { + if (e.css === n.css && e.media === n.media && e.sourceMap === n.sourceMap) return; + o(n = e) + } else i() + } + } + + function f(n, e, t, o) { + var i = t ? "" : o.css; + if (n.styleSheet) n.styleSheet.cssText = L(e, i); + else { + var r = document.createTextNode(i), + a = n.childNodes; + a[e] && n.removeChild(a[e]), a.length ? n.insertBefore(r, a[e]) : n.appendChild(r) + } + } + + function h(n, e) { + var t = e.css, + o = e.media; + if (o && n.setAttribute("media", o), n.styleSheet) n.styleSheet.cssText = t; + else { + for (; n.firstChild;) n.removeChild(n.firstChild); + n.appendChild(document.createTextNode(t)) + } + } + + function m(n, e, t) { + var o = t.css, + i = t.sourceMap, + r = void 0 === e.convertToAbsoluteUrls && i; + (e.convertToAbsoluteUrls || r) && (o = S(o)), i && (o += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(i)))) + " */"); + var a = new Blob([o], { + type: "text/css" + }), + s = n.href; + n.href = URL.createObjectURL(a), s && URL.revokeObjectURL(s) + } + var p = {}, + g = function(n) { + var e; + return function() { + return void 0 === e && (e = n.apply(this, arguments)), e + } + }(function() { + return window && document && document.all && !window.atob + }), + w = function(n, e) { + return e ? e.querySelector(n) : document.querySelector(n) + }, + v = function(n) { + var e = {}; + return function(n, t) { + if ("function" == typeof n) return n(); + if (void 0 === e[n]) { + var o = w.call(this, n, t); + if (window.HTMLIFrameElement && o instanceof window.HTMLIFrameElement) try { + o = o.contentDocument.head + } catch (n) { + o = null + } + e[n] = o + } + return e[n] + } + }(), + y = null, + b = 0, + x = [], + S = t(8); + n.exports = function(n, e) { + if ("undefined" != typeof DEBUG && DEBUG && "object" != typeof document) throw new Error("The style-loader cannot be used in a non-browser environment"); + e = e || {}, e.attrs = "object" == typeof e.attrs ? e.attrs : {}, e.singleton || "boolean" == typeof e.singleton || (e.singleton = g()), e.insertInto || (e.insertInto = "head"), e.insertAt || (e.insertAt = "bottom"); + var t = i(n, e); + return o(t, e), + function(n) { + for (var r = [], a = 0; a < t.length; a++) { + var s = t[a], + c = p[s.id]; + c.refs--, r.push(c) + } + if (n) { + o(i(n, e), e) + } + for (var a = 0; a < r.length; a++) { + var c = r[a]; + if (0 === c.refs) { + for (var l = 0; l < c.parts.length; l++) c.parts[l](); + delete p[c.id] + } + } + } + }; + var L = function() { + var n = []; + return function(e, t) { + return n[e] = t, n.filter(Boolean).join("\n") + } + }() +}, function(n, e) { + n.exports = function(n) { + var e = "undefined" != typeof window && window.location; + if (!e) throw new Error("fixUrls requires window.location"); + if (!n || "string" != typeof n) return n; + var t = e.protocol + "//" + e.host, + o = t + e.pathname.replace(/\/[^\/]*$/, "/"); + return n.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(n, e) { + var i = e.trim().replace(/^"(.*)"$/, function(n, e) { + return e + }).replace(/^'(.*)'$/, function(n, e) { + return e + }); + if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(i)) return n; + var r; + return r = 0 === i.indexOf("//") ? i : 0 === i.indexOf("/") ? t + i : o + i.replace(/^\.\//, ""), "url(" + JSON.stringify(r) + ")" + }) + } +}, function(n, e, t) { + "use strict"; + + function o() { + var n = window.innerWidth, + e = window.innerHeight, + t = n / e; + d.style.width = "".concat(n, "px"), d.style.height = "".concat(e, "px"), t > w.a.maxRatio ? d.style.width = "".concat(e * w.a.maxRatio, "px") : t < w.a.minRatio && (d.style.height = "".concat(n / w.a.minRatio, "px")); + var o = d.getBoundingClientRect(); + d.style.marginLeft = "".concat(-.5 * o.width, "px"), d.style.marginTop = "".concat(-.5 * o.height, "px") + } + + function i() { + d = document.getElementById("game-container"), u = document.getElementById("loader"), f = document.getElementById("progress-container"), h = document.getElementById("progress-fill"), m = document.getElementById("progress-amount"), p = document.getElementById("progress-comment"), window.addEventListener("resize", o), window.addEventListener("focus", o), window.PokiSDK.init().then(function() { + window.pokiBridge ? window.unityGame.SendMessage(window.pokiBridge, "ready") : window.pokiReady = !0 + }).catch(function() { + window.pokiBridge ? window.unityGame.SendMessage(window.pokiBridge, "adblock") : window.pokiAdBlock = !0, console.info("AdBlocker active") + }), window.PokiSDK.setDebug(w.a && w.a.debug) + } + + function r() { + d.style.display = "block", u.style.display = "none", o(), PokiSDK.gameLoadingFinished(), window.removeSlideshowEventListeners(), g && clearTimeout(g) + } + + function a(n, e) { + if (n.Module) { + var t = 100 * e; + h.style.width = "".concat(t, "%"), m.innerHTML = "".concat(t << 0, "%"), w.a.fileSize && (m.innerHTML += " of ".concat(w.a.fileSize, "MB")); + var o = { + percentageDone: t + }; + PokiSDK.gameLoadingProgress(o), e >= 1 && "done" !== f.className && (f.className = "done", document.getElementById("progress-comment").innerHTML = "Preparing game...", document.getElementById("progress-spinner").style.display = "flex", g && clearTimeout(g)) + } + } + + function s() { + var n = w.a.loadingComments || ["Loading..."]; + n ? (p.innerHTML = n[y], y++, y >= n.length && (y = 0), g = setTimeout(s, w.a.commentChangeTime)) : p.innerHTML = "" + } + + function c() { + ! function() { + var n = document.createElement("script"); + n.src = w.a.unityWebglLoaderUrl, n.addEventListener("load", function() { + window.unityGame = window.UnityLoader.instantiate("game", w.a.unityWebglBuildUrl, { + onProgress: a, + Module: { + onRuntimeInitialized: r + } + }) + }), document.body.appendChild(n) + }(), PokiSDK.gameLoadingStart(), w.a.fileSize && (m.innerHTML += " of ".concat(w.a.fileSize, "MB"), m.style.width = "12vh", m.style.whiteSpace = "nowrap"), s() + } + + function l() { + window.setTimeout(function() { + var n = document.getElementById("spinner"); + n && n.parentNode && n.parentNode.removeChild(n) + }, w.a.spinnerRemoveDelay) + } + var d, u, f, h, m, p, g, w = t(0), + v = t(1), + y = 0; + ! function() { + var n = document.createElement("div"); + n.setAttribute("id", "spinner"), n.className = "spinner", document.body.appendChild(n) + }(), window.onload = function() { + i(); + try { + Object(v.a)().then(function() { + l() + }) + } catch (n) { + console.info("Slideshow loading error", n), l() + } + c() + } +}, function(n, e, t) { + var o = function(n) { + "use strict"; + + function e(n, e, t, i) { + var r = e && e.prototype instanceof o ? e : o, + a = Object.create(r.prototype), + s = new f(i || []); + return a._invoke = c(n, t, s), a + } + + function t(n, e, t) { + try { + return { + type: "normal", + arg: n.call(e, t) + } + } catch (n) { + return { + type: "throw", + arg: n + } + } + } + + function o() {} + + function i() {} + + function r() {} + + function a(n) { + ["next", "throw", "return"].forEach(function(e) { + n[e] = function(n) { + return this._invoke(e, n) + } + }) + } + + function s(n) { + function e(o, i, r, a) { + var s = t(n[o], n, i); + if ("throw" !== s.type) { + var c = s.arg, + l = c.value; + return l && "object" == typeof l && w.call(l, "__await") ? Promise.resolve(l.__await).then(function(n) { + e("next", n, r, a) + }, function(n) { + e("throw", n, r, a) + }) : Promise.resolve(l).then(function(n) { + c.value = n, r(c) + }, function(n) { + return e("throw", n, r, a) + }) + } + a(s.arg) + } + + function o(n, t) { + function o() { + return new Promise(function(o, i) { + e(n, t, o, i) + }) + } + return i = i ? i.then(o, o) : o() + } + var i; + this._invoke = o + } + + function c(n, e, o) { + var i = S; + return function(r, a) { + if (i === k) throw new Error("Generator is already running"); + if (i === E) { + if ("throw" === r) throw a; + return m() + } + for (o.method = r, o.arg = a;;) { + var s = o.delegate; + if (s) { + var c = l(s, o); + if (c) { + if (c === I) continue; + return c + } + } + if ("next" === o.method) o.sent = o._sent = o.arg; + else if ("throw" === o.method) { + if (i === S) throw i = E, o.arg; + o.dispatchException(o.arg) + } else "return" === o.method && o.abrupt("return", o.arg); + i = k; + var d = t(n, e, o); + if ("normal" === d.type) { + if (i = o.done ? E : L, d.arg === I) continue; + return { + value: d.arg, + done: o.done + } + } + "throw" === d.type && (i = E, o.method = "throw", o.arg = d.arg) + } + } + } + + function l(n, e) { + var o = n.iterator[e.method]; + if (o === p) { + if (e.delegate = null, "throw" === e.method) { + if (n.iterator.return && (e.method = "return", e.arg = p, l(n, e), "throw" === e.method)) return I; + e.method = "throw", e.arg = new TypeError("The iterator does not provide a 'throw' method") + } + return I + } + var i = t(o, n.iterator, e.arg); + if ("throw" === i.type) return e.method = "throw", e.arg = i.arg, e.delegate = null, I; + var r = i.arg; + return r ? r.done ? (e[n.resultName] = r.value, e.next = n.nextLoc, "return" !== e.method && (e.method = "next", e.arg = p), e.delegate = null, I) : r : (e.method = "throw", e.arg = new TypeError("iterator result is not an object"), e.delegate = null, I) + } + + function d(n) { + var e = { + tryLoc: n[0] + }; + 1 in n && (e.catchLoc = n[1]), 2 in n && (e.finallyLoc = n[2], e.afterLoc = n[3]), this.tryEntries.push(e) + } + + function u(n) { + var e = n.completion || {}; + e.type = "normal", delete e.arg, n.completion = e + } + + function f(n) { + this.tryEntries = [{ + tryLoc: "root" + }], n.forEach(d, this), this.reset(!0) + } + + function h(n) { + if (n) { + var e = n[y]; + if (e) return e.call(n); + if ("function" == typeof n.next) return n; + if (!isNaN(n.length)) { + var t = -1, + o = function e() { + for (; ++t < n.length;) + if (w.call(n, t)) return e.value = n[t], e.done = !1, e; + return e.value = p, e.done = !0, e + }; + return o.next = o + } + } + return { + next: m + } + } + + function m() { + return { + value: p, + done: !0 + } + } + var p, g = Object.prototype, + w = g.hasOwnProperty, + v = "function" == typeof Symbol ? Symbol : {}, + y = v.iterator || "@@iterator", + b = v.asyncIterator || "@@asyncIterator", + x = v.toStringTag || "@@toStringTag"; + n.wrap = e; + var S = "suspendedStart", + L = "suspendedYield", + k = "executing", + E = "completed", + I = {}, + C = {}; + C[y] = function() { + return this + }; + var T = Object.getPrototypeOf, + B = T && T(T(h([]))); + B && B !== g && w.call(B, y) && (C = B); + var j = r.prototype = o.prototype = Object.create(C); + return i.prototype = j.constructor = r, r.constructor = i, r[x] = i.displayName = "GeneratorFunction", n.isGeneratorFunction = function(n) { + var e = "function" == typeof n && n.constructor; + return !!e && (e === i || "GeneratorFunction" === (e.displayName || e.name)) + }, n.mark = function(n) { + return Object.setPrototypeOf ? Object.setPrototypeOf(n, r) : (n.__proto__ = r, x in n || (n[x] = "GeneratorFunction")), n.prototype = Object.create(j), n + }, n.awrap = function(n) { + return { + __await: n + } + }, a(s.prototype), s.prototype[b] = function() { + return this + }, n.AsyncIterator = s, n.async = function(t, o, i, r) { + var a = new s(e(t, o, i, r)); + return n.isGeneratorFunction(o) ? a : a.next().then(function(n) { + return n.done ? n.value : a.next() + }) + }, a(j), j[x] = "Generator", j[y] = function() { + return this + }, j.toString = function() { + return "[object Generator]" + }, n.keys = function(n) { + var e = []; + for (var t in n) e.push(t); + return e.reverse(), + function t() { + for (; e.length;) { + var o = e.pop(); + if (o in n) return t.value = o, t.done = !1, t + } + return t.done = !0, t + } + }, n.values = h, f.prototype = { + constructor: f, + reset: function(n) { + if (this.prev = 0, this.next = 0, this.sent = this._sent = p, this.done = !1, this.delegate = null, this.method = "next", this.arg = p, this.tryEntries.forEach(u), !n) + for (var e in this) "t" === e.charAt(0) && w.call(this, e) && !isNaN(+e.slice(1)) && (this[e] = p) + }, + stop: function() { + this.done = !0; + var n = this.tryEntries[0], + e = n.completion; + if ("throw" === e.type) throw e.arg; + return this.rval + }, + dispatchException: function(n) { + function e(e, o) { + return r.type = "throw", r.arg = n, t.next = e, o && (t.method = "next", t.arg = p), !!o + } + if (this.done) throw n; + for (var t = this, o = this.tryEntries.length - 1; o >= 0; --o) { + var i = this.tryEntries[o], + r = i.completion; + if ("root" === i.tryLoc) return e("end"); + if (i.tryLoc <= this.prev) { + var a = w.call(i, "catchLoc"), + s = w.call(i, "finallyLoc"); + if (a && s) { + if (this.prev < i.catchLoc) return e(i.catchLoc, !0); + if (this.prev < i.finallyLoc) return e(i.finallyLoc) + } else if (a) { + if (this.prev < i.catchLoc) return e(i.catchLoc, !0) + } else { + if (!s) throw new Error("try statement without catch or finally"); + if (this.prev < i.finallyLoc) return e(i.finallyLoc) + } + } + } + }, + abrupt: function(n, e) { + for (var t = this.tryEntries.length - 1; t >= 0; --t) { + var o = this.tryEntries[t]; + if (o.tryLoc <= this.prev && w.call(o, "finallyLoc") && this.prev < o.finallyLoc) { + var i = o; + break + } + } + i && ("break" === n || "continue" === n) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); + var r = i ? i.completion : {}; + return r.type = n, r.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, I) : this.complete(r) + }, + complete: function(n, e) { + if ("throw" === n.type) throw n.arg; + return "break" === n.type || "continue" === n.type ? this.next = n.arg : "return" === n.type ? (this.rval = this.arg = n.arg, this.method = "return", this.next = "end") : "normal" === n.type && e && (this.next = e), I + }, + finish: function(n) { + for (var e = this.tryEntries.length - 1; e >= 0; --e) { + var t = this.tryEntries[e]; + if (t.finallyLoc === n) return this.complete(t.completion, t.afterLoc), u(t), I + } + }, + catch: function(n) { + for (var e = this.tryEntries.length - 1; e >= 0; --e) { + var t = this.tryEntries[e]; + if (t.tryLoc === n) { + var o = t.completion; + if ("throw" === o.type) { + var i = o.arg; + u(t) + } + return i + } + } + throw new Error("illegal catch attempt") + }, + delegateYield: function(n, e, t) { + return this.delegate = { + iterator: h(n), + resultName: e, + nextLoc: t + }, "next" === this.method && (this.arg = p), I + } + }, n + }(n.exports); + try { + regeneratorRuntime = o + } catch (n) { + Function("r", "regeneratorRuntime = r")(o) + } +}, function(n, e) { + window.initPokiBridge = function(n) { + window.pokiReady || window.pokiAdBlock ? window.pokiReady ? window.unityGame.SendMessage(n, "ready") : window.pokiAdBlock && window.unityGame.SendMessage(n, "adblock") : window.pokiBridge = n, window.commercialBreak = function() { + PokiSDK.commercialBreak().then(function() { + window.unityGame.SendMessage(n, "commercialBreakCompleted") + }) + }, window.rewardedBreak = function() { + PokiSDK.rewardedBreak().then(function(e) { + window.unityGame.SendMessage(n, "rewardedBreakCompleted", e.toString()) + }) + } + } +}]); \ No newline at end of file