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