Elethor Recyclobot Calculator

Calculate platinum production and gold profit with a UI

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

  1. // ==UserScript==
  2. // @name Elethor Recyclobot Calculator
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Calculate platinum production and gold profit with a UI
  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. // Define the target URL for the calculator to display
  29. const TARGET_URL = 'https://elethor.com/character/companion/recyclobot';
  30.  
  31. // Create a button to open the calculator
  32. const button = document.createElement('button');
  33. button.innerText = 'Open Recyclobot Calculator';
  34. button.style.padding = '10px';
  35. button.style.backgroundColor = '#18743c'; // Button color
  36. button.style.color = '#dee5ed'; // Text color
  37. button.style.border = 'none';
  38. button.style.borderRadius = '5px';
  39. button.style.cursor = 'pointer';
  40. button.style.position = 'absolute'; // Use absolute positioning for precise placement
  41. button.style.zIndex = '9999'; // Ensure the button appears on top
  42. button.style.display = 'none'; // Initially hidden
  43.  
  44. // Function to position the button
  45. function positionButton() {
  46. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full'); // Target button
  47. if (referenceElement) {
  48. const rect = referenceElement.getBoundingClientRect();
  49. button.style.top = `${rect.top + window.scrollY - button.offsetHeight - 50}px`; // Move button above the reference element (50px margin)
  50. button.style.left = `${rect.left + window.scrollX - button.offsetWidth - 10}px`; // Position to the left with a margin
  51. document.body.appendChild(button); // Append button to the body
  52. }
  53. }
  54.  
  55. // Create a MutationObserver to detect changes in the DOM
  56. const observer = new MutationObserver((mutations) => {
  57. // Check if the target element is in the DOM
  58. const referenceElement = document.querySelector('.button.is-info.is-multiline.w-full');
  59. if (referenceElement) {
  60. positionButton(); // Position the button
  61. observer.disconnect(); // Stop observing once the button is placed
  62. }
  63. });
  64.  
  65. // Start observing the document body for changes
  66. observer.observe(document.body, { childList: true, subtree: true });
  67.  
  68. // Create the calculator UI
  69. const calculatorContainer = document.createElement('div');
  70. calculatorContainer.style.display = 'none'; // Initially hidden
  71. calculatorContainer.style.position = 'fixed';
  72. calculatorContainer.style.border = '2px solid #505c6c'; // Border around calculator
  73. calculatorContainer.style.borderRadius = '10px';
  74. calculatorContainer.style.backgroundColor = '#202c3c'; // Background color
  75. calculatorContainer.style.zIndex = '1001';
  76. calculatorContainer.style.padding = '20px';
  77.  
  78. // Function to position the calculator below the "Prospector" element
  79. function positionCalculator() {
  80. const prospectorElement = document.querySelector('a[href="/character/companion/prospector"]');
  81. if (prospectorElement) {
  82. const rect = prospectorElement.getBoundingClientRect();
  83. calculatorContainer.style.top = `${rect.bottom + window.scrollY - 230 + 10}px`; // 250px up from the bottom of the Prospector element + 10px margin
  84. calculatorContainer.style.left = `${rect.left + window.scrollX}px`; // Align horizontally with the Prospector element
  85. }
  86. }
  87.  
  88. // Set the initial position of the calculator
  89. positionCalculator();
  90. document.body.appendChild(calculatorContainer);
  91.  
  92. // Add form elements to the calculator
  93. calculatorContainer.innerHTML = `
  94. <h3 style="color: #dee5ed;">Elethor Recyclobot Calculator</h3>
  95. <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>
  96. <div style="margin-bottom: 5px;">
  97. <label style="color: #dee5ed;">Bonus Platinum Level:</label>
  98. <input type="number" id="bonusPlatinumLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  99. </div>
  100. <div style="margin-bottom: 5px;">
  101. <label style="color: #dee5ed;">Exchange Rate Level:</label>
  102. <input type="number" id="exchangeRateLevel" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  103. </div>
  104. <div style="margin-bottom: 5px;">
  105. <label style="color: #dee5ed;">Gold cost per curved blade:</label>
  106. <input type="number" step="0.01" id="curvedBladeGoldCost" required style="border: 1px solid #dee5ed; border-radius: 5px; padding: 5px; width: 100px; margin-right: 10px; color: black;">
  107. </div>
  108. <div style="margin-bottom: 5px;">
  109. <label style="color: #dee5ed;">Gold value per platinum (in millions):</label>
  110. <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;">
  111. </div>
  112.  
  113. <div style="display: flex; gap: 10px; margin-top: 10px;">
  114. <button id="calculateBtn" style="background-color: #18743c; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">Calculate</button>
  115. <button id="resetBtn" style="background-color: #2596be; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">🔄 Reset</button>
  116. <button id="closeBtn" style="background-color: #a02424; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">X Close</button>
  117. </div>
  118. <div style="display: flex; gap: 10px; margin-top: 10px;">
  119. <button id="copyInputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Input</button>
  120. <button id="copyOutputBtn" style="background-color: #ea951f; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer;">📋 Copy Output</button>
  121. </div>
  122. <div id="results" style="margin-top: 20px;"></div>
  123. `;
  124.  
  125. // Show/hide calculator on button click
  126. button.addEventListener('click', () => {
  127. // Toggle the visibility of the calculator
  128. if (calculatorContainer.style.display === 'none') {
  129. calculatorContainer.style.display = 'block';
  130. positionCalculator(); // Ensure it is positioned correctly when opened
  131. loadInputs(); // Load saved inputs when opening
  132. } else {
  133. calculatorContainer.style.display = 'none';
  134. }
  135. });
  136.  
  137. // Close button
  138. calculatorContainer.querySelector('#closeBtn').addEventListener('click', () => {
  139. calculatorContainer.style.display = 'none';
  140. });
  141.  
  142. // Reset button functionality
  143. calculatorContainer.querySelector('#resetBtn').addEventListener('click', () => {
  144. document.querySelector('#bonusPlatinumLevel').value = '';
  145. document.querySelector('#exchangeRateLevel').value = '';
  146. document.querySelector('#curvedBladeGoldCost').value = '';
  147. document.querySelector('#goldPerPlatinum').value = '';
  148. clearInputs(); // Clear localStorage
  149. document.getElementById('results').innerHTML = ''; // Clear results
  150. });
  151.  
  152. // Copy Output button functionality
  153. calculatorContainer.querySelector('#copyOutputBtn').addEventListener('click', () => {
  154. const resultsText = document.getElementById('results').innerText;
  155. navigator.clipboard.writeText(resultsText)
  156. .then(() => {
  157. // You can log to console instead of showing an alert
  158. console.log('Output copied to clipboard!');
  159. })
  160. .catch(err => console.error('Failed to copy: ', err));
  161. });
  162.  
  163. // Copy Input button functionality
  164. calculatorContainer.querySelector('#copyInputBtn').addEventListener('click', () => {
  165. const bonusPlatinumLevel = document.querySelector('#bonusPlatinumLevel').value;
  166. const exchangeRateLevel = document.querySelector('#exchangeRateLevel').value;
  167. const curvedBladeGoldCost = document.querySelector('#curvedBladeGoldCost').value;
  168. const goldPerPlatinum = document.querySelector('#goldPerPlatinum').value;
  169.  
  170. const inputText = `
  171. Bonus Platinum Level: ${bonusPlatinumLevel}
  172. Exchange Rate Level: ${exchangeRateLevel}
  173. Gold cost per curved blade: ${curvedBladeGoldCost}
  174. Gold value per platinum (in millions): ${goldPerPlatinum}
  175. `;
  176.  
  177. navigator.clipboard.writeText(inputText.trim())
  178. .then(() => {
  179. // You can log to console instead of showing an alert
  180. console.log('Input values copied to clipboard!');
  181. })
  182. .catch(err => console.error('Failed to copy: ', err));
  183. });
  184.  
  185. // Calculate button functionality
  186. calculatorContainer.querySelector('#calculateBtn').addEventListener('click', () => {
  187. const bonusPlatinumLevel = parseFloat(document.querySelector('#bonusPlatinumLevel').value);
  188. const exchangeRateLevel = parseFloat(document.querySelector('#exchangeRateLevel').value);
  189. const curvedBladeGoldCost = parseFloat(document.querySelector('#curvedBladeGoldCost').value);
  190. const goldPerPlatinum = parseFloat(document.querySelector('#goldPerPlatinum').value);
  191.  
  192. // Validation: Check for positive values
  193. if (
  194. isNaN(bonusPlatinumLevel) || bonusPlatinumLevel < 0 ||
  195. isNaN(exchangeRateLevel) || exchangeRateLevel < 0 ||
  196. isNaN(curvedBladeGoldCost) || curvedBladeGoldCost < 0 ||
  197. isNaN(goldPerPlatinum) || goldPerPlatinum < 0
  198. ) {
  199. alert('Please enter positive values for all fields.');
  200. return; // Exit the function if validation fails
  201. }
  202.  
  203. saveInputs(bonusPlatinumLevel, exchangeRateLevel, curvedBladeGoldCost, goldPerPlatinum);
  204.  
  205. const CURVED_BLADES_PER_RECYCLE = 200;
  206. const BASE_PLATINUM_POINTS = 1080;
  207. const BASE_PLATINUM_COST = 10000;
  208. const MAX_COST_INCREASE_PLATINUM = 20;
  209. const PLATINUM_GOLD_COST = 250000;
  210. const BONUS_PLATINUM_INCREMENT = 0.002;
  211.  
  212. const exchangeRateMultiplier = 1 + (exchangeRateLevel * 0.01);
  213. const bonusPlatinumMultiplier = 1 + (bonusPlatinumLevel * BONUS_PLATINUM_INCREMENT);
  214. const platinumPointsPerRecycle = Math.floor(BASE_PLATINUM_POINTS * exchangeRateMultiplier);
  215. let totalGoldProfit = 0;
  216. let platinumCount = 0;
  217. let totalGoldSpent = 0;
  218. let totalCurvedBladesUsed = 0;
  219. let currentPlatinumCost = BASE_PLATINUM_COST;
  220.  
  221. while (true) {
  222. const platinumPointsNeeded = currentPlatinumCost;
  223. const requiredRecycles = Math.ceil(platinumPointsNeeded / platinumPointsPerRecycle);
  224. const requiredCurvedBlades = requiredRecycles * CURVED_BLADES_PER_RECYCLE;
  225. totalCurvedBladesUsed += requiredCurvedBlades;
  226.  
  227. const curvedBladesGoldCostTotal = requiredCurvedBlades * curvedBladeGoldCost;
  228. const totalGoldCost = PLATINUM_GOLD_COST + curvedBladesGoldCostTotal;
  229.  
  230. // Total platinum produced including bonus
  231. const totalPlatinum = 1 * bonusPlatinumMultiplier;
  232. const basePlatinum = 1; // Base platinum unit
  233. const bonusPlatinum = totalPlatinum - basePlatinum; // Bonus platinum units
  234.  
  235. // Cost per unit of platinum
  236. const costPerPlatinum = totalGoldCost / totalPlatinum;
  237.  
  238. // Calculate profit per platinum unit
  239. let profit = 0;
  240. // Profit from base platinum (100% of its gold value)
  241. profit += (basePlatinum * goldPerPlatinum * 1_000_000) - (costPerPlatinum * basePlatinum);
  242.  
  243. // Profit from bonus platinum (90% of its gold value)
  244. profit += (bonusPlatinum * goldPerPlatinum * 1_000_000 * 0.9) - (costPerPlatinum * bonusPlatinum);
  245.  
  246. // If profit is non-positive, break the loop
  247. if (profit <= 0) {
  248. break;
  249. }
  250.  
  251. // Accumulate the total profits and costs
  252. totalGoldProfit += profit;
  253. totalGoldSpent += totalGoldCost;
  254. platinumCount++;
  255.  
  256. // Increase platinum cost for the next cycle
  257. if (platinumCount < MAX_COST_INCREASE_PLATINUM) {
  258. currentPlatinumCost += 1000;
  259. } else {
  260. currentPlatinumCost += 500;
  261. }
  262. }
  263.  
  264. // Display results
  265. document.getElementById('results').innerHTML = `
  266. <p style="color: #dee5ed;">Platinum to produce: ${platinumCount}</p>
  267. <p style="color: #dee5ed;">Total Gold Profit: ${formatToBillions(totalGoldProfit)}</p>
  268. <p style="color: #dee5ed;">Maximum Bonus Platinum: ${(platinumCount * bonusPlatinumMultiplier).toFixed(2)}</p>
  269. <p style="color: #dee5ed;">Total Curved Blades Used: ${totalCurvedBladesUsed}</p> <!-- New output -->
  270. `;
  271. });
  272.  
  273. // Function to format value to billions
  274. function formatToBillions(value) {
  275. let billions = value / 1_000_000_000;
  276. return billions.toFixed(2) + " billion";
  277. }
  278.  
  279. // Save inputs to localStorage
  280. function saveInputs(bonusPlatinumLevel, exchangeRateLevel, curvedBladeGoldCost, goldPerPlatinum) {
  281. localStorage.setItem('bonusPlatinumLevel', bonusPlatinumLevel);
  282. localStorage.setItem('exchangeRateLevel', exchangeRateLevel);
  283. localStorage.setItem('curvedBladeGoldCost', curvedBladeGoldCost);
  284. localStorage.setItem('goldPerPlatinum', goldPerPlatinum);
  285. }
  286.  
  287. // Load inputs from localStorage
  288. function loadInputs() {
  289. document.querySelector('#bonusPlatinumLevel').value = localStorage.getItem('bonusPlatinumLevel') || '';
  290. document.querySelector('#exchangeRateLevel').value = localStorage.getItem('exchangeRateLevel') || '';
  291. document.querySelector('#curvedBladeGoldCost').value = localStorage.getItem('curvedBladeGoldCost') || '';
  292. document.querySelector('#goldPerPlatinum').value = localStorage.getItem('goldPerPlatinum') || '';
  293. }
  294.  
  295. // Clear inputs from localStorage
  296. function clearInputs() {
  297. localStorage.removeItem('bonusPlatinumLevel');
  298. localStorage.removeItem('exchangeRateLevel');
  299. localStorage.removeItem('curvedBladeGoldCost');
  300. localStorage.removeItem('goldPerPlatinum');
  301. }
  302.  
  303. // URL check function to show button and calculator only on the correct page
  304. function checkURL() {
  305. if (window.location.href === TARGET_URL) {
  306. button.style.display = 'block';
  307. if (calculatorContainer.style.display !== 'none') {
  308. positionCalculator();
  309. }
  310. } else {
  311. button.style.display = 'none';
  312. calculatorContainer.style.display = 'none';
  313. }
  314. }
  315.  
  316. // Check URL initially and set interval for changes
  317. checkURL();
  318. setInterval(checkURL, 500); // Check URL every 500 milliseconds
  319.  
  320. })();