├── .codeclimate.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── autoPlay.user.js ├── autoplay.noUpdate.user.js ├── leaderboard.user.js ├── tuningData.json ├── update_version.sh └── version.json /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | JavaScript: true 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = crlf 5 | insert_final_newline = true 6 | 7 | [*.js] 8 | indent_style = tab 9 | indent_size = 4 10 | charset = utf-8 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### SublimeText 2 | # cache files for sublime text 3 | *.tmlanguage.cache 4 | *.tmPreferences.cache 5 | *.stTheme.cache 6 | # workspace files are user-specific 7 | *.sublime-workspace 8 | # project files should be checked into the repository, unless a significant 9 | # proportion of contributors will probably not be using SublimeText 10 | *.sublime-project 11 | # sftp configuration file 12 | sftp-config.json 13 | 14 | ### Eclipse 15 | *.pydevproject 16 | .metadata 17 | .gradle 18 | bin/ 19 | tmp/ 20 | *.tmp 21 | *.bak 22 | *.swp 23 | *~.nib 24 | local.properties 25 | .settings/ 26 | .loadpath 27 | .project 28 | .externalToolBuilders/ 29 | # Locally stored "Eclipse launch configurations" 30 | *.launch 31 | 32 | ### JetBrains 33 | .idea/ 34 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": false, 3 | "browser": true, 4 | "devel": true, 5 | "curly": true, 6 | "newcap": false, 7 | "strict": true, 8 | "undef": true, 9 | "laxbreak": true, 10 | "globals": 11 | { 12 | "GM_xmlhttpRequest": false, 13 | "GM_info": false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | notifications: 5 | email: false 6 | install: 7 | - npm install jshint -g 8 | script: 9 | - jshint autoPlay.user.js 10 | - jshint leaderboard.user.js 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Martin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monster Minigame Wormhole Warp Script # 2 | 3 | #### WARNING: this script uses an autoupdater. If you're not down for this, switch your script to use `https://github.com/YeOldeWH/MonsterMinigameWormholeWarp/raw/master/autoPlay.noUpdate.user.js` and update manually through Greasemonkey/Tampermonkey like normal. #### 4 | 5 | ## Installation ## 6 | 7 | ### [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) ### 8 | 9 | 1. Visit https://github.com/YeOldeWH/MonsterMinigameWormholeWarp/raw/master/autoPlay.user.js 10 | 2. When the editor has loaded, click `Install` (*NOT* `Process with Chrome`). 11 | 12 | ### [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) ### 13 | 14 | 1. Navigate to https://github.com/YeOldeWH/MonsterMinigameWormholeWarp/raw/master/autoPlay.user.js 15 | 2. Right click on the page, and click `Save Page As`. 16 | 3. While Firefox is still open, open a File Manager of any sort, and navigate to the directory you saved the script. 17 | 4. Drag & drop the script file onto the Firefox window. 18 | 5. Press `Install`. 19 | 20 | ## Purpose ## 21 | 22 | WORMHOLES! 23 | 24 | This fork has: 25 | * Buys wormholes 26 | * Auto clicks 27 | * Uses wormholes at optimal time 28 | * Disable most particles 29 | * Auto-updates securely from this GitHub repo 30 | 31 | ## What's new in version 5 (a.k.a. the *everything you know is wrong* edition) ## 32 | 33 | * Interface changes: Many options have been removed; this should make things consistent. Just relax and don't touch anything! Autoclicking will be slowed as the boss level draws near. 34 | * No more *Like New* purchases are also intentional. A small number of people in the group are purchasing the *Like New* upgrades; all we need are the Wormholes. 35 | * Nukes will be used automatically outside of levels that include Our Great and Powerful Gold Helmed Overlord. 36 | * Off-laning on the x00 boss level allows players to spam Wormholes and Like News without players getting free Nuke charges, as well as keeping the autoclick DPS off of the boss. 37 | * Wormhole counter isn't updating during x00 is due to a change in the way we use Wormholes. So fast. 38 | * The script will now automatically check for updates itself every 5-15 mins and reload itself if it updates, this means no more manual/afk updates! 39 | * If you're not down for this, switch your script to use `https://github.com/YeOldeWH/MonsterMinigameWormholeWarp/raw/master/autoPlay.noUpdate.user.js` and update manually through Greasemonkey/Tampermonkey like normal. 40 | 41 | ## Features ## 42 | 43 | - Moves you to the lane most likely to give you gold, prioritized like so: 44 | 1. The lane with a Treasure Minion or Boss 45 | 2. The lane with the Miniboss with the lowest health 46 | 3. The lane with a Spawner below 40% health 47 | 4. The lane with a Creep below 10% health 48 | 5. The lane with the Spawner with the lowest health 49 | - Activates most reusable abilities, if they are purchased and cooled down: 50 | - Medics if your health is below 50% 51 | - Morale Booster, Napalm, and Cluster Bombs if the lane has a Spawner and 2-3 Creeps 52 | - Good Luck Charm as soon as possible 53 | - Tactical Nuke if the current Spawner is between 60% and 30% health 54 | - Activates some items if you have them and the situation calls for them: 55 | - God Mode if Medics is in cooldown and your health is low 56 | - Cripple Spawner if the spawner in the current lane has more than 95% health 57 | - Gold Rain if facing a Boss who has more than 60% health 58 | - Respawns you after 5 seconds (instead of 1 minute) if you die 59 | - Disables certain abilities and items if facing a Boss (to try to maximize Raining Gold and Metal Detector benefits) 60 | 61 | ### Manual Installation ### 62 | 63 | ##### Chrome ##### 64 | 1. Open https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js 65 | 2. Select All, Copy. 66 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 67 | 4. Press `Ctrl + Shift + J`. 68 | 5. Paste into the javascript input, and hit `Enter`. 69 | 70 | ##### Firefox ##### 71 | 1. Open https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js 72 | 2. Select All, Copy. 73 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 74 | 4. Press `Ctrl + Shift + K`. 75 | 5. Paste into the javascript input, and hit `Enter`. 76 | 77 | ##### Internet Explorer / Microsoft Edge ##### 78 | 1. Open https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js 79 | 2. Select All, Copy. 80 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 81 | 4. Press `F12` and navigate to the `Console` tab. 82 | 5. Paste into the javascript input, and hit `Enter`. 83 | 84 | To stop the manual script, type `window.clearTimeout(window.SteamDB_Minigame_Timer);` into the console and hit `Enter`. 85 | 86 | The game should now play itself, you should leave it running in the background. If you're not sure if it is auto-playing, try changing lanes. If it jumps back almost immediately, it's working. 87 | 88 | ## I want to contribute! ## 89 | 90 | This project is open-source on github. There are different ways you can help: 91 | 92 | - Find a Pull Request that's marked `needs testing`. Run that version of the script for a while and watch the console for errors. If there's no errors, pay attention to what the changes are doing gameplay-wise, and make sure it's doing what it's supposed to do. 93 | - Find an Issue that's marked `help wanted`. Make the changes needed by that issue, and create a Pull Request with your enhancement or bugfix. 94 | - Pick an item off the TODO list, below, and implement it. When it's done (and tested and working), create a Pull Request. 95 | - Got an idea for an improvement that's not already listed? Code it up, test it out, then make a Pull Request when it's ready. 96 | 97 | ### Pull Request Guidelines ### 98 | 99 | - Do NOT change the script version in your PR as it could be incremented before your PR is merged. 100 | - Test your changes both in console and Greasemonkey/Tampermonkey. 101 | - [Rebase](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request) your PR branch when you're done and upstream has changed. 102 | - Do NOT delete the current one and make a new PR to add new commits, just keep commiting on the PR's branch. 103 | - Squash your commits before submitting the PR. 104 | -------------------------------------------------------------------------------- /autoPlay.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Ye Olde Megajump (Auto-updating) 3 | // @namespace https://github.com/YeOldeWH/MonsterMinigameWormholeWarp 4 | // @description A script that runs the Steam Monster Minigame for you. Now with megajump. Brought to you by the Ye Olde Wormhole Schemers and DannyDaemonic 5 | // @version 6.0.3 6 | // @match *://steamcommunity.com/minigame/towerattack* 7 | // @match *://steamcommunity.com//minigame/towerattack* 8 | // @grant GM_xmlhttpRequest 9 | // @updateURL https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoPlay.user.js 10 | // @downloadURL https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoPlay.user.js 11 | // ==/UserScript== 12 | 13 | (function(x) { 14 | 15 | // Options 16 | var old_version = false; 17 | var update_json_url = 'https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/version.json'; 18 | var script_url = 'https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js'; 19 | var loader_version = '6.0.3'; 20 | 21 | // Load the actual script 22 | GM_xmlhttpRequest ({ 23 | method: "GET", 24 | url: script_url + "?" + new Date().getTime(), 25 | onload: function(response) { 26 | var scriptElement = document.createElement( "script" ); 27 | scriptElement.type = "text/javascript"; 28 | scriptElement.innerHTML = response.responseText; 29 | document.body.appendChild (scriptElement); 30 | } 31 | }); 32 | 33 | function do_check () { 34 | GM_xmlhttpRequest ({ 35 | method: "GET", 36 | url: update_json_url + "?" + new Date().getTime(), 37 | onload: function(response) { 38 | var version_data = JSON.parse(response.responseText); 39 | 40 | if (version_data.Version != old_version) { 41 | if (old_version === false) { 42 | // First time reading the JSON 43 | old_version = version_data.Version; 44 | 45 | // Display the version 46 | var versionDiv = document.createElement ("div"); 47 | versionDiv.innerHTML = '
Loader: ' + loader_version + ' Script: ' + old_version + '
'; 51 | document.body.appendChild (versionDiv); 52 | 53 | // Check again in 15 minutes 54 | x.setTimeout (do_check, 15 * 60 * 1000); 55 | } else { 56 | x.location.reload(true); 57 | } 58 | } else { 59 | // Check again in 5 minutes 60 | x.setTimeout (do_check, 5 * 60 * 1000); 61 | } 62 | } 63 | }); 64 | 65 | } 66 | 67 | do_check (); 68 | }(window)); 69 | -------------------------------------------------------------------------------- /autoplay.noUpdate.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Ye Olde Megajump 3 | // @namespace https://github.com/YeOldeWH/MonsterMinigameWormholeWarp 4 | // @description A script that runs the Steam Monster Minigame for you. Now with megajump. Brought to you by the Ye Olde Wormhole Schemers and DannyDaemonic 5 | // @version 8.0.3 6 | // @match *://steamcommunity.com/minigame/towerattack* 7 | // @match *://steamcommunity.com//minigame/towerattack* 8 | // @grant none 9 | // @updateURL https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js 10 | // @downloadURL https://raw.githubusercontent.com/YeOldeWH/MonsterMinigameWormholeWarp/master/autoplay.noUpdate.user.js 11 | // ==/UserScript== 12 | 13 | // IMPORTANT: Update the @version property above to a higher number such as 1.1 and 1.2 when you update the script! Otherwise, Tamper / Greasemonkey users will not update automatically. 14 | 15 | (function(w) { 16 | "use strict"; 17 | 18 | // OPTIONS 19 | var clickRate = 20; 20 | var logLevel = 1; // 5 is the most verbose, 0 disables all log 21 | 22 | var wormholeOn100 = 1; 23 | var likeNewOn100 = 0; 24 | var medicOn100 = 1; 25 | var clicksOnBossLevel = 0; 26 | var upgThreshold = 100; 27 | var minAbilityUsePercent = 0.3; 28 | 29 | var enableAutoClicker = true; 30 | 31 | var enableAutoUpgradeHP = true; 32 | var enableAutoUpgradeClick = true; 33 | var enableAutoUpgradeDPS = false; 34 | var enableAutoUpgradeElemental = true; 35 | var enableAutoPurchase = true; 36 | var enableAutoBadgePurchase = true; 37 | 38 | var removeInterface = getPreferenceBoolean("removeInterface", true); // get rid of a bunch of pointless DOM 39 | var removeParticles = getPreferenceBoolean("removeParticles", true); 40 | var removeFlinching = getPreferenceBoolean("removeFlinching", true); 41 | var removeCritText = getPreferenceBoolean("removeCritText", false); 42 | var removeAllText = getPreferenceBoolean("removeAllText", false); 43 | var enableFingering = getPreferenceBoolean("enableFingering", true); 44 | var disableRenderer = getPreferenceBoolean("disableRenderer", true); 45 | var enableTrollTrack = getPreferenceBoolean("enableTrollTrack", false); 46 | var enableElementLock = getPreferenceBoolean("enableElementLock", true); 47 | var enableAutoRefresh = true; 48 | var enableChen = getPreferenceBoolean("enableChen", false); 49 | 50 | var autoRefreshMinutes = 30; 51 | var autoRefreshMinutesRandomDelay = 10; 52 | var autoRefreshSecondsCheckLoadedDelay = 30; 53 | 54 | var predictTicks = 0; 55 | var predictJumps = 0; 56 | var predictLastWormholesUpdate = 0; 57 | 58 | // Auto refresh handler delay 59 | var autoRefreshDuringBossDelayTotal = 0; // Total delay already passed 60 | var autoRefreshDuringBossDelay = 60000; // Delay during the boss (ms) 61 | var autoRefreshDuringBossDelayStep = 2500; // Delay 'step' (until we try again) 62 | 63 | // DO NOT MODIFY 64 | var isPastFirstRun = false; 65 | var isAlreadyRunning = false; 66 | var refreshTimer = null; 67 | var currentClickRate = enableAutoClicker ? clickRate : 0; 68 | var lastLevel = 0; 69 | var lastLevelInfo = [{ 70 | level: 0, 71 | levelsGained: 0, 72 | timeStarted: 0, 73 | timeTakenInSeconds: 0 74 | }]; 75 | var _previousEnemyHP = [0]; 76 | var _previousDPS = [0]; 77 | var _tickInfo = []; 78 | 79 | var approxYOWHClients = 0; 80 | var skipsLastJump = 0; 81 | var updateSkips = false; 82 | 83 | var trt_oldCrit = function() {}; 84 | var trt_oldPush = function() {}; 85 | var trt_oldRender = function() {}; 86 | var ELEMENTS = { 87 | LockedElement: -1 88 | }; 89 | 90 | var UPGRADES = { 91 | LIGHT_ARMOR: 0, 92 | AUTO_FIRE_CANNON: 1, 93 | ARMOR_PIERCING_ROUND: 2, 94 | DAMAGE_TO_FIRE_MONSTERS: 3, 95 | DAMAGE_TO_WATER_MONSTERS: 4, 96 | DAMAGE_TO_AIR_MONSTERS: 5, 97 | DAMAGE_TO_EARTH_MONSTERS: 6, 98 | LUCKY_SHOT: 7, 99 | HEAVY_ARMOR: 8, 100 | ADVANCED_TARGETING: 9, 101 | EXPLOSIVE_ROUNDS: 10, 102 | MEDICS: 11, 103 | MORALE_BOOSTER: 12, 104 | GOOD_LUCK_CHARMS: 13, 105 | METAL_DETECTOR: 14, 106 | DECREASE_COOLDOWNS: 15, 107 | TACTICAL_NUKE: 16, 108 | CLUSTER_BOMB: 17, 109 | NAPALM: 18, 110 | BOSS_LOOT: 19, 111 | ENERGY_SHIELDS: 20, 112 | FARMING_EQUIPMENT: 21, 113 | RAILGUN: 22, 114 | PERSONAL_TRAINING: 23, 115 | AFK_EQUIPMENT: 24, 116 | NEW_MOUSE_BUTTON: 25, 117 | CYBERNETIC_ENHANCEMENTS: 26, 118 | LEVEL_1_SENTRY_GUN: 27, 119 | TITANIUM_MOUSE_BUTTON: 28 120 | }; 121 | 122 | var ABILITIES = { 123 | FIRE_WEAPON: 1, 124 | CHANGE_LANE: 2, 125 | RESPAWN: 3, 126 | CHANGE_TARGET: 4, 127 | MORALE_BOOSTER: 5, 128 | GOOD_LUCK_CHARMS: 6, 129 | MEDICS: 7, 130 | METAL_DETECTOR: 8, 131 | DECREASE_COOLDOWNS: 9, 132 | TACTICAL_NUKE: 10, 133 | CLUSTER_BOMB: 11, 134 | NAPALM: 12, 135 | RESURRECTION: 13, 136 | CRIPPLE_SPAWNER: 14, 137 | CRIPPLE_MONSTER: 15, 138 | MAX_ELEMENTAL_DAMAGE: 16, 139 | RAINING_GOLD: 17, 140 | CRIT: 18, 141 | PUMPED_UP: 19, 142 | THROW_MONEY_AT_SCREEN: 20, 143 | GOD_MODE: 21, 144 | TREASURE: 22, 145 | STEAL_HEALTH: 23, 146 | REFLECT_DAMAGE: 24, 147 | FEELING_LUCKY: 25, 148 | WORMHOLE: 26, 149 | LIKE_NEW: 27 150 | }; 151 | 152 | var ENEMY_TYPE = { 153 | SPAWNER: 0, 154 | CREEP: 1, 155 | BOSS: 2, 156 | MINIBOSS: 3, 157 | TREASURE: 4 158 | }; 159 | 160 | var BOSS_DISABLED_ABILITIES = [ 161 | ABILITIES.MORALE_BOOSTER, 162 | ABILITIES.GOOD_LUCK_CHARMS, 163 | ABILITIES.TACTICAL_NUKE, 164 | ABILITIES.CLUSTER_BOMB, 165 | ABILITIES.NAPALM, 166 | ABILITIES.CRIT, 167 | ABILITIES.CRIPPLE_SPAWNER, 168 | ABILITIES.CRIPPLE_MONSTER, 169 | ABILITIES.MAX_ELEMENTAL_DAMAGE, 170 | ABILITIES.REFLECT_DAMAGE, 171 | ABILITIES.STEAL_HEALTH, 172 | ABILITIES.THROW_MONEY_AT_SCREEN 173 | ]; 174 | 175 | var CONTROL = { 176 | speedThreshold: 2000, 177 | rainingRounds: 100, 178 | disableGoldRainLevels: 500, 179 | rainingSafeRounds: 9 180 | }; 181 | 182 | var GAME_STATUS = { 183 | LOBBY: 1, 184 | RUNNING: 2, 185 | OVER: 3 186 | }; 187 | 188 | var wormholesUsedOnLevel = 0; 189 | 190 | // Try to disable particles straight away, 191 | // if not yet available, they will be disabled in firstRun 192 | disableParticles(); 193 | 194 | // Define custom getters for document.hidden and the prefixed versions, so the game 195 | // doesn't stop ticking in the background. 196 | if (Object.defineProperty) { 197 | var props = ['hidden', 'webkitHidden', 'mozHidden', 'msHidden']; 198 | for (var i = 0; i < props.length; ++i) 199 | Object.defineProperty(document, props[i], {value: false}); 200 | } 201 | 202 | if(!getPreferenceBoolean("alertShown", false)) { 203 | w.ShowAlertDialog( 204 | 'Ye Olde Megajump', 205 | 206 | '
This dialog will be shown just once, so please read through it.

' + 207 | '

This script does not lag your game,
we are limiting it to 1 frame per second to lower CPU usage.

' + 208 | '

We have multiple options to configure this script, and disabling FPS limiter is one of them.

' + 209 | '

You can report issues on GitHub

' + 210 | '

Thanks and have fun!

' 211 | ).done(function(strButton) { 212 | setPreference("alertShown", true); 213 | }); 214 | } 215 | 216 | function s() { 217 | return w.g_Minigame.m_CurrentScene; 218 | } 219 | 220 | function firstRun() { 221 | advLog("Starting YOWH Script.", 1); 222 | 223 | trt_oldCrit = s().DoCritEffect; 224 | trt_oldPush = s().m_rgClickNumbers.push; 225 | trt_oldRender = w.g_Minigame.Render; 226 | 227 | toggleFingering(); 228 | 229 | if(enableElementLock) { 230 | lockElements(); 231 | } 232 | 233 | if (enableAutoRefresh) { 234 | autoRefreshPage(autoRefreshMinutes); 235 | } 236 | 237 | toggleRenderer(); 238 | 239 | // disable particle effects - this drastically reduces the game's memory leak 240 | disableParticles(); 241 | 242 | // disable enemy flinching animation when they get hit 243 | if(removeFlinching && w.CEnemy) { 244 | w.CEnemy.prototype.TakeDamage = function() {}; 245 | w.CEnemySpawner.prototype.TakeDamage = function() {}; 246 | w.CEnemyBoss.prototype.TakeDamage = function() {}; 247 | } 248 | 249 | if(removeCritText) { 250 | toggleCritText(); 251 | } 252 | 253 | if(removeAllText) { 254 | toggleAllText(); 255 | } 256 | 257 | // style 258 | var styleNode = document.createElement('style'); 259 | styleNode.type = 'text/css'; 260 | var styleText = [ 261 | // Page content 262 | ".pagecontent {padding: 0}", 263 | // Align abilities to the left 264 | "#abilitiescontainer {text-align: left;}", 265 | // Activitylog and ability list 266 | "#activeinlanecontainer {padding-left: 10px;}", 267 | "#activeinlanecontainer:hover {height: auto; background-image: radial-gradient(circle farthest-corner at 32px 0px, rgba(0,124,182,0.1), #11111C); padding-bottom: 10px; position:absolute; z-index: 1;}", 268 | "#activeinlanecontainer:hover + #activitylog {margin-top: 88px;}", 269 | "#activitylog {margin-top: 20px}", 270 | // Hide leave game button 271 | ".leave_game_btn {display: none;}", 272 | // Option menu 273 | ".game_options {height: auto;}", 274 | ".game_options .toggle_sfx_btn {margin: 6px 7px 0px 2px; float: right;}", 275 | ".game_options .toggle_music_btn {margin-right: 2px; float: right;}", 276 | ".options_box {background-color: #000; width: 940px; padding: 12px; box-shadow: 2px 2px 0px rgba(0, 0, 0, 0.6); color: #EDEDED; margin: 4px auto 0; overflow: auto; float: left;}", 277 | ".options_box span.asterisk {color: #FF5252; font-size: 24px; line-height: 4px; vertical-align: bottom;}", 278 | ".options_column {-moz-column-count: 2; -webkit-column-count: 2; column-count: 2; width: 50%; float: left;}", 279 | ".options_column label {display: inline-block;}", 280 | ".options_column input {float: left;}", 281 | ".options_column input[type=number] {margin: 6px 5px 0 0; padding: 2px 0px 0px 4px;}", 282 | ".options_column input[name=setLogLevel] {width: 25px;}", 283 | ".options_column span.asterisk {line-height: 14px;}", 284 | // Element lock box 285 | ".lock_elements_box {width: 165px; top: -76px; left: 303px; box-sizing: border-box; line-height: 1rem; padding: 7px 10px; position: absolute; color: #EDEDED;}", 286 | // Breadcrumbs 287 | ".breadcrumbs {color: #bbb;}", 288 | ".bc_span {text-shadow: 1px 1px 0px rgba( 0, 0, 0, 0.3 );}", 289 | ".bc_room {color: #ACE191;}", 290 | ".bc_level {color: #FFA07A;}", 291 | ".bc_time {color: #9AC0FF;}", 292 | ".bc_worms {color: #FFF79A;}", 293 | // Adjustments for hard to see areas on the new background 294 | "#upgradesscroll, #activityscroll {opacity: 0.75;}", 295 | ".teamhealth {background: rgba( 240, 240, 255, 0.2 );}", 296 | "#upgrades .title_upgrates {color: #67C;}", 297 | // Always show ability count 298 | ".abilitytemplate > a > .abilityitemquantity {visibility: visible; pointer-events: none;}", 299 | ".tv_ui {background-image: url(http://i.imgur.com/vM1gTFY.gif);}", 300 | "#Chen {position: absolute; bottom: 0px; left: 770px; width: 286px; height: 250px; background-image: url(//i.imgur.com/xMbQChA.png); }", 301 | "" 302 | ]; 303 | styleNode.textContent = styleText.join(""); 304 | document.head.appendChild(styleNode); 305 | 306 | if( removeInterface ) { 307 | var node = document.getElementById("global_header"); 308 | if (node && node.parentNode) { 309 | node.parentNode.removeChild( node ); 310 | } 311 | node = document.getElementById("footer"); 312 | if (node && node.parentNode) { 313 | node.parentNode.removeChild( node ); 314 | } 315 | node = document.getElementById("footer_spacer"); 316 | if (node && node.parentNode) { 317 | node.parentNode.removeChild( node ); 318 | } 319 | document.body.style.backgroundPosition = "0 0"; 320 | } 321 | 322 | originalUpdateLog = CUI.prototype.UpdateLog; 323 | 324 | // Set to match preferences 325 | toggleTrackTroll(); 326 | toggleChen(); 327 | 328 | // Add cool background 329 | $J('body.flat_page.game').css('background-image', 'url(http://i.imgur.com/P8TB236.jpg)'); 330 | 331 | // Add "players in game" label 332 | var titleActivity = document.querySelector( '.title_activity' ); 333 | var playersInGame = document.createElement( 'span' ); 334 | playersInGame.innerHTML = '0/1500 Players in room
'; 335 | titleActivity.insertBefore(playersInGame, titleActivity.firstChild); 336 | ELEMENTS.PlayersInGame = document.getElementById("players_in_game"); 337 | 338 | var info_box = document.querySelector(".leave_game_helper"); 339 | info_box.className = "options_box"; 340 | var options_menu = document.querySelector(".game_options"); 341 | var sfx_btn = document.querySelector(".toggle_sfx_btn"); 342 | options_menu.insertBefore(info_box, sfx_btn); 343 | 344 | info_box.innerHTML = 'OPTIONS' + ((typeof GM_info !== "undefined") ? ' (v' + GM_info.script.version + ')' : '') + '
Settings marked with a * requires a refresh to take effect.
'; 345 | 346 | var options1 = document.createElement("div"); 347 | options1.className = "options_column"; 348 | 349 | options1.appendChild(makeCheckBox("removeInterface", "Remove interface", removeInterface, handleEvent, true)); 350 | options1.appendChild(makeCheckBox("removeParticles", "Remove particle effects", removeParticles, handleEvent, true)); 351 | options1.appendChild(makeCheckBox("removeFlinching", "Remove flinching effects", removeFlinching, handleEvent, true)); 352 | options1.appendChild(makeCheckBox("removeCritText", "Remove crit text", removeCritText, toggleCritText, false)); 353 | options1.appendChild(makeCheckBox("removeAllText", "Remove all text", removeAllText, toggleAllText, false)); 354 | options1.appendChild(makeCheckBox("disableRenderer", "Limit frames per second to increase performance", disableRenderer, toggleRenderer, false)); 355 | options1.appendChild(makeCheckBox("enableChen", "Honk Honk?", enableChen, toggleChen, false)); 356 | 357 | info_box.appendChild(options1); 358 | 359 | var options2 = document.createElement("div"); 360 | options2.className = "options_column"; 361 | 362 | if (typeof GM_info !== "undefined") { 363 | options2.appendChild(makeCheckBox("enableAutoRefresh", "Enable AutoRefresh (mitigate memory leak)", enableAutoRefresh, toggleAutoRefresh, false)); 364 | } 365 | 366 | options2.appendChild(makeCheckBox("enableFingering", "Enable targeting pointer", enableFingering, toggleFingering, false)); 367 | options2.appendChild(makeCheckBox("enableTrollTrack", "Enable tracking trolls", enableTrollTrack, toggleTrackTroll, false)); 368 | options2.appendChild(makeNumber("setLogLevel", "Change the log level (you shouldn't need to touch this)", logLevel, 0, 5, updateLogLevel)); 369 | 370 | info_box.appendChild(options2); 371 | 372 | //Elemental upgrades lock 373 | var ab_box = document.getElementById("abilities"); 374 | var lock_elements_box = document.createElement("div"); 375 | lock_elements_box.className = "lock_elements_box"; 376 | lock_elements_box.title = "To maximise team damage players should max only one element. But distributions of elements through people should be equal. So we calculated your element using your unique ID. Upgrade your element to make maximum performance or disable this checkbox."; 377 | var lock_elements_checkbox = makeCheckBox("enableElementLock", "Lock element upgrades for more team dps", enableElementLock, toggleElementLock, false); 378 | lock_elements_box.appendChild(lock_elements_checkbox); 379 | ab_box.appendChild(lock_elements_box); 380 | 381 | // Easter egg 382 | $J('
').insertAfter($J('.tv_ui')); 383 | 384 | enhanceTooltips(); 385 | 386 | isPastFirstRun = true; 387 | } 388 | 389 | // Valve's update 390 | var originalUpdateLog = null; 391 | 392 | // The trolltrack 393 | var localUpdateLog = function( rgLaneLog ) { 394 | var abilities = this.m_Game.m_rgTuningData.abilities; 395 | var level = getGameLevel(); 396 | 397 | if( !this.m_Game.m_rgPlayerTechTree ) return; 398 | 399 | var nHighestTime = 0; 400 | 401 | for( var i=rgLaneLog.length-1; i >= 0; i--) { 402 | var rgEntry = rgLaneLog[i]; 403 | 404 | if( isNaN( rgEntry.time ) ) rgEntry.time = this.m_nActionLogTime + 1; 405 | 406 | if( rgEntry.time <= this.m_nActionLogTime ) continue; 407 | 408 | // If performance concerns arise move the level check out and swap switch for if. 409 | switch( rgEntry.type ) { 410 | case 'ability': 411 | if ( (level % 100 !== 0 && [26].indexOf(rgEntry.ability) > -1) || (level % 100 === 0 && [10, 11, 12, 15, 20].indexOf(rgEntry.ability) > -1) ) { 412 | var ele = this.m_eleUpdateLogTemplate.clone(); 413 | $J(ele).data('abilityid', rgEntry.ability); 414 | $J('.name', ele).text(rgEntry.actor_name).attr("style", "color: red; font-weight: bold;"); 415 | $J('.ability', ele).text(abilities[rgEntry.ability].name + " on level " + level); 416 | $J('img', ele).attr('src', g_rgIconMap['ability_' + rgEntry.ability].icon); 417 | 418 | $J(ele).v_tooltip({tooltipClass: 'ta_tooltip', location: 'top'}); 419 | 420 | this.m_eleUpdateLogContainer[0].insertBefore(ele[0], this.m_eleUpdateLogContainer[0].firstChild); 421 | 422 | advLog(rgEntry.actor_name + " used " + s().m_rgTuningData.abilities[ rgEntry.ability ].name + " on level " + level, 1); 423 | 424 | if(rgEntry.ability == ABILITIES.WORMHOLE) { 425 | wormholesUsedOnLevel++; 426 | } 427 | } 428 | break; 429 | default: 430 | console.log("Unknown action log type: %s", rgEntry.type); 431 | console.log(rgEntry); 432 | } 433 | 434 | if(rgEntry.time > nHighestTime) nHighestTime = rgEntry.time; 435 | } 436 | 437 | if( nHighestTime > this.m_nActionLogTime ) this.m_nActionLogTime = nHighestTime; 438 | 439 | var e = this.m_eleUpdateLogContainer[0]; 440 | while(e.children.length > 20 ) { 441 | e.children[e.children.length-1].remove(); 442 | } 443 | }; 444 | 445 | function disableParticles() { 446 | if (w.CSceneGame) { 447 | w.CSceneGame.prototype.DoScreenShake = function() {}; 448 | 449 | if(removeParticles) { 450 | w.CSceneGame.prototype.SpawnEmitter = function(emitter) { 451 | emitter.emit = false; 452 | return emitter; 453 | }; 454 | 455 | var particles = s().m_rgActiveParticles; 456 | 457 | if(particles) { 458 | if (particles[ 7 ]) { 459 | particles[ 7 ][0].emit = false; 460 | particles[ 7 ][1].emit = false; 461 | } 462 | 463 | if (particles[ 5 ]) { 464 | particles[ 5 ][0].emit = false; 465 | } 466 | 467 | if (particles[ 6 ]) { 468 | particles[ 6 ][0].emit = false; 469 | particles[ 6 ][1].emit = false; 470 | } 471 | 472 | if (particles[ 8 ]) { 473 | particles[ 8 ][0].emit = false; 474 | particles[ 8 ][1].emit = false; 475 | particles[ 8 ][2].emit = false; 476 | } 477 | 478 | if (particles[ 9 ]) { 479 | particles[ 9 ][0].emit = false; 480 | particles[ 9 ][1].emit = false; 481 | } 482 | } 483 | } 484 | } 485 | } 486 | 487 | function getTimeleft() { 488 | var cTime = new Date(); 489 | var cHours = cTime.getUTCHours(); 490 | var cMins = cTime.getUTCMinutes(); 491 | var timeLeft = 60 - cMins; 492 | 493 | if (cHours == 15) { 494 | return timeLeft; 495 | } 496 | 497 | return 61; 498 | } 499 | 500 | function unshiftIntoCircularBuffer(buffer, bufferSize, value) { 501 | buffer.unshift(value); 502 | 503 | if (buffer.length > bufferSize) { 504 | buffer.pop(); 505 | } 506 | } 507 | 508 | var _jumpHistory = []; 509 | var defaultEntryRemoved = false; 510 | function updateLevelAndDPSTracker() { 511 | var levelHasChanged = lastLevelInfo[0].level !== getGameLevel(); 512 | if (levelHasChanged) { 513 | unshiftIntoCircularBuffer(lastLevelInfo, 1000, 514 | { level: getGameLevel(), 515 | levelsGained: -1, 516 | timeStarted: s().m_rgGameData.timestamp, 517 | timeTakenInSeconds: -1, 518 | total_enemy_hp: getTotalLevelEnemyStats().max_hp, 519 | level_dps: 0 520 | }); 521 | 522 | var previousLevel = lastLevelInfo[1]; 523 | 524 | previousLevel.levelsGained = getGameLevel() - previousLevel.level; 525 | previousLevel.timeTakenInSeconds = s().m_rgGameData.timestamp - previousLevel.timeStarted; 526 | } 527 | 528 | // This is a weird place for this I know, but I couldn't think of anywhere better in my sleep deprived state 529 | updateCurrentDPS(levelHasChanged); 530 | 531 | if (levelHasChanged) { 532 | previousLevel.level_dps = getAverageDPS(); 533 | 534 | if (!defaultEntryRemoved) { 535 | defaultEntryRemoved = true; 536 | lastLevelInfo.pop(); 537 | } 538 | } 539 | } 540 | 541 | function updateCurrentDPS(levelHasChanged) { 542 | var skipped_dps = 0; 543 | 544 | if (levelHasChanged) { 545 | var previousLevel = lastLevelInfo[1]; 546 | var skipped_dps = (previousLevel.levelsGained * previousLevel.total_enemy_hp) / previousLevel.timeTakenInSeconds; 547 | } 548 | 549 | var current_hp = getTotalLevelEnemyStats().current_hp; 550 | var dps = Math.max(current_hp - _previousEnemyHP[0], 0); 551 | 552 | unshiftIntoCircularBuffer(_previousDPS, 10, (dps + skipped_dps) || 0); 553 | unshiftIntoCircularBuffer(_previousEnemyHP, 10, current_hp); 554 | } 555 | 556 | function getAverageDPS() { 557 | return _previousDPS.reduce(function (a, b) { 558 | return a + b; 559 | }) / _previousDPS.length; 560 | } 561 | 562 | function getTotalLevelEnemyStats() { 563 | var current_hp = 0; 564 | var max_hp = 0; 565 | s().m_rgGameData.lanes.map(function (lane) { 566 | lane.enemies.map(function(enemy) { 567 | current_hp += enemy.hp 568 | max_hp += enemy.max_hp 569 | }); 570 | }); 571 | 572 | return {current_hp: current_hp, max_hp: max_hp} 573 | } 574 | 575 | var predictedLevelHits = 0; 576 | var predictedLevelMisses = 0; 577 | function updateNextLevelPrediction() { 578 | unshiftIntoCircularBuffer(_tickInfo, 1000, 579 | { 580 | tick: s().m_rgGameData.timestamp, 581 | level: getGameLevel(), 582 | tickVelocity: levelsPerSec(), 583 | predictedLevel: getNextPredictedLevel() 584 | }); 585 | 586 | var currentLevel = _tickInfo[0].level; 587 | var predictedCurrentLevel = (_tickInfo[1] && _tickInfo[1].predictedLevel) || 0; 588 | 589 | if (currentLevel !== predictedCurrentLevel) { 590 | console.log("Prediction off by ", currentLevel - predictedCurrentLevel); 591 | predictedLevelMisses++; 592 | } else { 593 | predictedLevelHits++; 594 | } 595 | } 596 | 597 | function getPredictedLevelAccuracy() { 598 | return (predictedLevelHits / (predictedLevelHits + predictedLevelMisses)) 599 | } 600 | 601 | function getLevelForTick(tick) { 602 | var info = _tickInfo.filter(function(i) { return i.tick === tick; })[0]; 603 | 604 | if (info) { 605 | return info.level; 606 | } 607 | 608 | return 0; 609 | } 610 | 611 | function averageWormholesPerBoss() { 612 | var bosses = lastLevelInfo.filter(function(i) { return isBossLevel(i.level); }) 613 | return bosses.map(function(i) { return i.levelsGained }).reduce(function(curr, level) { return curr + level}, 0) / bosses.length 614 | } 615 | 616 | function averageBossesPerSecond() { 617 | var bosses = lastLevelInfo.filter(function(i) { return isBossLevel(i.level); }) 618 | if (bosses.length > 1) { 619 | return (bosses.length / (bosses[0].timeStarted - bosses[bosses.length-1].timeStarted)); 620 | } 621 | 622 | return 0; 623 | } 624 | 625 | var scriptStartedInfo = {level: 0, time: 0}; 626 | function getLevelsPerSecondSinceStart() { 627 | return (getGameLevel() - scriptStartedInfo.level) / (s().m_rgGameData.timestamp - scriptStartedInfo.time); 628 | } 629 | 630 | function getNextPredictedLevel() { 631 | var levelVelocity = Math.round(_tickInfo 632 | .filter(function(i) { return i.level === getGameLevel(); }) 633 | .map(function(i) { return i.tickVelocity; }) 634 | .reduce(function(curr, velocity) { return curr + velocity; }, 0)); 635 | 636 | var wormholes = $J.unique( 637 | s().m_rgActionLog 638 | .filter(function(entry) { return entry.ability === ABILITIES.WORMHOLE; }) 639 | .filter(function(entry) { return getLevelForTick(entry.time) === getGameLevel(); }) 640 | .map(function(entry) { return entry.actor + ":" + entry.time; }) 641 | ).reduce(function(curr, key) { 642 | return curr + 1; 643 | }, 0); 644 | 645 | return getGameLevel() + levelVelocity + wormholes; 646 | } 647 | 648 | function MainLoop() { 649 | var status = s().m_rgGameData.status; 650 | if(status != GAME_STATUS.RUNNING) { 651 | if(disableRenderer) { 652 | s().Tick(); 653 | } 654 | 655 | return; 656 | } 657 | 658 | var level = getGameLevel(); 659 | updateLevelAndDPSTracker(); 660 | updateApproxYOWHClients(); 661 | updateNextLevelPrediction(); 662 | 663 | if (scriptStartedInfo.level === 0) { 664 | scriptStartedInfo.level = level; 665 | scriptStartedInfo.time = s().m_rgGameData.timestamp; 666 | } 667 | 668 | if (!isAlreadyRunning) { 669 | isAlreadyRunning = true; 670 | 671 | if( level !== lastLevel ) { 672 | // Clear any unsent abilities still in the queue when our level changes 673 | s().m_rgAbilityQueue.clear(); 674 | // update skips if applicable 675 | if (updateSkips) { 676 | skipsLastJump = level - lastLevel; 677 | updateSkips = false; 678 | } 679 | 680 | wormholesUsedOnLevel = 0; 681 | } 682 | 683 | if (level % 100 == 0) { 684 | // On a WH level, jump everyone with wormholes to lane 0, unless there is a boss there, in which case jump to lane 1. 685 | var targetLane = 0; 686 | // Check lane 0, enemy 0 to see if it's a boss 687 | var enemyData = s().GetEnemy(0, 0).m_data; 688 | if(typeof enemyData !== "undefined"){ 689 | var enemyType = enemyData.type; 690 | if(enemyType == ENEMY_TYPE.BOSS) { 691 | advLog('In lane 0, there is a boss, avoiding', 4); 692 | targetLane = 1; 693 | var enemyDataLaneOne = s().GetEnemy(1, 0).m_data; 694 | var enemyDataLaneTwo = s().GetEnemy(2, 0).m_data; 695 | if(typeof enemyDataLaneOne != "undefined" && typeof enemyDataLaneTwo == "undefined"){ 696 | //Lane 1 has monsters. Lane 2 is empty. Switch to lane 2 instead. 697 | targetLane = 2; 698 | } 699 | } 700 | } 701 | if( s().m_rgPlayerData.current_lane != targetLane ) { 702 | advLog('Moving player to wormhole lane ' + targetLane, 4); 703 | s().TryChangeLane(targetLane); // put everyone in the same lane 704 | } 705 | 706 | updateSkips = true; 707 | 708 | // GoGo Chen 709 | honkingIntenstifys(true); 710 | } else { 711 | goToLaneWithBestTarget(level); 712 | honkingIntenstifys(false); 713 | } 714 | 715 | attemptRespawn(); 716 | 717 | if (level % 100 !== 0 && w.SteamDB_Wormhole_Timer) { 718 | w.clearInterval(w.SteamDB_Wormhole_Timer); 719 | w.SteamDB_Wormhole_Timer = false; 720 | } 721 | 722 | var timeLeft = getTimeleft(); // Time left in minutes 723 | if(level % 100 == 0){ 724 | useAbilitiesAt100(); 725 | } else if(timeLeft <= 15) { 726 | useAllAbilities(); 727 | } else { 728 | useAbilities(level); 729 | } 730 | 731 | updatePlayersInGame(); 732 | 733 | if( level !== lastLevel ) { 734 | if (level % 100 === 0) { 735 | enableAbility(ABILITIES.WORMHOLE); 736 | enableAbility(ABILITIES.LIKE_NEW); 737 | } else { 738 | disableAbility(ABILITIES.WORMHOLE); 739 | disableAbility(ABILITIES.LIKE_NEW); 740 | } 741 | 742 | lastLevel = level; 743 | updateLevelInfoTitle(level); 744 | refreshPlayerData(); 745 | } 746 | 747 | // only AutoUpgrade after we've spend all badge points 748 | if(s().m_rgPlayerTechTree) { 749 | if(s().m_rgPlayerTechTree.badge_points === 0) { 750 | useAutoUpgrade(); 751 | useAutoPurchaseAbilities(); 752 | } 753 | else { 754 | useAutoBadgePurchase(); 755 | } 756 | } 757 | 758 | var absoluteCurrentClickRate = 0; 759 | 760 | if(currentClickRate > 0) { 761 | var levelRainingMod = level % CONTROL.rainingRounds; 762 | 763 | absoluteCurrentClickRate = level > CONTROL.speedThreshold && (levelRainingMod === 0 || 3 >= (CONTROL.rainingRounds - levelRainingMod)) ? 0 : currentClickRate; 764 | 765 | var wormholesOnLine = getActiveAbilityLaneCount(ABILITIES.WORMHOLE); 766 | var levelsUntilBoss = (CONTROL.rainingRounds - (level % CONTROL.rainingRounds)); 767 | if(levelsUntilBoss < 10 && (wormholesOnLine > levelsUntilBoss || wormholesUsedOnLevel > levelsUntilBoss)) { 768 | advLog("Too much wormholes for throttle back. WH: " + wormholesOnLine + " / "+ wormholesUsedOnLevel+" > lvl: " + levelsUntilBoss, 4); 769 | absoluteCurrentClickRate = currentClickRate; 770 | } 771 | else { 772 | absoluteCurrentClickRate = currentClickRate; 773 | 774 | // throttle back as we approach 775 | var levelsPer = levelsPerSecRaw(); 776 | if (level > 100000 && levelsPer != 0) { // sanity check 777 | 778 | var ticksTillBoss = (level % 100) / levelsPerSec() 779 | if (ticksTillBoss <= 15) { 780 | absoluteCurrentClickRate = Math.round(currentClickRate * (Math.pow(1.333, -(15-ticksTillBoss)) * 39 / 40 + 0.1)); 781 | } 782 | } 783 | } 784 | 785 | //If at the boss level, dont click at all 786 | if (level % CONTROL.rainingRounds == 0) { 787 | absoluteCurrentClickRate = clicksOnBossLevel; 788 | } 789 | 790 | 791 | s().m_nClicks += absoluteCurrentClickRate; 792 | } 793 | 794 | s().m_nLastTick = false; 795 | w.g_msTickRate = 1000; 796 | 797 | var damagePerClick = s().CalculateDamage( 798 | s().m_rgPlayerTechTree.damage_per_click, 799 | s().m_rgGameData.lanes[s().m_rgPlayerData.current_lane].element 800 | ); 801 | 802 | advLog("Ticked. Current clicks per second: " + absoluteCurrentClickRate + ". Current damage per second: " + (damagePerClick * absoluteCurrentClickRate), 4); 803 | 804 | if(disableRenderer) { 805 | s().Tick(); 806 | 807 | requestAnimationFrame(function() { 808 | w.g_Minigame.Renderer.render(s().m_Container); 809 | }); 810 | } 811 | 812 | isAlreadyRunning = false; 813 | 814 | if( absoluteCurrentClickRate > 0) { 815 | var enemy = s().GetEnemy( 816 | s().m_rgPlayerData.current_lane, 817 | s().m_rgPlayerData.target); 818 | 819 | if (enemy) { 820 | displayText( 821 | enemy.m_Sprite.position.x - (enemy.m_nLane * 440), 822 | enemy.m_Sprite.position.y - 52, 823 | "-" + w.FormatNumberForDisplay((damagePerClick * absoluteCurrentClickRate), 5), 824 | "#aaf" 825 | ); 826 | 827 | if( s().m_rgStoredCrits.length > 0 ) { 828 | var rgDamage = s().m_rgStoredCrits.reduce(function(a,b) { 829 | return a + b; 830 | }); 831 | s().m_rgStoredCrits.length = 0; 832 | 833 | s().DoCritEffect( rgDamage, enemy.m_Sprite.position.x - (enemy.m_nLane * 440), enemy.m_Sprite.position.y + 17, 'Crit!' ); 834 | } 835 | 836 | var goldPerClickPercentage = s().m_rgGameData.lanes[s().m_rgPlayerData.current_lane].active_player_ability_gold_per_click; 837 | if (goldPerClickPercentage > 0 && enemy.m_data.hp > 0) { 838 | var goldPerSecond = enemy.m_data.gold * goldPerClickPercentage * absoluteCurrentClickRate; 839 | 840 | s().ClientOverride('player_data', 'gold', s().m_rgPlayerData.gold + goldPerSecond); 841 | s().ApplyClientOverrides('player_data', true); 842 | 843 | advLog( 844 | "Raining gold ability is active in current lane. Percentage per click: " + goldPerClickPercentage 845 | + "%. Approximately gold per second: " + goldPerSecond, 846 | 4 847 | ); 848 | displayText( 849 | enemy.m_Sprite.position.x - (enemy.m_nLane * 440), 850 | enemy.m_Sprite.position.y - 17, 851 | "+" + w.FormatNumberForDisplay(goldPerSecond, 5), 852 | "#e1b21e" 853 | ); 854 | } 855 | } 856 | } 857 | } 858 | } 859 | 860 | function useAutoBadgePurchase() { 861 | if(!enableAutoBadgePurchase) { return; } 862 | 863 | // id = ability 864 | // ratio = how much of the remaining badges to spend 865 | 866 | //Note: this isn't an actual ratio, because badge points get reduced and the values don't add to 1 867 | //For now, this is not a problem, but for stylistic reasons, should eventually be changed. 868 | 869 | //Regular users buy ratio 870 | 871 | 872 | if (likeNewOn100 == 1 && wormholeOn100 == 1) { 873 | // High Level, "Waste Not" Users 874 | var abilityPriorityList = [ 875 | { id: ABILITIES.WORMHOLE, ratio: 0.5 }, 876 | { id: ABILITIES.LIKE_NEW, ratio: 1 }, 877 | { id: ABILITIES.CRIT, ratio: 1 }, 878 | { id: ABILITIES.TREASURE, ratio: 1 }, 879 | { id: ABILITIES.PUMPED_UP, ratio: 1 }, 880 | ]; 881 | } else if (likeNewOn100 == 1) { 882 | // Like New Buyers 883 | var abilityPriorityList = [ 884 | { id: ABILITIES.WORMHOLE, ratio: 0 }, 885 | { id: ABILITIES.LIKE_NEW, ratio: 1 }, 886 | { id: ABILITIES.CRIT, ratio: 1 }, 887 | { id: ABILITIES.TREASURE, ratio: 1 }, 888 | { id: ABILITIES.PUMPED_UP, ratio: 1 }, 889 | ]; 890 | } else { 891 | // Regular User Buy Ratio 892 | var abilityPriorityList = [ 893 | { id: ABILITIES.WORMHOLE, ratio: 1 }, 894 | { id: ABILITIES.LIKE_NEW, ratio: 0 }, 895 | { id: ABILITIES.CRIT, ratio: 1 }, 896 | { id: ABILITIES.TREASURE, ratio: 1 }, 897 | { id: ABILITIES.PUMPED_UP, ratio: 1 }, 898 | ]; 899 | } 900 | 901 | var badgePoints = s().m_rgPlayerTechTree.badge_points; 902 | var abilityData = s().m_rgTuningData.abilities; 903 | var abilityPurchaseQueue = []; 904 | 905 | for (var i = 0; i < abilityPriorityList.length; i++) { 906 | var id = abilityPriorityList[i].id; 907 | var ratio = abilityPriorityList[i].ratio; 908 | var cost = abilityData[id].badge_points_cost; 909 | var portion = parseInt(badgePoints * ratio); 910 | badgePoints -= portion; 911 | 912 | while(portion >= cost) { 913 | abilityPurchaseQueue.push(id); 914 | portion -= cost; 915 | } 916 | 917 | badgePoints += portion; 918 | } 919 | 920 | s().m_rgPurchaseItemsQueue = s().m_rgPurchaseItemsQueue.concat(abilityPurchaseQueue); 921 | s().m_UI.UpdateSpendBadgePointsDialog(); 922 | } 923 | 924 | function toggleAutoBadgePurchase(event) { 925 | var value = enableAutoBadgePurchase; 926 | 927 | if(event !== undefined) { 928 | value = handleCheckBox(event); 929 | } 930 | 931 | enableAutoBadgePurchase = value; 932 | } 933 | 934 | function useAllAbilities() { 935 | for(var key in ABILITIES) { 936 | if(ABILITIES[key] == ABILITIES.WORMHOLE) { continue; } 937 | if(ABILITIES[key] == ABILITIES.LIKE_NEW) { continue; } 938 | tryUsingAbility(ABILITIES[key]); 939 | } 940 | } 941 | 942 | function isBossLevel(level) { 943 | return level % 100 === 0 944 | } 945 | 946 | 947 | function updateApproxYOWHClients() { 948 | var APPROXIMATE_WH_PER_PERSON_PER_SECOND = 10; 949 | 950 | if (lastLevelInfo.length < 2) { 951 | return; 952 | } 953 | 954 | var lastLevel = lastLevelInfo[1].level; 955 | 956 | if (isBossLevel(lastLevel)) { 957 | var levelsJumped = getGameLevel() - lastLevel; 958 | var bossLevelTime = lastLevelInfo[1].timeTakenInSeconds; 959 | 960 | var possiblyInaccurateCount = Math.round(levelsJumped / (bossLevelTime * APPROXIMATE_WH_PER_PERSON_PER_SECOND)); 961 | 962 | if (possiblyInaccurateCount < 1500) { 963 | approxYOWHClients = possiblyInaccurateCount; 964 | } else { 965 | console.log("Inaccurate count of YOWH Clients: ",possiblyInaccurateCount, 966 | ", levelsJumped: ", levelsJumped, 967 | ", bossLevelTime: ", bossLevelTime, 968 | ", lastLevelInfo", lastLevelInfo); 969 | } 970 | } 971 | } 972 | 973 | function levelsPerSecRaw() { 974 | if (lastLevelInfo.length < 2) { 975 | return 0; 976 | } 977 | 978 | var timeSpentOnBosses = 0; 979 | var levelsGainedFromBosses = 0; 980 | 981 | lastLevelInfo.filter(function(levelInfo) { 982 | return isBossLevel(levelInfo.level); 983 | }).map(function(levelInfo) { 984 | timeSpentOnBosses += levelInfo.timeTakenInSeconds; 985 | levelsGainedFromBosses += levelInfo.levelsGained; 986 | }) 987 | 988 | return (((getGameLevel() - lastLevelInfo.slice(-1).pop().level - levelsGainedFromBosses) 989 | / (s().m_rgGameData.timestamp - lastLevelInfo.slice(-1).pop().timeStarted - timeSpentOnBosses)) * 1000 ) / 1000; 990 | } 991 | 992 | function levelsPerSec() { 993 | if (lastLevelInfo.length < 2) { 994 | return 0; 995 | } 996 | 997 | var timeSpentOnBosses = 0; 998 | var levelsGainedFromBosses = 0; 999 | 1000 | lastLevelInfo.slice(0, 10).filter(function(levelInfo) { 1001 | return isBossLevel(levelInfo.level); 1002 | }).map(function(levelInfo) { 1003 | timeSpentOnBosses += levelInfo.timeTakenInSeconds; 1004 | levelsGainedFromBosses += levelInfo.levelsGained; 1005 | }) 1006 | 1007 | return ((getGameLevel() - lastLevelInfo.slice(0, 10).slice(-1).pop().level - levelsGainedFromBosses) 1008 | / (s().m_rgGameData.timestamp - lastLevelInfo.slice(0,10).slice(-1).pop().timeStarted - timeSpentOnBosses) * 1000 ) / 1000; 1009 | } 1010 | 1011 | 1012 | //at level 100 spam WH, Like New, and medics, based on your role 1013 | function useAbilitiesAt100() { 1014 | 1015 | if (wormholeOn100 && !w.SteamDB_Wormhole_Timer) { 1016 | advLog("At level % 100 = 0, forcing the use of wormholes nonstop", 2); 1017 | 1018 | // Fire one off now, so we don't wait the interval 1019 | if (bHaveItem(ABILITIES.WORMHOLE)) triggerAbility(ABILITIES.WORMHOLE); 1020 | 1021 | w.SteamDB_Wormhole_Timer = w.setInterval(function(){ 1022 | if (getGameLevel() % 100 !== 0) { 1023 | // We're not on a *00 level anymore, stop!! 1024 | w.clearInterval(w.SteamDB_Wormhole_Timer); 1025 | w.SteamDB_Wormhole_Timer = false; 1026 | return; 1027 | } 1028 | if (bHaveItem(ABILITIES.WORMHOLE)) triggerAbility(ABILITIES.WORMHOLE); //wormhole 1029 | }, 100); 1030 | /* ^ DO NOT TOUCH THIS. The fundamental idea of this strat is that we get as many wormhole 1031 | jumps in as we can when we hit a boss level. The server seems capable of taking a max of ~10wh/s 1032 | feel free to use the following (hasty) code to manually verify in console: 1033 | 1034 | // 1. Note down number of remaining wormholes 1035 | var dateStart = Date.now(); var interval = setInterval(function() { g_Server.UseAbilities($J.noop, $J.noop, {requested_abilities: [{ability: 26}]}); }, 100); 1036 | 1037 | // 2. Count 10 roughly 10 mississippi's (or however long you want), you will get the exact time taken between start and stop 1038 | clearInterval(interval); console.log("Time: ", (dateStop - dateStart) / 1000); 1039 | // => 13.002 1040 | 1041 | // 3. Click on any ability that can be used to refresh the abilities table from the server 1042 | 1043 | // 4. Subtract the old wormhole count from the new wormhole count and divide by the time taken to get wh/s 1044 | 1045 | Generally wh/s will be somewhere between 2-8 - I've never seen it go higher than 9.5, hence the 100ms resolution. 1046 | Changing this to 50ms will not give you 20 wh/s. 1047 | Changing this to 1000ms will leave wormholes on the table, so to speak. 1048 | 1049 | Yes, there will be overflow, but this a GOOD thing. a few wormholes that overflow and get us *closer* to the 1050 | next level x00 boss helps! 1051 | */ 1052 | } 1053 | 1054 | //This should equate to approximately 1.8 Like News per second 1055 | if (likeNewOn100) { 1056 | advLog("At level % 100 = 0, forcing the use of a like new", 2); 1057 | tryUsingAbility(ABILITIES.LIKE_NEW, false, true); //like new 1058 | } else { 1059 | // if people have LIKE_NEWs, there's no harm in letting them use them 1060 | if (Math.random() <= 0.05) { 1061 | tryUsingAbility(ABILITIES.LIKE_NEW, false, true); 1062 | } 1063 | } 1064 | 1065 | if (medicOn100) { 1066 | advLog("At level % 100 = 0, forcing the use of a medic", 2); 1067 | tryUsingAbility(ABILITIES.MEDICS, false, true); //medics 1068 | } 1069 | } 1070 | 1071 | function useAutoPurchaseAbilities() { 1072 | if(!enableAutoPurchase || autoupgrade_update_hilight) { return; } 1073 | 1074 | var elms = document.querySelectorAll(".container_purchase > div:not([class~='cantafford'])"); 1075 | 1076 | if(elms.length === 0) { return; } 1077 | 1078 | var pData = s().m_rgPlayerData; 1079 | 1080 | [].forEach.call(elms, function(elm) { 1081 | if(elm.style.display !== "") { return; } 1082 | 1083 | var idx = parseInt(elm.id.split('_')[1]); 1084 | 1085 | if(s().GetUpgradeCost(idx) < pData.gold) { 1086 | s().TryUpgrade(elm.querySelector('.link')); 1087 | } 1088 | }); 1089 | } 1090 | 1091 | var autoupgrade_update_hilight = true; 1092 | var autoupgrade_hp_threshold = 0; 1093 | 1094 | function useAutoUpgrade() { 1095 | if(!enableAutoUpgradeDPS 1096 | && !enableAutoUpgradeClick 1097 | && !enableAutoUpgradeHP 1098 | && !enableAutoUpgradeElemental 1099 | ) { 1100 | autoupgrade_update_hilight = false; 1101 | return; 1102 | } 1103 | 1104 | // fixes hiligh when we tick before elements are created 1105 | if(!document.querySelector('.container_upgrades') 1106 | || !document.querySelector('.container_upgrades').hasChildNodes() 1107 | ) { 1108 | return; 1109 | } 1110 | 1111 | var upg_order = [ 1112 | UPGRADES.ARMOR_PIERCING_ROUND, 1113 | UPGRADES.LIGHT_ARMOR, 1114 | UPGRADES.AUTO_FIRE_CANNON, 1115 | UPGRADES.LUCKY_SHOT, 1116 | ]; 1117 | if(enableAutoUpgradeElemental && ELEMENTS.LockedElement !== -1) { upg_order.push(ELEMENTS.LockedElement+3); } 1118 | var upg_map = {}; 1119 | upg_order.forEach(function(i) { upg_map[i] = {}; }); 1120 | var pData = s().m_rgPlayerData; 1121 | var pTree = s().m_rgPlayerTechTree; 1122 | var cache = s().m_UI.m_rgElementCache; 1123 | 1124 | // calculate hp threshold based on mob dps 1125 | var mob = s().m_rgEnemies[0]; 1126 | if(!!mob) { 1127 | var threshold = mob.m_data.dps * 300 * 2.5; 1128 | if(threshold > autoupgrade_hp_threshold) { 1129 | autoupgrade_hp_threshold = threshold; 1130 | } 1131 | } 1132 | 1133 | var upg_enabled = [ 1134 | enableAutoUpgradeClick && s().m_rgGameData.level > upgThreshold, 1135 | enableAutoUpgradeHP && pTree.max_hp < Math.max(100000, autoupgrade_hp_threshold), 1136 | enableAutoUpgradeDPS && s().m_rgGameData.level > upgThreshold, 1137 | ]; 1138 | 1139 | // loop over all upgrades and find the most cost effective ones 1140 | s().m_rgTuningData.upgrades.forEach(function(upg, idx) { 1141 | if(upg_map.hasOwnProperty(upg.type)) { 1142 | 1143 | var cost = s().GetUpgradeCost(idx) / parseFloat(upg.multiplier); 1144 | 1145 | if(!upg_map[upg.type].hasOwnProperty('idx') || upg_map[upg.type].cost_per_mult > cost) { 1146 | if(upg.hasOwnProperty('required_upgrade') && s().GetUpgradeLevel(upg.required_upgrade) < upg.required_upgrade_level) { return; } 1147 | 1148 | upg_map[upg.type] = { 1149 | 'idx': idx, 1150 | 'cost_per_mult': cost, 1151 | }; 1152 | } 1153 | } 1154 | }); 1155 | 1156 | // do hilighting if needed 1157 | if(autoupgrade_update_hilight) { 1158 | autoupgrade_update_hilight = false; 1159 | 1160 | // clear all currently hilighted 1161 | [].forEach.call(document.querySelectorAll('[id^="upgr_"] .info'), 1162 | function(elm) { elm.style.color = ''; }); 1163 | 1164 | // hilight targets 1165 | [].forEach.call(document.querySelectorAll(Object.keys(upg_map).map(function(i) { 1166 | if(i > UPGRADES.ARMOR_PIERCING_ROUND) { 1167 | return "#nonexistant"; 1168 | } else { 1169 | return "#upgr_" + upg_map[i].idx + " .info"; 1170 | } 1171 | }) 1172 | .join(",")), 1173 | function(elm) { elm.style.setProperty('color', '#E1B21E', 'important'); }); 1174 | } 1175 | 1176 | // do upgrading 1177 | for(var i = 0; i < upg_order.length; i++ ) { 1178 | if(!upg_enabled[i] || upg_order[i] > UPGRADES.ARMOR_PIERCING_ROUND) { continue; } 1179 | 1180 | // prioritize click upgrades over DPS ones, unless they are more cost effective 1181 | if(upg_order[i] === UPGRADES.AUTO_FIRE_CANNON && enableAutoUpgradeClick) { 1182 | if(upg_map[UPGRADES.AUTO_FIRE_CANNON].cost_per_mult > upg_map[UPGRADES.ARMOR_PIERCING_ROUND].cost_per_mult / 4) { continue; } 1183 | } 1184 | 1185 | var tree = upg_map[upg_order[i]]; 1186 | 1187 | // upgrade crit/elemental when necessary 1188 | if(upg_order[i] === UPGRADES.ARMOR_PIERCING_ROUND) { 1189 | if(upg_map[UPGRADES.LUCKY_SHOT].cost_per_mult < upg_map[UPGRADES.ARMOR_PIERCING_ROUND].cost_per_mult) { 1190 | tree = upg_map[UPGRADES.LUCKY_SHOT]; 1191 | } 1192 | else if(enableAutoUpgradeElemental 1193 | && upg_map.hasOwnProperty(ELEMENTS.LockedElement+3) 1194 | && upg_map[ELEMENTS.LockedElement+3].cost_per_mult < upg_map[UPGRADES.ARMOR_PIERCING_ROUND].cost_per_mult) { 1195 | tree = upg_map[ELEMENTS.LockedElement+3]; 1196 | } 1197 | } 1198 | 1199 | var key = 'upgr_' + tree.idx; 1200 | 1201 | if(s().GetUpgradeCost(tree.idx) < pData.gold && cache.hasOwnProperty(key)) { 1202 | var elm = cache[key]; 1203 | // valve pls... 1204 | s().TryUpgrade(!!elm.find ? elm.find('.link')[0] : elm.querySelector('.link')); 1205 | autoupgrade_update_hilight = true; 1206 | } 1207 | } 1208 | 1209 | } 1210 | 1211 | function toggleAutoUpgradeDPS(event) { 1212 | var value = enableAutoUpgradeDPS; 1213 | 1214 | if(event !== undefined) { 1215 | value = handleCheckBox(event); 1216 | } 1217 | 1218 | enableAutoUpgradeDPS = value; 1219 | } 1220 | 1221 | function toggleAutoUpgradeClick(event) { 1222 | var value = enableAutoUpgradeClick; 1223 | 1224 | if(event !== undefined) { 1225 | value = handleCheckBox(event); 1226 | } 1227 | 1228 | enableAutoUpgradeClick = value; 1229 | } 1230 | 1231 | function toggleAutoUpgradeHP(event) { 1232 | var value = enableAutoUpgradeHP; 1233 | 1234 | if(event !== undefined) { 1235 | value = handleCheckBox(event); 1236 | } 1237 | 1238 | enableAutoUpgradeHP = value; 1239 | } 1240 | 1241 | function toggleAutoUpgradeElemental(event) { 1242 | 1243 | var value = enableAutoUpgradeElemental; 1244 | 1245 | if(event !== undefined) { 1246 | value = handleCheckBox(event); 1247 | } 1248 | 1249 | enableAutoUpgradeElemental = value; 1250 | } 1251 | 1252 | function toggleAutoPurchase(event) { 1253 | 1254 | var value = enableAutoPurchase; 1255 | 1256 | if(event !== undefined) { 1257 | value = handleCheckBox(event); 1258 | } 1259 | 1260 | enableAutoPurchase = value; 1261 | } 1262 | 1263 | function refreshPlayerData() { 1264 | advLog("Refreshing player data", 2); 1265 | 1266 | w.g_Server.GetPlayerData( 1267 | function(rgResult) { 1268 | var instance = s(); 1269 | 1270 | if( rgResult.response.player_data ) { 1271 | instance.m_rgPlayerData = rgResult.response.player_data; 1272 | instance.ApplyClientOverrides('player_data'); 1273 | instance.ApplyClientOverrides('ability'); 1274 | instance.ApplyClientOverrides('upgrades'); 1275 | } 1276 | 1277 | if( rgResult.response.tech_tree ) { 1278 | instance.m_rgPlayerTechTree = rgResult.response.tech_tree; 1279 | if( rgResult.response.tech_tree.upgrades ) { 1280 | instance.m_rgPlayerUpgrades = w.V_ToArray( rgResult.response.tech_tree.upgrades ); 1281 | } else { 1282 | instance.m_rgPlayerUpgrades = []; 1283 | } 1284 | } 1285 | 1286 | instance.OnReceiveUpdate(); 1287 | }, 1288 | function() {}, 1289 | true 1290 | ); 1291 | } 1292 | 1293 | function makeNumber(name, desc, value, min, max, listener) { 1294 | var label = document.createElement("label"); 1295 | var description = document.createTextNode(desc); 1296 | var number = document.createElement("input"); 1297 | 1298 | number.type = "number"; 1299 | number.name = name; 1300 | number.value = value; 1301 | number.min = min; 1302 | number.max = max; 1303 | number.onchange = listener; 1304 | w[number.name] = number; 1305 | 1306 | label.appendChild(number); 1307 | label.appendChild(description); 1308 | label.appendChild(document.createElement("br")); 1309 | return label; 1310 | } 1311 | 1312 | function makeDropdown(name, desc, value, values, listener) { 1313 | var label = document.createElement("label"); 1314 | var description = document.createTextNode(desc); 1315 | var drop = document.createElement("select"); 1316 | 1317 | for(var k in values) { 1318 | var choice = document.createElement("option"); 1319 | choice.value = values[k]; 1320 | choice.textContent = k; 1321 | if(values[k] == value) { 1322 | choice.selected = true; 1323 | } 1324 | drop.appendChild(choice); 1325 | } 1326 | 1327 | drop.name = name; 1328 | drop.style.marginRight = "5px"; 1329 | drop.onchange = listener; 1330 | 1331 | label.appendChild(drop); 1332 | label.appendChild(description); 1333 | label.appendChild(document.createElement("br")); 1334 | return label; 1335 | } 1336 | 1337 | function makeCheckBox(name, desc, state, listener, reqRefresh) { 1338 | var asterisk = document.createElement('span'); 1339 | asterisk.className = "asterisk"; 1340 | asterisk.appendChild(document.createTextNode("*")); 1341 | 1342 | var label = document.createElement("label"); 1343 | var description = document.createTextNode(desc); 1344 | var checkbox = document.createElement("input"); 1345 | 1346 | checkbox.type = "checkbox"; 1347 | checkbox.name = name; 1348 | checkbox.checked = state; 1349 | checkbox.onclick = listener; 1350 | w[checkbox.name] = checkbox.checked; 1351 | 1352 | label.appendChild(checkbox); 1353 | label.appendChild(description); 1354 | if(reqRefresh) { 1355 | label.appendChild(asterisk); 1356 | } 1357 | label.appendChild(document.createElement("br")); 1358 | return label; 1359 | } 1360 | 1361 | function handleEvent(event) { 1362 | handleCheckBox(event); 1363 | } 1364 | 1365 | function handleCheckBox(event) { 1366 | var checkbox = event.target; 1367 | setPreference(checkbox.name, checkbox.checked); 1368 | 1369 | w[checkbox.name] = checkbox.checked; 1370 | return checkbox.checked; 1371 | } 1372 | 1373 | function toggleAutoClicker(event) { 1374 | var value = enableAutoClicker; 1375 | 1376 | if(event !== undefined) { 1377 | value = handleCheckBox(event); 1378 | } 1379 | 1380 | if(value) { 1381 | currentClickRate = clickRate; 1382 | } else { 1383 | currentClickRate = 0; 1384 | } 1385 | } 1386 | 1387 | function toggleFingering(event) { 1388 | var value = enableFingering; 1389 | 1390 | w.CSceneGame.prototype.ClearNewPlayer = function(){}; 1391 | 1392 | if(!s().m_spriteFinger) { 1393 | w.WebStorage.SetLocal('mg_how2click', 0); 1394 | s().CheckNewPlayer(); 1395 | w.WebStorage.SetLocal('mg_how2click', 1); 1396 | } 1397 | 1398 | if(event !== undefined) { 1399 | value = handleCheckBox(event); 1400 | } 1401 | 1402 | if(value) { 1403 | s().m_containerParticles.addChild(s().m_spriteFinger); 1404 | } else { 1405 | s().m_containerParticles.removeChild(s().m_spriteFinger); 1406 | } 1407 | document.getElementById('newplayer').style.display = 'none'; 1408 | } 1409 | 1410 | function toggleAutoRefresh(event) { 1411 | var value = enableAutoRefresh; 1412 | 1413 | if(event !== undefined) { 1414 | value = handleCheckBox(event); 1415 | } 1416 | 1417 | if(value) { 1418 | autoRefreshPage(autoRefreshMinutes); 1419 | } else { 1420 | clearTimeout(refreshTimer); 1421 | } 1422 | } 1423 | 1424 | function toggleRenderer(event) { 1425 | var value = disableRenderer; 1426 | 1427 | if (event !== undefined) { 1428 | value = disableRenderer = handleCheckBox(event); 1429 | } 1430 | 1431 | var ticker = w.PIXI.ticker.shared; 1432 | 1433 | if (!value) { 1434 | ticker.autoStart = true; 1435 | ticker.start(); 1436 | 1437 | w.g_Minigame.Render = trt_oldRender; 1438 | w.g_Minigame.Render(); 1439 | } else { 1440 | ticker.autoStart = false; 1441 | ticker.stop(); 1442 | 1443 | w.g_Minigame.Render = function() {}; 1444 | } 1445 | } 1446 | 1447 | var oldTvBg = ""; 1448 | function toggleChen(event) { 1449 | var value = enableChen; 1450 | 1451 | if(event !== undefined) { 1452 | value = handleCheckBox(event); 1453 | } 1454 | 1455 | enableChen = value; 1456 | 1457 | if (enableChen) { 1458 | addChen(); 1459 | honkingIntenstifys(isBossLevel(getGameLevel())); 1460 | oldTvBg = w.$J('.tv_ui').css('background-image'); 1461 | w.$J('.tv_ui').css('background-image', 'url(//i.imgur.com/9wmTsxr.png)'); 1462 | } else { 1463 | w.$J('.tv_ui').css('background-image', oldTvBg); 1464 | honkingIntenstifys(false); 1465 | } 1466 | } 1467 | 1468 | function addChen() { 1469 | var chenDiv = document.querySelector("#Chen"); 1470 | 1471 | if (!chenDiv) { // We can only handle one Chen D: 1472 | var chenHTML = document.createElement('div'); 1473 | chenHTML.id = "Chen"; 1474 | document.querySelector("#uicontainer > div.tv_ui").appendChild(chenHTML); 1475 | } 1476 | } 1477 | 1478 | function honkingIntenstifys(isBoss, hide) { 1479 | var chenDiv = document.querySelector("#Chen"); 1480 | 1481 | if (chenDiv && enableChen && isBoss) { 1482 | chenDiv.style.backgroundImage = "url(//i.imgur.com/eGnE1cD.gif)"; 1483 | } else if (chenDiv && enableChen) { 1484 | chenDiv.style.backgroundImage = "url(//i.imgur.com/xMbQChA.png)"; 1485 | } 1486 | 1487 | if (chenDiv && !enableChen) { 1488 | chenDiv.style.visibility = "hidden"; 1489 | } else if (chenDiv) { 1490 | chenDiv.style.visibility = "visible"; 1491 | } 1492 | } 1493 | 1494 | function autoRefreshPage(autoRefreshMinutes){ 1495 | var timerValue = (autoRefreshMinutes + autoRefreshMinutesRandomDelay * Math.random()) * 60 * 1000; 1496 | refreshTimer = setTimeout(function() { 1497 | autoRefreshHandler(); 1498 | }, timerValue); 1499 | } 1500 | 1501 | function autoRefreshHandler() { 1502 | 1503 | 1504 | // Only skip on % 100 levels when it's been less than the maximum delay specified. 1505 | if(getGameLevel() % 100 === 0 && autoRefreshDuringBossDelayTotal < autoRefreshDuringBossDelay) { 1506 | advLog('Not refreshing (boss level)', 5); 1507 | autoRefreshDuringBossDelayTotal += autoRefreshDuringBossDelayStep; 1508 | setTimeout(autoRefreshHandler, autoRefreshDuringBossDelayStep); 1509 | } else { 1510 | advLog('Refreshing (not a boss level)', 5); 1511 | w.location.reload(true); 1512 | } 1513 | } 1514 | 1515 | function toggleElementLock(event) { 1516 | var value = enableElementLock; 1517 | 1518 | if(event !== undefined) { 1519 | value = handleCheckBox(event); 1520 | } 1521 | 1522 | if(value) { 1523 | lockElements(); 1524 | } else { 1525 | unlockElements(); 1526 | } 1527 | } 1528 | 1529 | function toggleCritText(event) { 1530 | var value = removeCritText; 1531 | 1532 | if(event !== undefined) { 1533 | value = handleCheckBox(event); 1534 | } 1535 | 1536 | if (value) { 1537 | // Replaces the entire crit display function. 1538 | s().DoCritEffect = function() {}; 1539 | } else { 1540 | s().DoCritEffect = trt_oldCrit; 1541 | } 1542 | } 1543 | 1544 | function toggleAllText(event) { 1545 | var value = removeAllText; 1546 | 1547 | if(event !== undefined) { 1548 | value = handleCheckBox(event); 1549 | } 1550 | 1551 | if (value) { 1552 | // Replaces the entire text function. 1553 | s().m_rgClickNumbers.push = function(elem){ 1554 | elem.container.removeChild(elem); 1555 | }; 1556 | } else { 1557 | s().m_rgClickNumbers.push = trt_oldPush; 1558 | } 1559 | } 1560 | 1561 | function toggleTrackTroll(event) { 1562 | var value = enableTrollTrack; 1563 | 1564 | if(event !== undefined) { 1565 | value = handleCheckBox(event); 1566 | } 1567 | 1568 | if(value) { 1569 | CUI.prototype.UpdateLog = localUpdateLog; 1570 | } else { 1571 | CUI.prototype.UpdateLog = originalUpdateLog; 1572 | } 1573 | } 1574 | 1575 | function updateLogLevel(event) { 1576 | if(event !== undefined) { 1577 | logLevel = event.target.value; 1578 | } 1579 | } 1580 | 1581 | function setPreference(key, value) { 1582 | try { 1583 | if(localStorage !== 'undefined') { 1584 | localStorage.setItem('steamdb-minigame-wormholers/' + key, value); 1585 | } 1586 | } catch (e) { 1587 | console.log(e); // silently ignore error 1588 | } 1589 | } 1590 | 1591 | function getPreference(key, defaultValue) { 1592 | try { 1593 | if(localStorage !== 'undefined') { 1594 | var result = localStorage.getItem('steamdb-minigame-wormholers/' + key); 1595 | return (result !== null ? result : defaultValue); 1596 | } 1597 | } catch (e) { 1598 | console.log(e); // silently ignore error 1599 | return defaultValue; 1600 | } 1601 | } 1602 | 1603 | function getPreferenceBoolean(key, defaultValue) { 1604 | return (getPreference(key, defaultValue.toString()) == "true"); 1605 | } 1606 | 1607 | function unlockElements() { 1608 | var fire = document.querySelector("a.link.element_upgrade_btn[data-type=\"3\"]"); 1609 | var water = document.querySelector("a.link.element_upgrade_btn[data-type=\"4\"]"); 1610 | var air = document.querySelector("a.link.element_upgrade_btn[data-type=\"5\"]"); 1611 | var earth = document.querySelector("a.link.element_upgrade_btn[data-type=\"6\"]"); 1612 | 1613 | var elems = [fire, water, air, earth]; 1614 | 1615 | for (var i=0; i < elems.length; i++) { 1616 | elems[i].style.visibility = "visible"; 1617 | } 1618 | } 1619 | 1620 | //I'm sorry of the way I name things. This function predicts jumps on a warp boss level, returns the value. 1621 | function estimateJumps() { 1622 | var level = getGameLevel(); 1623 | var wormholesNow = 0; 1624 | //Gather total wormholes active. 1625 | for (var i = 0; i <= 2; i++) { 1626 | if (typeof w.g_Minigame.m_CurrentScene.m_rgLaneData[i].abilities[26] !== 'undefined') { 1627 | wormholesNow += w.g_Minigame.m_CurrentScene.m_rgLaneData[i].abilities[26]; 1628 | } 1629 | } 1630 | //During baws round fc 1631 | if (level % CONTROL.rainingRounds == 0) 1632 | { 1633 | if (predictLastWormholesUpdate !== wormholesNow) 1634 | { 1635 | predictTicks++; 1636 | predictJumps += wormholesNow; 1637 | predictLastWormholesUpdate = wormholesNow; 1638 | } 1639 | } 1640 | else 1641 | { 1642 | predictTicks = 0; 1643 | predictJumps = 0; 1644 | predictLastWormholesUpdate = 0; 1645 | return 0; 1646 | } 1647 | return predictJumps / predictTicks * (s().m_rgGameData.timestamp - s().m_rgGameData.timestamp_level_start); 1648 | } 1649 | 1650 | function lockElements() { 1651 | var elementMultipliers = [ 1652 | s().m_rgPlayerTechTree.damage_multiplier_fire, 1653 | s().m_rgPlayerTechTree.damage_multiplier_water, 1654 | s().m_rgPlayerTechTree.damage_multiplier_air, 1655 | s().m_rgPlayerTechTree.damage_multiplier_earth 1656 | ]; 1657 | 1658 | var elem = (parseInt(w.g_steamID.slice(-3), 10) + parseInt(w.g_GameID, 10)) % 4; 1659 | 1660 | // If more than two elements are leveled to 3 or higher, do not enable lock 1661 | var leveled = 0; 1662 | var lastLeveled = -1; 1663 | 1664 | for (var i=0; i < elementMultipliers.length; i++){ 1665 | advLog("Element " + i + " is at level " + (elementMultipliers[i]-1)/1.5, 3); 1666 | if ((elementMultipliers[i]-1)/1.5 >= 3) { 1667 | leveled++; 1668 | // Only used if there is only one so overwriting it doesn't matter 1669 | lastLeveled = i; 1670 | } 1671 | } 1672 | 1673 | if (leveled >= 2) { 1674 | advLog("More than 2 elementals leveled to 3 or above, not locking.", 1); 1675 | } else if (leveled == 1) { 1676 | advLog("Found existing lock on " + lastLeveled + ", locking to it.", 1); 1677 | lockToElement(lastLeveled); 1678 | } else { 1679 | advLog("Locking to element " + elem + " as chosen by SteamID", 1); 1680 | lockToElement(elem); 1681 | } 1682 | } 1683 | 1684 | function lockToElement(element) { 1685 | var fire = document.querySelector("a.link.element_upgrade_btn[data-type=\"3\"]"); 1686 | var water = document.querySelector("a.link.element_upgrade_btn[data-type=\"4\"]"); 1687 | var air = document.querySelector("a.link.element_upgrade_btn[data-type=\"5\"]"); 1688 | var earth = document.querySelector("a.link.element_upgrade_btn[data-type=\"6\"]"); 1689 | 1690 | var elems = [fire, water, air, earth]; 1691 | 1692 | for (var i=0; i < elems.length; i++) { 1693 | if (i === element) { 1694 | continue; 1695 | } 1696 | elems[i].style.visibility = "hidden"; 1697 | } 1698 | ELEMENTS.LockedElement = element; 1699 | } 1700 | 1701 | function displayText(x, y, strText, color) { 1702 | var text = new w.PIXI.Text(strText, {font: "35px 'Press Start 2P'", fill: color, stroke: '#000', strokeThickness: 2 }); 1703 | 1704 | text.x = x; 1705 | text.y = y; 1706 | 1707 | s().m_containerUI.addChild( text ); 1708 | text.container = s().m_containerUI; 1709 | 1710 | var e = new w.CEasingSinOut( text.y, -200, 1000 ); 1711 | e.parent = text; 1712 | text.m_easeY = e; 1713 | 1714 | e = new w.CEasingSinOut( 2, -2, 1000 ); 1715 | e.parent = text; 1716 | text.m_easeAlpha = e; 1717 | 1718 | s().m_rgClickNumbers.push(text); 1719 | } 1720 | 1721 | function updatePlayersInGame() { 1722 | var laneData = s().m_rgLaneData; 1723 | var totalPlayers = 1724 | laneData[ 0 ].players + 1725 | laneData[ 1 ].players + 1726 | laneData[ 2 ].players; 1727 | ELEMENTS.PlayersInGame.textContent = totalPlayers + "/1500"; 1728 | } 1729 | 1730 | function fixActiveCapacityUI() { 1731 | w.$J('.tv_ui').css('background-image', 'url(//i.imgur.com/9R0436k.gif)'); 1732 | w.$J('#activeinlanecontainer').css('height', '154px'); 1733 | w.$J('#activitycontainer').css('height', '270px'); 1734 | w.$J('#activityscroll').css('height', '270px'); 1735 | } 1736 | 1737 | function goToLaneWithBestTarget(level) { 1738 | // We can overlook spawners if all spawners are 40% hp or higher and a creep is under 10% hp 1739 | var spawnerOKThreshold = 0.4; 1740 | var creepSnagThreshold = 0.1; 1741 | 1742 | var targetFound = false; 1743 | var lowHP = 0; 1744 | var lowLane = 0; 1745 | var lowTarget = 0; 1746 | var lowPercentageHP = 0; 1747 | var preferredLane = -1; 1748 | var preferredTarget = -1; 1749 | 1750 | // determine which lane and enemy is the optimal target 1751 | var enemyTypePriority = [ 1752 | ENEMY_TYPE.TREASURE, 1753 | ENEMY_TYPE.BOSS, 1754 | ENEMY_TYPE.MINIBOSS, 1755 | ENEMY_TYPE.SPAWNER, 1756 | ENEMY_TYPE.CREEP 1757 | ]; 1758 | 1759 | var i; 1760 | var skippingSpawner = false; 1761 | var skippedSpawnerLane = 0; 1762 | var skippedSpawnerTarget = 0; 1763 | var targetIsTreasure = false; 1764 | var targetIsBoss = false; 1765 | 1766 | for (var k = 0; !targetFound && k < enemyTypePriority.length; k++) { 1767 | targetIsTreasure = (enemyTypePriority[k] == ENEMY_TYPE.TREASURE); 1768 | targetIsBoss = (enemyTypePriority[k] == ENEMY_TYPE.BOSS); 1769 | 1770 | var enemies = []; 1771 | 1772 | // gather all the enemies of the specified type. 1773 | for (i = 0; i < 3; i++) { 1774 | for (var j = 0; j < 4; j++) { 1775 | var enemy = s().GetEnemy(i, j); 1776 | if (enemy && enemy.m_data.type == enemyTypePriority[k]) { 1777 | enemies[enemies.length] = enemy; 1778 | } 1779 | } 1780 | } 1781 | 1782 | //Prefer lane with raining gold, unless current enemy target is a treasure or boss. 1783 | if(!targetIsTreasure && !targetIsBoss) { 1784 | var potential = 0; 1785 | // Loop through lanes by elemental preference 1786 | var sortedLanes = sortLanesByElementals(); 1787 | for(var notI = 0; notI < sortedLanes.length; notI++) { 1788 | // Maximize compability with upstream 1789 | i = sortedLanes[notI]; 1790 | // ignore if lane is empty 1791 | if(s().m_rgGameData.lanes[i].dps === 0) { 1792 | continue; 1793 | } 1794 | var stacks = 0; 1795 | if(typeof s().m_rgLaneData[i].abilities[ABILITIES.RAINING_GOLD] != 'undefined') { 1796 | stacks = s().m_rgLaneData[i].abilities[ABILITIES.RAINING_GOLD]; 1797 | advLog('[Gold rain] stacks: ' + stacks, 5); 1798 | 1799 | for(var m = 0; m < s().m_rgEnemies.length; m++){ 1800 | if(s().m_rgEnemies[m].m_nLane != i){ 1801 | continue; 1802 | } 1803 | advLog("[Gold rain] An enemy exists in raining gold lane: " + (i + 1), 5); 1804 | var enemyGold = s().m_rgEnemies[m].m_data.gold; 1805 | if(stacks * enemyGold > potential) { 1806 | potential = stacks * enemyGold; 1807 | preferredTarget = s().m_rgEnemies[m].m_nID; 1808 | preferredLane = i; 1809 | } 1810 | } 1811 | advLog("[Gold rain] preferredLane: " + preferredLane, 5); 1812 | advLog("[Gold rain] preferredTarget: " + preferredTarget, 5); 1813 | } 1814 | } 1815 | } 1816 | 1817 | // target the enemy of the specified type with the lowest hp 1818 | var mostHPDone = 0; 1819 | for (i = 0; i < enemies.length; i++) { 1820 | if (enemies[i] && !enemies[i].m_bIsDestroyed) { 1821 | // Only select enemy and lane if the preferedLane matches the potential enemy lane 1822 | if(lowHP < 1 || enemies[i].m_flDisplayedHP < lowHP) { 1823 | var element = s().m_rgGameData.lanes[enemies[i].m_nLane].element; 1824 | 1825 | var dmg = s().CalculateDamage( 1826 | s().m_rgPlayerTechTree.dps, 1827 | element 1828 | ); 1829 | 1830 | if(mostHPDone <= dmg) { 1831 | mostHPDone = dmg; 1832 | } else { 1833 | continue; 1834 | } 1835 | 1836 | targetFound = true; 1837 | lowHP = enemies[i].m_flDisplayedHP; 1838 | lowLane = enemies[i].m_nLane; 1839 | lowTarget = enemies[i].m_nID; 1840 | } 1841 | var percentageHP = enemies[i].m_flDisplayedHP / enemies[i].m_data.max_hp; 1842 | if (lowPercentageHP === 0 || percentageHP < lowPercentageHP) { 1843 | lowPercentageHP = percentageHP; 1844 | } 1845 | } 1846 | } 1847 | 1848 | if(preferredLane != -1 && preferredTarget != -1){ 1849 | lowLane = preferredLane; 1850 | lowTarget = preferredTarget; 1851 | advLog('Switching to a lane with best raining gold benefit', 2); 1852 | } 1853 | 1854 | // If we just finished looking at spawners, 1855 | // AND none of them were below our threshold, 1856 | // remember them and look for low creeps (so don't quit now) 1857 | // Don't skip spawner if lane has raining gold 1858 | if ((enemyTypePriority[k] == ENEMY_TYPE.SPAWNER && lowPercentageHP > spawnerOKThreshold) && preferredLane == -1) { 1859 | skippedSpawnerLane = lowLane; 1860 | skippedSpawnerTarget = lowTarget; 1861 | skippingSpawner = true; 1862 | targetFound = false; 1863 | } 1864 | 1865 | // If we skipped a spawner and just finished looking at creeps, 1866 | // AND the lowest was above our snag threshold, 1867 | // just go back to the spawner! 1868 | if (skippingSpawner && enemyTypePriority[k] == ENEMY_TYPE.CREEP && lowPercentageHP > creepSnagThreshold ) { 1869 | lowLane = skippedSpawnerLane; 1870 | lowTarget = skippedSpawnerTarget; 1871 | } 1872 | } 1873 | 1874 | // go to the chosen lane 1875 | if (targetFound) { 1876 | if (s().m_nExpectedLane != lowLane) { 1877 | advLog('Switching to lane' + lowLane, 3); 1878 | s().TryChangeLane(lowLane); 1879 | } 1880 | 1881 | // target the chosen enemy 1882 | if (s().m_nTarget != lowTarget) { 1883 | advLog('Switching targets', 3); 1884 | s().TryChangeTarget(lowTarget); 1885 | } 1886 | } 1887 | 1888 | var levelRainingMod = level % CONTROL.rainingRounds; 1889 | 1890 | // Prevent attack abilities and items if up against a boss or treasure minion 1891 | if (targetIsTreasure || (level < CONTROL.speedThreshold || levelRainingMod === 0 || CONTROL.rainingSafeRounds >= (CONTROL.rainingRounds - levelRainingMod))) { 1892 | BOSS_DISABLED_ABILITIES.forEach(disableAbility); 1893 | } else { 1894 | BOSS_DISABLED_ABILITIES.forEach(enableAbility); 1895 | } 1896 | 1897 | // Disable raining gold for the first levels 1898 | if(level < CONTROL.rainingRounds) { 1899 | disableAbility(ABILITIES.RAINING_GOLD); 1900 | } else { 1901 | enableAbility(ABILITIES.RAINING_GOLD); 1902 | } 1903 | 1904 | disableAbility(ABILITIES.REFLECT_DAMAGE); 1905 | disableAbility(ABILITIES.TACTICAL_NUKE); 1906 | } 1907 | 1908 | function hasMaxCriticalOnLane() { 1909 | var goodLuckCharms = getActiveAbilityLaneCount(ABILITIES.GOOD_LUCK_CHARMS); 1910 | var crit = getActiveAbilityLaneCount(ABILITIES.CRIT); 1911 | var totalCritical = goodLuckCharms + crit; 1912 | 1913 | return totalCritical >= 99; 1914 | } 1915 | 1916 | function useAbilities(level) 1917 | { 1918 | 1919 | var currentLane = s().m_nExpectedLane; 1920 | 1921 | var i = 0; 1922 | var enemyCount = 0; 1923 | var enemySpawnerExists = false; 1924 | var enemySpawnerHealthPercent = false; 1925 | var enemy = false; 1926 | var enemyBossHealthPercent = 0; 1927 | 1928 | 1929 | // If we have spare WH's fire them 1930 | enableAbility(ABILITIES.WORMHOLE); 1931 | var wormholeButton = getAbilityButton(ABILITIES.WORMHOLE); 1932 | if (getNextPredictedLevel() % 100 < 90 1933 | && wormholeButton 1934 | && wormholeButton.quantity > 15000) { 1935 | if (bHaveItem(ABILITIES.WORMHOLE)) { 1936 | triggerAbility(ABILITIES.WORMHOLE); 1937 | } 1938 | } 1939 | 1940 | // Cripple Monster 1941 | if(canUseAbility(ABILITIES.CRIPPLE_MONSTER)) { 1942 | if (level > CONTROL.speedThreshold && level % CONTROL.rainingRounds !== 0 && level % 10 === 0) { 1943 | enemy = s().GetEnemy(s().m_rgPlayerData.current_lane, s().m_rgPlayerData.target); 1944 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 1945 | enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 1946 | if (enemyBossHealthPercent>0.5){ 1947 | advLog("Cripple Monster available and used on boss", 2); 1948 | triggerAbility(ABILITIES.CRIPPLE_MONSTER); 1949 | } 1950 | } 1951 | } 1952 | } 1953 | 1954 | // Medic & Pumped Up 1955 | if (tryUsingAbility(ABILITIES.PUMPED_UP)){ 1956 | // Pumped Up is purchased, cooled down, and needed. Trigger it. 1957 | advLog('Pumped up is always good.', 2); 1958 | } 1959 | else 1960 | { 1961 | // check if Medics is purchased and cooled down 1962 | if (tryUsingAbility(ABILITIES.MEDICS)) { 1963 | advLog('BadMedic is purchased, cooled down. Trigger it.', 2); 1964 | } 1965 | 1966 | if(level > 5000 && tryUsingAbility(ABILITIES.REFLECT_DAMAGE)) { 1967 | advLog('We have reflect damage, cooled down. Trigger it.', 2); 1968 | } 1969 | else if(level > 2500 && tryUsingAbility(ABILITIES.STEAL_HEALTH)) { 1970 | advLog('We have steal health, cooled down. Trigger it.', 2); 1971 | } 1972 | else if (tryUsingAbility(ABILITIES.GOD_MODE)) { 1973 | advLog('We have god mode, cooled down. Trigger it.', 2); 1974 | } 1975 | 1976 | } 1977 | 1978 | var levelRainingMod = level % CONTROL.rainingRounds; 1979 | 1980 | if(levelRainingMod === 0) { 1981 | //advLog('Trying to rain and enable click after a while...', 1); 1982 | 1983 | tryUsingAbility(ABILITIES.DECREASE_COOLDOWNS, true); 1984 | tryUsingAbility(ABILITIES.RAINING_GOLD); 1985 | } 1986 | 1987 | // Skip doing any damage x levels before upcoming wormhole round 1988 | if(CONTROL.rainingSafeRounds >= (CONTROL.rainingRounds - levelRainingMod)) { 1989 | tryUsingAbility(ABILITIES.RESURRECTION, true); 1990 | 1991 | return; 1992 | } 1993 | 1994 | // Good Luck Charms / Crit 1995 | if(!hasMaxCriticalOnLane()) 1996 | { 1997 | if (tryUsingAbility(ABILITIES.CRIT)){ 1998 | // Crits is purchased, cooled down, and needed. Trigger it. 1999 | advLog('Crit chance is always good.', 3); 2000 | } 2001 | } 2002 | if(!hasMaxCriticalOnLane()) 2003 | { 2004 | // check if Good Luck Charms is purchased and cooled down 2005 | if (tryUsingAbility(ABILITIES.GOOD_LUCK_CHARMS)) { 2006 | advLog('Good Luck Charms is purchased, cooled down, and needed. Trigger it.', 2); 2007 | } 2008 | } 2009 | 2010 | // Cluster Bomb 2011 | if (canUseAbility(ABILITIES.CLUSTER_BOMB)) { 2012 | //Check lane has monsters to explode 2013 | enemyCount = 0; 2014 | enemySpawnerExists = false; 2015 | //Count each slot in lane 2016 | for (i = 0; i < 4; i++) { 2017 | enemy = s().GetEnemy(currentLane, i); 2018 | if (enemy) { 2019 | enemyCount++; 2020 | if (enemy.m_data.type === 0) { 2021 | enemySpawnerExists = true; 2022 | } 2023 | } 2024 | } 2025 | //Bombs away if spawner and 2+ other monsters 2026 | if (enemySpawnerExists && enemyCount >= 3) { 2027 | if (!tryUsingAbility(ABILITIES.DECREASE_COOLDOWNS, true)) { 2028 | triggerAbility(ABILITIES.CLUSTER_BOMB); 2029 | } 2030 | } 2031 | } 2032 | 2033 | // Napalm 2034 | if (canUseAbility(ABILITIES.NAPALM)) { 2035 | //Check lane has monsters to burn 2036 | enemyCount = 0; 2037 | enemySpawnerExists = false; 2038 | //Count each slot in lane 2039 | for (i = 0; i < 4; i++) { 2040 | enemy = s().GetEnemy(currentLane, i); 2041 | if (enemy) { 2042 | enemyCount++; 2043 | if (enemy.m_data.type === 0) { 2044 | enemySpawnerExists = true; 2045 | } 2046 | } 2047 | } 2048 | //Burn them all if spawner and 2+ other monsters 2049 | if (enemySpawnerExists && enemyCount >= 3) { 2050 | if (!tryUsingAbility(ABILITIES.DECREASE_COOLDOWNS, true)) { 2051 | triggerAbility(ABILITIES.NAPALM); 2052 | } 2053 | } 2054 | } 2055 | 2056 | // Morale Booster 2057 | if (canUseAbility(ABILITIES.MORALE_BOOSTER)) { 2058 | var numberOfWorthwhileEnemies = 0; 2059 | for(i = 0; i < s().m_rgGameData.lanes[s().m_nExpectedLane].enemies.length; i++) { 2060 | //Worthwhile enemy is when an enamy has a current hp value of at least 1,000,000 2061 | if(s().m_rgGameData.lanes[s().m_nExpectedLane].enemies[i].hp > 1000000) { 2062 | numberOfWorthwhileEnemies++; 2063 | } 2064 | } 2065 | 2066 | if(numberOfWorthwhileEnemies >= 2) { 2067 | // Moral Booster is purchased, cooled down, and needed. Trigger it. 2068 | advLog('Moral Booster is purchased, cooled down, and needed. Trigger it.', 2); 2069 | triggerAbility(ABILITIES.MORALE_BOOSTER); 2070 | } 2071 | } 2072 | 2073 | // Tactical Nuke 2074 | if(canUseAbility(ABILITIES.TACTICAL_NUKE) && (level % 100 !== 0) && (level % 100) <= 97) { 2075 | enemy = s().GetEnemy(s().m_rgPlayerData.current_lane, s().m_rgPlayerData.target); 2076 | // check whether current target is a boss 2077 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 2078 | if (level >= CONTROL.speedThreshold) { // Start nuking bosses at level CONTROL.speedThreshold 2079 | enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 2080 | 2081 | // Use Nuke on boss with >= 50% HP but only if Raining Gold is not active in the lane 2082 | if (enemyBossHealthPercent >= 0.5 && getActiveAbilityLaneCount(ABILITIES.RAINING_GOLD) <= 0) { 2083 | if (!tryUsingAbility(ABILITIES.DECREASE_COOLDOWNS, true)) { 2084 | advLog("Tactical Nuke is purchased, cooled down, and needed. Nuke 'em.", 2); 2085 | triggerAbility(ABILITIES.TACTICAL_NUKE); 2086 | } 2087 | } 2088 | } 2089 | } 2090 | else { 2091 | //Check that the lane has a spawner and record it's health percentage 2092 | //Count each slot in lane 2093 | for (i = 0; i < 4; i++) { 2094 | enemy = s().GetEnemy(currentLane, i); 2095 | if (enemy && enemy.m_data.type == ENEMY_TYPE.SPAWNER) { 2096 | enemySpawnerHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 2097 | // If there is a spawner and it's health is between 60% and 30%, nuke it! 2098 | if (enemySpawnerHealthPercent < 0.6 && enemySpawnerHealthPercent > 0.3) { 2099 | if (!tryUsingAbility(ABILITIES.DECREASE_COOLDOWNS, true)) { 2100 | advLog("Tactical Nuke is purchased, cooled down, and needed. Nuke 'em.", 2); 2101 | triggerAbility(ABILITIES.TACTICAL_NUKE); 2102 | } 2103 | } 2104 | break; // No reason to continue the loop after finding a spawner 2105 | } 2106 | } 2107 | } 2108 | } 2109 | 2110 | // Cripple Spawner 2111 | if(canUseAbility(ABILITIES.CRIPPLE_SPAWNER)) { 2112 | //Check that the lane has a spawner and record it's health percentage 2113 | enemySpawnerExists = false; 2114 | enemySpawnerHealthPercent = 0.0; 2115 | //Count each slot in lane 2116 | for (i = 0; i < 4; i++) { 2117 | enemy = s().GetEnemy(currentLane, i); 2118 | if (enemy) { 2119 | if (enemy.m_data.type === 0) { 2120 | enemySpawnerExists = true; 2121 | enemySpawnerHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 2122 | } 2123 | } 2124 | } 2125 | 2126 | // If there is a spawner and it's health is above 95%, cripple it! 2127 | if (enemySpawnerExists && enemySpawnerHealthPercent > 0.95) { 2128 | advLog("Cripple Spawner available, and needed. Cripple 'em.", 2); 2129 | triggerAbility(ABILITIES.CRIPPLE_SPAWNER); 2130 | } 2131 | } 2132 | 2133 | // Gold Rain 2134 | if (canUseAbility(ABILITIES.RAINING_GOLD)) { 2135 | // only use if the speed threshold has not been reached, 2136 | // or it's a designated gold round after the threshold 2137 | if (level > CONTROL.disableGoldRainLevels && (level < CONTROL.speedThreshold || level % CONTROL.rainingRounds === 0)) { 2138 | enemy = s().GetEnemy(s().m_rgPlayerData.current_lane, s().m_rgPlayerData.target); 2139 | // check if current target is a boss, otherwise its not worth using the gold rain 2140 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 2141 | enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 2142 | 2143 | if (enemyBossHealthPercent >= 0.6 || level % CONTROL.rainingRounds === 0) { // We want sufficient time for the gold rain to be applicable 2144 | // Gold Rain is purchased, cooled down, and needed. Trigger it. 2145 | advLog('Gold rain is purchased and cooled down, Triggering it on boss', 2); 2146 | triggerAbility(ABILITIES.RAINING_GOLD); 2147 | } 2148 | } 2149 | } 2150 | } 2151 | 2152 | // Metal Detector 2153 | if(canUseAbility(ABILITIES.METAL_DETECTOR)) { 2154 | 2155 | enemy = s().GetEnemy(s().m_rgPlayerData.current_lane, s().m_rgPlayerData.target); 2156 | // check if current target is a boss, otherwise we won't use metal detector 2157 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 2158 | enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 2159 | 2160 | // we want to use metal detector at 25% hp, or even less 2161 | if (enemyBossHealthPercent <= 0.25) { // We want sufficient time for the metal detector to be applicable 2162 | // Metal Detector is purchased, cooled down, and needed. Trigger it. 2163 | advLog('Metal Detector is purchased and cooled down, Triggering it on boss', 2); 2164 | triggerAbility(ABILITIES.METAL_DETECTOR); 2165 | } 2166 | } 2167 | } 2168 | 2169 | // Treasure 2170 | if (canUseAbility(ABILITIES.TREASURE)) { 2171 | 2172 | // check if current level is higher than 50 2173 | if (level > 50) { 2174 | enemy = s().GetTargetedEnemy(); 2175 | // check if current target is a boss, otherwise we won't use metal detector 2176 | if (enemy && enemy.type == ENEMY_TYPE.BOSS) { 2177 | enemyBossHealthPercent = enemy.hp / enemy.max_hp; 2178 | 2179 | // we want to use Treasure at 25% hp, or even less 2180 | if (enemyBossHealthPercent <= 0.25) { // We want sufficient time for the metal detector to be applicable 2181 | // Treasure is purchased, cooled down, and needed. Trigger it. 2182 | advLog('Treasure is purchased and cooled down, triggering it.', 2); 2183 | triggerAbility(ABILITIES.TREASURE); 2184 | } 2185 | } 2186 | } 2187 | else { 2188 | // Treasure is purchased, cooled down, and needed. Trigger it. 2189 | advLog('Treasure is purchased and cooled down, triggering it.', 2); 2190 | triggerAbility(ABILITIES.TREASURE); 2191 | } 2192 | } 2193 | 2194 | // Max Elemental 2195 | if (tryUsingAbility(ABILITIES.MAX_ELEMENTAL_DAMAGE, true)) { 2196 | // Max Elemental Damage is purchased, cooled down, and needed. Trigger it. 2197 | advLog('Max Elemental Damage is purchased and cooled down, triggering it.', 2); 2198 | } 2199 | 2200 | 2201 | 2202 | // Resurrect 2203 | if(level % 10 === 9 && tryUsingAbility(ABILITIES.RESURRECTION)) { 2204 | // Resurrect is purchased and we are using it. 2205 | advLog('Triggered Resurrect.'); 2206 | } 2207 | } 2208 | 2209 | function attemptRespawn() { 2210 | if ((s().m_bIsDead) && ((s().m_rgPlayerData.time_died) + 5) < (s().m_nTime)) { 2211 | w.RespawnPlayer(); 2212 | } 2213 | } 2214 | 2215 | function bHaveItem(itemId) { 2216 | var items = s().m_rgPlayerTechTree.ability_items; 2217 | 2218 | for(var i = 0; i < items.length; ++i) { 2219 | if(items[i].ability == itemId) { 2220 | return true; 2221 | } 2222 | } 2223 | 2224 | return false; 2225 | } 2226 | 2227 | function canUseAbility(abilityId, forceAbility) { 2228 | if(!s().bHaveAbility(abilityId) && !bHaveItem(abilityId)) { 2229 | return false; 2230 | } 2231 | 2232 | return s().GetCooldownForAbility(abilityId) <= 0 && (isAbilityEnabled(abilityId) || forceAbility); 2233 | } 2234 | 2235 | function tryUsingAbility(itemId, checkInLane, forceAbility) { 2236 | if (!canUseAbility(itemId, forceAbility)) { 2237 | return false; 2238 | } 2239 | 2240 | if (checkInLane && getActiveAbilityTimeout(itemId) >= getCurrentTime()) { 2241 | return false; 2242 | } 2243 | 2244 | var level = getGameLevel(); 2245 | var levelsPer = levelsPerSec(); 2246 | var needs_to_be_blocked = false; 2247 | var two_digit_level = level % 100; 2248 | 2249 | var needs_to_be_blocked = (BOSS_DISABLED_ABILITIES.indexOf(itemId) != -1); 2250 | 2251 | // must not use any damaging ability on boss levels 2252 | if (two_digit_level == 0 && needs_to_be_blocked) { 2253 | return false; 2254 | } 2255 | 2256 | // don't let good luck charm run up to x100 levels 2257 | if (itemId === ABILITIES.GOOD_LUCK_CHARMS && two_digit_level + levelsPer * 20 > 100) { 2258 | return false; 2259 | } 2260 | 2261 | // Randomly Don't use this ability when we're getting close to the boss 2262 | // This avoids overflow damage 2263 | if (two_digit_level > 50 && needs_to_be_blocked) { 2264 | // Calculate current ability usage rate 2265 | var nextTickLevel = Math.ceil(level + levelsPer); 2266 | var nextWHLevel = Math.ceil(nextTickLevel / 100)*100; 2267 | var abilityRate = Math.min( 1, Math.sqrt( nextWHLevel - nextTickLevel )/10 + minAbilityUsePercent ); 2268 | 2269 | if( Math.random() < (1 - abilityRate) ) { 2270 | advLog('Rate limited ability - not using'); 2271 | return false; 2272 | } 2273 | } 2274 | 2275 | triggerAbility(itemId); 2276 | 2277 | return true; 2278 | } 2279 | 2280 | function getAbilityButton(ability) { 2281 | return s().m_rgPlayerTechTree.ability_items.filter(function(item) { return item.ability === ability; })[0] 2282 | } 2283 | 2284 | function triggerAbility(abilityId) { 2285 | if (abilityId === ABILITIES.WORMHOLE) { 2286 | // Fire this bad boy off immediately 2287 | g_Server.UseAbilities(function() { 2288 | var wormholeButton = getAbilityButton(ABILITIES.WORMHOLE); 2289 | if (wormholeButton) { 2290 | wormholeButton.quantity--; 2291 | } 2292 | }, 2293 | function() { 2294 | advLog('lost a wormhole :(', 2); 2295 | }, {requested_abilities: [{ability: ABILITIES.WORMHOLE}]}); 2296 | } else { 2297 | s().m_rgAbilityQueue.push({'ability': abilityId}); 2298 | } 2299 | 2300 | var nCooldownDuration = s().m_rgTuningData.abilities[abilityId].cooldown; 2301 | s().ClientOverride('ability', abilityId, Math.floor(Date.now() / 1000) + nCooldownDuration); 2302 | s().ApplyClientOverrides('ability', true); 2303 | } 2304 | 2305 | function toggleAbilityVisibility(abilityId, show) { 2306 | var vis = show === true ? "visible" : "hidden"; 2307 | 2308 | var elem = document.getElementById('ability_' + abilityId); 2309 | 2310 | // temporary 2311 | if(!elem) { 2312 | elem = document.getElementById('abilityitem_' + abilityId); 2313 | } 2314 | 2315 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 2316 | elem.childElements()[0].style.visibility = vis; 2317 | } 2318 | } 2319 | 2320 | function disableAbility(abilityId) { 2321 | toggleAbilityVisibility(abilityId, false); 2322 | } 2323 | 2324 | function enableAbility(abilityId) { 2325 | toggleAbilityVisibility(abilityId, true); 2326 | } 2327 | 2328 | function isAbilityEnabled(abilityId) { 2329 | var elem = document.getElementById('ability_' + abilityId); 2330 | 2331 | // temporary 2332 | if(!elem) { 2333 | elem = document.getElementById('abilityitem_' + abilityId); 2334 | } 2335 | 2336 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 2337 | return elem.childElements()[0].style.visibility !== "hidden"; 2338 | } 2339 | return false; 2340 | } 2341 | 2342 | function getActiveAbilityTimeout(ability) { 2343 | var timeout = 0; 2344 | var abilities = s().m_rgGameData.lanes[s().m_rgPlayerData.current_lane].active_player_abilities; 2345 | var count = 0; 2346 | for(var i = 0; i < abilities.length; i++) { 2347 | if(abilities[i].ability == ability && abilities[i].timestamp_done > timeout) { 2348 | timeout = abilities[i].timestamp_done; 2349 | } 2350 | } 2351 | return timeout; 2352 | } 2353 | 2354 | function getActiveAbilityLaneCount(ability) { 2355 | var now = getCurrentTime(); 2356 | var abilities = s().m_rgGameData.lanes[s().m_rgPlayerData.current_lane].active_player_abilities; 2357 | var count = 0; 2358 | for(var i = 0; i < abilities.length; i++) { 2359 | if(abilities[i].ability == ability && abilities[i].timestamp_done > now) { 2360 | count++; 2361 | } 2362 | } 2363 | return count; 2364 | } 2365 | 2366 | function sortLanesByElementals() { 2367 | var elementPriorities = [ 2368 | s().m_rgPlayerTechTree.damage_multiplier_fire, 2369 | s().m_rgPlayerTechTree.damage_multiplier_water, 2370 | s().m_rgPlayerTechTree.damage_multiplier_air, 2371 | s().m_rgPlayerTechTree.damage_multiplier_earth 2372 | ]; 2373 | 2374 | var lanes = s().m_rgGameData.lanes; 2375 | var lanePointers = []; 2376 | 2377 | for (var i = 0; i < lanes.length; i++) { 2378 | lanePointers[i] = i; 2379 | } 2380 | 2381 | lanePointers.sort(function(a, b) { 2382 | return elementPriorities[lanes[b].element - 1] - elementPriorities[lanes[a].element - 1]; 2383 | }); 2384 | 2385 | advLog("Lane IDs : " + lanePointers[0] + " " + lanePointers[1] + " " + lanePointers[2], 4); 2386 | advLog("Elements : " + lanes[lanePointers[0]].element + " " + lanes[lanePointers[1]].element + " " + lanes[lanePointers[2]].element, 4); 2387 | 2388 | return lanePointers; 2389 | } 2390 | 2391 | function getCurrentTime() { 2392 | return s().m_rgGameData.timestamp; 2393 | } 2394 | 2395 | function advLog(msg, lvl) { 2396 | if (lvl <= logLevel) { 2397 | console.log(msg); 2398 | } 2399 | } 2400 | 2401 | if(w.SteamDB_Minigame_Timer) { 2402 | w.clearInterval(w.SteamDB_Minigame_Timer); 2403 | } 2404 | 2405 | w.SteamDB_Minigame_Timer = w.setInterval(function(){ 2406 | if (w.g_Minigame 2407 | && s().m_bRunning 2408 | && s().m_rgPlayerTechTree 2409 | && s().m_rgGameData) { 2410 | w.clearInterval(w.SteamDB_Minigame_Timer); 2411 | firstRun(); 2412 | w.SteamDB_Minigame_Timer = w.setInterval(MainLoop, 1000); 2413 | } 2414 | }, 1000); 2415 | 2416 | // reload page if game isn't fully loaded, regardless of autoRefresh setting 2417 | w.setTimeout(function() { 2418 | // m_rgGameData is 'undefined' if stuck at 97/97 or below 2419 | if (!w.g_Minigame 2420 | || !w.g_Minigame.m_CurrentScene 2421 | || !w.g_Minigame.m_CurrentScene.m_rgGameData) { 2422 | w.location.reload(true); 2423 | } 2424 | }, autoRefreshSecondsCheckLoadedDelay * 1000); 2425 | 2426 | appendBreadcrumbsTitleInfo(); 2427 | 2428 | function enhanceTooltips() { 2429 | var trt_oldTooltip = w.fnTooltipUpgradeDesc; 2430 | 2431 | w.fnTooltipUpgradeDesc = function(context){ 2432 | var $context = w.$J(context); 2433 | var desc = $context.data('desc'); 2434 | var strOut = desc; 2435 | var multiplier = parseFloat( $context.data('multiplier') ); 2436 | switch( $context.data('upgrade_type') ) { 2437 | case 2: // Type for click damage. All tiers. 2438 | strOut = trt_oldTooltip(context); 2439 | var currentCritMultiplier = s().m_rgPlayerTechTree.damage_multiplier_crit; 2440 | var currentCrit = s().m_rgPlayerTechTree.damage_per_click * currentCritMultiplier; 2441 | var newCrit = s().m_rgTuningData.player.damage_per_click * (s().m_rgPlayerTechTree.damage_per_click_multiplier + multiplier) * currentCritMultiplier; 2442 | strOut += '

Crit Click: ' + w.FormatNumberForDisplay( currentCrit ) + ' => ' + w.FormatNumberForDisplay( newCrit ); 2443 | break; 2444 | case 7: // Lucky Shot's type. 2445 | var currentMultiplier = s().m_rgPlayerTechTree.damage_multiplier_crit; 2446 | var newMultiplier = currentMultiplier + multiplier; 2447 | var dps = s().m_rgPlayerTechTree.dps; 2448 | var clickDamage = s().m_rgPlayerTechTree.damage_per_click; 2449 | 2450 | strOut += '

You can have multiple crits in a second. The server combines them into one.'; 2451 | 2452 | strOut += '

Crit Percentage: ' + (s().m_rgPlayerTechTree.crit_percentage * 100).toFixed(1) + '%'; 2453 | 2454 | strOut += '

Critical Damage Multiplier:'; 2455 | strOut += '
Current: ' + ( currentMultiplier ) + 'x'; 2456 | strOut += '
Next Level: ' + ( newMultiplier ) + 'x'; 2457 | 2458 | strOut += '

Damage with one crit:'; 2459 | strOut += '
DPS: ' + w.FormatNumberForDisplay( currentMultiplier * dps ) + ' => ' + w.FormatNumberForDisplay( newMultiplier * dps ); 2460 | strOut += '
Click: ' + w.FormatNumberForDisplay( currentMultiplier * clickDamage ) + ' => ' + w.FormatNumberForDisplay( newMultiplier * clickDamage ); 2461 | strOut += '

Base Increased By: ' + w.FormatNumberForDisplay(multiplier) + 'x'; 2462 | break; 2463 | case 9: // Boss Loot Drop's type 2464 | var bossLootChance = s().m_rgPlayerTechTree.boss_loot_drop_percentage * 100; 2465 | 2466 | strOut += '

Boss Loot Drop Rate:'; 2467 | strOut += '
Current: ' + bossLootChance.toFixed(0) + '%'; 2468 | strOut += '
Next Level: ' + (bossLootChance + multiplier * 100).toFixed(0) + '%'; 2469 | strOut += '

Base Increased By: ' + w.FormatNumberForDisplay(multiplier * 100) + '%'; 2470 | break; 2471 | default: 2472 | return trt_oldTooltip(context); 2473 | } 2474 | 2475 | return strOut; 2476 | }; 2477 | 2478 | var trt_oldElemTooltip = w.fnTooltipUpgradeElementDesc; 2479 | w.fnTooltipUpgradeElementDesc = function (context) { 2480 | var strOut = trt_oldElemTooltip(context); 2481 | 2482 | var $context = w.$J(context); 2483 | //var upgrades = s().m_rgTuningData.upgrades.slice(0); 2484 | // Element Upgrade index 3 to 6 2485 | var idx = $context.data('type'); 2486 | // Is the current tooltip for the recommended element? 2487 | var isRecommendedElement = (ELEMENTS.LockedElement == idx - 3); 2488 | 2489 | if (isRecommendedElement){ 2490 | strOut += "

This is your recommended element. Please upgrade this."; 2491 | 2492 | if (w.enableElementLock){ 2493 | strOut += "

Other elements are LOCKED to prevent accidentally upgrading."; 2494 | } 2495 | 2496 | } else if (-1 != ELEMENTS.LockedElement) { 2497 | strOut += "

This is NOT your recommended element. DO NOT upgrade this."; 2498 | } 2499 | 2500 | return strOut; 2501 | }; 2502 | } 2503 | 2504 | function getGameLevel() { 2505 | return s().m_rgGameData.level + 1; 2506 | } 2507 | 2508 | function countdown(time) { 2509 | var hours = 0; 2510 | var minutes = 0; 2511 | for (var i = 0; i < 24; i++) { 2512 | if (time >= 3600) { 2513 | time = time - 3600; 2514 | hours = hours + 1; 2515 | } 2516 | } 2517 | for (var j = 0; j < 60; j++) { 2518 | if (time >= 60) { 2519 | time = time - 60; 2520 | minutes = minutes + 1; 2521 | } 2522 | } 2523 | return {hours : hours, minutes : minutes}; 2524 | } 2525 | 2526 | function expectedLevel(level) { 2527 | var time = Math.floor(s().m_nTime) % 86400; 2528 | time = time - 16*3600; 2529 | if (time < 0) { 2530 | time = time + 86400; 2531 | } 2532 | 2533 | var remaining_time = 86400 - time; 2534 | var passed_time = getCurrentTime() - s().m_rgGameData.timestamp_game_start; 2535 | var expected_level = Math.floor(((level/passed_time)*remaining_time)+level); 2536 | var likely_level = Math.floor((expected_level - level)/Math.log(3))+ level; 2537 | 2538 | return {expected_level : expected_level, likely_level : likely_level, remaining_time : remaining_time}; 2539 | } 2540 | 2541 | function appendBreadcrumbsTitleInfo() { 2542 | var breadcrumbs = document.querySelector('.breadcrumbs'); 2543 | 2544 | if(!breadcrumbs) { 2545 | return; 2546 | } 2547 | 2548 | var element = document.createElement('span'); 2549 | element.textContent = ' > '; 2550 | breadcrumbs.appendChild(element); 2551 | 2552 | element = document.createElement('span'); 2553 | element.className = "bc_span bc_room"; 2554 | element.textContent = 'Room ' + w.g_GameID; 2555 | breadcrumbs.appendChild(element); 2556 | 2557 | element = document.createElement('span'); 2558 | element.textContent = ' > '; 2559 | breadcrumbs.appendChild(element); 2560 | 2561 | element = document.createElement('span'); 2562 | element.className = "bc_span bc_level"; 2563 | element.textContent = 'Level: 0'; 2564 | breadcrumbs.appendChild(element); 2565 | ELEMENTS.ExpectedLevel = element; 2566 | 2567 | element = document.createElement('span'); 2568 | element.textContent = ' > '; 2569 | breadcrumbs.appendChild(element); 2570 | 2571 | element = document.createElement('span'); 2572 | element.className = "bc_span bc_time"; 2573 | element.textContent = 'Remaining Time: 0 hours, 0 minutes'; 2574 | breadcrumbs.appendChild(element); 2575 | ELEMENTS.RemainingTime = element; 2576 | 2577 | element = document.createElement('span'); 2578 | element.textContent = ' > '; 2579 | breadcrumbs.appendChild(element); 2580 | 2581 | element = document.createElement('span'); 2582 | element.className = "bc_span bc_worms"; 2583 | element.textContent = 'Wormhole Activity: 0'; 2584 | breadcrumbs.appendChild(element); 2585 | ELEMENTS.WormholesJumped = element; 2586 | } 2587 | 2588 | function updateLevelInfoTitle(level) 2589 | { 2590 | var exp_lvl = expectedLevel(level); 2591 | var rem_time = countdown(exp_lvl.remaining_time); 2592 | 2593 | ELEMENTS.ExpectedLevel.textContent = 'Level: ' + level + 2594 | ', Levels/second: ' + Math.round(levelsPerSec() * 1000) / 1000 + 2595 | ', Prediction Accuracy: ' + Math.round(getPredictedLevelAccuracy() * 100) + "%" + 2596 | ', Version Levels/s: ' + Math.round(getLevelsPerSecondSinceStart()) + 2597 | ', WH/Boss: ' + Math.round(averageWormholesPerBoss()) + 2598 | ', Seconds/Boss\': ' + Math.round(1 / averageBossesPerSecond()) 2599 | ELEMENTS.RemainingTime.textContent = 'Remaining Time: ' + rem_time.hours + ' hours, ' + rem_time.minutes + ' minutes'; 2600 | ELEMENTS.WormholesJumped.textContent = 'Wormhole Activity: ' + (skipsLastJump.toLocaleString ? skipsLastJump.toLocaleString() : skipsLastJump); 2601 | } 2602 | 2603 | }(window)); 2604 | -------------------------------------------------------------------------------- /leaderboard.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Monster Minigame Leaderboard 3 | // @namespace https://github.com/SteamDatabase/steamSummerMinigame 4 | // @version 1.0 5 | // @description Display leaderboards from http://steamga.me 6 | // @match *://steamcommunity.com/minigame/towerattack* 7 | // @match *://steamcommunity.com//minigame/towerattack* 8 | // @grant GM_xmlhttpRequest 9 | // @updateURL https://raw.githubusercontent.com/SteamDatabase/steamSummerMinigame/master/leaderboard.user.js 10 | // @downloadURL https://raw.githubusercontent.com/SteamDatabase/steamSummerMinigame/master/leaderboard.user.js 11 | // ==/UserScript== 12 | //NOTE: This REQUIRES the use of GreaseMonkey or TamperMonkey 13 | 14 | (function(w) { 15 | "use strict"; 16 | 17 | function initLeaderboard() { 18 | 19 | var container = document.createElement('div'); 20 | container.id = 'leaderboard_wrapper'; 21 | container.style.overflow = "hidden"; 22 | container.style.height = "360px"; 23 | container.style.width = "261px"; 24 | container.style.display = "none"; 25 | container.style.position = "relative"; 26 | container.style.margin = "50px 0 0 5px"; 27 | container.style.padding = "5px"; 28 | 29 | document.getElementById('col_right').appendChild(container); 30 | 31 | var leaderboard = document.createElement('table'); 32 | leaderboard.id = 'leaderboard'; 33 | 34 | var th = document.createElement('tr'); 35 | th.style.fontSize = '11px'; 36 | th.style.color = '#ddd'; 37 | 38 | var thc = document.createElement('th'); 39 | var thn = document.createElement('th'); 40 | var thl = document.createElement('th'); 41 | thc.appendChild(document.createTextNode('Rank')); 42 | thn.appendChild(document.createTextNode('Name')); 43 | thl.appendChild(document.createTextNode('Level')); 44 | 45 | thn.style.textAlign = "center"; 46 | 47 | th.appendChild(thc); 48 | th.appendChild(thn); 49 | th.appendChild(thl); 50 | 51 | leaderboard.appendChild(th); 52 | 53 | document.getElementById('leaderboard_wrapper').appendChild(leaderboard); 54 | 55 | var credit = document.createElement('div'); 56 | credit.style.fontSize = "12px"; 57 | credit.style.textAlign = "center"; 58 | credit.innerHTML = 'Data by steamga.me'; 59 | 60 | document.getElementById('leaderboard_wrapper').appendChild(credit); 61 | 62 | var toggler = document.createElement('div'); 63 | toggler.id = "leaderboard_toggler"; 64 | toggler.onclick = toggleLeaderboard; 65 | toggler.style.position = 'absolute'; 66 | toggler.style.bottom = "-48px"; 67 | toggler.style.color = "black"; 68 | toggler.style.textAlign = "center"; 69 | toggler.style.width = '261px'; 70 | toggler.style.cursor = "pointer"; 71 | toggler.appendChild(document.createTextNode("Show Leaderboards")); 72 | 73 | document.getElementById('col_right').appendChild(toggler); 74 | 75 | if (!w.Leaderboard_Timer) { 76 | clearInterval(w.Leaderboard_Timer); 77 | } 78 | w.Leaderboard_Timer = setInterval(getLeaderboard, 1000 * 30); 79 | 80 | getLeaderboard(); 81 | } 82 | 83 | function drawLeaderboardRoom(room) { 84 | var item = document.createElement('tr'); 85 | item.className = 'leaderboard_item'; 86 | item.style.height = '23px'; 87 | item.style.fontSize = '10px'; 88 | 89 | var num = document.createElement('td'); 90 | num.appendChild(document.createTextNode('#' + room.position)); 91 | 92 | var name = document.createElement('td'); 93 | name.style.textAlign = 'center'; 94 | name.appendChild(document.createTextNode(room.name)); 95 | 96 | var level = document.createElement('td'); 97 | level.style.textAlign = 'right'; 98 | level.appendChild(document.createTextNode(room.level)); 99 | 100 | if(room.id == w.g_GameID) 101 | { 102 | item.style.color = '#d4e157'; 103 | } 104 | 105 | item.appendChild(num); 106 | item.appendChild(name); 107 | item.appendChild(level); 108 | 109 | document.getElementById('leaderboard').appendChild(item); 110 | } 111 | 112 | function getLeaderboard() { 113 | GM_xmlhttpRequest({ 114 | method: "GET", 115 | url: "http://steamga.me/data/api/leaderboard.json", 116 | onload: function(response) { 117 | console.log('Downloading new leaderboard...'); 118 | var elements = document.getElementsByClassName('leaderboard_item'); 119 | while(elements.length > 0){ 120 | elements[0].parentNode.removeChild(elements[0]); 121 | } 122 | var resp = JSON.parse(response.responseText); 123 | var leaderboard = Object.keys(resp).map(function (key) { 124 | return resp[key]; 125 | }); 126 | leaderboard.sort(function(a, b) { 127 | return b.level - a.level; 128 | }); 129 | leaderboard.forEach(drawLeaderboardRoom); 130 | } 131 | }); 132 | } 133 | 134 | function toggleLeaderboard() { 135 | var a = document.getElementById('leaderboard_wrapper'); 136 | var b = document.getElementById('activitylog'); 137 | var c = document.getElementById('leaderboard_toggler'); 138 | if (a.style.display == 'block') { 139 | a.style.display = 'none'; 140 | b.style.display = 'block'; 141 | c.innerHTML = "Show Leaderboards"; 142 | } else { 143 | a.style.display = 'block'; 144 | b.style.display = 'none'; 145 | c.innerHTML = "Show Activity"; 146 | } 147 | } 148 | 149 | initLeaderboard(); 150 | }(window)); 151 | -------------------------------------------------------------------------------- /tuningData.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1434557096, 3 | "player": { 4 | "respawn_time": 60, 5 | "min_dead_time": 5, 6 | "hp": 1000, 7 | "dps": 0, 8 | "gold_multiplier_while_dead": "0.75", 9 | "damage_per_click": 10, 10 | "damage_multiplier_fire": 1, 11 | "damage_multiplier_water": 1, 12 | "damage_multiplier_air": 1, 13 | "damage_multiplier_earth": 1, 14 | "damage_multiplier_crit": 2, 15 | "crit_percentage": "0.1", 16 | "loot_chance": "0.25", 17 | "start_condition_minigame_badge": { 18 | "badge_points_per_level_factor": "0.1" 19 | }, 20 | "start_condition_summersale_badge": { 21 | "badge_points_per_level_factor": 1 22 | }, 23 | "start_condition_summersale_foil_badge": { 24 | "badge_points_per_level_factor": 10 25 | } 26 | }, 27 | "tower": { 28 | "respawn_time": 30, 29 | "hp": 60000, 30 | "dps": "1.25", 31 | "gold": 100, 32 | "hp_multiplier": "1.2", 33 | "hp_exponent": "1.5", 34 | "dps_multiplier": 1, 35 | "dps_exponent": "1.2", 36 | "gold_multiplier": "1.6", 37 | "gold_exponent": "1.1" 38 | }, 39 | "mob": { 40 | "hp": 5000, 41 | "dps": 1, 42 | "gold": 30, 43 | "hp_multiplier": "1.2", 44 | "hp_multiplier_variance": "0.2", 45 | "hp_exponent": "1.5", 46 | "dps_multiplier": 1, 47 | "dps_exponent": "1.2", 48 | "gold_multiplier": "1.2", 49 | "gold_exponent": "1.02" 50 | }, 51 | "miniboss": { 52 | "respawn_time": 900, 53 | "hp": 60000, 54 | "dps": "1.25", 55 | "gold": 100, 56 | "hp_multiplier": "1.2", 57 | "hp_exponent": "1.5", 58 | "dps_multiplier": 1, 59 | "dps_exponent": "1.2", 60 | "gold_multiplier": "1.7", 61 | "gold_exponent": "1.1" 62 | }, 63 | "boss": { 64 | "hp": 260000, 65 | "dps": 3, 66 | "gold": 500, 67 | "hp_multiplier": "1.5", 68 | "hp_exponent": 3, 69 | "dps_multiplier": 1, 70 | "dps_exponent": "1.2", 71 | "gold_multiplier": "1.9", 72 | "gold_exponent": "1.25" 73 | }, 74 | "treasure_mob": { 75 | "hp": 60000, 76 | "dps": 1, 77 | "gold": 200, 78 | "hp_multiplier": "1.2", 79 | "hp_exponent": "1.5", 80 | "dps_exponent": "1.2", 81 | "lifetime": 10, 82 | "chance": "0.01", 83 | "gold_multiplier": "1.7", 84 | "gold_exponent": "1.1" 85 | }, 86 | "abilities": { 87 | "1": { 88 | "name": "Fire Weapon", 89 | "max_num_clicks": 20 90 | }, 91 | "2": { 92 | "name": "Change Lane", 93 | "cost": 0 94 | }, 95 | "3": { 96 | "name": "Respawn", 97 | "cost": 0 98 | }, 99 | "4": { 100 | "name": "Change Target", 101 | "cost": 0 102 | }, 103 | "5": { 104 | "name": "Morale Booster", 105 | "multiplier": "0.2", 106 | "cost": 0, 107 | "duration": 10, 108 | "cooldown": 1800, 109 | "desc": "Increases all damage done by players in the current lane" 110 | }, 111 | "6": { 112 | "name": "Good Luck Charms", 113 | "multiplier": "0.1", 114 | "cost": 0, 115 | "duration": 15, 116 | "cooldown": 1800, 117 | "desc": "Increases chance to do critical click damage for players in the current lane" 118 | }, 119 | "7": { 120 | "name": "Medics", 121 | "multiplier": "0.1", 122 | "cost": 0, 123 | "duration": 5, 124 | "cooldown": 300, 125 | "desc": "Slowly heals everyone in the current lane that is still alive" 126 | }, 127 | "8": { 128 | "name": "Metal Detector", 129 | "multiplier": "0.1", 130 | "cost": 0, 131 | "duration": 20, 132 | "cooldown": 3600, 133 | "desc": "Increases gold dropped by enemies in the current lane" 134 | }, 135 | "9": { 136 | "name": "Decrease Cooldowns", 137 | "multiplier": "0.25", 138 | "cost": 0, 139 | "duration": 15, 140 | "cooldown": 3600, 141 | "desc": "While active, decreases cooldowns for any newly activated ability in the current lane (does not stack)." 142 | }, 143 | "10": { 144 | "name": "Tactical Nuke", 145 | "multiplier": "0.5", 146 | "multiplier_boss": "0.2", 147 | "cost": 0, 148 | "instant": 1, 149 | "duration": 5, 150 | "cooldown": 3600, 151 | "desc": "Launches a tactical nuclear missile that does high damage to your current target." 152 | }, 153 | "11": { 154 | "name": "Cluster Bomb", 155 | "multiplier": "0.20", 156 | "cost": 0, 157 | "instant": 1, 158 | "duration": 5, 159 | "cooldown": 3600, 160 | "desc": "Drops a cluster bomb, damaging all enemies in your lane." 161 | }, 162 | "12": { 163 | "name": "Napalm", 164 | "multiplier": "0.05", 165 | "cost": 0, 166 | "duration": 10, 167 | "cooldown": 3600, 168 | "desc": "Drops napalm, inflicting damage on all enemies in your lane over time." 169 | }, 170 | "13": { 171 | "name": "Resurrection", 172 | "multiplier": 1, 173 | "badge_points_cost": 10, 174 | "instant": 1, 175 | "duration": 5, 176 | "cooldown": 30, 177 | "desc": "Resurrects dead players in your lane and restores them to 100% health." 178 | }, 179 | "14": { 180 | "name": "Cripple Spawner", 181 | "multiplier": 1, 182 | "badge_points_cost": 10, 183 | "instant": 1, 184 | "duration": 5, 185 | "cooldown": 60, 186 | "desc": "Damages a spawner down to 1 HP." 187 | }, 188 | "15": { 189 | "name": "Cripple Monster", 190 | "multiplier": "0.05", 191 | "badge_points_cost": 5, 192 | "instant": 1, 193 | "duration": 5, 194 | "cooldown": 60, 195 | "desc": "Reduces player's current target to 1 HP for normal monsters or up to 5% max health for a boss monster." 196 | }, 197 | "16": { 198 | "name": "Max Elemental Damage", 199 | "multiplier": 1, 200 | "badge_points_cost": 10, 201 | "duration": 5, 202 | "cooldown": 60, 203 | "desc": "All players in your lane apply their highest elemental damage multiplier, regardless of the monster's elemental weakness." 204 | }, 205 | "17": { 206 | "name": "Raining Gold", 207 | "multiplier": "0.01", 208 | "badge_points_cost": 10, 209 | "duration": 10, 210 | "cooldown": 30, 211 | "desc": "All players in your lane will receive 1% of their target's gold per click while this is active." 212 | }, 213 | "18": { 214 | "name": "Crit", 215 | "multiplier": "0.01", 216 | "badge_points_cost": 10, 217 | "instant": 1, 218 | "duration": 5, 219 | "cooldown": 60, 220 | "desc": "Permanently increases your chance to do critical click damage by 1% and automatically deploys Good Luck Charms for free." 221 | }, 222 | "19": { 223 | "name": "Pumped Up", 224 | "multiplier": "0.1", 225 | "badge_points_cost": 1, 226 | "instant": 1, 227 | "duration": 5, 228 | "cooldown": 10, 229 | "desc": "Permanently increases your max health and automatically deploys Medics in your lane for free." 230 | }, 231 | "20": { 232 | "name": "Throw Money At Screen", 233 | "multiplier": "0.1", 234 | "badge_points_cost": 10, 235 | "duration": 5, 236 | "cooldown": 10, 237 | "desc": "Spend 10% of your gold and damage your current target by up to 10% of their max health." 238 | }, 239 | "21": { 240 | "name": "GOD MODE", 241 | "multiplier": 1, 242 | "badge_points_cost": 1, 243 | "duration": 5, 244 | "cooldown": 5, 245 | "desc": "Temporarily take no damage." 246 | }, 247 | "22": { 248 | "name": "Treasure!", 249 | "multiplier": 100000, 250 | "badge_points_cost": 2, 251 | "instant": 1, 252 | "duration": 0, 253 | "cooldown": 5, 254 | "desc": "Gives you a base 100k of gold and automatically deploys Metal Detector in your lane for free.

More gold will be given based on the level you are on when this item is used." 255 | }, 256 | "23": { 257 | "name": "Steal Health", 258 | "multiplier": "0.1", 259 | "badge_points_cost": 5, 260 | "duration": 10, 261 | "cooldown": 10, 262 | "desc": "Replenishes the health of all players in your lane based on the click damage they do to their target." 263 | }, 264 | "24": { 265 | "name": "Reflect Damage", 266 | "multiplier": "0.5", 267 | "badge_points_cost": 5, 268 | "duration": 10, 269 | "cooldown": 30, 270 | "desc": "Monsters take damage based on how much damage they deal out." 271 | }, 272 | "25": { 273 | "name": "Feeling Lucky", 274 | "multiplier": 1, 275 | "badge_points_cost": 200, 276 | "instant": 1, 277 | "duration": 10, 278 | "cooldown": 1800, 279 | "start_of_game_cooldown": 0, 280 | "desc": "Grant every alive player in your lane one random special item.

This item can only be acquired by spending badge points." 281 | }, 282 | "26": { 283 | "name": "Wormhole", 284 | "multiplier": 1, 285 | "gold_multiplier": 50000, 286 | "badge_points_cost": 100, 287 | "instant": 1, 288 | "duration": 10, 289 | "cooldown": 60, 290 | "start_of_game_cooldown": 0, 291 | "desc": "Skip a level with each use (stackable) after the current level has been cleared, and grants 50k of gold to every player. Enemies will not drop gold or loot for any skipped levels.

When used on every 100th level, will skip 10 levels with each use and grant the appropriate amount of gold.

You must defeat a boss level after using Wormhole in order to level up your Monster Summer Badge.

This item can only be acquired by spending badge points." 292 | }, 293 | "27": { 294 | "name": "Like New", 295 | "multiplier": 1, 296 | "badge_points_cost": 100, 297 | "duration": 5, 298 | "cooldown": 1, 299 | "start_of_game_cooldown": 0, 300 | "desc": "While active, clears the cooldown on every ability for all players in your lane.

This item can only be acquired by spending badge points." 301 | } 302 | }, 303 | "upgrades": { 304 | "0": { 305 | "name": "Light Armor", 306 | "multiplier": "1.3", 307 | "type": 0, 308 | "cost": 100, 309 | "cost_exponential_base": "2.5", 310 | "desc": "Increases your health (HP)" 311 | }, 312 | "1": { 313 | "name": "Auto-fire Cannon", 314 | "initial_value": 10, 315 | "multiplier": 1, 316 | "type": 1, 317 | "cost": 150, 318 | "cost_exponential_base": "1.3", 319 | "desc": "Inflicts damage on your target every second" 320 | }, 321 | "2": { 322 | "name": "Armor Piercing Round", 323 | "multiplier": 1, 324 | "type": 2, 325 | "cost": 200, 326 | "cost_exponential_base": "1.2", 327 | "desc": "Increases your click damage" 328 | }, 329 | "3": { 330 | "name": "+Damage to Fire Monsters", 331 | "multiplier": "1.5", 332 | "type": 3, 333 | "cost": 50, 334 | "cost_exponential_base": "2.2", 335 | "desc": "Do additional damage to Fire Monsters" 336 | }, 337 | "4": { 338 | "name": "+Damage to Water Monsters", 339 | "multiplier": "1.5", 340 | "type": 4, 341 | "cost": 50, 342 | "cost_exponential_base": "2.2", 343 | "desc": "Do additional damage to Water Monsters" 344 | }, 345 | "5": { 346 | "name": "+Damage to Air Monsters", 347 | "multiplier": "1.5", 348 | "type": 5, 349 | "cost": 50, 350 | "cost_exponential_base": "2.2", 351 | "desc": "Do additional damage to Air Monsters" 352 | }, 353 | "6": { 354 | "name": "+Damage to Earth Monsters", 355 | "multiplier": "1.5", 356 | "type": 6, 357 | "cost": 50, 358 | "cost_exponential_base": "2.2", 359 | "desc": "Do additional damage to Earth Monsters" 360 | }, 361 | "7": { 362 | "name": "Lucky Shot", 363 | "multiplier": "1.5", 364 | "type": 7, 365 | "cost": 50, 366 | "cost_exponential_base": "2.5", 367 | "required_upgrade": 2, 368 | "required_upgrade_level": 5, 369 | "desc": "Increase your critical hit click damage" 370 | }, 371 | "8": { 372 | "name": "Heavy Armor", 373 | "multiplier": 10, 374 | "type": 0, 375 | "cost": 10000, 376 | "cost_exponential_base": "2.2", 377 | "required_upgrade": 0, 378 | "required_upgrade_level": 10, 379 | "desc": "Increases your health (HP)" 380 | }, 381 | "9": { 382 | "name": "Advanced Targeting", 383 | "multiplier": 10, 384 | "type": 1, 385 | "cost": 10000, 386 | "cost_exponential_base": "2.2", 387 | "required_upgrade": 1, 388 | "required_upgrade_level": 10, 389 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 390 | }, 391 | "10": { 392 | "name": "Explosive Rounds", 393 | "multiplier": 10, 394 | "type": 2, 395 | "cost": 10000, 396 | "cost_exponential_base": "2.2", 397 | "required_upgrade": 2, 398 | "required_upgrade_level": 10, 399 | "desc": "Increases your click damage" 400 | }, 401 | "11": { 402 | "name": "Medics", 403 | "type": 8, 404 | "cost": 5000, 405 | "cost_exponential_base": "2.2", 406 | "required_upgrade": 0, 407 | "ability": 7, 408 | "desc": "Slowly heals everyone in the current lane that is still alive" 409 | }, 410 | "12": { 411 | "name": "Morale Booster", 412 | "type": 8, 413 | "cost": 10000000, 414 | "cost_exponential_base": "2.2", 415 | "required_upgrade": 1, 416 | "required_upgrade_level": 20, 417 | "ability": 5, 418 | "desc": "Increases all damage done by players in the current lane" 419 | }, 420 | "13": { 421 | "name": "Good Luck Charms", 422 | "type": 8, 423 | "cost": 1000000, 424 | "cost_exponential_base": "2.2", 425 | "required_upgrade": 2, 426 | "required_upgrade_level": 5, 427 | "ability": 6, 428 | "desc": "Increases chance to do critical click damage for players in the current lane" 429 | }, 430 | "14": { 431 | "name": "Metal Detector", 432 | "type": 8, 433 | "cost": 10000000, 434 | "cost_exponential_base": "2.2", 435 | "ability": 8, 436 | "desc": "Increases gold dropped by enemies in the current lane" 437 | }, 438 | "15": { 439 | "name": "Decrease Cooldowns", 440 | "type": 8, 441 | "cost": 10000000, 442 | "cost_exponential_base": "2.2", 443 | "ability": 9, 444 | "desc": "While active, decreases cooldowns for any newly activated ability in the current lane (does not stack)." 445 | }, 446 | "16": { 447 | "name": "Tactical Nuke", 448 | "type": 8, 449 | "cost": 100000, 450 | "cost_exponential_base": 5, 451 | "required_upgrade": 2, 452 | "required_upgrade_level": 10, 453 | "ability": 10, 454 | "desc": "Launches a tactical nuclear missile that does high damage to your current target." 455 | }, 456 | "17": { 457 | "name": "Cluster Bomb", 458 | "type": 8, 459 | "cost": 1000000, 460 | "cost_exponential_base": "2.2", 461 | "ability": 11, 462 | "required_upgrade": 1, 463 | "required_upgrade_level": 10, 464 | "desc": "Drops a cluster bomb, damaging all enemies in your lane." 465 | }, 466 | "18": { 467 | "name": "Napalm", 468 | "type": 8, 469 | "cost": 2000000, 470 | "cost_exponential_base": "2.2", 471 | "ability": 12, 472 | "required_upgrade": 1, 473 | "required_upgrade_level": 10, 474 | "desc": "Drops napalm, inflicting damage on all enemies in your lane over time." 475 | }, 476 | "19": { 477 | "name": "Boss Loot", 478 | "multiplier": "0.01", 479 | "type": 9, 480 | "cost": 100000, 481 | "cost_exponential_base": "2.2", 482 | "required_upgrade": 1, 483 | "required_upgrade_level": 10, 484 | "desc": "Increase your chance to get loot after defeating a boss" 485 | }, 486 | "20": { 487 | "name": "Energy Shields", 488 | "multiplier": 100, 489 | "type": 0, 490 | "cost": 100000, 491 | "cost_exponential_base": "2.2", 492 | "required_upgrade": 8, 493 | "required_upgrade_level": 10, 494 | "desc": "Increases your health (HP)" 495 | }, 496 | "21": { 497 | "name": "Farming Equipment", 498 | "multiplier": 100, 499 | "type": 1, 500 | "cost": 100000, 501 | "cost_exponential_base": "2.2", 502 | "required_upgrade": 9, 503 | "required_upgrade_level": 10, 504 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 505 | }, 506 | "22": { 507 | "name": "Railgun", 508 | "multiplier": 100, 509 | "type": 2, 510 | "cost": 100000, 511 | "cost_exponential_base": "2.2", 512 | "required_upgrade": 10, 513 | "required_upgrade_level": 10, 514 | "desc": "Increases your click damage" 515 | }, 516 | "23": { 517 | "name": "Personal Training", 518 | "multiplier": 1000, 519 | "type": 0, 520 | "cost": 1000000, 521 | "cost_exponential_base": "2.2", 522 | "required_upgrade": 20, 523 | "required_upgrade_level": 10, 524 | "desc": "Increases your health (HP)" 525 | }, 526 | "24": { 527 | "name": "AFK Equipment", 528 | "multiplier": 1000, 529 | "type": 1, 530 | "cost": 1000000, 531 | "cost_exponential_base": "2.2", 532 | "required_upgrade": 21, 533 | "required_upgrade_level": 10, 534 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 535 | }, 536 | "25": { 537 | "name": "New Mouse Button", 538 | "multiplier": 1000, 539 | "type": 2, 540 | "cost": 1000000, 541 | "cost_exponential_base": "2.2", 542 | "required_upgrade": 22, 543 | "required_upgrade_level": 10, 544 | "desc": "Increases your click damage" 545 | }, 546 | "26": { 547 | "name": "Cybernetic Enhancements", 548 | "multiplier": 10000, 549 | "type": 0, 550 | "cost": "10000000.0", 551 | "cost_exponential_base": "2.2", 552 | "required_upgrade": 23, 553 | "required_upgrade_level": 10, 554 | "desc": "Increases your health (HP)" 555 | }, 556 | "27": { 557 | "name": "Level 1 Sentry Gun", 558 | "multiplier": 10000, 559 | "type": 1, 560 | "cost": "10000000.0", 561 | "cost_exponential_base": "2.2", 562 | "required_upgrade": 24, 563 | "required_upgrade_level": 10, 564 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 565 | }, 566 | "28": { 567 | "name": "Titanium Mouse Button", 568 | "multiplier": 10000, 569 | "type": 2, 570 | "cost": "10000000.0", 571 | "cost_exponential_base": "2.2", 572 | "required_upgrade": 25, 573 | "required_upgrade_level": 10, 574 | "desc": "Increases your click damage" 575 | }, 576 | "29": { 577 | "name": "Exoskeleton", 578 | "multiplier": 100000, 579 | "type": 0, 580 | "cost": "100000000.0", 581 | "cost_exponential_base": "2.2", 582 | "required_upgrade": 26, 583 | "required_upgrade_level": 10, 584 | "desc": "Increases your health (HP)" 585 | }, 586 | "30": { 587 | "name": "Level 2 Sentry Gun", 588 | "multiplier": 100000, 589 | "type": 1, 590 | "cost": "100000000.0", 591 | "cost_exponential_base": "2.2", 592 | "required_upgrade": 27, 593 | "required_upgrade_level": 10, 594 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 595 | }, 596 | "31": { 597 | "name": "Double-Barrelled Mouse", 598 | "multiplier": 100000, 599 | "type": 2, 600 | "cost": "100000000.0", 601 | "cost_exponential_base": "2.2", 602 | "required_upgrade": 28, 603 | "required_upgrade_level": 10, 604 | "desc": "Increases your click damage" 605 | }, 606 | "32": { 607 | "name": "Yoga Training", 608 | "multiplier": 1000000, 609 | "type": 0, 610 | "cost": "1000000000.0", 611 | "cost_exponential_base": "2.2", 612 | "required_upgrade": 29, 613 | "required_upgrade_level": 10, 614 | "desc": "Increases your health (HP)" 615 | }, 616 | "33": { 617 | "name": "Level 3 Sentry Gun", 618 | "multiplier": 1000000, 619 | "type": 1, 620 | "cost": "1000000000.0", 621 | "cost_exponential_base": "2.2", 622 | "required_upgrade": 30, 623 | "required_upgrade_level": 10, 624 | "desc": "Increases the damage inflicted by your Auto-fire Cannon" 625 | }, 626 | "34": { 627 | "name": "Bionic Finger", 628 | "multiplier": 1000000, 629 | "type": 2, 630 | "cost": "1000000000.0", 631 | "cost_exponential_base": "2.2", 632 | "required_upgrade": 31, 633 | "required_upgrade_level": 10, 634 | "desc": "Increases your click damage" 635 | }, 636 | "35": { 637 | "name": "#TowerAttack_Upgrade_HP8", 638 | "multiplier": 10000000, 639 | "type": 0, 640 | "cost": "10000000000.0", 641 | "cost_exponential_base": "2.2", 642 | "required_upgrade": 32, 643 | "required_upgrade_level": 10, 644 | "desc": "#TowerAttack_Upgrade_HP8_Desc" 645 | }, 646 | "36": { 647 | "name": "#TowerAttack_Upgrade_DPS8", 648 | "multiplier": 10000000, 649 | "type": 1, 650 | "cost": "10000000000.0", 651 | "cost_exponential_base": "2.2", 652 | "required_upgrade": 33, 653 | "required_upgrade_level": 10, 654 | "desc": "#TowerAttack_Upgrade_DPS8_Desc" 655 | }, 656 | "37": { 657 | "name": "#TowerAttack_Upgrade_Click8", 658 | "multiplier": 10000000, 659 | "type": 2, 660 | "cost": "10000000000.0", 661 | "cost_exponential_base": "2.2", 662 | "required_upgrade": 34, 663 | "required_upgrade_level": 10, 664 | "desc": "#TowerAttack_Upgrade_Click8_Desc" 665 | }, 666 | "38": { 667 | "name": "#TowerAttack_Upgrade_HP9", 668 | "multiplier": 100000000, 669 | "type": 0, 670 | "cost": "100000000000.0", 671 | "cost_exponential_base": "2.2", 672 | "required_upgrade": 35, 673 | "required_upgrade_level": 10, 674 | "desc": "#TowerAttack_Upgrade_HP9_Desc" 675 | }, 676 | "39": { 677 | "name": "#TowerAttack_Upgrade_DPS9", 678 | "multiplier": 100000000, 679 | "type": 1, 680 | "cost": "100000000000.0", 681 | "cost_exponential_base": "2.2", 682 | "required_upgrade": 36, 683 | "required_upgrade_level": 10, 684 | "desc": "#TowerAttack_Upgrade_DPS9_Desc" 685 | }, 686 | "40": { 687 | "name": "#TowerAttack_Upgrade_Click9", 688 | "multiplier": 100000000, 689 | "type": 2, 690 | "cost": "100000000000.0", 691 | "cost_exponential_base": "2.2", 692 | "required_upgrade": 37, 693 | "required_upgrade_level": 10, 694 | "desc": "#TowerAttack_Upgrade_Click9_Desc" 695 | }, 696 | "41": { 697 | "name": "#TowerAttack_Upgrade_HP10", 698 | "multiplier": 1000000000, 699 | "type": 0, 700 | "cost": "1000000000000.0", 701 | "cost_exponential_base": "2.2", 702 | "required_upgrade": 38, 703 | "required_upgrade_level": 10, 704 | "desc": "#TowerAttack_Upgrade_HP10_Desc" 705 | }, 706 | "42": { 707 | "name": "#TowerAttack_Upgrade_DPS10", 708 | "multiplier": 1000000000, 709 | "type": 1, 710 | "cost": "1000000000000.0", 711 | "cost_exponential_base": "2.2", 712 | "required_upgrade": 39, 713 | "required_upgrade_level": 10, 714 | "desc": "#TowerAttack_Upgrade_DPS10_Desc" 715 | }, 716 | "43": { 717 | "name": "#TowerAttack_Upgrade_Click10", 718 | "multiplier": 1000000000, 719 | "type": 2, 720 | "cost": "1000000000000.0", 721 | "cost_exponential_base": "2.2", 722 | "required_upgrade": 40, 723 | "required_upgrade_level": 10, 724 | "desc": "#TowerAttack_Upgrade_Click10_Desc" 725 | } 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /update_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPT=autoPlay.noUpdate.user.js 3 | LOADER=autoplay.user.js 4 | 5 | # Too many arguments, show usage 6 | if [ $# -gt 1 ]; then 7 | echo "Usage: $0 [version]" 8 | echo "If version is not specified, the build number is updated." 9 | exit 1 10 | fi 11 | 12 | # Get the old script version 13 | OLD_VERSION=`cat $SCRIPT | grep "// \@version" | awk '{print $3}'` 14 | 15 | # If a version is specified, make sure it's valid 16 | if [[ $# -eq 1 ]]; then 17 | if [[ "$1" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]]; then 18 | NEW_VERSION=$1 19 | else 20 | echo "Invalid version \"$1\"." 21 | echo "Version numbers must be in the format X.Y.Z.ZZ." 22 | exit 2 23 | fi 24 | else 25 | echo "Version not specified, loading from javascript." 26 | NEW_VERSION=`echo $OLD_VERSION | awk -F . '{print $1 "." $2 "." $3 "." $4+1}'` 27 | fi 28 | 29 | echo "Old Version: $OLD_VERSION" 30 | echo "New Version: $NEW_VERSION" 31 | 32 | # Replace the occurrances in the .js file 33 | OLD_REGEX=`echo $OLD_VERSION | sed 's/\./\\\\./g'` 34 | NEW_REGEX=`echo $NEW_VERSION | sed 's/\./\\\\./g'` 35 | sed -i "s/$OLD_REGEX/$NEW_REGEX/g" $SCRIPT 36 | sed -i "s/$OLD_REGEX/$NEW_REGEX/g" $LOADER 37 | 38 | cat << EOF > version.json 39 | { 40 | "_comment" : "This file is used for automatic updates. Please update with update_version.sh.", 41 | "Version" : "$NEW_VERSION" 42 | } 43 | EOF 44 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment" : "This file is used for automatic updates. Please update with update_version.sh.", 3 | "Version" : "8.0.3" 4 | } 5 | --------------------------------------------------------------------------------