├── .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 |
--------------------------------------------------------------------------------