HWM smith and cost per battle

ЦЗБ текущего ремонта, данные с аукциона для сравнения

  1. // ==UserScript==
  2. // @name HWM smith and cost per battle
  3. // @namespace http://tampermonkey.net/
  4. // @version 7.2
  5. // @description ЦЗБ текущего ремонта, данные с аукциона для сравнения
  6. // @author o3-mini-ChatGPT
  7. // @match https://www.heroeswm.ru/mod_workbench.php?art_id=*&type=repair
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. let globalItemName = "";
  16. let repairRatio = 0;
  17. let ratioSpan = null;
  18. let globalRepairCost = 0;
  19.  
  20. function runScript() {
  21. let itemTd = document.querySelector('td.wb[align="center"][colspan="2"]');
  22. if (itemTd) {
  23. let itemText = itemTd.textContent.trim();
  24. let parts = itemText.split(": ");
  25. if (parts.length > 1) {
  26. let itemName = parts[1].split(" [")[0].trim();
  27. globalItemName = itemName;
  28. }
  29. }
  30.  
  31. let form = document.querySelector("form[name='fmain']");
  32. if (!form) return;
  33.  
  34. let bTag = form.querySelector("b");
  35. if (!bTag) return;
  36. let durabilityText = bTag.textContent.trim();
  37. let durabilityParts = durabilityText.split('/');
  38. let currentDurability = parseFloat(durabilityParts[0]);
  39. if (isNaN(currentDurability)) return;
  40.  
  41. let costImg = form.querySelector("img[src*='gold.png']");
  42. if (!costImg) return;
  43. let costTextNode = costImg.nextSibling;
  44. while (costTextNode && costTextNode.nodeType === Node.TEXT_NODE && !costTextNode.nodeValue.trim()) {
  45. costTextNode = costTextNode.nextSibling;
  46. }
  47. if (!costTextNode) costTextNode = costImg.nextElementSibling;
  48. if (!costTextNode) return;
  49. let costText = costTextNode.nodeValue ? costTextNode.nodeValue.trim() : costTextNode.textContent.trim();
  50. let repairCost = parseFloat(costText.replace(/,/g, ''));
  51. if (isNaN(repairCost)) return;
  52. globalRepairCost = repairCost;
  53.  
  54. let ratio = repairCost / currentDurability;
  55. ratio = Math.round(ratio * 100) / 100;
  56. repairRatio = ratio;
  57.  
  58. let resultSpan = document.createElement("span");
  59. resultSpan.style.marginLeft = "10px";
  60. resultSpan.style.fontWeight = "bold";
  61. resultSpan.textContent = " ЦЗБ: " + ratio;
  62. bTag.parentNode.insertBefore(resultSpan, bTag.nextSibling);
  63. ratioSpan = resultSpan;
  64. }
  65.  
  66. function loadAuctionInfo() {
  67. let artInfoAnchor = document.querySelector('a[href^="art_info.php?id="]');
  68. if (!artInfoAnchor) return;
  69. let artInfoUrl = artInfoAnchor.href;
  70. artInfoUrl = new URL(artInfoUrl, window.location.origin).href;
  71.  
  72. let iframe = document.createElement("iframe");
  73. iframe.style.display = "none";
  74. iframe.src = artInfoUrl;
  75. iframe.onload = function() {
  76. try {
  77. let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  78. let observer = new MutationObserver(function(mutations, obs) {
  79. let auctionLinkElem = iframeDoc.querySelector("a[href*='auction.php?']");
  80. if (auctionLinkElem) {
  81. let urlParams = new URLSearchParams(auctionLinkElem.href.split('?')[1]);
  82. let sortParam = urlParams.get('sort') || '4';
  83. let catParam = urlParams.get('cat') || '';
  84. let artTypeParam = urlParams.get('art_type') || '';
  85. if (!sortParam || !catParam || !artTypeParam) {
  86. let links = iframeDoc.querySelectorAll("a");
  87. links.forEach(link => {
  88. if (link.href.indexOf("auction.php?") !== -1) {
  89. let paramsTemp = new URLSearchParams(link.href.split('?')[1]);
  90. sortParam = paramsTemp.get('sort') || sortParam;
  91. catParam = paramsTemp.get('cat') || catParam;
  92. artTypeParam = paramsTemp.get('art_type') || artTypeParam;
  93. }
  94. });
  95. }
  96. if (!sortParam || !catParam || !artTypeParam) {
  97. obs.disconnect();
  98. iframe.remove();
  99. return;
  100. }
  101.  
  102. let finalAuctionLink = `https://www.heroeswm.ru/auction.php?sort=${sortParam}&cat=${catParam}&art_type=${artTypeParam}&sbn=1&sau=0&snew=1`;
  103. obs.disconnect();
  104. iframe.remove();
  105. processAuctionPage(finalAuctionLink);
  106. }
  107. });
  108. observer.observe(iframeDoc.body, { childList: true, subtree: true });
  109. } catch(e) {
  110. iframe.remove();
  111. }
  112. };
  113. document.body.appendChild(iframe);
  114. }
  115.  
  116. function processAuctionPage(finalAuctionLink) {
  117. let auctionIframe = document.createElement("iframe");
  118. auctionIframe.style.display = "none";
  119. auctionIframe.src = finalAuctionLink;
  120. auctionIframe.onload = function() {
  121. try {
  122. let auctionDoc = auctionIframe.contentDocument || auctionIframe.contentWindow.document;
  123. setTimeout(() => {
  124. let allRows = auctionDoc.querySelectorAll("tr.wb");
  125. let lots = [];
  126. let processedItems = new Set(); // Track processed items to avoid duplicates
  127.  
  128. allRows.forEach(row => {
  129. if (row.innerText.indexOf("- " + globalItemName) !== -1) {
  130. let strengthCell = row.querySelector("td[valign='top']");
  131. if (strengthCell) {
  132. let strengthMatch = strengthCell.innerText.match(/Прочность:\s*(\d+)\//);
  133. if (strengthMatch) {
  134. let initialStrength = Number(strengthMatch[1]);
  135. let goldImg = row.querySelector("img[src*='gold.png']");
  136. if (goldImg && goldImg.parentElement) {
  137. let priceTd = goldImg.parentElement.nextElementSibling;
  138. if (priceTd) {
  139. let priceMatch = priceTd.textContent.match(/([\d,]+)/);
  140. if (priceMatch) {
  141. let priceStr = priceMatch[1].replace(/,/g, '');
  142. let initialPrice = Number(priceStr);
  143. // Create unique key for this item
  144. let itemKey = `${initialStrength}-${initialPrice}`;
  145. if (!processedItems.has(itemKey)) {
  146. processedItems.add(itemKey);
  147. let bestCostPerBattle = Infinity;
  148. let optimalStrength = initialStrength;
  149. let totalStrength = initialStrength;
  150. let repairCount = 0;
  151.  
  152. let initialCostPerBattle = initialPrice / totalStrength;
  153. if (initialCostPerBattle < bestCostPerBattle) {
  154. bestCostPerBattle = initialCostPerBattle;
  155. optimalStrength = initialStrength;
  156. }
  157.  
  158. for (let currentStrength = initialStrength - 1; currentStrength > 0; currentStrength--) {
  159. let repairedStrength = Math.ceil(currentStrength * 0.9);
  160. totalStrength += repairedStrength;
  161. repairCount++;
  162. let costPerBattle = (initialPrice + repairCount * globalRepairCost) / totalStrength;
  163.  
  164. if (costPerBattle < bestCostPerBattle) {
  165. bestCostPerBattle = costPerBattle;
  166. optimalStrength = currentStrength;
  167. } else if (costPerBattle === bestCostPerBattle) {
  168. optimalStrength = Math.min(optimalStrength, currentStrength);
  169. }
  170. }
  171. lots.push({
  172. initialStrength,
  173. initialPrice,
  174. optimalStrength,
  175. bestCostPerBattle
  176. });
  177. }
  178. }
  179. }
  180. }
  181. }
  182. }
  183. }
  184. });
  185.  
  186. if (lots.length === 0) {
  187. auctionIframe.remove();
  188. return;
  189. }
  190.  
  191. // Remove any existing auction links before adding new one
  192. let existingLinks = document.querySelectorAll("a[href*='auction.php']");
  193. existingLinks.forEach(link => link.remove());
  194.  
  195. let maxInitialStrength = Math.max(...lots.map(l => l.initialStrength));
  196. let bestMaxStrengthLot = lots
  197. .filter(l => l.initialStrength === maxInitialStrength)
  198. .reduce((best, current) =>
  199. current.bestCostPerBattle < best.bestCostPerBattle ? current : best
  200. );
  201.  
  202. let bestCostLot = lots.reduce((best, current) =>
  203. current.bestCostPerBattle < best.bestCostPerBattle ? current : best
  204. );
  205.  
  206. let linkHTML = " Аук: "
  207. + bestMaxStrengthLot.initialStrength + " - "
  208. + bestMaxStrengthLot.initialPrice + " - "
  209. + "<strong>" + bestMaxStrengthLot.optimalStrength + " - "
  210. + bestMaxStrengthLot.bestCostPerBattle.toFixed(2) + "</strong>; "
  211. + bestCostLot.initialStrength + " - "
  212. + bestCostLot.initialPrice + " - "
  213. + "<strong>" + bestCostLot.optimalStrength + " - "
  214. + bestCostLot.bestCostPerBattle.toFixed(2) + "</strong>";
  215. let formElem = document.querySelector("form[name='fmain']");
  216. if (formElem) {
  217. let goldImg = formElem.querySelector("img[src*='gold.png']");
  218. if (goldImg) {
  219. let candidateNode = goldImg.nextSibling;
  220. while (candidateNode && candidateNode.nodeType === Node.TEXT_NODE && !candidateNode.nodeValue.trim()) {
  221. candidateNode = candidateNode.nextSibling;
  222. }
  223. if (candidateNode) {
  224. let firstBr = candidateNode.nextElementSibling;
  225. if (firstBr && firstBr.tagName.toLowerCase() === "br") {
  226. let linkElement = document.createElement("a");
  227. linkElement.href = finalAuctionLink;
  228. linkElement.innerHTML = linkHTML;
  229. let secondBr = firstBr.nextElementSibling;
  230. candidateNode.parentNode.insertBefore(linkElement, secondBr);
  231. }
  232. }
  233. }
  234. }
  235. // Добавление изменения цвета для resultSpan в зависимости от repairRatio и bestCostPerBattle
  236. if (ratioSpan) {
  237. if (repairRatio < bestCostLot.bestCostPerBattle) {
  238. ratioSpan.style.color = "green";
  239. } else {
  240. ratioSpan.style.color = "red";
  241. }
  242. }
  243. auctionIframe.remove();
  244. }, 2000);
  245. } catch (e) {
  246. auctionIframe.remove();
  247. }
  248. };
  249. document.body.appendChild(auctionIframe);
  250. }
  251.  
  252. if (document.readyState === "loading") {
  253. document.addEventListener("DOMContentLoaded", function() {
  254. runScript();
  255. loadAuctionInfo();
  256. });
  257. } else {
  258. runScript();
  259. loadAuctionInfo();
  260. }
  261. })();