Skip to content

Instantly share code, notes, and snippets.

@Ansen
Last active January 28, 2026 07:20
Show Gist options
  • Select an option

  • Save Ansen/04af8cc1465388cc56b5597228e37cd0 to your computer and use it in GitHub Desktop.

Select an option

Save Ansen/04af8cc1465388cc56b5597228e37cd0 to your computer and use it in GitHub Desktop.
文字传奇界面调整(优化版)
// ==UserScript==
// @name 文字传奇界面调整(优化版)
// @version 2026-01-27
// @description 调整界面文字传奇布局(更稳健、更易维护)
// @author reginfos,langge
// @match https://chuanqi.makifx.com/
// @grant GM_addStyle
// ==/UserScript==
(function () {
'use strict';
/********************
* 样式统一管理
********************/
GM_addStyle(`
#stats-inventory {
max-height: 200px !important;
overflow-y: auto !important;
overflow-x: hidden !important;
}
.hidden {
display: none !important;
}
.status-card, .side {
height: 600px; /* 你自己调 */
max-height: 600px;
overflow-y: auto;
overflow-x: hidden;
}
`);
/********************
* 工具函数
********************/
const $ = (sel) => document.querySelector(sel);
const $$ = (sel) => Array.from(document.querySelectorAll(sel));
function safeAppend(parent, child) {
if (parent && child) {
parent.appendChild(child);
}
}
function safeRemove(el) {
if (el) {
el.remove();
}
}
function swapDivs(parentSelector, index1, index2) {
const parent = $(parentSelector);
if (!parent) return;
const children = Array.from(parent.children);
if (
index1 < 0 || index2 < 0 ||
index1 >= children.length ||
index2 >= children.length ||
index1 === index2
) return;
const el1 = children[index1];
const el2 = children[index2];
const next = el2.nextSibling === el1 ? el2 : el2.nextSibling;
parent.insertBefore(el1, el2);
parent.insertBefore(el2, next);
}
/********************
* 主逻辑(等待 DOM 就绪)
********************/
function init() {
const statusCards = $$('.status-card');
const actionGroups = $$('.action-group');
const sideActionGroups = $$('.side .action-group');
// 将目标放到【行动面板】
//safeAppend(statusCards[1], actionGroups[3]);
// 将技能框放到【常用操作】
safeAppend(statusCards[2],sideActionGroups[1]);
// 将召唤框放到【属性框】
safeAppend(statusCards[0],sideActionGroups[3]);
// 将物品放到【属性框】
safeAppend(statusCards[1], actionGroups[4]);
// 将移动面板移动到上面
const statusGrid = $('.status-grid');
const sidePanel = $('.side');
safeAppend(statusGrid, sidePanel);
// 去掉头部文字
safeRemove($('header'));
// 页面整体样式调整
const app = $('#app');
if (app) {
app.style.padding = '0';
app.style.maxWidth = 'none';
}
// 上方人物信息:4 列
if (statusGrid) {
statusGrid.style.setProperty(
'grid-template-columns',
'repeat(4, minmax(0, 1fr))'
);
}
// 聊天区域调整
const gameGrid = $('.game-grid');
if (gameGrid) {
gameGrid.style.setProperty('grid-template-columns', '2fr 2fr');
}
// 怪物目标框高度
if (actionGroups[3]) {
actionGroups[3].style.height = '150px';
}
// 隐藏修炼框
if (actionGroups[7]) {
actionGroups[7].classList.add('hidden');
}
// 系统日志移动到和聊天框平级
const logWrap = $('.log-wrap');
safeAppend(gameGrid, logWrap);
// 交换聊天框和系统日志位置
swapDivs('.game-grid', 0, 1);
// 日志高度调整
const log = $('#log');
if (log) {
log.style.minHeight = 'auto';
log.style.height = '320px';
}
}
/********************
* DOM Ready
********************/
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
@Ansen
Copy link
Author

Ansen commented Jan 27, 2026

使用方法

安装 Tampermonkey 插件,然后 点击 我安装

更新内容

20260126

  1. 适配新版本界面
  2. 将宝宝窗口移动到人物属性面板下方

@Ansen
Copy link
Author

Ansen commented Jan 28, 2026

// ==UserScript==
// @name         文字传奇智能挂机控制
// @version      2026-01-27
// @description  监控蓝量,自动停止/恢复挂机,智能选择挂机技能
// @author       reginfos
// @match        https://chuanqi.makifx.com/
// @grant        GM_addStyle
// @grant        GM_notification
// ==/UserScript==

(function () {
    'use strict';

    // 配置
    const CONFIG = {
        stopManaThreshold: 20,     // 蓝量低于20%时停止挂机
        resumeManaThreshold: 100,  // 蓝量恢复到100%时恢复挂机
        checkInterval: 3000,       // 每3秒检查一次
        enableNotification: true,  // 启用浏览器通知
        debug: true,               // 显示调试信息
        // 挂机设置
        autoSelectBestSkills: true, // 自动选择"自动最强"
        skillSelectDelay: 800,      // 技能选择延迟(毫秒)
        maxRetryCount: 3            // 最大重试次数
    };

    // 添加样式
    GM_addStyle(`
        .smart-hangup-control {
            position: fixed;
            top: 10px;
            right: 10px;
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 12px 15px;
            border-radius: 10px;
            font-size: 12px;
            z-index: 99999;
            border: 2px solid #4CAF50;
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            min-width: 220px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            backdrop-filter: blur(10px);
            user-select: none;
        }

        .smart-hangup-control.waiting {
            border-color: #ff9800;
            background: rgba(255, 152, 0, 0.15);
        }

        .smart-hangup-control.stopped {
            border-color: #f44336;
            background: rgba(244, 67, 54, 0.15);
        }

        .smart-hangup-control.selecting {
            border-color: #9C27B0;
            background: rgba(156, 39, 176, 0.15);
            animation: selecting-pulse 1.5s infinite;
        }

        @keyframes selecting-pulse {
            0% { box-shadow: 0 0 0 0 rgba(156, 39, 176, 0.7); }
            70% { box-shadow: 0 0 0 10px rgba(156, 39, 176, 0); }
            100% { box-shadow: 0 0 0 0 rgba(156, 39, 176, 0); }
        }

        .control-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
            padding-bottom: 8px;
            border-bottom: 1px solid #444;
        }

        .control-title {
            font-weight: bold;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .title-icon {
            font-size: 16px;
        }

        .control-status {
            font-size: 10px;
            padding: 3px 8px;
            border-radius: 12px;
            background: #4CAF50;
            color: white;
            font-weight: bold;
        }

        .control-status.waiting {
            background: #ff9800;
        }

        .control-status.stopped {
            background: #f44336;
        }

        .control-status.selecting {
            background: #9C27B0;
        }

        .mana-display {
            margin: 8px 0;
        }

        .mana-text {
            display: flex;
            justify-content: space-between;
            margin-bottom: 5px;
        }

        .current-mana {
            font-weight: bold;
            color: #4FC3F7;
        }

        .mana-threshold {
            font-size: 10px;
            color: #aaa;
        }

        .mana-bar-container {
            height: 10px;
            background: #2C2C2C;
            border-radius: 5px;
            overflow: hidden;
            position: relative;
        }

        .mana-bar-fill {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, #2196F3, #03A9F4);
            border-radius: 5px;
            transition: width 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
            position: relative;
        }

        .mana-bar-fill::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(90deg,
                transparent,
                rgba(255, 255, 255, 0.2),
                transparent);
            animation: shimmer 2s infinite;
        }

        @keyframes shimmer {
            0% { transform: translateX(-100%); }
            100% { transform: translateX(100%); }
        }

        .mana-bar-fill.low {
            background: linear-gradient(90deg, #ff9800, #ffb74d);
        }

        .mana-bar-fill.critical {
            background: linear-gradient(90deg, #f44336, #ff7961);
        }

        .mana-bar-fill.full {
            background: linear-gradient(90deg, #4CAF50, #8BC34A);
        }

        .control-buttons {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 6px;
            margin: 10px 0;
        }

        .ctrl-btn {
            padding: 6px 10px;
            border: none;
            border-radius: 6px;
            background: #444;
            color: white;
            cursor: pointer;
            font-size: 11px;
            font-weight: bold;
            transition: all 0.2s;
            text-align: center;
        }

        .ctrl-btn:hover {
            transform: translateY(-1px);
            box-shadow: 0 3px 8px rgba(0,0,0,0.3);
        }

        .ctrl-btn:active {
            transform: translateY(0);
        }

        .ctrl-btn.primary {
            background: linear-gradient(135deg, #4CAF50, #45a049);
        }

        .ctrl-btn.danger {
            background: linear-gradient(135deg, #f44336, #d32f2f);
        }

        .ctrl-btn.warning {
            background: linear-gradient(135deg, #ff9800, #f57c00);
        }

        .ctrl-btn.purple {
            background: linear-gradient(135deg, #9C27B0, #7B1FA2);
        }

        .stats-panel {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 6px;
            padding: 8px;
            margin-top: 8px;
        }

        .stats-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 5px;
            font-size: 10px;
        }

        .stat-item {
            text-align: center;
        }

        .stat-label {
            color: #aaa;
            margin-bottom: 2px;
        }

        .stat-value {
            color: white;
            font-weight: bold;
            font-size: 11px;
        }

        .last-action {
            margin-top: 5px;
            padding-top: 5px;
            border-top: 1px solid #333;
            font-size: 9px;
            color: #888;
            text-align: center;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* 按钮数据属性 */
        [data-hangup-action] {
            cursor: pointer;
        }

        /* 调试信息样式 */
        .hangup-debug-info {
            position: fixed;
            top: 50px;
            right: 10px;
            background: rgba(0, 0, 0, 0.8);
            color: #0f0;
            padding: 8px;
            border-radius: 4px;
            font-size: 11px;
            font-family: monospace;
            z-index: 99998;
            max-width: 300px;
            max-height: 200px;
            overflow: auto;
            display: none;
        }
    `);

    // 工具函数
    const $ = (sel) => document.querySelector(sel);
    const $$ = (sel) => Array.from(document.querySelectorAll(sel));

    // 智能挂机控制器
    class SmartHangupController {
        constructor() {
            this.isActive = true;
            this.isMonitoring = true;
            this.isHangupStopped = false;
            this.isSelectingSkills = false;
            this.isSkillModalOpen = false;
            this.statusElement = null;
            this.manaBarElement = null;
            this.checkTimer = null;
            this.skillSelectTimer = null;
            this.retryCount = 0;
            this.debugInfoElement = null;

            this.stats = {
                totalChecks: 0,
                lowManaEvents: 0,
                autoStops: 0,
                autoResumes: 0,
                skillSelects: 0,
                lastAction: '初始化',
                lastActionTime: null
            };

            // 绑定方法
            this.handleButtonClick = this.handleButtonClick.bind(this);
            this.handleGlobalKeydown = this.handleGlobalKeydown.bind(this);

            this.init();
        }

        init() {
            this.createControlPanel();
            this.createDebugInfo();
            this.setupEventListeners();
            this.startMonitoring();
            this.log('智能挂机控制已启动');

            // 启动技能选择检测
            this.startSkillModalDetection();

            // 初始检查
            setTimeout(() => this.checkMana(), 1500);

            // 每隔一段时间检查一次技能选择界面
            setInterval(() => this.checkForSkillModal(), 1000);
        }

        createControlPanel() {
            const oldPanel = $('.smart-hangup-control');
            if (oldPanel) oldPanel.remove();

            this.statusElement = document.createElement('div');
            this.statusElement.className = 'smart-hangup-control';
            this.statusElement.innerHTML = `
                <div class="control-header">
                    <div class="control-title">
                        <span class="title-icon">🤖</span>
                        <span>智能挂机</span>
                    </div>
                    <span class="control-status">监控中</span>
                </div>

                <div class="mana-display">
                    <div class="mana-text">
                        <span>蓝量: <span class="current-mana">--/--</span></span>
                        <span class="mana-threshold">≤${CONFIG.stopManaThreshold}%停</span>
                    </div>
                    <div class="mana-bar-container">
                        <div class="mana-bar-fill"></div>
                    </div>
                </div>

                <div class="control-buttons">
                    <button class="ctrl-btn warning" data-hangup-action="toggleMonitoring">暂停监控</button>
                    <button class="ctrl-btn purple" data-hangup-action="forceStartHangup">强制挂机</button>
                    <button class="ctrl-btn primary" data-hangup-action="manualResume">恢复挂机</button>
                    <button class="ctrl-btn danger" data-hangup-action="emergencyStop">紧急停止</button>
                </div>

                <div class="stats-panel">
                    <div class="stats-grid">
                        <div class="stat-item">
                            <div class="stat-label">检查</div>
                            <div class="stat-value stat-checks">0</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-label">低蓝</div>
                            <div class="stat-value stat-lowmana">0</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-label">恢复</div>
                            <div class="stat-value stat-resumes">0</div>
                        </div>
                    </div>
                    <div class="last-action" title="最后操作">最后操作: 初始化</div>
                </div>
            `;

            document.body.appendChild(this.statusElement);
            this.manaBarElement = this.statusElement.querySelector('.mana-bar-fill');

            // 存储实例引用
            window.__smartHangupCtrl = this;
        }

        createDebugInfo() {
            this.debugInfoElement = document.createElement('div');
            this.debugInfoElement.className = 'hangup-debug-info';
            this.debugInfoElement.innerHTML = `
                <div>调试信息:</div>
                <div id="debug-state">状态: 初始化</div>
                <div id="debug-modal">技能框: 未检测</div>
                <div id="debug-buttons">按钮: 未找到</div>
            `;
            document.body.appendChild(this.debugInfoElement);

            // 按F3显示/隐藏调试信息
            document.addEventListener('keydown', (e) => {
                if (e.key === 'F3') {
                    this.debugInfoElement.style.display =
                        this.debugInfoElement.style.display === 'none' ? 'block' : 'none';
                }
            });
        }

        updateDebugInfo(state, modal, buttons) {
            if (!this.debugInfoElement) return;

            const stateEl = this.debugInfoElement.querySelector('#debug-state');
            const modalEl = this.debugInfoElement.querySelector('#debug-modal');
            const buttonsEl = this.debugInfoElement.querySelector('#debug-buttons');

            if (stateEl) stateEl.textContent = `状态: ${state}`;
            if (modalEl) modalEl.textContent = `技能框: ${modal}`;
            if (buttonsEl) buttonsEl.textContent = `按钮: ${buttons}`;
        }

        setupEventListeners() {
            // 使用事件委托处理按钮点击
            if (this.statusElement) {
                this.statusElement.addEventListener('click', this.handleButtonClick);
            }

            // 全局键盘事件
            document.addEventListener('keydown', this.handleGlobalKeydown);
        }

        handleButtonClick(event) {
            const button = event.target.closest('[data-hangup-action]');
            if (!button) return;

            const action = button.getAttribute('data-hangup-action');
            event.preventDefault();
            event.stopPropagation();

            this.log(`按钮点击: ${action}`);

            switch (action) {
                case 'toggleMonitoring':
                    this.toggleMonitoring();
                    break;
                case 'forceStartHangup':
                    this.forceStartHangup();
                    break;
                case 'manualResume':
                    this.manualResume();
                    break;
                case 'emergencyStop':
                    this.emergencyStop();
                    break;
            }
        }

        handleGlobalKeydown(event) {
            // Alt+Shift+H 切换监控
            if (event.altKey && event.shiftKey && event.key === 'H') {
                event.preventDefault();
                this.toggleMonitoring();
            }
            // Alt+Shift+S 强制开始
            if (event.altKey && event.shiftKey && event.key === 'S') {
                event.preventDefault();
                this.forceStartHangup();
            }
            // Alt+Shift+E 紧急停止
            if (event.altKey && event.shiftKey && event.key === 'E') {
                event.preventDefault();
                this.emergencyStop();
            }
        }

        startSkillModalDetection() {
            // 定期检查技能选择界面
            this.skillModalCheckInterval = setInterval(() => {
                this.checkForSkillModal();
            }, 500);
        }

        checkForSkillModal() {
            if (!this.isSelectingSkills) return;

            // 方法1: 查找特定的模态框元素
            let skillModal = null;

            // 查找所有模态框
            const modals = $$('.modal, .modal-dialog, .modal-content, [class*="modal"], [class*="dialog"], [class*="popup"]');

            for (const modal of modals) {
                const text = modal.textContent || '';
                if (text.includes('挂机技能') ||
                    text.includes('点击技能即可自动释放') ||
                    text.includes('自动最强') ||
                    text.includes('开始挂机') ||
                    text.includes('取消')) {

                    // 检查是否可见
                    const style = window.getComputedStyle(modal);
                    if (style.display !== 'none' && style.visibility !== 'hidden') {
                        skillModal = modal;
                        break;
                    }
                }
            }

            // 方法2: 查找特定ID的元素
            if (!skillModal) {
                const afkElements = $$('#afk-skill-list, .afk-skill-list, #afk-start, #afk-auto');
                for (const el of afkElements) {
                    const style = window.getComputedStyle(el);
                    if (style.display !== 'none') {
                        skillModal = el.closest('.modal, .modal-dialog, [class*="modal"]') || document.body;
                        break;
                    }
                }
            }

            if (skillModal && !this.isSkillModalOpen) {
                this.log('检测到技能选择界面');
                this.isSkillModalOpen = true;
                this.updateDebugInfo('选择技能', '已检测到', '检查中');
                this.handleSkillModal(skillModal);
            } else if (!skillModal && this.isSkillModalOpen) {
                this.isSkillModalOpen = false;
            }

            // 更新调试信息
            this.updateDebugInfo(
                this.isSelectingSkills ? '选择中' : '监控中',
                this.isSkillModalOpen ? '已打开' : '未检测',
                '等待检测'
            );
        }

        handleSkillModal(modalElement) {
            this.log('处理技能选择界面');
            this.updateStatus('选择技能');

            // 等待界面完全渲染
            setTimeout(() => {
                if (CONFIG.autoSelectBestSkills) {
                    this.selectBestSkills();
                }
            }, CONFIG.skillSelectDelay);
        }

        selectBestSkills() {
            this.log('正在选择自动最强技能...');

            // 首先尝试直接查找"自动最强"按钮
            let autoBestBtn = $('#afk-auto');

            // 如果没找到,查找所有按钮
            if (!autoBestBtn) {
                const allButtons = $$('button');
                for (const btn of allButtons) {
                    const btnText = btn.textContent.trim();
                    if (btnText === '自动最强' || btnText.includes('自动最强')) {
                        autoBestBtn = btn;
                        break;
                    }
                }
            }

            if (autoBestBtn) {
                this.log('找到"自动最强"按钮,点击选择');
                this.updateDebugInfo('选择技能', '已检测到', '找到自动最强');

                // 点击"自动最强"按钮
                this.simulateClick(autoBestBtn);
                this.stats.skillSelects++;

                // 短暂延迟后点击"开始挂机"按钮
                setTimeout(() => {
                    this.startHangupFromModal();
                }, 300);

                return true;
            } else {
                this.log('未找到"自动最强"按钮,尝试其他方式');
                this.updateDebugInfo('选择技能', '已检测到', '未找到自动最强');

                // 尝试直接查找"开始挂机"按钮
                this.startHangupFromModal();
                return false;
            }
        }

        startHangupFromModal() {
            setTimeout(() => {
                // 查找"开始挂机"按钮
                let startBtn = $('#afk-start');

                // 如果没找到,查找所有按钮
                if (!startBtn) {
                    const allButtons = $$('button');
                    for (const btn of allButtons) {
                        const btnText = btn.textContent.trim();
                        if (btnText === '开始挂机' || btnText.includes('开始挂机')) {
                            startBtn = btn;
                            break;
                        }
                    }
                }

                if (startBtn) {
                    this.log('点击"开始挂机"按钮');
                    this.updateDebugInfo('选择技能', '已检测到', '找到开始挂机');

                    this.simulateClick(startBtn);
                    this.isSelectingSkills = false;
                    this.isSkillModalOpen = false;
                    this.updateStatus('监控中');

                    // 挂机成功启动
                    this.isHangupStopped = false;
                    this.retryCount = 0;

                    setTimeout(() => {
                        this.recordAction('智能挂机已启动');
                        this.notify('挂机恢复', '已选择自动最强技能并开始挂机');
                        this.updateDebugInfo('监控中', '已关闭', '完成');
                    }, 500);
                } else {
                    this.error('未找到开始挂机按钮');
                    this.updateDebugInfo('选择技能', '已检测到', '未找到开始挂机');
                    this.retrySelectSkills();
                }
            }, 500);
        }

        startHangupDirectly() {
            this.log('尝试直接开始挂机');
            const success = this.findAndClickHangupButton('挂机');
            if (success) {
                this.isHangupStopped = false;
                this.retryCount = 0;
                this.isSelectingSkills = false;
                this.updateStatus('监控中');
                this.recordAction('直接开始挂机');
                this.notify('挂机恢复', '已直接开始挂机');
            } else {
                this.error('未找到挂机按钮');
                this.retrySelectSkills();
            }
        }

        retrySelectSkills() {
            if (this.retryCount < CONFIG.maxRetryCount) {
                this.retryCount++;
                this.log(`重试选择技能 (${this.retryCount}/${CONFIG.maxRetryCount})`);

                setTimeout(() => {
                    if (this.isHangupStopped) {
                        this.resumeAutoHangup(100); // 重新尝试恢复挂机
                    }
                }, 2000);
            } else {
                this.error('达到最大重试次数,请手动操作');
                this.notify('挂机失败', '已达到最大重试次数,请手动操作');
                this.isSelectingSkills = false;
                this.updateStatus('监控中');
            }
        }

        startMonitoring() {
            this.stopMonitoring();
            this.isMonitoring = true;
            this.checkTimer = setInterval(() => this.checkMana(), CONFIG.checkInterval);
            this.updateStatus('监控中');
            this.recordAction('监控已启动');
        }

        stopMonitoring() {
            if (this.checkTimer) {
                clearInterval(this.checkTimer);
                this.checkTimer = null;
            }
            this.isMonitoring = false;
            this.updateStatus('已暂停');
        }

        toggleMonitoring() {
            if (this.isMonitoring) {
                this.stopMonitoring();
                this.notify('监控暂停', '智能挂机控制已暂停');
            } else {
                this.startMonitoring();
                this.notify('监控恢复', '智能挂机控制已恢复');
            }
        }

        checkMana() {
            if (!this.isMonitoring) return;

            this.stats.totalChecks++;
            this.updateStats();

            try {
                const manaInfo = this.getManaInfo();
                if (!manaInfo) {
                    this.updateDisplay('--/--', 0);
                    return;
                }

                const { current, max, percentage } = manaInfo;
                this.updateDisplay(`${current}/${max}`, percentage);

                // 检查是否低于停止阈值
                if (percentage <= CONFIG.stopManaThreshold && !this.isHangupStopped) {
                    this.stopAutoHangup(percentage);
                }
                // 检查是否达到恢复阈值
                else if (percentage >= CONFIG.resumeManaThreshold && this.isHangupStopped && !this.isSelectingSkills) {
                    this.resumeAutoHangup(percentage);
                }

            } catch (error) {
                this.error('检查蓝量时出错:', error);
            }
        }

        getManaInfo() {
            // 方法1: 查找数值显示
            const mpValueElement = $('#ui-mp');
            if (mpValueElement) {
                const text = mpValueElement.textContent.trim();
                const match = text.match(/(\d+)\s*\/\s*(\d+)/);
                if (match) {
                    const current = parseInt(match[1]);
                    const max = parseInt(match[2]);
                    const percentage = max > 0 ? (current / max) * 100 : 0;
                    return { current, max, percentage };
                }
            }

            // 方法2: 查找进度条显示
            const mpBarElement = $('#bar-mp');
            if (mpBarElement) {
                const style = mpBarElement.style.width;
                const match = style.match(/(\d+(\.\d+)?)%/);
                if (match) {
                    const percentage = parseFloat(match[1]);
                    let max = 0;

                    const mpValue = $('#ui-mp');
                    if (mpValue) {
                        const text = mpValue.textContent.trim();
                        const m = text.match(/\d+\s*\/\s*(\d+)/);
                        if (m) max = parseInt(m[1]);
                    }

                    if (max === 0) max = 1000;
                    const current = Math.round((percentage / 100) * max);

                    return { current, max, percentage };
                }
            }

            this.log('未找到蓝量显示元素');
            return null;
        }

        updateDisplay(valueText, percentage) {
            if (!this.statusElement) return;

            const manaValueSpan = this.statusElement.querySelector('.current-mana');
            if (manaValueSpan) {
                manaValueSpan.textContent = valueText;
            }

            if (this.manaBarElement) {
                this.manaBarElement.style.width = `${Math.min(percentage, 100)}%`;

                // 更新颜色
                this.manaBarElement.className = 'mana-bar-fill';
                if (percentage >= 95) {
                    this.manaBarElement.classList.add('full');
                } else if (percentage <= CONFIG.stopManaThreshold) {
                    this.manaBarElement.classList.add('critical');
                } else if (percentage <= CONFIG.stopManaThreshold * 2) {
                    this.manaBarElement.classList.add('low');
                }
            }
        }

        updateStatus(status) {
            if (!this.statusElement) return;

            const statusElement = this.statusElement.querySelector('.control-status');
            const titleElement = this.statusElement.querySelector('.control-title');

            if (statusElement) {
                statusElement.textContent = status;
                statusElement.className = 'control-status';

                // 移除所有状态类
                this.statusElement.classList.remove('waiting', 'stopped', 'selecting');

                if (status === '等待回蓝' || status === '已暂停') {
                    statusElement.classList.add('waiting');
                    this.statusElement.classList.add('waiting');
                } else if (status === '挂机已停') {
                    statusElement.classList.add('stopped');
                    this.statusElement.classList.add('stopped');
                } else if (status === '选择技能') {
                    statusElement.classList.add('selecting');
                    this.statusElement.classList.add('selecting');
                }
            }

            if (titleElement) {
                const icon = titleElement.querySelector('.title-icon');
                if (icon) {
                    if (status === '挂机已停') {
                        icon.textContent = '⛔';
                    } else if (status === '选择技能') {
                        icon.textContent = '⚡';
                    } else if (status === '已暂停') {
                        icon.textContent = '⏸️';
                    } else {
                        icon.textContent = '🤖';
                    }
                }
            }
        }

        updateStats() {
            if (!this.statusElement) return;

            const checksSpan = this.statusElement.querySelector('.stat-checks');
            const lowManaSpan = this.statusElement.querySelector('.stat-lowmana');
            const resumesSpan = this.statusElement.querySelector('.stat-resumes');
            const lastActionSpan = this.statusElement.querySelector('.last-action');

            if (checksSpan) checksSpan.textContent = this.stats.totalChecks;
            if (lowManaSpan) lowManaSpan.textContent = this.stats.lowManaEvents;
            if (resumesSpan) resumesSpan.textContent = this.stats.autoResumes;
            if (lastActionSpan) {
                lastActionSpan.textContent = `最后: ${this.stats.lastAction}`;
                lastActionSpan.title = this.stats.lastAction;
            }
        }

        recordAction(action) {
            this.stats.lastAction = action;
            this.stats.lastActionTime = new Date();
            this.updateStats();
        }

        stopAutoHangup(percentage) {
            this.stats.lowManaEvents++;
            this.stats.autoStops++;
            this.recordAction(`蓝量${percentage.toFixed(1)}%停止挂机`);

            this.log(`蓝量过低 (${percentage.toFixed(1)}%),停止自动挂机`);
            this.updateStatus('挂机已停');
            this.isHangupStopped = true;
            this.isSelectingSkills = false;
            this.isSkillModalOpen = false;
            this.retryCount = 0;

            // 停止挂机
            this.findAndClickHangupButton('停止挂机');

            if (CONFIG.enableNotification) {
                this.notify('挂机已停止', `蓝量过低: ${percentage.toFixed(1)}%`);
            }
        }

        resumeAutoHangup(percentage) {
            this.stats.autoResumes++;
            this.recordAction(`蓝量${percentage.toFixed(1)}%恢复挂机`);

            this.log(`蓝量已恢复 (${percentage.toFixed(1)}%),准备恢复挂机`);
            this.updateStatus('等待回蓝');

            // 设置技能选择状态
            this.isSelectingSkills = true;
            this.isSkillModalOpen = false;

            // 点击挂机按钮(这会触发技能选择界面)
            setTimeout(() => {
                if (this.findAndClickHangupButton('挂机')) {
                    this.log('已点击挂机按钮,等待技能选择界面');
                    this.updateDebugInfo('等待界面', '等待中', '已点击挂机');

                    // 设置超时,防止界面未弹出
                    setTimeout(() => {
                        if (this.isSelectingSkills && !this.isSkillModalOpen) {
                            this.log('技能选择界面未弹出,尝试直接挂机');
                            this.startHangupDirectly();
                        }
                    }, 5000);
                }
            }, 1000);
        }

        findAndClickHangupButton(text) {
            // 首先查找.chip元素
            const chips = $$('.chip');
            for (const chip of chips) {
                if (chip.textContent.trim() === text) {
                    this.simulateClick(chip);
                    this.log(`点击按钮: ${text}`);
                    return true;
                }
            }

            // 查找其他可能的按钮
            const buttons = $$('button, div[onclick], span[onclick], a[onclick]');
            for (const btn of buttons) {
                const btnText = btn.textContent.trim();
                if (btnText === text || btnText.includes(text)) {
                    this.simulateClick(btn);
                    this.log(`点击按钮: ${btnText}`);
                    return true;
                }
            }

            this.log(`未找到按钮: ${text}`);
            return false;
        }

        simulateClick(element) {
            try {
                // 尝试多种点击方式
                if (typeof element.click === 'function') {
                    element.click();
                } else {
                    const event = new MouseEvent('click', {
                        view: window,
                        bubbles: true,
                        cancelable: true
                    });
                    element.dispatchEvent(event);
                }
                return true;
            } catch (e) {
                this.error('点击失败:', e);
                return false;
            }
        }

        forceStartHangup() {
            this.log('强制开始挂机');
            this.isHangupStopped = false;
            this.resumeAutoHangup(100);
            this.recordAction('强制开始挂机');
            this.notify('强制操作', '已强制开始挂机流程');
        }

        manualResume() {
            if (this.isHangupStopped) {
                this.log('手动恢复挂机');
                this.isHangupStopped = false;
                this.resumeAutoHangup(100);
                this.recordAction('手动恢复挂机');
            }
        }

        emergencyStop() {
            this.log('紧急停止所有操作');
            this.isMonitoring = false;
            this.isHangupStopped = true;
            this.isSelectingSkills = false;
            this.isSkillModalOpen = false;
            this.stopMonitoring();
            this.findAndClickHangupButton('停止挂机');
            this.updateStatus('已暂停');
            this.recordAction('紧急停止');
            this.notify('紧急停止', '所有自动操作已停止');
        }

        notify(title, message) {
            if (CONFIG.enableNotification && typeof GM_notification === 'function') {
                GM_notification({
                    title: `🤖 ${title}`,
                    text: message,
                    timeout: 4000,
                    highlight: true
                });
            }
        }

        log(...args) {
            if (CONFIG.debug) {
                console.log('[智能挂机]', ...args);
            }
        }

        error(...args) {
            console.error('[智能挂机]', ...args);
        }
    }

    // 主函数
    function main() {
        // 检查是否已经存在控制器
        if (window.__smartHangupCtrl) {
            console.log('[智能挂机] 控制器已存在,跳过初始化');
            return;
        }

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                setTimeout(() => {
                    new SmartHangupController();
                }, 3000);
            });
        } else {
            setTimeout(() => {
                new SmartHangupController();
            }, 3000);
        }
    }

    // 启动
    main();

})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment