// ==UserScript==
// @name Manarion helper
// @name:zh-CN Manarion 助手
// @name:en Manarion helper
// @namespace http://tampermonkey.net/
// @version 0.0.5
// @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;
--loading-bg: #cce5ff;
--loading-text: #004085;
--loading-border: #b8daff;
}
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);
--loading-bg: oklch(25% .1 259);
--loading-text: oklch(85% .2 259);
--loading-border: oklch(62.3% .214 259.815);
}
/* --- 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,
.input-section h3,
.calculator-section h3 {
margin: 0 0 10px 0;
font-size: 1.1em;
color: var(--text-color); /* This is the key line */
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-suffix {
margin-left: 4px;
color: var(--text-color-light);
flex-shrink: 0;
}
.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);
}
.success, .error, .loading {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid;
}
.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); }
.loading { background-color: var(--loading-bg); color: var(--loading-text); border-color: var(--loading-border); text-align: center; }
.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; }
.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",
calculateCombatROI: "Combat ROI:",
calculateCombatROIButton: "Calculate Combat 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}, Resonance Potion Level: \${resonancePotionLevel}",
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:",
combatROIComparison: "Combat Investment ROI Comparison:",
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(only calculate for elemental shards)",
farm: "Farm total 300 levels",
codexMana: "Codex Mana Dust 5 levels",
enemyLevel: "Increase enemy level by 100"
},
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.",
maxLevelWarning: "Already at max level (100), cannot upgrade further.",
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: "计算采集投资回报率",
calculateCombatROI: "战斗投资回报率计算:",
calculateCombatROIButton: "计算战斗投资回报率",
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}, 回响药水等级: \${resonancePotionLevel}",
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} 投资回报率对比:",
combatROIComparison: "战斗投资回报率对比:",
cost: "成本:",
extraDailyProfit: "额外日收益:",
investments: {
research: "研究加成3000级",
baseResource: "基础资源100级",
codexBase: "法典基础5级",
potion: "药水加成5级",
codexDrop: "法典掉落5级(只计算了 elemental shards)",
farm: "农场总共提升300级",
codexMana: "法典魔法尘5级",
enemyLevel: "当前怪物等级提升100级"
},
maxLevelWarning: "已达满级 (100),无法继续提升。",
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();
document.getElementById('message').innerHTML = '';
// 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);
getAllCurrentValues();
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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">/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 class="stat-suffix">/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 class="stat-suffix">%</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 class="stat-suffix">%</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 class="stat-suffix">%</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).replace('\${resonancePotionLevel}', values.guild.resonancePotionLevel)}
</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;
console.log("oldPotionBoost: " + oldPotionBoost + " newPotionBoost: " + values.equipment.potionBoost);
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);
const codexBaseItem = investments.find(item => item.name === t.investments.codexBase);
if (codexBaseItem && values.codex.codexBaseResourceBase >= 100) {
codexBaseItem.name += \` <span style="opacity: 0.7;">(\${t.maxLevelWarning})</span>\`;
}
const codexDropItem = investments.find(item => item.name === t.investments.codexDrop);
if (codexDropItem && values.codex.codexDropBoostBase >= 100) {
codexDropItem.name += \` <span style="opacity: 0.7;">(\${t.maxLevelWarning})</span>\`;
}
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 CombatROI
// Add this entire function into your test.js file
function calculateCombatROI() {
const t = translations[currentLanguage];
// Helper function to calculate cost of Codex upgrades
function calculateCodexCost(baseLevel, incrementLevel) {
function calculateSum(from, to) {
const n = to - from;
return n * (from + 1 + to) / 2;
}
const codexNeed = calculateSum(baseLevel, baseLevel + incrementLevel);
return codexNeed * values.market.codexPrice;
}
// Path 1: Upgrade Codex Mana Dust
function calculateCodexManaDust(incrementLevel) {
// Note: BaseBoosts['101'] is the base level for Codex Mana Dust
const priceNeed = calculateCodexCost(globalPlayerData.BaseBoosts['101'] || 0, incrementLevel);
const oldCodexManaDust = values.codex.codexManaDust;
values.codex.codexManaDust += incrementLevel;
const newProfit = calculateCombatEfficiency(false);
values.codex.codexManaDust = oldCodexManaDust;
return [priceNeed, newProfit];
}
// Path 2: Upgrade Codex Drop Rate
function calculateCodexDropRate(incrementLevel) {
const priceNeed = calculateCodexCost(values.codex.codexDropBoostBase, incrementLevel);
const oldCodexDropRate = values.codex.codexDropBoost;
values.codex.codexDropBoost += incrementLevel;
const newProfit = calculateCombatEfficiency(false);
values.codex.codexDropBoost = oldCodexDropRate;
return [priceNeed, newProfit];
}
// Path 3: Upgrade Potion Boost
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 = calculateCombatEfficiency(false);
values.equipment.potionBoost = oldPotionBoost;
return [priceNeed, newProfit];
}
// Path 4: Upgrade Farm Levels (as it affects overall profit)
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 };
// This allocation logic is complex, assuming it's correct from calculateGatheringROI
function optimizedAllocation(inc) {
const levels = [currentLevels.harvestGolem, currentLevels.fertilizer, currentLevels.farmPlots];
const allocations = [0, 0, 0];
let remaining = inc;
while(remaining > 0){ let minLvl=Math.min(...levels),indices=[]; for(let i=0;i<3;i++) if(levels[i]===minLvl) indices.push(i); let nextHigher=Math.min(...levels.filter(l=>l>minLvl)),gap=isFinite(nextHigher)?nextHigher-minLvl:remaining,alloc=Math.min(gap,Math.floor(remaining/indices.length)); for(let idx of indices){ allocations[idx]+=alloc; levels[idx]+=alloc; } remaining -= alloc * indices.length; if(alloc<gap && remaining>0) { for(let i=0;i<remaining;i++){ let idx=indices[i%indices.length]; allocations[idx]++; levels[idx]++; } 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 newGolem = currentLevels.harvestGolem + allocations.harvestGolem;
const newFert = currentLevels.fertilizer + allocations.fertilizer;
const newPlots = currentLevels.farmPlots + allocations.farmPlots;
values.farm.farmHerbHarvest = 2.5 * Math.pow(1 + newGolem/100, 0.9) * Math.pow(1 + newFert/100, 0.9) * Math.pow(1 + newPlots/100, 0.9);
const newProfit = calculateCombatEfficiency(false);
values.farm.farmHerbHarvest = oldFarmHerbHarvest;
return [priceNeed, newProfit];
}
function calculateEnemyLevelIncrease(incrementLevel) {
const priceNeed = 0; // No cost
const oldEnemyLevel = values.basic.currentEnemy;
values.basic.currentEnemy += incrementLevel;
const newProfit = calculateCombatEfficiency(false);
values.basic.currentEnemy = oldEnemyLevel; // IMPORTANT: Restore original value
return [priceNeed, newProfit];
}
// --- Main Execution ---
const currentProfit = calculateCombatEfficiency(false);
const codexMana = calculateCodexManaDust(5);
const codexDrop = calculateCodexDropRate(5);
const potion = calculatePotionBoost(5);
const farm = calculateFarmLevel(300);
const enemyIncrease = calculateEnemyLevelIncrease(100);
const investments = [
{ name: t.investments.codexMana, cost: codexMana[0], profit: codexMana[1] - currentProfit },
{ name: t.investments.codexDrop, cost: codexDrop[0], profit: codexDrop[1] - currentProfit },
{ name: t.investments.potion, cost: potion[0], profit: potion[1] - currentProfit },
{ name: t.investments.farm, cost: farm[0], profit: farm[1] - currentProfit },
{ name: t.investments.enemyLevel, cost: enemyIncrease[0], profit: enemyIncrease[1] - currentProfit },
];
investments.forEach(item => {
item.roi = item.cost > 0 ? (item.profit / item.cost * 10000) : Infinity;
});
investments.sort((a, b) => {
if (a.cost === 0 || b.cost === 0) {
return b.profit - a.profit;
}
return b.roi - a.roi;
});
const codexManaItem = investments.find(item => item.name === t.investments.codexMana);
if (codexManaItem && (globalPlayerData.BaseBoosts['101'] || 0) >= 100) {
codexManaItem.name += \` <span style="opacity: 0.7;">(\${t.maxLevelWarning})</span>\`;
}
const codexDropItem = investments.find(item => item.name === t.investments.codexDrop);
console.log("values.codex.codexDropBoostBase: " + values.codex.codexDropBoostBase);
if (codexDropItem && values.codex.codexDropBoostBase >= 100) {
codexDropItem.name += \` <span style="opacity: 0.7;">(\${t.maxLevelWarning})</span>\`;
}
let resultHTML = \`
<div class="calc-result">
<div class="result-content-box">
<strong>\${t.combatROIComparison}</strong><br><br>
\`;
investments.forEach((item, index) => {
resultHTML += \`\${index + 1}. \${item.name}<br>\`;
if (item.cost > 0) {
resultHTML += \` \${t.cost} \${formatNumber(item.cost)} | \${t.extraDailyProfit} \${formatNumber(item.profit)}<br>\`;
resultHTML += \` ROI: \${item.roi.toFixed(2)}<br><br>\`;
} else {
resultHTML += \` \${t.extraDailyProfit}\${formatNumber(item.profit)}<br><br>\`;
}
});
resultHTML += \`</div></div>\`;
document.getElementById('calcResults').innerHTML = resultHTML;
}
// #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);
}
})();