Elethor Recyclobot Calculator

Calculate platinum production and gold profit with a UI, now with searchable item selection

当前为 2024-11-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Elethor Recyclobot Calculator
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Calculate platinum production and gold profit with a UI, now with searchable item selection
  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. ];
  110.  
  111. // Create a button to open the calculator
  112. let button;
  113. try {
  114. button = document.createElement('button');
  115. button.innerText = 'Open Recyclobot Calculator';
  116. button.style.padding = '10px';
  117. button.style.backgroundColor = '#18743c'; // Button color
  118. button.style.color = '#dee5ed'; // Text color
  119. button.style.border = 'none';
  120. button.style.borderRadius = '5px';
  121. button.style.cursor = 'pointer';
  122. button.style.position = 'absolute'; // Use absolute positioning for precise placement
  123. button.style.zIndex = '9999'; // Ensure the button appears on top
  124. button.style.display = 'none'; // Initially hidden
  125. } catch (error) {
  126. logError('Error creating calculator button', error);
  127. }
  128.  
  129. // Function to position the button
  130. function positionButton() {
  131. try {
  132. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full'); // Target button
  133. if (referenceElement) {
  134. const rect = referenceElement.getBoundingClientRect();
  135. button.style.top = `${rect.top + window.scrollY - button.offsetHeight - 50}px`; // Move button above the reference element (50px margin)
  136. button.style.left = `${rect.left + window.scrollX - button.offsetWidth - 10}px`; // Position to the left with a margin
  137. document.body.appendChild(button); // Append button to the body
  138. }
  139. } catch (error) {
  140. logError('Error positioning calculator button', error);
  141. }
  142. }
  143.  
  144. // Create a MutationObserver to detect changes in the DOM
  145. let observer;
  146. try {
  147. observer = new MutationObserver((mutations) => {
  148. // Check if the target element is in the DOM
  149. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full');
  150. if (referenceElement) {
  151. positionButton(); // Position the button
  152. observer.disconnect(); // Stop observing once the button is placed
  153. }
  154. });
  155.  
  156. // Start observing the document body for changes
  157. observer.observe(document.body, { childList: true, subtree: true });
  158. } catch (error) {
  159. logError('Error setting up MutationObserver', error);
  160. }
  161.  
  162. // Create the calculator UI
  163. let calculatorContainer;
  164. try {
  165. calculatorContainer = document.createElement('div');
  166. calculatorContainer.style.display = 'none'; // Initially hidden
  167. calculatorContainer.style.position = 'fixed';
  168. calculatorContainer.style.border = '2px solid #505c6c'; // Border around calculator
  169. calculatorContainer.style.borderRadius = '10px';
  170. calculatorContainer.style.backgroundColor = '#202c3c'; // Background color
  171. calculatorContainer.style.zIndex = '1001';
  172. calculatorContainer.style.padding = '20px';
  173. } catch (error) {
  174. logError('Error creating calculator container', error);
  175. }
  176.  
  177. // Function to position the calculator below the "Prospector" element
  178. function positionCalculator() {
  179. try {
  180. const prospectorElement = document.querySelector('a[href="/character/companion/prospector"]');
  181. if (prospectorElement) {
  182. const rect = prospectorElement.getBoundingClientRect();
  183. calculatorContainer.style.top = `${rect.bottom + window.scrollY - 230 + 10}px`; // 250px up from the bottom of the Prospector element + 10px margin
  184. calculatorContainer.style.left = `${rect.left + window.scrollX}px`; // Align horizontally with the Prospector element
  185. }
  186. } catch (error) {
  187. logError('Error positioning calculator', error);
  188. }
  189. }
  190.  
  191. // Set the initial position of the calculator
  192. try {
  193. positionCalculator();
  194. document.body.appendChild(calculatorContainer);
  195. } catch (error) {
  196. logError('Error setting initial calculator position', error);
  197. }
  198.  
  199. // Add form elements to the calculator
  200. calculatorContainer.innerHTML = `
  201. <style>
  202. .tooltip {
  203. position: relative;
  204. display: inline-block;
  205. cursor: pointer;
  206. margin-left: 5px;
  207. }
  208. .tooltip .tooltiptext {
  209. visibility: hidden;
  210. width: 200px;
  211. background-color: #555;
  212. color: #fff;
  213. text-align: center;
  214. border-radius: 6px;
  215. padding: 5px;
  216. position: absolute;
  217. z-index: 1;
  218. bottom: 125%;
  219. left: 50%;
  220. margin-left: -100px;
  221. opacity: 0;
  222. transition: opacity 0.3s;
  223. }
  224. .tooltip:hover .tooltiptext {
  225. visibility: visible;
  226. opacity: 1;
  227. }
  228. #itemDropdown {
  229. background-color: #fff;
  230. border: 1px solid #dee5ed;
  231. border-radius: 5px;
  232. max-height: 200px;
  233. overflow-y: auto;
  234. position: absolute;
  235. width: 200px;
  236. z-index: 1000;
  237. }
  238. #itemDropdown div {
  239. padding: 5px;
  240. cursor: pointer;
  241. color: black; /* This matches the color of other input fields */
  242. }
  243. #itemDropdown div:hover {
  244. background-color: #f0f0f0;
  245. }
  246. </style>
  247. <h3 style="color: #dee5ed;">Elethor Recyclobot Calculator</h3>
  248. <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>
  249. <div style="margin-bottom: 5px; position: relative;">
  250. <label style="color: #dee5ed;">Select Item:</label>
  251. <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;">
  252. <div id="itemDropdown" style="display: none;"></div>
  253. <span class="tooltip">ℹ️<span class="tooltiptext">Choose the item you want to recycle</span></span>
  254. </div>
  255. <div style="margin-bottom: 5px;">
  256. <label style="color: #dee5ed;">Bonus Platinum Level:</label>
  257. <input type="number" id="bonusPlatinumLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  258. <span class="tooltip">ℹ️<span class="tooltiptext">Your current Bonus Platinum level</span></span>
  259. </div>
  260. <div style="margin-bottom: 5px;">
  261. <label style="color: #dee5ed;">Exchange Rate Level:</label>
  262. <input type="number" id="exchangeRateLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  263. <span class="tooltip">ℹ️<span class="tooltiptext">Your current Exchange Rate level</span></span>
  264. </div>
  265. <div style="margin-bottom: 5px;">
  266. <label style="color: #dee5ed;">Gold cost per item:</label>
  267. <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;">
  268. <span class="tooltip">ℹ️<span class="tooltiptext">The gold cost for each item you're recycling</span></span>
  269. </div>
  270. <div style="margin-bottom: 5px;">
  271. <label style="color: #dee5ed;">Gold value per platinum (in millions):</label>
  272. <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;">
  273. <span class="tooltip">ℹ️<span class="tooltiptext">The current gold value of one platinum in millions</span></span>
  274. </div>
  275.  
  276. <div style="display: flex; gap: 10px; margin-top: 10px;">
  277. <button id="calculateBtn" style="background-color: #18743c; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">Calculate</button>
  278. <button id="resetBtn" style="background-color: #2596be; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">🔄 Reset</button>
  279. <button id="closeBtn" style="background-color: #a02424; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">X Close</button>
  280. </div>
  281. <div style="display: flex; gap: 10px; margin-top: 10px;">
  282. <button id="copyInputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Input</button>
  283. <button id="copyOutputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Output</button>
  284. </div>
  285. <div id="results" style="margin-top: 20px;"></div>
  286. `;
  287.  
  288. // Show/hide calculator on button click
  289. button.addEventListener('click', () => {
  290. try {
  291. // Toggle the visibility of the calculator
  292. if (calculatorContainer.style.display === 'none') {
  293. calculatorContainer.style.display = 'block';
  294. positionCalculator(); // Ensure it is positioned correctly when opened
  295. loadInputs(); // Load saved inputs when opening
  296. } else {
  297. calculatorContainer.style.display = 'none';
  298. }
  299. } catch (error) {
  300. logError('Error toggling calculator visibility', error);
  301. }
  302. });
  303.  
  304. // Close button
  305. calculatorContainer.querySelector('#closeBtn').addEventListener('click', () => {
  306. try {
  307. calculatorContainer.style.display = 'none';
  308. } catch (error) {
  309. logError('Error closing calculator', error);
  310. }
  311. });
  312.  
  313. // Reset button functionality
  314. calculatorContainer.querySelector('#resetBtn').addEventListener('click', () => {
  315. try {
  316. document.querySelector('#itemSearch').value = ''; // Clear search input
  317. document.querySelector('#bonusPlatinumLevel').value = '';
  318. document.querySelector('#exchangeRateLevel').value = '';
  319. document.querySelector('#itemGoldCost').value = '';
  320. document.querySelector('#goldPerPlatinum').value = '';
  321. clearInputs(); // Clear localStorage
  322. document.getElementById('results').innerHTML = ''; // Clear results
  323. } catch (error) {
  324. logError('Error resetting calculator', error);
  325. }
  326. });
  327.  
  328. // Function to flash the button when clicked
  329. function flashButton(button) {
  330. try {
  331. const originalColor = button.style.backgroundColor;
  332. button.style.backgroundColor = '#4CAF50'; // Success color (green)
  333. setTimeout(() => {
  334. button.style.backgroundColor = originalColor; // Revert back after 300ms
  335. }, 300);
  336. } catch (error) {
  337. logError('Error flashing button', error);
  338. }
  339. }
  340.  
  341. // Copy Output button functionality with flash effect
  342. calculatorContainer.querySelector('#copyOutputBtn').addEventListener('click', () => {
  343. try {
  344. const resultsText = document.getElementById('results').innerText;
  345. navigator.clipboard.writeText(resultsText)
  346. .then(() => {
  347. flashButton(calculatorContainer.querySelector('#copyOutputBtn')); // Flash button
  348. console.log('Output copied to clipboard!');
  349. })
  350. .catch(err => logError('Failed to copy output', err));
  351. } catch (error) {
  352. logError('Error copying output', error);
  353. }
  354. });
  355.  
  356. // Copy Input button functionality with flash effect
  357. calculatorContainer.querySelector('#copyInputBtn').addEventListener('click', () => {
  358. try {
  359. const selectedItem = document.querySelector('#itemSearch').value;
  360. const bonusPlatinumLevel = document.querySelector('#bonusPlatinumLevel').value;
  361. const exchangeRateLevel = document.querySelector('#exchangeRateLevel').value;
  362. const itemGoldCost = document.querySelector('#itemGoldCost').value;
  363. const goldPerPlatinum = document.querySelector('#goldPerPlatinum').value;
  364.  
  365. const inputText = `
  366. Selected Item: ${selectedItem}
  367. Bonus Platinum Level: ${bonusPlatinumLevel}
  368. Exchange Rate Level: ${exchangeRateLevel}
  369. Gold cost per item: ${itemGoldCost}
  370. Gold value per platinum (in millions): ${goldPerPlatinum}
  371. `;
  372.  
  373. navigator.clipboard.writeText(inputText.trim())
  374. .then(() => {
  375. flashButton(calculatorContainer.querySelector('#copyInputBtn')); // Flash button
  376. console.log('Input values copied to clipboard!');
  377. })
  378. .catch(err => logError('Failed to copy input', err));
  379. } catch (error) {
  380. logError('Error copying input', error);
  381. }
  382. });
  383.  
  384. // Searchable dropdown functionality
  385. const itemSearch = document.getElementById('itemSearch');
  386. const itemDropdown = document.getElementById('itemDropdown');
  387. let selectedItemPoints = 0;
  388.  
  389. function populateDropdown(items) {
  390. try {
  391. itemDropdown.innerHTML = '';
  392. items.forEach(item => {
  393. const div = document.createElement('div');
  394. div.textContent = item.name;
  395. div.onclick = function() {
  396. itemSearch.value = item.name;
  397. selectedItemPoints = item.points;
  398. itemDropdown.style.display = 'none';
  399. };
  400. itemDropdown.appendChild(div);
  401. });
  402. itemDropdown.style.display = 'block';
  403. } catch (error) {
  404. logError('Error populating dropdown', error);
  405. }
  406. }
  407.  
  408. itemSearch.addEventListener('focus', function() {
  409. try {
  410. if (this.value === '') {
  411. populateDropdown(itemList);
  412. }
  413. } catch (error) {
  414. logError('Error handling item search focus', error);
  415. }
  416. });
  417.  
  418. itemSearch.addEventListener('input', function() {
  419. try {
  420. const searchTerm = this.value.toLowerCase();
  421. const filteredItems = searchTerm === '' ? itemList : itemList.filter(item =>
  422. item.name.toLowerCase().includes(searchTerm)
  423. );
  424. populateDropdown(filteredItems);
  425. } catch (error) {
  426. logError('Error handling item search input', error);
  427. }
  428. });
  429.  
  430. document.addEventListener('click', function(e) {
  431. try {
  432. if (e.target !== itemSearch && e.target !== itemDropdown) {
  433. itemDropdown.style.display = 'none';
  434. }
  435. } catch (error) {
  436. logError('Error handling document click', error);
  437. }
  438. });
  439.  
  440. // Calculate button functionality
  441. calculatorContainer.querySelector('#calculateBtn').addEventListener('click', () => {
  442. try {
  443. const bonusPlatinumLevel = parseFloat(document.querySelector('#bonusPlatinumLevel').value);
  444. const exchangeRateLevel = parseFloat(document.querySelector('#exchangeRateLevel').value);
  445. const itemGoldCost = parseFloat(document.querySelector('#itemGoldCost').value);
  446. const goldPerPlatinum = parseFloat(document.querySelector('#goldPerPlatinum').value);
  447.  
  448. // Validation: Check for positive values
  449. if (
  450. selectedItemPoints <= 0 ||
  451. isNaN(bonusPlatinumLevel) || bonusPlatinumLevel < 0 ||
  452. isNaN(exchangeRateLevel) || exchangeRateLevel < 0 ||
  453. isNaN(itemGoldCost) || itemGoldCost < 0 ||
  454. isNaN(goldPerPlatinum) || goldPerPlatinum < 0
  455. ) {
  456. throw new Error('Invalid input values');
  457. }
  458.  
  459. saveInputs(selectedItemPoints, bonusPlatinumLevel, exchangeRateLevel, itemGoldCost, goldPerPlatinum);
  460.  
  461. const ITEMS_PER_RECYCLE = 200;
  462. const BASE_PLATINUM_POINTS = selectedItemPoints;
  463. const BASE_PLATINUM_COST = 10000;
  464. const MAX_COST_INCREASE_PLATINUM = 20;
  465. const PLATINUM_GOLD_COST = 250000;
  466. const BONUS_PLATINUM_INCREMENT = 0.002;
  467.  
  468. const exchangeRateMultiplier = 1 + (exchangeRateLevel * 0.01);
  469. const bonusPlatinumMultiplier = 1 + (bonusPlatinumLevel * BONUS_PLATINUM_INCREMENT);
  470. const platinumPointsPerRecycle = Math.floor(BASE_PLATINUM_POINTS * exchangeRateMultiplier);
  471. let totalGoldProfit = 0;
  472. let platinumCount = 0;
  473. let totalGoldSpent = 0;
  474. let totalItemsUsed = 0;
  475. let currentPlatinumCost = BASE_PLATINUM_COST;
  476.  
  477. while (true) {
  478. const platinumPointsNeeded = currentPlatinumCost;
  479. const requiredRecycles = Math.ceil(platinumPointsNeeded / platinumPointsPerRecycle);
  480. const requiredItems = requiredRecycles * ITEMS_PER_RECYCLE;
  481. totalItemsUsed += requiredItems;
  482.  
  483. const itemsGoldCostTotal = requiredItems * itemGoldCost;
  484. const totalGoldCost = PLATINUM_GOLD_COST + itemsGoldCostTotal;
  485.  
  486. // Total platinum produced including bonus
  487. const totalPlatinum = 1 * bonusPlatinumMultiplier;
  488. const basePlatinum = 1; // Base platinum unit
  489. const bonusPlatinum = totalPlatinum - basePlatinum; // Bonus platinum units
  490.  
  491. // Cost per unit of platinum
  492. const costPerPlatinum = totalGoldCost / totalPlatinum;
  493.  
  494. // Calculate profit per platinum unit
  495. let profit = 0;
  496.  
  497. // Profit from base platinum (100% of its gold value)
  498. profit += (basePlatinum * goldPerPlatinum * 1_000_000) - (costPerPlatinum * basePlatinum);
  499.  
  500. // Profit from bonus platinum (90% of its gold value)
  501. profit += (bonusPlatinum * goldPerPlatinum * 1_000_000 * 0.9) - (costPerPlatinum * bonusPlatinum);
  502.  
  503. // If profit is non-positive, break the loop
  504. if (profit <= 0) {
  505. break;
  506. }
  507.  
  508. // Accumulate the total profits and costs
  509. totalGoldProfit += profit;
  510. totalGoldSpent += totalGoldCost;
  511. platinumCount++;
  512.  
  513. // Increase platinum cost for the next cycle
  514. if (platinumCount < MAX_COST_INCREASE_PLATINUM) {
  515. currentPlatinumCost += 1000;
  516. } else {
  517. currentPlatinumCost += 500;
  518. }
  519. }
  520.  
  521. // Display results
  522. document.getElementById('results').innerHTML = `
  523. <p style="color: #dee5ed;">Platinum to produce: ${platinumCount}</p>
  524. <p style="color: #dee5ed;">Total Gold Profit: ${formatToBillions(totalGoldProfit)}</p>
  525. <p style="color: #dee5ed;">Maximum Bonus Platinum: ${(platinumCount * bonusPlatinumMultiplier).toFixed(2)}</p>
  526. <p style="color: #dee5ed;">Total Items Used: ${totalItemsUsed}</p>
  527. `;
  528. } catch (error) {
  529. logError('Error in calculation', error);
  530. alert('An error occurred during calculation. Please check your inputs and try again.');
  531. }
  532. });
  533.  
  534. // Function to format value to billions
  535. function formatToBillions(value) {
  536. try {
  537. let billions = value / 1_000_000_000;
  538. return billions.toFixed(2) + " billion";
  539. } catch (error) {
  540. logError('Error formatting to billions', error);
  541. return "Error";
  542. }
  543. }
  544.  
  545. // Save inputs to localStorage
  546. function saveInputs(selectedItemPoints, bonusPlatinumLevel, exchangeRateLevel, itemGoldCost, goldPerPlatinum) {
  547. try {
  548. localStorage.setItem('selectedItemPoints', selectedItemPoints);
  549. localStorage.setItem('bonusPlatinumLevel', bonusPlatinumLevel);
  550. localStorage.setItem('exchangeRateLevel', exchangeRateLevel);
  551. localStorage.setItem('itemGoldCost', itemGoldCost);
  552. localStorage.setItem('goldPerPlatinum', goldPerPlatinum);
  553. } catch (error) {
  554. logError('Error saving inputs to localStorage', error);
  555. }
  556. }
  557.  
  558. // Load inputs from localStorage
  559. function loadInputs() {
  560. try {
  561. const savedItemPoints = localStorage.getItem('selectedItemPoints');
  562. if (savedItemPoints) {
  563. const savedItem = itemList.find(item => item.points == savedItemPoints);
  564. if (savedItem) {
  565. document.querySelector('#itemSearch').value = savedItem.name;
  566. selectedItemPoints = savedItem.points;
  567. }
  568. }
  569. document.querySelector('#bonusPlatinumLevel').value = localStorage.getItem('bonusPlatinumLevel') || '';
  570. document.querySelector('#exchangeRateLevel').value = localStorage.getItem('exchangeRateLevel') || '';
  571. document.querySelector('#itemGoldCost').value = localStorage.getItem('itemGoldCost') || '';
  572. document.querySelector('#goldPerPlatinum').value = localStorage.getItem('goldPerPlatinum') || '';
  573. } catch (error) {
  574. logError('Error loading inputs from localStorage', error);
  575. }
  576. }
  577.  
  578. // Clear inputs from localStorage
  579. function clearInputs() {
  580. try {
  581. localStorage.removeItem('selectedItemPoints');
  582. localStorage.removeItem('bonusPlatinumLevel');
  583. localStorage.removeItem('exchangeRateLevel');
  584. localStorage.removeItem('itemGoldCost');
  585. localStorage.removeItem('goldPerPlatinum');
  586. } catch (error) {
  587. logError('Error clearing inputs from localStorage', error);
  588. }
  589. }
  590.  
  591. // URL check function to show button and calculator only on the correct page
  592. function checkURL() {
  593. try {
  594. if (window.location.href === TARGET_URL) {
  595. button.style.display = 'block';
  596. if (calculatorContainer.style.display !== 'none') {
  597. positionCalculator();
  598. }
  599. } else {
  600. button.style.display = 'none';
  601. calculatorContainer.style.display = 'none';
  602. }
  603. } catch (error) {
  604. logError('Error checking URL', error);
  605. }
  606. }
  607.  
  608. // Check URL initially and set interval for changes
  609. checkURL();
  610. setInterval(checkURL, 500); // Check URL every 500 milliseconds
  611.  
  612. })();