您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Manarion 游戏数据计算器
// ==UserScript== // @name Manarion helper // @name:zh-CN Manarion 助手 // @name:en Manarion helper // @namespace http://tampermonkey.net/ // @version 0.0.4.1 // @description game Manarion calculation tool with combat/gathering efficiency calculator // @description:zh-CN Manarion 游戏数据计算器 // @description:en game Manarion calculation tool with combat/gathering efficiency calculator // @author LemonApostle // @match https://manarion.com/* // @match https://*.manarion.com/* // @icon https://s2.loli.net/2025/05/28/YmWGhwXJVHonOsI.png // @grant GM_xmlhttpRequest // @connect api.manarion.com // @run-at document-end // @license MIT // @homepage https://greasyfork.org/zh-CN/scripts/546781-manarion-helper // @supportURL https://greasyfork.org/zh-CN/scripts/546781-manarion-helper/feedback // ==/UserScript== if (window.top !== window) return; (function() { 'use strict'; if (window.top !== window) return; function getFullHTMLContent() { return `<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title data-translate-key="title">计算器</title> <style> /* --- THEME COLORS USING CSS VARIABLES --- */ :root { --bg-gradient-start: #667eea; --bg-gradient-end: #764ba2; --container-bg: rgba(255, 255, 255, 0.95); --card-bg: #ffffff; --section-bg: #f8f9fa; --input-bg: #f9f9f9; --button-gradient-start: #667eea; --button-gradient-end: #764ba2; --text-color: #333; --text-color-light: #666; --border-color: #e0e0e0; --input-border-color: #ddd; --accent-border-color: #667eea; --success-bg: #d4edda; --success-text: #155724; --success-border: #28a745; --error-bg: #f8d7da; --error-text: #721c24; --error-border: #dc3545; --note-bg: #e3f2fd; --note-text: #1565c0; --note-border: #bbdefb; --calc-result-bg: #e8f5e8; --calc-result-text: #2e7d2e; --result-content-bg: #f5f5f5; } body.dark { --bg-gradient-start: oklch(20% .07 262.5); --bg-gradient-end: oklch(4.8% .02 262.5); --container-bg: oklch(12% .05 262.5 / .95); --card-bg: oklch(17.2% .07 262.5); --section-bg: oklch(15% .06 262.5); --input-bg: oklch(20% .07 262.5); --button-gradient-start: oklch(52.5% .23 290); --button-gradient-end: oklch(59.8% .26 292); --text-color: oklch(98.1% .004 257.1); --text-color-light: oklch(67.3% .03 270); --border-color: oklch(25% .07 262.5); --input-border-color: oklch(30% .07 262.5); --accent-border-color: oklch(52.5% .23 290); --success-bg: oklch(25% .1 149); --success-text: oklch(85% .2 149); --success-border: oklch(72.3% .219 149.579); --error-bg: oklch(25% .1 27); --error-text: oklch(85% .2 27); --error-border: oklch(63% .25 27); --note-bg: oklch(25% .1 259); --note-text: oklch(85% .2 259); --note-border: oklch(62.3% .214 259.815); --calc-result-bg: oklch(20% .08 149); --calc-result-text: oklch(85% .2 149); --result-content-bg: oklch(22% .07 262.5); } /* --- END THEME COLORS --- */ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, var(--bg-gradient-start), var(--bg-gradient-end)); min-height: 100vh; box-sizing: border-box; transition: background-color 0.3s ease; } .container { background: var(--container-bg); border-radius: 15px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); transition: background-color 0.3s ease; } h1 { color: var(--text-color); text-align: center; margin-bottom: 30px; font-size: 2.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.1); } .header-buttons { display: flex; gap: 10px; } .input-section { background: var(--section-bg); padding: 20px; border-radius: 10px; margin-bottom: 20px; border-left: 4px solid var(--accent-border-color); transition: background-color 0.3s ease, border-color 0.3s ease; } .input-group { display: flex; gap: 10px; align-items: center; margin-bottom: 15px; } input[type="text"] { flex-grow: 1; min-width: 0; padding: 12px; border: 2px solid var(--input-border-color); background-color: var(--input-bg); color: var(--text-color); border-radius: 8px; font-size: 16px; transition: border-color 0.3s, background-color 0.3s ease; } input[type="text"]:focus { outline: none; border-color: var(--accent-border-color); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent-border-color) 20%, transparent); } button, .calc-button { background: linear-gradient(45deg, var(--button-gradient-start), var(--button-gradient-end)); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; transition: transform 0.2s, box-shadow 0.2s; flex-shrink: 0; } button { padding: 12px 24px; font-size: 16px; } .calc-button { padding: 8px 16px; font-size: 14px; } button:hover, .calc-button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px color-mix(in oklab, var(--button-gradient-start) 40%, transparent); } button:disabled, .calc-button:disabled { background: var(--text-color-light); cursor: not-allowed; transform: none; box-shadow: none; } .theme-toggle-button { background: var(--container-bg); color: var(--accent-border-color); border: 2px solid var(--accent-border-color); padding: 8px; border-radius: 50%; cursor: pointer; font-size: 16px; font-weight: 600; transition: all 0.3s; width: 44px; height: 44px; display: flex; justify-content: center; align-items: center; } .theme-toggle-button:hover { background: var(--accent-border-color); color: var(--card-bg); } .usage-note { background: var(--note-bg); border: 1px solid var(--note-border); border-radius: 6px; padding: 10px; margin-bottom: 15px; color: var(--note-text); font-size: 14px; } .calc-input { display: flex; align-items: center; justify-content: space-between; margin: 10px 0; gap: 15px; } .calc-input label { flex-shrink: 0; font-weight: 500; color: var(--text-color); padding-right: 10px; } .calc-input .input-controls { display: flex; align-items: center; gap: 10px; flex-grow: 1; justify-content: flex-end; } #gatheringType, #gatheringType2 { padding: 8px; border: 1px solid var(--input-border-color); background-color: var(--input-bg); color: var(--text-color); border-radius: 4px; min-width: 120px; } .player-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; margin-bottom: 20px; } .info-card { background: var(--card-bg); border: 1px solid var(--border-color); padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); transition: background-color 0.3s ease, border-color 0.3s ease; } .info-card h3 { margin: 0 0 10px 0; font-size: 1.1em; color: var(--text-color); border-bottom: 1px solid var(--border-color); padding-bottom: 8px; } .stat-row { display: flex; justify-content: space-between; align-items: center; margin: 6px 0; gap: 10px; padding: 2px 0; } .stat-label { font-size: 0.9em; color: var(--text-color-light); font-weight: 500; min-width: 160px; flex-shrink: 0; } .stat-value { font-weight: bold; color: var(--text-color); } .stat-input { width: 80px; padding: 3px 6px; border: 1px solid var(--input-border-color); background: var(--input-bg); color: var(--text-color); border-radius: 4px; text-align: right; font-size: 0.9em; } .stat-input:focus { outline: none; border-color: var(--accent-border-color); background: var(--card-bg); } .error { background-color: var(--error-bg); color: var(--error-text); border-color: var(--error-border); } .success { background-color: var(--success-bg); color: var(--success-text); border-color: var(--success-border); } .success, .error { padding: 15px; border-radius: 8px; margin: 10px 0; border-left: 4px solid; } .calculator-section { background: var(--section-bg); padding: 20px; border-radius: 10px; margin-top: 20px; border-left: 4px solid var(--success-border); transition: background-color 0.3s ease, border-color 0.3s ease; } .calc-result { background: var(--calc-result-bg); padding: 15px; border-radius: 8px; margin-top: 15px; font-weight: bold; color: var(--calc-result-text); word-wrap: break-word; transition: background-color 0.3s ease, color 0.3s ease; } .result-content-box { font-family: monospace; background-color: var(--result-content-bg); color: inherit; padding: 10px; border-radius: 5px; transition: background-color 0.3s ease, color 0.3s ease; } @media (max-width: 768px) { body { padding: 10px; } .container { padding: 15px; } h1 { font-size: 1.8em; } .header-buttons { margin-left: auto; } .container > div:first-child { flex-direction: row; align-items: center; } /* Revert to row for header */ .input-group { flex-direction: column; align-items: stretch; } .calc-input { flex-direction: column; align-items: stretch; gap: 8px; } .calc-input label { padding-right: 0; } .calc-input .input-controls { flex-direction: column; align-items: stretch; width: 100%; gap: 8px; } .calc-button { width: 100%; } } </style> </head> <body> <div class="container"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px;"> <h1 style="margin: 0; flex: 1; text-align: center;" data-translate-key="mainTitle">🏰 Manarion 数据计算器</h1> <div class="header-buttons"> <button id="langToggle" class="theme-toggle-button" onclick="toggleLanguage()" data-translate-key="langToggle" style="min-width: 60px; border-radius: 20px; padding-left: 12px; padding-right: 12px; width: auto;"></button> <button id="themeToggle" class="theme-toggle-button" onclick="toggleTheme()"> <span id="themeIcon">🌙</span> </button> </div> </div> <div class="input-section"> <h3 data-translate-key="getPlayerData">📊 获取玩家数据</h3> <div class="input-group"> <input type="text" id="playerName" data-translate-key="playerNamePlaceholder" placeholder="输入玩家名称或ID" value="LemonApostle"> <button onclick="fetchPlayerData()" data-translate-key="fetchData">获取数据</button> </div> <div id="message"></div> </div> <div id="playerData" style="display:none;"> <div class="player-info"> <div class="info-card"><h3 data-translate-key="basicInfo">基本信息</h3><div id="basicInfo"></div></div> <div class="info-card"><h3 data-translate-key="gatheringBoosts">采集加成</h3><div id="gatheringBoosts"></div></div> <div class="info-card"><h3 data-translate-key="codexBoosts">法典加成</h3><div id="codexBoosts"></div></div> <div class="info-card"><h3 data-translate-key="otherEquipmentBoosts">其他装备加成</h3><div id="otherEquipmentBoosts"></div></div> <div class="info-card"><h3 data-translate-key="farm">农场</h3><div id="farmBoosts"></div></div> <div class="info-card"><h3 data-translate-key="marketPrice">市场价格</h3><div id="marketPrice"></div></div> <div class="info-card"><h3 data-translate-key="needToSupplement">需要自己补充的内容</h3><div id="needToSupplement"></div></div> </div> </div> <div class="calculator-section"> <h3 data-translate-key="dataCalculator">🧮 数据计算器</h3> <div class="usage-note"> <strong data-translate-key="usageNoteTitle">使用说明:</strong> <span data-translate-key="usageNote">填完必要的数据后,先点击"更新数据"按钮,再点击其他计算按钮。</span> </div> <div class="calc-input"> <label data-translate-key="getAllCurrentValues">获取当前所有数据:</label> <button class="calc-button" onclick="getAllCurrentValues()" data-translate-key="updateData">更新数据</button> </div> <div class="calc-input"> <label data-translate-key="calculateCombat">战斗计算:</label> <button class="calc-button" onclick="calculateCombatEfficiency()" data-translate-key="calculateCombatEfficiency">计算战斗效率</button> </div> <div class="calc-input"> <label data-translate-key="calculateGathering">采集效率计算:</label> <div class="input-controls"> <select id="gatheringType"> <option value="mining" data-translate-key="miningOption">挖矿</option> <option value="fishing" data-translate-key="fishingOption">钓鱼</option> <option value="woodcutting" data-translate-key="woodcuttingOption">伐木</option> </select> <button class="calc-button" onclick="calculateGatheringEfficiency()" data-translate-key="calculateGatheringEfficiency">计算采集效率</button> </div> </div> <div class="calc-input"> <label data-translate-key="calculateResonance">回响药水最佳等级计算:</label> <button class="calc-button" onclick="calculateResonancePotionLevel()" data-translate-key="calculateLevel">计算等级</button> </div> <div class="calc-input"> <label data-translate-key="calculateROI">采集投资回报率计算:</label> <div class="input-controls"> <select id="gatheringType2"> <option value="mining" data-translate-key="miningOption">挖矿</option> <option value="fishing" data-translate-key="fishingOption">钓鱼</option> <option value="woodcutting" data-translate-key="woodcuttingOption">伐木</option> </select> <button class="calc-button" onclick="calculateGatheringROI()" data-translate-key="calculateGatheringROI">计算采集投资回报率</button> </div> </div> <div class="calc-input"> <label data-translate-key="calculateGatherEquip">采集装备替换检测:</label> <button class="calc-button" onclick="calculateGatherEquipReplace()" data-translate-key="detectGatherEquip">检测采集装备替换</button> </div> <div class="calc-input"> <label data-translate-key="calculateBattleEquip">战斗装备替换检测:</label> <button class="calc-button" onclick="calculateBattlerEquipReplace()" data-translate-key="detectBattleEquip">检测战斗装备替换</button> </div> <div id="calcResults"></div> </div> </div> <script> let values = null; let globalPlayerData = null; let globalMarketData = null; let currentLanguage = localStorage.getItem('manarion-language') || 'zh'; let currentTheme = localStorage.getItem('manarion-theme') || 'light'; const translations = { en: { // General UI langToggle: "中/EN", fetchData: "Get Data", updateData: "Update Data", playerNamePlaceholder: "Enter player name or ID", getPlayerData: "📊 Get Player Data", dataCalculator: "🧮 Data Calculator", usageNoteTitle: "Instructions:", usageNote: "After filling in necessary data, click 'Update Data' first, then other calculation buttons.", mainTitle: "🏰 Manarion Data Calculator", // Titles basicInfo: "Basic Info", gatheringBoosts: "Gathering Boosts", codexBoosts: "Codex Boosts", otherEquipmentBoosts: "Other Equipment Boosts", farm: "Farm", marketPrice: "Market Price", needToSupplement: "Manual Input", // Basic Info Labels playerName: "Player Name:", currentEnemy: "Current Enemy:", battleLevel: "Battle Level:", miningLevel: "Mining Level:", fishingLevel: "Fishing Level:", woodcuttingLevel: "Woodcutting Level:", // Gathering Boosts Labels mining: "Mining:", fishing: "Fishing:", woodcutting: "Woodcutting:", baseResourceBoost: "Base Resource Amount:", // Codex Boosts Labels codexManaDust: "Base Mana Dust:", codexBaseResource: "Base Resource:", codexDropBoost: "Drop Boost:", // Other Equipment Boosts Labels manaBoost: "Mana Dust:", shardBoost: "Elemental Shards:", potionBoost: "Potion Boost:", // Farm Labels harvestGolem: "Harvest Golem:", fertilizer: "Fertilizer:", farmPlots: "Farm Plots:", farmHerbHarvest: "Herb Harvest:", farmTax: "Tax:", // Market Price Labels shardPrice: "Elemental Shards:", codexPrice: "Codex:", fishPrice: "Fish:", woodPrice: "Wood:", ironPrice: "Iron:", sagerootPrice: "Sageroot:", bloomwellPrice: "Bloomwell:", // Manual Input Labels guildLevel: "Guild Level:", guildNexusCrystalLevel: "Nexus Crystal Level:", guildManaTax: "Guild Mana Tax:", guildResourceTax: "Guild Resource Tax:", guildShardTax: "Guild Shard Tax:", wisdomPotionLevel: "Wisdom Potion Level:", harvestingPotionLevel: "Harvesting Potion Level:", resonancePotionLevel: "Resonance Potion Level:", // Calculator Buttons getAllCurrentValues: "Get current data:", calculateCombat: "Combat Calculation:", calculateCombatEfficiency: "Calculate Combat Efficiency", calculateGathering: "Gathering Efficiency:", calculateGatheringEfficiency: "Calculate Gathering Efficiency", calculateResonance: "Optimal Resonance Potion Level:", calculateLevel: "Calculate Level", calculateROI: "Gathering ROI:", calculateGatheringROI: "Calculate Gathering ROI", calculateGatherEquip: "Gathering Equipment Replacement:", detectGatherEquip: "Detect Gather Equip Replace", calculateBattleEquip: "Battle Equipment Replacement:", detectBattleEquip: "Detect Battle Equip Replace", // Select Options miningOption: "Mining", fishingOption: "Fishing", woodcuttingOption: "Woodcutting", // Messages enterPlayerNameError: "Please enter a player name", fetchingData: "Fetching data...", fetchDataError: "Failed to fetch data. Possible reasons: 1. API is temporarily unavailable 2. Player name does not exist 3. Network connection issue", fetchSuccess: "Data fetched successfully!", showExampleData: "Showing example data", loadSavedData: "Loading saved data for user: ", // Calculation Results currentDataSnapshot: "Current data snapshot. If there are large discrepancies, please check if the content here is correct:", basicInfoTitle: "Basic Info:", skillLevels: "Skill Levels: Mining \${miningLevel} Fishing \${fishingLevel} Woodcutting \${woodcuttingLevel}", gatheringBoostsTitle: "Gathering Boosts:", baseResourceAmount: "Base Resource Amount: \${baseResourceAmountTotal}", codexBoostsTitle: "Codex Boosts:", equipmentBoostsTitle: "Equipment Boosts:", farmSystemTitle: "Farm System:", herbHarvest: "Herb Harvest: \${farmHerbHarvest}/h, Tax: \${farmTax}/h", marketPricesTitle: "Market Prices:", manualInputTitle: "Manual Input:", guildTaxRates: "Guild Mana Tax: \${guildManaTax}%, Guild Resource Tax: \${guildResonanceTax}%, Guild Shard Tax: \${guildShardTax}%", potionLevels: "Wisdom Potion Level: \${wisdomPotionLevel}, Harvesting Potion Level: \${harvestingPotionLevel}", manaPerDay: "Mana Dust per day", shardRangePerAction: "Shards per action (after tax)", shardsPerDay: "Shards per day", shardsDailyValue: "Shards daily value", sagerootHourlyProduction: "Sageroot hourly production", bloomwellHourlyProduction: "Bloomwell hourly production", sagerootHourlyConsumption: "Sageroot hourly consumption", bloomwellHourlyConsumption: "Bloomwell hourly consumption", sagerootNetProduction: "Sageroot net hourly production", bloomwellNetProduction: "Bloomwell net hourly production", sagerootDailyProfit: "Sageroot daily net profit", bloomwellDailyProfit: "Bloomwell daily net profit", farmDailyMaintenance: "Farm daily maintenance cost", farmDailyValue: "Farm daily value", dailyIncome: "Daily income", resourcePerAction: "Resource per action:", resourcePerDay: "Resource per day:", resourcePrice: "Resource price:", resourceDailyValue: "Resource daily value:", optimalResonanceLevel: "Optimal Resonance Potion Level:", comparedToLevel0: "Compared to level 0 potion", extraFarmConsumption: "Extra farm consumption:", extraIncome: "Extra income:", increasedIncome: "Increased income:", gatheringROIComparison: "\${gatheringType} Investment ROI Comparison:", cost: "Cost:", extraDailyProfit: "Extra Daily Profit:", investments: { research: "Research Boost 3000 levels", baseResource: "Base Resource 100 levels", codexBase: "Codex Base Resource 5 levels", potion: "Potion Boost 5 levels", codexDrop: "Codex Drop Rate 5 levels", farm: "Farm total 300 levels" }, gatherEquipReplaceTitle: "Gathering Equipment Replacement", gatherEquipReplaceDesc: "Check current equipment and harvesting potion level to see if any base resource gear can be replaced with elemental shard gear (calculates for the lowest value item).", noBaseResourceEquip: "No base resource equipment found, ending check.", currentPotionLevel: "Current potion level:", newPotionLevel: "New potion level after replacement:", canReplaceGatherEquip: "Current base resource equipment: \${name} can be replaced with elemental shard equipment.", baseResourceAmountLost: "Base resource amount from equipment: \${amount}, can be replaced with elemental shard boost \${shardBoost}%", manaIncreaseAfterReplace: "Daily mana dust increase after replacement", farmCostIncreaseAfterReplace: "Daily farm cost increase after replacement", incomeIncreaseAfterReplace: "Daily income increase after replacement", cannotReplaceGatherEquip: "It seems replacement is not beneficial with the current potion level and gear. Ending check.", battleEquipReplaceTitle: "Battle Equipment Replacement", battleEquipReplaceDesc: "Check current equipment and wisdom potion level to see if any experience gear can be replaced with mana dust/elemental shard gear (calculates for the lowest value item).", noExpEquip: "No experience boost equipment found, ending check.", canReplaceBattleEquipToMana: "Current experience equipment: \${name} can be replaced with mana dust equipment.", canReplaceBattleEquipToShard: "Current experience equipment: \${name} can be replaced with elemental shard equipment.", cannotReplaceBattleEquip: "It seems replacement is not beneficial with the current potion level and gear. Ending check." }, zh: { // General UI langToggle: "中/EN", fetchData: "获取数据", updateData: "更新数据", playerNamePlaceholder: "输入玩家名称或ID", getPlayerData: "📊 获取玩家数据", dataCalculator: "🧮 数据计算器", usageNoteTitle: "使用说明:", usageNote: "填完必要的数据后,先点击“更新数据”按钮,再点击其他计算按钮。", mainTitle: "🏰 Manarion 数据计算器", // Titles basicInfo: "基本信息", gatheringBoosts: "采集加成", codexBoosts: "法典加成", otherEquipmentBoosts: "其他装备加成", farm: "农场", marketPrice: "市场价格", needToSupplement: "需要自己补充的内容", // Basic Info Labels playerName: "玩家名:", currentEnemy: "当前敌人:", battleLevel: "战斗等级:", miningLevel: "挖矿等级:", fishingLevel: "钓鱼等级:", woodcuttingLevel: "伐木等级:", // Gathering Boosts Labels mining: "挖矿:", fishing: "钓鱼:", woodcutting: "伐木:", baseResourceBoost: "基础资源量:", // Codex Boosts Labels codexManaDust: "魔法尘:", codexBaseResource: "基础资源量:", codexDropBoost: "掉落加成:", // Other Equipment Boosts Labels manaBoost: "魔法尘:", shardBoost: "元素碎片:", potionBoost: "药水效果:", // Farm Labels harvestGolem: "收获傀儡:", fertilizer: "肥料:", farmPlots: "地块:", farmHerbHarvest: "草药收获:", farmTax: "税收:", // Market Price Labels shardPrice: "元素碎片:", codexPrice: "法典:", fishPrice: "鱼:", woodPrice: "木:", ironPrice: "铁:", sagerootPrice: "智慧之根:", bloomwellPrice: "繁茂精华:", // Manual Input Labels guildLevel: "工会等级:", guildNexusCrystalLevel: "连结水晶等级:", guildManaTax: "工会魔法尘税率:", guildResourceTax: "工会资源税率:", guildShardTax: "工会碎片税率:", wisdomPotionLevel: "智慧药水等级:", harvestingPotionLevel: "收获药水等级:", resonancePotionLevel: "回响药水等级:", // Calculator Buttons getAllCurrentValues: "获取当前所有数据:", calculateCombat: "战斗计算:", calculateCombatEfficiency: "计算战斗效率", calculateGathering: "采集效率计算:", calculateGatheringEfficiency: "计算采集效率", calculateResonance: "回响药水最佳等级计算:", calculateLevel: "计算等级", calculateROI: "采集投资回报率计算:", calculateGatheringROI: "计算采集投资回报率", calculateGatherEquip: "采集装备替换检测:", detectGatherEquip: "检测采集装备替换", calculateBattleEquip: "战斗装备替换检测:", detectBattleEquip: "检测战斗装备替换", // Select Options miningOption: "挖矿", fishingOption: "钓鱼", woodcuttingOption: "伐木", // Messages enterPlayerNameError: "请输入玩家名称", fetchingData: "正在获取数据...", fetchDataError: "无法获取数据。可能原因:1. API暂时不可用 2. 玩家名称不存在 3. 网络连接问题", fetchSuccess: "数据获取成功!", showExampleData: "显示示例数据", loadSavedData: "加载已保存的数据 用户名: ", // Calculation Results currentDataSnapshot: "当前数据快照, 如有大出入,请检查这里的内容是否正确:", basicInfoTitle: "基础信息:", skillLevels: "技能等级: 挖矿\${miningLevel} 钓鱼\${fishingLevel} 伐木\${woodcuttingLevel}", gatheringBoostsTitle: "采集加成:", baseResourceAmount: "基础资源量: \${baseResourceAmountTotal}", codexBoostsTitle: "法典加成:", equipmentBoostsTitle: "装备加成:", farmSystemTitle: "农场系统:", herbHarvest: "草药收获: \${farmHerbHarvest}/h, 税收: \${farmTax}/h", marketPricesTitle: "市场价格:", manualInputTitle: "手动输入信息:", guildTaxRates: "工会魔法尘税率: \${guildManaTax}%, 工会资源税率: \${guildResonanceTax}%, 工会碎片税率: \${guildShardTax}%", potionLevels: "智慧药水等级: \${wisdomPotionLevel}, 收获药水等级: \${harvestingPotionLevel}", manaPerDay: "魔法尘每天", shardRangePerAction: "税后每动作碎片范围", shardsPerDay: "碎片每天", shardsDailyValue: "碎片每天价值", sagerootHourlyProduction: "慧根每小时产出", bloomwellHourlyProduction: "繁茂每小时产出", sagerootHourlyConsumption: "慧根每小时消耗", bloomwellHourlyConsumption: "繁茂每小时消耗", sagerootNetProduction: "慧根每小时净产出", bloomwellNetProduction: "繁茂每小时净产出", sagerootDailyProfit: "慧根每日净利", bloomwellDailyProfit: "繁茂每日净利", farmDailyMaintenance: "农场每日维护费", farmDailyValue: "农场每天价值", dailyIncome: "每日收入", resourcePerAction: "资源每动作:", resourcePerDay: "资源每天:", resourcePrice: "资源价格:", resourceDailyValue: "资源每天价值:", optimalResonanceLevel: "最佳的碎片药水等级:", comparedToLevel0: "相对0级药水", extraFarmConsumption: "额外的农场消耗量:", extraIncome: "额外的收益:", increasedIncome: "提升的收益:", gatheringROIComparison: "\${gatheringType} 投资回报率对比:", cost: "成本:", extraDailyProfit: "额外日收益:", investments: { research: "研究加成3000级", baseResource: "基础资源100级", codexBase: "法典基础5级", potion: "药水加成5级", codexDrop: "法典掉落5级", farm: "农场总共提升300级" }, gatherEquipReplaceTitle: "采集装备替换说明", gatherEquipReplaceDesc: "检查当前身上的装备,目前使用的收获药水等级,计算是否有基础资源装备可以被替换为元素碎片装备(每次只会计算最小数值的装备)", noBaseResourceEquip: "当前没有找到基础资源加成装备,结束", currentPotionLevel: "当前使用的药水等级:", newPotionLevel: "替换后使用的药水等级:", canReplaceGatherEquip: "当前基础资源加成装备:\${name} 可以被替换为 elemental shard 装备", baseResourceAmountLost: "当前基础资源加成装备基础资源量: \${amount} 可以被替换为元素碎片加成 \${shardBoost}%", manaIncreaseAfterReplace: "替换后每日魔法尘增加", farmCostIncreaseAfterReplace: "替换后每日农场支出增加", incomeIncreaseAfterReplace: "替换后每日收入增加", cannotReplaceGatherEquip: "看起来现在的药水等级+基础资源装情况,替换不了呢。结束", battleEquipReplaceTitle: "战斗装备替换说明", battleEquipReplaceDesc: "检查当前身上的装备,目前使用的智慧药水等级,计算是否有经验装备可以被替换为魔法尘/元素碎片装备(每次只会计算最小数值的装备)", noExpEquip: "当前没有找到经验加成装备,结束", canReplaceBattleEquipToMana: "当前经验加成装备:\${name} 可以被替换为 mana dust 装备", canReplaceBattleEquipToShard: "当前经验加成装备:\${name} 可以被替换为 elemental shard 装备", cannotReplaceBattleEquip: "看起来现在的药水等级+经验装情况,替换不了呢。结束" } }; function setLanguage(lang) { currentLanguage = lang; localStorage.setItem('manarion-language', lang); updateUIForLanguage(); // Re-render data if it exists if (globalPlayerData) { displayData(globalPlayerData, globalMarketData); } if (document.getElementById('calcResults').innerHTML.trim() !== '') { getAllCurrentValues(); } } function toggleLanguage() { const newLang = currentLanguage === 'zh' ? 'en' : 'zh'; setLanguage(newLang); } //#region UI language function updateUIForLanguage() { const t = translations[currentLanguage]; document.querySelectorAll('[data-translate-key]').forEach(element => { const key = element.dataset.translateKey; // 确保key存在于翻译对象中 if (t && t[key]) { const translation = t[key]; // 根据元素类型设置不同的属性 if (element.tagName === 'INPUT') { if(element.type === 'button' || element.type === 'submit' || element.type === 'reset') { element.value = translation; } else { element.placeholder = translation; } } else if (element.tagName === 'OPTION') { element.textContent = translation; } else if (element.tagName === 'TITLE') { element.textContent = translation; } else { // 适用于 <h1>, <button>, <span>, <strong>, <label>, <h3> 等 element.innerHTML = translation; } } }); } //#region Theme function setTheme(theme) { localStorage.setItem('manarion-theme', theme); currentTheme = theme; const themeIcon = document.getElementById('themeIcon'); if (theme === 'dark') { document.body.classList.add('dark'); if (themeIcon) themeIcon.innerHTML = '☀️'; // Sun icon for switching to light mode } else { document.body.classList.remove('dark'); if (themeIcon) themeIcon.innerHTML = '🌙'; // Moon icon for switching to dark mode } } function toggleTheme() { const newTheme = currentTheme === 'light' ? 'dark' : 'light'; setTheme(newTheme); } //#region global data function loadGlobalData() { try { const savedValues = localStorage.getItem('manarion-values'); const savedPlayerData = localStorage.getItem('manarion-player-data'); const savedMarketData = localStorage.getItem('manarion-market-data'); if (savedValues) { values = JSON.parse(savedValues); console.log('✅ manarion_helper_values LOAD'); if (values && values.ui) { const gatheringType1 = document.getElementById('gatheringType'); const gatheringType2 = document.getElementById('gatheringType2'); if (gatheringType1 && values.ui.lastGatheringType) { gatheringType1.value = values.ui.lastGatheringType; } if (gatheringType2 && values.ui.lastGatheringType2) { gatheringType2.value = values.ui.lastGatheringType2; } } } if (savedPlayerData) { globalPlayerData = JSON.parse(savedPlayerData); const playerNameInput = document.getElementById('playerName'); if (playerNameInput && globalPlayerData.Name) { playerNameInput.value = globalPlayerData.Name; } console.log('✅ manarion_helper_player_data LOAD'); } if (savedMarketData) { globalMarketData = JSON.parse(savedMarketData); console.log('✅ manarion_helper_market_data LOAD'); } } catch (error) { console.log('❌ manarion_helper_load_data ERROR'); } } function saveGlobalData() { try { if (values !== null) { localStorage.setItem('manarion-values', JSON.stringify(values)); } if (globalPlayerData !== null) { localStorage.setItem('manarion-player-data', JSON.stringify(globalPlayerData)); } if (globalMarketData !== null) { localStorage.setItem('manarion-market-data', JSON.stringify(globalMarketData)); } console.log('💾 marion_helper_save_data SUCCESS'); } catch (error) { console.log('❌ manarion_helper_save_data ERROR'); } } function removeGlobalData() { try { localStorage.removeItem('manarion-values'); localStorage.removeItem('manarion-player-data'); localStorage.removeItem('manarion-market-data'); globalMarketData = null; globalPlayerData = null; values = null; console.log('💾 manarion_helper_remove_data SUCCESS'); } catch (error) { console.log('❌ manarion_helper_remove_data ERROR'); } } async function fetchPlayerData() { const playerName = document.getElementById('playerName').value.trim(); let data = null, marketData = null; if (!playerName) { showMessage(translations[currentLanguage].enterPlayerNameError, 'error'); return; } showMessage(translations[currentLanguage].fetchingData, 'loading'); try { const playerUrl = \`https://api.manarion.com/players/\${encodeURIComponent(playerName)}\`; data = await window.fetchDataWithGM(playerUrl); } catch (error) { console.error('获取数据失败:', error); showExampleData(); showMessage(translations[currentLanguage].fetchDataError, 'error'); return; } marketData = await fetchMarketData(); showMessage(translations[currentLanguage].fetchSuccess, 'success'); globalPlayerData = data; displayData(data, marketData); saveGlobalData(); } async function fetchMarketData() { try { const marketUrl = 'https://api.manarion.com/market'; const marketData = await window.fetchDataWithGM(marketUrl); globalMarketData = marketData; return marketData; } catch (error) { console.warn('获取市场价格失败:', error); return null; } } // region example data function showExampleData() { const examplePlayerData = {"ID":2582,"Banned":false,"Title":"Scholar","TitleColor":"#b1d852","Name":"LemonApostle","ProfileText":"","Level":2269,"Zone":"blazing_core","Enemy":58000,"MagicType":"water","ActionType":"battle","MiningLevel":636,"FishingLevel":2442,"WoodcuttingLevel":701,"GatherActions":1599128,"Kills":241570,"Deaths":821,"EventActions":138637,"EventPoints":10867920.089814264,"BattleQuestNumber":415,"GatherQuestNumber":903,"BaseBoosts":{"1":1600,"100":100,"101":17,"102":13,"103":16,"105":0,"106":0,"108":85,"11":724583,"124":1800,"130":2600,"131":2600,"132":2600,"133":17,"140":416,"141":27,"142":2,"143":0,"144":0,"145":0,"146":0,"2":1800,"22":13,"23":6,"24":11,"3":19047,"30":1100,"31":69562,"32":500,"4":19112,"40":36000,"41":26000,"42":36000,"43":36000,"44":26000,"45":36000,"46":40000,"47":36000,"48":31000,"49":22000,"5":19194,"50":36000,"6":19246,"69":1,"7":18966,"70":1},"TotalBoosts":{"1":1600,"10":0,"100":299,"101":56,"102":52,"103":55,"105":0,"106":0,"107":0,"108":231,"11":763724,"12":0,"120":4683,"121":1398,"122":0,"123":0,"124":1800,"130":2600,"131":2600,"132":2600,"133":17,"140":416,"141":27,"142":2,"143":0,"144":0,"145":0,"146":0,"2":1800,"21":0,"22":13,"23":6,"24":11,"3":118166.33,"30":1.6534,"31":42.319828,"32":1.297,"4":101749.68000000001,"40":46.6062,"41":25.868000000000002,"42":29.2158,"43":36.0982,"44":16.444000000000003,"45":29.253,"46":24.76,"47":41.803,"48":19.414,"49":31.699399999999997,"5":96850.66,"50":46.2424,"6":101426.94,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":1,"7":84603.74,"70":1,"71":0,"8":2666813,"80":0,"81":39,"82":0,"83":38,"84":0,"85":0,"86":39,"9":2535942},"SigilBoost":108,"GuildID":86,"LastFatigue":1753980262,"UnfatiguedStreak":492529,"HighestEnemy":58000,"Equipment":{"1":{"ID":5025890,"Name":"Heirloom Staff of Water +16 (3046)","IsEquipped":true,"Level":3046,"Rarity":6,"Slot":1,"Boosts":{"11":21745,"120":562,"3":11089,"40":27909,"41":26178,"5":12022,"6":10837,"8":770006,"81":39},"Quality":0.99403,"UpgradeChance":0.04681,"Infusions":16},"2":{"ID":4951400,"Name":"Heirloom Robes +14 (2492)","IsEquipped":true,"Level":2492,"Rarity":6,"Slot":2,"Boosts":{"101":39,"121":466,"4":8821,"49":22310,"50":22010,"6":8642,"7":9083,"9":108884},"Quality":0.99292,"UpgradeChance":0.04852,"Infusions":14},"3":{"ID":4969542,"Name":"Heirloom Sandals +14 (2163)","IsEquipped":true,"Level":2163,"Rarity":6,"Slot":3,"Boosts":{"103":39,"120":401,"3":7623,"40":18591,"43":18751,"6":8244,"7":9253,"9":85431},"Quality":0.99324,"UpgradeChance":0.04643,"Infusions":14},"4":{"ID":4940806,"Name":"Heirloom Gloves +14 (2149)","IsEquipped":true,"Level":2149,"Rarity":6,"Slot":4,"Boosts":{"102":39,"121":400,"3":7535,"4":7931,"42":19947,"50":18620,"6":7661,"9":90438},"Quality":0.99402,"UpgradeChance":0.04116,"Infusions":14},"5":{"ID":4937498,"Name":"Heirloom Hood +15 (2167)","IsEquipped":true,"Level":2167,"Rarity":6,"Slot":5,"Boosts":{"100":39,"120":388,"3":7516,"43":19540,"47":18825,"5":8153,"6":7681,"9":88907},"Quality":0.99269,"UpgradeChance":0.0501,"Infusions":15},"6":{"ID":5030783,"Name":"Heirloom Cloak +16 (3057)","IsEquipped":true,"Level":3057,"Rarity":6,"Slot":6,"Boosts":{"120":547,"3":10676,"4":10889,"49":27906,"5":10830,"50":27901,"83":38,"9":152982},"Quality":0.99299,"UpgradeChance":0.048,"Infusions":16},"7":{"ID":4954354,"Name":"Heirloom Pendant +14 (2181)","IsEquipped":true,"Level":2181,"Rarity":6,"Slot":7,"Boosts":{"120":418,"3":7997,"4":8307,"45":20203,"47":18226,"7":8001},"Quality":0.99343,"UpgradeChance":0.03895,"Infusions":14},"8":{"ID":4921970,"Name":"Heirloom Ring +14 (2169)","IsEquipped":true,"Level":2169,"Rarity":6,"Slot":8,"Boosts":{"120":364,"4":7639,"40":23101,"47":19511,"5":8689,"7":7923,"86":39},"Quality":0.99264,"UpgradeChance":0.04351,"Infusions":14}}}; const exampleMarketData = {"Buy":{"10":5100000,"11":4050000,"12":2250000,"13":40410105,"14":33000000,"15":39700001,"16":1902328816,"17":12000000000,"18":6060000009,"19":1402439508,"2":1091,"20":13072500002,"21":13261300010,"22":1800000000,"23":2049999999,"24":1040300000,"25":502000000,"26":2750000000,"27":431000000,"28":800000000,"29":6250000,"3":16000000105,"30":2400006,"31":3300000,"32":7777778,"33":30500009,"34":11250000,"35":6920016671,"36":17220500000,"37":40150000000,"39":25000009,"4":11000009,"40":980000,"41":999500,"44":27500000015,"45":313000000000,"46":796001001,"47":7885373010,"5":12625009,"50":4500000000000,"6":8010009,"7":407,"8":377,"9":387},"Sell":{"10":15000000,"11":37000000,"12":17641798,"13":41550000,"14":39589994,"15":42000000,"16":2000000000,"17":14199000000,"18":25000000000,"19":4257000000,"2":1232,"20":1000000000000,"21":99000000000,"22":4514801227,"23":2305430421,"24":1900000000,"25":790000000,"26":7920000000,"27":633977685,"28":1300000000,"29":21000000,"3":16416671000,"30":10500000,"31":14900000,"32":41250000,"33":74979999,"34":35000000,"35":8035830000,"36":19999999990,"37":57247631000,"39":67320000,"4":69000000,"40":1127114,"41":1000000,"44":38999999999,"45":361000000000,"46":835000000,"47":8612000000,"5":60000000,"50":21900000000000,"6":66000000,"7":503,"8":409,"9":402}}; let displayExampleData = false; if(globalPlayerData == null){ globalPlayerData = examplePlayerData; displayExampleData = true; } if(globalMarketData == null){ globalMarketData = exampleMarketData; displayExampleData = true; } displayData(globalPlayerData, globalMarketData); const t = translations[currentLanguage]; if(displayExampleData){ showMessage(t.showExampleData, 'success'); } else { showMessage(t.loadSavedData + globalPlayerData.Name, 'success'); } } function showMessage(message, type) { const messageDiv = document.getElementById('message'); messageDiv.innerHTML = \`<div class="\${type}">\${message}</div>\`; } // region show data function displayData(playerData, marketData) { document.getElementById('playerData').style.display = 'block'; const t = translations[currentLanguage]; document.getElementById('basicInfo').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.playerName}</span> <span class="stat-value">\${playerData.Name}</span></div> <div class="stat-row"><span class="stat-label">\${t.currentEnemy}</span> <input type="number" class="stat-input" value="\${playerData.Enemy || 0}" id="currentEnemy"></div> <div class="stat-row"><span class="stat-label">\${t.battleLevel}</span> <input type="number" class="stat-input" value="\${playerData.Level || 0}" id="battleLevel"></div> <div class="stat-row"><span class="stat-label">\${t.miningLevel}</span> <input type="number" class="stat-input" value="\${playerData.MiningLevel || 0}" id="miningLevel"></div> <div class="stat-row"><span class="stat-label">\${t.fishingLevel}</span> <input type="number" class="stat-input" value="\${playerData.FishingLevel || 0}" id="fishingLevel"></div> <div class="stat-row"><span class="stat-label">\${t.woodcuttingLevel}</span> <input type="number" class="stat-input" value="\${playerData.WoodcuttingLevel || 0}" id="woodcuttingLevel"></div> \`; document.getElementById('gatheringBoosts').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.mining}</span> <input type="number" class="stat-input" value="\${((playerData.TotalBoosts['30'] - 1 || 0) * 100).toFixed(2)}" id="miningBoost"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.fishing}</span> <input type="number" class="stat-input" value="\${((playerData.TotalBoosts['31'] - 1 || 0) * 100).toFixed(2)}" id="fishingBoost"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.woodcutting}</span> <input type="number" class="stat-input" value="\${((playerData.TotalBoosts['32'] - 1 || 0) * 100).toFixed(2)}" id="woodcuttingBoost"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.baseResourceBoost}</span> <input type="number" class="stat-input" value="\${((playerData.TotalBoosts['124'] || 0) /100).toFixed(2)}" id="baseResourceBoost"></div> \`; document.getElementById('codexBoosts').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.codexManaDust}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['101'] || 0}" id="codexManaDust"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.codexBaseResource}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['106'] || 0}" id="codexBaseResource"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.codexDropBoost}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['102'] || 0}" id="codexDropBoost"><span style="margin-left: 4px;">%</span></div> \`; document.getElementById('otherEquipmentBoosts').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.manaBoost}</span> <input type="number" class="stat-input" value="\${(playerData.TotalBoosts['121'] || 0)}" id="manaBoost"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.shardBoost}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['122'] || 0}" id="shardBoost"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.potionBoost}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['108'] || 0}" id="potionBoost"><span style="margin-left: 4px;">%</span></div> \`; document.getElementById('farmBoosts').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.harvestGolem}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['130'] || 0}" id="harvestGolem"></div> <div class="stat-row"><span class="stat-label">\${t.fertilizer}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['131'] || 0}" id="fertilizer"></div> <div class="stat-row"><span class="stat-label">\${t.farmPlots}</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['132'] || 0}" id="farmPlots"></div> <div class="stat-row"><span class="stat-label">\${t.farmHerbHarvest}</span> <input type="number" class="stat-input" value="\${(2.5 * (1 + playerData.TotalBoosts['130']/100)**0.9 * (1 + playerData.TotalBoosts['131']/100)**0.9 * (1 + playerData.TotalBoosts['132']/100)**0.9).toFixed(0) || 0}" id="farmHerbHarvest"><span style="margin-left: 4px;">/h</span></div> <div class="stat-row"><span class="stat-label">\${t.farmTax}</span> <input type="number" class="stat-input" value="\${(2.5 * (1 + playerData.TotalBoosts['130']/100)**0.9 * (1 + playerData.TotalBoosts['131']/100)**0.9 * (1 + playerData.TotalBoosts['132']/100)**0.9*200000).toFixed(0) || 0}" id="farmTax"><span style="margin-left: 4px;">/h</span></div> \`; document.getElementById('marketPrice').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.shardPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['2'] || 0}" id="shardPrice"></div> <div class="stat-row"><span class="stat-label">\${t.codexPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['3'] || 0}" id="codexPrice"></div> <div class="stat-row"><span class="stat-label">\${t.fishPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['7'] || 0}" id="fishPrice"></div> <div class="stat-row"><span class="stat-label">\${t.woodPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['8'] || 0}" id="woodPrice"></div> <div class="stat-row"><span class="stat-label">\${t.ironPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['9'] || 0}" id="ironPrice"></div> <div class="stat-row"><span class="stat-label">\${t.sagerootPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['40'] || 0}" id="sagerootPrice"></div> <div class="stat-row"><span class="stat-label">\${t.bloomwellPrice}</span> <input type="number" class="stat-input" value="\${marketData.Sell['41'] || 0}" id="bloomwellPrice"></div> \`; document.getElementById('needToSupplement').innerHTML = \` <div class="stat-row"><span class="stat-label">\${t.guildLevel}</span> <input type="number" class="stat-input" value="\${values?.guild?.guildLevel || 100}" id="guildLevel"></div> <div class="stat-row"><span class="stat-label">\${t.guildNexusCrystalLevel}</span> <input type="number" class="stat-input" value="\${values?.guild?.guildNexusCrystalLevel || 100}" id="guildNexusCrystalLevel"></div> <div class="stat-row"><span class="stat-label">\${t.guildManaTax}</span> <input type="number" class="stat-input" value="\${values?.guild?.guildManaTax || 10}" id="guildManaTax"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.guildResourceTax}</span> <input type="number" class="stat-input" value="\${values?.guild?.guildResonanceTax || 60}" id="guildResourceTax"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.guildShardTax}</span> <input type="number" class="stat-input" value="\${values?.guild?.guildShardTax || 10}" id="guildShardTax"><span style="margin-left: 4px;">%</span></div> <div class="stat-row"><span class="stat-label">\${t.wisdomPotionLevel}</span> <input type="number" class="stat-input" value="\${values?.guild?.wisdomPotionLevel || 10}" id="wisdomPotionLevel"></div> <div class="stat-row"><span class="stat-label">\${t.harvestingPotionLevel}</span> <input type="number" class="stat-input" value="\${values?.guild?.harvestingPotionLevel || 10}" id="harvestingPotionLevel"></div> <div class="stat-row"><span class="stat-label">\${t.resonancePotionLevel}</span> <input type="number" class="stat-input" value="\${values?.guild?.resonancePotionLevel || 10}" id="resonancePotionLevel"></div> \`; } //#region getvalue function getAllCurrentValues() { values = { basic: { battleLevel: getValue('battleLevel'), currentEnemy: getValue('currentEnemy'), miningLevel: getValue('miningLevel'), fishingLevel: getValue('fishingLevel'), woodcuttingLevel: getValue('woodcuttingLevel') }, gathering: { miningBoost: getValue('miningBoost'), fishingBoost: getValue('fishingBoost'), woodcuttingBoost: getValue('woodcuttingBoost'), miningBaseBoost: globalPlayerData.BaseBoosts['30'], fishingBaseBoost: globalPlayerData.BaseBoosts['31'], woodcuttingBaseBoost: globalPlayerData.BaseBoosts['32'], baseResourceAmountTotal: getValue('baseResourceBoost'), baseResourceAmountBase: globalPlayerData.BaseBoosts['124'] }, codex: { codexManaDust: getValue('codexManaDust'), codexBaseResource: getValue('codexBaseResource'), codexDropBoost: getValue('codexDropBoost'), codexBaseResourceBase: globalPlayerData.BaseBoosts['106'], codexDropBoostBase: globalPlayerData.BaseBoosts['102'] }, equipment: { manaBoost: getValue('manaBoost'), shardBoost: getValue('shardBoost'), potionBoost: getValue('potionBoost'), potionBoostBase: globalPlayerData.BaseBoosts['108'] }, farm: { harvestGolem: getValue('harvestGolem'), fertilizer: getValue('fertilizer'), farmPlots: getValue('farmPlots'), farmHerbHarvest: getValue('farmHerbHarvest'), farmTax: getValue('farmTax') }, market: { codexPrice: getValue('codexPrice'), fishPrice: getValue('fishPrice'), woodPrice: getValue('woodPrice'), ironPrice: getValue('ironPrice'), shardPrice: getValue('shardPrice'), sagerootPrice: getValue('sagerootPrice'), bloomwellPrice: getValue('bloomwellPrice') }, guild: { guildLevel: getValue('guildLevel'), guildNexusCrystalLevel: getValue('guildNexusCrystalLevel'), guildManaTax: getValue('guildManaTax'), guildResonanceTax: getValue('guildResourceTax'), guildShardTax: getValue('guildShardTax'), wisdomPotionLevel: getValue('wisdomPotionLevel'), harvestingPotionLevel: getValue('harvestingPotionLevel'), resonancePotionLevel: getValue('resonancePotionLevel') } }; const t = translations[currentLanguage]; const skillLevelsText = t.skillLevels .replace('\${miningLevel}', values.basic.miningLevel) .replace('\${fishingLevel}', values.basic.fishingLevel) .replace('\${woodcuttingLevel}', values.basic.woodcuttingLevel); document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <h4>\${t.currentDataSnapshot}</h4> <div class="result-content-box"> <strong>\${t.basicInfoTitle}</strong><br> \${t.battleLevel} \${values.basic.battleLevel}, \${t.currentEnemy} \${values.basic.currentEnemy}<br> \${skillLevelsText}<br><br> <strong>\${t.gatheringBoostsTitle}</strong><br> \${t.mining} \${values.gathering.miningBoost}%, \${t.fishing} \${values.gathering.fishingBoost}%, \${t.woodcutting} \${values.gathering.woodcuttingBoost}%<br> \${t.baseResourceAmount.replace('\${baseResourceAmountTotal}', values.gathering.baseResourceAmountTotal)}<br><br> <strong>\${t.codexBoostsTitle}</strong><br> \${t.codexManaDust} \${values.codex.codexManaDust}%, \${t.codexBaseResource} \${values.codex.codexBaseResource}%, \${t.codexDropBoost} \${values.codex.codexDropBoost}% <br><br> <strong>\${t.equipmentBoostsTitle}</strong><br> \${t.manaBoost} \${values.equipment.manaBoost}%, \${t.shardBoost} \${values.equipment.shardBoost}%, \${t.potionBoost} \${values.equipment.potionBoost}%<br><br> <strong>\${t.farmSystemTitle}</strong><br> \${t.harvestGolem} \${values.farm.harvestGolem}, \${t.fertilizer} \${values.farm.fertilizer}, \${t.farmPlots} \${values.farm.farmPlots}<br> \${t.herbHarvest.replace('\${farmHerbHarvest}', values.farm.farmHerbHarvest).replace('\${farmTax}', values.farm.farmTax)}<br><br> <strong>\${t.marketPricesTitle}</strong><br> \${t.shardPrice} \${values.market.shardPrice}, \${t.codexPrice} \${values.market.codexPrice} <br> \${t.fishPrice} \${values.market.fishPrice}, \${t.woodPrice} \${values.market.woodPrice}, \${t.ironPrice} \${values.market.ironPrice}<br> \${t.sagerootPrice} \${values.market.sagerootPrice}, \${t.bloomwellPrice} \${values.market.bloomwellPrice} <br><br> <strong>\${t.manualInputTitle}</strong><br> \${t.guildLevel} \${values.guild.guildLevel}, \${t.guildNexusCrystalLevel} \${values.guild.guildNexusCrystalLevel}<br> \${t.guildTaxRates.replace('\${guildManaTax}', values.guild.guildManaTax).replace('\${guildResonanceTax}', values.guild.guildResonanceTax).replace('\${guildShardTax}', values.guild.guildShardTax)} <br> \${t.potionLevels.replace('\${wisdomPotionLevel}', values.guild.wisdomPotionLevel).replace('\${harvestingPotionLevel}', values.guild.harvestingPotionLevel)} </div> </div> \`; saveGlobalData(); } function getValue(id) { const element = document.getElementById(id); return element ? (parseFloat(element.value) || 0) : 0; } function formatNumber(num) { if (num === 0) return '0.00'; if (isNaN(num) || !isFinite(num)) return 'NaN'; const isNegative = num < 0; const absNum = Math.abs(num); let result; if (absNum >= 1e15) { result = (absNum / 1e15).toFixed(2) + 'P'; } else if (absNum >= 1e12) { result = (absNum / 1e12).toFixed(2) + 'T'; } else if (absNum >= 1e9) { result = (absNum / 1e9).toFixed(2) + 'B'; } else if (absNum >= 1e6) { result = (absNum / 1e6).toFixed(2) + 'M'; } else if (absNum >= 1e3) { result = (absNum / 1e3).toFixed(2) + 'K'; } else { result = absNum.toFixed(2); } return isNegative ? '-' + result : result; } //#region shards function calculateShards(){ const totalLevel = values.basic.battleLevel*3 + values.basic.miningLevel + values.basic.fishingLevel + values.basic.woodcuttingLevel; const MinBaseShard = 100*Math.pow(1+totalLevel/10, 1-0.3*(totalLevel/(totalLevel+20000))); const potionShardBoost = 5* (1 + values.equipment.potionBoost/100) * values.guild.resonancePotionLevel; const totalShardBoost = values.equipment.shardBoost + potionShardBoost; const minShardPerActionAfterTax = MinBaseShard * (1 + totalShardBoost / 100) * (1 - values.guild.guildShardTax / 100); const shardPerActionAfterTax = 1.5 * MinBaseShard * (1 + totalShardBoost / 100) * (1 - values.guild.guildShardTax / 100); const shardsPerDayAfterTax = shardPerActionAfterTax * ((1+(values.codex.codexDropBoost/100))*(1/80)*28800); const shardDailyValue = shardsPerDayAfterTax * values.market.shardPrice; return [shardsPerDayAfterTax, shardDailyValue, minShardPerActionAfterTax]; } //#region farm function calculateFarmIncome(){ // 200k Mana Dust/herb/hr const herbsHourHarvestTotal = values.farm.farmHerbHarvest; const manaHourConsume = herbsHourHarvestTotal * 200000; const sageRootHourHarvest = herbsHourHarvestTotal/2; const bloomHourHarvest = herbsHourHarvestTotal/2; const wisdomPotionConsume = values.guild.wisdomPotionLevel * (1+values.guild.wisdomPotionLevel) / 2; const harvestingPotionConsume = values.guild.harvestingPotionLevel * (1+values.guild.harvestingPotionLevel) / 2; const resonancePotionConsume = values.guild.resonancePotionLevel * (1+values.guild.resonancePotionLevel) / 2; const netSageRootGain = sageRootHourHarvest - wisdomPotionConsume - resonancePotionConsume; const netBloomGain = bloomHourHarvest - harvestingPotionConsume - resonancePotionConsume; const sagePrice = values.market.sagerootPrice; const bloomPrice = values.market.bloomwellPrice; const sageProfitPerDay = netSageRootGain * sagePrice * 24; const bloomProfitPerDay = netBloomGain * bloomPrice * 24; const manaConsumePerDay = manaHourConsume * 24; return [manaConsumePerDay, sageRootHourHarvest, bloomHourHarvest, (wisdomPotionConsume + resonancePotionConsume), (harvestingPotionConsume + resonancePotionConsume), netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay]; } //#region combat function calculateCombatEfficiency(display = true){ const t = translations[currentLanguage]; const baseMana = 0.0001 * Math.pow((values.basic.currentEnemy+150), 2) + Math.pow((values.basic.currentEnemy+150), 1.2) + 10 * (values.basic.currentEnemy+150); const manaPerAction = baseMana * (1 + values.codex.codexManaDust / 100) * (1 + values.equipment.manaBoost / 100); const manaPerDay = manaPerAction * 28800; const manaPerDayAfterTax = manaPerDay * (1 - values.guild.guildManaTax / 100); const [shardPerActionAfterTax, shardDailyValue,minShardPerActionAfterTax] = calculateShards(); const [manaConsumePerDay, sageRootHourHarvest, bloomHourHarvest, sageRootHourConsume, bloomHourConsume, netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay] = calculateFarmIncome(); const farmProfitPerDay = sageProfitPerDay + bloomProfitPerDay - manaConsumePerDay; const profitPerDayTotal = manaPerDayAfterTax + shardDailyValue + farmProfitPerDay; if(display){ document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.manaPerDay} \${formatNumber(manaPerDayAfterTax)}<br><br> \${t.shardRangePerAction} \${formatNumber(minShardPerActionAfterTax)} ~ \${formatNumber(2*minShardPerActionAfterTax)}<br> \${t.shardsPerDay} \${formatNumber(shardPerActionAfterTax)}<br> \${t.shardsDailyValue} \${formatNumber(shardDailyValue)}<br><br> \${t.sagerootHourlyProduction} \${formatNumber(sageRootHourHarvest)} \${t.bloomwellHourlyProduction} \${formatNumber(bloomHourHarvest)}<br> \${t.sagerootHourlyConsumption} \${formatNumber(sageRootHourConsume)} \${t.bloomwellHourlyConsumption} \${formatNumber(bloomHourConsume)}<br> \${t.sagerootNetProduction} \${formatNumber(netSageRootGain)} \${t.bloomwellNetProduction} \${formatNumber(netBloomGain)}<br> \${t.sagerootDailyProfit} \${formatNumber(sageProfitPerDay)} \${t.bloomwellDailyProfit} \${formatNumber(bloomProfitPerDay)}<br> \${t.farmDailyMaintenance} \${formatNumber(manaConsumePerDay)}<br> \${t.farmDailyValue} \${formatNumber(farmProfitPerDay)}<br><br> \${t.dailyIncome} \${formatNumber(profitPerDayTotal)}<br> </div> </div> \`; } return profitPerDayTotal; } //#region gathering function calculateGatheringEfficiency(display = true, gatheringType = null){ const t = translations[currentLanguage]; if (display && gatheringType === null) { const selectedType = document.getElementById('gatheringType').value; if (!values.ui) values.ui = {}; values.ui.lastGatheringType = selectedType; } if(gatheringType === null){ gatheringType = document.getElementById('gatheringType').value; } let level = 0; if (gatheringType === 'woodcutting') { level = values.basic.woodcuttingLevel; } else if (gatheringType === 'mining') { level = values.basic.miningLevel; } else if (gatheringType === 'fishing') { level = values.basic.fishingLevel; } const [shardPerActionAfterTax, shardDailyValue,minShardPerActionAfterTax] = calculateShards(); const potionBaseResource = values.guild.harvestingPotionLevel * 0.1 * (1 + values.equipment.potionBoost/100); const baseResource = 1 + level * 0.03 + values.gathering.baseResourceAmountTotal + potionBaseResource; const factor2 = 1 + values.codex.codexBaseResource/100; const factor3 = 1 + values.guild.guildLevel/2/100; const factor4 = 1 + values.gathering[gatheringType + 'Boost'] / 100; const resourcePerAction = baseResource * factor2 * factor3 * factor4; const resourcePerActionAfterTax = resourcePerAction * (1 - values.guild.guildResonanceTax / 100); const resourcePerDayAfterTax = resourcePerActionAfterTax * 28800; let resourcePrice = 0; if (gatheringType === 'woodcutting') { resourcePrice = values.market.woodPrice; } else if (gatheringType === 'mining') { resourcePrice = values.market.ironPrice; } else if (gatheringType === 'fishing') { resourcePrice = values.market.fishPrice; } let resourceProfitPerDay = resourcePerDayAfterTax * resourcePrice; const [manaConsumePerDay, sageRootHourHarvest, bloomHourHarvest, sageRootHourConsume, bloomHourConsume, netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay] = calculateFarmIncome(); const farmProfitPerDay = sageProfitPerDay + bloomProfitPerDay - manaConsumePerDay; const profitPerDayTotal = resourceProfitPerDay + shardDailyValue + farmProfitPerDay; if(display){ document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.resourcePerAction} \${formatNumber(resourcePerActionAfterTax)}<br> \${t.resourcePerDay} \${formatNumber(resourcePerDayAfterTax)}<br> \${t.resourcePrice} \${formatNumber(resourcePrice)}<br> \${t.resourceDailyValue} \${formatNumber(resourceProfitPerDay)}<br><br> \${t.shardRangePerAction} \${formatNumber(minShardPerActionAfterTax)} ~ \${formatNumber(2*minShardPerActionAfterTax)}<br> \${t.shardsPerDay} \${formatNumber(shardPerActionAfterTax)}<br> \${t.shardsDailyValue} \${formatNumber(shardDailyValue)}<br><br> \${t.sagerootHourlyProduction} \${formatNumber(sageRootHourHarvest)} \${t.bloomwellHourlyProduction} \${formatNumber(bloomHourHarvest)}<br> \${t.sagerootHourlyConsumption} \${formatNumber(sageRootHourConsume)} \${t.bloomwellHourlyConsumption} \${formatNumber(bloomHourConsume)}<br> \${t.sagerootNetProduction} \${formatNumber(netSageRootGain)} \${t.bloomwellNetProduction} \${formatNumber(netBloomGain)}<br> \${t.sagerootDailyProfit} \${formatNumber(sageProfitPerDay)} \${t.bloomwellDailyProfit} \${formatNumber(bloomProfitPerDay)}<br> \${t.farmDailyMaintenance} \${formatNumber(manaConsumePerDay)}<br> \${t.farmDailyValue} \${formatNumber(farmProfitPerDay)}<br><br> \${t.dailyIncome} \${formatNumber(profitPerDayTotal)}<br> </div> </div> \`; } return profitPerDayTotal; } //#region ResonanceLevel function calculateResonancePotionLevel(){ const t = translations[currentLanguage]; let resonancePotionLevelX = 0; function getResonancePotionConsume(resonancePotionLevelX) { const resonancePotionConsume = resonancePotionLevelX * (1+resonancePotionLevelX) / 2; const sageRootConsumePerDay = resonancePotionConsume * values.market.sagerootPrice * 24; const bloomConsumePerDay = resonancePotionConsume * values.market.bloomwellPrice * 24; const farmConsumePerDay = sageRootConsumePerDay + bloomConsumePerDay; return farmConsumePerDay; } function getResonancePotionExtraIncome(resonancePotionLevelX) { const totalLevel = values.basic.battleLevel*3 + values.basic.miningLevel + values.basic.fishingLevel + values.basic.woodcuttingLevel; const MinBaseShard = 100*Math.pow(1+totalLevel/10, 1-0.3*(totalLevel/(totalLevel+20000))); const potionShardBoost = 5* (1 + values.equipment.potionBoost/100) * resonancePotionLevelX; const shardPerActionAfterTax = 1.5 * MinBaseShard * (1 + potionShardBoost / 100) * (1 - values.guild.guildShardTax / 100); const shardsPerDayAfterTax = shardPerActionAfterTax * ((1+(values.codex.codexDropBoost/100))*(1/80)*28800); const shardDailyValue = shardsPerDayAfterTax * values.market.shardPrice; return shardDailyValue; } const totalLevel = values.basic.battleLevel * 3 + values.basic.miningLevel + values.basic.fishingLevel + values.basic.woodcuttingLevel; const MinBaseShard = 100 * Math.pow(1 + totalLevel/10, 1 - 0.3 * (totalLevel/(totalLevel + 20000))); const A = (values.market.sagerootPrice + values.market.bloomwellPrice) * 24; const B = 0.05 * (1 + values.equipment.potionBoost / 100); const C = 1.5 * MinBaseShard * (1 - values.guild.guildShardTax / 100) * ((1 + values.codex.codexDropBoost / 100) * (1/80) * 28800) * values.market.shardPrice; const optimalLevel = (C * B) / A - 0.5; const finalOptimalLevel = Math.max(0, optimalLevel); document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.optimalResonanceLevel} \${finalOptimalLevel.toFixed(2)}<br><br> \${t.comparedToLevel0}<br> \${t.extraFarmConsumption} \${formatNumber(getResonancePotionConsume(finalOptimalLevel))}/day<br> \${t.extraIncome} \${formatNumber(getResonancePotionExtraIncome(finalOptimalLevel))}/day<br> \${t.increasedIncome} \${formatNumber(getResonancePotionExtraIncome(finalOptimalLevel)-getResonancePotionConsume(finalOptimalLevel))}<br> </div> </div>\`; } // region Gather ROI function calculateGatheringROI(){ const t = translations[currentLanguage]; const gatheringType = document.getElementById('gatheringType2').value; const paths = { mining: { base: 'miningBaseBoost', total: 'miningBoost' }, fishing: { base: 'fishingBaseBoost', total: 'fishingBoost' }, woodcutting: { base: 'woodcuttingBaseBoost', total: 'woodcuttingBoost' } }; // FIXED function calculateResearchBoost(incrementLevel) { const path = paths[gatheringType]; const currentLevel = values.gathering[path.base]; const oldBoostTotal = values.gathering[path.total]; const shardsNeed = incrementLevel * (currentLevel + 1 + currentLevel + incrementLevel); const priceNeed = shardsNeed * values.market.shardPrice; const newBoostTotal = oldBoostTotal + incrementLevel *( 0.02 + values.guild.guildNexusCrystalLevel * 0.0002); // console.log("oldBoostTotal: " + oldBoostTotal + " newBoostTotal: " + newBoostTotal); values.gathering[path.total] = newBoostTotal; const newProfit = calculateGatheringEfficiency(false,gatheringType); values.gathering[path.total] = oldBoostTotal; return [priceNeed, newProfit]; } // no problem function calculateBaseResourceAmount(incrementLevel) { const currentLevel = values.gathering.baseResourceAmountBase; function sumOfSquares(n) { return n * (n + 1) * (2 * n + 1) / 6; } const priceNeed = (sumOfSquares(currentLevel+incrementLevel) - sumOfSquares(currentLevel))*100; const oldBaseResourceAmoutTotal = values.gathering.baseResourceAmountTotal; values.gathering.baseResourceAmountTotal += incrementLevel/100; const newProfit = calculateGatheringEfficiency(false, gatheringType); values.gathering.baseResourceAmountTotal = oldBaseResourceAmoutTotal; return [priceNeed, newProfit]; } // no problem function calculateCodexBaseResource(incrementLevel) { const currentLevel = values.codex.codexBaseResourceBase; function calculateCost(fromLevel, toLevel) { const startValue = fromLevel + 1; const endValue = toLevel; const n = endValue - startValue + 1; const sum = n * (startValue + endValue) / 2; return sum; } const codexNeed = calculateCost(currentLevel, currentLevel+incrementLevel); const priceNeed = codexNeed * values.market.codexPrice; const oldCodexBaseResource = values.codex.codexBaseResource; values.codex.codexBaseResource += incrementLevel; const newProfit = calculateGatheringEfficiency(false, gatheringType); values.codex.codexBaseResource = oldCodexBaseResource; return [priceNeed, newProfit]; } // no problem function calculatePotionBoost(incrementLevel) { const currentLevel = values.equipment.potionBoostBase; function calculateCost(fromLevel, toLevel) { const baseCost = 10000000; const growthRate = 1.1; return Array.from({ length: toLevel - fromLevel }, (_, i) => fromLevel + i) .map(level => Math.floor(baseCost * Math.pow(growthRate, level))) .reduce((total, cost) => total + cost, 0); } const priceNeed = calculateCost(currentLevel, currentLevel+incrementLevel); const oldPotionBoost = values.equipment.potionBoost; values.equipment.potionBoost += incrementLevel; const newProfit = calculateGatheringEfficiency(false, gatheringType); values.equipment.potionBoost = oldPotionBoost; return [priceNeed, newProfit]; } // no problem function calculateCodexDropRate(incrementLevel) { const currentLevel = values.codex.codexDropBoostBase; function calculateCost(fromLevel, toLevel) { const startValue = fromLevel + 1; const endValue = toLevel; const n = endValue - startValue + 1; const sum = n * (startValue + endValue) / 2; return sum; } const codexNeed = calculateCost(currentLevel, currentLevel+incrementLevel); const priceNeed = codexNeed * values.market.codexPrice; const oldCodexDropRate = values.codex.codexDropBoost; values.codex.codexDropBoost += incrementLevel; const newProfit = calculateGatheringEfficiency(false, gatheringType); values.codex.codexDropBoost = oldCodexDropRate; return [priceNeed, newProfit]; } function calculateFarmLevel(incrementLevel) { function calculateCost(fromLevel, toLevel) { function sumOfSquares(n) { return n * (n + 1) * (2 * n + 1) / 6; } return sumOfSquares(toLevel) - sumOfSquares(fromLevel); } const currentLevels = { harvestGolem: values.farm.harvestGolem, fertilizer: values.farm.fertilizer, farmPlots: values.farm.farmPlots }; function optimizedAllocation(incrementLevel) { const projects = ['harvestGolem', 'fertilizer', 'farmPlots']; const levels = [currentLevels.harvestGolem, currentLevels.fertilizer, currentLevels.farmPlots]; const allocations = [0, 0, 0]; let remaining = incrementLevel; while (remaining > 0) { let minLevel = Math.min(...levels); let minIndices = []; for (let i = 0; i < levels.length; i++) { if (levels[i] === minLevel) { minIndices.push(i); } } let nextHigherLevel = Math.min(...levels.filter(l => l > minLevel)); if (!isFinite(nextHigherLevel)) { const avgAllocation = Math.floor(remaining / minIndices.length); const extraLevels = remaining % minIndices.length; for (let i = 0; i < minIndices.length; i++) { const idx = minIndices[i]; const allocation = avgAllocation + (i < extraLevels ? 1 : 0); allocations[idx] += allocation; levels[idx] += allocation; } remaining = 0; } else { const levelGap = nextHigherLevel - minLevel; const totalNeeded = levelGap * minIndices.length; if (totalNeeded <= remaining) { for (const idx of minIndices) { allocations[idx] += levelGap; levels[idx] += levelGap; } remaining -= totalNeeded; } else { const avgAllocation = Math.floor(remaining / minIndices.length); const extraLevels = remaining % minIndices.length; for (let i = 0; i < minIndices.length; i++) { const idx = minIndices[i]; const allocation = avgAllocation + (i < extraLevels ? 1 : 0); allocations[idx] += allocation; levels[idx] += allocation; } remaining = 0; } } } return { harvestGolem: allocations[0], fertilizer: allocations[1], farmPlots: allocations[2] }; } const allocations = optimizedAllocation(incrementLevel); const costs = { harvestGolem: calculateCost(currentLevels.harvestGolem, currentLevels.harvestGolem + allocations.harvestGolem), fertilizer: calculateCost(currentLevels.fertilizer, currentLevels.fertilizer + allocations.fertilizer), farmPlots: calculateCost(currentLevels.farmPlots, currentLevels.farmPlots + allocations.farmPlots) }; const priceNeed = costs.harvestGolem * values.market.ironPrice + costs.fertilizer * values.market.fishPrice + costs.farmPlots * values.market.woodPrice; const oldfarmHerbHarvest = values.farm.farmHerbHarvest; const newHarvestGolemLevel = currentLevels.harvestGolem + allocations.harvestGolem; const newFertilizerLevel = currentLevels.fertilizer + allocations.fertilizer; const newFarmPlotsLevel = currentLevels.farmPlots + allocations.farmPlots; values.farm.farmHerbHarvest = 2.5 * Math.pow(1 + newHarvestGolemLevel/100, 0.9) * Math.pow(1 + newFertilizerLevel/100, 0.9) * Math.pow(1 + newFarmPlotsLevel/100, 0.9); const newProfit = calculateGatheringEfficiency(false, gatheringType); values.farm.farmHerbHarvest = oldfarmHerbHarvest; return [priceNeed, newProfit]; } const currentProfit = calculateGatheringEfficiency(false, gatheringType); const research = calculateResearchBoost(3000); const baseResource = calculateBaseResourceAmount(100); const codexBase = calculateCodexBaseResource(5); const potion = calculatePotionBoost(5); const codexDrop = calculateCodexDropRate(5); const farm = calculateFarmLevel(300); const investments = [ { name: t.investments.research, cost: research[0], profit: research[1]-currentProfit }, { name: t.investments.baseResource, cost: baseResource[0], profit: baseResource[1]-currentProfit }, { name: t.investments.codexBase, cost: codexBase[0], profit: codexBase[1]-currentProfit }, { name: t.investments.potion, cost: potion[0], profit: potion[1]-currentProfit }, { name: t.investments.codexDrop, cost: codexDrop[0], profit: codexDrop[1]-currentProfit }, { name: t.investments.farm, cost: farm[0], profit: farm[1]-currentProfit } ]; investments.forEach(item => { item.roi = item.cost > 0 ? (item.profit / item.cost * 10000) : 0; }); investments.sort((a, b) => b.roi - a.roi); let resultHTML = \` <div class="calc-result"> <div class="result-content-box"> <strong>\${t.gatheringROIComparison.replace('\${gatheringType}', gatheringType)}</strong><br><br> \`; investments.forEach((item, index) => { resultHTML += \`\${index + 1}. \${item.name}<br>\`; resultHTML += \` \${t.cost} \${formatNumber(item.cost)} | \${t.extraDailyProfit} \${formatNumber(item.profit)}<br>\`; resultHTML += \` ROI: \${item.roi.toFixed(2)}<br><br>\`; }); resultHTML += \`</div></div>\`; document.getElementById('calcResults').innerHTML = resultHTML; const selectedType = document.getElementById('gatheringType2').value; if (!values.ui) values.ui = {}; values.ui.lastGatheringType2 = selectedType; } // #region GatherEquip function calculateGatherEquipReplace(){ const t = translations[currentLanguage]; function findLowestTypeBoostEquip(playerData) { const equipment = playerData.Equipment; let lowestEquip = null; let lowestBoost = Infinity; Object.values(equipment).forEach(item => { if (item.Slot == 1) return; const boosts = item.Boosts; const boostKeys = Object.keys(boosts); const lastBoostType = boostKeys[boostKeys.length - 1]; if (lastBoostType === '124') { const infusions = item.Infusions || 0; const infusionMultiplier = 1 + (infusions * 0.05); const gatherBaseResourceAmout = boosts['124'] * infusionMultiplier / 100; if (gatherBaseResourceAmout < lowestBoost) { lowestBoost = gatherBaseResourceAmout; lowestEquip = item; } } }); return lowestEquip ? lowestEquip: null; } function calculateHarvestingPotionCost(Level){ const HarvestingPotionConsume = Level * (1+Level) / 2; const bloomwellPrice = values.market.bloomwellPrice; return HarvestingPotionConsume*bloomwellPrice*24; } const lowestEquip = findLowestTypeBoostEquip(globalPlayerData); if (lowestEquip === null) { document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.gatherEquipReplaceTitle}<br> \${t.gatherEquipReplaceDesc}<br><br> \${t.noBaseResourceEquip}<br> </div> </div>\`; return; } const baseResourceAmoutLose = lowestEquip.Boosts['124'] * (1 + (lowestEquip.Infusions * 0.05)) / 100; const currentHarvestingPotionLevel = values.guild.harvestingPotionLevel; function findMinHarvestingPotionLevel(baseResourceAmountLose, currentHarvestingPotionLevel) { function potionBaseResource(level) { return level * 0.1 * (1 + values.equipment.potionBoost / 100); } const currentPotionResource = potionBaseResource(currentHarvestingPotionLevel); const targetResource = currentPotionResource + baseResourceAmountLose; const targetLevel = targetResource / (0.1 * (1 + values.equipment.potionBoost / 100)); return Math.ceil(targetLevel); } const minHarvestingPotionLevel = findMinHarvestingPotionLevel(baseResourceAmoutLose, currentHarvestingPotionLevel); const oldProfit = calculateGatheringEfficiency(false); const OldShardBoost = values.equipment.shardBoost; values.equipment.shardBoost += baseResourceAmoutLose*40; const newProfit = calculateGatheringEfficiency(false); values.equipment.shardBoost = OldShardBoost; const potionPriceDiffer = calculateHarvestingPotionCost(minHarvestingPotionLevel) - calculateHarvestingPotionCost(currentHarvestingPotionLevel); let resultHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.gatherEquipReplaceTitle}<br> \${t.gatherEquipReplaceDesc}<br> \${t.zh === "zh" ? "注意:根据【计算采集效率】按钮前面的框的值决定采集类型" : "Note: The gathering type is determined by the selection box next to the 'Calculate Gathering Efficiency' button."}<br><br> \${t.currentPotionLevel} \${currentHarvestingPotionLevel}。\${t.newPotionLevel} \${minHarvestingPotionLevel}<br><br> \`; if(newProfit - oldProfit > potionPriceDiffer){ resultHTML += \` \${t.canReplaceGatherEquip.replace('\${name}', lowestEquip.Name)}<br> \${t.baseResourceAmountLost.replace('\${amount}', baseResourceAmoutLose.toFixed(2)).replace('\${shardBoost}', (baseResourceAmoutLose*40).toFixed(2))}<br> \${t.manaIncreaseAfterReplace} \${formatNumber(newProfit - oldProfit)}<br> \${t.farmCostIncreaseAfterReplace} \${formatNumber(calculateHarvestingPotionCost(minHarvestingPotionLevel) - calculateHarvestingPotionCost(currentHarvestingPotionLevel))}<br> \${t.incomeIncreaseAfterReplace} \${formatNumber((newProfit - oldProfit) - (calculateHarvestingPotionCost(minHarvestingPotionLevel) - calculateHarvestingPotionCost(currentHarvestingPotionLevel)))}<br><br> \`; } else { resultHTML += \`\${t.cannotReplaceGatherEquip}<br>\`; } resultHTML += \`</div></div>\`; document.getElementById('calcResults').innerHTML = resultHTML; } // #region battlerEquip function calculateBattlerEquipReplace(){ const t = translations[currentLanguage]; const BOOST_TYPES = { 120: "BATTLE_EXPERIENCE_BOOST", 121: "MANA_DUST_BOOST", 122: "ELEMENTAL_SHARD_BOOST", 123: "STAT_DROP" }; function cheakReplaceEquip(playerData) { function findLowestTypeBoostEquip(playerData) { const equipment = playerData.Equipment; let lowestEquip = null; let lowestBoost = Infinity; Object.values(equipment).forEach(item => { const boosts = item.Boosts; const boostKeys = Object.keys(boosts); const lastBoostType = boostKeys[boostKeys.length - 1]; if (lastBoostType === '120') { const infusions = item.Infusions || 0; const infusionMultiplier = 1 + (infusions * 0.05); const battleExpBoost = boosts['120'] * infusionMultiplier; if (battleExpBoost < lowestBoost) { lowestBoost = battleExpBoost; lowestEquip = item; } } }); return lowestEquip ? lowestEquip: null; } function analyzeCurrentEquipment(playerData) { const equipment = playerData.Equipment; const typeCount = {}; const typeBoosts = {}; Object.keys(BOOST_TYPES).forEach(type => { typeCount[type] = 0; typeBoosts[type] = 0; }); Object.values(equipment).forEach(item => { const boosts = item.Boosts; const infusions = item.Infusions || 0; const infusionMultiplier = 1 + (infusions * 0.05); const boostKeys = Object.keys(boosts); const lastBoostType = boostKeys[boostKeys.length - 1]; if (BOOST_TYPES[lastBoostType]) { typeCount[lastBoostType]++; let enhancedBoosts = {}; Object.entries(boosts).forEach(([boostId, baseValue]) => { if (boostId !== lastBoostType) { const enhancedValue = baseValue * infusionMultiplier; enhancedBoosts[boostId] = enhancedValue; } else { typeBoosts[lastBoostType] += baseValue * infusionMultiplier; } }); } }); if(typeCount[121] > 1){ typeBoosts[121] = typeBoosts[121] * (1-(typeCount[121]-1)*0.05); } return { typeCount, typeBoosts}; } const lowestEquip = findLowestTypeBoostEquip(playerData); if (lowestEquip === null) { document.getElementById('calcResults').innerHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.battleEquipReplaceTitle}<br> \${t.battleEquipReplaceDesc}<br><br> \${t.noExpEquip}<br> </div> </div>\`; return null; } const EXPLost = lowestEquip.Boosts['120'] * (1 + (lowestEquip.Infusions * 0.05)); let { typeCount, typeBoosts } = analyzeCurrentEquipment(playerData); if(typeCount[121] > 1){ typeBoosts[121] = typeBoosts[121] / (1-(typeCount[121]-1)*0.05); } typeBoosts[121] += EXPLost; typeCount[121]++; typeCount[120]--; if(typeCount[121] > 1){ typeBoosts[121] = typeBoosts[121] * (1-(typeCount[121]-1)*0.05); } const oldProfit = calculateCombatEfficiency(false); const OldManaBoost = values.equipment.manaBoost; values.equipment.manaBoost = typeBoosts[121]; let newProfit = calculateCombatEfficiency(false); values.equipment.manaBoost = OldManaBoost; function findMinTargetLevel(currentWisdomPotionLevel, EXPLost) { const potionBoostMultiplier = 1 + values.equipment.potionBoost/100; const currentPotionEXP = currentWisdomPotionLevel * 5 * potionBoostMultiplier; const minTargetLevel = (EXPLost + currentPotionEXP) / (5 * potionBoostMultiplier); return Math.ceil(minTargetLevel); } function calculateWisdomPotionCost(Level){ const wisdomPotionConsume = Level * (1+Level) / 2; const sageRootPrice = values.market.sagerootPrice; return wisdomPotionConsume*sageRootPrice*24; } const currentWisdomPotionLevel = values.guild.wisdomPotionLevel; const targetWisdomPotionLevel = findMinTargetLevel(currentWisdomPotionLevel, EXPLost); let resultHTML = \` <div class="calc-result"> <div class="result-content-box"> \${t.battleEquipReplaceTitle}<br> \${t.battleEquipReplaceDesc}<br><br> \${t.currentPotionLevel} \${currentWisdomPotionLevel}。\${t.newPotionLevel} \${targetWisdomPotionLevel}<br><br> \`; let replaceEquip = 0; if ((newProfit - oldProfit) > (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))) { resultHTML += \` \${t.canReplaceBattleEquipToMana.replace('\${name}', lowestEquip.Name)}<br> \${t.manaIncreaseAfterReplace} \${formatNumber(newProfit - oldProfit)}<br> \${t.farmCostIncreaseAfterReplace} \${formatNumber(calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))}<br> \${t.incomeIncreaseAfterReplace} \${formatNumber((newProfit - oldProfit) - (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel)))}<br><br> \`; replaceEquip = 1; } ({ typeCount, typeBoosts } = analyzeCurrentEquipment(globalPlayerData)); typeBoosts[122] += EXPLost; const OldShardBoost = values.equipment.shardBoost; values.equipment.shardBoost = typeBoosts[122]; newProfit = calculateCombatEfficiency(false); values.equipment.shardBoost = OldShardBoost; if ((newProfit - oldProfit) > (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))) { resultHTML += \` \${t.canReplaceBattleEquipToShard.replace('\${name}', lowestEquip.Name)}<br> \${t.manaIncreaseAfterReplace} \${formatNumber(newProfit - oldProfit)}<br> \${t.farmCostIncreaseAfterReplace} \${formatNumber(calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))}<br> \${t.incomeIncreaseAfterReplace} \${formatNumber((newProfit - oldProfit) - (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel)))}<br><br> \`; } else if(replaceEquip === 0) { resultHTML += \`\${t.cannotReplaceBattleEquip}<br>\`; } resultHTML += \`</div></div>\`; document.getElementById('calcResults').innerHTML = resultHTML; } cheakReplaceEquip(globalPlayerData); } window.onload = function() { setTheme(currentTheme); setLanguage(currentLanguage); loadGlobalData(); showExampleData(); }; window.addEventListener('beforeunload', function() { saveGlobalData(); }); </script> </body> </html>`; } // 按钮点击处理 function handleButtonClickCalculate() { console.log("Button clicked!"); // 检查是否已存在面板 let panel = document.getElementById('manarion-calculator-panel'); if (panel) { panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; return; } // 创建浮动面板 panel = document.createElement('div'); panel.id = 'manarion-calculator-panel'; panel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 1200px; height: 80vh; background: white; border-radius: 15px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); z-index: 1000000; overflow: auto; `; const iframe = document.createElement('iframe'); iframe.style.cssText = 'width: 100%; height: 100%; border: none; border-radius: 15px;'; panel.appendChild(iframe); document.body.appendChild(panel); const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; iframeDoc.open(); iframeDoc.write(getFullHTMLContent()); iframeDoc.close(); iframe.contentWindow.fetchDataWithGM = async function(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Accept': 'application/json', 'User-Agent': 'Mozilla/5.0' }, onload: function(response) { try { resolve(JSON.parse(response.responseText)); } catch (e) { reject(e); } }, onerror: reject }); }); }; const closeBtn = document.createElement('button'); closeBtn.innerHTML = '✕'; closeBtn.style.cssText = ` position: absolute; top: 10px; right: 10px; width: 40px; height: 40px; border-radius: 50%; border: none; background: #ff4444; color: white; font-size: 24px; cursor: pointer; z-index: 1000001; `; closeBtn.onclick = () => panel.style.display = 'none'; panel.appendChild(closeBtn); } function initHelper() { createDraggableButton(); } function createDraggableButton() { if (document.getElementById('manarion-helper-button')) return; const button = document.createElement('div'); button.id = 'manarion-helper-button'; button.innerHTML = ` <svg width="40" height="40" fill="white" viewBox="0 0 24 24"> <path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/> </svg> `; Object.assign(button.style, { position: 'fixed', top: '50%', right: '20px', transform: 'translateY(-50%)', width: '80px', height: '80px', background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'move', boxShadow: '0 8px 25px rgba(0,0,0,0.3)', zIndex: '999999', transition: 'all 0.3s ease', border: '4px solid rgba(255,255,255,0.2)', userSelect: 'none' }); // 拖拽功能 let isDragging = false; let hasMoved = false; let startX = 0; let startY = 0; let initialX = 0; let initialY = 0; button.addEventListener('mousedown', (e) => { isDragging = true; hasMoved = false; startX = e.clientX; startY = e.clientY; const rect = button.getBoundingClientRect(); initialX = rect.left; initialY = rect.top; button.style.transition = 'none'; button.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; // 检查是否真的移动了(移动超过5像素才算拖拽) if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { hasMoved = true; } if (hasMoved) { const newX = initialX + deltaX; const newY = initialY + deltaY; // 限制在窗口内 const limitedX = Math.max(0, Math.min(newX, window.innerWidth - 80)); const limitedY = Math.max(0, Math.min(newY, window.innerHeight - 80)); button.style.left = limitedX + 'px'; button.style.top = limitedY + 'px'; button.style.right = 'auto'; button.style.transform = 'none'; } }); document.addEventListener('mouseup', () => { if (isDragging) { if (!hasMoved) { handleButtonClickCalculate(); } isDragging = false; hasMoved = false; button.style.transition = 'all 0.3s ease'; button.style.cursor = 'move'; } }); // 悬停效果 button.addEventListener('mouseenter', () => { if (!isDragging) { button.style.transform = button.style.transform === 'translateY(-50%)' ? 'translateY(-50%) scale(1.1)' : 'scale(1.1)'; button.style.boxShadow = '0 12px 35px rgba(0,0,0,0.4)'; } }); button.addEventListener('mouseleave', () => { if (!isDragging) { button.style.transform = button.style.transform.includes('translateY(-50%)') ? 'translateY(-50%)' : 'none'; button.style.boxShadow = '0 8px 25px rgba(0,0,0,0.3)'; } }); document.body.appendChild(button); console.log('Manarion Helper Button created!'); } // 初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initHelper); } else { setTimeout(initHelper, 1000); } })();