MWI Avatar and Name Replacer

Overlay avatar with custom image and name, hide original SVG <use>, and provide UI to change avatar.

// ==UserScript==
// @name         MWI Avatar and Name Replacer
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Overlay avatar with custom image and name, hide original SVG <use>, and provide UI to change avatar.
// @author       Pythius-Demon
// @match        https://www.milkywayidle.com/game*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let currentImageURL = localStorage.getItem('customAvatarURL') || '';
    let customAvatarName = localStorage.getItem('customAvatarName') || '';

    function updateOverlayPosition() {
        const topRightUse = document.querySelector('svg use[href*="avatar_outfits_sprite"]:not(.combat)');
        if (topRightUse) {
            const container = topRightUse.closest('g') || topRightUse.closest('svg') || topRightUse;
            const rect = container.getBoundingClientRect();

            let overlay = document.getElementById('custom-avatar-overlay-top');
            if (!overlay) {
                overlay = document.createElement('img');
                overlay.id = 'custom-avatar-overlay-top';
                overlay.style.position = 'fixed';
                overlay.style.pointerEvents = 'none';
                overlay.style.objectFit = 'cover';
                overlay.style.zIndex = '9999';
                document.body.appendChild(overlay);
            }

            if (currentImageURL) {
                topRightUse.style.visibility = 'hidden';
                overlay.style.left = rect.left + 'px';
                overlay.style.top = rect.top + 'px';
                overlay.style.width = rect.width + 'px';
                overlay.style.height = rect.height + 'px';
                overlay.src = currentImageURL;
                overlay.style.display = 'block';
            } else {
                topRightUse.style.visibility = 'visible';
                overlay.style.display = 'none';
                overlay.src = '';
            }
        }

        const combatHeader = document.querySelector('.CombatPanel_title__WUVp8');
        const isCombatTask = combatHeader?.firstElementChild?.textContent?.trim().toLowerCase() === 'combat';

        const combatUse = document.querySelector('.CombatUnit_unitIconContainer__kVrff svg use[href*="avatar_outfits_sprite"]');
        const existingOverlay = document.getElementById('custom-avatar-overlay-combat');

        if (!isCombatTask) {
            if (existingOverlay) existingOverlay.remove();
            if (combatUse) combatUse.style.visibility = 'visible';
            return;
        }

        if (combatUse && isCombatTask) {
            const avatarBox = document.querySelector('.CombatUnit_unitIconContainer__kVrff');
            if (!avatarBox) return;

            let overlay = existingOverlay;
            if (!overlay) {
                overlay = document.createElement('img');
                overlay.id = 'custom-avatar-overlay-combat';
                overlay.style.position = 'absolute';
                overlay.style.pointerEvents = 'none';
                overlay.style.objectFit = 'cover';
                overlay.style.zIndex = '0';
                avatarBox.style.position = 'relative';
                avatarBox.insertBefore(overlay, avatarBox.firstChild);
            }

            overlay.style.left = '0';
            overlay.style.top = '0';
            overlay.style.width = avatarBox.clientWidth + 'px';
            overlay.style.height = avatarBox.clientHeight + 'px';

            if (currentImageURL) {
                combatUse.style.visibility = 'hidden';
                overlay.src = currentImageURL;
                overlay.style.display = 'block';
            } else {
                combatUse.style.visibility = 'visible';
                overlay.style.display = 'none';
                overlay.src = '';
            }
        }
    }

    function updateAvatarName() {
        const nameDiv = document.querySelector('.CharacterName_name__1amXp span');
        if (nameDiv) {
            if (customAvatarName) {
                nameDiv.textContent = customAvatarName;
            } else {
                nameDiv.textContent = nameDiv.parentElement.getAttribute('data-name') || 'Avatar';
            }
        }
    }

    function createUI() {
        const wrapper = document.createElement('div');
        wrapper.id = 'custom-avatar-ui';
        wrapper.style.position = 'absolute';
        wrapper.style.zIndex = '10000';
        wrapper.style.fontFamily = 'Arial, sans-serif';
        wrapper.style.display = 'flex';
        wrapper.style.flexDirection = 'column';
        wrapper.style.alignItems = 'flex-start';
        wrapper.style.gap = '6px';
        wrapper.style.pointerEvents = 'auto';

        const panel = document.createElement('div');
        panel.style.background = '#111';
        panel.style.border = '1px solid #0f0';
        panel.style.borderRadius = '10px';
        panel.style.padding = '8px';
        panel.style.display = 'none';
        panel.style.flexDirection = 'column';
        panel.style.gap = '6px';
        panel.style.width = '240px';
        panel.style.color = '#fff';

        const input = document.createElement('input');
        input.type = 'text';
        input.placeholder = 'Paste image URL';
        input.style.padding = '6px';
        input.style.borderRadius = '4px';
        input.style.border = '1px solid #ccc';
        input.style.width = '100%';
        input.value = currentImageURL;

        const nameInput = document.createElement('input');
        nameInput.type = 'text';
        nameInput.placeholder = 'Enter custom avatar name';
        nameInput.style.padding = '6px';
        nameInput.style.borderRadius = '4px';
        nameInput.style.border = '1px solid #ccc';
        nameInput.style.width = '100%';
        nameInput.value = customAvatarName;

        const applyBtn = document.createElement('button');
        applyBtn.innerText = 'Apply';
        applyBtn.style.padding = '5px';
        applyBtn.style.background = '#222';
        applyBtn.style.border = '1px solid #0f0';
        applyBtn.style.color = '#0f0';
        applyBtn.style.borderRadius = '4px';
        applyBtn.style.cursor = 'pointer';

        applyBtn.onclick = () => {
            const newURL = input.value.trim();
            const newName = nameInput.value.trim();

            if (newURL) {
                localStorage.setItem('customAvatarURL', newURL);
                currentImageURL = newURL;
            } else {
                localStorage.removeItem('customAvatarURL');
                currentImageURL = '';
            }

            if (newName) {
                localStorage.setItem('customAvatarName', newName);
                customAvatarName = newName;
            } else {
                localStorage.removeItem('customAvatarName');
                customAvatarName = '';
            }

            updateOverlayPosition();
            updateAvatarName();
        };

        panel.appendChild(input);
        panel.appendChild(nameInput);
        panel.appendChild(applyBtn);
        wrapper.appendChild(panel);

        const toggleBtn = document.createElement('div');
        toggleBtn.title = 'Toggle Avatar Input';
        toggleBtn.style.width = '16px';
        toggleBtn.style.height = '16px';
        toggleBtn.style.borderRadius = '50%';
        toggleBtn.style.backgroundColor = '#0f0';
        toggleBtn.style.cursor = 'pointer';
        toggleBtn.style.boxShadow = '0 0 4px #0f0';
        toggleBtn.style.position = 'absolute';
        toggleBtn.style.bottom = '-8px';
        toggleBtn.style.right = '-8px';
        toggleBtn.style.zIndex = '10001';

        toggleBtn.addEventListener('click', () => {
            panel.style.display = panel.style.display === 'none' ? 'flex' : 'none';
            toggleBtn.style.backgroundColor = panel.style.display === 'flex' ? '#b00' : '#0f0';
            toggleBtn.style.boxShadow = panel.style.display === 'flex' ? '0 0 6px #b00' : '0 0 4px #0f0';

            if (panel.style.display === 'flex') {
                panel.style.position = 'absolute';
                panel.style.top = '100%';
                panel.style.left = 'auto';
                panel.style.right = '0';
            }
        });

        wrapper.appendChild(toggleBtn);

        const interval = setInterval(() => {
            const avatar = document.querySelector('.FullAvatar_fullAvatar__3RB2h');
            if (avatar && avatar.offsetParent !== null) {
                const rect = avatar.getBoundingClientRect();
                wrapper.style.left = rect.left + window.scrollX + rect.width - wrapper.offsetWidth + 'px';
                wrapper.style.top = rect.top + window.scrollY + rect.height + 'px';
            }
        }, 500);

        document.body.appendChild(wrapper);
    }

    function injectCustomAvatarHTML() {
        const combatHeader = document.querySelector('.CombatPanel_title__WUVp8');
        const isCombatTask = combatHeader?.firstElementChild?.textContent?.trim().toLowerCase() === 'combat';

        if (!isCombatTask) return;

        if (!document.querySelector('.CombatUnit_unitIconContainer__kVrff')) {
            const div = document.createElement('div');
            div.className = 'CombatUnit_unitIconContainer__kVrff';
            div.style.position = 'fixed';
            div.style.top = '200px';
            div.style.left = '10px';
            div.style.zIndex = '9999';

            const avatarHTML = `
                <div class="FullAvatar_fullAvatar__3RB2h">
                    <div class="FullAvatar_avatar__2w8kS">
                        <svg role="img" aria-label="avatar" class="Icon_icon__2LtL_" width="100%" height="100%">
                            <use href="/static/media/avatars_sprite.4dea577e.svg#person_default"></use>
                        </svg>
                    </div>
                    <div class="FullAvatar_avatarOutfit__3GHXg">
                        <svg role="img" aria-label="avatar outfit" class="Icon_icon__2LtL_" width="100%" height="100%">
                            <use href="/static/media/avatar_outfits_sprite.b1f4dc7f.svg#tshirt_default"></use>
                        </svg>
                    </div>
                </div>`;

            div.innerHTML = avatarHTML;
            document.body.appendChild(div);
            console.log('[MWI] Combat avatar block injected.');
        }
    }

    function waitForGameAndInject() {
        const anchor = document.querySelector('.CharacterName_name__1amXp span');
        if (!anchor) {
            setTimeout(waitForGameAndInject, 500);
            return;
        }

        createUI();
        updateOverlayPosition();
        updateAvatarName();
        injectCustomAvatarHTML();
        window.addEventListener('resize', updateOverlayPosition);
        window.addEventListener('scroll', updateOverlayPosition);
        setInterval(() => {
            updateOverlayPosition();
            injectCustomAvatarHTML();
        }, 2000);
    }

    waitForGameAndInject();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址