Light.gg Bilingual Display Tool

命运2工具网站 light.gg 的增强脚本,将物品名显示为双语,并可选择性设置tooltip语言。

// ==UserScript==
// @name                Light.gg Bilingual Display Tool
// @version             2.1
// @description         命运2工具网站 light.gg 的增强脚本,将物品名显示为双语,并可选择性设置tooltip语言。
// @author              Eliver
// @match               https://www.light.gg/*
// @grant               GM_setValue
// @grant               GM_getValue
// @license             MIT
// @namespace https://greasyfork.org/users/1267935
// ==/UserScript==

(function () {
    'use strict';

    const CACHE_KEY = 'lightgg_item_list';
    const LAST_UPDATE_KEY = 'lightgg_last_update';
    const TOOLTIP_LANG_SETTING_KEY = 'lightgg_tooltip_lang_setting';
    const ITEM_LIST_URL = 'https://20xiji.github.io/Destiny-item-list/item-list-8-2-0.json';

    let setTooltipLang = GM_getValue(TOOLTIP_LANG_SETTING_KEY, true);
    let originalLang;

    function createSettingsUI() {
        const settingsDiv = document.createElement('div');
        settingsDiv.style.cssText = `
            position: fixed;
            top: 65px;
            right: 10px;
            z-index: 9999;
            background: rgba(30, 30, 30, 0.9);
            color: #00ff00;
            padding: 12px;
            border-radius: 4px;
            font-family: Tahoma, sans-serif;
            font-size: 12px;
            box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
            display: flex;
            flex-direction: column;
            gap: 8px;
            min-width: 140px;
        `;

        // 语言切换行
        const langRow = document.createElement('div');
        langRow.style.cssText = `
            display: flex;
            align-items: center;
            justify-content: space-between;
        `;

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = setTooltipLang;
        checkbox.style.cssText = `
            appearance: none;
            width: 30px;
            height: 16px;
            background: ${setTooltipLang ? '#00ff00' : '#333'};
            border-radius: 8px;
            position: relative;
            cursor: pointer;
            margin-right: 8px;
        `;

        checkbox.addEventListener('change', function() {
            setTooltipLang = this.checked;
            GM_setValue(TOOLTIP_LANG_SETTING_KEY, setTooltipLang);
            if (setTooltipLang) {
                lggTooltip.lang = "zh-chs";
                this.style.background = '#00ff00';
            } else {
                lggTooltip.lang = originalLang;
                this.style.background = '#333';
            }
        });

        const labelText = document.createElement('span');
        labelText.textContent = '中文-Perk';
        labelText.style.cssText = 'user-select: none;';

        const label = document.createElement('label');
        label.style.cssText = 'cursor: pointer; display: flex; align-items: center;';
        label.appendChild(checkbox);
        label.appendChild(labelText);
        langRow.appendChild(label);

        // 更新按钮
        const updateButton = document.createElement('button');
        updateButton.textContent = '立即更新数据';
        updateButton.style.cssText = `
            width: 100%;
            background: #444;
            border: 1px solid #00ff00;
            color: #00ff00;
            padding: 6px 0;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
            transition: all 0.3s;
            &:hover {
                background: #555;
            }
            &:active {
                transform: scale(0.98);
            }
        `;

        updateButton.addEventListener('click', async () => {
            updateButton.disabled = true;
            updateButton.textContent = '更新中...';
            updateButton.style.opacity = '0.7';

            try {
                GM_setValue(CACHE_KEY, '');
                GM_setValue(LAST_UPDATE_KEY, '');
                itemListPromise = loadItemList();
                await itemListPromise;
                optimizedTransformReviewItems();
                alert('数据已更新!');
            } catch (error) {
                alert('更新失败:' + error.message);
            } finally {
                updateButton.disabled = false;
                updateButton.textContent = '立即更新数据';
                updateButton.style.opacity = '1';
            }
        });

        settingsDiv.appendChild(langRow);
        settingsDiv.appendChild(updateButton);
        document.body.appendChild(settingsDiv);

        // 复选框样式
        const style = document.createElement('style');
        style.textContent = `
            input[type="checkbox"]:checked::before {
                content: '';
                position: absolute;
                width: 14px;
                height: 14px;
                border-radius: 50%;
                top: 1px;
                left: 16px;
                background: #00ff00;
                transition: 0.3s;
            }
            input[type="checkbox"]::before {
                content: '';
                position: absolute;
                width: 14px;
                height: 14px;
                border-radius: 50%;
                top: 1px;
                left: 1px;
                background: #666;
                transition: 0.3s;
            }
        `;
        document.head.appendChild(style);
    }

    async function loadItemList() {
        const now = new Date().toDateString();
        const lastUpdate = GM_getValue(LAST_UPDATE_KEY, '');

        if (lastUpdate !== now) {
            try {
                const response = await fetch(ITEM_LIST_URL);
                const data = await response.json();
                GM_setValue(CACHE_KEY, JSON.stringify(data.data));
                GM_setValue(LAST_UPDATE_KEY, now);
                return data;
            } catch (error) {
                console.error('更新失败:', error);
                return JSON.parse(GM_getValue(CACHE_KEY) || '{}');
            }
        }
        return JSON.parse(GM_getValue(CACHE_KEY) || '{}');
    }

    let itemListPromise = loadItemList();

    function optimizedTransformReviewItems() {
        const aElements = document.querySelectorAll('.item-name h2, .item-name a, .key-perk strong');
        const lang = window.location.pathname.includes('/zh-chs/') ? 'zh-chs' : 'en';

        itemListPromise.then(itemList => {
            aElements.forEach(element => {
                const originalText = element.textContent.trim();
                const key = Object.keys(itemList).find(key =>
                    itemList[key][lang]?.toLowerCase() === originalText.toLowerCase());
                if (key) {
                    const translatedName = lang === 'zh-chs' ? itemList[key].en : itemList[key]['zh-chs'];
                    element.textContent = `${originalText} | ${translatedName}`;
                }
            });
        });
    }

    function debounce(func, wait) {
        let timeout;
        return (...args) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    // XHR拦截
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        const url = arguments[1];
        if (/api\.light\.gg\/items\/\d*\/?/.test(url)) {
            this.addEventListener('load', debounce(optimizedTransformReviewItems, 300));
        }
        originalOpen.apply(this, arguments);
    };

    // DOM观察者
    const observer = new MutationObserver(debounce((mutations) => {
        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                optimizedTransformReviewItems();
                break;
            }
        }
    }, 300));
    observer.observe(document.body, { childList: true, subtree: true });

    // 初始化
    window.addEventListener('load', () => {
        createSettingsUI();
        originalLang = lggTooltip.lang;
        if (setTooltipLang) lggTooltip.lang = "zh-chs";

        const reviewTab = document.getElementById('review-tab');
        reviewTab?.click();
        optimizedTransformReviewItems();
    });
})();