MWI-Hit-Tracker-More-Animation

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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';
        });
    }

})();