Advertisement
sil3nced

Special Stats

Mar 19th, 2018
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.80 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Special Gym Ratios
  3. // @namespace RGiskard.hanksRatio
  4. // @version 2.2.6
  5. // @description Monitors battle stat ratios and provides warnings if they approach levels that would preclude access to special gyms
  6. // @author RGiskard [1953860]
  7. // @include *.torn.com/gym.php*
  8. // @require http://code.jquery.com/jquery-latest.js
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // Based off of Torn Gym Pony by Zanoab (http://puu.sh/jFtro/1af393771e.user.js).
  13.  
  14.  
  15. // Maximum amount below the stat limit another stat can be before we start warning the player.
  16. var statSafeDistance = localStorage.statSafeDistance;
  17. if (statSafeDistance === null) {
  18. statSafeDistance = 1000000;
  19. }
  20.  
  21.  
  22. // A second method, "Baldr's Ratio", is in this code, but the ability to select it has been
  23. // deliberately excluded for public release. This has been done for clarity, as there is
  24. // no accompanying information about what this ratio is. Those who would like to use it,
  25. // which unlocks a specialty gym that is one of the same stats as a combo gym
  26. // (e.g.: Frontline Fitness (str/spd) and Gym 3000 (str), can uncomment the lines of code
  27. // adding those options to $specialistGymBuild.
  28.  
  29.  
  30. jQuery.noConflict(true)(document).ready(function($) {
  31. var cleanNumber = function(a) {
  32. return Number(a.replace(/[$,]/g, "").trim());
  33. };
  34.  
  35. /**
  36. * Formats a number into an abbreviated string with an appropriate trailing descriptive unit
  37. * up to 't' for trillion.
  38. * @param {float} number the number to be formatted
  39. * @param {int} maxFractionDigits the maximum number of fractional digits to display
  40. * @returns a string representing the number, abbreviated if appropriate
  41. **/
  42. var FormatAbbreviatedNumber = function(number, maxFractionDigits) {
  43. var abbreviations = [];
  44. abbreviations[0] = '';
  45. abbreviations[1] = 'k';
  46. abbreviations[2] = 'm';
  47. abbreviations[3] = 'b';
  48. abbreviations[4] = 't';
  49.  
  50. var outputNumber = number;
  51. var abbreviationIndex = 0;
  52. for (; outputNumber >= 1000 && abbreviationIndex < abbreviations.length; ++abbreviationIndex) {
  53. outputNumber = outputNumber / 1000;
  54. }
  55.  
  56. return outputNumber.toLocaleString('EN', { maximumFractionDigits : maxFractionDigits })
  57. + abbreviations[abbreviationIndex];
  58. };
  59.  
  60. var getStats = function($doc) {
  61. var ReplaceStatValueAndReturnCleanNumber = function(elementId) {
  62. var $statTotalElement = $doc.find('#' + elementId);
  63. if ($statTotalElement.size() == 0) throw 'No element found with id "' + elementId + '".';
  64. var numericalValue = cleanNumber($statTotalElement.text());
  65. if (numericalValue > 1000000000) {
  66. // If the number is very large, adding the current % may cause numbers above 1b to wrap.
  67. $statTotalElement.text(numericalValue.toLocaleString('EN', { maximumFractionDigits : 0 }));
  68. } else if (numericalValue > 100000000) {
  69. // Even 100m might be causing the dexterity title bar to wrap.
  70. $statTotalElement.text(numericalValue.toLocaleString('EN', { minimumFractionDigits : 1, maximumFractionDigits : 1 }));
  71. }
  72. return numericalValue;
  73. };
  74. $doc = $($doc || document);
  75. return {
  76. strength: ReplaceStatValueAndReturnCleanNumber('strengthTotal'),
  77. defense: ReplaceStatValueAndReturnCleanNumber('defenceTotal'),
  78. speed: ReplaceStatValueAndReturnCleanNumber('speedTotal'),
  79. dexterity: ReplaceStatValueAndReturnCleanNumber('dexterityTotal'),
  80. };
  81. };
  82.  
  83. var noBuildKeyValue = {value: 'none', text: 'No specialty gyms'};
  84. var defenseDexterityGymKeyValue = {value: 'balboas', text: 'Defense and dexterity specialist',
  85. stat1: 'defense', stat2: 'dexterity', secondarystat1: 'strength', secondarystat2: 'speed'};
  86. var strengthSpeedGymKeyValue = {value: 'frontline', text: 'Strength and speed specialist',
  87. stat1: 'strength', stat2: 'speed', secondarystat1: 'defense', secondarystat2: 'dexterity'};
  88. var strengthComboGymKeyValue = {value: 'frontlinegym3000', text: 'Strength combo specialist (Baldr\'s Ratio)', stat: 'strength', combogym: strengthSpeedGymKeyValue};
  89. var defenseComboGymKeyValue = {value: 'balboasisoyamas', text: 'Defense combo specialist (Baldr\'s Ratio)', stat: 'defense', combogym: defenseDexterityGymKeyValue};
  90. var speedComboGymKeyValue = {value: 'frontlinetotalrebound', text: 'Speed combo specialist (Baldr\'s Ratio)', stat: 'speed', combogym: strengthSpeedGymKeyValue};
  91. var dexterityComboGymKeyValue = {value: 'balboaselites', text: 'Dexterity combo specialist (Baldr\'s Ratio)', stat: 'dexterity', combogym: defenseDexterityGymKeyValue};
  92. var strengthGymKeyValue = {value: 'gym3000', text: 'Strength specialist (Hank\'s Ratio)', stat: 'strength', combogym: defenseDexterityGymKeyValue};
  93. var defenseGymKeyValue = {value: 'isoyamas', text: 'Defense specialist (Hank\'s Ratio)', stat: 'defense', combogym: strengthSpeedGymKeyValue};
  94. var speedGymKeyValue = {value: 'totalrebound', text: 'Speed specialist (Hank\'s Ratio)', stat: 'speed', combogym: defenseDexterityGymKeyValue};
  95. var dexterityGymKeyValue = {value: 'elites', text: 'Dexterity specialist (Hank\'s Ratio)', stat: 'dexterity', combogym: strengthSpeedGymKeyValue};
  96. function GetStoredGymKeyValuePair() {
  97. if (localStorage.specialistGymType == defenseDexterityGymKeyValue.value) return defenseDexterityGymKeyValue;
  98. if (localStorage.specialistGymType == strengthSpeedGymKeyValue.value) return strengthSpeedGymKeyValue;
  99. if (localStorage.specialistGymType == strengthComboGymKeyValue.value) return strengthComboGymKeyValue;
  100. if (localStorage.specialistGymType == defenseComboGymKeyValue.value) return defenseComboGymKeyValue;
  101. if (localStorage.specialistGymType == speedComboGymKeyValue.value) return speedComboGymKeyValue;
  102. if (localStorage.specialistGymType == dexterityComboGymKeyValue.value) return dexterityComboGymKeyValue;
  103. if (localStorage.specialistGymType == strengthGymKeyValue.value) return strengthGymKeyValue;
  104. if (localStorage.specialistGymType == defenseGymKeyValue.value) return defenseGymKeyValue;
  105. if (localStorage.specialistGymType == speedGymKeyValue.value) return speedGymKeyValue;
  106. if (localStorage.specialistGymType == dexterityGymKeyValue.value) return dexterityGymKeyValue;
  107. return noBuildKeyValue;
  108. }
  109.  
  110.  
  111. var $hanksRatioDiv = $('<div></div>');
  112. var $titleDiv = $('<div>', {'class': 'title-black top-round', 'aria-level': '5', 'text': 'Special Gym Ratios'})
  113. .css('margin-top', '10px');
  114. $hanksRatioDiv.append($titleDiv);
  115. var $bottomDiv = $('<div class="bottom-round gym-box cont-gray p10"></div>');
  116. $bottomDiv.append($('<p class="sub-title">Select desired specialist build:</p>'));
  117. var $specialistGymBuild = $('<select>', {'class': 'vinkuun-enemeyDifficulty'}).css('margin-top', '10px').on('change', function() {
  118. localStorage.specialistGymType = $specialistGymBuild.val();
  119. });
  120. $specialistGymBuild.append($('<option>', noBuildKeyValue));
  121. $specialistGymBuild.append($('<option>', defenseDexterityGymKeyValue));
  122. $specialistGymBuild.append($('<option>', strengthSpeedGymKeyValue));
  123. $specialistGymBuild.append($('<option>', strengthComboGymKeyValue));
  124. $specialistGymBuild.append($('<option>', defenseComboGymKeyValue));
  125. $specialistGymBuild.append($('<option>', speedComboGymKeyValue));
  126. $specialistGymBuild.append($('<option>', dexterityComboGymKeyValue));
  127. $specialistGymBuild.append($('<option>', strengthGymKeyValue));
  128. $specialistGymBuild.append($('<option>', defenseGymKeyValue));
  129. $specialistGymBuild.append($('<option>', speedGymKeyValue));
  130. $specialistGymBuild.append($('<option>', dexterityGymKeyValue));
  131. localStorage.specialistGymType = GetStoredGymKeyValuePair().value; // In case there is bad data, replace it.
  132. $specialistGymBuild.val(GetStoredGymKeyValuePair().value);
  133. $bottomDiv.append($specialistGymBuild);
  134. // $bottomDiv.append('<p class="sub-title" id="trainingRecommendations" style="margin-top: 10px; display: none;"></p>');
  135. $hanksRatioDiv.append($bottomDiv);
  136. $('#gym_content_left').append($hanksRatioDiv);
  137.  
  138. var oldTotal = 0;
  139. var oldBuild = '';
  140. setInterval(function() {
  141. var stats = getStats();
  142. var total = 0;
  143. var highestSecondaryStat = 0;
  144. for (var stat in stats) {
  145. total += stats[stat];
  146. if (GetStoredGymKeyValuePair().stat && GetStoredGymKeyValuePair().stat != stat && stats[stat] > highestSecondaryStat) {
  147. highestSecondaryStat = stats[stat];
  148. }
  149. }
  150. var currentBuild = $specialistGymBuild.val();
  151.  
  152. if (oldTotal == total && oldBuild == currentBuild) {
  153. return;
  154. }
  155.  
  156. if (currentBuild == noBuildKeyValue.value) {
  157. // Clear the training info in case it exists.
  158. $('.ability-boxes > .gym-container > .title-black').each(function(index, element) {
  159. var $element = $(element);
  160. var $statInfoDiv = $element.parent().find('.msg').find('p');
  161. var statInfoHtml = $statInfoDiv[0].innerHTML;
  162. $statInfoDiv[0].innerHTML = statInfoHtml.split('per train</span>')[0] + 'per train</span>';
  163. });
  164. return;
  165. }
  166.  
  167. var isComboGymOnlyRatio = (
  168. localStorage.specialistGymType == defenseDexterityGymKeyValue.value ||
  169. localStorage.specialistGymType == strengthSpeedGymKeyValue.value);
  170. var isComboGymCombinedRatio = (
  171. localStorage.specialistGymType == strengthComboGymKeyValue.value ||
  172. localStorage.specialistGymType == defenseComboGymKeyValue.value ||
  173. localStorage.specialistGymType == speedComboGymKeyValue.value ||
  174. localStorage.specialistGymType == dexterityComboGymKeyValue.value);
  175. var isSingleGymRatio = (
  176. localStorage.specialistGymType == strengthGymKeyValue.value ||
  177. localStorage.specialistGymType == defenseGymKeyValue.value ||
  178. localStorage.specialistGymType == speedGymKeyValue.value ||
  179. localStorage.specialistGymType == dexterityGymKeyValue.value);
  180.  
  181. // The combined total of the primary stats must be 25% higher than the total of the secondary stats.
  182. var minPrimaryComboSum = 0; // The minimum amount the combined primary stats must be to unlock the gym based on the secondary stat sum.
  183. var maxSecondaryComboSum = 0; // The maximum amount the combined secondary stats must be to unlock the gym based on the primary stat sum.
  184. // The primary stat needs to be 25% higher than the second highest stat.
  185. var minPrimaryStat = 0;
  186. var maxSecondaryStat = 0;
  187. var comboGymKeyValuePair = noBuildKeyValue;
  188. var primaryGymKeyValuePair = noBuildKeyValue;
  189. if (isComboGymOnlyRatio) {
  190. comboGymKeyValuePair = GetStoredGymKeyValuePair();
  191. } else if (isComboGymCombinedRatio || isSingleGymRatio) {
  192. primaryGymKeyValuePair = GetStoredGymKeyValuePair();
  193. comboGymKeyValuePair = primaryGymKeyValuePair.combogym;
  194. minPrimaryStat = highestSecondaryStat * 1.25;
  195. maxSecondaryStat = stats[primaryGymKeyValuePair.stat] / 1.25;
  196. } else {
  197. console.debug('Somehow attempted to calculate stat requirements for invalid gym: ' + GetStoredGymKeyValuePair());
  198. return;
  199. }
  200. minPrimaryComboSum = (stats[comboGymKeyValuePair.secondarystat1] + stats[comboGymKeyValuePair.secondarystat2]) * 1.25;
  201. maxSecondaryComboSum = (stats[comboGymKeyValuePair.stat1] + stats[comboGymKeyValuePair.stat2]) / 1.25;
  202.  
  203. var distanceFromComboGymMin = minPrimaryComboSum - stats[comboGymKeyValuePair.stat1] - stats[comboGymKeyValuePair.stat2];
  204. var distanceToComboGymMax = maxSecondaryComboSum - stats[comboGymKeyValuePair.secondarystat1] - stats[comboGymKeyValuePair.secondarystat2];
  205.  
  206. $('.ability-boxes > .gym-container > .title-black').each(function(index, element) {
  207. var $element = $(element);
  208. var title = $element.children().first();
  209. var stat = $element.attr('zStat');
  210. if (!stat) {
  211. stat = title.text().toLowerCase();
  212. $element.attr('zStat', stat);
  213. }
  214. if (stats[stat]) {
  215. var a = (stats[stat] / total * 100).toFixed(2);
  216. var b = title.children('.pony');
  217. if (!b.length) b = $("<span class='pony' style='margin-left:10px'></span>").appendTo(title);
  218. b.text(a + "%");
  219.  
  220. var gymStatus;
  221. if (isComboGymOnlyRatio) {
  222. if (stat == comboGymKeyValuePair.stat1 || stat == comboGymKeyValuePair.stat2) {
  223. var statIdentifierString = GetStatAbbreviation(comboGymKeyValuePair.stat1).capitalizeFirstLetter() +
  224. ' + ' + GetStatAbbreviation(comboGymKeyValuePair.stat2);
  225. if (distanceFromComboGymMin > 0) {
  226. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceFromComboGymMin, 1) + ' too low!</span>';
  227. } else if (distanceFromComboGymMin < statSafeDistance) {
  228. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(-distanceFromComboGymMin, 1) + ' above the limit.</span>';
  229. } else {
  230. gymStatus = '<span class="t-green">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(-distanceFromComboGymMin, 1) + ' above the limit.</span>';
  231. }
  232. } else {
  233. var statIdentifierString = GetStatAbbreviation(comboGymKeyValuePair.secondarystat1).capitalizeFirstLetter() +
  234. ' + ' + GetStatAbbreviation(comboGymKeyValuePair.secondarystat2);
  235. if (distanceToComboGymMax < 0) {
  236. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(-distanceToComboGymMax, 1) + ' too high!</span>';
  237. } else if (distanceToComboGymMax < statSafeDistance) {
  238. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceToComboGymMax, 1) + ' below the limit.</span>';
  239. } else {
  240. gymStatus = '<span class="t-green">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceToComboGymMax, 1) + ' below the limit.</span>';
  241. }
  242. }
  243.  
  244.  
  245. } else {
  246. var distanceFromSpecialistGymMin = minPrimaryStat - stats[stat];
  247. var distanceToSpecialistGymMax = maxSecondaryStat - stats[stat];
  248.  
  249. var distanceToMax = 0;
  250. var statIdentifierString = stat.capitalizeFirstLetter();
  251. if (stat == primaryGymKeyValuePair.stat) {
  252. if (distanceFromSpecialistGymMin <= 0) {
  253. if (isSingleGymRatio) {
  254. // Specialist stat for Hank's Gym Ratio is never one of the primary combo stats.
  255. // Only set the identifier if we don't already know this stat is too low to unlock its own specific gym.
  256. distanceToMax = distanceToComboGymMax;
  257. if (distanceToMax < 0) {
  258. statIdentifierString = GetStatAbbreviation(comboGymKeyValuePair.secondarystat1).capitalizeFirstLetter() +
  259. ' + ' + GetStatAbbreviation(comboGymKeyValuePair.secondarystat2);
  260. }
  261. } else {
  262. // Specialist stat IS the combo stat; we only care to show how it's doing in relation to the specialist gym.
  263. distanceToMax = distanceFromSpecialistGymMin;
  264. }
  265. }
  266. } else if (stat == comboGymKeyValuePair.stat1 || stat == comboGymKeyValuePair.stat2) {
  267. // We don't have to worry about this stat going too high for the combo gym.
  268. distanceToMax = distanceToSpecialistGymMax;
  269. } else {
  270. // This stat is neither the primary stat nor a combo gym stat, so it's limited by both.
  271. distanceToMax = Math.min(distanceToSpecialistGymMax, distanceToComboGymMax);
  272. if (distanceToComboGymMax < distanceToSpecialistGymMax && distanceToMax < 0) {
  273. statIdentifierString = GetStatAbbreviation(comboGymKeyValuePair.secondarystat1).capitalizeFirstLetter() +
  274. ' + ' + GetStatAbbreviation(comboGymKeyValuePair.secondarystat2);
  275. }
  276. }
  277.  
  278. if (stat == primaryGymKeyValuePair.stat) {
  279. console.debug(stat + ' distanceFromSpecialistGymMin: ' + distanceFromSpecialistGymMin);
  280. console.debug(stat + ' distanceToComboGymMax: ' + distanceToComboGymMax);
  281. } else if (stat == comboGymKeyValuePair.stat1 || stat == comboGymKeyValuePair.stat2) {
  282. console.debug(stat + ' distanceToSpecialistGymMax: ' + distanceToSpecialistGymMax);
  283. console.debug(stat + ' distanceFromComboGymMin: ' + distanceFromComboGymMin);
  284. } else {
  285. console.debug(stat + ' distanceToSpecialistGymMax: ' + distanceToSpecialistGymMax);
  286. console.debug(stat + ' distanceToComboGymMax: ' + distanceToComboGymMax);
  287. }
  288. console.debug(stat + ' distanceToMax: ' + distanceToMax);
  289.  
  290. if (stat == primaryGymKeyValuePair.stat && distanceFromSpecialistGymMin > 0) {
  291. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceFromSpecialistGymMin, 1) + ' too low!</span>';
  292. } else if (distanceToMax < 0) {
  293. if (stat == primaryGymKeyValuePair.stat && isComboGymCombinedRatio) {
  294. gymStatus = '<span class="t-green">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(-distanceToMax, 1) + ' above the limit.</span>';
  295. } else {
  296. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(-distanceToMax, 1) + ' too high!</span>';
  297. }
  298. } else if (distanceToMax < statSafeDistance) {
  299. gymStatus = '<span class="t-red bold">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceToMax, 1) + ' below the limit.</span>';
  300. } else {
  301. gymStatus = '<span class="t-green">' + statIdentifierString + ' is ' + FormatAbbreviatedNumber(distanceToMax, 1) + ' below the limit.</span>';
  302. }
  303. }
  304.  
  305. var $statInfoDiv = $element.parent().find('.msg').find('p');
  306. // Clear the training info in case it exists.
  307. var statInfoHtml = $statInfoDiv[0].innerHTML;
  308. $statInfoDiv[0].innerHTML = statInfoHtml.split('per train</span>')[0] + 'per train</span>';
  309.  
  310. $statInfoDiv.append(gymStatus);
  311. }
  312. });
  313. oldTotal = total;
  314. oldBuild = currentBuild;
  315. console.debug("Stat spread updated!");
  316. }, 400);
  317. });
  318.  
  319.  
  320. String.prototype.capitalizeFirstLetter = function() {
  321. return this.charAt(0).toUpperCase() + this.slice(1);
  322. };
  323.  
  324.  
  325. function GetStatAbbreviation(statString) {
  326. if (statString == 'strength') {
  327. return 'str';
  328. } else if (statString == 'defense') {
  329. return 'def';
  330. } else if (statString == 'speed') {
  331. return 'spd';
  332. } else if (statString == 'dexterity') {
  333. return 'dex';
  334. }
  335. return statString;
  336. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement