MWI-Hit-Tracker-More-Animation

战斗过程中实时显示攻击命中目标,增加了更多的特效和玩家自定义图片弹道功能等等等(更多功能请自行探索)。(想扔什么直接上传图片就行,支持文件上传和剪贴板)

// ==UserScript==
// @name         MWI-Hit-Tracker-More-Animation
// @namespace    http://tampermonkey.net/
// @version      2.0.4
// @description  战斗过程中实时显示攻击命中目标,增加了更多的特效和玩家自定义图片弹道功能等等等(更多功能请自行探索)。(想扔什么直接上传图片就行,支持文件上传和剪贴板)
// @author       Artintel (Artintel), Yuk111
// @license MIT
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.milkywayidle.com/favicon.svg
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // 状态变量,存储战斗相关信息
    const battleState = {
        monstersHP: [],
        monstersMP: [],
        playersHP: [],
        playersMP: []
    };

    // 存储是否已添加窗口大小改变监听器
    let isResizeListenerAdded = false;

    // 标记脚本是否暂停
    let isPaused = false;

    // 粒子对象池
    const particlePool = [];

    // 标记按钮是否已添加
    let isCustomColorButtonAdded = false;

    // 用于存储自定义弹道图片
    let customProjectileImage = null;
    // 弹道图片状态(是否启用)
    let useCustomProjectileImage = false;
    // 新增:各玩家的自定义弹道图片
    let playerProjectileImages = [null, null, null, null, null];
    // 新增:各玩家是否使用自定义弹道
    let playerUseProjectile = [false, false, false, false, false];

    // 颜色配置 - 使用单一来源的颜色定义
    const colorConfig = {
        // 定义基础颜色,这是唯一需要修改的地方
        baseColors: [
            "rgba(255, 99, 132, 1)",  // 浅粉色 - 玩家一
            "rgba(54, 162, 235, 1)",  // 浅蓝色 - 玩家二
            "rgba(255, 206, 86, 1)",  // 浅黄色 - 玩家三
            "rgba(75, 192, 192, 1)",  // 浅绿色 - 玩家四
            "rgba(153, 102, 255, 1)", // 浅紫色 - 玩家五
            "rgba(255, 0, 0, 1)"      // 红色 - 敌人攻击颜色
        ],

        // 获取线条颜色
        getLineColors() {
            return [...this.baseColors];
        },

        // 获取滤镜颜色,自动从基础颜色生成
        getFilterColors() {
            return this.baseColors.map(color => color.replace('1)', '0.8)'));
        },

        // 重置颜色数组
        resetColors(lineColorArray, filterColorArray) {
            lineColorArray.splice(0, lineColorArray.length, ...this.getLineColors());
            filterColorArray.splice(0, filterColorArray.length, ...this.getFilterColors());
        }
    };

    // 存储每个玩家的勾选状态,默认全部勾选
    const playerDrawEnabled = new Array(6).fill(true);
    // 存储特效的勾选状态,默认全勾选
    // 索引含义:0-伤害数字,1-线条绘制,2-粒子拖尾,3-击中特效,4-震动特效,5-隐藏数字,6-浮动数字
    const effectDrawEnabled = new Array(7).fill(true);
    // 设置隐藏数字默认为不勾选
    effectDrawEnabled[5] = false;

    // 定义线条颜色数组,用于不同角色的攻击线条颜色
    const lineColor = colorConfig.getLineColors();
    // 定义滤镜颜色数组,用于线条的外发光效果颜色
    const filterColor = colorConfig.getFilterColors();

    // 从 localStorage 加载保存的设置
    function readSettings() {
        const ls = localStorage.getItem("MWI_Hit_Tracker_Settings");
        if (ls) {
            try {
                const lsObj = JSON.parse(ls);
                lineColor.splice(0, lineColor.length, ...lsObj.lineColor);
                filterColor.splice(0, filterColor.length, ...lsObj.filterColor);
                playerDrawEnabled.splice(0, playerDrawEnabled.length, ...lsObj.playerDrawEnabled);
                
                // 读取自定义弹道图片设置
                if (lsObj.customProjectileImage) {
                    customProjectileImage = lsObj.customProjectileImage;
                }
                if (lsObj.useCustomProjectileImage !== undefined) {
                    useCustomProjectileImage = lsObj.useCustomProjectileImage;
                }
                
                // 读取各玩家自定义弹道图片设置
                if (lsObj.playerProjectileImages) {
                    playerProjectileImages = lsObj.playerProjectileImages;
                }
                if (lsObj.playerUseProjectile) {
                    playerUseProjectile = lsObj.playerUseProjectile;
                }
            } catch (e) {
                console.error('解析基本设置失败:', e);
                // 如果解析失败,使用colorConfig重置颜色
                colorConfig.resetColors(lineColor, filterColor);
            }
        }

        // 读取特效设置
        const effectLs = localStorage.getItem("MWI_Hit_Tracker_Effect_Settings");
        if (effectLs) {
            try {
                const effectLsObj = JSON.parse(effectLs);
                // 确保数组长度足够存储所有特效设置,包括浮动数字选项
                if (effectLsObj.effectDrawEnabled) {
                    // 如果长度不足以存储浮动数字选项,则扩展数组
                    while (effectLsObj.effectDrawEnabled.length < 7) {
                        // 对于索引5(隐藏数字),默认为false,其他默认为true
                        const defaultValue = effectLsObj.effectDrawEnabled.length === 5 ? false : true;
                        effectLsObj.effectDrawEnabled.push(defaultValue);
                    }
                    effectDrawEnabled.splice(0, effectDrawEnabled.length, ...effectLsObj.effectDrawEnabled);
                }
                
                // 如果设置了隐藏数字,应用隐藏效果
                if (effectDrawEnabled[5]) {
                    toggleDamageNumbers(true);
                }
            } catch (e) {
                console.error('解析特效设置失败:', e);
            }
        }
    }

    // 保存设置到 localStorage
    function saveSettings() {
        const settings = {
            lineColor: lineColor,
            filterColor: filterColor,
            playerDrawEnabled: playerDrawEnabled,
            customProjectileImage: customProjectileImage,
            useCustomProjectileImage: useCustomProjectileImage,
            playerProjectileImages: playerProjectileImages,
            playerUseProjectile: playerUseProjectile
        };
        localStorage.setItem("MWI_Hit_Tracker_Settings", JSON.stringify(settings));

        // 保存特效设置
        const effectSettings = {
            effectDrawEnabled: effectDrawEnabled
        };
        localStorage.setItem("MWI_Hit_Tracker_Effect_Settings", JSON.stringify(effectSettings));
    }

    // 在初始化时加载设置
    readSettings();

    // 创建自定义颜色按钮
    /**
     * 创建自定义颜色设置按钮,用于打开设置弹出窗口,可设置玩家攻击线条颜色和显示状态。
     */
    function createCustomColorButton() {
        // 使用选择器,查找按钮的父元素
        const tabsContainer = document.querySelector("#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_middlePanel__uDts7 > div.GamePage_mainPanel__2njyb > div > div:nth-child(1) > div > div > div > div.TabsComponent_tabsContainer__3BDUp > div > div > div");
        // 获取参考标签,如果 tabsContainer 存在,则取其第二个子元素
        const referenceTab = tabsContainer ? tabsContainer.children[1] : null;

        // 检查是否找到目标元素,如果未找到则输出提示信息并返回
        if (!tabsContainer || !referenceTab) {
            console.log('未找到目标元素,请检查选择器是否正确。');
            return;
        }
        // 检查是否已经存在自定义颜色按钮,如果存在则返回
        if (tabsContainer.querySelector('.Button_customColor__custom')) return;

        // 创建自定义颜色设置按钮
        const customColorButton = document.createElement('button');
        // 为按钮设置自定义类名
        customColorButton.className = 'Button_customColor__custom css-1q2h7u5';
        // 设置按钮的显示文本
        customColorButton.textContent = 'Hit自定义';

        // 获取标签容器中的最后一个标签
        const lastTab = tabsContainer.children[tabsContainer.children.length - 1];

        // 遍历标签容器中的所有标签,检查是否存在文本内容为"商品列表"的标签,如果存在则返回
        for (let i = 0; i < tabsContainer.children.length; i++) {
            if (tabsContainer.children[i].textContent === "商品列表") {
                return;
            }
        }

        // 将自定义颜色设置按钮插入到最后一个标签之后
        lastTab.insertAdjacentElement('afterend', customColorButton);

        // 创建样式元素,用于设置按钮和弹出窗口相关的样式
        const style = document.createElement('style');
        // 设置样式内容
        style.innerHTML = `
            .Button_customColor__custom {
                background-color: #546ddb;
                color: white;
                border-radius: 5px;
                padding: 5px 10px;
                cursor: pointer;
                transition: background-color 0.3s;
            }
            .Button_customColor__custom:hover {
                background-color: #131419;
            }
            .expandable-section {
                cursor: pointer;
                padding: 10px;
                background-color: #e0e0e0;
                margin-bottom: 10px;
                border-radius: 5px;
            }
            .expandable-content {
                display: none;
                padding-left: 20px;
            }
            .expandable-content.show {
                display: block;
            }
            .draggable {
                cursor: move;
            }
            .inner-expandable-section {
                transition: background-color 0.3s;
            }
            .inner-expandable-section:hover {
                background-color: #d8d8d8;
            }
            .param-slider-container {
                margin-bottom: 12px;
            }
            input[type="range"] {
                height: 5px;
                border-radius: 5px;
                background: #d3d3d3;
                outline: none;
            }
            input[type="range"]::-webkit-slider-thumb {
                -webkit-appearance: none;
                appearance: none;
                width: 15px;
                height: 15px;
                border-radius: 50%;
                background: #546ddb;
                cursor: pointer;
            }
        `;
        // 将样式元素添加到文档头部
        document.head.appendChild(style);

        // 为自定义颜色设置按钮添加点击事件监听器
        customColorButton.addEventListener('click', () => {
            // 检查文档中是否已存在类名为 "自定义菜单" 的元素,如果有则销毁它
            const customMenu = document.querySelector('.自定义菜单');
            const customMask = document.querySelector('.自定义菜单遮罩');
            if (customMenu) {
                if (customMask) document.body.removeChild(customMask);
                document.body.removeChild(customMenu);
                return;
            }

            // ===== 新增:创建遮罩层 =====
            const mask = document.createElement('div');
            mask.className = '自定义菜单遮罩';
            mask.style.position = 'fixed';
            mask.style.top = '0';
            mask.style.left = '0';
            mask.style.width = '100vw';
            mask.style.height = '100vh';
            mask.style.background = 'rgba(0,0,0,0)'; // 完全透明
            mask.style.zIndex = '9998';
            document.body.appendChild(mask);

            // 创建弹出窗口元素并添加类名 "自定义菜单"
            const popup = document.createElement('div');
            popup.classList.add('自定义菜单');
            // 设置弹出窗口的定位方式为固定定位
            popup.style.position = 'fixed';
            
            // 添加响应式样式,使菜单适应不同设备
            popup.style.maxWidth = '80vw'; // 最大宽度不超过视口宽度的80%
            popup.style.maxHeight = '80vh'; // 最大高度不超过视口高度的80%
            popup.style.width = 'min(350px, 80vw)'; // 宽度取350px和80vw中的较小值
            popup.style.overflowY = 'auto'; // 内容过多时可滚动
            popup.style.overflowX = 'hidden'; // 防止水平方向滚动
            
            // 动态计算居中位置,考虑视口尺寸
            const positionMenu = () => {
                const menuWidth = Math.min(350, window.innerWidth * 0.8); // 计算实际宽度
                const menuHeight = Math.min(popup.scrollHeight, window.innerHeight * 0.8); // 计算实际高度
                popup.style.left = `calc(50% - ${menuWidth/2}px)`;
                popup.style.top = `calc(50% - ${menuHeight/2}px)`;
                
                // 从本地存储读取之前保存的位置
                const savedPos = localStorage.getItem('MWI_Hit_Tracker_Menu_Pos');
                if (savedPos) {
                    try {
                        const pos = JSON.parse(savedPos);
                        // 确保位置合理,不超出屏幕
                        const maxLeft = window.innerWidth - menuWidth;
                        const maxTop = window.innerHeight - menuHeight;
                        if (pos.left >= 0 && pos.left <= maxLeft && 
                            pos.top >= 0 && pos.top <= maxTop) {
                            popup.style.left = `${pos.left}px`;
                            popup.style.top = `${pos.top}px`;
                        }
                    } catch (e) {
                        console.error('解析菜单位置失败:', e);
                    }
                }
            };
            
            // 设置弹出窗口的背景颜色
            popup.style.backgroundColor = '#f9f9f9';
            // 设置弹出窗口的内边距
            popup.style.padding = '30px';
            // 设置弹出窗口的边框样式
            popup.style.border = '2px solid #ddd';
            // 设置弹出窗口的边框圆角
            popup.style.borderRadius = '10px';
            // 设置弹出窗口的阴影效果
            popup.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
            // 设置弹出窗口的层级,确保其显示在最上层
            popup.style.zIndex = '9999';

            // ====== 菜单标题(风格与 MWI-Hit-Tracker-More-Animation.js 一致)======
            const titleBar = document.createElement('div');
            titleBar.style.position = 'absolute';
            titleBar.style.top = '0';
            titleBar.style.left = '0';
            titleBar.style.right = '0';
            titleBar.style.height = '36px';
            titleBar.style.backgroundColor = '#546ddb';
            titleBar.style.borderTopLeftRadius = '8px';
            titleBar.style.borderTopRightRadius = '8px';
            titleBar.style.cursor = 'move';
            titleBar.style.userSelect = 'none';
            titleBar.style.display = 'flex';
            titleBar.style.alignItems = 'center';
            titleBar.style.justifyContent = 'center';
            titleBar.style.padding = '0 10px';
            titleBar.style.color = 'white';
            titleBar.style.fontWeight = 'bold';
            titleBar.style.fontSize = '20px';
            titleBar.textContent = customColorButton.textContent;

            // 调整弹出窗口的内边距,为标题栏留出空间
            popup.style.paddingTop = '46px';

            // 将标题栏添加到弹出窗口
            popup.insertBefore(titleBar, popup.firstChild);

            // 修改窗口拖动处理逻辑,确保在移动设备上也能正常工作
            let isDragging = false;
            let startX, startY, initialLeft, initialTop;

            // 判断是否为可拖动区域(标题栏或背景)
            function isDraggableArea(target) {
                // 标题栏或弹窗本身允许拖动
                if (target === titleBar || target.closest('.expandable-section') || target === popup) return true;
                // 交互元素禁止拖动
                const tag = target.tagName;
                if (tag === 'INPUT' || tag === 'BUTTON' || tag === 'SELECT' || tag === 'LABEL') return false;
                // 滑块等自定义控件可加 class 判断
                if (target.classList.contains('no-drag')) return false;
                return true;
            }

            // 鼠标按下事件
            popup.addEventListener('mousedown', (e) => {
                if (!isDraggableArea(e.target)) return;
                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                initialLeft = popup.offsetLeft;
                initialTop = popup.offsetTop;
                document.body.style.userSelect = 'none';
            });

            // 鼠标移动事件
            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    e.preventDefault();
                    const dx = e.clientX - startX;
                    const dy = e.clientY - startY;
                    let newLeft = initialLeft + dx;
                    let newTop = initialTop + dy;
                    // 限制不超出窗口
                    const maxLeft = window.innerWidth - popup.offsetWidth;
                    const maxTop = window.innerHeight - popup.offsetHeight;
                    newLeft = Math.max(0, Math.min(newLeft, maxLeft));
                    newTop = Math.max(0, Math.min(newTop, maxTop));
                    popup.style.left = newLeft + 'px';
                    popup.style.top = newTop + 'px';
                }
            });

            // 鼠标释放事件
            document.addEventListener('mouseup', () => {
                if (isDragging) {
                    isDragging = false;
                    document.body.style.userSelect = '';
                    // 保存当前位置
                    localStorage.setItem('MWI_Hit_Tracker_Menu_Pos', JSON.stringify({
                        left: popup.offsetLeft,
                        top: popup.offsetTop
                    }));
                }
            });

            // 鼠标离开窗口也结束拖动
            document.addEventListener('mouseleave', () => {
                if (isDragging) {
                    isDragging = false;
                    document.body.style.userSelect = '';
                    localStorage.setItem('MWI_Hit_Tracker_Menu_Pos', JSON.stringify({
                        left: popup.offsetLeft,
                        top: popup.offsetTop
                    }));
                }
            });

            // 添加窗口大小改变监听器,确保菜单不超出屏幕
            window.addEventListener('resize', () => {
                if (popup.parentNode) {
                    // 重新计算位置
                    const maxLeft = window.innerWidth - popup.offsetWidth;
                    const maxTop = window.innerHeight - popup.offsetHeight;
                    let newLeft = popup.offsetLeft;
                    let newTop = popup.offsetTop;
                    // 如果菜单超出屏幕,调整位置
                    if (newLeft > maxLeft) newLeft = maxLeft;
                    if (newTop > maxTop) newTop = maxTop;
                    popup.style.left = `${newLeft}px`;
                    popup.style.top = `${newTop}px`;
                }
            });

            // 在DOM添加后调整位置
            setTimeout(positionMenu, 0);

            // 封装创建可展开部分的函数
            function createExpandableSection(title, contentGenerator = null) {
                // 创建可展开区域元素
                const expandableSection = document.createElement('div');
                // 为可展开区域元素设置类名
                expandableSection.className = 'expandable-section draggable';
                // 初始化展开状态为未展开
                let isExpanded = false;

                // 根据展开状态设置可展开区域的显示文本
                function updateExpandableSectionText() {
                    expandableSection.textContent = isExpanded ? `${title}          ▼` : `${title}           ▶`;
                }

                // 初始设置文本
                updateExpandableSectionText();

                // 创建可展开内容元素
                const expandableContent = document.createElement('div');
                // 为可展开内容元素设置类名
                expandableContent.className = 'expandable-content';

                // 如果有内容生成函数,则调用该函数生成内容
                if (contentGenerator) {
                    contentGenerator(expandableContent);
                }

                // 修改点击事件监听器,更新展开状态并更新文本,同时切换可展开内容的显示状态
                expandableSection.addEventListener('click', () => {
                    isExpanded = !isExpanded;
                    expandableContent.classList.toggle('show');
                    updateExpandableSectionText();
                });

                return { expandableSection, expandableContent };
            }

            // 生成玩家和颜色部分内容的函数
            function generatePlayerColorContent(expandableContent) {
                // 定义玩家名称数组,明确表示只控制线条绘制
                const players = ['玩家一线条', '玩家二线条', '玩家三线条', '玩家四线条', '玩家五线条', '敌人线条'];

                // 封装创建勾选框和标签的通用函数
                function createCheckboxAndLabel(container, labelText, checked, changeHandler) {
                    // 创建勾选框元素
                    const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.checked = checked;
                    checkbox.addEventListener('change', changeHandler);
                    container.appendChild(checkbox);

                    // 创建标签元素
                    const label = document.createElement('span');
                    label.textContent = labelText;
                    label.style.flex = '1';
                    label.style.fontSize = '14px';
                    label.style.marginLeft = '10px';
                    container.appendChild(label);
                }

                // 遍历玩家名称数组,为每个玩家创建颜色选择器和预览
                players.forEach((player, index) => {
                    // 创建容器元素,用于包裹勾选框、标签、颜色选择器和预览
                    const container = document.createElement('div');
                    container.style.marginBottom = '15px';
                    container.style.display = 'flex';
                    container.style.alignItems = 'center';

                    // 创建勾选框和标签
                    createCheckboxAndLabel(container, `${player}: `, playerDrawEnabled[index], (e) => {
                        playerDrawEnabled[index] = e.target.checked;
                        
                        // 保存设置,但不影响自定义弹道勾选框状态
                        saveSettings();
                    });

                    // 创建颜色选择器元素
                    const colorInput = document.createElement('input');
                    colorInput.type = 'color';
                    colorInput.value = lineColor[index];
                    colorInput.addEventListener('input', (e) => {
                        if (playerDrawEnabled[index]) {
                            lineColor[index] = e.target.value;
                            filterColor[index] = e.target.value.replace('1)', '0.8)');
                            saveSettings(); // 保存设置
                        }
                    });
                    colorInput.style.marginRight = '10px';

                    // 创建颜色预览元素
                    const preview = document.createElement('div');
                    preview.style.width = '30px';
                    preview.style.height = '30px';
                    preview.style.border = '1px solid #ccc';
                    preview.style.borderRadius = '4px';
                    preview.style.backgroundColor = lineColor[index];
                    colorInput.addEventListener('input', (e) => {
                        preview.style.backgroundColor = e.target.value;
                    });

                    // 将颜色选择器和预览元素添加到容器元素中
                    container.appendChild(colorInput);
                    container.appendChild(preview);

                    // 将容器元素添加到可展开内容元素中
                    expandableContent.appendChild(container);
                    
                    // 不为"敌人"创建自定义弹道选项
                    if (index < 5) {
                        // 创建自定义弹道图片控制区域
                        const projectileContainer = document.createElement('div');
                        projectileContainer.style.marginBottom = '15px';
                        projectileContainer.style.display = 'flex';
                        projectileContainer.style.alignItems = 'center';
                        projectileContainer.style.marginLeft = '30px'; // 缩进以表示从属关系
                        projectileContainer.style.padding = '6px 8px';
                        projectileContainer.style.borderRadius = '6px';
                        projectileContainer.style.transition = 'background-color 0.2s ease';
                        
                        // 添加粘贴事件监听,允许直接粘贴图片
                        projectileContainer.setAttribute('tabindex', '0'); // 使容器可以获取焦点
                        projectileContainer.style.outline = 'none'; // 去除获取焦点时的轮廓
                        
                        // 添加提示获取焦点的样式
                        projectileContainer.addEventListener('mouseenter', () => {
                            projectileContainer.style.backgroundColor = 'rgba(84, 109, 219, 0.1)';
                            // 添加提示边框
                            projectileContainer.style.border = '1px dashed rgba(84, 109, 219, 0.5)';
                            // 设置光标为可点击状态
                            projectileContainer.style.cursor = 'pointer';
                        });
                        
                        projectileContainer.addEventListener('mouseleave', () => {
                            projectileContainer.style.backgroundColor = 'transparent';
                            // 移除边框
                            projectileContainer.style.border = '1px solid transparent';
                            // 恢复默认光标
                            projectileContainer.style.cursor = 'default';
                        });
                        
                        // 获取焦点时的特效
                        projectileContainer.addEventListener('focus', () => {
                            projectileContainer.style.backgroundColor = 'rgba(84, 109, 219, 0.15)';
                            projectileContainer.style.border = '1px dashed rgba(84, 109, 219, 0.7)';
                        });
                        
                        projectileContainer.addEventListener('blur', () => {
                            projectileContainer.style.backgroundColor = 'transparent';
                            projectileContainer.style.border = '1px solid transparent';
                        });
                        
                        // 点击容器自动获取焦点
                        projectileContainer.addEventListener('click', (e) => {
                            // 只有当点击的不是按钮和勾选框时才设置焦点
                            if (e.target === projectileContainer || e.target === pasteHint || 
                                e.target.tagName.toLowerCase() === 'span') {
                                projectileContainer.focus();
                            }
                        });
                        
                        // 添加粘贴事件处理
                        projectileContainer.addEventListener('paste', (e) => {
                            // 检查剪贴板是否包含图片
                            const items = e.clipboardData.items;
                            let imageFile = null;
                            
                            for (let i = 0; i < items.length; i++) {
                                if (items[i].type.indexOf('image') !== -1) {
                                    imageFile = items[i].getAsFile();
                                    break;
                                }
                            }
                            
                            if (imageFile) {
                                e.preventDefault(); // 阻止默认粘贴行为
                                // 高亮容器表示成功粘贴
                                const originalBg = projectileContainer.style.backgroundColor;
                                projectileContainer.style.backgroundColor = 'rgba(75, 192, 192, 0.2)';
                                projectileContainer.style.border = '1px solid rgba(75, 192, 192, 0.5)';
                                
                                // 处理图片
                                processPlayerImage(imageFile, index, imagePreview, null);
                                
                                // 设置定时器,恢复原始颜色
                                setTimeout(() => {
                                    projectileContainer.style.backgroundColor = originalBg;
                                    projectileContainer.style.border = '1px solid transparent';
                                }, 1000);
                            }
                        });
                        
                        // 创建自定义弹道勾选框
                        let projectileCheckbox = null;
                        if (customProjectileImage || playerProjectileImages[index]) {
                            projectileCheckbox = document.createElement('input');
                            projectileCheckbox.type = 'checkbox';
                            projectileCheckbox.checked = playerUseProjectile[index];
                            // 移除禁用逻辑,让自定义弹道勾选框始终可用
                            // projectileCheckbox.disabled = !playerDrawEnabled[index];
                            
                            projectileCheckbox.addEventListener('change', (e) => {
                                playerUseProjectile[index] = e.target.checked;
                                saveSettings();
                            });
                            
                            projectileContainer.appendChild(projectileCheckbox);
                            
                            // 创建标签
                            const projectileLabel = document.createElement('span');
                            projectileLabel.textContent = '使用自定义弹道';
                            projectileLabel.style.flex = '1';
                            projectileLabel.style.fontSize = '14px';
                            projectileLabel.style.marginLeft = '10px';
                            projectileContainer.appendChild(projectileLabel);
                            
                            // 将类名添加到勾选框便于引用
                            projectileCheckbox.classList.add('使用自定义弹道');
                            
                            // 创建图片预览区域
                            const imagePreview = document.createElement('div');
                            imagePreview.style.width = '28px';
                            imagePreview.style.height = '28px';
                            imagePreview.style.border = '1px solid #ccc';
                            imagePreview.style.borderRadius = '4px';
                            imagePreview.style.overflow = 'hidden';
                            imagePreview.style.display = 'flex';
                            imagePreview.style.alignItems = 'center';
                            imagePreview.style.justifyContent = 'center';
                            imagePreview.style.marginLeft = '10px';
                            imagePreview.style.backgroundColor = '#f8f8f8';
                            imagePreview.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
                            imagePreview.style.position = 'relative'; // 为悬停提示添加
                            
                            // 判断使用哪个图片
                            const imageUrl = playerProjectileImages[index] || customProjectileImage;
                            
                            if (imageUrl) {
                                // 创建图片元素
                                const img = document.createElement('img');
                                img.src = imageUrl;
                                img.style.maxWidth = '100%';
                                img.style.maxHeight = '100%';
                                
                                imagePreview.appendChild(img);
                            } else {
                                // 如果没有图片,显示提示文本
                                const placeholderText = document.createElement('span');
                                placeholderText.textContent = '?';
                                placeholderText.style.color = '#aaa';
                                placeholderText.style.fontSize = '14px';
                                placeholderText.style.fontWeight = 'bold';
                                
                                imagePreview.appendChild(placeholderText);
                                
                                // 添加悬停提示
                                imagePreview.title = '点击右侧按钮上传图片或粘贴图片';
                            }
                            
                            // 添加图片预览悬停效果
                            imagePreview.addEventListener('mouseenter', () => {
                                imagePreview.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
                                imagePreview.style.borderColor = '#546ddb';
                            });
                            
                            imagePreview.addEventListener('mouseleave', () => {
                                imagePreview.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
                                imagePreview.style.borderColor = '#ccc';
                            });
                            
                            projectileContainer.appendChild(imagePreview);
                            
                            // 创建按钮组容器
                            const buttonGroup = document.createElement('div');
                            buttonGroup.style.display = 'flex';
                            buttonGroup.style.marginLeft = '8px';
                            
                            // 设置按钮样式的通用函数
                            const styleButton = (btn, text, tooltip) => {
                                btn.textContent = text;
                                btn.title = tooltip;
                                btn.style.width = '28px';
                                btn.style.height = '28px';
                                btn.style.padding = '0';
                                btn.style.backgroundColor = '#546ddb';
                                btn.style.color = 'white';
                                btn.style.border = 'none';
                                btn.style.borderRadius = '4px';
                                btn.style.cursor = 'pointer';
                                btn.style.fontSize = '12px';
                                btn.style.fontWeight = 'bold';
                                btn.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
                                btn.style.transition = 'all 0.2s ease';
                                
                                // 添加悬停效果
                                btn.addEventListener('mouseenter', () => {
                                    btn.style.backgroundColor = '#4056a1';
                                    btn.style.transform = 'translateY(-1px)';
                                    btn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
                                });
                                
                                btn.addEventListener('mouseleave', () => {
                                    btn.style.backgroundColor = '#546ddb';
                                    btn.style.transform = 'translateY(0)';
                                    btn.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
                                });
                                
                                // 添加点击效果
                                btn.addEventListener('mousedown', () => {
                                    btn.style.transform = 'translateY(1px)';
                                    btn.style.boxShadow = '0 1px 2px rgba(0,0,0,0.2)';
                                });
                                
                                btn.addEventListener('mouseup', () => {
                                    btn.style.transform = 'translateY(-1px)';
                                    btn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
                                });
                                
                                return btn;
                            };
                            
                            // 添加上传专属图片按钮
                            const uploadButton = styleButton(
                                document.createElement('button'),
                                '+',
                                '为该玩家上传专属图片'
                            );
                            
                            uploadButton.addEventListener('click', () => {
                                // 打开图片上传器,不自动修改勾选框状态
                                openPlayerImageUploader(index, imagePreview, null);
                            });
                            
                            buttonGroup.appendChild(uploadButton);
                            
                            // 添加从剪贴板获取图片的按钮
                            const clipboardButton = styleButton(
                                document.createElement('button'),
                                '剪',
                                '从剪贴板获取图片'
                            );
                            clipboardButton.style.marginLeft = '5px';
                            
                            clipboardButton.addEventListener('click', async () => {
                                try {
                                    // 检查是否支持剪贴板API
                                    if (!navigator.clipboard || !navigator.clipboard.read) {
                                        alert('您的浏览器不支持直接访问剪贴板。请使用上传按钮或Ctrl+V粘贴图片。');
                                        return;
                                    }
                                    
                                    // 获取剪贴板内容
                                    const clipboardItems = await navigator.clipboard.read();
                                    let imageFound = false;
                                    
                                    for (const clipboardItem of clipboardItems) {
                                        // 检查剪贴板是否包含图片
                                        const imageTypes = clipboardItem.types.filter(type => type.startsWith('image/'));
                                        if (imageTypes.length > 0) {
                                            // 获取图片的Blob
                                            const imageBlob = await clipboardItem.getType(imageTypes[0]);
                                            // 处理图片
                                            processPlayerImage(imageBlob, index, imagePreview, null);
                                            imageFound = true;
                                            break;
                                        }
                                    }
                                    
                                    if (!imageFound) {
                                        alert('剪贴板中未找到图片。请复制一张图片后重试。');
                                    }
                                } catch (err) {
                                    console.error('获取剪贴板内容失败:', err);
                                    alert('获取剪贴板内容失败。请尝试使用上传按钮。');
                                }
                            });
                            
                            buttonGroup.appendChild(clipboardButton);
                            projectileContainer.appendChild(buttonGroup);

                        }
                        
                        expandableContent.appendChild(projectileContainer);
                    }
                });
                
                // 添加重置按钮
                const resetButton = document.createElement('button');
                resetButton.textContent = '重置为默认颜色';
                resetButton.style.marginTop = '15px';
                resetButton.style.padding = '5px 10px';
                resetButton.style.backgroundColor = '#546ddb';
                resetButton.style.color = 'white';
                resetButton.style.border = 'none';
                resetButton.style.borderRadius = '4px';
                resetButton.style.cursor = 'pointer';

                resetButton.addEventListener('click', () => {
                    // 使用colorConfig重置颜色数组,只重置颜色相关的设置
                    colorConfig.resetColors(lineColor, filterColor);

                    // 重置玩家显示状态数组,全部设置为勾选状态
                    playerDrawEnabled.fill(true);
                    
                    // 保存重置后的设置
                    saveSettings();

                    // 更新颜色选择器和预览
                    // 获取玩家和颜色可展开内容中的所有颜色选择器元素
                    const colorInputs = expandableContent.querySelectorAll('input[type="color"]');
                    // 获取玩家和颜色可展开内容中的所有颜色预览元素
                    const previews = expandableContent.querySelectorAll('div:last-child');
                    // 遍历颜色选择器和预览元素,更新其值和背景颜色
                    colorInputs.forEach((input, index) => {
                        input.value = lineColor[index];
                        previews[index].style.backgroundColor = lineColor[index];
                    });

                    // 更新玩家勾选框状态
                    const playerCheckboxes = expandableContent.querySelectorAll('input[type="checkbox"]:not(.使用自定义弹道)');
                    playerCheckboxes.forEach(checkbox => {
                        checkbox.checked = true;
                    });
                });

                expandableContent.appendChild(resetButton);
            }

            // 生成特效自定义部分内容的函数
            function generateEffectCustomContent(expandableContent) {
                // 封装创建勾选框和标签的通用函数
                function createCheckboxAndLabel(container, labelText, checked, changeHandler) {
                    // 创建勾选框元素
                    const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.checked = checked;
                    checkbox.addEventListener('change', changeHandler);
                    container.appendChild(checkbox);

                    // 创建标签元素
                    const label = document.createElement('span');
                    label.textContent = labelText;
                    label.style.flex = '1';
                    label.style.fontSize = '14px';
                    label.style.marginLeft = '10px';
                    container.appendChild(label);
                }

                // 创建特效勾选框的函数
                function createEffectCheckbox(effectName, index) {
                    const container = document.createElement('div');
                    container.style.marginBottom = '15px';
                    container.style.display = 'flex';
                    container.style.alignItems = 'center';

                    // 创建勾选框和标签
                    createCheckboxAndLabel(container, effectName, effectDrawEnabled[index], (e) => {
                        effectDrawEnabled[index] = e.target.checked;
                        saveSettings(); // 保存设置

                        // 同步更新参数菜单中的特效显示状态
                        updateEffectParamSection(index, e.target.checked);
                        
                        // 如果是隐藏数字选项,立即应用隐藏/显示效果
                        if (index === 5) {
                            toggleDamageNumbers(e.target.checked);
                        }
                    });

                    return container;
                }

                // 更新参数菜单中特效显示状态的函数
                function updateEffectParamSection(effectIndex, isEnabled) {
                    // 获取参数菜单的展开内容
                    const paramMenuContainer = document.querySelector('.自定义菜单');
                    if (!paramMenuContainer) return;
                    
                    // 找到特效参数面板,查找在所有可展开内容中
                    const paramSections = paramMenuContainer.querySelectorAll('.expandable-content');
                    let effectParamsContent = null;
                    
                    // 遍历找到特效参数的内容区域
                    paramSections.forEach(section => {
                        // 寻找包含"特效参数"标题的展开区
                        const sectionHeader = section.previousElementSibling;
                        if (sectionHeader && sectionHeader.textContent.includes('特效参数')) {
                            effectParamsContent = section;
                        }
                    });
                    
                    if (!effectParamsContent) return;
                    
                    // 映射effectIndex到效果名称
                    const effectNames = {
                        0: '伤害数字',
                        1: '路径绘制',
                        2: '粒子拖尾',
                        3: '击中特效',
                        4: '震动',
                        5: '隐藏数字区域',
                        6: '浮动数字'
                    };
                    
                    const effectName = effectNames[effectIndex];
                    if (!effectName) return;
                    
                    // 查找对应的参数区域
                    const allParamSections = effectParamsContent.querySelectorAll('.inner-expandable-section');
                    let targetSection = null;
                    
                    allParamSections.forEach(section => {
                        const titleElement = section.querySelector('span');
                        if (titleElement && titleElement.textContent === `${effectName}参数`) {
                            targetSection = section;
                        }
                    });
                    
                    // 如果找到了目标区域
                    if (targetSection) {
                        if (isEnabled) {
                            // 如果启用特效,显示参数区域
                            targetSection.style.display = 'block';
                        } else {
                            // 如果禁用特效,隐藏参数区域
                            targetSection.style.display = 'none';
                        }
                    } else if (isEnabled) {
                        // 如果启用了特效但没找到对应的参数区域,可能需要重新生成整个参数菜单
                        // 清空并重新生成参数菜单内容
                        effectParamsContent.innerHTML = '';
                        generateEffectParamsContent(effectParamsContent);
                    }
                }

                // 创建两列布局容器
                const columnContainer = document.createElement('div');
                columnContainer.style.display = 'flex';
                columnContainer.style.flexWrap = 'wrap';
                columnContainer.style.gap = '10px';
                
                // 创建左列和右列
                const leftColumn = document.createElement('div');
                leftColumn.style.flex = '1';
                leftColumn.style.minWidth = '120px';
                
                const rightColumn = document.createElement('div');
                rightColumn.style.flex = '1';
                rightColumn.style.minWidth = '120px';
                
                // 创建线条绘制特效勾选框
                const lineDrawContainer = createEffectCheckbox('路径绘制', 1);
                leftColumn.appendChild(lineDrawContainer);

                // 创建伤害数字特效勾选框
                const damageNumberContainer = createEffectCheckbox('伤害数字', 0);
                leftColumn.appendChild(damageNumberContainer);

                // 创建粒子拖尾特效勾选框(作为伤害数字的子菜单)
                const particleTrailContainer = createEffectCheckbox('粒子拖尾', 2);
                particleTrailContainer.style.marginLeft = '20px'; // 缩进,表示是子菜单
                leftColumn.appendChild(particleTrailContainer);

                // 创建击中特效勾选框
                const hitEffectContainer = createEffectCheckbox('击中特效', 3);
                leftColumn.appendChild(hitEffectContainer);
                
                // 创建浮动数字勾选框
                const floatingNumberContainer = createEffectCheckbox('浮动数字', 6);
                rightColumn.appendChild(floatingNumberContainer);
                
                // 创建震动特效勾选框
                const shakeEffectContainer = createEffectCheckbox('震动', 4);
                rightColumn.appendChild(shakeEffectContainer);

                // 创建隐藏数字勾选框
                const hideDamageContainer = createEffectCheckbox('隐藏数字区域', 5);
                rightColumn.appendChild(hideDamageContainer);
                
                // 添加列到容器
                columnContainer.appendChild(leftColumn);
                columnContainer.appendChild(rightColumn);
                
                // 将列容器添加到主容器
                expandableContent.appendChild(columnContainer);
            }

            // 生成特效参数自定义部分内容的函数
            function generateEffectParamsContent(expandableContent) {
                // 定义特效参数配置
                const effectParams = {
                    '路径绘制': {
                        enabled: effectDrawEnabled[1],
                        params: [
                            { name: '显示时间(ms)', id: 'lineDuration', min: 100, max: 3000, value: 1000, step: 100 },
                            { name: '随机弯曲方向', id: 'randomCurve', type: 'checkbox', value: true },
                            { name: '最小弯曲程度', id: 'minCurveIntensity', min: 1, max: 15, value: 3, step: 0.5 },
                            { name: '最大弯曲程度', id: 'maxCurveIntensity', min: 0.5, max: 10, value: 7, step: 0.5 }
                        ]
                    },
                    '击中特效': {
                        enabled: effectDrawEnabled[3],
                        params: [
                            { name: '粒子数量', id: 'particleCount', min: 5, max: 50, value: 20, step: 1 },
                            { name: '粒子半径', id: 'particleRadius', min: 1, max: 5, value: 2, step: 0.5 },
                            { name: '最大距离', id: 'maxDistance', min: 5, max: 40, value: 20, step: 1 },
                            { name: '批次数量', id: 'batchCount', min: 1, max: 8, value: 4, step: 1 }
                        ]
                    },
                    '粒子拖尾': {
                        enabled: effectDrawEnabled[2],
                        params: [
                            { name: '粒子数量', id: 'trailParticleCount', min: 3, max: 20, value: 6, step: 1 },
                            { name: '持续时间(ms)', id: 'trailDuration', min: 100, max: 1000, value: 500, step: 50 },
                            { name: '扩散范围', id: 'trailSpreadRange', min: 5, max: 30, value: 8, step: 1 }
                        ]
                    },
                    '伤害数字': {
                        enabled: effectDrawEnabled[0],
                        params: [
                            { name: '显示时间(ms)', id: 'damageDisplayDuration', min: 500, max: 5000, value: 1350, step: 50 },
                            { name: '动画帧数', id: 'damageFrames', min: 10, max: 120, value: 30, step: 5 },
                            { name: '描边宽度', id: 'damageStrokeWidth', min: 0, max: 5, value: 1.5, step: 0.5 },
                            { name: '描边颜色', id: 'damageStrokeColor', type: 'color', value: '#FFFFFF' },
                            { name: '结束缩放比例', id: 'damageEndScale', min: 1, max: 3, value: 1.5, step: 0.1 },
                            { name: '淡出时间(ms)', id: 'damageEndFadeDuration', min: 100, max: 1000, value: 200, step: 50 }
                        ]
                    },
                    '浮动数字': {
                        enabled: effectDrawEnabled[6],
                        params: [
                            { name: '显示时间(ms)', id: 'floatingDuration', min: 500, max: 5000, value: 1000, step: 100 },
                            { name: '字号大小', id: 'floatingFontSize', min: 10, max: 50, value: 30, step: 1 },
                            { name: '描边宽度', id: 'floatingStrokeWidth', min: 0, max: 5, value: 2, step: 0.5 },
                            { name: '描边颜色', id: 'floatingStrokeColor', type: 'color', value: '#FFFFFF' },
                            { name: '上升高度(px)', id: 'floatingRiseHeight', min: 10, max: 100, value: 20, step: 1 },
                            { name: '透明度延迟(%)', id: 'floatingOpacityDelay', min: 0, max: 100, value: 50, step: 5 }
                        ]
                    },
                    '自定义弹道': {
                        enabled: useCustomProjectileImage && customProjectileImage !== null,
                        params: [
                            { name: '飞行时间(ms)', id: 'projectileDuration', min: 500, max: 3000, value: 1000, step: 100 },
                            { name: '是否旋转图片', id: 'rotateProjectile', type: 'checkbox', value: true },
                            { name: '发射延迟(ms)', id: 'projectileDelay', min: 0, max: 500, value: 50, step: 10 },
                            { name: '淡出时间(ms)', id: 'projectileFadeDuration', min: 100, max: 1000, value: 500, step: 50 },
                            { name: '图片大小(px)', id: 'projectileSize', min: 20, max: 100, value: 40, step: 5, description: '图片最短边的长度,图片将按等比例缩放' },
                            { name: '敌人使用图片', id: 'enemyProjectile', type: 'checkbox', value: true },
                            { name: '启用弹道拖尾', id: 'projectileTrailEnabled', type: 'checkbox', value: true },
                            { name: '拖尾数量', id: 'projectileTrailCount', min: 1, max: 20, value: 5, step: 1 },
                            { name: '拖尾淡出时间(ms)', id: 'projectileTrailFadeTime', min: 100, max: 1000, value: 300, step: 50 },
                            { name: '拖尾生成间隔(ms)', id: 'projectileTrailInterval', min: 10, max: 200, value: 50, step: 10 }
                        ]
                    },
                    '震动': {
                        enabled: effectDrawEnabled[4],
                        params: [
                            { name: '震动强度', id: 'shakeIntensity', min: 1, max: 10, value: 5, step: 1 },
                            { name: '震动时间(ms)', id: 'shakeDuration', min: 100, max: 500, value: 300, step: 50 }
                        ]
                    }
                };

                // 从localStorage读取已保存的参数设置
                const savedParams = localStorage.getItem('MWI_Hit_Tracker_Effect_Params');
                let effectParamsValues = {};

                if (savedParams) {
                    try {
                        effectParamsValues = JSON.parse(savedParams);
                    } catch (e) {
                        console.error('解析保存的特效参数失败:', e);
                        effectParamsValues = {};
                    }
                }

                // 全局参数对象,用于存储当前参数值
                window.hitTrackerEffectParams = window.hitTrackerEffectParams || {};

                // 创建内部可展开区域的函数
                function createInnerExpandableSection(title, isEnabled, effectIndex) {
                    const section = document.createElement('div');
                    section.className = 'inner-expandable-section';
                    section.style.backgroundColor = '#e8e8e8';
                    section.style.padding = '8px';
                    section.style.borderRadius = '4px';
                    section.style.marginBottom = '10px';
                    section.style.cursor = 'pointer';
                    section.dataset.effectIndex = effectIndex;

                    const headerDiv = document.createElement('div');
                    headerDiv.style.display = 'flex';
                    headerDiv.style.justifyContent = 'space-between';
                    headerDiv.style.alignItems = 'center';

                    const titleSpan = document.createElement('span');
                    titleSpan.textContent = `${title}参数`;
                    titleSpan.style.fontWeight = 'bold';

                    const arrowSpan = document.createElement('span');
                    arrowSpan.textContent = '▶';
                    arrowSpan.style.transition = 'transform 0.3s';

                    headerDiv.appendChild(titleSpan);
                    headerDiv.appendChild(arrowSpan);
                    section.appendChild(headerDiv);

                    const content = document.createElement('div');
                    content.className = 'inner-expandable-content';
                    content.style.display = 'none';
                    content.style.marginTop = '10px';
                    content.style.paddingLeft = '10px';
                    section.appendChild(content);

                    // 点击展开/收起
                    section.addEventListener('click', (e) => {
                        // 防止点击内部控件时触发展开/收起
                        if (e.target.tagName === 'INPUT' ||
                            e.target.closest('.param-slider-container') !== null) {
                            return;
                        }

                        const isExpanded = content.style.display !== 'none';
                        content.style.display = isExpanded ? 'none' : 'block';
                        arrowSpan.textContent = isExpanded ? '▶' : '▼';
                        arrowSpan.style.transform = isExpanded ? 'rotate(0deg)' : 'rotate(90deg)';
                    });

                    return { section, content };
                }

                // 为每个特效创建参数调整区域
                Object.keys(effectParams).forEach(effectName => {
                    // 检查特效是否启用
                    let effectEnabled = false;
                    let effectIndex = -1;
                    if (effectName === '击中特效') {
                        effectEnabled = effectDrawEnabled[3];
                        effectIndex = 3;
                    } else if (effectName === '粒子拖尾') {
                        effectEnabled = effectDrawEnabled[2];
                        effectIndex = 2;
                    } else if (effectName === '伤害数字') {
                        effectEnabled = effectDrawEnabled[0];
                        effectIndex = 0;
                    } else if (effectName === '自定义弹道') {
                        effectEnabled = useCustomProjectileImage && customProjectileImage !== null;
                        effectIndex = 5; // 使用新的索引
                    } else if (effectName === '震动') {
                        effectEnabled = effectDrawEnabled[4];
                        effectIndex = 4;
                    } else if (effectName === '路径绘制') {
                        effectEnabled = effectDrawEnabled[1];
                        effectIndex = 1;
                    } else if (effectName === '浮动数字') {
                        effectEnabled = effectDrawEnabled[6];
                        effectIndex = 6;
                    }

                    // 如果特效被禁用,则不显示该特效的参数区域
                    if (!effectEnabled) {
                        return;
                    }

                    // 创建特效参数组
                    const { section, content } = createInnerExpandableSection(effectName, effectEnabled, effectIndex);
                    section.id = `effect-param-section-${effectIndex}`;
                    
                    // 如果特效被禁用,则隐藏该特效的参数区域
                    if (!effectEnabled) {
                        section.style.display = 'none';
                    }
                    
                    expandableContent.appendChild(section);

                    // 为每个参数创建滑块控制
                    effectParams[effectName].params.forEach(param => {
                        // 从保存的设置中读取值,如果没有则使用默认值
                        const savedValue = effectParamsValues[param.id];
                        param.value = savedValue !== undefined ? savedValue : param.value;

                        // 保存到全局参数对象
                        window.hitTrackerEffectParams[param.id] = param.value;

                        // 创建参数容器
                        const paramContainer = document.createElement('div');
                        paramContainer.className = 'param-slider-container';
                        paramContainer.style.marginBottom = '10px';

                        // 创建参数名称和当前值显示
                        const paramLabel = document.createElement('div');
                        paramLabel.style.display = 'flex';
                        paramLabel.style.justifyContent = 'space-between';
                        paramLabel.style.marginBottom = '5px';

                        const nameSpan = document.createElement('span');
                        nameSpan.textContent = param.name;
                        paramLabel.appendChild(nameSpan);

                        const valueSpan = document.createElement('span');
                        if (param.type !== 'color') {
                            valueSpan.textContent = param.value;
                        } else {
                            valueSpan.textContent = '';
                        }
                        valueSpan.id = `${param.id}-value`;
                        paramLabel.appendChild(valueSpan);

                        paramContainer.appendChild(paramLabel);
                        
                        // 如果有描述信息,添加描述文本
                        if (param.description) {
                            const descriptionDiv = document.createElement('div');
                            descriptionDiv.style.fontSize = '12px';
                            descriptionDiv.style.color = '#666';
                            descriptionDiv.style.marginBottom = '5px';
                            descriptionDiv.style.fontStyle = 'italic';
                            descriptionDiv.textContent = param.description;
                            paramContainer.appendChild(descriptionDiv);
                        }

                        // 根据参数类型创建不同的控件
                        if (param.type === 'color') {
                            // 创建颜色选择器
                            const colorInput = document.createElement('input');
                            colorInput.type = 'color';
                            colorInput.value = param.value;
                            colorInput.style.width = '100%';
                            colorInput.className = 'no-drag';

                            // 添加颜色选择器事件监听
                            colorInput.addEventListener('input', (e) => {
                                const newValue = e.target.value;
                                window.hitTrackerEffectParams[param.id] = newValue;

                                // 保存参数设置
                                saveEffectParams();
                            });

                            paramContainer.appendChild(colorInput);
                        } else if (param.type === 'checkbox') {
                            // 创建复选框控件
                            const checkboxContainer = document.createElement('div');
                            checkboxContainer.style.display = 'flex';
                            checkboxContainer.style.alignItems = 'center';
                            checkboxContainer.style.width = '100%';
                            
                            const checkbox = document.createElement('input');
                            checkbox.type = 'checkbox';
                            checkbox.id = param.id;
                            checkbox.checked = param.value;
                            checkbox.className = 'no-drag';
                            
                            const checkboxLabel = document.createElement('label');
                            checkboxLabel.htmlFor = param.id;
                            checkboxLabel.textContent = '开启';
                            checkboxLabel.style.marginLeft = '10px';
                            
                            // 添加复选框事件监听
                            checkbox.addEventListener('change', (e) => {
                                const newValue = e.target.checked;
                                window.hitTrackerEffectParams[param.id] = newValue;
                                
                                // 更新显示值
                                valueSpan.textContent = newValue ? '是' : '否';
                                
                                // 保存参数设置
                                saveEffectParams();
                            });
                            
                            // 设置初始显示值
                            valueSpan.textContent = param.value ? '是' : '否';
                            
                            checkboxContainer.appendChild(checkbox);
                            checkboxContainer.appendChild(checkboxLabel);
                            paramContainer.appendChild(checkboxContainer);
                        } else {
                            // 创建滑块控制容器,用于布局滑块和上下箭头
                            const sliderControlContainer = document.createElement('div');
                            sliderControlContainer.style.display = 'flex';
                            sliderControlContainer.style.alignItems = 'center';
                            sliderControlContainer.style.width = '100%';

                            // 创建滑块
                            const slider = document.createElement('input');
                            slider.type = 'range';
                            slider.min = param.min;
                            slider.max = param.max;
                            slider.step = param.step;
                            slider.value = param.value;
                            slider.style.flex = '1'; // 让滑块占据主要空间
                            slider.style.marginRight = '10px'; // 与微调按钮保持一定距离
                            slider.className = 'no-drag'; // 防止拖动滑块时拖动整个菜单

                            // 创建微调按钮容器
                            const adjustButtonsContainer = document.createElement('div');
                            adjustButtonsContainer.style.display = 'flex';
                            adjustButtonsContainer.style.flexDirection = 'column';
                            adjustButtonsContainer.style.width = '20px';

                            // 创建上箭头按钮
                            const upButton = document.createElement('button');
                            upButton.innerHTML = '▲';
                            upButton.style.fontSize = '10px';
                            upButton.style.padding = '0';
                            upButton.style.height = '16px';
                            upButton.style.border = '1px solid #999';
                            upButton.style.borderRadius = '3px';
                            upButton.style.backgroundColor = '#546ddb'; // 使用脚本中已有的主题蓝色
                            upButton.style.color = 'white'; // 白色文字
                            upButton.style.cursor = 'pointer';
                            upButton.style.marginBottom = '2px';
                            upButton.style.boxShadow = '0 1px 2px rgba(0,0,0,0.1)';
                            upButton.className = 'no-drag';

                            // 添加悬停效果
                            upButton.addEventListener('mouseover', () => {
                                upButton.style.backgroundColor = '#4056a1'; // 深一点的蓝色
                            });
                            upButton.addEventListener('mouseout', () => {
                                upButton.style.backgroundColor = '#546ddb';
                            });

                            // 创建下箭头按钮
                            const downButton = document.createElement('button');
                            downButton.innerHTML = '▼';
                            downButton.style.fontSize = '10px';
                            downButton.style.padding = '0';
                            downButton.style.height = '16px';
                            downButton.style.border = '1px solid #999';
                            downButton.style.borderRadius = '3px';
                            downButton.style.backgroundColor = '#546ddb'; // 使用脚本中已有的主题蓝色
                            downButton.style.color = 'white'; // 白色文字
                            downButton.style.cursor = 'pointer';
                            downButton.style.boxShadow = '0 1px 2px rgba(0,0,0,0.1)';
                            downButton.className = 'no-drag';

                            // 添加悬停效果
                            downButton.addEventListener('mouseover', () => {
                                downButton.style.backgroundColor = '#4056a1'; // 深一点的蓝色
                            });
                            downButton.addEventListener('mouseout', () => {
                                downButton.style.backgroundColor = '#546ddb';
                            });

                            // 添加箭头按钮功能
                            function adjustValue(increment) {
                                // 获取当前值
                                let currentValue = parseFloat(slider.value);
                                // 根据步长调整值
                                let step = parseFloat(param.step);
                                // 计算新值
                                let newValue = increment ? currentValue + step : currentValue - step;
                                // 确保新值在范围内
                                newValue = Math.max(parseFloat(param.min), Math.min(parseFloat(param.max), newValue));
                                // 更新滑块值
                                slider.value = newValue;
                                // 更新显示值
                                valueSpan.textContent = newValue;
                                // 更新存储的参数值
                                window.hitTrackerEffectParams[param.id] = newValue;
                                // 保存参数设置
                                saveEffectParams();
                            }

                            // 点击上箭头增加值
                            upButton.addEventListener('click', (e) => {
                                e.stopPropagation(); // 防止触发父元素点击事件
                                adjustValue(true);
                            });

                            // 点击下箭头减少值
                            downButton.addEventListener('click', (e) => {
                                e.stopPropagation(); // 防止触发父元素点击事件
                                adjustValue(false);
                            });

                            // 添加到微调按钮容器
                            adjustButtonsContainer.appendChild(upButton);
                            adjustButtonsContainer.appendChild(downButton);

                            // 添加滑块事件监听
                            slider.addEventListener('input', (e) => {
                                const newValue = parseFloat(e.target.value);
                                valueSpan.textContent = newValue;
                                window.hitTrackerEffectParams[param.id] = newValue;

                                // 保存参数设置
                                saveEffectParams();
                            });

                            // 组装控件
                            sliderControlContainer.appendChild(slider);
                            sliderControlContainer.appendChild(adjustButtonsContainer);
                            paramContainer.appendChild(sliderControlContainer);
                        }
                        content.appendChild(paramContainer);
                    });
                });

                // 添加重置按钮
                const resetButton = document.createElement('button');
                resetButton.textContent = '重置为默认值';
                resetButton.style.marginTop = '15px';
                resetButton.style.padding = '5px 10px';
                resetButton.style.backgroundColor = '#546ddb';
                resetButton.style.color = 'white';
                resetButton.style.border = 'none';
                resetButton.style.borderRadius = '4px';
                resetButton.style.cursor = 'pointer';

                resetButton.addEventListener('click', () => {
                    // 重置所有参数为默认值
                    Object.keys(effectParams).forEach(effectName => {
                        effectParams[effectName].params.forEach(param => {
                            const defaultValue = param.defaultValue || param.value; // 使用预定义的默认值
                            window.hitTrackerEffectParams[param.id] = defaultValue;

                            // 更新UI
                            const valueSpan = document.getElementById(`${param.id}-value`);
                            if (valueSpan && param.type !== 'color') {
                                valueSpan.textContent = defaultValue;
                            }

                            // 更新控件值
                            if (param.type === 'color') {
                                // 更新颜色选择器
                                const colorInput = document.querySelector(`input[type="color"][value="${window.hitTrackerEffectParams[param.id]}"]`);
                                if (colorInput) colorInput.value = defaultValue;
                            } else {
                                // 更新滑块
                                const paramLabel = document.getElementById(`${param.id}-value`);
                                if (paramLabel) {
                                    const sliderContainer = paramLabel.closest('.param-slider-container');
                                    if (sliderContainer) {
                                        const slider = sliderContainer.querySelector('input[type="range"]');
                                        if (slider) slider.value = defaultValue;
                                    }
                                }
                            }
                        });
                    });

                    // 保存参数设置
                    saveEffectParams();
                });

                expandableContent.appendChild(resetButton);
            }

            // 保存特效参数设置
            function saveEffectParams() {
                localStorage.setItem('MWI_Hit_Tracker_Effect_Params', JSON.stringify(window.hitTrackerEffectParams));
            }
    
            // 生成自定义弹道内容的函数
            function generateCustomProjectileContent(expandableContent) {
                // 创建容器
                const container = document.createElement('div');
                container.style.padding = '10px 0';
                
                // 创建标题
                const title = document.createElement('h3');
                title.textContent = '上传自定义弹道图片';
                title.style.margin = '0 0 10px 0';
                title.style.fontSize = '14px';
                container.appendChild(title);
                
                // 创建说明文字
                const description = document.createElement('p');
                description.textContent = '此处上传的是通用弹道图片,您也可以在"玩家和颜色"菜单中为每个玩家单独设置专属弹道图片。';
                description.style.margin = '0 0 15px 0';
                description.style.fontSize = '12px';
                description.style.color = '#666';
                container.appendChild(description);
                
                // 创建启用自定义弹道的复选框
                const enableContainer = document.createElement('div');
                enableContainer.style.display = 'flex';
                enableContainer.style.alignItems = 'center';
                enableContainer.style.marginBottom = '15px';
                
                const enableCheckbox = document.createElement('input');
                enableCheckbox.type = 'checkbox';
                enableCheckbox.checked = useCustomProjectileImage;
                enableCheckbox.id = 'enable-custom-projectile';
                
                const enableLabel = document.createElement('label');
                enableLabel.textContent = '启用自定义弹道图片';
                enableLabel.htmlFor = 'enable-custom-projectile';
                enableLabel.style.marginLeft = '10px';
                
                enableContainer.appendChild(enableCheckbox);
                enableContainer.appendChild(enableLabel);
                container.appendChild(enableContainer);
                
                // 添加复选框事件监听
                enableCheckbox.addEventListener('change', (e) => {
                    useCustomProjectileImage = e.target.checked;
                    saveSettings();
                    
                    // 同步更新特效参数菜单中的自定义弹道显示状态
                    const paramMenuContainer = document.querySelector('.自定义菜单');
                    if (paramMenuContainer) {
                        // 找到特效参数面板
                        const paramSections = paramMenuContainer.querySelectorAll('.expandable-content');
                        let effectParamsContent = null;
                        
                        // 遍历找到特效参数的内容区域
                        paramSections.forEach(section => {
                            const sectionHeader = section.previousElementSibling;
                            if (sectionHeader && sectionHeader.textContent.includes('特效参数')) {
                                effectParamsContent = section;
                            }
                        });
                        
                        if (effectParamsContent) {
                            // 查找自定义弹道参数区域
                            const allParamSections = effectParamsContent.querySelectorAll('.inner-expandable-section');
                            let targetSection = null;
                            
                            allParamSections.forEach(section => {
                                const titleElement = section.querySelector('span');
                                if (titleElement && titleElement.textContent === '自定义弹道参数') {
                                    targetSection = section;
                                }
                            });
                            
                            // 根据启用状态和图片状态显示或隐藏参数区域
                            if (targetSection) {
                                const isEnabled = useCustomProjectileImage && customProjectileImage !== null;
                                targetSection.style.display = isEnabled ? 'block' : 'none';
                            } else if (useCustomProjectileImage && customProjectileImage !== null) {
                                // 如果启用了特效但没找到对应的参数区域,重新生成菜单
                                effectParamsContent.innerHTML = '';
                                generateEffectParamsContent(effectParamsContent);
                            }
                        }
                    }
                });
                
                // 创建图片预览区域
                const previewContainer = document.createElement('div');
                previewContainer.style.marginBottom = '15px';
                previewContainer.style.textAlign = 'center';
                
                const previewTitle = document.createElement('div');
                previewTitle.textContent = '图片预览:';
                previewTitle.style.marginBottom = '5px';
                previewTitle.style.textAlign = 'left';
                previewContainer.appendChild(previewTitle);
                
                const previewArea = document.createElement('div');
                previewArea.style.width = '100px';
                previewArea.style.height = '100px';
                previewArea.style.border = '1px dashed #ccc';
                previewArea.style.borderRadius = '4px';
                previewArea.style.display = 'flex';
                previewArea.style.alignItems = 'center';
                previewArea.style.justifyContent = 'center';
                previewArea.style.margin = '0 auto';
                previewArea.style.backgroundColor = '#f5f5f5';
                previewArea.style.position = 'relative';
                
                // 添加预览图片
                const previewImage = document.createElement('img');
                previewImage.style.maxWidth = '40px';
                previewImage.style.maxHeight = '40px';
                previewImage.style.display = 'none';
                
                // 如果已有自定义图片,则显示
                if (customProjectileImage) {
                    previewImage.src = customProjectileImage;
                    previewImage.style.display = 'block';
                }
                
                // 默认提示文字
                const previewText = document.createElement('span');
                previewText.textContent = '无图片';
                previewText.style.color = '#999';
                previewText.style.fontSize = '12px';
                
                // 添加缩放后的图片尺寸显示
                const sizeInfo = document.createElement('div');
                sizeInfo.style.position = 'absolute';
                sizeInfo.style.bottom = '2px';
                sizeInfo.style.right = '2px';
                sizeInfo.style.fontSize = '10px';
                sizeInfo.style.color = '#666';
                sizeInfo.style.backgroundColor = 'rgba(255,255,255,0.7)';
                sizeInfo.style.padding = '1px 3px';
                sizeInfo.style.borderRadius = '2px';
                
                // 根据是否有图片决定显示预览图或提示文字
                if (customProjectileImage) {
                    previewText.style.display = 'none';
                    sizeInfo.textContent = '40×40px';
                } else {
                    sizeInfo.style.display = 'none';
                }
                
                previewArea.appendChild(previewImage);
                previewArea.appendChild(previewText);
                previewArea.appendChild(sizeInfo);
                previewContainer.appendChild(previewArea);
                container.appendChild(previewContainer);
                
                // 创建文件上传按钮
                const uploadContainer = document.createElement('div');
                uploadContainer.style.display = 'flex';
                uploadContainer.style.flexDirection = 'column';
                uploadContainer.style.gap = '10px';
                uploadContainer.style.marginBottom = '15px';
                
                const fileInput = document.createElement('input');
                fileInput.type = 'file';
                fileInput.accept = 'image/*';
                fileInput.style.display = 'none';
                fileInput.id = 'projectile-file-input';
                
                // 创建按钮容器
                const buttonRow = document.createElement('div');
                buttonRow.style.display = 'flex';
                buttonRow.style.gap = '10px';
                
                const uploadButton = document.createElement('button');
                uploadButton.textContent = '上传图片';
                uploadButton.style.padding = '6px 12px';
                uploadButton.style.backgroundColor = '#546ddb';
                uploadButton.style.color = 'white';
                uploadButton.style.border = 'none';
                uploadButton.style.borderRadius = '4px';
                uploadButton.style.cursor = 'pointer';
                uploadButton.style.flex = '1';
                uploadButton.onclick = () => fileInput.click();
                
                // 创建获取剪贴板按钮
                const clipboardButton = document.createElement('button');
                clipboardButton.textContent = '获取剪贴板';
                clipboardButton.style.padding = '6px 12px';
                clipboardButton.style.backgroundColor = '#546ddb';
                clipboardButton.style.color = 'white';
                clipboardButton.style.border = 'none';
                clipboardButton.style.borderRadius = '4px';
                clipboardButton.style.cursor = 'pointer';
                clipboardButton.style.flex = '1';
                
                // 处理图片上传
                function processImage(file) {
                    if (!file || !file.type.startsWith('image/')) {
                        alert('请选择有效的图片文件!');
                        return;
                    }
                    
                    const reader = new FileReader();
                    reader.onload = function(e) {
                        const img = new Image();
                        img.onload = function() {
                            // 获取自定义图片大小参数
                            const params = window.hitTrackerEffectParams || {};
                            const targetSize = params.projectileSize || 40; // 默认40px
                            
                            // 创建canvas来调整图片大小
                            const canvas = document.createElement('canvas');
                            const ctx = canvas.getContext('2d');
                            
                            // 计算等比例缩放尺寸
                            let width = img.width;
                            let height = img.height;
                            let scale;
                            
                            // 确定哪个边是最短的,并根据最短边计算缩放比例
                            if (width < height) {
                                // 宽度是最短边
                                scale = targetSize / width;
                                width = targetSize;
                                height = Math.round(height * scale);
                            } else {
                                // 高度是最短边或宽高相等
                                scale = targetSize / height;
                                height = targetSize;
                                width = Math.round(width * scale);
                            }
                            
                            // 设置画布尺寸为缩放后的尺寸
                            canvas.width = width;
                            canvas.height = height;
                            
                            // 绘制并调整图片大小
                            ctx.drawImage(img, 0, 0, width, height);
                            
                            // 转换为dataURL
                            const dataURL = canvas.toDataURL('image/png');
                            
                            // 更新预览
                            previewImage.src = dataURL;
                            previewImage.style.display = 'block';
                            previewText.style.display = 'none';
                            sizeInfo.style.display = 'block';
                            sizeInfo.textContent = `${width}×${height}px`;
                            
                            // 保存到设置
                            customProjectileImage = dataURL;
                            saveSettings();
                            
                            // 自动启用自定义图片
                            enableCheckbox.checked = true;
                            useCustomProjectileImage = true;
                            saveSettings();
                            
                            // 同步更新特效参数菜单
                            updateCustomProjectileParamSection();
                        };
                        img.src = e.target.result;
                    };
                    reader.readAsDataURL(file);
                }
                
                // 定义更新特效参数菜单的函数,方便重复调用
                function updateCustomProjectileParamSection() {
                    const paramMenuContainer = document.querySelector('.自定义菜单');
                    if (!paramMenuContainer) return;
                    
                    // 找到特效参数面板
                    const paramSections = paramMenuContainer.querySelectorAll('.expandable-content');
                    let effectParamsContent = null;
                    
                    // 遍历找到特效参数的内容区域
                    paramSections.forEach(section => {
                        const sectionHeader = section.previousElementSibling;
                        if (sectionHeader && sectionHeader.textContent.includes('特效参数')) {
                            effectParamsContent = section;
                        }
                    });
                    
                    if (!effectParamsContent) return;
                    
                    // 查找自定义弹道参数区域
                    const allParamSections = effectParamsContent.querySelectorAll('.inner-expandable-section');
                    let targetSection = null;
                    
                    allParamSections.forEach(section => {
                        const titleElement = section.querySelector('span');
                        if (titleElement && titleElement.textContent === '自定义弹道参数') {
                            targetSection = section;
                        }
                    });
                    
                    // 根据启用状态和图片状态显示或隐藏参数区域
                    if (targetSection) {
                        const isEnabled = useCustomProjectileImage && customProjectileImage !== null;
                        targetSection.style.display = isEnabled ? 'block' : 'none';
                    } else if (useCustomProjectileImage && customProjectileImage !== null) {
                        // 如果启用了特效但没找到对应的参数区域,重新生成菜单
                        effectParamsContent.innerHTML = '';
                        generateEffectParamsContent(effectParamsContent);
                    }
                }
                
                // 替换先前的事件监听器,使用共享的更新函数
                enableCheckbox.addEventListener('change', (e) => {
                    useCustomProjectileImage = e.target.checked;
                    saveSettings();
                    
                    // 同步更新特效参数菜单
                    updateCustomProjectileParamSection();
                });
                
                // 点击事件 - 获取剪贴板图片
                clipboardButton.addEventListener('click', async () => {
                    try {
                        // 检查是否支持剪贴板API
                        if (!navigator.clipboard || !navigator.clipboard.read) {
                            alert('您的浏览器不支持直接访问剪贴板。请使用Ctrl+V粘贴或上传图片。');
                            return;
                        }
                        
                        // 获取剪贴板内容
                        const clipboardItems = await navigator.clipboard.read();
                        let imageFound = false;
                        
                        for (const clipboardItem of clipboardItems) {
                            // 检查剪贴板是否包含图片
                            const imageTypes = clipboardItem.types.filter(type => type.startsWith('image/'));
                            if (imageTypes.length > 0) {
                                // 获取图片的Blob
                                const imageBlob = await clipboardItem.getType(imageTypes[0]);
                                // 处理图片
                                processImage(imageBlob);
                                imageFound = true;
                                break;
                            }
                        }
                        
                        if (!imageFound) {
                            alert('剪贴板中未找到图片。请复制一张图片后重试。');
                        }
                    } catch (err) {
                        console.error('获取剪贴板内容失败:', err);
                        alert('获取剪贴板内容失败。请尝试使用上传图片或直接粘贴(Ctrl+V)。');
                    }
                });
                
                // 添加按钮到行容器
                buttonRow.appendChild(uploadButton);
                buttonRow.appendChild(clipboardButton);
                
                uploadContainer.appendChild(fileInput);
                uploadContainer.appendChild(buttonRow);
                
                // 创建粘贴提示
                const pasteHint = document.createElement('div');
                pasteHint.textContent = '提示: 也可以直接粘贴剪贴板中的图片 (Ctrl+V)';
                pasteHint.style.fontSize = '12px';
                pasteHint.style.color = '#666';
                pasteHint.style.marginTop = '5px';
                pasteHint.style.textAlign = 'center';
                
                uploadContainer.appendChild(pasteHint);
                container.appendChild(uploadContainer);
                
                // 文件上传事件
                fileInput.addEventListener('change', (e) => {
                    if (e.target.files && e.target.files[0]) {
                        processImage(e.target.files[0]);
                    }
                });
                
                // 增强粘贴事件捕获能力 - 使整个容器都能响应粘贴事件
                container.setAttribute('tabindex', '0'); // 使容器可以获取焦点
                container.style.outline = 'none'; // 去除获取焦点时的轮廓
                
                // 增加点击时自动获取焦点,以便捕获粘贴事件
                container.addEventListener('click', () => {
                    container.focus();
                });
                
                // 为整个容器添加粘贴事件
                container.addEventListener('paste', (e) => {
                    // 检查是否包含图片
                    const items = e.clipboardData.items;
                    let imageFile = null;
                    
                    for (let i = 0; i < items.length; i++) {
                        if (items[i].type.indexOf('image') !== -1) {
                            imageFile = items[i].getAsFile();
                            break;
                        }
                    }
                    
                    if (imageFile) {
                        e.preventDefault();
                        processImage(imageFile);
                    }
                });
                
                expandableContent.appendChild(container);
                
                // 为面板本身也添加粘贴事件
                expandableContent.addEventListener('paste', (e) => {
                    // 检查是否包含图片
                    const items = e.clipboardData.items;
                    let imageFile = null;
                    
                    for (let i = 0; i < items.length; i++) {
                        if (items[i].type.indexOf('image') !== -1) {
                            imageFile = items[i].getAsFile();
                            break;
                        }
                    }
                    
                    if (imageFile) {
                        e.preventDefault();
                        processImage(imageFile);
                    }
                });
            }

            // 创建特效自定义可展开部分
            const { expandableSection: effectCustomSection, expandableContent: effectCustomContent } = createExpandableSection('启动特效', generateEffectCustomContent);
            popup.appendChild(effectCustomSection);
            popup.appendChild(effectCustomContent);

            // 创建玩家和颜色可展开部分
            const { expandableSection: playerColorSection, expandableContent: playerColorContent } = createExpandableSection('玩家和颜色', generatePlayerColorContent);
            popup.appendChild(playerColorSection);
            popup.appendChild(playerColorContent);
            
            // 创建自定义弹道可展开部分
            const { expandableSection: customProjectileSection, expandableContent: customProjectileContent } = createExpandableSection('自定义弹道', generateCustomProjectileContent);
            popup.appendChild(customProjectileSection);
            popup.appendChild(customProjectileContent);

            // 创建特效参数可展开部分
            const { expandableSection: effectParamsSection, expandableContent: effectParamsContent } = createExpandableSection('特效参数', generateEffectParamsContent);
            popup.appendChild(effectParamsSection);
            popup.appendChild(effectParamsContent);

            // 创建重置按钮元素
            const resetButton = document.createElement('button');
            // 设置重置按钮的显示文本
            resetButton.textContent = '重置';
            // 设置重置按钮的背景颜色
            resetButton.style.backgroundColor = '#ff4444';
            // 设置重置按钮的文本颜色
            resetButton.style.color = 'white';
            // 设置重置按钮无边框
            resetButton.style.border = 'none';
            // 设置重置按钮的边框圆角
            resetButton.style.borderRadius = '4px';
            // 设置重置按钮的内边距
            resetButton.style.padding = '8px 15px';
            // 设置重置按钮的右外边距
            resetButton.style.marginRight = '10px';
            // 设置重置按钮的鼠标指针样式
            resetButton.style.cursor = 'pointer';
            // 为重置按钮添加点击事件监听器,点击时重置所有设置
            resetButton.addEventListener('click', () => {
                // 添加确认对话框
                const confirmReset = confirm('确定要重置所有设置吗?这将恢复所有默认值。');

                // 如果用户取消,则不执行重置操作
                if (!confirmReset) {
                    return;
                }

                // 使用colorConfig重置颜色数组
                colorConfig.resetColors(lineColor, filterColor);

                // 重置玩家显示状态数组,全部设置为勾选状态
                playerDrawEnabled.fill(true);
                // 重置特效选项数组,全部设置为勾选状态
                effectDrawEnabled.fill(true);

                // 保存重置后的设置
                saveSettings();

                // 清空特效参数,强制重新使用默认值
                localStorage.removeItem('MWI_Hit_Tracker_Effect_Params');
                window.hitTrackerEffectParams = {};

                // 更新颜色选择器和预览
                // 获取玩家和颜色可展开内容中的所有颜色选择器元素
                const colorInputs = playerColorContent.querySelectorAll('input[type="color"]');
                // 获取玩家和颜色可展开内容中的所有颜色预览元素
                const previews = playerColorContent.querySelectorAll('div:last-child');
                // 遍历颜色选择器和预览元素,更新其值和背景颜色
                colorInputs.forEach((input, index) => {
                    input.value = lineColor[index];
                    previews[index].style.backgroundColor = lineColor[index];
                });

                // 更新特效选项的勾选状态
                // 获取所有效果设置部分的勾选框
                const effectCheckboxes = effectCustomContent.querySelectorAll('input[type="checkbox"]');
                // 遍历所有勾选框,将其设置为勾选状态
                effectCheckboxes.forEach(checkbox => {
                    checkbox.checked = true;

                    // 如果这是子菜单项,确保它是启用的
                    if (checkbox.disabled) {
                        checkbox.disabled = false;
                    }
                });

                // 更新玩家勾选框状态
                const playerCheckboxes = playerColorContent.querySelectorAll('input[type="checkbox"]:not(.使用自定义弹道)');
                playerCheckboxes.forEach(checkbox => {
                    checkbox.checked = true;
                });

                //关闭菜单后再打开新的菜单
                document.body.removeChild(popup);
                //点击customColorButton
                customColorButton.click();
            });

            // 创建保存按钮元素
            const closeButton = document.createElement('button');
            // 设置保存按钮的显示文本
            closeButton.textContent = '保存';
            // 设置保存按钮的背景颜色
            closeButton.style.backgroundColor = '#2196F3';
            // 设置保存按钮的文本颜色
            closeButton.style.color = 'white';
            // 设置保存按钮无边框
            closeButton.style.border = 'none';
            // 设置保存按钮的边框圆角
            closeButton.style.borderRadius = '4px';
            // 设置保存按钮的内边距
            closeButton.style.padding = '8px 15px';
            // 设置保存按钮的鼠标指针样式
            closeButton.style.cursor = 'pointer';
            // 为保存按钮添加点击事件监听器,点击时保存设置并关闭弹出窗口
            closeButton.addEventListener('click', () => {
                saveSettings();
                document.body.removeChild(popup);
                const mask = document.querySelector('.自定义菜单遮罩');
                if (mask) document.body.removeChild(mask);
            });

            // 创建按钮容器元素
            const buttonContainer = document.createElement('div');
            // 设置按钮容器元素的顶部外边距
            buttonContainer.style.marginTop = '20px';
            // 设置按钮容器元素的显示方式为弹性布局
            buttonContainer.style.display = 'flex';
            // 设置按钮容器元素内子元素的水平靠右对齐
            buttonContainer.style.justifyContent = 'flex-end';
            // 将重置按钮添加到按钮容器元素中
            buttonContainer.appendChild(resetButton);
            // 将保存按钮添加到按钮容器元素中
            buttonContainer.appendChild(closeButton);

            // 将按钮容器元素添加到弹出窗口中
            popup.appendChild(buttonContainer);

            // 将弹出窗口添加到文档主体中
            document.body.appendChild(popup);

            // 遮罩点击事件:保存设置并关闭菜单
            mask.addEventListener('click', () => {
                saveSettings();
                document.body.removeChild(popup);
                document.body.removeChild(mask);
            });
        });

        // 标记自定义颜色设置按钮已添加
        isCustomColorButtonAdded = true;
        // 输出提示信息,表示自定义颜色按钮已成功添加
        console.log('自定义颜色按钮已成功添加。');
    }

    // 循环检查按钮是否创建成功
    function checkAndCreateButton() {
        const created = createCustomColorButton();
        if (!created) {
            setTimeout(checkAndCreateButton, 500); // 每 500 毫秒检查一次
        }
    }

    // 修改初始化函数添加弹道拖尾参数
    function init() {
        console.log('初始化函数已调用。');
        // 先加载设置
        readSettings();
        
        // 初始化特效参数全局对象
        window.hitTrackerEffectParams = window.hitTrackerEffectParams || {};

        // 设置所有参数的默认值
        // 这部分参数值来自 generateEffectParamsContent 函数中的定义
        const defaultParams = {
            // 击中特效参数
            'particleCount': 20,
            'particleRadius': 2,
            'maxDistance': 20,
            'batchCount': 4,
            // 粒子拖尾参数
            'trailParticleCount': 6,
            'trailDuration': 500,
            'trailSpreadRange': 8,
            // 伤害数字参数
            'damageDisplayDuration': 1350,
            'damageFrames': 30,
            'damageStrokeWidth': 1.5,
            'damageStrokeColor': '#FFFFFF',
            'damageEndScale': 1.5,
            'damageEndFadeDuration': 200,
            // 浮动数字参数
            'floatingDuration': 1000,
            'floatingFontSize': 30,
            'floatingStrokeWidth': 2,
            'floatingStrokeColor': '#FFFFFF',
            'floatingRiseHeight': 20,
            'floatingOpacityDelay': 50,
            // 自定义弹道参数
            'projectileDuration': 1000,
            'rotateProjectile': true,
            'projectileDelay': 50,
            'projectileFadeDuration': 500,
            'projectileSize': 40,
            'enemyProjectile': true, // 是否允许敌人发射自定义弹道
            'projectileTrailEnabled': true, // 是否启用弹道拖尾
            'projectileTrailCount': 5, // 拖尾数量
            'projectileTrailFadeTime': 300, // 拖尾淡出时间(ms)
            'projectileTrailInterval': 50, // 拖尾生成间隔(ms)
            // 震动参数
            'shakeIntensity': 5,
            'shakeDuration': 300,
            // 路径绘制参数
            'lineDuration': 1000, // 线条显示时间(ms)
            'randomCurve': true, // 是否启用随机弯曲方向
            'minCurveIntensity': 3, // 最小弯曲程度(0.5-10)
            'maxCurveIntensity': 7 // 最大弯曲程度(1-15)
        };

        // 从localStorage读取已保存的特效参数设置
        const savedParams = localStorage.getItem('MWI_Hit_Tracker_Effect_Params');
        
        if (savedParams) {
            try {
                const effectParamsValues = JSON.parse(savedParams);
                // 将保存的参数值合并到默认值中
                Object.keys(defaultParams).forEach(key => {
                    // 只有当保存的参数中存在且不为undefined时才使用保存的值
                    if (effectParamsValues[key] !== undefined) {
                        window.hitTrackerEffectParams[key] = effectParamsValues[key];
                    } else {
                        // 否则使用默认值
                        window.hitTrackerEffectParams[key] = defaultParams[key];
                    }
                });
                console.log('特效参数加载成功:', window.hitTrackerEffectParams);
            } catch (e) {
                console.error('解析保存的特效参数失败:', e);
                // 解析失败时使用所有默认值
                Object.assign(window.hitTrackerEffectParams, defaultParams);
                console.log('使用默认特效参数');
            }
        } else {
            // 如果没有保存过参数,则使用所有默认值
            Object.assign(window.hitTrackerEffectParams, defaultParams);
            console.log('未找到保存的特效参数,使用默认值');
        }
        
        // 初始化时将 effectDrawEnabled[5](隐藏数字)设为 false,其他保持默认为 true
        if (effectDrawEnabled.length < 7) {
            // 确保数组长度足够
            while (effectDrawEnabled.length < 7) {
                effectDrawEnabled.push(true);
            }
            // 设置隐藏数字默认为不勾选
            effectDrawEnabled[5] = false;
        }
        
        // 劫持 WebSocket 消息,以便处理战斗相关的消息
        hookWS();
        // 添加网页可见性变化监听器,当网页从后台恢复时进行清理操作
        addVisibilityChangeListener();
        // 创建动画样式,用于攻击路径的闪烁效果和目标震动效果
        createAnimationStyle();
        // 调用循环检查函数
        checkAndCreateButton();
        
        // 初始化完成后应用隐藏数字设置
        if (effectDrawEnabled[5]) {
            setTimeout(() => toggleDamageNumbers(true), 500);
        }
    }

    // 创建动画样式,包括路径闪烁和目标震动效果
    function createAnimationStyle() {
        // console.log('动画样式函数已调用。');
        const style = document.createElement('style');
        style.textContent = `
            @keyframes lineFlash {
                0% { stroke-opacity: 0.7; }
                50% { stroke-opacity: 0.3; }
                100% { stroke-opacity: 0.7; }
            }

            @keyframes shake {
                0%, 100% { transform: translateX(0); }
                50% { transform: translateX(-1px); } /* 减小震动幅度 */
            }

            .mwht-shake {
                animation: shake 0.2s cubic-bezier(.36,.07,.19,.97) forwards; /* 固定0.2秒持续时间 */
                transform-origin: center;
                position: relative;
                z-index: 200;
            }
        `;
        document.head.appendChild(style);
    }

    // 劫持 WebSocket 消息,拦截并处理战斗相关的消息
    function hookWS() {
        // console.log('劫持函数已调用。');
        const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
        const oriGet = dataProperty.get;

        dataProperty.get = function hookedGet() {
            const socket = this.currentTarget;
            if (!(socket instanceof WebSocket)) {
                return oriGet.call(this);
            }
            if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
                return oriGet.call(this);
            }

            if (isPaused) {
                return oriGet.call(this);
            }

            const message = oriGet.call(this);
            Object.defineProperty(this, "data", { value: message });

            return handleMessage(message);
        };

        Object.defineProperty(MessageEvent.prototype, "data", dataProperty);
    }

    // 计算元素中心点坐标
    function getElementCenter(element) {
        const rect = element.getBoundingClientRect();
        if (element.innerText.trim() === '') {
            return {
                x: rect.left + rect.width / 2,
                y: rect.top
            };
        }
        return {
            x: rect.left + rect.width / 2,
            y: rect.top + rect.height / 2
        };
    }

    // 创建抛物线路径,用于攻击动画的路径显示
    function createParabolaPath(startElem, endElem, reversed = false) {
        const start = getElementCenter(startElem);
        const end = getElementCenter(endElem);

        // 获取用户设置的参数
        const params = window.hitTrackerEffectParams || {};
        const randomCurve = params.randomCurve || false;
        const minCurveIntensity = params.minCurveIntensity || 3;
        const maxCurveIntensity = params.maxCurveIntensity || 7;
        
        // 在最小和最大弯曲程度之间随机选择一个值
        const curveIntensity = minCurveIntensity + Math.random() * (maxCurveIntensity - minCurveIntensity);
        
        // 根据弯曲程度计算弯曲系数
        // 原始弯曲系数为reversed ? 4 : 2.5,现在根据用户设置的强度调整
        // curveIntensity范围是1-10,我们把基础比率映射到范围1.5-7
        const baseRatio = 1.5 + ((curveIntensity - 1) / 9) * 5.5;
        const curveRatio = reversed ? baseRatio * 1.5 : baseRatio;
        
        // 计算弯曲高度
        let curveHeight = -Math.abs(start.x - end.x) / curveRatio;
        
        // 如果启用随机弯曲,随机决定向上或向下弯曲
        if (randomCurve) {
            // 使用随机数决定弯曲方向,50%概率向上弯曲,50%概率向下弯曲
            if (Math.random() < 0.5) {
                curveHeight = -curveHeight; // 反转弯曲方向
            }
        }

        const controlPoint = {
            x: (start.x + end.x) / 2,
            y: Math.min(start.y, end.y) + curveHeight
        };

        if (reversed) {
            return `M ${end.x} ${end.y} Q ${controlPoint.x} ${controlPoint.y}, ${start.x} ${start.y}`;
        }
        return `M ${start.x} ${start.y} Q ${controlPoint.x} ${controlPoint.y}, ${end.x} ${end.y}`;
    }

    // 为目标元素的第三个父级元素添加震动效果,根据第五个父级元素决定震动方向
    function shakeTarget(element) {
        if (!element || isPaused) return;

        // 检查震动特效是否启用
        if (!effectDrawEnabled[4]) return;

        // 获取参数,如果未设置则使用默认值
        const params = window.hitTrackerEffectParams || {};
        const intensity = params.shakeIntensity || 5;
        const duration = params.shakeDuration || 200;

        // 向上查找第三个父级元素(用于实际震动)
        let shakeElement = element;
        for (let i = 0; i < 3 && shakeElement; i++) {
            shakeElement = shakeElement.parentElement;
        }

        // 向上查找第五个父级元素(用于判断震动方向)
        let directionElement = element;
        for (let i = 0; i < 5 && directionElement; i++) {
            directionElement = directionElement.parentElement;
        }

        // 如果找到了相应的父级元素,应用震动效果
        if (shakeElement && directionElement) {
            const className = directionElement.className;
            // 根据震动强度计算位移值
            const pixelIntensity = intensity * 0.4; // 将强度值转换为像素值
            let transformValue = 'translate(0, 0)';

            // 根据第五个父级元素的类名决定震动方向
            if (className.includes('playersArea')) {
                transformValue = `translate(-${pixelIntensity}px, ${pixelIntensity}px)`;
            } else if (className.includes('monstersArea')) {
                transformValue = `translate(${pixelIntensity}px, ${pixelIntensity}px)`;
            }

            // 添加震动类并设置动画
            shakeElement.classList.add('mwht-shake');

            // 使用自定义动画实现不同方向的震动
            shakeElement.style.animation = `customShake ${duration/1000}s cubic-bezier(.36,.07,.19,.97) forwards`;
            shakeElement.style.transformOrigin = 'center';
            shakeElement.style.willChange = 'transform';

            // 存储原始transform值,动画结束后恢复
            const originalTransform = shakeElement.style.transform;

            // 动画帧函数
            let startTime = null;

            function animate(currentTime) {
                if (isPaused) return;

                if (!startTime) startTime = currentTime;
                const elapsed = currentTime - startTime;
                const progress = Math.min(elapsed / duration, 1);

                // 计算动画曲线
                const easeOut = 1 - Math.pow(1 - progress, 3);

                // 应用变换
                if (progress < 0.5) {
                    // 前半段:从0到目标偏移
                    const scale = easeOut * 2;
                    shakeElement.style.transform = `translate(${parseFloat(transformValue.split('(')[1]) * scale}px, ${parseFloat(transformValue.split(',')[1]) * scale}px)`;
                } else {
                    // 后半段:从目标偏移回到0
                    const scale = 2 - (easeOut * 2);
                    shakeElement.style.transform = `translate(${parseFloat(transformValue.split('(')[1]) * scale}px, ${parseFloat(transformValue.split(',')[1]) * scale}px)`;
                }

                if (progress < 1) {
                    requestAnimationFrame(animate);
                } else {
                    // 动画结束,恢复原始transform
                    shakeElement.style.transform = originalTransform;
                    shakeElement.classList.remove('mwht-shake');
                    shakeElement.style.animation = '';
                }
            }

            // 启动动画
            requestAnimationFrame(animate);
        }
    }

    // 创建动画效果,包括攻击路径和伤害数字的动画
    function createEffect(startElem, endElem, hpDiff, index, reversed = false) {
        if (isPaused) return;
        
        // 修改检查逻辑,让玩家勾选框只控制线条绘制
        // 如果是反向攻击或玩家索引超出范围,依然使用玩家勾选框
        // 但对于普通玩家攻击,只有当线条绘制效果启用且玩家启用了线条绘制时,
        // 或者当玩家启用了自定义弹道时,才会显示线条/弹道效果
        // 其他特效不再受玩家勾选框影响
        let showLineOrProjectile = true; // 默认显示线条或弹道
        
        if (reversed || index >= 5) {
            // 对于敌人攻击或索引超出范围的情况,仍依赖玩家勾选框
            if (!playerDrawEnabled[index]) {
                showLineOrProjectile = false;
            }
        } else {
            // 对于玩家攻击的情况,检查是否显示线条或弹道
            // 只有当线条绘制特效启用且玩家绘制启用,或者玩家启用了自定义弹道时,才显示
            if (!(playerDrawEnabled[index] && effectDrawEnabled[1]) && !playerUseProjectile[index]) {
                showLineOrProjectile = false;
            }
        }

        if (reversed) {
            const dmgDivs = startElem.querySelector('.CombatUnit_splatsContainer__2xcc0')?.querySelectorAll('div') || [];
            for (const div of dmgDivs) {
                if (div.innerText.trim() === '') {
                    startElem = div;
                    break;
                }
            }
        } else {
            const dmgDivs = endElem.querySelector('.CombatUnit_splatsContainer__2xcc0')?.querySelectorAll('div') || [];
            for (const div of dmgDivs) {
                if (div.innerText.trim() === '') {
                    endElem = div;
                    break;
                }
            }
        }

        const svg = document.getElementById('svg-container');
        const frag = document.createDocumentFragment();


        // 根据reversed参数决定目标元素
        const targetElem = reversed ? startElem : endElem;
        // 存储需要在结束时触发击中特效的位置
        const effectPosition = {
            x: 0,
            y: 0
        };

        let path = null;
        let text = null;
        let pathLength = 0;
        let hasTextOrPath = false;
        
        // 修改条件:仅在showLineOrProjectile为true且线条绘制特效启用时创建路径
        // 这确保玩家勾选框只影响线条绘制,不影响其他特效
        if (showLineOrProjectile && playerDrawEnabled[index] && effectDrawEnabled[1]) {
            hasTextOrPath = true;
            
            // 创建默认线条
            // 原始线条逻辑
            let strokeWidth = '1px';
            let filterWidth = '1px';
            if (hpDiff >= 1000) {
                strokeWidth = '5px';
                filterWidth = '6px';
            } else if (hpDiff >= 700) {
                strokeWidth = '4px';
                filterWidth = '5px';
            } else if (hpDiff >= 500) {
                strokeWidth = '3px';
                filterWidth = '4px';
            } else if (hpDiff >= 300) {
                strokeWidth = '2px';
                filterWidth = '3px';
            } else if (hpDiff >= 100) {
                filterWidth = '2px';
            }

            path = document.createElementNS("http://www.w3.org/2000/svg", "path");
            if (reversed) index = 5;
            Object.assign(path.style, {
                stroke: lineColor[index],
                strokeWidth,
                fill: 'none',
                strokeLinecap: 'round',
                filter: `drop-shadow(0 0 ${filterWidth} ${filterColor[index]})`,
                willChange: 'stroke-dashoffset, opacity'
            });
            path.setAttribute('d', createParabolaPath(startElem, endElem, reversed));
            pathLength = path.getTotalLength();
            path.style.strokeDasharray = pathLength;
            path.style.strokeDashoffset = pathLength;

            // 计算路径终点位置,用于后续触发击中特效
            const endPoint = path.getPointAtLength(pathLength);
            effectPosition.x = endPoint.x;
            effectPosition.y = endPoint.y;

            frag.appendChild(path);
        }
        
        // 获取当前索引对应玩家是否使用自定义弹道
        // 只有非反向攻击(玩家攻击怪物)且是玩家索引时才检查自定义弹道设置
        let usePlayerProjectile = false;
        let projectileImgToUse = null;
        
        // 获取特效参数
        const params = window.hitTrackerEffectParams || {};
        
        // 检查是否使用自定义弹道图片
        if (!reversed && index < 5) {
            // 如果玩家勾选了使用自定义弹道且有专属图片
            if (playerUseProjectile[index] && playerProjectileImages[index]) {
                usePlayerProjectile = true;
                projectileImgToUse = playerProjectileImages[index];
            }
            // 如果玩家勾选了使用自定义弹道但没有专属图片,使用通用图片
            else if (playerUseProjectile[index] && useCustomProjectileImage && customProjectileImage) {
                usePlayerProjectile = true;
                projectileImgToUse = customProjectileImage;
            }
        } 
        // 如果是敌人攻击且允许敌人使用自定义弹道
        else if (reversed && useCustomProjectileImage && customProjectileImage) {
            const enemyProjectileEnabled = params.enemyProjectile !== undefined ? params.enemyProjectile : true;
            if (enemyProjectileEnabled) {
                usePlayerProjectile = true;
                projectileImgToUse = customProjectileImage;
            }
        }
        
        // 修改条件:仅在showLineOrProjectile为true且线条绘制特效启用时创建弹道图片
        if (showLineOrProjectile && usePlayerProjectile && effectDrawEnabled[1]) {
            hasTextOrPath = true;
            
            // 创建自定义弹道图片
            const startPoint = getElementCenter(startElem);
            const endPoint = getElementCenter(endElem);
            
            // 如果没有线条路径,创建一个用于计算的临时路径
            if (!path) {
                const tempCalcPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
                tempCalcPath.setAttribute('d', createParabolaPath(startElem, endElem, reversed));
                pathLength = tempCalcPath.getTotalLength();
                
                // 计算路径终点位置,用于后续触发击中特效
                const endPoint = tempCalcPath.getPointAtLength(pathLength);
                effectPosition.x = endPoint.x;
                effectPosition.y = endPoint.y;
            }
            
            // 获取参数,如果未设置则使用默认值
            const duration = params.projectileDuration || 1000; // 默认1秒
            const rotateProjectile = params.rotateProjectile !== undefined ? params.rotateProjectile : true;
            const fadeOutDuration = params.projectileFadeDuration || 500; // 默认0.5秒淡出
            const startDelay = params.projectileDelay || 50; // 默认50ms延迟
            const imageSize = params.projectileSize || 40; // 默认图片大小40px
            const halfSize = imageSize / 2; // 计算图片半尺寸,用于居中定位
            
            // 创建图片元素
            const projectileImage = document.createElementNS("http://www.w3.org/2000/svg", "image");
            projectileImage.setAttribute("href", projectileImgToUse);
            projectileImage.setAttribute("width", imageSize);
            projectileImage.setAttribute("height", imageSize);
            // 设置初始位置为起点,并偏移以使图片中心对准起点
            projectileImage.setAttribute("x", startPoint.x - halfSize);
            projectileImage.setAttribute("y", startPoint.y - halfSize);
            
            // 计算起点到终点的路径
            const pathD = createParabolaPath(startElem, endElem, reversed);
            const tempPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
            tempPath.setAttribute('d', pathD);
            // 使用已存在的pathLength变量,路径长度应该与线条相同
            
            // 添加到文档片段
            frag.appendChild(projectileImage);
            
            // 设置动画
            let startTime = null;
            let lastProgress = 0;
            let lastTrailTime = 0; // 记录上次创建拖尾的时间
            let angle = 0; // 用于存储当前旋转角度
            
            const originalAnimateProjectile = function(currentTime) {
                if (isPaused) return;
                
                if (!startTime) startTime = currentTime;
                const elapsed = currentTime - startTime;
                const progress = Math.min(elapsed / duration, 1);
                
                // 计算路径上的当前位置
                const point = tempPath.getPointAtLength(progress * pathLength);
                
                // 更新图片位置
                projectileImage.setAttribute("x", point.x - halfSize);
                projectileImage.setAttribute("y", point.y - halfSize);
                
                // 如果需要旋转图片,计算旋转角度
                if (rotateProjectile) {
                    // 只有当进度有明显变化时才重新计算角度,提高性能
                    if (progress - lastProgress > 0.01 || progress === 1) {
                        lastProgress = progress;
                        
                        // 获取当前点和下一点,计算角度
                        const nextPoint = tempPath.getPointAtLength(Math.min((progress + 0.01) * pathLength, pathLength));
                        angle = Math.atan2(nextPoint.y - point.y, nextPoint.x - point.x) * 180 / Math.PI;
                        
                        // 应用旋转
                        projectileImage.setAttribute("transform", `rotate(${angle}, ${point.x}, ${point.y})`);
                    }
                }
                
                // 添加拖尾效果
                const trailEnabled = params.projectileTrailEnabled !== undefined ? params.projectileTrailEnabled : true;
                
                if (trailEnabled) {
                    // 获取当前时间戳,用于控制拖尾生成频率
                    const now = Date.now();
                    // 获取拖尾生成间隔参数
                    const trailInterval = params.projectileTrailInterval || 50;
                    
                    // 根据时间间隔创建拖尾
                    if (!lastTrailTime || now - lastTrailTime >= trailInterval) {
                        createProjectileTrail(point.x, point.y, halfSize, imageSize, projectileImgToUse, rotateProjectile ? angle : 0);
                        lastTrailTime = now;
                    }
                }
                
                // 继续动画或结束
                if (progress < 1) {
                    requestAnimationFrame(animateProjectile);
                } else {
                    // 动画结束,添加淡出效果
                    projectileImage.style.transition = `opacity ${fadeOutDuration}ms`;
                    projectileImage.style.opacity = 0;
                    
                    setTimeout(() => {
                        projectileImage.remove();
                        // 弹道动画完成时触发效果
                        if (triggerHitEffects) {
                            triggerHitEffects();
                        } else {
                            // 如果没有传入回调函数,直接触发效果
                            if (effectDrawEnabled[3] && !hasTriggeredParticle) {
                                createParticleEffect(effectPosition.x, effectPosition.y, lineColor[index]);
                            }
                            if (effectDrawEnabled[4] && !hasTriggeredShake) {
                                shakeTarget(targetElem);
                                hasTriggeredShake = true;
                            }
                        }
                    }, fadeOutDuration);
                }
            };
            
            // 使用新的动画函数
            const animateProjectile = originalAnimateProjectile;
            
            // 启动动画,应用延迟参数
            setTimeout(() => {
                requestAnimationFrame(animateProjectile);
            }, startDelay);
        }

        // 只有当伤害数字特效启用时才创建文本,不再依赖玩家勾选框
        if (effectDrawEnabled[0]) {
            hasTextOrPath = true;
            text = document.createElementNS("http://www.w3.org/2000/svg", "text");
            text.textContent = hpDiff;
            const baseFontSize = 5;
            const fontSize = Math.floor(200 * Math.pow(hpDiff / (20000 + hpDiff), 0.45)) - baseFontSize;
            text.setAttribute('font-size', fontSize);
            text.setAttribute('fill', lineColor[index]);

            // 获取参数,如果未设置则使用默认值
            const damageStrokeWidth = params.damageStrokeWidth || 0.5;
            const strokeColor = params.damageStrokeColor || '#000000';

            // 添加描边效果
            if (damageStrokeWidth > 0) {
                text.setAttribute('stroke', strokeColor);
                text.setAttribute('stroke-width', damageStrokeWidth);
                text.setAttribute('paint-order', 'stroke fill');
            }

            Object.assign(text.style, {
                opacity: 0,
                filter: `drop-shadow(0 0 5px ${lineColor[index]})`,
                transformOrigin: 'center',
                fontWeight: 'bold',
                willChange: 'transform, opacity, x, y'
            });
            frag.appendChild(text);
        }

        // 如果没有任何可视化效果但仍然需要处理伤害,直接触发震动和击中特效
        // 所有特效现在都不再依赖玩家勾选框
        if (!hasTextOrPath) {
            // 获取目标元素的位置用于粒子效果
            const targetCenter = getElementCenter(targetElem);
            effectPosition.x = targetCenter.x;
            effectPosition.y = targetCenter.y;

            // 独立触发震动和击中特效,只检查特效是否启用
            if (effectDrawEnabled[3]) {
                createParticleEffect(effectPosition.x, effectPosition.y, lineColor[index]);
            }

            if (effectDrawEnabled[4]) {
                shakeTarget(targetElem);
            }
            
            // 添加浮动数字效果,只检查特效是否启用
            if (effectDrawEnabled[6]) {
                createFloatingNumber(effectPosition.x, effectPosition.y, hpDiff, lineColor[index]);
            }
            
            return;
        }

        svg.appendChild(frag);

        // 记录是否启用了弹道图片
        const hasProjectile = usePlayerProjectile && projectileImgToUse;
        // 创建计数器跟踪完成的动画数量
        let animationCompletedCount = 0;
        // 需要完成的动画总数(线条+弹道)
        const totalAnimations = path && hasProjectile ? 2 : 1;
        // 添加一个标记,表示是否已触发震动效果
        let hasTriggeredShake = false;
        // 添加一个标记,表示是否已触发粒子效果
        let hasTriggeredParticle = false;
        // 添加一个标记,表示是否已触发浮动数字效果
        let hasTriggeredFloating = false;
        
        // 延迟执行击中效果的函数
        const triggerHitEffects = () => {
            animationCompletedCount++;
            
            // 任何特效到达终点时就触发震动效果,但只触发一次
            if (effectDrawEnabled[4] && !hasTriggeredShake) {
                shakeTarget(targetElem);
                hasTriggeredShake = true;
            }
            
            // 任何特效到达终点时就触发粒子效果,但只触发一次
            if (effectDrawEnabled[3] && !hasTriggeredParticle) {
                createParticleEffect(effectPosition.x, effectPosition.y, lineColor[index]);
                hasTriggeredParticle = true;
            }
            
            // 添加浮动数字效果
            if (effectDrawEnabled[6] && !hasTriggeredFloating) {
                createFloatingNumber(effectPosition.x, effectPosition.y, hpDiff, lineColor[index]);
                hasTriggeredFloating = true;
            }
        };

        // 如果创建了路径,设置路径动画
        if (path) {
            // 获取用户自定义的路径动画持续时间,默认为1000ms
            const params = window.hitTrackerEffectParams || {};
            const lineDuration = params.lineDuration || 1000;
            // 计算半程时间,用于第一阶段和第二阶段的动画
            const halfDuration = lineDuration / 2;
            
            setTimeout(() => {
                requestAnimationFrame(() => {
                    path.style.transition = `stroke-dashoffset ${halfDuration}ms linear`;
                    path.style.strokeDashoffset = '0';
                });
            }, 100);

            setTimeout(() => {
                requestAnimationFrame(() => {
                    path.style.transition = `stroke-dashoffset ${halfDuration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity ${halfDuration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
                    path.style.strokeDashoffset = -pathLength;
                    path.style.opacity = 0;

                    const removePath = () => {
                        path.remove();

                        // 如果没有启用伤害数字特效且没有弹道图片,在路径移除后触发击中特效
                        if (!text) {
                            triggerHitEffects();
                        }
                    };
                    path.addEventListener('transitionend', removePath, { once: true });
                });
            }, 100 + halfDuration);
        }

        // 如果创建了文本,设置文本动画
        if (text) {
            // 获取自定义显示时间参数
            const displayDuration = params.damageDisplayDuration || 1350;

            // 如果同时有路径和文本,让文本沿着路径移动
            if (path) {
                setTimeout(() => {
                    requestAnimationFrame(() => {
                        // 传递显示时间参数和triggerHitEffects函数给动画函数
                        animateText(path, text, pathLength, lineColor[index], targetElem, {
                            duration: displayDuration,
                            triggerHitEffects: triggerHitEffects
                        });
                    });
                }, 100);
            } else {
                // 如果只有文本没有路径,创建一个虚拟路径用于文本动画
                const virtualPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
                virtualPath.setAttribute('d', createParabolaPath(startElem, endElem, reversed));
                const virtualPathLength = virtualPath.getTotalLength();

                // 计算虚拟路径终点位置,用于后续触发击中特效
                const endPoint = virtualPath.getPointAtLength(virtualPathLength);
                effectPosition.x = endPoint.x;
                effectPosition.y = endPoint.y;

                // 直接设置文本动画,不添加虚拟路径到DOM
                // 传递显示时间参数和triggerHitEffects函数给动画函数
                animateText(virtualPath, text, virtualPathLength, lineColor[index], targetElem, {
                    duration: displayDuration,
                    triggerHitEffects: triggerHitEffects
                });
            }
        }
    }

    // 从对象池获取粒子元素
    function getParticleFromPool() {
        if (particlePool.length > 0) {
            return particlePool.pop();
        }
        return document.createElementNS("http://www.w3.org/2000/svg", "circle");
    }

    // 将粒子元素返回对象池
    function returnParticleToPool(particle) {
        particle.removeAttribute('r');
        particle.removeAttribute('fill');
        particle.removeAttribute('cx');
        particle.removeAttribute('cy');
        particle.style.opacity = 1;
        particle.style.transform = 'none';
        particle.removeEventListener('transitionend', () => {});
        particlePool.push(particle);
    }

    // 创建击中粒子特效,在伤害数字消失时显示
    /**
     * 创建粒子爆炸特效
     * @param {number} x - 粒子爆炸的x坐标
     * @param {number} y - 粒子爆炸的y坐标
     * @param {string} color - 粒子的颜色
     */
    function createParticleEffect(x, y, color) {
        // 如果动画暂停则直接返回
        if (isPaused) return;
        // 如果击中特效未启用,则直接返回
        if (!effectDrawEnabled[3]) return;

        // 获取SVG容器
        const svg = document.getElementById('svg-container');
        if (!svg) return;

        // 获取参数,如果未设置则使用默认值
        const params = window.hitTrackerEffectParams || {};
        const numParticles = params.particleCount || 20;
        const batchCount = params.batchCount || 4;
        const particleRadius = params.particleRadius || 2;
        const maxDistance = params.maxDistance || 20;

        // 创建文档片段用于批量添加粒子
        const frag = document.createDocumentFragment();

        // 计算每批创建的粒子数量(向上取整确保所有粒子都被创建)
        const batchSize = Math.ceil(numParticles / batchCount);
        // 批次计数器
        let currentBatch = 0;

        /**
         * 分批创建粒子
         */
        function createBatch() {
            // 计算当前批次应创建的粒子起始索引和结束索引
            const startIndex = currentBatch * batchSize;
            const endIndex = Math.min(startIndex + batchSize, numParticles);

            // 遍历创建当前批次的粒子
            for (let i = startIndex; i < endIndex; i++) {
                // 从对象池获取粒子
                const particle = getParticleFromPool();
                // 设置粒子基本属性
                particle.setAttribute('r', particleRadius.toString());
                particle.setAttribute('fill', color);
                particle.setAttribute('cx', x);
                particle.setAttribute('cy', y);
                particle.style.opacity = 1;
                particle.style.transformOrigin = 'center';
                particle.style.willChange = 'transform, opacity';

                // 计算粒子的运动轨迹
                // 随机生成0~2π之间的角度
                const angle = Math.random() * 2 * Math.PI;

                // 使用更强的随机数生成方式计算距离
                let distance;
                if (window.crypto && window.crypto.getRandomValues) {
                    const array = new Uint32Array(1);
                    window.crypto.getRandomValues(array);
                    distance = (array[0] / 0xffffffff) * (maxDistance - 10) + 10;
                } else {
                    // 添加额外的随机性扰动
                    for (let j = 0; j < (Date.now() % 5) + 1; j++) {
                        Math.random();
                    }
                    distance = Math.random() * (maxDistance - 10) + 10;
                }

                const endX = parseFloat(x) + distance * Math.cos(angle);
                const endY = parseFloat(y) + distance * Math.sin(angle);

                // 将粒子添加到文档片段
                frag.appendChild(particle);

                // 在下一帧开始粒子动画
                requestAnimationFrame(() => {
                    // 设置过渡动画
                    particle.style.transition = 'all 0.3s ease-out';
                    particle.setAttribute('cx', endX);
                    particle.setAttribute('cy', endY);
                    particle.style.opacity = 0;

                    // 设置安全清理定时器
                    setTimeout(() => {
                        if (particle.parentNode) {
                            particle.parentNode.removeChild(particle);
                            returnParticleToPool(particle);
                        }
                    }, 500); // 减少回收时间,提高性能
                });
            }

            // 增加批次计数
            currentBatch++;

            // 如果还有未创建的批次,继续创建下一批
            if (currentBatch < batchCount) {
                setTimeout(createBatch, 50);
            } else {
                // 所有粒子创建完成,将文档片段添加到SVG容器
                svg.appendChild(frag);
            }
        }

        // 开始创建第一批粒子
        createBatch();
    }


    /**
     * 创建粒子拖尾效果
     * @param {number} x - 粒子起始x坐标
     * @param {number} y - 粒子起始y坐标
     * @param {string} color - 粒子颜色
     */
    function createParticleTrail(x, y, color) {
        // 如果动画暂停则直接返回
        if (isPaused) return;
        // 如果粒子拖尾特效未启用,则直接返回
        if (!effectDrawEnabled[2]) return;

        // 获取SVG容器
        const svg = document.getElementById('svg-container');
        if (!svg) return;

        // 获取参数,如果未设置则使用默认值
        const params = window.hitTrackerEffectParams || {};
        const particleCount = params.trailParticleCount || 2;
        const duration = params.trailDuration || 500;
        const spreadRange = params.trailSpreadRange || 10;

        // 创建文档片段用于批量添加粒子
        const frag = document.createDocumentFragment();

        // 创建指定数量的粒子
        for (let i = 0; i < particleCount; i++) {
            // 从对象池获取粒子或创建新粒子
            let particle;
            if (particlePool.length > 0) {
                particle = particlePool.pop();
                // 重置粒子属性
                particle.style.opacity = 1;
            } else {
                particle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                particle.setAttribute('r', '2');
            }

            // 设置粒子颜色
            particle.setAttribute('fill', color);

            // 计算粒子的随机位置偏移
            const randomSpread = Math.random() * spreadRange;
            const angle = Math.random() * 2 * Math.PI;
            const offsetX = randomSpread * Math.cos(angle);
            const offsetY = randomSpread * Math.sin(angle);

            // 设置粒子位置
            particle.setAttribute('cx', parseFloat(x) + offsetX);
            particle.setAttribute('cy', parseFloat(y) + offsetY);

            // 添加到文档片段
            frag.appendChild(particle);

            // 设置粒子动画
            const fadeOutDuration = duration + Math.random() * 200; // 添加一些随机性

            // 使用CSS过渡动画
            particle.style.transition = `opacity ${fadeOutDuration}ms ease-out`;

            // 延迟一帧开始动画,确保过渡效果生效
            requestAnimationFrame(() => {
                particle.style.opacity = 0;

                // 动画结束后回收粒子
                setTimeout(() => {
                    if (particle.parentNode) {
                        particle.parentNode.removeChild(particle);
                        particlePool.push(particle);
                    }
                }, fadeOutDuration);
            });
        }

        // 将所有粒子一次性添加到SVG
        svg.appendChild(frag);
    }

    // 文本动画函数 - 使用 requestAnimationFrame 实现更流畅的动画
    /**
     * 执行文本动画,让文本沿着指定路径移动,并在移动过程中产生粒子效果。
     *
     * @param {SVGPathElement} path - 文本移动的路径元素。
     * @param {SVGTextElement} text - 要进行动画的文本元素。
     * @param {number} pathLength - 路径的总长度。
     * @param {string} color - 文本和粒子的颜色。
     * @param {HTMLElement} targetElem - 目标元素,用于震动效果。
     */
    function animateText(path, text, pathLength, color, targetElem, config = {}) {
        // 动画配置对象,包含动画持续时间、淡入开始和结束时间、粒子生成间隔
        const animationConfig = {
            duration: config.duration || 1350, // 动画总持续时间,单位为毫秒
            fadeInStart: config.fadeInStart || 0.0, // 淡入开始的进度比例
            fadeInEnd: config.fadeInEnd || 0.3, // 淡入结束的进度比例
            particleInterval: config.particleInterval || 3, // 粒子生成的间隔百分比
            frameRate: config.frameRate || 60, // 动画帧率
            endScale: config.endScale || 1.5, // 结束时的缩放比例
            endFadeDuration: config.endFadeDuration || 200, // 结束淡出动画持续时间
            particleSize: config.particleSize || 2, // 粒子大小
            particleSpread: config.particleSpread || 10, // 粒子扩散范围
            triggerHitEffects: config.triggerHitEffects || null // 触发击中效果的回调函数
        };

        // 获取用户在参数菜单中配置的伤害数字参数
        const params = window.hitTrackerEffectParams || {};
        if (params.damageDisplayDuration) {
            animationConfig.duration = params.damageDisplayDuration;
        }

        // 添加动画结束时的缩放比例和淡出时间配置
        if (params.damageEndScale) {
            animationConfig.endScale = params.damageEndScale;
        }
        if (params.damageEndFadeDuration) {
            animationConfig.endFadeDuration = params.damageEndFadeDuration;
        }

        let startTime = null; // 动画开始的时间戳
        let lastParticleFrame = 0; // 上一次生成粒子时的进度百分比
        let lastFrameTime = 0; // 上一帧的时间戳

        /**
         * 动画循环函数,使用 requestAnimationFrame 不断更新文本和粒子的状态。
         *
         * @param {number} currentTime - 当前的时间戳。
         */
        function animate(currentTime) {
            // 如果脚本处于暂停状态,则停止动画
            if (isPaused) return;

            // 如果动画还未开始,记录当前时间为开始时间
            if (!startTime) startTime = currentTime;

            // 计算帧间隔
            const frameInterval = 1000 / animationConfig.frameRate;

            // 如果距离上一帧的时间间隔小于目标帧间隔,跳过这一帧
            if (currentTime - lastFrameTime < frameInterval) {
                requestAnimationFrame(animate);
                return;
            }

            // 更新上一帧时间戳
            lastFrameTime = currentTime;

            // 计算从动画开始到现在经过的时间
            const elapsed = currentTime - startTime;
            // 计算动画的进度,取值范围为 0 到 1
            const progress = Math.min(elapsed / animationConfig.duration, 1);

            // 根据进度获取路径上的点
            const point = path.getPointAtLength(progress * pathLength);

            // 更新文本的位置
            text.setAttribute('x', point.x);
            text.setAttribute('y', point.y);

            let opacity = 1; // 文本的透明度
            // 如果进度小于淡入开始时间,文本完全透明
            if (progress < animationConfig.fadeInStart) {
                opacity = 0;
            }
            // 如果进度在淡入开始和结束时间之间,计算透明度的渐变值
            else if (progress < animationConfig.fadeInEnd) {
                opacity = 0.7 + 0.3 * ((progress - animationConfig.fadeInStart) / (animationConfig.fadeInEnd - animationConfig.fadeInStart));
            }
            // 更新文本的透明度
            text.style.opacity = opacity;

            // 检查是否达到粒子生成的间隔,并且上次生成粒子的进度不同
            if (Math.floor(progress * 100) % animationConfig.particleInterval === 0 && lastParticleFrame !== Math.floor(progress * 100)) {
                // 记录当前生成粒子的进度
                lastParticleFrame = Math.floor(progress * 100);
            }

            // 如果动画进度小于 1,继续请求下一帧动画
            if (progress < 1) {
                requestAnimationFrame(animate);

                // 只在粒子拖尾特效启用时才创建粒子
                if (effectDrawEnabled[2]) {
                    createParticleTrail(point.x, point.y, color);
                }
            }
            // 动画进度达到 1,执行结束动画
            else {
                text.style.transition = `all ${animationConfig.endFadeDuration/1000}s ease-out`;
                text.style.transform = `scale(${animationConfig.endScale})`;
                text.style.opacity = 0;

                const finalX = text.getAttribute('x');
                const finalY = text.getAttribute('y');

                setTimeout(() => {
                    text.remove();

                    if (animationConfig.triggerHitEffects) {
                        // 如果传入了triggerHitEffects回调,则使用它
                        animationConfig.triggerHitEffects();
                    } else {
                        // 否则使用原来的代码
                        if (effectDrawEnabled[3] && !hasTriggeredParticle) {
                            createParticleEffect(finalX, finalY, color);
                        }

                        if (effectDrawEnabled[4] && !hasTriggeredShake) {
                            shakeTarget(targetElem);
                            hasTriggeredShake = true;
                        }
                        
                        // 添加浮动数字效果
                        if (effectDrawEnabled[6]) {
                            createFloatingNumber(finalX, finalY, text.textContent, color);
                        }
                    }
                }, animationConfig.endFadeDuration);
            }
        }

        // 启动动画循环
        requestAnimationFrame(animate);
    }
    // 创建线条动画,根据攻击信息创建攻击路径和伤害数字动画
    /**
     * 创建从一个角色到另一个角色的攻击线条,并触发相应的特效。
     *
     * @param {number} from - 攻击发起者的索引。
     * @param {number} to - 攻击目标的索引。
     * @param {number} hpDiff - 伤害值,即生命值的差值。
     * @param {boolean} [reversed=false] - 指示攻击是否是反向的,默认为 false。
     */
    function createLine(from, to, hpDiff, reversed = false) {
        // 如果脚本处于暂停状态,则直接返回,不执行后续操作
        if (isPaused) return;
        // 查找玩家区域元素
        const playerArea = document.querySelector(".BattlePanel_playersArea__vvwlB");
        // 查找怪物区域元素
        const monsterArea = document.querySelector(".BattlePanel_monstersArea__2dzrY");
        // 查找游戏主面板元素
        const gamePanel = document.querySelector(".GamePage_mainPanel__2njyb");

        // 如果任何一个必要元素未找到,则直接返回,不执行后续操作
        if (!playerArea || !monsterArea || !gamePanel) return;

        // 获取玩家区域的第一个子元素,作为玩家容器
        const playersContainer = playerArea.firstElementChild;
        // 获取怪物区域的第一个子元素,作为怪物容器
        const monsterContainer = monsterArea.firstElementChild;

        // 获取攻击发起者的元素
        const effectFrom = playersContainer?.children[from];
        // 获取攻击目标的元素
        const effectTo = monsterContainer?.children[to];

        // 如果攻击发起者或目标元素未找到,则直接返回,不执行后续操作
        if (!effectFrom || !effectTo) return;

        // 查找 SVG 容器元素
        let svgContainer = document.getElementById('svg-container');

        // 如果 SVG 容器元素不存在,则创建一个新的 SVG 容器
        if (!svgContainer) {
            // 定义 SVG 的命名空间
            const svgNS = 'http://www.w3.org/2000/svg';
            // 创建 SVG 元素
            svgContainer = document.createElementNS(svgNS, 'svg');
            // 设置 SVG 元素的 ID
            svgContainer.id = 'svg-container';

            // 设置 SVG 元素的样式
            Object.assign(svgContainer.style, {
                position: 'fixed',
                top: '0',
                left: '0',
                width: '100%',
                height: '100%',
                pointerEvents: 'none',
                overflow: 'visible',
                zIndex: '190'
            });

            // 定义一个函数,用于设置 SVG 的 viewBox 属性
            const setViewBox = () => {
                // 获取窗口的宽度
                const width = window.innerWidth;
                // 获取窗口的高度
                const height = window.innerHeight;
                // 设置 SVG 的 viewBox 属性
                svgContainer.setAttribute('viewBox', `0 0 ${width} ${height}`);
            };

            // 首次调用 setViewBox 函数,设置 SVG 的 viewBox 属性
            setViewBox();
            // 设置 SVG 的 preserveAspectRatio 属性
            svgContainer.setAttribute('preserveAspectRatio', 'none');
            // 将 SVG 元素添加到游戏主面板中
            gamePanel.appendChild(svgContainer);

            // 如果窗口大小改变监听器还未添加,则添加该监听器
            if (!isResizeListenerAdded) {
                // 监听窗口大小改变事件,当窗口大小改变时调用 setViewBox 函数
                window.addEventListener('resize', setViewBox);
                // 标记窗口大小改变监听器已添加
                isResizeListenerAdded = true;
            }
        }

        // 根据攻击是否反向,确定攻击发起者的索引
        const originIndex = reversed ? to : from;
        // 调用 createEffect 函数,创建攻击特效
        createEffect(effectFrom, effectTo, hpDiff, originIndex, reversed);
    }

    // 处理伤害信息,根据新旧生命值计算伤害差值并创建动画
    /**
     * 处理伤害数据,根据旧的生命值数组和新的实体映射,计算生命值差值,并在满足条件时创建攻击线条。
     *
     * @param {Array} oldHPArr - 旧的生命值数组,存储每个实体的旧生命值。
     * @param {Object} newMap - 新的实体映射,键为实体索引,值为包含当前生命值(cHP)的实体对象。
     * @param {number} castIndex - 施法者的索引。
     * @param {Array} attackerIndices - 攻击者的索引数组。
     * @param {boolean} [isReverse=false] - 可选参数,指示攻击方向是否反转。
     */
    function processDamage(oldHPArr, newMap, castIndex, attackerIndices, isReverse = false) {
        // 遍历旧的生命值数组
        oldHPArr.forEach((oldHP, index) => {
            // 从新的实体映射中获取对应索引的实体
            const entity = newMap[index];
            // 如果实体不存在,则跳过当前循环
            if (!entity) return;

            // 计算旧生命值和当前生命值的差值
            const hpDiff = oldHP - entity.cHP;
            // 更新旧生命值数组中的值为当前生命值
            oldHPArr[index] = entity.cHP;

            // 如果生命值差值大于 0 且攻击者索引数组不为空
            if (hpDiff > 0 && attackerIndices.length > 0) {
                // 如果攻击者索引数组长度大于 1
                if (attackerIndices.length > 1) {
                    // 遍历攻击者索引数组
                    attackerIndices.forEach(attackerIndex => {
                        // 如果攻击者索引等于施法者索引
                        if (attackerIndex === castIndex) {
                            // 调用 createLine 函数创建攻击线条
                            createLine(attackerIndex, index, hpDiff, isReverse);
                        }
                    });
                } else {
                    // 如果攻击者索引数组长度为 1,直接调用 createLine 函数创建攻击线条
                    createLine(attackerIndices[0], index, hpDiff, isReverse);
                }
            }
        });
    }

    // 检测施法者,通过比较新旧魔法值找出施法者索引
    function detectCaster(oldMPArr, newMap) {
        let casterIndex = -1;
        Object.keys(newMap).forEach(index => {
            const newMP = newMap[index].cMP;
            if (newMP < oldMPArr[index]) {
                casterIndex = index;
            }
            oldMPArr[index] = newMP;
        });
        return casterIndex;
    }

    // 处理 WebSocket 消息,根据消息类型更新战斗状态并创建攻击动画
    function handleMessage(message) {
        if (isPaused) {
            return message;
        }

        let obj;
        try {
            obj = JSON.parse(message);
        } catch (error) {
            console.error('Failed to parse WebSocket message:', error);
            return message;
        }

        if (obj && obj.type === "new_battle") {
            battleState.monstersHP = obj.monsters.map((monster) => monster.currentHitpoints);
            battleState.monstersMP = obj.monsters.map((monster) => monster.currentManapoints);
            battleState.playersHP = obj.players.map((player) => player.currentHitpoints);
            battleState.playersMP = obj.players.map((player) => player.currentManapoints);

            const svg = document.getElementById('svg-container');
            if (svg) {
                while (svg.firstChild) {
                    svg.removeChild(svg.firstChild);
                }
            }
            particlePool.length = 0;
            
            // 在新战斗开始时应用隐藏数字设置
            if (effectDrawEnabled[5]) {
                setTimeout(() => toggleDamageNumbers(true), 100);
            }
        } else if (obj && obj.type === "battle_updated" && battleState.monstersHP.length) {
            const mMap = obj.mMap;
            const pMap = obj.pMap;
            const monsterIndices = Object.keys(obj.mMap);
            const playerIndices = Object.keys(obj.pMap);

            const castMonster = detectCaster(battleState.monstersMP, mMap);
            const castPlayer = detectCaster(battleState.playersMP, pMap);

            processDamage(battleState.monstersHP, mMap, castPlayer, playerIndices, false);
            processDamage(battleState.playersHP, pMap, castMonster, monsterIndices, true);
            
            // 在战斗更新时应用隐藏数字设置
            if (effectDrawEnabled[5]) {
                toggleDamageNumbers(true);
            } else {
                toggleDamageNumbers(false);
            }
        }

        return message;
    }

    // 检测网页是否从后台恢复,当网页从后台恢复时清理 SVG 容器中的元素
    function addVisibilityChangeListener() {
        document.addEventListener('visibilitychange', function () {
            if (document.visibilityState === 'hidden') {
                isPaused = true;
            } else if (document.visibilityState === 'visible') {
                isPaused = false;
                const svg = document.getElementById('svg-container');
                if (svg) {
                    while (svg.firstChild) {
                        svg.removeChild(svg.firstChild);
                    }
                }
                document.querySelectorAll('[id^="mwi-hit-tracker-"]').forEach(el => {
                    if (el) {
                        el.remove();
                    }
                });
                document.querySelectorAll('circle[fill^="rgba"]').forEach(el => {
                    if (el.parentNode === svg) {
                        el.parentNode.removeChild(el);
                    }
                });
            }
        });
    }

    // 启动初始化函数
    init();

    // 添加弹道拖尾创建函数
    /**
     * 创建弹道拖尾效果
     * @param {number} x - 拖尾图片的x坐标
     * @param {number} y - 拖尾图片的y坐标
     * @param {number} halfSize - 图片半尺寸,用于居中定位
     * @param {number} size - 图片尺寸
     * @param {string} imageUrl - 图片URL
     * @param {number} angle - 旋转角度
     */
    function createProjectileTrail(x, y, halfSize, size, imageUrl, angle) {
        if (isPaused) return;
        
        // 获取SVG容器
        const svg = document.getElementById('svg-container');
        if (!svg) return;
        
        // 获取参数
        const params = window.hitTrackerEffectParams || {};
        const fadeTime = params.projectileTrailFadeTime || 300; // 默认300ms
        const trailCount = params.projectileTrailCount || 5; // 默认拖尾数量
        
        // 创建克隆的图片元素作为拖尾
        const trailImage = document.createElementNS("http://www.w3.org/2000/svg", "image");
        trailImage.setAttribute("href", imageUrl);
        trailImage.setAttribute("width", size);
        trailImage.setAttribute("height", size);
        trailImage.setAttribute("x", x - halfSize);
        trailImage.setAttribute("y", y - halfSize);
        
        // 应用相同的旋转
        if (angle !== 0) {
            trailImage.setAttribute("transform", `rotate(${angle}, ${x}, ${y})`);
        }
        
        // 控制拖尾数量
        const existingTrails = svg.querySelectorAll('.projectile-trail');
        if (existingTrails.length >= trailCount) {
            // 如果拖尾数量超过设定值,移除最早创建的拖尾
            const oldestTrail = existingTrails[0];
            if (oldestTrail && oldestTrail.parentNode) {
                oldestTrail.parentNode.removeChild(oldestTrail);
            }
        }
        
        // 添加类名以便管理
        trailImage.classList.add('projectile-trail');
        
        // 设置初始透明度
        trailImage.style.opacity = '0.7';
        
        // 添加到SVG
        svg.appendChild(trailImage);
        
        // 设置淡出效果
        setTimeout(() => {
            trailImage.style.transition = `opacity ${fadeTime}ms linear`;
            trailImage.style.opacity = '0';
            
            // 淡出后移除
            setTimeout(() => {
                if (trailImage.parentNode) {
                    trailImage.parentNode.removeChild(trailImage);
                }
            }, fadeTime);
        }, 10);
    }

    // 打开玩家专属图片上传器
    function openPlayerImageUploader(playerIndex, previewElement, checkboxElement) {
        // 创建文件输入框
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'image/*';
        fileInput.style.display = 'none';
        document.body.appendChild(fileInput);
        
        // 处理文件选择
        fileInput.addEventListener('change', (e) => {
            if (e.target.files && e.target.files[0]) {
                const file = e.target.files[0];
                // 处理图片上传,不更改勾选框状态
                processPlayerImage(file, playerIndex, previewElement, null);
            }
            // 移除文件输入框
            document.body.removeChild(fileInput);
        });
        
        // 处理取消选择
        fileInput.addEventListener('cancel', () => {
            document.body.removeChild(fileInput);
        });
        
        // 触发文件选择对话框
        fileInput.click();
    }
    
    // 处理玩家专属图片
    function processPlayerImage(file, playerIndex, previewElement, checkboxElement) {
        if (!file || !file.type.startsWith('image/')) {
            alert('请选择有效的图片文件!');
            return;
        }
        
        const reader = new FileReader();
        reader.onload = function(e) {
            const img = new Image();
            img.onload = function() {
                // 获取自定义图片大小参数
                const params = window.hitTrackerEffectParams || {};
                const targetSize = params.projectileSize || 40; // 默认40px
                
                // 创建canvas来调整图片大小
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                
                // 计算等比例缩放尺寸
                let width = img.width;
                let height = img.height;
                let scale;
                
                // 确定哪个边是最短的,并根据最短边计算缩放比例
                if (width < height) {
                    // 宽度是最短边
                    scale = targetSize / width;
                    width = targetSize;
                    height = Math.round(height * scale);
                } else {
                    // 高度是最短边或宽高相等
                    scale = targetSize / height;
                    height = targetSize;
                    width = Math.round(width * scale);
                }
                
                // 设置画布尺寸为缩放后的尺寸
                canvas.width = width;
                canvas.height = height;
                
                // 绘制并调整图片大小
                ctx.drawImage(img, 0, 0, width, height);
                
                // 转换为dataURL
                const dataURL = canvas.toDataURL('image/png');
                
                // 保存到玩家设置
                playerProjectileImages[playerIndex] = dataURL;
                
                // 更新预览
                if (previewElement) {
                    const previewImg = previewElement.querySelector('img');
                    if (previewImg) {
                        previewImg.src = dataURL;
                    } else {
                        const newImg = document.createElement('img');
                        newImg.src = dataURL;
                        newImg.style.maxWidth = '100%';
                        newImg.style.maxHeight = '100%';
                        previewElement.appendChild(newImg);
                    }
                }
                
                // 保存设置,但不修改勾选框状态
                saveSettings();
            };
            img.src = e.target.result;
        };
        reader.readAsDataURL(file);
    }

    // 创建浮动伤害数字效果
    function createFloatingNumber(x, y, damage, color) {
        if (isPaused || !effectDrawEnabled[6]) return;
        
        const svg = document.getElementById('svg-container');
        if (!svg) return;
        
        // 获取自定义参数
        const params = window.hitTrackerEffectParams || {};
        const duration = params.floatingDuration || 1000; // 默认1000ms
        const fontSize = params.floatingFontSize || 30; // 默认字号30px
        const strokeWidth = params.floatingStrokeWidth || 2; // 默认描边宽度2px
        const strokeColor = params.floatingStrokeColor || '#FFFFFF'; // 默认描边颜色白色
        const riseHeight = params.floatingRiseHeight || 20; // 默认上升高度20px
        const opacityDelay = params.floatingOpacityDelay || 50; // 默认透明度延迟50%
        
        // 创建文本元素
        const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
        text.textContent = damage; // 设置伤害数值
        text.setAttribute('x', x);
        text.setAttribute('y', y);
        text.setAttribute('text-anchor', 'middle'); // 文本居中对齐
        text.setAttribute('font-size', fontSize);
        text.setAttribute('font-weight', 'bold'); // 添加字体加粗
        text.setAttribute('fill', color);
        
        // 设置描边
        if (strokeWidth > 0) {
            text.setAttribute('stroke', strokeColor);
            text.setAttribute('stroke-width', strokeWidth);
            text.setAttribute('paint-order', 'stroke fill');
        }
        
        svg.appendChild(text);
        
        // 动画
        let startTime = null;
        
        function animateFloating(timestamp) {
            if (isPaused) return;
            
            if (!startTime) startTime = timestamp;
            const elapsed = timestamp - startTime;
            const progress = Math.min(elapsed / duration, 1);
            
            // 计算当前位置(上升效果)
            const currentY = y - (progress * riseHeight);
            text.setAttribute('y', currentY);
            
            // 计算不透明度(后半段渐渐消失)
            let opacity = 1;
            // 使用不透明度延迟参数,例如:50%表示动画进行到50%时才开始降低不透明度
            const opacityDelayPoint = opacityDelay / 100;
            if (progress > opacityDelayPoint) {
                // 重新映射进度以实现延迟效果
                const fadeProgress = (progress - opacityDelayPoint) / (1 - opacityDelayPoint);
                opacity = 1 - fadeProgress;
            }
            text.setAttribute('opacity', opacity);
            
            if (progress < 1) {
                requestAnimationFrame(animateFloating);
            } else {
                text.remove();
            }
        }
        
        requestAnimationFrame(animateFloating);
    }

    // 切换伤害数字显示/隐藏
    function toggleDamageNumbers(hide) {
        const splatsContainers = document.querySelectorAll('.CombatUnit_splatsContainer__2xcc0');
        splatsContainers.forEach(container => {
            container.style.visibility = hide ? 'hidden' : 'visible';
        });
    }

})();