// ==UserScript==
// @name Manarion helper
// @name:zh-CN Manarion 助手
// @name:en Manarion helper
// @namespace http://tampermonkey.net/
// @version 0.0.2.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==
(function() {
'use strict';
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>计算器</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.input-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
border-left: 4px solid #667eea;
}
.input-group {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 15px;
}
input[type="text"] {
flex: 1;
padding: 12px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s;
}
input[type="text"]:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
button {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: transform 0.2s, box-shadow 0.2s;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.usage-note {
background: #e3f2fd;
border: 1px solid #bbdefb;
border-radius: 6px;
padding: 10px;
margin-bottom: 15px;
color: #1565c0;
font-size: 14px;
}
.calc-input {
display: flex;
align-items: center;
margin: 10px 0;
gap: 10px;
padding-right: 600px;
}
.calc-input label {
min-width: 200px;
font-weight: 500;
}
.calc-input.with-select label {
min-width: 180px;
}
.calc-input.with-select select {
min-width: 100px;
margin-right: 10px;
}
.calc-button {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: transform 0.2s, box-shadow 0.2s;
min-width: 120px;
margin-left: auto;
}
.calc-button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.calc-button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
#gatheringType {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
#gatheringType2 {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
.player-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-bottom: 15px;
}
.info-card {
background: #ffffff;
border: 1px solid #e0e0e0;
padding: 12px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.info-card h3 {
margin: 0 0 10px 0;
font-size: 1.1em;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.stat-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 4px 0;
padding: 2px 0;
}
.stat-label {
font-size: 0.9em;
color: #666;
font-weight: 500;
min-width: 80px;
}
.stat-value {
font-weight: bold;
color: #333;
}
.stat-input {
width: 80px;
padding: 3px 6px;
border: 1px solid #ddd;
border-radius: 4px;
text-align: right;
font-size: 0.9em;
background: #f9f9f9;
}
.stat-input:focus {
outline: none;
border-color: #667eea;
background: white;
}
.loading {
text-align: center;
color: #667eea;
font-size: 1.2em;
padding: 20px;
}
.error {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #dc3545;
margin: 10px 0;
}
.success {
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #28a745;
margin: 10px 0;
}
.rarity-6 { background: linear-gradient(135deg, #ff6b6b, #ee5a24); }
.rarity-5 { background: linear-gradient(135deg, #ffa726, #fb8c00); }
.rarity-4 { background: linear-gradient(135deg, #ab47bc, #8e24aa); }
.rarity-3 { background: linear-gradient(135deg, #42a5f5, #1e88e5); }
.calculator-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-top: 20px;
border-left: 4px solid #28a745;
}
.calc-input {
margin: 10px 0;
}
.calc-result {
background: #e8f5e8;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
font-weight: bold;
color: #2e7d2e;
}
</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;">🏰 Manarion 数据计算器</h1>
<button id="langToggle" onclick="toggleLanguage()" style="
background: rgba(255, 255, 255, 0.9);
color: #667eea;
border: 2px solid #667eea;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s;
min-width: 60px;
" onmouseover="this.style.background='#667eea'; this.style.color='white';"
onmouseout="this.style.background='rgba(255, 255, 255, 0.9)'; this.style.color='#667eea';">
中/EN
</button>
</div>
<div class="input-section">
<h3>📊 获取玩家数据</h3>
<div class="input-group">
<input type="text" id="playerName" placeholder="输入玩家名称或ID" value="LemonApostle">
<button onclick="fetchPlayerData()">获取数据</button>
</div>
<div id="message"></div>
</div>
<div id="playerData" style="display:none;">
<div class="player-info">
<div class="info-card">
<h3>基本信息</h3>
<div id="basicInfo"></div>
</div>
<div class="info-card">
<h3>采集加成</h3>
<div id="gatheringBoosts"></div>
</div>
<div class="info-card">
<h3>法典加成</h3>
<div id="codexBoosts"></div>
</div>
<div class="info-card">
<h3>其他装备加成</h3>
<div id="otherEquipmentBoosts"></div>
</div>
<div class="info-card">
<h3>农场</h3>
<div id="farmBoosts"></div>
</div>
<div class="info-card">
<h3>市场价格</h3>
<div id="marketPrice"></div>
</div>
<div class="info-card">
<h3>需要自己补充的内容</h3>
<div id="needToSupplement"></div>
</div>
</div>
</div>
<div class="calculator-section">
<h3>🧮 数据计算器</h3>
<div class="usage-note">
<strong>使用说明:</strong> 填完必要的数据后,先点击"更新数据"按钮,再点击其他计算按钮。
</div>
<div class="calc-input">
<label>获取当前所有数据:</label>
<button class="calc-button" onclick="getAllCurrentValues()">更新数据</button>
</div>
<div class="calc-input">
<label>战斗计算:</label>
<button class="calc-button" onclick="calculateCombatEfficiency()">计算战斗效率</button>
</div>
<div class="calc-input with-select">
<label>采集效率计算:</label>
<select id="gatheringType">
<option value="mining">挖矿</option>
<option value="fishing">钓鱼</option>
<option value="woodcutting">伐木</option>
</select>
<button class="calc-button" onclick="calculateGatheringEfficiency()">计算采集效率</button>
</div>
<div class="calc-input">
<label>回响药水最佳等级计算:</label>
<button class="calc-button" onclick="calculateResonancePotionLevel()">计算等级</button>
</div>
<div class="calc-input with-select">
<label>采集投资回报率计算:</label>
<select id="gatheringType2">
<option value="mining">挖矿</option>
<option value="fishing">钓鱼</option>
<option value="woodcutting">伐木</option>
</select>
<button class="calc-button" onclick="calculateGatheringROI()">计算采集投资回报率</button>
</div>
<div class="calc-input">
<label>采集装备替换检测:</label>
<button class="calc-button" onclick="calculateGatherEquipReplace()">检测采集装备替换</button>
</div>
<div class="calc-input">
<label>战斗装备替换检测:</label>
<button class="calc-button" onclick="calculateBattlerEquipReplace()">检测战斗装备替换</button>
</div>
<div id="calcResults"></div>
</div>
</div>
<script>
let values = null;
let globalPlayerData = null;
let globalMarketData = null;
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');
}
}
function toggleLanguage() {
console.log('TODO')
}
async function fetchPlayerData() {
const playerName = document.getElementById('playerName').value.trim();
let data = null, marketData = null;
if (!playerName) {
showMessage('请输入玩家名称', 'error');
return;
}
showMessage('正在获取数据...', 'loading');
try {
const playerUrl = \`https://api.manarion.com/players/\${encodeURIComponent(playerName)}\`;
data = await window.fetchDataWithGM(playerUrl);
} catch (error) {
console.error('获取数据失败:', error);
showExampleData();
showMessage('无法获取数据。可能原因:1. API暂时不可用 2. 玩家名称不存在 3. 网络连接问题', 'error');
return;
}
marketData = await fetchMarketData();
showMessage('数据获取成功!', '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":673200000,"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);
if(displayExampleData){
showMessage('显示示例数据', 'success');
} else {
showMessage('加载已保存的数据 用户名: '+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';
document.getElementById('basicInfo').innerHTML = \`
<div class="stat-row"><span class="stat-label">玩家名:</span> <span class="stat-value">\${playerData.Name}</span></div>
<div class="stat-row"><span class="stat-label">当前敌人:</span> <input type="number" class="stat-input" value="\${playerData.Enemy || 0}" id="currentEnemy"></div>
<div class="stat-row"><span class="stat-label">战斗等级:</span> <input type="number" class="stat-input" value="\${playerData.Level || 0}" id="battleLevel"></div>
<div class="stat-row"><span class="stat-label">挖矿等级:</span> <input type="number" class="stat-input" value="\${playerData.MiningLevel || 0}" id="miningLevel"></div>
<div class="stat-row"><span class="stat-label">钓鱼等级:</span> <input type="number" class="stat-input" value="\${playerData.FishingLevel || 0}" id="fishingLevel"></div>
<div class="stat-row"><span class="stat-label">伐木等级:</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">挖矿:</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">钓鱼:</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">伐木:</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">基础资源量:</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">魔法尘:</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">基础资源量:</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">掉落加成:</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">魔法尘:</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">元素碎片:</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">药水效果:</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">收获傀儡:</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['130'] || 0}" id="harvestGolem"></div>
<div class="stat-row"><span class="stat-label">肥料:</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['131'] || 0}" id="fertilizer"></div>
<div class="stat-row"><span class="stat-label">地块:</span> <input type="number" class="stat-input" value="\${playerData.TotalBoosts['132'] || 0}" id="farmPlots"></div>
<div class="stat-row"><span class="stat-label">草药收获:</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">税收:</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">元素碎片:</span> <input type="number" class="stat-input" value="\${marketData.Sell['2'] || 0}" id="shardPrice"></div>
<div class="stat-row"><span class="stat-label">法典:</span> <input type="number" class="stat-input" value="\${marketData.Sell['3'] || 0}" id="codexPrice"></div>
<div class="stat-row"><span class="stat-label">鱼:</span> <input type="number" class="stat-input" value="\${marketData.Sell['7'] || 0}" id="fishPrice"></div>
<div class="stat-row"><span class="stat-label">木:</span> <input type="number" class="stat-input" value="\${marketData.Sell['8'] || 0}" id="woodPrice"></div>
<div class="stat-row"><span class="stat-label">铁:</span> <input type="number" class="stat-input" value="\${marketData.Sell['9'] || 0}" id="ironPrice"></div>
<div class="stat-row"><span class="stat-label">智慧之根:</span> <input type="number" class="stat-input" value="\${marketData.Sell['40'] || 0}" id="sagerootPrice"></div>
<div class="stat-row"><span class="stat-label">繁茂精华:</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">工会等级:</span> <input type="number" class="stat-input" value="\${values?.guild?.guildLevel || 100}" id="guildLevel"></div>
<div class="stat-row"><span class="stat-label">连结水晶等级:</span> <input type="number" class="stat-input" value="\${values?.guild?.guildNexusCrystalLevel || 100}" id="guildNexusCrystalLevel"></div>
<div class="stat-row"><span class="stat-label">工会魔法尘税率:</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">工会资源税率:</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">工会碎片税率:</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">智慧药水等级:</span> <input type="number" class="stat-input" value="\${values?.guild?.wisdomPotionLevel || 10}" id="wisdomPotionLevel"></div>
<div class="stat-row"><span class="stat-label">收获药水等级:</span> <input type="number" class="stat-input" value="\${values?.guild?.harvestingPotionLevel || 10}" id="harvestingPotionLevel"></div>
<div class="stat-row"><span class="stat-label">回响药水等级:</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')
}
};
document.getElementById('calcResults').innerHTML = \`
<div class="calc-result">
<h4>当前数据快照, 如有大出入,请检查这里的内容是否正确:</h4>
<div style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
<strong>基础信息:</strong><br>
战斗等级: \${values.basic.battleLevel}, 当前敌人: \${values.basic.currentEnemy}<br>
技能等级: 挖矿\${values.basic.miningLevel} 钓鱼\${values.basic.fishingLevel} 伐木\${values.basic.woodcuttingLevel}<br><br>
<strong>采集加成:</strong><br>
挖矿: \${values.gathering.miningBoost}%, 钓鱼: \${values.gathering.fishingBoost}%, 伐木: \${values.gathering.woodcuttingBoost}%<br>
基础资源量: \${values.gathering.baseResourceAmountTotal}<br><br>
<strong>法典加成:</strong><br>
魔法尘: \${values.codex.codexManaDust}%, 基础资源量: \${values.codex.codexBaseResource}%, 掉落加成: \${values.codex.codexDropBoost}%
<br><br>
<strong>装备加成:</strong><br>
魔法尘: \${values.equipment.manaBoost}%, 元素碎片: \${values.equipment.shardBoost}%, 药水效果: \${values.equipment.potionBoost}%<br><br>
<strong>农场系统:</strong><br>
收获傀儡: \${values.farm.harvestGolem}, 肥料: \${values.farm.fertilizer}, 地块: \${values.farm.farmPlots}<br>
草药收获: \${values.farm.farmHerbHarvest}/h, 税收: \${values.farm.farmTax}/h<br><br>
<strong>市场价格:</strong><br>
元素碎片: \${values.market.shardPrice}, 法典: \${values.market.codexPrice} <br>
鱼: \${values.market.fishPrice}, 木: \${values.market.woodPrice}, 铁: \${values.market.ironPrice}<br>
智慧之根: \${values.market.sagerootPrice}, 繁茂精华: \${values.market.bloomwellPrice}
<br><br>
<strong>手动输入信息:</strong><br>
工会等级: \${values.guild.guildLevel}, 连结水晶等级: \${values.guild.guildNexusCrystalLevel}<br>
工会魔法尘税率: \${values.guild.guildManaTax}%, 工会资源税率: \${values.guild.guildResonanceTax}%, 工会碎片税率: \${values.guild.guildShardTax}% <br>
智慧药水等级: \${values.guild.wisdomPotionLevel}, 收获药水等级: \${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(){
const herbsHourHarvestTotal = values.farm.farmHerbHarvest;
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;
// console.log(netSageRootGain,netBloomGain,sageProfitPerDay,bloomProfitPerDay);
return [sageRootHourHarvest, bloomHourHarvest, (wisdomPotionConsume + resonancePotionConsume), (harvestingPotionConsume + resonancePotionConsume), netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay];
}
//#region combat
function calculateCombatEfficiency(display = true){
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 [sageRootHourHarvest, bloomHourHarvest, sageRootHourConsume, bloomHourConsume, netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay] = calculateFarmIncome();
const farmProfitPerDay = sageProfitPerDay + bloomProfitPerDay;
const profitPerDayTotal = manaPerDayAfterTax + shardDailyValue + farmProfitPerDay;
if(display){
document.getElementById('calcResults').innerHTML = \`
<div class="calc-result">
<div style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
魔法尘每天 \${formatNumber(manaPerDayAfterTax)}<br><br>
税后每动作碎片范围 \${formatNumber(minShardPerActionAfterTax)} ~ \${formatNumber(2*minShardPerActionAfterTax)}<br>
碎片每天 \${formatNumber(shardPerActionAfterTax)}<br>
碎片每天价值 \${formatNumber(shardDailyValue)}<br><br>
慧根每小时产出 \${formatNumber(sageRootHourHarvest)} 繁茂每小时产出 \${formatNumber(bloomHourHarvest)}<br>
慧根每小时消耗 \${formatNumber(sageRootHourConsume)} 繁茂每小时消耗 \${formatNumber(bloomHourConsume)}<br>
慧根每小时净产出 \${formatNumber(netSageRootGain)} 繁茂每小时净产出 \${formatNumber(netBloomGain)}<br>
慧根每日净利 \${formatNumber(sageProfitPerDay)} 繁茂每日净利 \${formatNumber(bloomProfitPerDay)}<br>
农场每天价值 \${formatNumber(farmProfitPerDay)}<br><br>
每日收入 \${formatNumber(profitPerDayTotal)}<br>
</div>
</div>
\`;
}
return profitPerDayTotal;
}
//#region gathering
function calculateGatheringEfficiency(display = true, gatheringType = null){
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();
// (1 + lvl * 0.03 + base_res_amount) * (100% + base_res%) * (100% + guild_lvl_res%) * (100% + skill_boost)
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;
// console.log(level, baseResource, factor2, factor3, factor4);
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 [sageRootHourHarvest, bloomHourHarvest, sageRootHourConsume, bloomHourConsume, netSageRootGain, netBloomGain, sageProfitPerDay, bloomProfitPerDay] = calculateFarmIncome();
const farmProfitPerDay = sageProfitPerDay + bloomProfitPerDay;
const profitPerDayTotal = resourceProfitPerDay + shardDailyValue + farmProfitPerDay;
if(display){
document.getElementById('calcResults').innerHTML = \`
<div class="calc-result">
<div style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
资源每动作: \${formatNumber(resourcePerActionAfterTax)}<br>
资源每天: \${formatNumber(resourcePerDayAfterTax)}<br>
资源价格: \${formatNumber(resourcePrice)}<br>
资源每天价值: \${formatNumber(resourceProfitPerDay)}<br><br>
税后每动作碎片范围 \${formatNumber(minShardPerActionAfterTax)} ~ \${formatNumber(2*minShardPerActionAfterTax)}<br>
碎片每天 \${formatNumber(shardPerActionAfterTax)}<br>
碎片每天价值 \${formatNumber(shardDailyValue)}<br><br>
慧根每小时产出 \${formatNumber(sageRootHourHarvest)} 繁茂每小时产出 \${formatNumber(bloomHourHarvest)}<br>
慧根每小时消耗 \${formatNumber(sageRootHourConsume)} 繁茂每小时消耗 \${formatNumber(bloomHourConsume)}<br>
慧根每小时净产出 \${formatNumber(netSageRootGain)} 繁茂每小时净产出 \${formatNumber(netBloomGain)}<br>
慧根每日净利 \${formatNumber(sageProfitPerDay)} 繁茂每日净利 \${formatNumber(bloomProfitPerDay)}<br>
农场每天价值 \${formatNumber(farmProfitPerDay)}<br><br>
每日收入 \${formatNumber(profitPerDayTotal)}<br>
</div>
</div>
\`;
}
return profitPerDayTotal;
}
//#region ResonanceLevel
function calculateResonancePotionLevel(){
// 我们要计算最佳的碎片药水等级
// 具体说来就是,我们给定一个碎片药水等级 X,会有这个药水的农场每小时消耗,也会有这个药水可以额外带来的收益。
// 我们要计算的首先就是这么一个函数,看一看函数具体是什么形状的。
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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
最佳的碎片药水等级: \${finalOptimalLevel.toFixed(2)}<br><br>
相对0级药水<br>
额外的农场消耗量: \${formatNumber(getResonancePotionConsume(finalOptimalLevel))}/day<br>
额外的收益: \${formatNumber(getResonancePotionExtraIncome(finalOptimalLevel))}/day<br>
提升的收益: \${formatNumber(getResonancePotionExtraIncome(finalOptimalLevel)-getResonancePotionConsume(finalOptimalLevel))}<br>
</div>
</div>\`;
}
// region Gather ROI
function calculateGatheringROI(){
// 采集的投资回报率有以下几个方面的比较 0.0594 * 1000
// 1 提升 1000 级 research boost
// 2 提升 100 级 base resource amount
// 3 提升 5 级 codex base resource
// 4 提升 5 级 potion boost
// 5 提升 5 级 codex drop rate
// 6 总共提升 300 级 farm
const gatheringType = document.getElementById('gatheringType2').value;
const paths = {
mining: { base: 'miningBaseBoost', total: 'miningBoost' },
fishing: { base: 'fishingBaseBoost', total: 'fishingBoost' },
woodcutting: { base: 'woodcuttingBaseBoost', total: 'woodcuttingBoost' }
};
// 1
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 / 100;
values.gathering[path.total] = newBoostTotal;
const newProfit = calculateGatheringEfficiency(false,gatheringType);
values.gathering[path.total] = oldBoostTotal;
// console.log(\`research boost \${incrementLevel} level, \${shardsNeed} shards, \${formatNumber(priceNeed)} price, \${formatNumber(newProfit)} profit\`);
return [priceNeed, newProfit];
}
// 2
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);
values.gathering.baseResourceAmountTotal = oldBaseResourceAmoutTotal;
// console.log(\`base resource amount current Level \${currentLevel}, increment \${incrementLevel}, price need \${formatNumber(priceNeed)}, new profit \${formatNumber(newProfit)}\`);
return [priceNeed, newProfit];
}
// 3
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/100;
const newProfit = calculateGatheringEfficiency(false);
values.codex.codexBaseResource = oldCodexBaseResource;
// console.log(\`codex base resource current Level \${currentLevel}, increment \${incrementLevel}, codex need \${codexNeed}, price need \${formatNumber(priceNeed)}, new profit \${formatNumber(newProfit)}\`);
return [priceNeed, newProfit];
}
// 4
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/100;
const newProfit = calculateGatheringEfficiency(false);
values.equipment.potionBoost = oldPotionBoost;
// console.log(\`potion boost current Level \${currentLevel}, increment \${incrementLevel}, price need \${formatNumber(priceNeed)}, new profit \${formatNumber(newProfit)}\`);
return [priceNeed, newProfit];
}
// 5
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/100;
const newProfit = calculateGatheringEfficiency(false);
values.codex.codexDropBoost = oldCodexDropRate;
// console.log(\`codex drop rate current Level \${currentLevel}, increment \${incrementLevel}, codex need \${codexNeed}, price need \${formatNumber(priceNeed)}, new profit \${formatNumber(newProfit)}\`);
return [priceNeed, newProfit];
}
// 6
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);
values.farm.farmHerbHarvest = oldfarmHerbHarvest;
// console.log(\`farm level current Level \${currentLevels.harvestGolem}, \${currentLevels.fertilizer}, \${currentLevels.farmPlots}, increment \${incrementLevel}, price need \${formatNumber(priceNeed)}, new profit \${formatNumber(newProfit)}\`);
return [priceNeed, newProfit];
}
const currentProfit = calculateGatheringEfficiency(false);
// console.log(\`current profit \${currentProfit}\`);
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: "研究加成3000级", cost: research[0], profit: research[1]-currentProfit },
{ name: "基础资源100级", cost: baseResource[0], profit: baseResource[1]-currentProfit },
{ name: "法典基础5级", cost: codexBase[0], profit: codexBase[1]-currentProfit },
{ name: "药水加成5级", cost: potion[0], profit: potion[1]-currentProfit },
{ name: "法典掉落5级", cost: codexDrop[0], profit: codexDrop[1]-currentProfit },
{ name: "农场总共提升300级", cost: farm[0], profit: farm[1]-currentProfit }
];
// console.log(farm.priceNeed, farm.newProfit);
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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
<strong>\${gatheringType} 投资回报率对比:</strong><br><br>
\`;
investments.forEach((item, index) => {
resultHTML += \`\${index + 1}. \${item.name}<br>\`;
resultHTML += \` 成本: \${formatNumber(item.cost)} | 额外日收益: \${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(){
// 采集装备替换
// 采集装备替换逻辑:
// 1. 得到当前装备信息,当前使用的收获药水等级
// 2. 每次点击按钮检查可以替换多少 base amount。
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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
采集装备替换说明<br>
检查当前身上的装备,目前使用的收获药水等级,计算是否有基础资源装备可以被替换为元素碎片装备(每次只会计算最小数值的装备)<br><br>
当前没有找到基础资源加成装备,结束<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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
采集装备替换说明<br>
检查当前身上的装备,目前使用的收获药水等级,计算是否有基础资源装备可以被替换为元素碎片装备(每次只会计算最小数值的装备)<br>
注意:根据【计算采集效率】按钮前面的框的值决定采集类型<br><br>
当前使用的药水等级:\${currentHarvestingPotionLevel}。替换后使用的药水等级:\${minHarvestingPotionLevel}<br><br>
\`;
if(newProfit - oldProfit > potionPriceDiffer){
resultHTML += \`
当前基础资源加成装备:\${lowestEquip.Name} 可以被替换为 elemental shard 装备<br>
当前基础资源加成装备基础资源量: \${baseResourceAmoutLose} 可以被替换为元素碎片加成 \${(baseResourceAmoutLose*40).toFixed(2)}%<br>
替换后每日魔法尘增加 \${formatNumber(newProfit - oldProfit)}<br>
替换后每日农场支出增加 \${formatNumber(calculateHarvestingPotionCost(minHarvestingPotionLevel) - calculateHarvestingPotionCost(currentHarvestingPotionLevel))}<br>
替换后每日收入增加 \${formatNumber((newProfit - oldProfit) - (calculateHarvestingPotionCost(minHarvestingPotionLevel) - calculateHarvestingPotionCost(currentHarvestingPotionLevel)))}<br><br>
\`;
} else {
resultHTML += \`看起来现在的药水等级+基础资源装情况,替换不了呢。结束<br>\`;
}
resultHTML += \`</div></div>\`;
document.getElementById('calcResults').innerHTML = resultHTML;
}
// #region battlerEquip
function calculateBattlerEquipReplace(){
const BOOST_TYPES = {
120: "BATTLE_EXPERIENCE_BOOST",
121: "MANA_DUST_BOOST",
122: "ELEMENTAL_SHARD_BOOST",
123: "STAT_DROP"
};
function cheakReplaceEquip(playerData) {
// region find lowest
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;
}
// region analyze all equip
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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
战斗装备替换说明<br>
检查当前身上的装备,目前使用的智慧药水等级,计算是否有经验装备可以被替换为魔法尘/元素碎片装备(每次只会计算最小数值的装备)<br><br>
当前没有找到经验加成装备,结束<br>
</div>
</div>\`;
return null;
}
const EXPLost = lowestEquip.Boosts['120'] * (1 + (lowestEquip.Infusions * 0.05));
let { typeCount, typeBoosts } = analyzeCurrentEquipment(playerData);
// region to mana
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);
const targetLevel = Math.ceil(minTargetLevel);
return targetLevel;
}
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 style="font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 5px;">
战斗装备替换说明<br>
检查当前身上的装备,目前使用的智慧药水等级,计算是否有经验装备可以被替换为魔法尘装备(每次只会计算最小数值的装备)<br><br>
当前使用的药水等级:\${currentWisdomPotionLevel}。替换后使用的药水等级:\${targetWisdomPotionLevel}<br><br>
\`;
let replaceEquip = 0;
if ((newProfit - oldProfit) > (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))) {
resultHTML += \`
当前经验加成装备:\${lowestEquip.Name} 可以被替换为 mana dust 装备<br>
替换后每日魔法尘增加 \${formatNumber(newProfit - oldProfit)}<br>
替换后每日农场支出增加 \${formatNumber(calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))}<br>
替换后每日收入增加 \${formatNumber((newProfit - oldProfit) - (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel)))}<br><br>
\`;
replaceEquip = 1;
}
// region to shards
({ 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 += \`
当前经验加成装备:\${lowestEquip.Name} 可以被替换为 elemental shard 装备<br>
替换后每日魔法尘增加 \${formatNumber(newProfit - oldProfit)}<br>
替换后每日农场支出增加 \${formatNumber(calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel))}<br>
替换后每日收入增加 \${formatNumber((newProfit - oldProfit) - (calculateWisdomPotionCost(targetWisdomPotionLevel) - calculateWisdomPotionCost(currentWisdomPotionLevel)))}<br><br>
\`;
} else if(replaceEquip === 0) {
resultHTML += \`看起来现在的药水等级+经验装情况,替换不了呢。结束<br>\`;
}
resultHTML += \`</div></div>\`;
document.getElementById('calcResults').innerHTML = resultHTML;
}
cheakReplaceEquip(globalPlayerData);
}
window.onload = function() {
showMessage('点击"获取数据"按钮获取最新玩家数据,或查看下方示例数据', 'success');
loadGlobalData();
showExampleData();
};
window.addEventListener('beforeunload', function() {
saveGlobalData();
});
</script>
</body>
</html>`; }
// 按钮点击处理
function handleButtonClick() {
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) {
handleButtonClick();
}
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);
}
})();