├── LICENSE ├── README.md ├── autoPlay.js └── main.user.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Martin ( http://github.com/mouseas/ ) 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 | # Steam Summer 2015 Monster Minigame AutoScript # 2 | 3 | ## Purpose ## 4 | 5 | This javascript automatically plays the 2015 Steam Summer minigame for you in a semi-optimal way. 6 | 7 | It goes beyond the autoclicker scripts already out there. It will keep you in the lane where you'll make the most money, activate abilities as they are available and best apply, and possibly purchase upgrades and 8 | powerups for you. 9 | 10 | ## Features ## 11 | 12 | - Moves you to the lane most likely to give you gold, prioritized like so: 13 | 1. The lane with a Treasure Minion or Boss 14 | 2. The lane with the Miniboss with the lowest health 15 | 3. The lane with a Spawner below 40% health 16 | 4. The lane with a Creep below 10% health 17 | 5. The lane with the Spawner with the lowest health 18 | - Activates most reusable abilities, if they are purchased and cooled down: 19 | - Medics if your health is below 50% 20 | - Morale Booster, Napalm, and Cluster Bombs if the lane has a Spawner and 2-3 Creeps 21 | - Good Luck Charm as soon as possible 22 | - Tactical Nuke if the current Spawner is between 60% and 30% health 23 | - Metal Detector is facing a Boss who has less than 30% health 24 | - Activates some items if you have them and the situation calls for them: 25 | - God Mode if Medics is in cooldown and your health is low 26 | - Cripple Spawner if the spawner in the current lane has more than 95% health 27 | - Gold Rain if facing a Boss who has more than 60% health 28 | - Respawns you after 5 seconds (instead of 1 minute) if you die 29 | - Disables certain abilities and items if facing a Boss (to try to maximize Raining Gold and Metal Detector benefits) 30 | - Auto-clicks if the current lane has Raining Gold active 31 | - Adds UI elements to adjust some settings 32 | 33 | ## Installation ## 34 | 35 | ### Greasemonkey & Tampermonkey ### 36 | 1. Navigate to `https://raw.githubusercontent.com/mouseas/steamSummerMinigame/master/main.user.js`. 37 | 2. Press `Install`. 38 | 3. That's it! 39 | 40 | ### Manual ### 41 | 42 | ##### Chrome ##### 43 | 1. Open `main.user.js` in a text editor. 44 | 2. Select All, Copy. 45 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 46 | 4. Press `Ctrl + Shift + J`. 47 | 5. Paste into the javascript input, and hit `Enter`. 48 | 49 | ##### Firefox ##### 50 | 1. Open `main.user.js` in a text editor. 51 | 2. Select All, Copy. 52 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 53 | 4. Press `Ctrl + Shift + K`. 54 | 5. Paste into the javascript input, and hit `Enter`. 55 | 56 | ##### Internet Explorer / Microsoft Edge ##### 57 | 1. Open `main.user.js` in a text editor. 58 | 2. Select All, Copy. 59 | 3. Navigate to `http://steamcommunity.com/minigame/` and join or start a game. 60 | 4. Press `F12` and navigate to the `Console` tab. 61 | 5. Paste into the javascript input, and hit `Enter`. 62 | 63 | To stop the manual script, type `window.clearTimeout(thingTimer);` into the console and hit `Enter`. 64 | 65 | 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. 66 | 67 | ## Update to 2.0 (Greasemonkey / Tampermonkey only) ## 68 | If you have been redirected here, it means you are on an outdated version of the script that we cannot automatically update for you due to GM / TM limitations. To update the script, simply delete your version of the script from GM / TM, and follow the installation instructions above as you did the first time. 69 | 70 | Thank you! 71 | 72 | — /u/mouseasw & /u/WinneonSword 73 | 74 | ## I want to contribute! ## 75 | 76 | This project is open-source on github. There are different ways you can help: 77 | 78 | - 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. 79 | - 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. 80 | - Pick an item off the TODO list, below, and implement it. When it's done (and tested and working), create a Pull Request. 81 | - 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. 82 | 83 | ### TODO ### 84 | 85 | - Use abilities if available and a suitable target exists: 86 | - Decrease Cooldowns right before using another long-cooldown item. (Decrease Cooldown affects abilities triggered while it is active, not right before it's used) 87 | - Steal Health item if Medics is in cooldown and health is low. This should happen before using God Mode, and God Mode shouldn't be used if Steal Health is active. 88 | - purchase abilities and upgrades intelligently 89 | - automatically update the manual script by periodically checking https://raw.githubusercontent.com/mouseas/steamSummerMinigame/master/main.user.js 90 | - add some buttons on the page for enabling/disabling certain features 91 | -------------------------------------------------------------------------------- /autoPlay.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Monster Minigame AutoScript Redirect 3 | // @author /u/mouseasw for creating and maintaining the script, /u/WinneonSword for the Greasemonkey support, and every contributor on the GitHub repo for constant enhancements. 4 | // @version 1.11 5 | // @namespace https://github.com/mouseas/steamSummerMinigame 6 | // @description A redirect so you may update to the new file of the autoscript. 7 | // @match http://steamcommunity.com/minigame* 8 | // @match http://steamcommunity.com//minigame* 9 | // @match https://steamcommunity.com/minigame* 10 | // @match https://steamcommunity.com//minigame* 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | window.location.replace("https://github.com/mouseas/steamSummerMinigame#update-to-20-greasemonkey--tampermonkey-only"); -------------------------------------------------------------------------------- /main.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Monster Minigame AutoScript 3 | // @author /u/mouseasw for creating and maintaining the script, /u/WinneonSword for the Greasemonkey support, and every contributor on the GitHub repo for constant enhancements. 4 | // @version 2.4.3 5 | // @namespace https://github.com/mouseas/steamSummerMinigame 6 | // @description A script that runs the Steam Monster Minigame for you. 7 | // @match *://steamcommunity.com/minigame/towerattack* 8 | // @match *://steamcommunity.com//minigame/towerattack* 9 | // @grant none 10 | // @updateURL https://raw.githubusercontent.com/mouseas/steamSummerMinigame/master/main.user.js 11 | // @downloadURL https://raw.githubusercontent.com/mouseas/steamSummerMinigame/master/main.user.js 12 | // ==/UserScript== 13 | 14 | /////////////////////////////////////////////////////////// 15 | // ___ __ __ ____ ___ ____ _____ _ _ _ _____ // 16 | // |_ _| \/ | _ \ / _ \| _ \_ _|/ \ | \ | |_ _| // 17 | // | || |\/| | |_) | | | | |_) || | / _ \ | \| | | | // 18 | // | || | | | __/| |_| | _ < | |/ ___ \| |\ | | | // 19 | // |___|_| |_|_| \___/|_| \_\|_/_/ \_\_| \_| |_| // 20 | // // 21 | // Increment the @version parameter every time you // 22 | // update the script (2.0.1, 2.0.2, etc.). Otherwise // 23 | // Greasemonkey / Tampermonkey users will NOT update // 24 | // automatically!! // 25 | // // 26 | // Do not update version number in pull requests; // 27 | // It usually causes a merge conflict. // 28 | // // 29 | /////////////////////////////////////////////////////////// 30 | 31 | 32 | // Options. Can also be set in the options menu below game. 33 | var purchaseUpgradeToggle = false; 34 | var clickRate = 10; // change to number of desired clicks per second 35 | 36 | // Do not touch these 37 | var isAlreadyRunning = false; 38 | var timer = 0; 39 | var lastAction = 500; //start with the max. Array length 40 | var clickTimer; 41 | var purchasedShieldsWhileRespawning = false; 42 | 43 | var ABILITIES = { 44 | "MORALE_BOOSTER": 5, 45 | "GOOD_LUCK": 6, 46 | "MEDIC": 7, 47 | "METAL_DETECTOR": 8, 48 | "COOLDOWN": 9, 49 | "NUKE": 10, 50 | "CLUSTER_BOMB": 11, 51 | "NAPALM": 12 52 | }; 53 | 54 | var ITEMS = { 55 | "REVIVE": 13, 56 | "CRIPPLE_SPAWNER": 14, 57 | "CRIPPLE_MONSTER": 15, 58 | "MAXIMIZE_ELEMENT": 16, 59 | "GOLD_RAIN": 17, 60 | "CRIT": 18, 61 | "PUMPED_UP": 19, 62 | "THROW_MONEY": 20, 63 | "GOD_MODE": 21, 64 | "TREASURE": 22, 65 | "STEAL_HEALTH": 23, 66 | "REFLECT_DAMAGE": 24, 67 | "FEELING_LUCKY": 25, 68 | "WORMHOLE": 26, 69 | "LIKE_NEW": 27 70 | }; 71 | 72 | var ENEMY_TYPE = { 73 | "SPAWNER": 0, 74 | "CREEP": 1, 75 | "BOSS": 2, 76 | "MINIBOSS": 3, 77 | "TREASURE": 4 78 | }; 79 | 80 | // Each elemental damage, lucky shot and loot have their own type in m_rgTuningData 81 | var UPGRADE_TYPES = { 82 | "ARMOR": 0, 83 | "DPS": 1, 84 | "CLICK_DAMAGE": 2, 85 | "ELEMENTAL_FIRE": 3, 86 | "ELEMENTAL_WATER": 4, 87 | "ELEMENTAL_AIR": 5, 88 | "ELEMENTAL_EARTH": 6, 89 | "LUCKY_SHOT": 7, 90 | "ABILITY": 8, 91 | "LOOT": 9 92 | }; 93 | 94 | if (thingTimer){ 95 | window.clearInterval(thingTimer); 96 | } 97 | 98 | function firstRun() { 99 | // if the purchase item window is open, spend your badge points! 100 | if (g_Minigame.CurrentScene().m_UI.m_spendBadgePointsDialog.is(":visible")) { 101 | purchaseBadgeItems(); 102 | } 103 | createOptionsMenu(); 104 | 105 | // disable particle effects - this drastically reduces the game's memory leak 106 | if (g_Minigame !== undefined) { 107 | g_Minigame.CurrentScene().DoClickEffect = function() {}; 108 | g_Minigame.CurrentScene().DoCritEffect = function( nDamage, x, y, additionalText ) {}; 109 | g_Minigame.CurrentScene().SpawnEmitter = function(emitter) { 110 | emitter.emit = false; 111 | return emitter; 112 | }; 113 | } 114 | 115 | // disable enemy flinching animation when they get hit 116 | if (CEnemy !== undefined) { 117 | CEnemy.prototype.TakeDamage = function() {}; 118 | CEnemySpawner.prototype.TakeDamage = function() {}; 119 | CEnemyBoss.prototype.TakeDamage = function() {}; 120 | } 121 | 122 | // flat disable Throw Money At Screen item - it causes more harm than benefit in every conceivable case 123 | disableAbilityItem(ITEMS.THROW_MONEY); 124 | } 125 | 126 | function doTheThing() { 127 | if (!isAlreadyRunning){ 128 | isAlreadyRunning = true; 129 | 130 | try { 131 | goToLaneWithBestTarget(); 132 | 133 | if (purchaseUpgradeToggle){ 134 | purchaseUpgrades(); 135 | } 136 | 137 | useGoodLuckCharmIfRelevant(); 138 | useCritIfRelevant(); 139 | useReviveIfRelevant(); 140 | useMedicsIfRelevant(); 141 | useMoraleBoosterIfRelevant(); 142 | useClusterBombIfRelevant(); 143 | useNapalmIfRelevant(); 144 | useTacticalNukeIfRelevant(); 145 | useCrippleSpawnerIfRelevant(); 146 | useMetalDetectorAndTreasureIfRelevant(); 147 | useGoldRainIfRelevant(); 148 | attemptRespawn(); 149 | 150 | if (clickRate > 0) { 151 | startGoldRainClick(); 152 | } 153 | } catch (e) { 154 | console.log("An error occurred, but we'll keep running."); 155 | console.log(e); 156 | } 157 | 158 | isAlreadyRunning = false; 159 | } 160 | } 161 | 162 | function purchaseBadgeItems() { 163 | // Spends badge points (BP's) when joining a new game. 164 | // Dict contains the priority in terms of amount to buy (percentage of purchase). Probably a nicer way to do this... 165 | // First version of priorities is based on this badge point table 'usefulness' from reddit: 166 | // http://www.reddit.com/r/Steam/comments/39i0qc/psa_how_the_monster_game_works_an_indepth/ 167 | var abilityItemPriority = [ 168 | [ITEMS.GOLD_RAIN, 50], 169 | [ITEMS.CRIT, 20], 170 | [ITEMS.TREASURE, 6], 171 | [ITEMS.MAXIMIZE_ELEMENT, 5], 172 | [ITEMS.CRIPPLE_MONSTER, 5], 173 | [ITEMS.CRIPPLE_SPAWNER, 5], 174 | [ITEMS.REVIVE, 5], 175 | [ITEMS.STEAL_HEALTH, 4], 176 | [ITEMS.GOD_MODE, 3], 177 | [ITEMS.REFLECT_DAMAGE, 2], 178 | [ITEMS.PUMPED_UP, 1] 179 | //[ITEMS.THROW_MONEY, 0] 180 | //[ITEMS.FEELING_LUCKY, 0] 181 | //[ITEMS.WORMHOLE, 0] 182 | //[ITEMS.LIKE_NEW, 0] 183 | // Only go up to the second-last item. Throw money should never be used, 184 | // but it's here just in case. Similarly, feeling lucky, wormhole, and 185 | // like new are ridiculously priced and honestly aren't worth it 186 | ]; 187 | 188 | // Being extra paranoid about spending, since abilities update slowly. 189 | var safeToBuy = true; 190 | var intervalID = window.setInterval( function() { 191 | var queueLen = g_Minigame.CurrentScene().m_rgPurchaseItemsQueue.length; 192 | if (safeToBuy && queueLen > 0) 193 | safeToBuy = false; 194 | else if (!safeToBuy && queueLen === 0) 195 | safeToBuy = true; 196 | }, 100); 197 | 198 | var buyItem = function(id) { 199 | g_Minigame.CurrentScene().TrySpendBadgePoints(document.getElementById('purchase_abilityitem_' + id)); 200 | }; 201 | 202 | var badgePoints = g_Minigame.CurrentScene().m_rgPlayerTechTree.badge_points; 203 | 204 | for (var i = 0; i < abilityItemPriority.length; i++) { 205 | var abilityItem = abilityItemPriority[i]; 206 | var cost = $J(document.getElementById('purchase_abilityitem_' + abilityItem[0])).data('cost'); 207 | 208 | // Maximum amount to spend on each upgrade. i.e. 100 BP on item with a 10% share = 10 BP 209 | var maxSpend = badgePoints * abilityItem[1] / 100; 210 | var spent = 0; 211 | 212 | // Don't over-spend the budget for each item, and don't overdraft on the BP 213 | while (spent < maxSpend && cost <= g_Minigame.CurrentScene().m_rgPlayerTechTree.badge_points) { 214 | if (!safeToBuy) 215 | continue; 216 | buyItem(abilityItem[0]); 217 | spent += cost; 218 | } 219 | } 220 | 221 | // Get any stragling 1 or 2 BP left over, using the last item (1 BP) in the priority array 222 | while (g_Minigame.CurrentScene().m_rgPlayerTechTree.badge_points > 0) { 223 | if (!safeToBuy) 224 | continue; 225 | buyItem(abilityItemPriority[abilityItemPriority.length - 1][0]); 226 | } 227 | 228 | // Get rid of that interval, it could end up taking up too many resources 229 | window.clearInterval(intervalID); 230 | } 231 | 232 | function goToLaneWithBestTarget() { 233 | // We can overlook spawners if all spawners are 40% hp or higher and a creep is under 10% hp 234 | var spawnerOKThreshold = 0.4; 235 | var creepSnagThreshold = 0.1; 236 | 237 | var targetFound = false; 238 | var lowHP = 0; 239 | var lowLane = 0; 240 | var lowTarget = 0; 241 | var lowPercentageHP = 0; 242 | 243 | // determine which lane and enemy is the optimal target 244 | var enemyTypePriority = [ 245 | ENEMY_TYPE.TREASURE, 246 | ENEMY_TYPE.BOSS, 247 | ENEMY_TYPE.MINIBOSS, 248 | ENEMY_TYPE.SPAWNER, 249 | ENEMY_TYPE.CREEP 250 | ]; 251 | 252 | var skippingSpawner = false; 253 | var skippedSpawnerLane = 0; 254 | var skippedSpawnerTarget = 0; 255 | var targetIsTreasureOrBoss = false; 256 | 257 | for (var k = 0; !targetFound && k < enemyTypePriority.length; k++) { 258 | 259 | if (enemyTypePriority[k] == ENEMY_TYPE.TREASURE || enemyTypePriority[k] == ENEMY_TYPE.BOSS){ 260 | targetIsTreasureOrBoss = true; 261 | } else { 262 | targetIsTreasureOrBoss = false; 263 | } 264 | 265 | var enemies = []; 266 | 267 | // gather all the enemies of the specified type. 268 | for (var i = 0; i < 3; i++) { 269 | for (var j = 0; j < 4; j++) { 270 | var enemy = g_Minigame.CurrentScene().GetEnemy(i, j); 271 | if (enemy && enemy.m_data.type == enemyTypePriority[k]) { 272 | enemies[enemies.length] = enemy; 273 | } 274 | } 275 | } 276 | 277 | // target the enemy of the specified type with the lowest hp 278 | for (var i = 0; i < enemies.length; i++) { 279 | if (enemies[i] && !enemies[i].m_bIsDestroyed) { 280 | if (lowHP < 1 || enemies[i].m_flDisplayedHP < lowHP) { 281 | targetFound = true; 282 | lowHP = enemies[i].m_flDisplayedHP; 283 | lowLane = enemies[i].m_nLane; 284 | lowTarget = enemies[i].m_nID; 285 | } 286 | var percentageHP = enemies[i].m_flDisplayedHP / enemies[i].m_data.max_hp; 287 | if (lowPercentageHP == 0 || percentageHP < lowPercentageHP) { 288 | lowPercentageHP = percentageHP; 289 | } 290 | } 291 | } 292 | 293 | // If we just finished looking at spawners, 294 | // AND none of them were below our threshold, 295 | // remember them and look for low creeps (so don't quit now) 296 | if (enemyTypePriority[k] == ENEMY_TYPE.SPAWNER && lowPercentageHP > spawnerOKThreshold) { 297 | skippedSpawnerLane = lowLane; 298 | skippedSpawnerTarget = lowTarget; 299 | skippingSpawner = true; 300 | targetFound = false; 301 | } 302 | 303 | // If we skipped a spawner and just finished looking at creeps, 304 | // AND the lowest was above our snag threshold, 305 | // just go back to the spawner! 306 | if (skippingSpawner && enemyTypePriority[k] == ENEMY_TYPE.CREEP && lowPercentageHP > creepSnagThreshold ) { 307 | lowLane = skippedSpawnerLane; 308 | lowTarget = skippedSpawnerTarget; 309 | } 310 | } 311 | 312 | 313 | // go to the chosen lane 314 | if (targetFound) { 315 | if (g_Minigame.CurrentScene().m_nExpectedLane != lowLane) { 316 | //console.log('switching langes'); 317 | g_Minigame.CurrentScene().TryChangeLane(lowLane); 318 | } 319 | 320 | // target the chosen enemy 321 | if (g_Minigame.CurrentScene().m_nTarget != lowTarget) { 322 | //console.log('switching targets'); 323 | g_Minigame.CurrentScene().TryChangeTarget(lowTarget); 324 | } 325 | 326 | 327 | // Prevent attack abilities and items if up against a boss or treasure minion 328 | if (targetIsTreasureOrBoss) { 329 | // Morale 330 | disableAbility(ABILITIES.MORALE_BOOSTER); 331 | // Luck 332 | disableAbility(ABILITIES.GOOD_LUCK); 333 | // Nuke 334 | disableAbility(ABILITIES.NUKE); 335 | // Clusterbomb 336 | disableAbility(ABILITIES.CLUSTER_BOMB); 337 | // Napalm 338 | disableAbility(ABILITIES.NAPALM); 339 | // Crit 340 | disableAbilityItem(ITEMS.CRIT); 341 | // Cripple Spawner 342 | disableAbilityItem(ITEMS.CRIPPLE_SPAWNER); 343 | // Cripple Monster 344 | disableAbilityItem(ITEMS.CRIPPLE_MONSTER); 345 | // Max Elemental Damage 346 | disableAbilityItem(ITEMS.MAXIMIZE_ELEMENT); 347 | // Reflect Damage 348 | disableAbilityItem(ITEMS.REFLECT_DAMAGE); 349 | } else { 350 | // Morale 351 | enableAbility(ABILITIES.MORALE_BOOSTER); 352 | // Luck 353 | enableAbility(ABILITIES.GOOD_LUCK); 354 | // Nuke 355 | enableAbility(ABILITIES.NUKE); 356 | // Clusterbomb 357 | enableAbility(ABILITIES.CLUSTER_BOMB); 358 | // Napalm 359 | enableAbility(ABILITIES.NAPALM); 360 | // Crit 361 | enableAbilityItem(ITEMS.CRIT); 362 | // Cripple Spawner 363 | enableAbilityItem(ITEMS.CRIPPLE_SPAWNER); 364 | // Cripple Monster 365 | enableAbilityItem(ITEMS.CRIPPLE_MONSTER); 366 | // Max Elemental Damage 367 | enableAbilityItem(ITEMS.MAXIMIZE_ELEMENT); 368 | // Reflect Damage 369 | enableAbilityItem(ITEMS.REFLECT_DAMAGE); 370 | } 371 | } 372 | } 373 | 374 | 375 | function purchaseUpgrades() { 376 | var oddsOfElement = 1 - (0.75*0.75*0.75); //This values elemental too much because best element lanes are not focused(0.578) 377 | var avgClicksPerSecond = 1; //Set this yourself to serve your needs 378 | 379 | var upgrades = g_Minigame.CurrentScene().m_rgTuningData.upgrades.slice(0); 380 | 381 | var buyUpgrade = function(id) { 382 | console.log("Buying " + upgrades[id].name + " level " + (g_Minigame.CurrentScene().GetUpgradeLevel(id) + 1)); 383 | if(id >= 3 && 6 >= id) { //If upgrade is element damage 384 | g_Minigame.CurrentScene().TryUpgrade(document.getElementById('upgr_' + id).childElements()[3]); 385 | } else { 386 | g_Minigame.CurrentScene().TryUpgrade(document.getElementById('upgr_' + id).childElements()[0].childElements()[1]); 387 | } 388 | }; 389 | 390 | var myGold = g_Minigame.CurrentScene().m_rgPlayerData.gold; 391 | 392 | //Initial values for armor & damage 393 | var bestUpgradeForDamage,bestUpgradeForArmor; 394 | var highestUpgradeValueForDamage = 0; 395 | var highestUpgradeValueForArmor = 0; 396 | var bestElement = -1; 397 | var highestElementLevel = 0; 398 | 399 | var critMultiplier = g_Minigame.CurrentScene().m_rgPlayerTechTree.damage_multiplier_crit; 400 | var critRate = Math.min(g_Minigame.CurrentScene().m_rgPlayerTechTree.crit_percentage, 1); 401 | var dpc = g_Minigame.CurrentScene().m_rgPlayerTechTree.damage_per_click; 402 | var basedpc = g_Minigame.CurrentScene().m_rgTuningData.player.damage_per_click; 403 | 404 | for( var i=0; i< upgrades.length; i++ ) { 405 | var upgrade = upgrades[i]; 406 | 407 | if ( upgrade.required_upgrade != undefined ) 408 | { 409 | var requiredUpgradeLevel = upgrade.required_upgrade_level != undefined ? upgrade.required_upgrade_level : 1; 410 | var parentUpgradeLevel = g_Minigame.CurrentScene().GetUpgradeLevel(upgrade.required_upgrade); 411 | if ( requiredUpgradeLevel > parentUpgradeLevel ) 412 | { 413 | //If upgrade is not available, we skip it 414 | continue; 415 | } 416 | } 417 | 418 | var upgradeCurrentLevel = g_Minigame.CurrentScene().GetUpgradeLevel(i); 419 | var upgradeCost = g_Minigame.CurrentScene().GetUpgradeCost(i); 420 | 421 | switch(upgrade.type) { 422 | case UPGRADE_TYPES.ARMOR: 423 | if(upgrade.multiplier / upgradeCost > highestUpgradeValueForArmor) { // hp increase per moneys 424 | bestUpgradeForArmor = i; 425 | highestUpgradeValueForArmor = upgrade.multiplier / upgradeCost; 426 | } 427 | break; 428 | case UPGRADE_TYPES.CLICK_DAMAGE: 429 | if((critRate * critMultiplier + (1 - critRate)) * avgClicksPerSecond * upgrade.multiplier * basedpc / upgradeCost > highestUpgradeValueForDamage) { // dmg increase per moneys 430 | bestUpgradeForDamage = i; 431 | highestUpgradeValueForDamage = (critRate * critMultiplier + (1 - critRate)) * avgClicksPerSecond * upgrade.multiplier * basedpc / upgradeCost; 432 | } 433 | break; 434 | case UPGRADE_TYPES.DPS: 435 | if(upgrade.multiplier * basedpc / upgradeCost > highestUpgradeValueForDamage) { // dmg increase per moneys 436 | bestUpgradeForDamage = i; 437 | highestUpgradeValueForDamage = upgrade.multiplier / upgradeCost; 438 | } 439 | break; 440 | case UPGRADE_TYPES.ELEMENTAL_FIRE: 441 | case UPGRADE_TYPES.ELEMENTAL_WATER: 442 | case UPGRADE_TYPES.ELEMENTAL_AIR: 443 | case UPGRADE_TYPES.ELEMENTAL_EARTH: 444 | /*if(upgradeCurrentLevel > highestElementLevel){ 445 | highestElementLevel = upgradeCurrentLevel; 446 | bestElement = i; 447 | }*/ 448 | break; 449 | case UPGRADE_TYPES.LUCKY_SHOT: 450 | if(upgrade.multiplier * dpc * critRate * avgClicksPerSecond / upgradeCost > highestUpgradeValueForDamage) { // dmg increase per moneys 451 | bestUpgradeForDamage = i; 452 | highestUpgradeValueForDamage = upgrade.multiplier / upgradeCost; 453 | } 454 | break; 455 | default: 456 | break; 457 | } 458 | } 459 | /* 460 | if(bestElement != -1) { 461 | //Let user choose what element to level up by adding the point to desired element 462 | upgradeCost = g_Minigame.CurrentScene().GetUpgradeCost(bestElement); 463 | 464 | var dps = g_Minigame.CurrentScene().m_rgPlayerTechTree.dps; 465 | dps = dps + (g_Minigame.CurrentScene().m_rgPlayerTechTree.damage_per_click * avgClicksPerSecond); 466 | if(0.25 * oddsOfElement * dps * upgrades[bestElement].multiplier / upgradeCost > highestUpgradeValueForDamage) { //dmg increase / moneys 467 | //bestUpgradeForDamage = bestElement; // Not doing this because this values element damage too much 468 | } 469 | }*/ 470 | 471 | var currentHealth = g_Minigame.CurrentScene().m_rgPlayerData.hp; 472 | var myMaxHealth = g_Minigame.CurrentScene().m_rgPlayerTechTree.max_hp; 473 | // check if health is below 30% 474 | var hpPercent = currentHealth / myMaxHealth; 475 | if (hpPercent < 0.3) { 476 | // Prioritize armor over damage 477 | // - Should we by any armor we can afford or just wait for the best one possible? 478 | // currently waiting 479 | upgradeCost = g_Minigame.CurrentScene().GetUpgradeCost(bestUpgradeForArmor); 480 | 481 | // Prevent purchasing multiple shields while waiting to respawn. 482 | if (purchasedShieldsWhileRespawning && currentHealth < 1) { 483 | return; 484 | } 485 | 486 | if(myGold > upgradeCost && bestUpgradeForArmor !== undefined) { 487 | buyUpgrade(bestUpgradeForArmor); 488 | myGold = g_Minigame.CurrentScene().m_rgPlayerData.gold; 489 | 490 | purchasedShieldsWhileRespawning = currentHealth < 1; 491 | } 492 | } 493 | else if (purchasedShieldsWhileRespawning) { 494 | purchasedShieldsWhileRespawning = false; 495 | } 496 | 497 | // Try to buy some damage 498 | upgradeCost = g_Minigame.CurrentScene().GetUpgradeCost(bestUpgradeForDamage); 499 | var upgradeCostBestArmor = g_Minigame.CurrentScene().GetUpgradeCost(bestUpgradeForArmor); 500 | 501 | if(myGold - upgradeCostBestArmor > upgradeCost && bestUpgradeForDamage !== undefined) { 502 | buyUpgrade(bestUpgradeForDamage); 503 | } 504 | } 505 | 506 | function useReviveIfRelevant() { 507 | // Use resurrection if doable 508 | if (numItem(ITEMS.REVIVE) === 0 || isAbilityCoolingDown(ITEMS.REVIVE)) { 509 | return; 510 | } 511 | 512 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 513 | // Check if anyone needs reviving 514 | var numDead = g_Minigame.CurrentScene().m_rgGameData.lanes[ currentLane ].player_hp_buckets[0]; 515 | var numPlayers = g_Minigame.CurrentScene().m_rgLaneData[ currentLane ].players; 516 | var numRevives = currentLaneHasAbility(ABILITIES.REVIVE); 517 | 518 | if (numPlayers === 0) 519 | return; // no one alive, apparently 520 | 521 | var deadPercent = numDead / numPlayers; 522 | 523 | // If it was recently used in current lane, don't bother ('instants' take a few seconds to 524 | // register and last for 5 seconds). Also skip if number of dead players < 1/3 of lane team or 525 | // lane consists of < 20% of total team players. 526 | if (numRevives === 0 && deadPercent > 0.33 && getLanePercent() > 0.2) { 527 | console.log('We have revive, cooled down, and needed. Trigger it.'); 528 | triggerItem(ITEMS.REVIVE); 529 | } 530 | } 531 | 532 | function useMedicsIfRelevant() { 533 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 534 | var HPbuckets = g_Minigame.CurrentScene().m_rgGameData.lanes[ currentLane ].player_hp_buckets; 535 | var playersAlive = g_Minigame.CurrentScene().m_rgLaneData[ currentLane ].players - HPbuckets[0]; 536 | 537 | // Get players between health buckets 2 and 6 of 10 (0 means dead). 538 | var playersInjured = HPbuckets.slice(1,6).reduce(function(a, b) {return a + b}); 539 | 540 | if (playersAlive === 0) 541 | return; 542 | 543 | var injuredPercent = playersInjured / playersAlive; 544 | 545 | // Check if medic is already active, health is below 50%, 546 | // lane consists of > 20 % of total team players, or if really hurt players > 40% 547 | var myHP = g_Minigame.CurrentScene().m_rgPlayerData.hp; 548 | var myMaxHealth = g_Minigame.CurrentScene().m_rgPlayerTechTree.max_hp; 549 | var hpPercent = myHP / myMaxHealth; 550 | if (currentLaneHasAbility(ABILITIES.MEDIC) > 0 || 551 | ( (hpPercent > 0.5 || myHP < 1) && 552 | (getLanePercent() < 0.2 || injuredPercent < 0.4) )) { 553 | return; // no need to heal - HP is above 50% or already dead 554 | } 555 | 556 | // check if Medics is purchased and cooled down 557 | if(numItem(ITEMS.PUMPED_UP) > 0 && !isAbilityCoolingDown(ITEMS.PUMPED_UP)) { 558 | // The item PUMPED UP will be the first used in order to regenerate our health 559 | // This is because PUMPED_UP is basically a better version of "MEDIC" 560 | // and it gets dropped by monsters as loot 561 | console.log('We can pump up our HP. Trigger it.'); 562 | triggerItem(ITEMS.PUMPED_UP); 563 | } else if (hasPurchasedAbility(ABILITIES.MEDIC) && !isAbilityCoolingDown(ABILITIES.MEDIC)) { 564 | // Medics is purchased, cooled down, and needed. Trigger it. 565 | console.log('Medics is purchased, cooled down, and needed. Trigger it.'); 566 | triggerAbility(ABILITIES.MEDIC); 567 | } else if (hpPercent <= 0.5 && myHP > 0 && numItem(ITEMS.GOD_MODE) > 0 && !isAbilityCoolingDown(ITEMS.GOD_MODE)) { 568 | // Only use on yourself, not if others need healing. 569 | // Don't have Medic or Pumped Up? 570 | // We'll use godmode so we can delay our death in case the cooldowns come back. 571 | // Instead of just firing it, we could maybe only use godmode 572 | // if the medic / pumped up ability is going to be back before godmode expires 573 | console.log('We have god mode, cooled down, and needed. Trigger it.'); 574 | triggerItem(ITEMS.GOD_MODE); 575 | } else if(numItem(ITEMS.STEAL_HEALTH) > 0 && !isAbilityCoolingDown(ITEMS.STEAL_HEALTH)) { 576 | // Use Steal Health as a last resort as that 577 | // allows us to gain HP depending on our click-damage 578 | console.log("Last resort for survival: STEALING HEALTH"); 579 | triggerItem(ITEMS.STEAL_HEALTH); 580 | } 581 | } 582 | 583 | // Use Good Luck Charm if doable 584 | function useGoodLuckCharmIfRelevant() { 585 | // check if Good Luck Charms is purchased and cooled down 586 | if (hasPurchasedAbility(ABILITIES.GOOD_LUCK)) { 587 | if (isAbilityCoolingDown(ABILITIES.GOOD_LUCK)) { 588 | return; 589 | } 590 | 591 | if (! isAbilityEnabled(ABILITIES.GOOD_LUCK)) { 592 | return; 593 | } 594 | 595 | // Good Luck Charms is purchased, cooled down, and needed. Trigger it. 596 | console.log('Good Luck Charms is purchased, cooled down, and needed. Trigger it.'); 597 | triggerAbility(ABILITIES.GOOD_LUCK); 598 | } 599 | } 600 | 601 | function useClusterBombIfRelevant() { 602 | //Check if Cluster Bomb is purchased and cooled down 603 | if (hasPurchasedAbility(ABILITIES.CLUSTER_BOMB)) { 604 | if (isAbilityCoolingDown(ABILITIES.CLUSTER_BOMB)) { 605 | return; 606 | } 607 | 608 | //Check lane has monsters to explode 609 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 610 | var enemyCount = 0; 611 | var enemySpawnerExists = false; 612 | //Count each slot in lane 613 | for (var i = 0; i < 4; i++) { 614 | var enemy = g_Minigame.CurrentScene().GetEnemy(currentLane, i); 615 | if (enemy) { 616 | enemyCount++; 617 | if (enemy.m_data.type == 0) { 618 | enemySpawnerExists = true; 619 | } 620 | } 621 | } 622 | //Bombs away if spawner and 2+ other monsters 623 | if (enemySpawnerExists && enemyCount >= 3) { 624 | // Wait 60 seconds if the cooldown ability 625 | // is within 1 minute of coming back 626 | if (getCooldownTime(ABILITIES.COOLDOWN) > 60 || !hasPurchasedAbility(ABILITIES.COOLDOWN) || (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN))) { 627 | if (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN) && !currentLaneHasAbility(ABILITIES.COOLDOWN)) { 628 | console.log("Cooling down prior to long-downtime ability use"); 629 | triggerAbility(ABILITIES.COOLDOWN); 630 | } else { 631 | triggerAbility(ABILITIES.CLUSTER_BOMB); 632 | } 633 | } 634 | } 635 | } 636 | } 637 | 638 | function useNapalmIfRelevant() { 639 | //Check if Napalm is purchased and cooled down 640 | if (hasPurchasedAbility(ABILITIES.NAPALM)) { 641 | if (isAbilityCoolingDown(ABILITIES.NAPALM)) { 642 | return; 643 | } 644 | 645 | //Check lane has monsters to burn 646 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 647 | var enemyCount = 0; 648 | var enemySpawnerExists = false; 649 | //Count each slot in lane 650 | for (var i = 0; i < 4; i++) { 651 | var enemy = g_Minigame.CurrentScene().GetEnemy(currentLane, i); 652 | if (enemy) { 653 | enemyCount++; 654 | if (enemy.m_data.type == 0) { 655 | enemySpawnerExists = true; 656 | } 657 | } 658 | } 659 | //Burn them all if spawner and 2+ other monsters 660 | if (enemySpawnerExists && enemyCount >= 3) { 661 | // Wait 60 seconds if the cooldown ability 662 | // is within 1 minute of coming back 663 | if (getCooldownTime(ABILITIES.COOLDOWN) > 60 || !hasPurchasedAbility(ABILITIES.COOLDOWN) || (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN))) { 664 | if (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN) && !currentLaneHasAbility(ABILITIES.COOLDOWN)) { 665 | console.log("Cooling down prior to long-downtime ability use"); 666 | triggerAbility(ABILITIES.COOLDOWN); 667 | } else { 668 | triggerAbility(ABILITIES.NAPALM); 669 | } 670 | } 671 | } 672 | } 673 | } 674 | 675 | function useMoraleBoosterIfRelevant() { 676 | // Check if Morale Booster is purchased 677 | if (hasPurchasedAbility(ABILITIES.MORALE_BOOSTER)) { 678 | if (isAbilityCoolingDown(ABILITIES.MORALE_BOOSTER)) { 679 | return; 680 | } 681 | //Check lane has monsters so the hype isn't wasted 682 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 683 | var enemyCount = 0; 684 | var enemySpawnerExists = false; 685 | //Count each slot in lane 686 | for (var i = 0; i < 4; i++) { 687 | var enemy = g_Minigame.CurrentScene().GetEnemy(currentLane, i); 688 | if (enemy) { 689 | enemyCount++; 690 | if (enemy.m_data.type == 0) { 691 | enemySpawnerExists = true; 692 | } 693 | } 694 | } 695 | //Hype everybody up! 696 | if (enemySpawnerExists && enemyCount >= 3) { 697 | console.log("Morale Booster is purchased, cooled down, and needed. Rally around, everyone!"); 698 | triggerAbility(ABILITIES.MORALE_BOOSTER); 699 | } 700 | } 701 | } 702 | 703 | function useTacticalNukeIfRelevant() { 704 | // Check if Tactical Nuke is purchased 705 | if(hasPurchasedAbility(ABILITIES.NUKE)) { 706 | if (isAbilityCoolingDown(ABILITIES.NUKE)) { 707 | return; 708 | } 709 | 710 | //Check that the lane has a spawner and record it's health percentage 711 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 712 | var enemySpawnerExists = false; 713 | var enemySpawnerHealthPercent = 0.0; 714 | //Count each slot in lane 715 | for (var i = 0; i < 4; i++) { 716 | var enemy = g_Minigame.CurrentScene().GetEnemy(currentLane, i); 717 | if (enemy) { 718 | if (enemy.m_data.type == 0) { 719 | enemySpawnerExists = true; 720 | enemySpawnerHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 721 | } 722 | } 723 | } 724 | 725 | // If there is a spawner and it's health is between 60% and 30%, nuke it! 726 | if (enemySpawnerExists && enemySpawnerHealthPercent < 0.6 && enemySpawnerHealthPercent > 0.3) { 727 | console.log("Tactical Nuke is purchased, cooled down, and needed. Nuke 'em."); 728 | // Wait 60 seconds if the cooldown ability 729 | // is within 1 minute of coming back 730 | if (getCooldownTime(ABILITIES.COOLDOWN) > 60 || !hasPurchasedAbility(ABILITIES.COOLDOWN) || (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN))) { 731 | if (hasPurchasedAbility(ABILITIES.COOLDOWN) && !isAbilityCoolingDown(ABILITIES.COOLDOWN) && !currentLaneHasAbility(ABILITIES.COOLDOWN)) { 732 | console.log("Cooling down prior to long-downtime ability use"); 733 | triggerAbility(ABILITIES.COOLDOWN); 734 | } else { 735 | triggerAbility(ABILITIES.NUKE); 736 | } 737 | } 738 | } 739 | } 740 | } 741 | 742 | function useMetalDetectorAndTreasureIfRelevant() { 743 | 744 | var enemy = g_Minigame.m_CurrentScene.GetEnemy(g_Minigame.m_CurrentScene.m_rgPlayerData.current_lane, g_Minigame.m_CurrentScene.m_rgPlayerData.target); 745 | 746 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 747 | var enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 748 | 749 | if (enemyBossHealthPercent < 0.3) { 750 | if (hasPurchasedAbility(ABILITIES.METAL_DETECTOR) && !isAbilityCoolingDown(ABILITIES.METAL_DETECTOR)) { 751 | console.log('Metal detector is purchased and cooled down, Triggering it on boss'); 752 | triggerAbility(ABILITIES.METAL_DETECTOR); 753 | } 754 | if (numItem(ITEMS.TREASURE) && !isAbilityCoolingDown(ITEMS.TREASURE)) { 755 | console.log('Treasure! is purchased and cooled down, Triggering it on boss'); 756 | triggerItem(ITEMS.TREASURE); 757 | } 758 | } 759 | } 760 | } 761 | 762 | function useCrippleSpawnerIfRelevant() { 763 | // Check if Cripple Spawner is available 764 | if(numItem(ITEMS.CRIPPLE_SPAWNER) > 0) { 765 | if (isAbilityCoolingDown(ITEMS.CRIPPLE_SPAWNER)) { 766 | return; 767 | } 768 | 769 | //Check that the lane has a spawner and record it's health percentage 770 | var currentLane = g_Minigame.CurrentScene().m_nExpectedLane; 771 | var enemySpawnerExists = false; 772 | var enemySpawnerHealthPercent = 0.0; 773 | //Count each slot in lane 774 | for (var i = 0; i < 4; i++) { 775 | var enemy = g_Minigame.CurrentScene().GetEnemy(currentLane, i); 776 | if (enemy) { 777 | if (enemy.m_data.type == 0) { 778 | enemySpawnerExists = true; 779 | enemySpawnerHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 780 | } 781 | } 782 | } 783 | 784 | // If there is a spawner and it's health is above 95%, cripple it! 785 | if (enemySpawnerExists && enemySpawnerHealthPercent > 0.9 && Math.random() < 1/10) { 786 | console.log("Cripple Spawner available, and needed. Cripple 'em."); 787 | triggerItem(ITEMS.CRIPPLE_SPAWNER); 788 | } 789 | } 790 | } 791 | 792 | function useGoldRainIfRelevant() { 793 | // Check if gold rain is purchased 794 | if (numItem(ITEMS.GOLD_RAIN) > 0) { 795 | if (isAbilityCoolingDown(ITEMS.GOLD_RAIN)) { 796 | return; 797 | } 798 | 799 | if(Math.random() > g_Minigame.CurrentScene().m_rgGameData.level / 10000) { 800 | return; 801 | } 802 | 803 | var enemy = g_Minigame.m_CurrentScene.GetEnemy(g_Minigame.m_CurrentScene.m_rgPlayerData.current_lane, g_Minigame.m_CurrentScene.m_rgPlayerData.target); 804 | // check if current target is a boss, otherwise its not worth using the gold rain 805 | if (enemy && enemy.m_data.type == ENEMY_TYPE.BOSS) { 806 | var enemyBossHealthPercent = enemy.m_flDisplayedHP / enemy.m_data.max_hp; 807 | 808 | if (enemyBossHealthPercent >= 0.3) { // We want sufficient time for the gold rain to be applicable 809 | // Gold Rain is purchased, cooled down, and needed. Trigger it. 810 | console.log('Gold rain is purchased and cooled down, Triggering it on boss'); 811 | triggerItem(ITEMS.GOLD_RAIN); 812 | } 813 | } 814 | } 815 | } 816 | 817 | // Upgrades the crit by 1% if we have the CRIT item available for use. 818 | function useCritIfRelevant() { 819 | if(numItem(ITEMS.CRIT) > 0 && !isAbilityCoolingDown(ITEMS.CRIT)) 820 | { 821 | triggerItem(ITEMS.CRIT); 822 | } 823 | } 824 | 825 | //If player is dead, call respawn method 826 | function attemptRespawn() { 827 | if ((g_Minigame.CurrentScene().m_bIsDead) && 828 | ((g_Minigame.CurrentScene().m_rgPlayerData.time_died) + 5) < (g_Minigame.CurrentScene().m_nTime)) { 829 | RespawnPlayer(); 830 | } 831 | } 832 | 833 | function isAbilityActive(abilityId) { 834 | return g_Minigame.CurrentScene().bIsAbilityActive(abilityId); 835 | } 836 | 837 | function numItem(itemId) { 838 | for ( var i = 0; i < g_Minigame.CurrentScene().m_rgPlayerTechTree.ability_items.length; ++i ) { 839 | var abilityItem = g_Minigame.CurrentScene().m_rgPlayerTechTree.ability_items[i]; 840 | if (abilityItem.ability == itemId) { 841 | return abilityItem.quantity; 842 | } 843 | } 844 | return 0; 845 | } 846 | 847 | // This calculates a 5 second moving average of clicks per second based 848 | // on the values that the game is recording. 849 | function updateAvgClickRate() { 850 | // Make sure we have updated info from the game first 851 | if (previousTickTime != g_Minigame.CurrentScene().m_nLastTick){ 852 | totalClicksPastFiveSeconds -= avgClickRate; 853 | totalClicksPastFiveSeconds += g_Minigame.CurrentScene().m_nLastClicks / ((g_Minigame.CurrentScene().m_nLastTick - previousTickTime) / 1000); 854 | avgClickRate = totalClicksPastFiveSeconds / 5; 855 | previousTickTime = g_Minigame.CurrentScene().m_nLastTick; 856 | } 857 | } 858 | // disable enemy flinching animation when they get hit 859 | function disableFlinchingAnimation() { 860 | if (CEnemy !== undefined) { 861 | CEnemy.prototype.TakeDamage = function() {}; 862 | CEnemySpawner.prototype.TakeDamage = function() {}; 863 | CEnemyBoss.prototype.TakeDamage = function() {}; 864 | } 865 | } 866 | // disable damage text from clicking 867 | function disableDamageText() { 868 | g_Minigame.CurrentScene().DoClickEffect = function() {}; 869 | g_Minigame.CurrentScene().DoCritEffect = function( nDamage, x, y, additionalText ) {}; 870 | } 871 | 872 | function getCooldownTime(abilityId) { 873 | return g_Minigame.CurrentScene().GetCooldownForAbility(abilityId); 874 | } 875 | 876 | function isAbilityCoolingDown(abilityId) { 877 | return g_Minigame.CurrentScene().GetCooldownForAbility(abilityId) > 0; 878 | } 879 | 880 | function hasPurchasedAbility(abilityId) { 881 | // each bit in unlocked_abilities_bitfield corresponds to an ability. 882 | // the above condition checks if the ability's bit is set or cleared. I.e. it checks if 883 | // the player has purchased the specified ability. 884 | return (1 << abilityId) & g_Minigame.CurrentScene().m_rgPlayerTechTree.unlocked_abilities_bitfield; 885 | } 886 | 887 | function triggerItem(itemId) { 888 | var elem = document.getElementById('abilityitem_' + itemId); 889 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 890 | g_Minigame.CurrentScene().TryAbility(document.getElementById('abilityitem_' + itemId).childElements()[0]); 891 | } 892 | } 893 | 894 | function triggerAbility(abilityId) { 895 | var elem = document.getElementById('ability_' + abilityId); 896 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 897 | g_Minigame.CurrentScene().TryAbility(document.getElementById('ability_' + abilityId).childElements()[0]); 898 | } 899 | } 900 | 901 | function toggleAbilityVisibility(abilityId, show) { 902 | var vis = show === true ? "visible" : "hidden"; 903 | 904 | var elem = document.getElementById('ability_' + abilityId); 905 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 906 | elem.childElements()[0].style.visibility = vis; 907 | } 908 | } 909 | 910 | function disableAbility(abilityId) { 911 | toggleAbilityVisibility(abilityId, false); 912 | } 913 | 914 | function enableAbility(abilityId) { 915 | toggleAbilityVisibility(abilityId, true); 916 | } 917 | 918 | function isAbilityEnabled(abilityId) { 919 | var elem = document.getElementById('ability_' + abilityId); 920 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 921 | return elem.childElements()[0].style.visibility == "visible"; 922 | } 923 | return false; 924 | } 925 | 926 | function toggleAbilityItemVisibility(abilityId, show) { 927 | var vis = show === true ? "visible" : "hidden"; 928 | 929 | var elem = document.getElementById('abilityitem_' + abilityId); 930 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 931 | elem.childElements()[0].style.visibility = show; 932 | } 933 | } 934 | 935 | function disableAbilityItem(abilityId) { 936 | toggleAbilityItemVisibility(abilityId, false); 937 | } 938 | 939 | function enableAbilityItem(abilityId) { 940 | toggleAbilityItemVisibility(abilityId, true); 941 | } 942 | 943 | function isAbilityItemEnabled(abilityId) { 944 | var elem = document.getElementById('abilityitem_' + abilityId); 945 | if (elem && elem.childElements() && elem.childElements().length >= 1) { 946 | return elem.childElements()[0].style.visibility == "visible"; 947 | } 948 | return false; 949 | } 950 | 951 | function currentLaneHasAbility(abilityID) { 952 | var lane = g_Minigame.CurrentScene().m_rgPlayerData.current_lane; 953 | if (typeof(g_Minigame.m_CurrentScene.m_rgLaneData[lane].abilities[abilityID]) == 'undefined') 954 | return 0; 955 | return g_Minigame.m_CurrentScene.m_rgLaneData[lane].abilities[abilityID]; 956 | } 957 | 958 | function getLanePercent(lane) { 959 | // Gets the percentage of total players in current lane. Useful in deciding if an ability is worthwhile to use 960 | 961 | lane = lane || g_Minigame.CurrentScene().m_nExpectedLane 962 | var currentPlayers = g_Minigame.CurrentScene().m_rgLaneData[ lane ].players 963 | var numPlayers = 0; 964 | for (var i=0; i < g_Minigame.CurrentScene().m_rgGameData.lanes.length; i++) { 965 | numPlayers += g_Minigame.CurrentScene().m_rgLaneData[ i ].players; 966 | } 967 | 968 | if (numPlayers === 0) 969 | return 0; 970 | 971 | return currentPlayers / numPlayers; 972 | } 973 | 974 | function clickTheThing() { 975 | // If we're going to be clicking, we should reset g_msTickRate 976 | // There's a reddit thread about why and we might as well be safe 977 | g_msTickRate = 1100; 978 | 979 | g_Minigame.m_CurrentScene.DoClick({ 980 | data: { 981 | getLocalPosition: function () { 982 | var enemy = g_Minigame.m_CurrentScene.GetEnemy( 983 | g_Minigame.m_CurrentScene.m_rgPlayerData.current_lane, 984 | g_Minigame.m_CurrentScene.m_rgPlayerData.target); 985 | var laneOffset = enemy.m_nLane * 440; 986 | 987 | return { 988 | x: enemy.m_Sprite.position.x - laneOffset, 989 | y: enemy.m_Sprite.position.y - 52 990 | } 991 | } 992 | } 993 | }); 994 | 995 | timer--; 996 | 997 | // clear the click timer if it's done. 998 | if (timer <= 0){ 999 | clearInterval(clickTimer); 1000 | console.log('It has stopped raining.'); 1001 | timer = 0; 1002 | } 1003 | } 1004 | 1005 | function startGoldRainClick() { 1006 | var activeAbilities = g_Minigame.CurrentScene().m_rgLaneData[g_Minigame.CurrentScene().m_nExpectedLane].abilities; 1007 | 1008 | // check if the current lane has Gold Rain active 1009 | if (activeAbilities[ITEMS.GOLD_RAIN] !== undefined) { 1010 | clearInterval(clickTimer); 1011 | if (timer <= 0) { 1012 | console.log('Let the GOLD rain!'); 1013 | } 1014 | clickTimer = window.setInterval(clickTheThing, 1000 / clickRate); 1015 | timer = clickRate * 2; // click for 2 seconds; this will be topped off as long as Gold Rain is still active. 1016 | } 1017 | } 1018 | 1019 | function createOptionsMenu() { 1020 | // remove any existing options menu before adding a new one 1021 | jQuery('.options_box').remove(); 1022 | 1023 | // Remove the junk at the bottom to make room for options 1024 | node = document.getElementById("footer"); 1025 | if (node && node.parentNode) { 1026 | node.parentNode.removeChild( node ); 1027 | } 1028 | jQuery('.leave_game_helper').remove(); 1029 | 1030 | // Make space for option menu 1031 | var options_menu = document.querySelector(".game_options"); 1032 | var sfx_btn = document.querySelector(".toggle_sfx_btn"); 1033 | sfx_btn.style.marginLeft = "2px"; 1034 | sfx_btn.style.marginRight = "7px"; 1035 | sfx_btn.style.cssFloat = "right"; 1036 | sfx_btn.style.styleFloat = "right"; 1037 | var music_btn = document.querySelector(".toggle_music_btn"); 1038 | music_btn.style.marginRight = "2px"; 1039 | music_btn.style.cssFloat = "right"; 1040 | music_btn.style.styleFloat = "right"; 1041 | var leave_btn = document.querySelector(".leave_game_btn"); 1042 | leave_btn.style.marginRight = "2px"; 1043 | leave_btn.style.cssFloat = "right"; 1044 | leave_btn.style.styleFloat = "right"; 1045 | 1046 | var pagecontent = document.querySelector(".pagecontent"); 1047 | pagecontent.style.padding = "0"; 1048 | 1049 | var info_box = document.createElement('div'); 1050 | options_menu.insertBefore(info_box, sfx_btn); 1051 | 1052 | info_box.innerHTML = '
OPTIONS
' + ((typeof GM_info !== "undefined") ? ' (v' + GM_info.script.version + ')' : ''); 1053 | 1054 | // reset the CSS for the info box for aesthetics 1055 | info_box.className = "options_box"; 1056 | info_box.style.backgroundColor = "#000000"; 1057 | info_box.style.width = "300px"; 1058 | info_box.style.padding = "12px"; 1059 | info_box.style.boxShadow = "2px 2px 0 rgba( 0, 0, 0, 0.6 )"; 1060 | info_box.style.color = "#ededed"; 1061 | info_box.style.margin = "2px auto"; 1062 | info_box.style.overflow = "auto"; 1063 | info_box.style.cssFloat = "left"; 1064 | info_box.style.styleFloat = "left"; 1065 | 1066 | var options = document.createElement("div"); 1067 | options.style["-moz-column-count"] = 1; 1068 | options.style["-webkit-column-count"] = 1; 1069 | options.style["column-count"] = 1; 1070 | options.style.width = "100%"; 1071 | options.style.float = "left"; 1072 | 1073 | options.appendChild(makeNumber("setAutoClickRate", "CPS during gold rain", "45px", clickRate, 0, 30, updateAutoClickRate)); 1074 | options.appendChild(makeCheckBox("purchaseUpgradeToggle", "Auto upgrade items", purchaseUpgradeToggle, toggleAutoUpgrade)); 1075 | 1076 | info_box.appendChild(options); 1077 | } 1078 | 1079 | function makeCheckBox(name, desc, state, listener) { 1080 | var label= document.createElement("label"); 1081 | var description = document.createTextNode(desc); 1082 | var checkbox = document.createElement("input"); 1083 | 1084 | checkbox.type = "checkbox"; 1085 | checkbox.name = name; 1086 | checkbox.checked = state; 1087 | checkbox.onclick = listener; 1088 | window[checkbox.name] = checkbox.checked; 1089 | 1090 | label.appendChild(checkbox); 1091 | label.appendChild(description); 1092 | 1093 | label.appendChild(document.createElement("br")); 1094 | return label; 1095 | } 1096 | 1097 | function makeNumber(name, desc, width, value, min, max, listener) { 1098 | var label= document.createElement("label"); 1099 | var description = document.createTextNode(desc); 1100 | var number = document.createElement("input"); 1101 | 1102 | number.type = "number"; 1103 | number.name = name; 1104 | number.style.width = width; 1105 | number.style.marginRight = "5px"; 1106 | number.value = value; 1107 | number.min = min; 1108 | number.max = max; 1109 | number.onchange = listener; 1110 | number.addEventListener("keypress", function (evt) { 1111 | if (evt.which === 13) 1112 | number.onchange(); 1113 | else if (evt.which < 48 || evt.which > 57) 1114 | evt.preventDefault(); 1115 | }); 1116 | window[number.name] = number; 1117 | 1118 | label.appendChild(number); 1119 | label.appendChild(description); 1120 | label.appendChild(document.createElement("br")); 1121 | return label; 1122 | } 1123 | 1124 | function handleCheckBox(event) { 1125 | var checkbox = event.target; 1126 | 1127 | window[checkbox.name] = checkbox.checked; 1128 | return checkbox.checked; 1129 | } 1130 | 1131 | function updateAutoClickRate(event) { 1132 | if(event !== undefined && event.target.value != "") { 1133 | 1134 | var val = event.target.value; 1135 | if (val > event.target.max) 1136 | clickRate = event.target.max; 1137 | else if (val < event.target.min) 1138 | clickRate = event.target.min; 1139 | else 1140 | clickRate = val; 1141 | console.log('Click rate is now ' + clickRate); 1142 | } 1143 | } 1144 | 1145 | function toggleAutoUpgrade(event) { 1146 | if(event !== undefined) { 1147 | purchaseUpgradeToggle = handleCheckBox(event); 1148 | } 1149 | } 1150 | 1151 | var thingTimer = window.setInterval(function(){ 1152 | if (g_Minigame && g_Minigame.CurrentScene().m_bRunning && g_Minigame.CurrentScene().m_rgPlayerTechTree) { 1153 | window.clearInterval(thingTimer); 1154 | firstRun(); 1155 | thingTimer = window.setInterval(doTheThing, 1000); 1156 | } 1157 | }, 1000); 1158 | --------------------------------------------------------------------------------