// ==UserScript==
// @name 猫猫放置强化助手
// @version v3.1
// @description 强化自动化工具
// @author YuoHira
// @license MIT
// @match https://www.moyu-idle.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=moyu-idle.com
// @grant none
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/pako.min.js
// @namespace https://gf.qytechs.cn/users/397156
// ==/UserScript==
(function() {
'use strict';
// —— 默认配置 ——
let TARGET_LEVEL = 5; // 目标强化等级
let ENHANCE_INTERVAL = 1000; // 强化间隔(毫秒)
let isAutoEnhancing = false; // 自动强化状态
let enhanceTimer = null; // 强化定时器
let storedEnhanceData = null; // 储存的强化数据(完整的请求对象)
let currentEnhanceItem = null; // 当前强化物品信息
let DEBUG_MODE = false; // 调试模式(默认关闭)
let PROTECT_START_LEVEL = 3; // 从几级开始使用保护材料
let BATCH_COUNT = 1; // 批量强化次数
let currentBatchCount = 0; // 当前剩余强化次数
let waitingForResult = false; // 等待强化结果
// —— 强化统计数据 ——
let enhanceStats = {
baseItem: '', // 基础物品名称
currentLevel: 0, // 当前等级
targetLevel: 0, // 目标等级
maxReachedLevel: 0, // 历史最大等级
levelStats: {}, // 每级统计 {level: {attempts: 0, success: 0}}
totalAttempts: 0, // 总尝试次数
totalSuccess: 0, // 总成功次数
startTime: null // 开始时间
};
// —— 本地存储键名 ——
const STORAGE_KEYS = {
POSITION: 'enhanceHelper_position',
TARGET_LEVEL: 'enhanceHelper_targetLevel',
INTERVAL: 'enhanceHelper_interval',
CURRENT_ITEM: 'enhanceHelper_currentItem',
STORED_REQUEST: 'enhanceHelper_storedRequest',
DEBUG_MODE: 'enhanceHelper_debugMode',
PROTECT_START_LEVEL: 'enhanceHelper_protectStartLevel',
IS_MINIMIZED: 'enhanceHelper_isMinimized',
BATCH_COUNT: 'enhanceHelper_batchCount',
CURRENT_BATCH_COUNT: 'enhanceHelper_currentBatchCount'
};
// —— 界面状态 ——
let isMinimized = false;
// —— 加载本地配置 ——
function loadConfig() {
const savedTarget = localStorage.getItem(STORAGE_KEYS.TARGET_LEVEL);
const savedInterval = localStorage.getItem(STORAGE_KEYS.INTERVAL);
const savedCurrentItem = localStorage.getItem(STORAGE_KEYS.CURRENT_ITEM);
const savedStoredRequest = localStorage.getItem(STORAGE_KEYS.STORED_REQUEST);
const savedDebugMode = localStorage.getItem(STORAGE_KEYS.DEBUG_MODE);
const savedProtectStartLevel = localStorage.getItem(STORAGE_KEYS.PROTECT_START_LEVEL);
const savedIsMinimized = localStorage.getItem(STORAGE_KEYS.IS_MINIMIZED);
const savedBatchCount = localStorage.getItem(STORAGE_KEYS.BATCH_COUNT);
const savedCurrentBatchCount = localStorage.getItem(STORAGE_KEYS.CURRENT_BATCH_COUNT);
if (savedTarget) TARGET_LEVEL = parseInt(savedTarget, 10);
if (savedInterval) ENHANCE_INTERVAL = parseInt(savedInterval, 10);
if (savedDebugMode) DEBUG_MODE = savedDebugMode === 'true';
if (savedProtectStartLevel) PROTECT_START_LEVEL = parseInt(savedProtectStartLevel, 10);
if (savedIsMinimized) isMinimized = savedIsMinimized === 'true';
if (savedBatchCount) BATCH_COUNT = parseInt(savedBatchCount, 10);
if (savedCurrentBatchCount) currentBatchCount = parseInt(savedCurrentBatchCount, 10);
// 恢复当前强化物品
if (savedCurrentItem) {
try {
currentEnhanceItem = JSON.parse(savedCurrentItem);
} catch (e) {
currentEnhanceItem = null;
}
}
// 恢复储存的请求数据
if (savedStoredRequest) {
try {
storedEnhanceData = JSON.parse(savedStoredRequest);
} catch (e) {
storedEnhanceData = null;
}
}
}
// —— 保存配置 ——
function saveConfig() {
localStorage.setItem(STORAGE_KEYS.TARGET_LEVEL, TARGET_LEVEL);
localStorage.setItem(STORAGE_KEYS.INTERVAL, ENHANCE_INTERVAL);
localStorage.setItem(STORAGE_KEYS.DEBUG_MODE, DEBUG_MODE);
localStorage.setItem(STORAGE_KEYS.PROTECT_START_LEVEL, PROTECT_START_LEVEL);
localStorage.setItem(STORAGE_KEYS.IS_MINIMIZED, isMinimized);
localStorage.setItem(STORAGE_KEYS.BATCH_COUNT, BATCH_COUNT);
localStorage.setItem(STORAGE_KEYS.CURRENT_BATCH_COUNT, currentBatchCount);
}
// —— 保存当前强化物品 ——
function saveCurrentItem() {
if (currentEnhanceItem) {
localStorage.setItem(STORAGE_KEYS.CURRENT_ITEM, JSON.stringify(currentEnhanceItem));
} else {
localStorage.removeItem(STORAGE_KEYS.CURRENT_ITEM);
}
}
// —— 保存强化请求数据 ——
function saveStoredRequest() {
if (storedEnhanceData) {
localStorage.setItem(STORAGE_KEYS.STORED_REQUEST, JSON.stringify(storedEnhanceData));
} else {
localStorage.removeItem(STORAGE_KEYS.STORED_REQUEST);
}
}
// —— 边界检查函数 ——
function constrainPosition(x, y, panelWidth = 480, panelHeight = 400) {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// 确保面板至少有50px在可见范围内
const minVisible = 50;
// 检查右边界(x是right值)
if (x < 0) {
x = 20; // 默认右边距
} else if (x > windowWidth - minVisible) {
x = windowWidth - minVisible;
}
// 检查上下边界(y是top值)
if (y < 0) {
y = 20; // 默认上边距
} else if (y > windowHeight - minVisible) {
y = windowHeight - minVisible;
}
return {x, y};
}
// —— 保存位置 ——
function savePosition(x, y) {
const constrained = constrainPosition(x, y);
localStorage.setItem(STORAGE_KEYS.POSITION, JSON.stringify(constrained));
}
// —— 加载位置 ——
function loadPosition() {
const saved = localStorage.getItem(STORAGE_KEYS.POSITION);
let position = {x: 20, y: 20}; // 默认位置
if (saved) {
try {
position = JSON.parse(saved);
} catch (e) {
position = {x: 20, y: 20};
}
}
// 加载时也进行边界检查,防止窗口大小改变后面板跑到屏幕外
return constrainPosition(position.x, position.y);
}
// —— 辅助:检测压缩格式 ——
function detectCompression(buf) {
const b = new Uint8Array(buf);
if (b.length >= 2) {
if (b[0] === 0x1f && b[1] === 0x8b) return 'gzip';
if (b[0] === 0x78 && (((b[0] << 8) | b[1]) % 31) === 0) return 'zlib';
}
return 'deflate';
}
// —— 判断是否为强化请求 ——
function isEnhanceRequest(data) {
if (typeof data === 'string') {
try {
// 检查是否包含 enhance:require 事件
return data.includes('"enhance:require"') || data.includes('enhance:require');
} catch (e) {
return false;
}
}
return false;
}
// —— 解析强化数据 ——
function parseEnhanceData(data) {
try {
if (typeof data === 'string') {
// 尝试解析Socket.IO格式的数据
const match = data.match(/\["enhance:require",(.+)\]/);
if (match) {
const payload = JSON.parse(match[1]);
if (payload.data && payload.data.resourceId) {
return {
resourceId: payload.data.resourceId,
protectedResourceId: payload.data.protectedResourceId || null,
user: payload.user ? payload.user.name : 'Unknown',
fullPayload: payload // 保存完整的payload用于重构请求
};
}
}
}
} catch (e) {
// 解析失败,忽略
}
return null;
}
// —— 解析强化结果 ——
function parseEnhanceResult(text) {
try {
const data = JSON.parse(text);
if (data.data && data.data.hasOwnProperty('success') && data.data.enhanceResultId) {
return {
success: data.data.success,
message: data.data.msg,
resultId: data.data.enhanceResultId,
user: data.user ? data.user.name : 'Unknown'
};
}
} catch (e) {
// 不是强化结果,忽略
}
return null;
}
// —— 解析物品等级 ——
function parseItemLevel(itemId) {
const match = itemId.match(/(.+?)\+(\d+)$/);
if (match) {
return {
baseItem: match[1],
level: parseInt(match[2], 10)
};
}
return {
baseItem: itemId,
level: 0
};
}
// —— 初始化统计数据 ——
function initStats(itemId, targetLevel) {
const parsed = parseItemLevel(itemId);
enhanceStats = {
baseItem: parsed.baseItem,
currentLevel: parsed.level,
targetLevel: targetLevel,
maxReachedLevel: parsed.level, // 初始化为当前等级
levelStats: {},
totalAttempts: 0,
totalSuccess: 0,
startTime: Date.now()
};
// 初始化每级统计 - 始终从+1到目标等级
for (let i = 0; i < targetLevel; i++) {
enhanceStats.levelStats[i] = { attempts: 0, success: 0 };
}
}
// —— 更新统计数据 ——
function updateStats(result) {
// 解析结果物品等级
const resultItem = parseItemLevel(result.resultId);
// 记录这次尝试
const attemptLevel = enhanceStats.currentLevel;
const levelStats = enhanceStats.levelStats[attemptLevel];
if (levelStats) {
levelStats.attempts++;
enhanceStats.totalAttempts++;
if (result.success) {
levelStats.success++;
enhanceStats.totalSuccess++;
}
}
// 更新当前等级为结果等级
enhanceStats.currentLevel = resultItem.level;
// 更新历史最大等级
if (resultItem.level > enhanceStats.maxReachedLevel) {
enhanceStats.maxReachedLevel = resultItem.level;
}
updateStatsDisplay();
}
// —— 自动强化函数 ——
function startAutoEnhance(ws) {
if (enhanceTimer) {
clearTimeout(enhanceTimer);
}
// 发送第一次强化请求
sendEnhanceRequest(ws);
}
// —— 发送强化请求 ——
function sendEnhanceRequest(ws) {
if (!isAutoEnhancing || !ws || ws.readyState !== WebSocket.OPEN || !currentEnhanceItem || !storedEnhanceData) {
return;
}
// 检查是否达到目标等级
if (enhanceStats.currentLevel >= enhanceStats.targetLevel) {
// 获取当前输入框的批量次数
const currentInputValue = parseInt(batchCountInput.value, 10);
// 批量次数-1,直接修改输入框的值
if (currentInputValue > 1) {
batchCountInput.value = currentInputValue - 1;
BATCH_COUNT = currentInputValue - 1; // 同步内部变量
currentBatchCount = currentInputValue - 1; // 同步当前批量次数
saveConfig(); // 保存配置
// 获取基础物品ID(去掉+数字后缀)
const currentItemInfo = parseItemLevel(currentEnhanceItem.resourceId);
const baseItemId = currentItemInfo.baseItem; // 基础物品ID,不包含+数字
// 更新当前强化物品为基础版本
currentEnhanceItem.resourceId = baseItemId;
saveCurrentItem(); // 保存更新后的物品信息
// 重置统计数据,准备下一轮强化(使用基础物品ID)
initStats(baseItemId, TARGET_LEVEL);
updateStatsDisplay();
updateItemDisplay(currentEnhanceItem, `批量剩余: ${currentInputValue - 1}`);
updateMinimizedDisplay();
// Debug模式记录批量循环信息
if (DEBUG_MODE) {
console.group('🔄 [强化助手] 批量强化循环');
console.log('批量信息:', {
完成轮次: BATCH_COUNT - (currentInputValue - 1) + 1,
剩余次数: currentInputValue - 1,
基础物品ID: baseItemId,
原强化物品ID: enhanceStats.baseItem + '+' + enhanceStats.targetLevel,
时间: new Date().toLocaleTimeString()
});
console.groupEnd();
}
// 继续发送强化请求
setTimeout(() => {
sendEnhanceRequest(ws);
}, ENHANCE_INTERVAL);
return;
}
// 批量完成,停止自动强化并更新UI状态
isAutoEnhancing = false;
stopAutoEnhance();
updateItemDisplay(currentEnhanceItem, '批量完成');
updateToggleButtonState();
updateMinimizedDisplay();
return;
}
// 构造当前物品的强化请求,使用完整保存的数据
const requestData = {
user: storedEnhanceData.user,
data: {
resourceId: currentEnhanceItem.resourceId
}
};
// 根据当前等级和设置决定是否使用保护材料
if (storedEnhanceData.data && storedEnhanceData.data.protectedResourceId &&
enhanceStats.currentLevel >= PROTECT_START_LEVEL) {
requestData.data.protectedResourceId = storedEnhanceData.data.protectedResourceId;
}
const enhanceRequest = `42["enhance:require",${JSON.stringify(requestData)}]`;
// Debug模式记录自动强化请求
if (DEBUG_MODE) {
console.group('🤖 [强化助手] 发送自动强化请求');
console.log('请求数据:', enhanceRequest);
console.log('构造的请求对象:', requestData);
console.log('目标物品:', {
物品ID: currentEnhanceItem.resourceId,
保护材料ID: requestData.data.protectedResourceId || '无',
当前等级: enhanceStats.currentLevel,
目标等级: enhanceStats.targetLevel,
批量进度: `${BATCH_COUNT - currentBatchCount + 1}/${BATCH_COUNT}`,
剩余次数: currentBatchCount,
保护设置: `≥${PROTECT_START_LEVEL}级使用`,
实际使用保护: requestData.data.protectedResourceId ? '是' : '否',
用户: requestData.user.name,
时间: new Date().toLocaleTimeString()
});
console.groupEnd();
}
waitingForResult = true;
ws.__originalSend(enhanceRequest);
}
// —— 处理强化结果 ——
function handleEnhanceResult(result) {
waitingForResult = false;
// 总是更新当前强化物品为结果物品(不管成功失败)
const wasFirstCapture = !currentEnhanceItem;
currentEnhanceItem = {
resourceId: result.resultId,
user: result.user
};
// 保存到本地存储
saveCurrentItem();
// 如果是第一次捕获物品,启用按钮
if (wasFirstCapture) {
updateItemDisplay(currentEnhanceItem);
} else {
// 更新UI显示当前物品
updateItemDisplay(currentEnhanceItem);
// 更新统计数据(只有在自动强化时才统计)
if (isAutoEnhancing) {
updateStats(result);
}
}
// 如果还在自动强化模式,延迟后继续
if (isAutoEnhancing) {
enhanceTimer = setTimeout(() => {
sendEnhanceRequest(window.currentWS);
}, ENHANCE_INTERVAL);
}
}
function stopAutoEnhance() {
if (enhanceTimer) {
clearTimeout(enhanceTimer);
enhanceTimer = null;
}
waitingForResult = false;
}
// —— 初始化配置 ——
loadConfig();
// Debug模式启动提示
if (DEBUG_MODE) {
console.log('🐛 [强化助手] 调试模式已启用,将记录所有WebSocket强化请求和结果');
}
// 如果有恢复的物品,延迟更新UI显示
if (currentEnhanceItem) {
setTimeout(() => {
updateItemDisplay(currentEnhanceItem);
}, 100);
}
// 初始化保护显示
setTimeout(() => {
updateProtectDisplay();
// 初始化保护等级提示
if (PROTECT_START_LEVEL === 0) {
protectLevelHint.textContent = '不使用保护';
protectLevelHint.style.color = '#f44336';
} else {
protectLevelHint.textContent = `≥${PROTECT_START_LEVEL}级使用保护`;
protectLevelHint.style.color = '#aaa';
}
// 如果有恢复的物品,更新显示
if (currentEnhanceItem) {
updateItemDisplay(currentEnhanceItem);
}
// 如果页面加载时是收起状态,调整面板宽度和显示状态
if (isMinimized) {
panel.style.width = '280px';
titleBar.style.display = 'none';
mainContent.style.display = 'none';
minimizedBar.style.display = 'flex';
}
// 初始化收起状态显示
updateMinimizedDisplay();
}, 100);
// —— 创建浮动控制面板 ——
const panel = document.createElement('div');
panel.id = 'enhanceHelperPanel'; // 添加唯一ID
const savedPos = loadPosition();
panel.style.cssText = `
position: fixed; top: ${savedPos.y}px; right: ${savedPos.x}px;
width: 480px; padding: 12px;
background: rgba(25,35,45,0.95); color: #fff;
font-family: 'Consolas', 'Monaco', monospace; font-size: 12px;
border-radius: 10px; z-index: 9999;
cursor: move; border: 1px solid rgba(100,200,255,0.3);
box-shadow: 0 6px 25px rgba(0,0,0,0.4);
backdrop-filter: blur(5px);
`;
panel.innerHTML = `
<div id="enhanceHelper_titleBar" style="cursor: default; margin-bottom:10px; font-weight:bold; color:#64B5F6; display:${isMinimized ? 'none' : 'flex'}; align-items:center;">
<span>🛠️ 强化助手</span>
<div style="margin-left:auto; display:flex; align-items:center; gap:6px;">
<button id="enhanceHelper_minimizeBtn" style="
padding:2px 6px; background:rgba(100,200,255,0.2); border:none; border-radius:3px;
color:#64B5F6; cursor:pointer; font-size:10px;
">${isMinimized ? '📋' : '📌'}</button>
<div style="font-size:10px; color:#888;">v2.5</div>
</div>
</div>
<!-- 收起状态的小横条 -->
<div id="enhanceHelper_minimizedBar" style="display:${isMinimized ? 'flex' : 'none'};
background:rgba(0,0,0,0.5); padding:6px 8px; border-radius:6px; margin-bottom:8px;
font-size:11px; color:#FFA726; align-items:center; justify-content:space-between;">
<div id="enhanceHelper_minimizedStatus">等待强化结果...</div>
<button id="enhanceHelper_expandBtn" style="
padding:2px 6px; background:rgba(100,200,255,0.2); border:none; border-radius:3px;
color:#64B5F6; cursor:pointer; font-size:10px; margin-left:8px;
">📋</button>
</div>
<div id="enhanceHelper_mainContent" style="display:${isMinimized ? 'none' : 'block'};">
<!-- 主要内容区域:左右分布 -->
<div style="display: flex; gap: 12px;">
<!-- 左侧控制区 -->
<div style="flex: 1; min-width: 220px;">
<div style="background:rgba(0,0,0,0.3); padding:8px; border-radius:6px; margin-bottom:8px;">
<div style="font-size:10px; color:#aaa; margin-bottom:4px;">目标物品:</div>
<div id="enhanceHelper_itemDisplay" style="color:#FFA726; font-weight:bold; min-height:16px;">
等待强化结果...
</div>
<div id="enhanceHelper_protectMaterialDisplay" style="font-size:9px; color:#81C784; margin-top:4px; min-height:12px;">
<!-- 保护材料信息 -->
</div>
</div>
<div style="display:flex; gap:8px; margin-bottom:8px;">
<label style="flex:1;">
目标等级:
<input id="enhanceHelper_targetInput" type="number" min="1" max="15" value="${TARGET_LEVEL}"
style="width:100%; padding:4px; border-radius:4px; border:none; background:rgba(255,255,255,0.1); color:#fff; margin-top:2px;">
</label>
<label style="flex:1;">
间隔(ms):
<input id="enhanceHelper_intervalInput" type="number" min="100" value="${ENHANCE_INTERVAL}"
style="width:100%; padding:4px; border-radius:4px; border:none; background:rgba(255,255,255,0.1); color:#fff; margin-top:2px;">
</label>
</div>
<div style="display:flex; gap:8px; margin-bottom:8px;">
<label style="flex:1;">
批量次数:
<input id="enhanceHelper_batchCountInput" type="number" min="1" max="99" value="${BATCH_COUNT}"
style="width:100%; padding:4px; border-radius:4px; border:none; background:rgba(255,255,255,0.1); color:#fff; margin-top:2px;"
title="连续强化多少个物品到目标等级">
</label>
<div style="flex:1; font-size:10px; color:#aaa; padding:4px;">
<div style="font-size:9px; color:#666;">剩余次数会自动减少</div>
<div style="font-size:9px; color:#666;">1=单次强化</div>
</div>
</div>
<div style="display:flex; gap:8px; margin-bottom:8px;">
<label style="flex:1;">
保护等级:
<input id="enhanceHelper_protectStartLevelInput" type="number" min="0" max="15" value="${PROTECT_START_LEVEL}"
style="width:100%; padding:4px; border-radius:4px; border:none; background:rgba(255,255,255,0.1); color:#fff; margin-top:2px;"
title="从几级开始使用保护材料 (0=不使用)">
</label>
<div style="flex:1; font-size:10px; color:#aaa; padding:4px;">
<div id="enhanceHelper_protectLevelHint">≥${PROTECT_START_LEVEL}级使用保护</div>
<div style="font-size:9px; color:#666;">0=不使用保护</div>
</div>
</div>
<div style="margin-bottom:8px;">
<label style="display:flex; align-items:center; font-size:11px; color:#aaa; cursor:pointer;">
<input id="enhanceHelper_debugModeCheckbox" type="checkbox" ${DEBUG_MODE ? 'checked' : ''}
style="margin-right:6px; transform:scale(0.9);">
<span>🐛 调试模式 (记录WS强化请求)</span>
</label>
</div>
<button id="enhanceHelper_toggleBtn" style="
width:100%; padding:10px;
background:linear-gradient(45deg, #4CAF50, #45a049); color:white; border:none;
border-radius:6px; cursor:pointer; font-size:13px; font-weight:bold;
transition: all 0.3s ease;
" disabled>🚀 开始强化</button>
<div style="display:flex; justify-content:space-between; font-size:10px; color:#aaa; margin-top:8px;">
<span>状态: <span id="enhanceHelper_status" style="color:#FFA726;">待机中</span></span>
<span id="enhanceHelper_counter">就绪</span>
</div>
<button id="enhanceHelper_clearDataBtn" style="
width:100%; padding:6px; margin-top:6px;
background:rgba(244,67,54,0.8); color:white; border:none;
border-radius:4px; cursor:pointer; font-size:10px;
transition: all 0.3s ease;
">🗑️ 清除保存数据</button>
</div>
<!-- 右侧统计区 -->
<div style="flex: 1; min-width: 220px;">
<div style="background:rgba(0,0,0,0.3); padding:8px; border-radius:6px; height: 100%;">
<div style="font-size:10px; color:#aaa; margin-bottom:6px; display:flex; align-items:center;">
<span>📊 强化统计</span>
</div>
<div id="enhanceHelper_statsDisplay" style="font-size:10px; color:#FFA726;">
等待开始强化...
</div>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(panel);
// —— 获取控制元素 ——
const targetInput = document.getElementById('enhanceHelper_targetInput');
const intervalInput = document.getElementById('enhanceHelper_intervalInput');
const batchCountInput = document.getElementById('enhanceHelper_batchCountInput');
const protectStartLevelInput = document.getElementById('enhanceHelper_protectStartLevelInput');
const debugCheckbox = document.getElementById('enhanceHelper_debugModeCheckbox');
const toggleBtn = document.getElementById('enhanceHelper_toggleBtn');
const statusSpan = document.getElementById('enhanceHelper_status');
const itemDisplay = document.getElementById('enhanceHelper_itemDisplay');
const protectMaterialDisplay = document.getElementById('enhanceHelper_protectMaterialDisplay');
const protectLevelHint = document.getElementById('enhanceHelper_protectLevelHint');
const counterSpan = document.getElementById('enhanceHelper_counter');
const statsDisplay = document.getElementById('enhanceHelper_statsDisplay');
const clearDataBtn = document.getElementById('enhanceHelper_clearDataBtn');
// 收起/展开相关元素
const titleBar = document.getElementById('enhanceHelper_titleBar');
const minimizeBtn = document.getElementById('enhanceHelper_minimizeBtn');
const expandBtn = document.getElementById('enhanceHelper_expandBtn');
const mainContent = document.getElementById('enhanceHelper_mainContent');
const minimizedBar = document.getElementById('enhanceHelper_minimizedBar');
const minimizedStatus = document.getElementById('enhanceHelper_minimizedStatus');
// —— 拖拽逻辑 ——
(function makeDraggable(el) {
let isDown = false, offsetX = 0, offsetY = 0, hasMoved = false;
el.addEventListener('mousedown', e => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
isDown = true;
hasMoved = false;
offsetX = e.clientX - el.offsetLeft;
offsetY = e.clientY - el.offsetTop;
});
document.addEventListener('mousemove', e => {
if (!isDown) return;
hasMoved = true;
const newX = e.clientX - offsetX;
const newY = e.clientY - offsetY;
el.style.left = newX + 'px';
el.style.top = newY + 'px';
el.style.right = 'auto'; // 拖拽时清除right定位
});
document.addEventListener('mouseup', () => {
if (isDown && hasMoved) {
// 只有真正拖拽过才应用边界检查
setTimeout(() => {
const rect = el.getBoundingClientRect();
const rightDistance = window.innerWidth - rect.right;
const topDistance = rect.top;
// 保存并应用边界约束后的位置
const constrained = constrainPosition(rightDistance, topDistance);
savePosition(constrained.x, constrained.y);
// 重新设置为right定位
el.style.right = constrained.x + 'px';
el.style.top = constrained.y + 'px';
el.style.left = 'auto';
}, 0); // 使用setTimeout避免干扰点击事件
}
isDown = false;
hasMoved = false;
});
})(panel);
// —— 更新保护材料显示 ——
function updateProtectDisplay() {
if (storedEnhanceData && storedEnhanceData.data && storedEnhanceData.data.protectedResourceId) {
const protectMaterial = storedEnhanceData.data.protectedResourceId;
if (PROTECT_START_LEVEL === 0) {
protectMaterialDisplay.innerHTML = `🛡️ 保护: ${protectMaterial} <span style="color:#f44336;">(已禁用)</span>`;
} else {
protectMaterialDisplay.innerHTML = `🛡️ 保护: ${protectMaterial} <span style="color:#64B5F6;">(≥${PROTECT_START_LEVEL}级)</span>`;
}
} else {
protectMaterialDisplay.textContent = '无保护材料';
}
}
// —— 更新UI显示 ——
function updateItemDisplay(itemInfo, customStatus = null) {
if (itemInfo) {
itemDisplay.textContent = itemInfo.resourceId; // 保持英文原始名称
itemDisplay.title = `用户: ${itemInfo.user}`;
toggleBtn.disabled = false;
toggleBtn.style.opacity = '1';
statusSpan.textContent = customStatus || '就绪';
statusSpan.style.color = customStatus === '已完成' ? '#4CAF50' : '#4CAF50';
counterSpan.textContent = customStatus === '已完成' ? '完成' : '已捕获';
} else {
itemDisplay.textContent = '等待强化结果...';
itemDisplay.title = '';
toggleBtn.disabled = true;
toggleBtn.style.opacity = '0.5';
statusSpan.textContent = '等待中';
statusSpan.style.color = '#FFA726';
counterSpan.textContent = '就绪';
}
// 更新保护材料显示
updateProtectDisplay();
// 更新收起状态显示
updateMinimizedDisplay();
}
// —— 更新统计显示 ——
function updateStatsDisplay() {
if (!enhanceStats.baseItem) {
statsDisplay.innerHTML = '等待开始强化...';
return;
}
const totalRate = enhanceStats.totalAttempts > 0 ?
(enhanceStats.totalSuccess / enhanceStats.totalAttempts * 100).toFixed(1) : '0.0';
let html = `
<div style="margin-bottom:6px; padding-bottom:6px; border-bottom:1px solid rgba(255,255,255,0.1);">
<div style="color:#64B5F6; font-weight:bold; margin-bottom:2px;">${enhanceStats.baseItem}</div>
<div style="font-size:11px; color:#FFA726;">
进度: Lv${enhanceStats.currentLevel}/${enhanceStats.targetLevel} |
总计: ${enhanceStats.totalAttempts}次 (${totalRate}%)
</div>
</div>
`;
// 显示每级详细统计 - 每级一行,从高到低排序
const levels = Object.keys(enhanceStats.levelStats).sort((a, b) => parseInt(b) - parseInt(a));
if (levels.length > 0) {
html += '<div>';
levels.forEach(level => {
const stats = enhanceStats.levelStats[level];
const levelNum = parseInt(level);
const targetLevelNum = levelNum + 1;
const rate = stats.attempts > 0 ? (stats.success / stats.attempts * 100).toFixed(1) : '0.0';
const targetLevel = levelNum + 1; // 目标等级 (Lv1, Lv2, etc.)
const currentItemLevel = enhanceStats.currentLevel; // 当前物品等级
const maxReachedLevel = enhanceStats.maxReachedLevel; // 历史最大等级
// 确定显示样式
let bgColor, textColor;
if (targetLevel === currentItemLevel + 1) {
// 当前正在强化的目标等级(蓝色)- 优先级最高
bgColor = 'rgba(100,181,246,0.2)';
textColor = '#64B5F6';
} else if (targetLevel <= maxReachedLevel) {
// 历史上到过的等级(绿色)
bgColor = 'rgba(76,175,80,0.1)';
textColor = '#81C784';
} else {
// 还没到过的等级(灰色)
bgColor = 'transparent';
textColor = '#666';
}
const displayText = `${stats.attempts}次 (${rate}%)`;
html += `
<div style="
display:flex; justify-content:space-between; align-items:center;
padding:2px 4px; margin:1px 0; border-radius:3px;
background:${bgColor}; font-size:9px; color:${textColor};
">
<span>Lv${targetLevelNum}</span>
<span>${displayText}</span>
</div>
`;
});
html += '</div>';
}
statsDisplay.innerHTML = html;
}
// —— 收起/展开功能 ——
function toggleMinimize() {
isMinimized = !isMinimized;
if (isMinimized) {
titleBar.style.display = 'none';
mainContent.style.display = 'none';
minimizedBar.style.display = 'flex';
panel.style.width = '280px';
} else {
titleBar.style.display = 'flex';
mainContent.style.display = 'block';
minimizedBar.style.display = 'none';
minimizeBtn.textContent = '📌';
panel.style.width = '480px';
}
saveConfig();
updateMinimizedDisplay();
}
// —— 更新收起状态显示 ——
function updateMinimizedDisplay() {
if (!isMinimized) return;
let statusText = '等待强化结果...';
if (currentEnhanceItem) {
const itemInfo = parseItemLevel(currentEnhanceItem.resourceId);
const baseItem = itemInfo.baseItem.length > 15 ? itemInfo.baseItem.substring(0, 15) + '..' : itemInfo.baseItem;
const currentLevel = itemInfo.level;
if (isAutoEnhancing) {
// 获取当前输入框的剩余次数
const remainingCount = parseInt(batchCountInput.value, 10);
const batchInfo = remainingCount > 1 ? ` [剩余${remainingCount}次]` : '';
statusText = `🔨 ${baseItem} +${currentLevel} → +${TARGET_LEVEL}${batchInfo}`;
} else {
const status = enhanceStats.currentLevel >= enhanceStats.targetLevel ? '已完成' : '就绪';
statusText = `${status}: ${baseItem} +${currentLevel}`;
if (status === '已完成') {
statusText = `✅ ${baseItem} +${currentLevel} 已完成`;
}
}
}
minimizedStatus.textContent = statusText;
}
// —— 更新按钮状态 ——
function updateToggleButtonState() {
if (isAutoEnhancing) {
toggleBtn.innerHTML = '⏹️ 停止强化';
toggleBtn.style.background = 'linear-gradient(45deg, #f44336, #d32f2f)';
statusSpan.textContent = '运行中';
statusSpan.style.color = '#4CAF50';
counterSpan.textContent = '活动中';
} else {
toggleBtn.innerHTML = '🚀 开始强化';
toggleBtn.style.background = 'linear-gradient(45deg, #4CAF50, #45a049)';
if (enhanceStats.currentLevel >= enhanceStats.targetLevel && enhanceStats.baseItem) {
statusSpan.textContent = '已完成';
statusSpan.style.color = '#4CAF50';
counterSpan.textContent = '完成';
} else {
statusSpan.textContent = '已停止';
statusSpan.style.color = '#f44336';
counterSpan.textContent = '就绪';
}
}
}
// —— 双击标题栏重置位置 ——
titleBar.addEventListener('dblclick', () => {
const defaultPos = {x: 20, y: 20};
panel.style.right = defaultPos.x + 'px';
panel.style.top = defaultPos.y + 'px';
panel.style.left = 'auto';
savePosition(defaultPos.x, defaultPos.y);
// 显示重置提示
const oldTitle = titleBar.querySelector('span').textContent;
titleBar.querySelector('span').textContent = '🔄 位置已重置';
setTimeout(() => {
titleBar.querySelector('span').textContent = oldTitle;
}, 1000);
});
// —— 事件监听器 ——
targetInput.addEventListener('change', e => {
const v = parseInt(e.target.value, 10);
if (!isNaN(v) && v > 0 && v <= 15) {
TARGET_LEVEL = v;
saveConfig();
}
});
intervalInput.addEventListener('change', e => {
const v = parseInt(e.target.value, 10);
if (!isNaN(v) && v >= 100) {
ENHANCE_INTERVAL = v;
saveConfig();
}
});
batchCountInput.addEventListener('change', e => {
const v = parseInt(e.target.value, 10);
if (!isNaN(v) && v >= 1 && v <= 99) {
BATCH_COUNT = v;
saveConfig();
}
});
protectStartLevelInput.addEventListener('change', e => {
const v = parseInt(e.target.value, 10);
if (!isNaN(v) && v >= 0 && v <= 15) {
PROTECT_START_LEVEL = v;
saveConfig();
updateProtectDisplay(); // 更新保护显示
// 更新提示文本
if (v === 0) {
protectLevelHint.textContent = '不使用保护';
protectLevelHint.style.color = '#f44336';
} else {
protectLevelHint.textContent = `≥${v}级使用保护`;
protectLevelHint.style.color = '#aaa';
}
}
});
debugCheckbox.addEventListener('change', e => {
DEBUG_MODE = e.target.checked;
saveConfig();
console.log(`[强化助手] 调试模式已${DEBUG_MODE ? '开启' : '关闭'}`);
});
toggleBtn.addEventListener('click', () => {
if (!isAutoEnhancing) {
if (!currentEnhanceItem) {
return; // 按钮应该是禁用状态
}
// 获取当前批量次数设置
const inputBatchCount = parseInt(batchCountInput.value, 10);
BATCH_COUNT = inputBatchCount;
currentBatchCount = inputBatchCount;
// 如果是批量强化(>1次),确保从基础物品开始
if (inputBatchCount > 1) {
const currentItemInfo = parseItemLevel(currentEnhanceItem.resourceId);
const baseItemId = currentItemInfo.baseItem; // 基础物品ID,不包含+数字
// 更新当前强化物品为基础版本
currentEnhanceItem.resourceId = baseItemId;
saveCurrentItem(); // 保存更新后的物品信息
// 初始化统计数据(使用基础物品ID)
initStats(baseItemId, TARGET_LEVEL);
// Debug模式记录批量开始信息
if (DEBUG_MODE) {
console.group('🚀 [强化助手] 开始批量强化');
console.log('批量设置:', {
批量次数: inputBatchCount,
基础物品ID: baseItemId,
原物品ID: currentItemInfo.baseItem + (currentItemInfo.level > 0 ? '+' + currentItemInfo.level : ''),
目标等级: TARGET_LEVEL,
时间: new Date().toLocaleTimeString()
});
console.groupEnd();
}
} else {
// 单次强化,使用当前物品
initStats(currentEnhanceItem.resourceId, TARGET_LEVEL);
}
updateStatsDisplay();
// 开始自动强化
isAutoEnhancing = true;
updateToggleButtonState();
updateMinimizedDisplay();
if (window.currentWS) {
startAutoEnhance(window.currentWS);
}
} else {
// 停止自动强化
isAutoEnhancing = false;
stopAutoEnhance();
updateToggleButtonState();
updateMinimizedDisplay();
}
});
// —— 收起/展开按钮事件 ——
minimizeBtn.addEventListener('click', toggleMinimize);
expandBtn.addEventListener('click', toggleMinimize);
// —— 清除数据按钮事件 ——
clearDataBtn.addEventListener('click', () => {
if (confirm('确定要清除所有保存的数据吗?这将清除当前强化物品和请求数据。')) {
// 清除内存中的数据
currentEnhanceItem = null;
storedEnhanceData = null;
currentBatchCount = 0; // 重置批量次数
// 重置批量次数输入框
batchCountInput.value = 1;
BATCH_COUNT = 1;
// 清除本地存储
localStorage.removeItem(STORAGE_KEYS.CURRENT_ITEM);
localStorage.removeItem(STORAGE_KEYS.STORED_REQUEST);
// 重置UI
updateItemDisplay(null);
updateProtectDisplay();
// 如果正在强化,停止强化
if (isAutoEnhancing) {
isAutoEnhancing = false;
stopAutoEnhance();
updateToggleButtonState();
}
// 更新收起状态显示
updateMinimizedDisplay();
// 保存配置
saveConfig();
}
});
// —— 拦截全局 WebSocket(增强助手命名空间) ——
const NativeWS = window.WebSocket;
// 检查是否已经被其他脚本拦截
if (!window.WebSocket.__enhanceHelperIntercepted) {
const OriginalWebSocket = window.WebSocket;
window.WebSocket = function(url, protocols) {
const ws = protocols ? new OriginalWebSocket(url, protocols) : new OriginalWebSocket(url);
// 保存当前WebSocket实例
window.currentWS = ws;
// —— 拦截 send ——
const originalSend = ws.send;
ws.__originalSend = originalSend; // 保存原始方法供自动强化使用
ws.send = function(data) {
// 检查是否为强化请求
if (isEnhanceRequest(data)) {
const enhanceData = parseEnhanceData(data);
if (enhanceData && enhanceData.fullPayload) {
// 储存完整的强化数据供后续自动使用
storedEnhanceData = enhanceData.fullPayload;
saveStoredRequest(); // 保存到本地存储
updateProtectDisplay(); // 更新保护材料显示
// 检测到手动强化指令,如果批量次数为0,设为1
if (currentBatchCount === 0) {
currentBatchCount = 1;
batchCountInput.value = 1;
BATCH_COUNT = 1;
saveConfig(); // 保存批量次数
updateMinimizedDisplay(); // 更新收起状态显示
}
// Debug模式记录强化请求保存
if (DEBUG_MODE) {
console.group('💾 [强化助手] 保存强化请求数据');
console.log('原始数据:', data);
console.log('保存的完整数据:', storedEnhanceData);
console.log('解析结果:', {
物品ID: enhanceData.resourceId,
保护材料ID: enhanceData.protectedResourceId || '无',
用户: enhanceData.user,
批量次数设置: currentBatchCount,
时间: new Date().toLocaleTimeString()
});
console.groupEnd();
}
}
// Debug模式记录强化请求拦截
if (DEBUG_MODE) {
console.group('🔨 [强化助手] 拦截到强化请求');
console.log('原始数据:', data);
if (enhanceData) {
console.log('解析结果:', {
物品ID: enhanceData.resourceId,
保护材料ID: enhanceData.protectedResourceId || '无',
用户: enhanceData.user,
时间: new Date().toLocaleTimeString()
});
}
console.groupEnd();
}
}
// 正常发送原始请求,不影响游戏运行
originalSend.call(this, data);
};
// —— 拦截接收消息并解压 ——
ws.addEventListener('message', ev => {
if (ev.data instanceof ArrayBuffer) {
try {
const format = detectCompression(ev.data);
let text;
switch (format) {
case 'gzip':
text = pako.ungzip(new Uint8Array(ev.data), { to: 'string' });
break;
case 'zlib':
text = pako.inflate(new Uint8Array(ev.data), { to: 'string' });
break;
default:
text = pako.inflateRaw(new Uint8Array(ev.data), { to: 'string' });
}
// 检查是否为强化结果(不管是否在等待,都要处理)
const enhanceResult = parseEnhanceResult(text);
if (enhanceResult) {
// Debug模式记录强化结果
if (DEBUG_MODE) {
console.group('✨ [强化助手] 拦截到强化结果');
console.log('原始数据:', text);
console.log('解析结果:', {
成功: enhanceResult.success,
消息: enhanceResult.message,
结果物品ID: enhanceResult.resultId,
用户: enhanceResult.user,
时间: new Date().toLocaleTimeString()
});
// 如果有保存的请求数据,也显示相关信息
if (storedEnhanceData && storedEnhanceData.data) {
console.log('关联的请求信息:', {
原始物品ID: storedEnhanceData.data.resourceId,
保护材料ID: storedEnhanceData.data.protectedResourceId || '无'
});
}
console.groupEnd();
}
handleEnhanceResult(enhanceResult);
}
} catch (err) {
// 解压失败,忽略
}
}
});
// WebSocket关闭时清理
ws.addEventListener('close', () => {
if (isAutoEnhancing) {
stopAutoEnhance();
isAutoEnhancing = false;
updateToggleButtonState();
statusSpan.textContent = '连接断开';
statusSpan.style.color = '#f44336';
counterSpan.textContent = '离线';
updateMinimizedDisplay();
}
});
return ws;
};
// 继承原型与静态属性
window.WebSocket.prototype = OriginalWebSocket.prototype;
Object.getOwnPropertyNames(OriginalWebSocket).forEach(prop => {
if (!(prop in window.WebSocket)) {
window.WebSocket[prop] = OriginalWebSocket[prop];
}
});
// 标记已被强化助手拦截
window.WebSocket.__enhanceHelperIntercepted = true;
}
// —— 窗口大小改变时检查面板位置 ——
window.addEventListener('resize', () => {
const rect = panel.getBoundingClientRect();
const rightDistance = window.innerWidth - rect.right;
const topDistance = rect.top;
// 检查并应用边界约束
const constrained = constrainPosition(rightDistance, topDistance);
// 如果位置需要调整,立即应用
if (constrained.x !== rightDistance || constrained.y !== topDistance) {
panel.style.right = constrained.x + 'px';
panel.style.top = constrained.y + 'px';
panel.style.left = 'auto';
savePosition(constrained.x, constrained.y);
}
});
})();