Method.gg - Fellowship Auto-Load Routes

Adding quick load buttons to dungeons, to automatically load routes and open the map in full screen

// ==UserScript==
// @name         	Method.gg - Fellowship Auto-Load Routes
// @name:de        	Method.gg - Fellowship Routen automatisch laden
// @namespace    	https://gf.qytechs.cn/users/928242
// @version      	1.0.0
// @description  	Adding quick load buttons to dungeons, to automatically load routes and open the map in full screen
// @description:de 	Schnellladetasten zu Dungeons hinzufügen, um Routen automatisch zu laden und die Karte im Vollbildmodus zu öffnen
// @author       	Kamikaze (https://github.com/Kamiikaze)
// @supportURL     	https://github.com/Kamiikaze/Tampermonkey/issues
// @iconURL      	https://www.playfellowship.com/fel_assets/favicon.ico?v=3
// @match        	https://www.method.gg/fellowship/route-planner*
// @license      	MIT
// @grant         	none
// ==/UserScript==

(function() {
    'use strict';

    // ============================================
    // CONFIGURATION - Adjust these settings
    // ============================================
    const CONFIG = {
        // Which route to load (1 = first route, 2 = second route, etc.)
        // Will fallback to first route if selected route doesn't exist
        ROUTE_INDEX: 1,

        // Enable fullscreen mode after loading route
        ENABLE_FULLSCREEN: true,

        // Hide the right sidebar after loading
        HIDE_SIDEBAR: true,

        // Show notifications
        SHOW_NOTIFICATIONS: true,

        // Notification duration in milliseconds (-1 for permanent)
        NOTIFICATION_DURATION: 5000
    };
    // ============================================

    // Simple notification system with stacking fix
    function showNotification(text, type = 'default', permanent = false) {
        if (!CONFIG.SHOW_NOTIFICATIONS) return;

        const notification = document.createElement('div');
        notification.className = `method-notification ${type}`;
        notification.textContent = text;

        // Calculate position based on existing notifications
        const existingNotifications = document.querySelectorAll('.method-notification');
        let topPosition = 90;
        existingNotifications.forEach(notif => {
            topPosition += notif.offsetHeight + 10; // 10px gap between notifications
        });

        notification.style.top = `${topPosition}px`;
        document.body.appendChild(notification);

        // Trigger animation
        setTimeout(() => notification.classList.add('show'), 10);

        // Auto-remove after duration
        if (CONFIG.NOTIFICATION_DURATION > 0) {
            setTimeout(() => {
                notification.classList.remove('show');
                setTimeout(() => {
                    notification.remove();
                    // Reposition remaining notifications
                    repositionNotifications();
                }, 300);
            }, permanent ? -1 : CONFIG.NOTIFICATION_DURATION);
        }
    }

    // Reposition all notifications to fill gaps
    function repositionNotifications() {
        const notifications = document.querySelectorAll('.method-notification');
        let topPosition = 90;
        notifications.forEach(notif => {
            notif.style.top = `${topPosition}px`;
            topPosition += notif.offsetHeight + 10;
        });
    }

    // Add notification styles
    const style = document.createElement('style');
    style.textContent = `
        .method-notification {
            position: fixed;
            right: 20px;
            background: #243743;
            color: white;
            padding: 15px 20px;
            border-radius: 8px;
            border: 2px solid #637cf9;
            box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.3);
            z-index: 10000;
            opacity: 0;
            transform: translateX(400px);
            transition: all 0.3s ease;
            max-width: 300px;
            font-size: 14px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        }
        .method-notification.show {
            opacity: 1;
            transform: translateX(0);
        }
        .method-notification.error {
            background: #9c0000;
            border-color: #f96363;
        }
    `;
    document.head.appendChild(style);

    // Use simple element waiter
    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if (element) {
                resolve(element);
                return;
            }

            const observer = new MutationObserver((mutations, obs) => {
                const element = document.querySelector(selector);
                if (element) {
                    obs.disconnect();
                    resolve(element);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timeout waiting for ${selector}`));
            }, timeout);
        });
    }

    // Helper function to click an element
    function clickElement(element) {
        if (element) {
            element.click();
            return true;
        }
        return false;
    }

    // Get route name from element
    function getRouteName(routeElement) {
        const nameElement = routeElement.querySelector('.dungeon-guide-route__name');
        return nameElement ? nameElement.textContent.trim() : 'Unknown Route';
    }

    // Get dungeon name from page
    function getDungeonName() {
        const titleElement = document.querySelector('.raid-boss-name');
        return titleElement ? titleElement.textContent.trim() : 'Unknown Dungeon';
    }

    // Main automation function for dungeon pages
    async function autoLoadRoute() {
        console.info('Starting auto-load sequence...');
        showNotification('🎮 Starting auto-load sequence...', 'default');

        try {
            const dungeonName = getDungeonName();

            // Wait a bit for page to fully load
            await new Promise(resolve => setTimeout(resolve, 1000));

            // Step 1: Find and click the route load button
            console.info(`Looking for route #${CONFIG.ROUTE_INDEX} Load button...`);

            const allLoadButtons = await waitForElement('.dungeon-guide-route__load');
            const loadButtons = document.querySelectorAll('.dungeon-guide-route__load');

            let targetIndex = CONFIG.ROUTE_INDEX - 1; // Convert to 0-based index
            let loadButton = loadButtons[targetIndex];

            // Fallback to first route if selected route doesn't exist
            if (!loadButton && loadButtons.length > 0) {
                console.warn(`Route #${CONFIG.ROUTE_INDEX} not found, falling back to first route`);
                targetIndex = 0;
                loadButton = loadButtons[0];
            }

            if (loadButton) {
                // Get the route container to find the route name
                const routeContainer = loadButton.closest('.dungeon-guide-route');
                const routeName = getRouteName(routeContainer);

                console.info(`Clicking Load button for route: "${routeName}"`);
                showNotification(`📥 Loading route: ${routeName}`, 'default');

                clickElement(loadButton);
                await new Promise(resolve => setTimeout(resolve, 1500));

                showNotification(`✅ Route "${routeName}" loaded successfully!`, 'default');
            } else {
                console.error('No load buttons found');
                showNotification('❌ No routes found to load', 'error');
            }

            // Step 2: Enable fullscreen
            if (CONFIG.ENABLE_FULLSCREEN) {
                console.info('Looking for Fullscreen button...');
                const fullscreenButton = await waitForElement('#route-planner-fullscreen');

                if (fullscreenButton) {
                    console.info('Clicking Fullscreen button...');
                    clickElement(fullscreenButton);
                    await new Promise(resolve => setTimeout(resolve, 500));
                } else {
                    console.warn('Fullscreen button not found');
                }
            } else {
                console.info('Fullscreen disabled in config');
            }

            // Step 3: Hide right sidebar
            if (CONFIG.HIDE_SIDEBAR) {
                console.info('Looking for sidebar hide button...');
                const hideButton = await waitForElement('.route-rail-toggle');

                if (hideButton) {
                    console.info('Clicking hide sidebar button...');
                    clickElement(hideButton);
                } else {
                    console.warn('Sidebar hide button not found');
                }
            } else {
                console.info('Sidebar hiding disabled in config');
            }

            console.info('Auto-load sequence completed!');
            showNotification(`🎉 Auto-load completed for ${dungeonName}!`, 'default');

        } catch (error) {
            console.error('Error during auto-load:', error);
            showNotification(`❌ Error: ${error.message}`, 'error');
        }
    }

    // Add quick load buttons to dungeon list
    function addQuickLoadButtons() {
        console.debug('Adding quick load buttons...');

        let dungeonElements = document.querySelectorAll('.items-list .item-tile');

        dungeonElements.forEach(element => {
            // Skip if button already added
            if (element.dataset.quickloadAdded) return;
            element.dataset.quickloadAdded = 'true';

            element.style.cssText = `
                display: flex;
                flex-direction: row;
                flex-wrap: nowrap;
                align-items: center;
            `;

            // Get the dungeon URL
            let dungeonUrlEl = element.querySelector('a');
            let dungeonUrl = dungeonUrlEl.href;
            if (!dungeonUrl) return;

            dungeonUrlEl.style.width = "100%";

            // Don't add button to the main route-planner page link
            if (dungeonUrl.endsWith('/route-planner') || dungeonUrl.endsWith('/route-planner/')) return;

            // Get dungeon name for tooltip
            const dungeonNameEl = element.querySelector('.item-tile__name, h3, h4');
            const dungeonName = dungeonNameEl ? dungeonNameEl.textContent.trim() : 'dungeon';

            // Create the quick load button
            const button = document.createElement('button');
            button.textContent = '⚡';
            button.style.cssText = `
                padding: 4px 8px;
                background-color: #4CAF50;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
                font-weight: bold;
                transition: background-color 0.3s;
            `;

            const routeText = CONFIG.ROUTE_INDEX === 1 ? 'first' :
                            CONFIG.ROUTE_INDEX === 2 ? 'second' :
                            `${CONFIG.ROUTE_INDEX}th`;
            button.title = `Quick Load: Open ${dungeonName} and auto-load ${routeText} route${CONFIG.ENABLE_FULLSCREEN ? ' in fullscreen' : ''}`;

            button.addEventListener('mouseover', () => {
                button.style.backgroundColor = '#45a049';
            });

            button.addEventListener('mouseout', () => {
                button.style.backgroundColor = '#4CAF50';
            });

            button.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                console.info(`Quick load clicked for: ${dungeonName}`);
                showNotification(`🚀 Opening ${dungeonName}...`, 'default');

                // Add autoload parameter to URL
                const url = new URL(dungeonUrl);
                url.searchParams.set('autoload', '1');

                // Navigate to the dungeon page with autoload parameter
                setTimeout(() => {
                    window.location.href = url.toString();
                }, 300);
            });

            // Insert the button before the dungeon element
            element.prepend(button);
        });
    }

    // Check if we're on a dungeon page with autoload parameter
    function checkAutoLoad() {
        const urlParams = new URLSearchParams(window.location.search);
        const currentPath = window.location.pathname;

        // Check if we're on a specific dungeon page (not the main list)
        const isDungeonPage = currentPath.includes('/route-planner/') &&
                             !currentPath.endsWith('/route-planner') &&
                             !currentPath.endsWith('/route-planner/');

        if (isDungeonPage && urlParams.get('autoload') === '1') {
            console.info('Autoload parameter detected!');
            autoLoadRoute();
            return true;
        }
        return false;
    }

    // Initialize script
    function init() {
        console.info('Script initialized');
        console.info('Configuration:', CONFIG);

        // Check if we should auto-load
        const isAutoLoading = checkAutoLoad();

        // If not auto-loading, add quick load buttons to the list page
        if (!isAutoLoading) {
            // Wait for page to load
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => {
                    setTimeout(addQuickLoadButtons, 1000);
                });
            } else {
                setTimeout(addQuickLoadButtons, 1000);
            }

            // Also observe for dynamic content loading
            const observer = new MutationObserver(() => {
                addQuickLoadButtons();
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
    }

    init();
})();

QingJ © 2025

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