FlatMMO Data Pages Beautifier

Beautifies FlatMMO mining and woodcutting data pages with complete tool and resource level requirements

当前为 2025-08-20 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         FlatMMO Data Pages Beautifier
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Beautifies FlatMMO mining and woodcutting data pages with complete tool and resource level requirements
// @author       Pizza1337
// @match        https://flatmmo.com/data/mining.html
// @match        https://flatmmo.com/data/woodcutting.html
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Get current page type
    const pageType = window.location.pathname.includes('mining') ? 'mining' : 'woodcutting';
    const toolType = pageType === 'mining' ? 'pickaxe' : 'axe';
    const otherPage = pageType === 'mining' ? 'woodcutting' : 'mining';
    const otherPageUrl = pageType === 'mining' ?
        'https://flatmmo.com/data/woodcutting.html' :
        'https://flatmmo.com/data/mining.html';

    // Define available tools
    const tools = ['bronze', 'iron', 'silver', 'gold', 'promethium', 'titanium', 'ancient'];

    // Define level brackets
    const levelBrackets = [
        {min: 1, max: 9, label: '1-9'},
        {min: 10, max: 19, label: '10-19'},
        {min: 20, max: 29, label: '20-29'},
        {min: 30, max: 39, label: '30-39'},
        {min: 40, max: 49, label: '40-49'},
        {min: 50, max: 59, label: '50-59'},
        {min: 60, max: 69, label: '60-69'},
        {min: 70, max: 79, label: '70-79'},
        {min: 80, max: 89, label: '80-89'},
        {min: 90, max: 99, label: '90-99'},
        {min: 100, max: 100, label: '100'}
    ];

    // Define tool level requirements
    const toolLevelRequirements = {
        mining: {
            'bronze_pickaxe': 1,
            'iron_pickaxe': 10,
            'silver_pickaxe': 20,
            'gold_pickaxe': 30,
            'promethium_pickaxe': 50,
            'titanium_pickaxe': 65,
            'ancient_pickaxe': 101
        },
        woodcutting: {
            'bronze_axe': 1,
            'iron_axe': 10,
            'silver_axe': 20,
            'gold_axe': 30,
            'promethium_axe': 50,
            'titanium_axe': 65,
            'ancient_axe': 101
        }
    };

    const toolReqs = toolLevelRequirements[pageType];

    // Define level requirements for resources
    const resourceLevelRequirements = {
        mining: {
            'coal': 1,
            'copper': 1,
            'iron': 5,
            'silver': 15,
            'gold': 30,
            'promethium': 50,
            'titanium': 65,
            'giant_coal': 1,
            'giant_copper': 1,
            'giant_iron': 5
        },
        woodcutting: {
            'tree': 1,
            'oak_tree': 10,
            'willow_tree': 20,
            'maple_tree': 30,
            'mangrove_tree': 50,
            'haunted_tree': 65
        }
    };

    const levelReqs = resourceLevelRequirements[pageType];

    // Parse the original table data BEFORE clearing the page
    function parseTableData() {
        const data = [];

        // Try to find an actual table element
        const tables = document.getElementsByTagName('table');
        if (tables.length > 0) {
            const table = tables[0];
            const rows = table.getElementsByTagName('tr');

            for (let i = 1; i < rows.length; i++) { // Skip header row
                const cells = rows[i].getElementsByTagName('td');
                if (cells.length >= 7) {
                    const resourceName = cells[0].textContent.trim();
                    const toolName = cells[2].textContent.trim();
                    const xpPerHourText = cells[6].textContent.trim();
                    const xpMatch = xpPerHourText.match(/([\d,]+\.?\d*)/);
                    const xpPerHourValue = xpMatch ? parseFloat(xpMatch[1].replace(/,/g, '')) : 0;

                    // Get base XP value
                    let xpValue = parseInt(cells[1].textContent.trim()) || 0;

                    // Fix giant resources XP (they give same as non-giant)
                    if (resourceName === 'giant_coal') xpValue = 10;
                    if (resourceName === 'giant_copper') xpValue = 15;
                    if (resourceName === 'giant_iron') xpValue = 40;

                    // Fix ticks (add 1 to all values)
                    const originalTicks = parseInt(cells[4].textContent.trim()) || 0;
                    const actualTicks = originalTicks + 1;

                    // Recalculate XP per tick and XP per hour with corrected values
                    const xpPerTick = xpValue / actualTicks;
                    const ticksPerHour = 3600 / (actualTicks * 0.5); // 0.5 seconds per tick
                    const actualXpPerHour = xpValue * ticksPerHour;

                    data.push({
                        resource: resourceName,
                        xp: xpValue,
                        tool: toolName,
                        toolLevelRequired: toolReqs[toolName] || 1,
                        level: parseInt(cells[3].textContent.trim()) || 0,
                        ticks: actualTicks,
                        xpPerTick: xpPerTick,
                        xpPerHour: actualXpPerHour,
                        xpPerHourText: `${actualXpPerHour.toLocaleString()} xp/h`,
                        levelRequired: levelReqs[resourceName] || 1
                    });
                }
            }
        }

        return data;
    }

    // Store the data BEFORE modifying the page
    const tableData = parseTableData();

    if (tableData.length === 0) {
        alert('Error: Could not parse data from the page. Please refresh and try again.');
        return;
    }

    // Store original order of resources
    const resourceOrder = {};
    const uniqueResources = [...new Set(tableData.map(item => item.resource))];
    uniqueResources.forEach((resource, index) => {
        resourceOrder[resource] = index;
    });

    // Fetch user level from hiscores API
    async function fetchUserLevel(username) {
        return new Promise((resolve) => {
            const apiUrl = `https://flatmmo.com/api/hiscores/${pageType}.php`;

            //console.log(`Fetching ${pageType} level for "${username}" from API: ${apiUrl}`);

            GM_xmlhttpRequest({
                method: 'GET',
                url: apiUrl,
                headers: {
                    'Accept': 'application/json'
                },
                onload: function(response) {
                    try {
                        // Parse JSON response
                        const data = JSON.parse(response.responseText);
                        //console.log(`API returned ${data.length} entries`);

                        // Convert username to lowercase for matching (API uses lowercase)
                        const searchUsername = username.toLowerCase().trim();
                        //console.log(`Searching for username: "${searchUsername}"`);

                        // Search for the username in the data
                        for (let i = 0; i < data.length; i++) {
                            const entry = data[i];
                            const entryUsername = entry.username ? entry.username.toLowerCase().trim() : '';

                            if (entryUsername === searchUsername) {
                                // Get the level based on page type
                                const levelField = `${pageType}_level`;
                                const xpField = `${pageType}_xp`;

                                //console.log(`Found matching entry for "${searchUsername}":`, entry);
                                //console.log(`Looking for level field: "${levelField}"`);
                                //console.log(`Level field value: ${entry[levelField]}`);

                                const level = parseInt(entry[levelField]);
                                const xp = entry[xpField] || 0;

                                if (!isNaN(level) && level > 0) {
                                    //console.log(`✅ Found ${username} with ${pageType} level: ${level} (XP: ${xp.toLocaleString()})`);
                                    resolve(level);
                                    return;
                                } else {
                                    console.error(`❌ Invalid level value: ${entry[levelField]}`);
                                    console.error(`Full entry data:`, entry);
                                    console.error(`All fields in entry:`, Object.keys(entry));
                                }
                            }
                        }

                        console.log(`❌ Username "${searchUsername}" not found in API data`);

                        // Check if username might be there with different casing or spaces
                        const possibleMatch = data.find(entry =>
                            entry.username && entry.username.replace(/\s/g, '').toLowerCase() === searchUsername.replace(/\s/g, '')
                        );

                        if (possibleMatch) {
                            console.log(`Found possible match with different formatting: "${possibleMatch.username}"`);
                        }

                        // Log first few entries for debugging
                        if (data.length > 0) {
                            //console.log('First 5 entries from API:');
                            data.slice(0, 5).forEach(entry => {
                                //console.log(`- "${entry.username}": Level ${entry[`${pageType}_level`]}`);
                            });
                        }

                        resolve(null);
                    } catch (error) {
                        console.error('Error parsing API response:', error);
                        console.log('Response text:', response.responseText.substring(0, 500));
                        resolve(null);
                    }
                },
                onerror: function(error) {
                    console.error('Failed to fetch from API:', error);
                    resolve(null);
                }
            });
        });
    }

    // Inject modern styles
    GM_addStyle(`
        @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Poppins', system-ui, -apple-system, sans-serif;
            background: #0a0e27;
            color: #e8e6e3;
            padding: 0;
            min-height: 100vh;
            overflow-x: hidden;
        }

        body::before {
            content: '';
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background:
                radial-gradient(circle at 20% 50%, rgba(120, 80, 255, 0.15) 0%, transparent 50%),
                radial-gradient(circle at 80% 80%, rgba(0, 255, 136, 0.1) 0%, transparent 50%),
                radial-gradient(circle at 40% 20%, rgba(0, 212, 255, 0.1) 0%, transparent 50%);
            pointer-events: none;
            z-index: 1;
        }

        .container {
            max-width: 1600px;
            margin: 0 auto;
            padding: 20px;
            position: relative;
            z-index: 2;
        }

        .header {
            text-align: center;
            padding: 60px 20px 40px;
            position: relative;
        }

        .header h1 {
            font-size: 4em;
            font-weight: 700;
            margin: 0;
            background: linear-gradient(135deg, #667eea 0%, #00ff88 50%, #00d4ff 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-transform: uppercase;
            letter-spacing: 3px;
            animation: glow 3s ease-in-out infinite;
        }

        @keyframes glow {
            0%, 100% { filter: brightness(1); }
            50% { filter: brightness(1.2); }
        }

        .header p {
            margin-top: 10px;
            color: #8892b0;
            font-size: 1.2em;
        }

        .page-switcher {
            position: absolute;
            top: 20px;
            right: 20px;
            z-index: 10;
        }

        .page-switcher a {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 12px 24px;
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 255, 136, 0.2));
            border: 1px solid rgba(0, 255, 136, 0.3);
            border-radius: 12px;
            color: #00ff88;
            text-decoration: none;
            font-weight: 500;
            transition: all 0.3s;
        }

        .page-switcher a:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 30px rgba(0, 255, 136, 0.3);
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(0, 255, 136, 0.3));
        }

        .user-section {
            background: rgba(255, 255, 255, 0.03);
            backdrop-filter: blur(20px);
            border-radius: 25px;
            padding: 25px;
            margin-bottom: 30px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }

        .user-input-container {
            display: flex;
            gap: 15px;
            align-items: center;
            justify-content: center;
            flex-wrap: wrap;
        }

        .user-input-container label {
            color: #00ff88;
            font-weight: 500;
        }

        .user-input-container input {
            padding: 10px 15px;
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            color: white;
            font-family: inherit;
            width: 200px;
        }

        .user-input-container button {
            padding: 10px 20px;
            background: linear-gradient(135deg, #667eea, #00ff88);
            border: none;
            border-radius: 12px;
            color: white;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
        }

        .user-input-container button:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 30px rgba(0, 255, 136, 0.3);
        }

        .user-level-display {
            margin-top: 15px;
            text-align: center;
            padding: 15px;
            background: rgba(0, 255, 136, 0.1);
            border-radius: 12px;
            border: 1px solid rgba(0, 255, 136, 0.2);
        }

        .user-level-display .level-text {
            font-size: 1.2em;
            color: #00ff88;
            font-weight: 600;
        }

        .tool-selector {
            background: rgba(255, 255, 255, 0.03);
            backdrop-filter: blur(20px);
            border-radius: 25px;
            padding: 30px;
            margin-bottom: 40px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }

        .tool-selector h2 {
            margin: 0 0 25px 0;
            background: linear-gradient(90deg, #00ff88, #00d4ff);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            font-size: 1.5em;
            font-weight: 600;
            text-align: center;
        }

        .tool-grid {
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 25px;
        }

        .tool-option {
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            background: rgba(255, 255, 255, 0.05);
            border: 2px solid transparent;
            border-radius: 20px;
            cursor: pointer;
            transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            position: relative;
            overflow: hidden;
            min-width: 100px;
        }

        .tool-option::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 100%;
            height: 100%;
            background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
            transform: translate(-50%, -50%) scale(0);
            transition: transform 0.5s;
        }

        .tool-option:hover::before {
            transform: translate(-50%, -50%) scale(2);
        }

        .tool-option:hover {
            transform: translateY(-5px) scale(1.05);
            box-shadow: 0 15px 40px rgba(0, 255, 136, 0.3);
        }

        .tool-option.active {
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 255, 136, 0.2));
            border-color: #00ff88;
            box-shadow: 0 0 30px rgba(0, 255, 136, 0.4);
            transform: scale(1.05);
        }

        .tool-option img {
            width: 56px;
            height: 56px;
            margin-bottom: 10px;
            filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.4));
            position: relative;
            z-index: 1;
        }

        .tool-option span {
            font-size: 0.95em;
            font-weight: 500;
            text-transform: capitalize;
            position: relative;
            z-index: 1;
        }

        .filters {
            display: flex;
            gap: 20px;
            align-items: center;
            justify-content: center;
            flex-wrap: wrap;
        }

        .filter-group {
            display: flex;
            align-items: center;
            gap: 10px;
            background: rgba(255, 255, 255, 0.05);
            padding: 10px 20px;
            border-radius: 15px;
        }

        .filter-group label {
            color: #00ff88;
            font-weight: 500;
            font-size: 0.9em;
        }

        .filter-group select {
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            color: white;
            padding: 8px 12px;
            border-radius: 10px;
            font-family: inherit;
            cursor: pointer;
            transition: all 0.3s;
        }

        .filter-group select:hover {
            background: rgba(255, 255, 255, 0.15);
            border-color: rgba(0, 255, 136, 0.5);
        }

        .filter-group select:focus {
            outline: none;
            border-color: #00ff88;
            box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
        }

        .filter-group select option {
            background: #1a1f3a;
            color: white;
        }

        .level-brackets {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }

        .bracket-btn {
            padding: 8px 16px;
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            color: #8892b0;
            cursor: pointer;
            transition: all 0.3s;
            font-weight: 500;
            font-size: 0.9em;
        }

        .bracket-btn:hover {
            background: rgba(255, 255, 255, 0.1);
            color: white;
            transform: translateY(-2px);
        }

        .bracket-btn.active {
            background: linear-gradient(135deg, #667eea, #00ff88);
            border-color: transparent;
            color: white;
        }

        .bracket-btn.auto-selected {
            background: linear-gradient(135deg, #00ff88, #00d4ff);
            border-color: transparent;
            color: white;
            box-shadow: 0 0 20px rgba(0, 255, 136, 0.4);
        }

        .view-toggle {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin: 30px 0;
        }

        .view-btn {
            padding: 12px 24px;
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            color: white;
            cursor: pointer;
            transition: all 0.3s;
            font-weight: 500;
        }

        .view-btn.active {
            background: linear-gradient(135deg, #667eea, #00ff88);
            border-color: transparent;
        }

        .view-btn:hover:not(.active) {
            background: rgba(255, 255, 255, 0.1);
        }

        .stats-header {
            display: flex;
            justify-content: center;
            margin: 40px 0;
        }

        .stat-card {
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 255, 136, 0.2));
            backdrop-filter: blur(10px);
            border-radius: 25px;
            padding: 30px 40px;
            text-align: center;
            border: 2px solid rgba(0, 255, 136, 0.3);
            transition: all 0.3s;
            position: relative;
            overflow: hidden;
            min-width: 400px;
            box-shadow: 0 20px 60px rgba(0, 255, 136, 0.2);
        }

        .stat-card::before {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
            animation: shimmer 3s ease-in-out infinite;
        }

        @keyframes shimmer {
            0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
            50% { transform: translateX(0%) translateY(0%) rotate(45deg); }
            100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
        }

        .stat-card:hover {
            transform: translateY(-5px) scale(1.02);
            box-shadow: 0 25px 70px rgba(102, 126, 234, 0.3);
        }

        .stat-value {
            font-size: 3em;
            font-weight: 700;
            background: linear-gradient(135deg, #ffd700, #00ff88, #00d4ff);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 10px;
            text-shadow: 0 0 30px rgba(0, 255, 136, 0.5);
        }

        .stat-label {
            color: #00ff88;
            font-size: 1.2em;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 2px;
            margin-bottom: 15px;
        }

        .stat-sublabel {
            color: #e8e6e3;
            font-size: 1.1em;
            margin-top: 5px;
            font-weight: 500;
        }

        .stat-resource {
            color: #00d4ff;
            font-size: 1.3em;
            font-weight: 600;
            text-transform: capitalize;
        }

        .stat-level {
            color: #ffd700;
            font-weight: 700;
        }

        .resources-container {
            margin-top: 40px;
        }

        /* Card View Styles */
        .resource-cards {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
            gap: 25px;
            animation: fadeIn 0.5s ease-in;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }

        .resource-card {
            background: rgba(255, 255, 255, 0.03);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            overflow: hidden;
            transition: all 0.3s;
            cursor: pointer;
        }

        .resource-card:hover {
            transform: translateY(-5px) scale(1.02);
            box-shadow: 0 20px 50px rgba(0, 255, 136, 0.2);
            border-color: rgba(0, 255, 136, 0.3);
        }

        .resource-header {
            padding: 20px;
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 255, 136, 0.1));
            border-bottom: 1px solid rgba(255, 255, 255, 0.08);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .resource-name {
            font-size: 1.3em;
            font-weight: 600;
            color: #00d4ff;
            text-transform: capitalize;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .resource-icon {
            width: 40px;
            height: 40px;
            filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
            object-fit: contain;
        }

        .resource-levels {
            padding: 20px;
            max-height: 300px;
            overflow-y: auto;
        }

        .resource-levels::-webkit-scrollbar {
            width: 6px;
        }

        .resource-levels::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 3px;
        }

        .resource-levels::-webkit-scrollbar-thumb {
            background: linear-gradient(135deg, #667eea, #00ff88);
            border-radius: 3px;
        }

        .level-entry {
            display: grid;
            grid-template-columns: auto 1fr auto;
            gap: 15px;
            padding: 12px;
            margin-bottom: 10px;
            background: rgba(255, 255, 255, 0.02);
            border-radius: 12px;
            align-items: center;
            transition: all 0.3s;
        }

        .level-entry:hover {
            background: rgba(255, 255, 255, 0.05);
            transform: translateX(5px);
        }

        .level-entry:last-child {
            margin-bottom: 0;
        }

        .level-badge {
            background: linear-gradient(135deg, #ff6b6b, #ff8e53);
            color: white;
            padding: 6px 12px;
            border-radius: 20px;
            font-weight: 600;
            font-size: 0.85em;
        }

        .level-stats {
            display: flex;
            gap: 20px;
            align-items: center;
        }

        .stat-item {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .stat-item-value {
            font-weight: 600;
            color: #00ff88;
            font-size: 0.95em;
        }

        .stat-item-label {
            font-size: 0.75em;
            color: #8892b0;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .xp-hour-badge {
            background: linear-gradient(135deg, #667eea, #764ba2);
            padding: 8px 16px;
            border-radius: 12px;
            font-weight: 600;
            font-size: 0.9em;
            white-space: nowrap;
        }

        /* List View Styles */
        .resource-list {
            background: rgba(255, 255, 255, 0.03);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            overflow: hidden;
            animation: fadeIn 0.5s ease-in;
        }

        .list-header {
            display: grid;
            grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1.5fr;
            padding: 20px;
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 255, 136, 0.1));
            border-bottom: 2px solid rgba(0, 255, 136, 0.3);
            font-weight: 600;
            color: #00ff88;
            text-transform: uppercase;
            font-size: 0.9em;
            letter-spacing: 1px;
        }

        .list-item {
            display: grid;
            grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1.5fr;
            padding: 20px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.05);
            align-items: center;
            transition: all 0.3s;
            cursor: pointer;
        }

        .list-item:hover {
            background: rgba(255, 255, 255, 0.05);
            transform: translateX(10px);
        }

        .list-item:last-child {
            border-bottom: none;
        }

        .list-resource {
            display: flex;
            align-items: center;
            gap: 12px;
            font-weight: 600;
            color: #00d4ff;
            text-transform: capitalize;
        }

        .list-level {
            color: #ff6b6b;
            font-weight: 600;
        }

        .list-ticks {
            color: #00ff88;
        }

        .list-xp {
            color: #ffd700;
            font-weight: 500;
        }

        .sort-indicator {
            display: inline-block;
            margin-left: 5px;
            transition: transform 0.3s;
        }

        .sort-asc::after {
            content: '▲';
            font-size: 0.8em;
        }

        .sort-desc::after {
            content: '▼';
            font-size: 0.8em;
        }

        .hidden {
            display: none !important;
        }

        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: #8892b0;
        }

        .empty-state h3 {
            font-size: 1.5em;
            margin-bottom: 10px;
            color: #64748b;
        }
    `);

    // Create new UI
    document.body.innerHTML = '';

    const container = document.createElement('div');
    container.className = 'container';

    // Page switcher
    const pageSwitcher = document.createElement('div');
    pageSwitcher.className = 'page-switcher';
    pageSwitcher.innerHTML = `
        <a href="${otherPageUrl}">
            ${otherPage === 'mining' ? '⛏️' : '🪓'} Switch to ${otherPage.charAt(0).toUpperCase() + otherPage.slice(1)}
        </a>
    `;
    container.appendChild(pageSwitcher);

    // Header
    const header = document.createElement('div');
    header.className = 'header';
    header.innerHTML = `
        <h1>${pageType}</h1>
        <p>Optimize your XP gains with the perfect tool and level combination</p>
    `;
    container.appendChild(header);

    // User section
    const userSection = document.createElement('div');
    userSection.className = 'user-section';
    userSection.innerHTML = `
        <div class="user-input-container">
            <label>Username:</label>
            <input type="text" id="usernameInput" placeholder="Enter your username">
            <button id="fetchLevelBtn">Fetch Level</button>
            <button id="clearUserBtn" style="background: linear-gradient(135deg, #ff6b6b, #ff8e53);">Clear</button>
        </div>
        <div id="userLevelDisplay" class="user-level-display hidden">
            <div class="level-text">Loading...</div>
        </div>
    `;
    container.appendChild(userSection);

    // Tool selector
    const toolSelector = document.createElement('div');
    toolSelector.className = 'tool-selector';
    toolSelector.innerHTML = `
        <h2>⚒️ Select Your Tool</h2>
        <div class="tool-grid" id="toolGrid"></div>
        <div class="filters">
            <div class="filter-group">
                <label>Level Range:</label>
                <div class="level-brackets" id="levelBrackets"></div>
            </div>
            <div class="filter-group">
                <label>Sort by:</label>
                <select id="sortBy">
                    <option value="resource">Resource (Original Order)</option>
                    <option value="xpPerHour">XP per Hour</option>
                </select>
            </div>
        </div>
    `;
    container.appendChild(toolSelector);

    const toolGrid = toolSelector.querySelector('#toolGrid');
    const levelBracketsContainer = toolSelector.querySelector('#levelBrackets');

    // Add tool options
    tools.forEach(tool => {
        const toolFullName = `${tool}_${toolType}`;
        const toolOption = document.createElement('div');
        toolOption.className = 'tool-option';
        toolOption.dataset.tool = toolFullName;
        const toolLevelReq = toolReqs[toolFullName] || 1;

        toolOption.innerHTML = `
            <img src="https://flatmmo.com/images/items/${toolFullName}.png"
                 alt="${tool} ${toolType}"
                 onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2256%22 height=%2256%22 viewBox=%220 0 56 56%22><rect width=%2256%22 height=%2256%22 fill=%22%23444%22 rx=%228%22/><text x=%2228%22 y=%2235%22 text-anchor=%22middle%22 fill=%22%23aaa%22 font-size=%2224%22>⚒️</text></svg>'">
            <span>${tool}</span>
            <span style="font-size: 0.75em; color: #ff6b6b;">Lv ${toolLevelReq}</span>
        `;
        toolOption.addEventListener('click', () => filterByTool(toolFullName));
        toolGrid.appendChild(toolOption);
    });

    // Add "All Levels" button first
    const allLevelsBtn = document.createElement('button');
    allLevelsBtn.className = 'bracket-btn active';
    allLevelsBtn.dataset.min = '1';
    allLevelsBtn.dataset.max = '100';
    allLevelsBtn.textContent = 'All Levels';
    allLevelsBtn.addEventListener('click', () => selectLevelBracket({min: 1, max: 100, label: 'All Levels'}));
    levelBracketsContainer.appendChild(allLevelsBtn);

    // Add level brackets
    levelBrackets.forEach(bracket => {
        const bracketBtn = document.createElement('button');
        bracketBtn.className = 'bracket-btn';
        bracketBtn.dataset.min = bracket.min;
        bracketBtn.dataset.max = bracket.max;
        bracketBtn.textContent = bracket.label;
        bracketBtn.addEventListener('click', () => selectLevelBracket(bracket));
        levelBracketsContainer.appendChild(bracketBtn);
    });

    // View toggle
    const viewToggle = document.createElement('div');
    viewToggle.className = 'view-toggle';
    viewToggle.innerHTML = `
        <button class="view-btn active" data-view="cards">📊 Card View</button>
        <button class="view-btn" data-view="list">📋 List View</button>
    `;
    container.appendChild(viewToggle);

    // Stats summary
    const statsHeader = document.createElement('div');
    statsHeader.className = 'stats-header';
    statsHeader.id = 'statsHeader';
    container.appendChild(statsHeader);

    // Resources container
    const resourcesContainer = document.createElement('div');
    resourcesContainer.className = 'resources-container';
    resourcesContainer.id = 'resourcesContainer';
    container.appendChild(resourcesContainer);

    document.body.appendChild(container);

    // State management
    let currentTool = 'all';
    let minLevelFilter = 1;
    let maxLevelFilter = 100;
    let currentView = 'cards';
    let sortBy = 'resource';
    let sortOrder = 'asc';
    let currentUsername = GM_getValue('flatmmo_username', '');
    let currentUserLevel = null;

    // Define functions before using them
    function selectLevelBracket(bracket) {
        minLevelFilter = bracket.min;
        maxLevelFilter = bracket.max;

        // Update button states
        document.querySelectorAll('.bracket-btn').forEach(btn => {
            btn.classList.remove('active', 'auto-selected');
            if (btn.dataset.min == bracket.min && btn.dataset.max == bracket.max) {
                btn.classList.add('active');
            }
        });

        applyFilters();
    }

    function filterByTool(tool) {
        // Update active states
        document.querySelectorAll('.tool-option').forEach(opt => {
            opt.classList.toggle('active', opt.dataset.tool === tool);
        });

        // If clicking the same tool, toggle to show all
        if (currentTool === tool) {
            currentTool = 'all';
            document.querySelectorAll('.tool-option').forEach(opt => {
                opt.classList.remove('active');
            });
        } else {
            currentTool = tool;
        }

        applyFilters();
    }

    // Load saved username
    if (currentUsername) {
        document.getElementById('usernameInput').value = currentUsername;
        fetchAndSetUserLevel(currentUsername);
    }

    // Function definitions
    function selectLevelBracket(bracket) {
        minLevelFilter = bracket.min;
        maxLevelFilter = bracket.max;

        // Update button states
        document.querySelectorAll('.bracket-btn').forEach(btn => {
            btn.classList.remove('active', 'auto-selected');
            if (btn.dataset.min == bracket.min && btn.dataset.max == bracket.max) {
                btn.classList.add('active');
            }
        });

        applyFilters();
    }

    function findBestBracketForLevel(level) {
        // Find the bracket that contains the user's level
        for (let bracket of levelBrackets) {
            if (level >= bracket.min && level <= bracket.max) {
                return bracket;
            }
        }
        return {min: 1, max: 100, label: 'All Levels'};
    }

    async function fetchAndSetUserLevel(username) {
        const levelDisplay = document.getElementById('userLevelDisplay');
        const levelText = levelDisplay.querySelector('.level-text');

        levelDisplay.classList.remove('hidden');
        levelText.textContent = 'Fetching level...';

        //console.log(`Starting level fetch for username: "${username}"`);
        const level = await fetchUserLevel(username);
        //console.log(`fetchUserLevel returned: ${level}`);

        if (level !== null && level > 0) {
            currentUserLevel = level;
            levelText.innerHTML = `
                <strong style="color: #00d4ff;">${username}</strong>
                <span style="color: #8892b0;">•</span>
                ${pageType.charAt(0).toUpperCase() + pageType.slice(1)} Level:
                <strong style="color: #ffd700;">${level}</strong>
            `;

            // Auto-select the appropriate level bracket
            const bestBracket = findBestBracketForLevel(level);
            minLevelFilter = bestBracket.min;
            maxLevelFilter = bestBracket.max;

            // Update button states
            document.querySelectorAll('.bracket-btn').forEach(btn => {
                btn.classList.remove('active', 'auto-selected');
                if (btn.dataset.min == bestBracket.min && btn.dataset.max == bestBracket.max) {
                    btn.classList.add('auto-selected');
                }
            });

            applyFilters();
        } else {
            levelText.innerHTML = `
                <span style="color: #ff6b6b;">⚠️ Username not found in hiscores</span>
                <br>
                <span style="color: #8892b0; font-size: 0.9em;">
                    Make sure the username exists in the ${pageType} hiscores (may need to be in top players)
                </span>
            `;
            currentUserLevel = null;
        }
    }

    function updateStats(data) {
        const statsHeader = document.getElementById('statsHeader');

        if (data.length === 0) {
            statsHeader.innerHTML = `
                <div class="stat-card">
                    <div class="stat-label">⚠️ No Data Available</div>
                    <div class="stat-sublabel">Try adjusting your filters or check level requirements</div>
                </div>
            `;
            return;
        }

        const bestXp = Math.max(...data.map(d => d.xpPerHour));
        const bestItem = data.find(d => d.xpPerHour === bestXp);

        statsHeader.innerHTML = `
            <div class="stat-card">
                <div class="stat-label">🏆 Best XP Per Hour</div>
                <div class="stat-value">${Math.round(bestXp).toLocaleString()}</div>
                <div class="stat-sublabel">
                    <span class="stat-resource">${bestItem.resource.replace(/_/g, ' ')}</span>
                    at
                    <span class="stat-level">Level ${bestItem.level}</span>
                </div>
                <div class="stat-sublabel" style="margin-top: 5px;">
                    <span style="color: #ff6b6b;">Resource requires Lv ${bestItem.levelRequired} • Tool requires Lv ${bestItem.toolLevelRequired}</span>
                </div>
                <div class="stat-sublabel" style="margin-top: 10px; opacity: 0.8;">
                    ${bestItem.tool.replace(/_/g, ' ')} • ${bestItem.ticks} ticks • ${bestItem.xp} XP per resource
                </div>
            </div>
        `;
    }

    function getResourceImage(resource) {
        if (pageType === 'woodcutting') {
            // Use wiki images for trees
            const treeImages = {
                'tree': 'https://flatmmo.wiki/images/7/76/Normal_tree.png',
                'oak_tree': 'https://flatmmo.wiki/images/c/cf/Oak_tree.png',
                'willow_tree': 'https://flatmmo.wiki/images/1/19/Willow_tree.png',
                'maple_tree': 'https://flatmmo.wiki/images/4/4b/Maple_tree.png',
                'mangrove_tree': 'https://flatmmo.wiki/images/6/67/Mangrove_tree.png',
                'haunted_tree': 'https://flatmmo.wiki/images/a/a7/Haunted_tree.png'
            };
            return treeImages[resource] || `https://flatmmo.com/images/items/${resource}.png`;
        } else if (pageType === 'mining') {
            // Use wiki images for rocks
            const rockImages = {
                'giant_coal': 'https://flatmmo.wiki/images/f/f8/Coal_rock.png',
                'giant_copper': 'https://flatmmo.wiki/images/8/89/Copper_rock.png',
                'giant_iron': 'https://flatmmo.wiki/images/d/db/Iron_rock.png'
            };
            return rockImages[resource] || `https://flatmmo.com/images/items/${resource}.png`;
        }
        return `https://flatmmo.com/images/items/${resource}.png`;
    }

    function renderCardView(data) {
        const container = document.getElementById('resourcesContainer');

        if (data.length === 0) {
            container.innerHTML = `
                <div class="empty-state">
                    <h3>No resources found</h3>
                    <p>Try adjusting your filters or selecting a different tool</p>
                    <p style="color: #ff6b6b; margin-top: 10px;">
                        Note: Resources and tools you can't use at your level are hidden
                    </p>
                </div>
            `;
            return;
        }

        // Group data by resource
        const groupedData = {};
        data.forEach(item => {
            if (!groupedData[item.resource]) {
                groupedData[item.resource] = [];
            }
            groupedData[item.resource].push(item);
        });

        // Sort each group by level
        Object.keys(groupedData).forEach(resource => {
            groupedData[resource].sort((a, b) => a.level - b.level);
        });

        // Sort resources
        const sortedResources = Object.keys(groupedData).sort((a, b) => {
            if (sortBy === 'xpPerHour') {
                const maxA = Math.max(...groupedData[a].map(item => item.xpPerHour));
                const maxB = Math.max(...groupedData[b].map(item => item.xpPerHour));
                return sortOrder === 'asc' ? maxA - maxB : maxB - maxA;
            } else {
                // Use original order
                return resourceOrder[a] - resourceOrder[b];
            }
        });

        const cardsHtml = sortedResources.map(resource => {
            const levels = groupedData[resource];
            const bestLevel = levels.reduce((best, current) =>
                current.xpPerHour > best.xpPerHour ? current : best
            );
            const levelReq = levels[0].levelRequired;

            return `
                <div class="resource-card">
                    <div class="resource-header">
                        <div class="resource-name">
                            <img src="${getResourceImage(resource)}"
                                 class="resource-icon"
                                 onerror="this.style.display='none'">
                            ${resource.replace(/_/g, ' ')}
                        </div>
                        <div style="color: #ff6b6b; font-size: 0.9em;">Requires Lv ${levelReq}</div>
                    </div>
                    <div class="resource-levels">
                        ${levels.map(level => `
                            <div class="level-entry ${level === bestLevel ? 'best-level' : ''}">
                                <div class="level-badge">Lv ${level.level}</div>
                                <div class="level-stats">
                                    <div class="stat-item">
                                        <span class="stat-item-value">${level.ticks}</span>
                                        <span class="stat-item-label">Ticks</span>
                                    </div>
                                    <div class="stat-item">
                                        <span class="stat-item-value">${level.xp}</span>
                                        <span class="stat-item-label">XP</span>
                                    </div>
                                </div>
                                <div class="xp-hour-badge">${Math.round(level.xpPerHour).toLocaleString()} xp/h</div>
                            </div>
                        `).join('')}
                    </div>
                </div>
            `;
        }).join('');

        container.innerHTML = `<div class="resource-cards">${cardsHtml}</div>`;
    }

    function renderListView(data) {
        const container = document.getElementById('resourcesContainer');

        if (data.length === 0) {
            container.innerHTML = `
                <div class="empty-state">
                    <h3>No resources found</h3>
                    <p>Try adjusting your filters or selecting a different tool</p>
                    <p style="color: #ff6b6b; margin-top: 10px;">
                        Note: Resources and tools you can't use at your level are hidden
                    </p>
                </div>
            `;
            return;
        }

        // Sort data
        const sortedData = [...data].sort((a, b) => {
            if (sortBy === 'xpPerHour') {
                return sortOrder === 'asc' ? a.xpPerHour - b.xpPerHour : b.xpPerHour - a.xpPerHour;
            } else {
                // Sort by resource using original order
                const orderDiff = resourceOrder[a.resource] - resourceOrder[b.resource];
                if (orderDiff !== 0) return orderDiff;
                // If same resource, sort by level
                return a.level - b.level;
            }
        });

        const listHtml = `
            <div class="resource-list">
                <div class="list-header">
                    <div class="sortable" data-sort="resource">Resource <span class="sort-indicator ${sortBy === 'resource' ? `sort-${sortOrder}` : ''}"></span></div>
                    <div>Req. Level</div>
                    <div>Your Level</div>
                    <div>Ticks</div>
                    <div>XP</div>
                    <div class="sortable" data-sort="xpPerHour">XP/Hour <span class="sort-indicator ${sortBy === 'xpPerHour' ? `sort-${sortOrder}` : ''}"></span></div>
                </div>
                ${sortedData.map(item => `
                    <div class="list-item">
                        <div class="list-resource">
                            <img src="${getResourceImage(item.resource)}"
                                 class="resource-icon"
                                 onerror="this.style.display='none'">
                            ${item.resource.replace(/_/g, ' ')}
                        </div>
                        <div class="list-level" style="color: #ff6b6b;">${item.levelRequired}</div>
                        <div class="list-level">${item.level}</div>
                        <div class="list-ticks">${item.ticks}</div>
                        <div class="list-xp">${item.xp}</div>
                        <div class="xp-hour-badge">${Math.round(item.xpPerHour).toLocaleString()} /h</div>
                    </div>
                `).join('')}
            </div>
        `;

        container.innerHTML = listHtml;

        // Add click handlers for sorting
        container.querySelectorAll('.sortable').forEach(header => {
            header.style.cursor = 'pointer';
            header.addEventListener('click', () => {
                const newSortBy = header.dataset.sort;
                if (sortBy === newSortBy) {
                    sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
                } else {
                    sortBy = newSortBy;
                    sortOrder = sortBy === 'xpPerHour' ? 'desc' : 'asc'; // Default desc for XP/hour
                }
                document.getElementById('sortBy').value = sortBy;
                applyFilters();
            });
        });
    }

    function applyFilters() {
        let filteredData = tableData;

        // Filter by tool
        if (currentTool !== 'all') {
            filteredData = filteredData.filter(row => row.tool === currentTool);
        }

        // Filter by level - check both resource and tool requirements
        filteredData = filteredData.filter(row => {
            // Check if player level is within the bracket
            const inBracket = row.level >= minLevelFilter && row.level <= maxLevelFilter;
            // Check if player can actually harvest this resource
            const canHarvestResource = row.level >= row.levelRequired;
            // Check if player can use this tool
            const canUseTool = row.level >= row.toolLevelRequired;
            return inBracket && canHarvestResource && canUseTool;
        });

        // Update stats
        updateStats(filteredData);

        // Render view
        if (currentView === 'cards') {
            renderCardView(filteredData);
        } else {
            renderListView(filteredData);
        }
    }

    function filterByTool(tool) {
        // Update active states
        document.querySelectorAll('.tool-option').forEach(opt => {
            opt.classList.toggle('active', opt.dataset.tool === tool);
        });

        // If clicking the same tool, toggle to show all
        if (currentTool === tool) {
            currentTool = 'all';
            document.querySelectorAll('.tool-option').forEach(opt => {
                opt.classList.remove('active');
            });
        } else {
            currentTool = tool;
        }

        applyFilters();
    }

    // Event listeners
    document.getElementById('fetchLevelBtn').addEventListener('click', async () => {
        const username = document.getElementById('usernameInput').value.trim();
        if (username) {
            //console.log(`Fetching level for username: "${username}"`);
            GM_setValue('flatmmo_username', username);
            currentUsername = username;
            await fetchAndSetUserLevel(username);
        } else {
            alert('Please enter a username');
        }
    });

    document.getElementById('clearUserBtn').addEventListener('click', () => {
        GM_setValue('flatmmo_username', '');
        currentUsername = '';
        currentUserLevel = null;
        document.getElementById('usernameInput').value = '';
        document.getElementById('userLevelDisplay').classList.add('hidden');

        // Reset to all levels
        selectLevelBracket({min: 1, max: 100, label: 'All Levels'});
    });

    document.getElementById('usernameInput').addEventListener('keypress', async (e) => {
        if (e.key === 'Enter') {
            const username = e.target.value.trim();
            if (username) {
                GM_setValue('flatmmo_username', username);
                currentUsername = username;
                await fetchAndSetUserLevel(username);
            }
        }
    });

    document.getElementById('sortBy').addEventListener('change', (e) => {
        sortBy = e.target.value;
        sortOrder = sortBy === 'xpPerHour' ? 'desc' : 'asc';
        applyFilters();
    });

    // View toggle
    document.querySelectorAll('.view-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
            currentView = btn.dataset.view;
            applyFilters();
        });
    });

    // Initialize and load saved username
    applyFilters();

    // Load saved username after all functions are defined
    if (currentUsername) {
        document.getElementById('usernameInput').value = currentUsername;
        fetchAndSetUserLevel(currentUsername);
    }

})();