Elethor Recyclobot Calculator

Calculate platinum production and gold profit with a UI, now with searchable item selection, on Elethor.com.

当前为 2025-02-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Elethor Recyclobot Calculator
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4
  5. // @description Calculate platinum production and gold profit with a UI, now with searchable item selection, on Elethor.com.
  6. // @author Eugene
  7. // @match https://elethor.com/*
  8. // @grant none
  9. // @license GPL-3.0-or-later
  10. // ==/UserScript==
  11. /*
  12. * This program is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation, either version 3 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  24. */
  25. (function() {
  26. 'use strict';
  27.  
  28. // Error logging function
  29. function logError(message, error) {
  30. console.error(`Elethor Recyclobot Calculator Error: ${message}`, error);
  31. }
  32.  
  33. // Global error handler
  34. window.addEventListener('error', function(event) {
  35. logError('Uncaught error', event.error);
  36. });
  37.  
  38. // Define the target URL for the calculator to display
  39. const TARGET_URL = 'https://elethor.com/character/companion/recyclobot';
  40.  
  41. // Define the items and their base platinum points
  42. const itemList = [
  43. { name: "Rat Pelt", points: 55 },
  44. { name: "Lava Worm Tooth", points: 60 },
  45. { name: "Tormented Scale", points: 65 },
  46. { name: "Skrivet Pelt", points: 70 },
  47. { name: "Rippling Scale", points: 75 },
  48. { name: "Worg Tooth", points: 80 },
  49. { name: "Razen Hide", points: 85 },
  50. { name: "T0 Scrap", points: 600 },
  51. { name: "T1 Scrap", points: 1000 },
  52. { name: "T2 Scrap", points: 2000 },
  53. { name: "T3 Scrap", points: 3000 },
  54. { name: "Puncture 1 Essence", points: 320000 },
  55. { name: "Puncture 2 Essence", points: 480000 },
  56. { name: "Puncture 3 Essence", points: 800000 },
  57. { name: "Puncture 4 Essence", points: 1280000 },
  58. { name: "Worg Claw", points: 160 },
  59. { name: "Azure Tusk", points: 90 },
  60. { name: "Fire Scale", points: 1200 },
  61. { name: "Slotted Talon", points: 1500 },
  62. { name: "Crimson Tusk", points: 95 },
  63. { name: "Gory Tusk", points: 100 },
  64. { name: "Poisonous Barb", points: 105 },
  65. { name: "Karth Plate", points: 110 },
  66. { name: "Bat Claw", points: 160 },
  67. { name: "T4 Scrap", points: 4000 },
  68. { name: "T5 Scrap", points: 5000 },
  69. { name: "T6 Scrap", points: 6000 },
  70. { name: "T7 Scrap", points: 7000 },
  71. { name: "Spine Fragment", points: 165 },
  72. { name: "Black Ink", points: 170 },
  73. { name: "Dark Tusk", points: 175 },
  74. { name: "Dark Delve", points: 180 },
  75. { name: "Fireproof Tongue", points: 185 },
  76. { name: "Serrated Thorn", points: 190 },
  77. { name: "Imprinted Skull", points: 195 },
  78. { name: "Unhinged Jawbone", points: 200 },
  79. { name: "Pierced Voicebox", points: 205 },
  80. { name: "Broken Shovel", points: 210 },
  81. { name: "Pincered Talon", points: 215 },
  82. { name: "Translucent Scale", points: 220 },
  83. { name: "Scarred Fang", points: 225 },
  84. { name: "Crooked Leg", points: 230 },
  85. { name: "Venom Sample", points: 235 },
  86. { name: "Condensed Vapor", points: 240 },
  87. { name: "Fragment of Silence", points: 245 },
  88. { name: "Unholy Remainder", points: 290 },
  89. { name: "Synthetic Tar", points: 295 },
  90. { name: "Growg Arm", points: 300 },
  91. { name: "Decomposing Mask", points: 305 },
  92. { name: "Severed Claw", points: 310 },
  93. { name: "Void Fragment", points: 315 },
  94. { name: "Shattered Larynx", points: 340 },
  95. { name: "Slimy Tentacle", points: 345 },
  96. { name: "Broken Shackle", points: 350 },
  97. { name: "Drop of Aether", points: 650 },
  98. { name: "Torn Shadow", points: 355 },
  99. { name: "Cursed Bane", points: 360 },
  100. { name: "Volatile Dust", points: 365 },
  101. { name: "Unsorted Valuables", points: 390 },
  102. { name: "Spyglass", points: 445 },
  103. { name: "Shattered Shield", points: 455 },
  104. { name: "Shimmering Spearhead", points: 465 },
  105. { name: "T'Chek Incisor", points: 475 },
  106. { name: "Cracked Chestpiece", points: 485 },
  107. { name: "Fractured Rocket", points: 500 },
  108. { name: "Curved Blade", points: 1080 },
  109. { name: "Void Artifact", points: 1110000 }
  110. ];
  111.  
  112. // Create a button to open the calculator
  113. let button;
  114. try {
  115. button = document.createElement('button');
  116. button.innerText = 'Open Recyclobot Calculator';
  117. button.style.padding = '10px';
  118. button.style.backgroundColor = '#18743c'; // Button color
  119. button.style.color = '#dee5ed'; // Text color
  120. button.style.border = 'none';
  121. button.style.borderRadius = '5px';
  122. button.style.cursor = 'pointer';
  123. button.style.position = 'absolute'; // Use absolute positioning for precise placement
  124. button.style.zIndex = '9999'; // Ensure the button appears on top
  125. button.style.display = 'none'; // Initially hidden
  126. } catch (error) {
  127. logError('Error creating calculator button', error);
  128. }
  129.  
  130. // Function to position the button
  131. function positionButton() {
  132. try {
  133. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full'); // Target button
  134. if (referenceElement) {
  135. const rect = referenceElement.getBoundingClientRect();
  136. button.style.top = `${rect.top + window.scrollY - button.offsetHeight - 50}px`; // Move button above the reference element (50px margin)
  137. button.style.left = `${rect.left + window.scrollX - button.offsetWidth - 10}px`; // Position to the left with a margin
  138. document.body.appendChild(button); // Append button to the body
  139. }
  140. } catch (error) {
  141. logError('Error positioning calculator button', error);
  142. }
  143. }
  144.  
  145. // Create a MutationObserver to detect changes in the DOM
  146. let observer;
  147. try {
  148. observer = new MutationObserver((mutations) => {
  149. // Check if the target element is in the DOM
  150. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full');
  151. if (referenceElement) {
  152. positionButton(); // Position the button
  153. observer.disconnect(); // Stop observing once the button is placed
  154. }
  155. });
  156.  
  157. // Start observing the document body for changes
  158. observer.observe(document.body, { childList: true, subtree: true });
  159. } catch (error) {
  160. logError('Error setting up MutationObserver', error);
  161. }
  162.  
  163. // Create the calculator UI
  164. let calculatorContainer;
  165. try {
  166. calculatorContainer = document.createElement('div');
  167. calculatorContainer.style.display = 'none'; // Initially hidden
  168. calculatorContainer.style.position = 'fixed';
  169. calculatorContainer.style.border = '2px solid #505c6c'; // Border around calculator
  170. calculatorContainer.style.borderRadius = '10px';
  171. calculatorContainer.style.backgroundColor = '#202c3c'; // Background color
  172. calculatorContainer.style.zIndex = '1001';
  173. calculatorContainer.style.padding = '20px';
  174. } catch (error) {
  175. logError('Error creating calculator container', error);
  176. }
  177.  
  178. // Function to position the calculator below the "Prospector" element
  179. function positionCalculator() {
  180. try {
  181. const prospectorElement = document.querySelector('a[href="/character/companion/prospector"]');
  182. if (prospectorElement) {
  183. const rect = prospectorElement.getBoundingClientRect();
  184. calculatorContainer.style.top = `${rect.bottom + window.scrollY - 230 + 10}px`; // 250px up from the bottom of the Prospector element + 10px margin
  185. calculatorContainer.style.left = `${rect.left + window.scrollX}px`; // Align horizontally with the Prospector element
  186. }
  187. } catch (error) {
  188. logError('Error positioning calculator', error);
  189. }
  190. }
  191.  
  192. // Set the initial position of the calculator
  193. try {
  194. positionCalculator();
  195. document.body.appendChild(calculatorContainer);
  196. } catch (error) {
  197. logError('Error setting initial calculator position', error);
  198. }
  199.  
  200. // Add form elements to the calculator
  201. calculatorContainer.innerHTML = `
  202. <style>
  203. .tooltip {
  204. position: relative;
  205. display: inline-block;
  206. cursor: pointer;
  207. margin-left: 5px;
  208. }
  209. .tooltip .tooltiptext {
  210. visibility: hidden;
  211. width: 200px;
  212. background-color: #555;
  213. color: #fff;
  214. text-align: center;
  215. border-radius: 6px;
  216. padding: 5px;
  217. position: absolute;
  218. z-index: 1;
  219. bottom: 125%;
  220. left: 50%;
  221. margin-left: -100px;
  222. opacity: 0;
  223. transition: opacity 0.3s;
  224. }
  225. .tooltip:hover .tooltiptext {
  226. visibility: visible;
  227. opacity: 1;
  228. }
  229. #itemDropdown {
  230. background-color: #fff;
  231. border: 1px solid #dee5ed;
  232. border-radius: 5px;
  233. max-height: 200px;
  234. overflow-y: auto;
  235. position: absolute;
  236. width: 200px;
  237. z-index: 1000;
  238. }
  239. #itemDropdown div {
  240. padding: 5px;
  241. cursor: pointer;
  242. color: black; /* This matches the color of other input fields */
  243. }
  244. #itemDropdown div:hover {
  245. background-color: #f0f0f0;
  246. }
  247. </style>
  248. <h3 style="color: #dee5ed;">Elethor Recyclobot Calculator</h3>
  249. <p style="color: #dee5ed; font-size: 0.9em;">Made by <a href="https://elethor.com/profile/49979" target="_blank" style="color: #6cb4e4; text-decoration: underline;">Eugene</a></p>
  250. <div style="margin-bottom: 5px; position: relative;">
  251. <label style="color: #dee5ed;">Select Item:</label>
  252. <input type="text" id="itemSearch" placeholder="Search items..." style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 200px; margin-right: 10px; color: black;">
  253. <div id="itemDropdown" style="display: none;"></div>
  254. <span class="tooltip">ℹ️<span class="tooltiptext">Choose the item you want to recycle</span></span>
  255. </div>
  256. <div style="margin-bottom: 5px;">
  257. <label style="color: #dee5ed;">Bonus Platinum Level:</label>
  258. <input type="number" id="bonusPlatinumLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  259. <span class="tooltip">ℹ️<span class="tooltiptext">Your current Bonus Platinum level</span></span>
  260. </div>
  261. <div style="margin-bottom: 5px;">
  262. <label style="color: #dee5ed;">Exchange Rate Level:</label>
  263. <input type="number" id="exchangeRateLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  264. <span class="tooltip">ℹ️<span class="tooltiptext">Your current Exchange Rate level</span></span>
  265. </div>
  266. <div style="margin-bottom: 5px;">
  267. <label style="color: #dee5ed;">Gold cost per item:</label>
  268. <input type="number" step="0.01" id="itemGoldCost" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  269. <span class="tooltip">ℹ️<span class="tooltiptext">The gold cost for each item you're recycling</span></span>
  270. </div>
  271. <div style="margin-bottom: 5px;">
  272. <label style="color: #dee5ed;">Gold value per platinum (in millions):</label>
  273. <input type="number" step="0.01" id="goldPerPlatinum" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  274. <span class="tooltip">ℹ️<span class="tooltiptext">The current gold value of one platinum in millions</span></span>
  275. </div>
  276.  
  277. <div style="display: flex; gap: 10px; margin-top: 10px;">
  278. <button id="calculateBtn" style="background-color: #18743c; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">Calculate</button>
  279. <button id="resetBtn" style="background-color: #2596be; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">🔄 Reset</button>
  280. <button id="closeBtn" style="background-color: #a02424; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">X Close</button>
  281. </div>
  282. <div style="display: flex; gap: 10px; margin-top: 10px;">
  283. <button id="copyInputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Input</button>
  284. <button id="copyOutputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Output</button>
  285. </div>
  286. <div id="results" style="margin-top: 20px;"></div>
  287. `;
  288.  
  289. // Show/hide calculator on button click
  290. button.addEventListener('click', () => {
  291. try {
  292. // Toggle the visibility of the calculator
  293. if (calculatorContainer.style.display === 'none') {
  294. calculatorContainer.style.display = 'block';
  295. positionCalculator(); // Ensure it is positioned correctly when opened
  296. loadInputs(); // Load saved inputs when opening
  297. } else {
  298. calculatorContainer.style.display = 'none';
  299. }
  300. } catch (error) {
  301. logError('Error toggling calculator visibility', error);
  302. }
  303. });
  304.  
  305. // Close button
  306. calculatorContainer.querySelector('#closeBtn').addEventListener('click', () => {
  307. try {
  308. calculatorContainer.style.display = 'none';
  309. } catch (error) {
  310. logError('Error closing calculator', error);
  311. }
  312. });
  313.  
  314. // Reset button functionality
  315. calculatorContainer.querySelector('#resetBtn').addEventListener('click', () => {
  316. try {
  317. document.querySelector('#itemSearch').value = ''; // Clear search input
  318. document.querySelector('#bonusPlatinumLevel').value = '';
  319. document.querySelector('#exchangeRateLevel').value = '';
  320. document.querySelector('#itemGoldCost').value = '';
  321. document.querySelector('#goldPerPlatinum').value = '';
  322. clearInputs(); // Clear localStorage
  323. document.getElementById('results').innerHTML = ''; // Clear results
  324. } catch (error) {
  325. logError('Error resetting calculator', error);
  326. }
  327. });
  328.  
  329. // Function to flash the button when clicked
  330. function flashButton(button) {
  331. try {
  332. const originalColor = button.style.backgroundColor;
  333. button.style.backgroundColor = '#4CAF50'; // Success color (green)
  334. setTimeout(() => {
  335. button.style.backgroundColor = originalColor; // Revert back after 300ms
  336. }, 300);
  337. } catch (error) {
  338. logError('Error flashing button', error);
  339. }
  340. }
  341.  
  342. // Copy Output button functionality with flash effect
  343. calculatorContainer.querySelector('#copyOutputBtn').addEventListener('click', () => {
  344. try {
  345. const resultsText = document.getElementById('results').innerText;
  346. navigator.clipboard.writeText(resultsText)
  347. .then(() => {
  348. flashButton(calculatorContainer.querySelector('#copyOutputBtn')); // Flash button
  349. console.log('Output copied to clipboard!');
  350. })
  351. .catch(err => logError('Failed to copy output', err));
  352. } catch (error) {
  353. logError('Error copying output', error);
  354. }
  355. });
  356.  
  357. // Copy Input button functionality with flash effect
  358. calculatorContainer.querySelector('#copyInputBtn').addEventListener('click', () => {
  359. try {
  360. const selectedItem = document.querySelector('#itemSearch').value;
  361. const bonusPlatinumLevel = document.querySelector('#bonusPlatinumLevel').value;
  362. const exchangeRateLevel = document.querySelector('#exchangeRateLevel').value;
  363. const itemGoldCost = document.querySelector('#itemGoldCost').value;
  364. const goldPerPlatinum = document.querySelector('#goldPerPlatinum').value;
  365.  
  366. const inputText = `
  367. Selected Item: ${selectedItem}
  368. Bonus Platinum Level: ${bonusPlatinumLevel}
  369. Exchange Rate Level: ${exchangeRateLevel}
  370. Gold cost per item: ${itemGoldCost}
  371. Gold value per platinum (in millions): ${goldPerPlatinum}
  372. `;
  373.  
  374. navigator.clipboard.writeText(inputText.trim())
  375. .then(() => {
  376. flashButton(calculatorContainer.querySelector('#copyInputBtn')); // Flash button
  377. console.log('Input values copied to clipboard!');
  378. })
  379. .catch(err => logError('Failed to copy input', err));
  380. } catch (error) {
  381. logError('Error copying input', error);
  382. }
  383. });
  384.  
  385. // Searchable dropdown functionality
  386. const itemSearch = document.getElementById('itemSearch');
  387. const itemDropdown = document.getElementById('itemDropdown');
  388. let selectedItemPoints = 0;
  389.  
  390. function populateDropdown(items) {
  391. try {
  392. itemDropdown.innerHTML = '';
  393. items.forEach(item => {
  394. const div = document.createElement('div');
  395. div.textContent = item.name;
  396. div.onclick = function() {
  397. itemSearch.value = item.name;
  398. selectedItemPoints = item.points;
  399. itemDropdown.style.display = 'none';
  400. };
  401. itemDropdown.appendChild(div);
  402. });
  403. itemDropdown.style.display = 'block';
  404. } catch (error) {
  405. logError('Error populating dropdown', error);
  406. }
  407. }
  408.  
  409. itemSearch.addEventListener('focus', function() {
  410. try {
  411. if (this.value === '') {
  412. populateDropdown(itemList);
  413. }
  414. } catch (error) {
  415. logError('Error handling item search focus', error);
  416. }
  417. });
  418.  
  419. itemSearch.addEventListener('input', function() {
  420. try {
  421. const searchTerm = this.value.toLowerCase();
  422. const filteredItems = searchTerm === '' ? itemList : itemList.filter(item =>
  423. item.name.toLowerCase().includes(searchTerm)
  424. );
  425. populateDropdown(filteredItems);
  426. } catch (error) {
  427. logError('Error handling item search input', error);
  428. }
  429. });
  430.  
  431. document.addEventListener('click', function(e) {
  432. try {
  433. if (e.target !== itemSearch && e.target !== itemDropdown) {
  434. itemDropdown.style.display = 'none';
  435. }
  436. } catch (error) {
  437. logError('Error handling document click', error);
  438. }
  439. });
  440.  
  441. // Calculate button functionality
  442. calculatorContainer.querySelector('#calculateBtn').addEventListener('click', () => {
  443. try {
  444. const bonusPlatinumLevel = parseFloat(document.querySelector('#bonusPlatinumLevel').value);
  445. const exchangeRateLevel = parseFloat(document.querySelector('#exchangeRateLevel').value);
  446. const itemGoldCost = parseFloat(document.querySelector('#itemGoldCost').value);
  447. const goldPerPlatinum = parseFloat(document.querySelector('#goldPerPlatinum').value);
  448.  
  449. // Validation: Check for positive values
  450. if (
  451. selectedItemPoints <= 0 ||
  452. isNaN(bonusPlatinumLevel) || bonusPlatinumLevel < 0 ||
  453. isNaN(exchangeRateLevel) || exchangeRateLevel < 0 ||
  454. isNaN(itemGoldCost) || itemGoldCost < 0 ||
  455. isNaN(goldPerPlatinum) || goldPerPlatinum < 0
  456. ) {
  457. throw new Error('Invalid input values');
  458. }
  459.  
  460. saveInputs(selectedItemPoints, bonusPlatinumLevel, exchangeRateLevel, itemGoldCost, goldPerPlatinum);
  461.  
  462. const ITEMS_PER_RECYCLE = 200;
  463. const BASE_PLATINUM_POINTS = selectedItemPoints;
  464. const BASE_PLATINUM_COST = 10000;
  465. const MAX_COST_INCREASE_PLATINUM = 20;
  466. const PLATINUM_GOLD_COST = 250000;
  467. const BONUS_PLATINUM_INCREMENT = 0.002;
  468.  
  469. const exchangeRateMultiplier = 1 + (exchangeRateLevel * 0.01);
  470. const bonusPlatinumMultiplier = 1 + (bonusPlatinumLevel * BONUS_PLATINUM_INCREMENT);
  471. const platinumPointsPerRecycle = Math.floor(BASE_PLATINUM_POINTS * exchangeRateMultiplier);
  472. let totalGoldProfit = 0;
  473. let platinumCount = 0;
  474. let totalGoldSpent = 0;
  475. let totalItemsUsed = 0;
  476. let currentPlatinumCost = BASE_PLATINUM_COST;
  477.  
  478. while (true) {
  479. const platinumPointsNeeded = currentPlatinumCost;
  480. const requiredRecycles = Math.ceil(platinumPointsNeeded / platinumPointsPerRecycle);
  481. const requiredItems = requiredRecycles * ITEMS_PER_RECYCLE;
  482. totalItemsUsed += requiredItems;
  483.  
  484. const itemsGoldCostTotal = requiredItems * itemGoldCost;
  485. const totalGoldCost = PLATINUM_GOLD_COST + itemsGoldCostTotal;
  486.  
  487. // Total platinum produced including bonus
  488. const totalPlatinum = 1 * bonusPlatinumMultiplier;
  489. const basePlatinum = 1; // Base platinum unit
  490. const bonusPlatinum = totalPlatinum - basePlatinum; // Bonus platinum units
  491.  
  492. // Cost per unit of platinum
  493. const costPerPlatinum = totalGoldCost / totalPlatinum;
  494.  
  495. // Calculate profit per platinum unit
  496. let profit = 0;
  497.  
  498. // Profit from base platinum (100% of its gold value)
  499. profit += (basePlatinum * goldPerPlatinum * 1_000_000) - (costPerPlatinum * basePlatinum);
  500.  
  501. // Profit from bonus platinum (90% of its gold value)
  502. profit += (bonusPlatinum * goldPerPlatinum * 1_000_000 * 0.9) - (costPerPlatinum * bonusPlatinum);
  503.  
  504. // If profit is non-positive, break the loop
  505. if (profit <= 0) {
  506. break;
  507. }
  508.  
  509. // Accumulate the total profits and costs
  510. totalGoldProfit += profit;
  511. totalGoldSpent += totalGoldCost;
  512. platinumCount++;
  513.  
  514. // Increase platinum cost for the next cycle
  515. if (platinumCount < MAX_COST_INCREASE_PLATINUM) {
  516. currentPlatinumCost += 1000;
  517. } else {
  518. currentPlatinumCost += 500;
  519. }
  520. }
  521.  
  522. // Display results
  523. document.getElementById('results').innerHTML = `
  524. <p style="color: #dee5ed;">Platinum to produce: ${platinumCount}</p>
  525. <p style="color: #dee5ed;">Total Gold Profit: ${formatToBillions(totalGoldProfit)}</p>
  526. <p style="color: #dee5ed;">Maximum Bonus Platinum: ${(platinumCount * bonusPlatinumMultiplier).toFixed(2)}</p>
  527. <p style="color: #dee5ed;">Total Items Used: ${totalItemsUsed}</p>
  528. `;
  529. } catch (error) {
  530. logError('Error in calculation', error);
  531. alert('An error occurred during calculation. Please check your inputs and try again.');
  532. }
  533. });
  534.  
  535. // Function to format value to billions
  536. function formatToBillions(value) {
  537. try {
  538. let billions = value / 1_000_000_000;
  539. return billions.toFixed(2) + " billion";
  540. } catch (error) {
  541. logError('Error formatting to billions', error);
  542. return "Error";
  543. }
  544. }
  545.  
  546. // Save inputs to localStorage
  547. function saveInputs(selectedItemPoints, bonusPlatinumLevel, exchangeRateLevel, itemGoldCost, goldPerPlatinum) {
  548. try {
  549. localStorage.setItem('selectedItemPoints', selectedItemPoints);
  550. localStorage.setItem('bonusPlatinumLevel', bonusPlatinumLevel);
  551. localStorage.setItem('exchangeRateLevel', exchangeRateLevel);
  552. localStorage.setItem('itemGoldCost', itemGoldCost);
  553. localStorage.setItem('goldPerPlatinum', goldPerPlatinum);
  554. } catch (error) {
  555. logError('Error saving inputs to localStorage', error);
  556. }
  557. }
  558.  
  559. // Load inputs from localStorage
  560. function loadInputs() {
  561. try {
  562. const savedItemPoints = localStorage.getItem('selectedItemPoints');
  563. if (savedItemPoints) {
  564. const savedItem = itemList.find(item => item.points == savedItemPoints);
  565. if (savedItem) {
  566. document.querySelector('#itemSearch').value = savedItem.name;
  567. selectedItemPoints = savedItem.points;
  568. }
  569. }
  570. document.querySelector('#bonusPlatinumLevel').value = localStorage.getItem('bonusPlatinumLevel') || '';
  571. document.querySelector('#exchangeRateLevel').value = localStorage.getItem('exchangeRateLevel') || '';
  572. document.querySelector('#itemGoldCost').value = localStorage.getItem('itemGoldCost') || '';
  573. document.querySelector('#goldPerPlatinum').value = localStorage.getItem('goldPerPlatinum') || '';
  574. } catch (error) {
  575. logError('Error loading inputs from localStorage', error);
  576. }
  577. }
  578.  
  579. // Clear inputs from localStorage
  580. function clearInputs() {
  581. try {
  582. localStorage.removeItem('selectedItemPoints');
  583. localStorage.removeItem('bonusPlatinumLevel');
  584. localStorage.removeItem('exchangeRateLevel');
  585. localStorage.removeItem('itemGoldCost');
  586. localStorage.removeItem('goldPerPlatinum');
  587. } catch (error) {
  588. logError('Error clearing inputs from localStorage', error);
  589. }
  590. }
  591.  
  592. // URL check function to show button and calculator only on the correct page
  593. function checkURL() {
  594. try {
  595. if (window.location.href === TARGET_URL) {
  596. button.style.display = 'block';
  597. if (calculatorContainer.style.display !== 'none') {
  598. positionCalculator();
  599. }
  600. } else {
  601. button.style.display = 'none';
  602. calculatorContainer.style.display = 'none';
  603. }
  604. } catch (error) {
  605. logError('Error checking URL', error);
  606. }
  607. }
  608.  
  609. // Check URL initially and set interval for changes
  610. checkURL();
  611. setInterval(checkURL, 500); // Check URL every 500 milliseconds
  612.  
  613. })();