您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Embeds a collapsible gym gains calculator at the top of the gym page with fine-tuned formula, adjusted happiness decay, and persistent user inputs
当前为
// ==UserScript== // @name TornPDA - Gym Gains Calculator // @namespace http://tampermonkey.net/ // @version 1.8 // @description Embeds a collapsible gym gains calculator at the top of the gym page with fine-tuned formula, adjusted happiness decay, and persistent user inputs // @author Jvmie[2094564] // @match https://www.torn.com/gym.php* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // Gym data from your spreadsheet const gyms = { "Premier Fitness": { Str: 2, Spe: 2, Def: 2, Dex: 2, Energy: 5 }, "Average Joes": { Str: 2.4, Spe: 2.4, Def: 2.8, Dex: 2.4, Energy: 5 }, "Woody's Workout": { Str: 2.8, Spe: 3.2, Def: 3, Dex: 2.8, Energy: 5 }, "Beach Bods": { Str: 3.2, Spe: 3.2, Def: 3.2, Dex: 0, Energy: 5 }, "Silver Gym": { Str: 3.4, Spe: 3.6, Def: 3.4, Dex: 3.2, Energy: 5 }, "Pour Femme": { Str: 3.4, Spe: 3.6, Def: 3.6, Dex: 3.8, Energy: 5 }, "Davies Den": { Str: 3.7, Spe: 0, Def: 3.7, Dex: 3.7, Energy: 5 }, "Global Gym": { Str: 4, Spe: 4, Def: 4, Dex: 4, Energy: 5 }, "Knuckle Heads": { Str: 4.8, Spe: 4.4, Def: 4, Dex: 4.2, Energy: 10 }, "Pioneer Fitness": { Str: 4.4, Spe: 4.6, Def: 4.8, Dex: 4.4, Energy: 10 }, "Anabolic Anomalies": { Str: 5, Spe: 4.6, Def: 5.2, Dex: 4.6, Energy: 10 }, "Core": { Str: 5, Spe: 5.2, Def: 5, Dex: 5, Energy: 10 }, "Racing Fitness": { Str: 5, Spe: 5.4, Def: 4.8, Dex: 5.2, Energy: 10 }, "Complete Cardio": { Str: 5.5, Spe: 5.8, Def: 5.5, Dex: 5.2, Energy: 10 }, "Legs Bums and Tums": { Str: 0, Spe: 5.6, Def: 5.6, Dex: 5.8, Energy: 10 }, "Deep Burn": { Str: 6, Spe: 6, Def: 6, Dex: 6, Energy: 10 }, "Apollo Gym": { Str: 6, Spe: 6.2, Def: 6.4, Dex: 6.2, Energy: 10 }, "Gun Shop": { Str: 6.6, Spe: 6.4, Def: 6.2, Dex: 6.2, Energy: 10 }, "Force Training": { Str: 6.4, Spe: 6.6, Def: 6.4, Dex: 6.8, Energy: 10 }, "Cha Cha's": { Str: 6.4, Spe: 6.4, Def: 6.8, Dex: 7, Energy: 10 }, "Atlas": { Str: 7, Spe: 6.4, Def: 6.4, Dex: 6.6, Energy: 10 }, "Last Round": { Str: 6.8, Spe: 6.6, Def: 7, Dex: 6.6, Energy: 10 }, "The Edge": { Str: 6.8, Spe: 7, Def: 7, Dex: 6.8, Energy: 10 }, "George's": { Str: 7.3, Spe: 7.3, Def: 7.3, Dex: 7.3, Energy: 10 }, "Balboas Gym": { Str: 0, Spe: 0, Def: 7.5, Dex: 7.5, Energy: 25 }, "Frontline Fitness": { Str: 7.5, Spe: 7.5, Def: 0, Dex: 0, Energy: 25 }, "Gym 3000": { Str: 8, Spe: 0, Def: 0, Dex: 0, Energy: 50 }, "Mr Isoyamas": { Str: 0, Spe: 0, Def: 8, Dex: 0, Energy: 50 }, "Total Rebound": { Str: 0, Spe: 8, Def: 0, Dex: 0, Energy: 50 }, "Elites": { Str: 0, Spe: 0, Def: 0, Dex: 8, Energy: 50 }, "Sports Science Lab": { Str: 9, Spe: 9, Def: 9, Dex: 9, Energy: 25 } }; // Fine-tuned gain formula based on actual Torn City mechanics function calculateSingleGain(stat, happy, gymDots, bonusMultiplier) { const baseGain = (gymDots * (0.00019106 * stat + 0.00226263 * happy + 0.55) * 4) / 12.94; return baseGain * bonusMultiplier; } // Calculate total gain with happiness decay function calculateTotalGain(stat, initialHappy, gymDots, numTrains, bonusMultiplier) { let totalGain = 0; let currentHappy = initialHappy; let currentStat = stat; for (let i = 0; i < numTrains; i++) { const gain = calculateSingleGain(currentStat, currentHappy, gymDots, bonusMultiplier); totalGain += gain; currentStat += gain; // Update stat for next iteration currentHappy -= 4.7; // Happiness decays by 4.7 per train (adjusted to match log) if (currentHappy < 0) currentHappy = 0; // Prevent negative happiness } return { singleGain: calculateSingleGain(stat, initialHappy, gymDots, bonusMultiplier), totalGain }; } // Dropdown options for bonuses const factionBonuses = Array.from({ length: 19 }, (_, i) => i); // 0% to 18% const propertyBonuses = [0, 1, 2]; // 0% to 2% const educationBonuses = [0, 1]; // 0% to 1% // HTML for collapsible menu and calculator with improved styling const calculatorHTML = ` <div id="gymCalcContainer" style="margin: 10px; font-family: 'Arial', sans-serif; color: #fff;"> <button id="collapseBtn" style="width: 100%; padding: 10px; background: #1a1a1a; color: #fff; border: none; cursor: pointer; text-align: left; font-size: 16px; font-weight: bold;"> Gym Gains Calculator ▼ </button> <div id="calcContent" style="display: none; padding: 15px; background: #2a2a2a; border: 1px solid #444; border-radius: 5px;"> <div style="display: flex; flex-wrap: wrap; gap: 10px;"> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Stat to Train: <select id="statSelect" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> <option value="Str">Strength</option> <option value="Spe">Speed</option> <option value="Def">Defense</option> <option value="Dex">Dexterity</option> </select> </label> </div> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Gym: <select id="gymSelect" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> ${Object.keys(gyms).map(gym => `<option value="${gym}" ${gym === "Gun Shop" ? "selected" : ""}>${gym}</option>`).join('')} </select> </label> </div> </div> <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;"> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Current Stat: <input type="number" id="currentStat" min="0" value="0" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> </label> </div> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Happiness: <input type="number" id="happy" min="0" value="0" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> </label> </div> </div> <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;"> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Total Energy: <input type="number" id="energy" min="0" value="0" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> </label> </div> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Faction Bonus (%): <select id="factionBonus" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> ${factionBonuses.map(val => `<option value="${val}" ${val === 0 ? "selected" : ""}>${val}%</option>`).join('')} </select> </label> </div> </div> <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;"> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Property Bonus (%): <select id="propBonus" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> ${propertyBonuses.map(val => `<option value="${val}" ${val === 0 ? "selected" : ""}>${val}%</option>`).join('')} </select> </label> </div> <div style="flex: 1; min-width: 200px;"> <label style="font-size: 14px; color: #ccc;">Education Bonus (%): <select id="eduBonus" style="width: 100%; padding: 5px; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px;"> ${educationBonuses.map(val => `<option value="${val}" ${val === 0 ? "selected" : ""}>${val}%</option>`).join('')} </select> </label> </div> </div> <button id="calcBtn" style="margin-top: 15px; padding: 8px 15px; background: #4CAF50; color: #fff; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;">Calculate</button> <div id="result" style="margin-top: 15px; font-size: 14px; color: #fff;"></div> </div> </div> `; // Inject calculator at the top of the gym page const gymPage = document.querySelector('.content-wrapper'); if (gymPage) { gymPage.insertAdjacentHTML('afterbegin', calculatorHTML); // Collapsible menu functionality const collapseBtn = document.getElementById('collapseBtn'); const calcContent = document.getElementById('calcContent'); collapseBtn.addEventListener('click', () => { if (calcContent.style.display === 'none') { calcContent.style.display = 'block'; collapseBtn.textContent = 'Gym Gains Calculator ▲'; } else { calcContent.style.display = 'none'; collapseBtn.textContent = 'Gym Gains Calculator ▼'; } }); // Function to save input values to localStorage const saveToLocalStorage = (key, value) => { localStorage.setItem(`gymCalc_${key}`, value); }; // Function to load input values from localStorage const loadFromLocalStorage = (key, defaultValue) => { return localStorage.getItem(`gymCalc_${key}`) || defaultValue; }; // Get all input elements const statSelect = document.getElementById('statSelect'); const gymSelect = document.getElementById('gymSelect'); const currentStat = document.getElementById('currentStat'); const happy = document.getElementById('happy'); const energy = document.getElementById('energy'); const factionBonus = document.getElementById('factionBonus'); const propBonus = document.getElementById('propBonus'); const eduBonus = document.getElementById('eduBonus'); // Load saved values from localStorage statSelect.value = loadFromLocalStorage('statSelect', 'Str'); gymSelect.value = loadFromLocalStorage('gymSelect', 'Gun Shop'); currentStat.value = loadFromLocalStorage('currentStat', '0'); happy.value = loadFromLocalStorage('happy', '0'); energy.value = loadFromLocalStorage('energy', '0'); factionBonus.value = loadFromLocalStorage('factionBonus', '0'); propBonus.value = loadFromLocalStorage('propBonus', '0'); eduBonus.value = loadFromLocalStorage('eduBonus', '0'); // Save values to localStorage on change statSelect.addEventListener('change', () => saveToLocalStorage('statSelect', statSelect.value)); gymSelect.addEventListener('change', () => saveToLocalStorage('gymSelect', gymSelect.value)); currentStat.addEventListener('input', () => saveToLocalStorage('currentStat', currentStat.value)); happy.addEventListener('input', () => saveToLocalStorage('happy', happy.value)); energy.addEventListener('input', () => saveToLocalStorage('energy', energy.value)); factionBonus.addEventListener('change', () => saveToLocalStorage('factionBonus', factionBonus.value)); propBonus.addEventListener('change', () => saveToLocalStorage('propBonus', propBonus.value)); eduBonus.addEventListener('change', () => saveToLocalStorage('eduBonus', eduBonus.value)); // Calculate button functionality document.getElementById('calcBtn').addEventListener('click', () => { const statKey = statSelect.value; const gymName = gymSelect.value; const currentStatValue = parseFloat(currentStat.value) || 0; const happyValue = parseFloat(happy.value) || 0; const energyValue = parseFloat(energy.value) || 0; const factionBonusValue = (parseFloat(factionBonus.value) || 0) / 100; const propBonusValue = (parseFloat(propBonus.value) || 0) / 100; const eduBonusValue = (parseFloat(eduBonus.value) || 0) / 100; const gym = gyms[gymName]; const gymDots = gym[statKey]; const energyPerTrain = gym.Energy; const numTrains = Math.floor(energyValue / energyPerTrain); const bonusMultiplier = (1 + factionBonusValue) * (1 + propBonusValue) * (1 + eduBonusValue); if (gymDots === 0) { document.getElementById('result').innerHTML = `<span style="color: #ff5555;">This gym does not train ${statKey}!</span>`; return; } const { singleGain, totalGain } = calculateTotalGain(currentStatValue, happyValue, gymDots, numTrains, bonusMultiplier); document.getElementById('result').innerHTML = ` <b style="color: #4CAF50;">Results:</b><br> Single Train Gain: ${singleGain.toFixed(2)}<br> Total Trains: ${numTrains}<br> Total Gain: ${totalGain.toFixed(2)} `; }); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址