FMP Position Analyzer and Comparator

Multi-language FMP position analyzer and player comparison tool with custom charts

当前为 2025-11-26 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         FMP Position Analyzer and Comparator
// @namespace    http://tampermonkey.net/
// @version      4.2
// @description  Multi-language FMP position analyzer and player comparison tool with custom charts
// @author       FMP Assistant
// @match        https://footballmanagerproject.com/Team/Player*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=footballmanagerproject.com
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @license      MIT
// ==/UserScript==

/*
 * FMP Position Analyzer and Comparator v4.2
 * Multi-language FMP position analyzer with custom chart visualization
 * Automatically detects game language (EN/TR/DE/ES/FR/IT)
 * Provides professional player analysis and comparison tools with custom SVG charts
 * 
 * Features:
 * - Auto-language detection
 * - Position-based skill highlighting
 * - Player saving system (up to 5 players)
 * - Advanced comparison modal with custom charts
 * - Radar charts for skill visualization using pure SVG
 * - Draggable popup windows
 * 
 * Supported positions: GK, DC, DL/DR, DMC, MC, ML/MR, AMC, AML/AMR, FC/ST
 * 
 * Author: FMP Assistant
 * License: MIT
 */

(function() {
    'use strict';

    // ===== MULTI-LANGUAGE SUPPORT =====
    const translations = {
        en: {
            savePlayer: '💾 Save Player',
            compareWindow: '📊 Comparison Window',
            modalTitle: '👥 Player Comparison',
            clearList: 'Clear List',
            savedCount: 'Saved players',
            noPlayers: 'No players saved. Go to player profile and click "Save".',
            confirmClear: 'Delete all saved players?',
            playerSaved: ' saved successfully!',
            maxPlayers: 'Maximum 5 players. Please clear list first.',
            unknownPlayer: 'Unknown Player',
            unknownPosition: 'Unknown',
            age: 'Age',
            salary: 'Salary',
            rating: 'Rating',
            quality: 'Quality',
            points: 'POINTS',
            difference: 'DIFF',
            feature: 'FEATURE',
            tableView: 'Table View',
            chartView: 'Chart View',
            skillsChart: 'Skills Radar Chart',
            physical: 'Physical',
            technical: 'Technical',
            mental: 'Mental',
            attacking: 'Attacking',
            defending: 'Defending',
            overallRating: 'Overall Rating'
        },
        tr: {
            savePlayer: '💾 Oyuncuyu Kaydet',
            compareWindow: '📊 Karşılaştırma Penceresi',
            modalTitle: '👥 Oyuncu Karşılaştırma',
            clearList: 'Listeyi Temizle',
            savedCount: 'Kayıtlı oyuncular',
            noPlayers: 'Henüz oyuncu kaydedilmedi. Oyuncu profiline gidip "Kaydet" butonuna basın.',
            confirmClear: 'Tüm kayıtlı oyuncular silinsin mi?',
            playerSaved: ' başarıyla kaydedildi!',
            maxPlayers: 'Maksimum 5 oyuncu. Lütfen önce listeyi temizleyin.',
            unknownPlayer: 'Bilinmeyen Oyuncu',
            unknownPosition: 'Bilinmiyor',
            age: 'Yaş',
            salary: 'Maaş',
            rating: 'Derece',
            quality: 'Kalite',
            points: 'PUAN',
            difference: 'FARK',
            feature: 'ÖZELLİK',
            tableView: 'Tablo Görünümü',
            chartView: 'Grafik Görünümü',
            skillsChart: 'Yetenek Radar Grafiği',
            physical: 'Fiziksel',
            technical: 'Teknik',
            mental: 'Mental',
            attacking: 'Hücum',
            defending: 'Savunma',
            overallRating: 'Genel Değerlendirme'
        }
    };

    // Auto-detect game language
    function detectGameLanguage() {
        const htmlLang = document.documentElement.lang;
        if (htmlLang && translations[htmlLang]) {
            return htmlLang;
        }
        
        const bodyText = document.body.innerText;
        if (bodyText.includes('Yaş') || bodyText.includes('Maaş')) return 'tr';
        return 'en';
    }

    const currentLang = detectGameLanguage();
    const t = translations[currentLang] || translations.en;

    // ===== POSITION DEFINITIONS =====
    const positionSkills = {
        'KL': { primary: ['Poz', '1e1', 'ElK'], secondary: ['Ref', 'HH', 'Sçr', 'Zıp'] },
        'GK': { primary: ['Poz', '1e1', 'ElK'], secondary: ['Ref', 'HH', 'Sçr', 'Zıp'] },
        'DC': { primary: ['Mrkj', 'TpK', 'Poz'], secondary: ['Kaf', 'Day', 'Hız'] },
        'DL': { primary: ['TpK', 'Ort', 'Poz'], secondary: ['Pas', 'Tek', 'Hız'] },
        'DR': { primary: ['TpK', 'Ort', 'Poz'], secondary: ['Pas', 'Tek', 'Hız'] },
        'DMC': { primary: ['Mrkj', 'TpK', 'Poz'], secondary: ['Kaf', 'Pas', 'Day'] },
        'MC': { primary: ['Pas', 'Tek', 'Poz'], secondary: ['TpK', 'Kaf', 'Day'] },
        'ML': { primary: ['Ort', 'Pas', 'Poz'], secondary: ['Tek', 'Kaf', 'Hız'] },
        'MR': { primary: ['Ort', 'Pas', 'Poz'], secondary: ['Tek', 'Kaf', 'Hız'] },
        'AMC': { primary: ['Pas', 'Bit', 'Tek'], secondary: ['Ort', 'Uza', 'Poz'] },
        'AML': { primary: ['Ort', 'Pas', 'Poz'], secondary: ['Tek', 'Bit', 'Hız'] },
        'AMR': { primary: ['Ort', 'Pas', 'Poz'], secondary: ['Tek', 'Bit', 'Hız'] },
        'FC': { primary: ['Bit', 'Kaf'], secondary: ['Uza', 'Poz', 'Hız'] },
        'ST': { primary: ['Bit', 'Kaf'], secondary: ['Uza', 'Poz', 'Hız'] }
    };

    // ===== SKILL CATEGORIES FOR CHARTS =====
    const skillCategories = {
        technical: ['Pas', 'Tek', 'Ort', 'Şut', 'Bit', 'Kaf', 'Uza'],
        physical: ['Hız', 'Çab', 'Day', 'Kuv', 'Zıp'],
        mental: ['Mrkj', 'Poz', 'TpK', 'Ces', 'HH', 'Tak', 'Kar'],
        goalkeeping: ['Poz', '1e1', 'ElK', 'Ref', 'HH', 'Sçr', 'Zıp']
    };

    // ===== CUSTOM CHART SYSTEM =====
    
    function createCustomRadarChart(players, containerId) {
        const playerIds = Object.keys(players);
        if (playerIds.length === 0) return '';
        
        const keySkills = ['Pas', 'Tek', 'Bit', 'Kaf', 'Hız', 'Day', 'Mrkj', 'TpK', 'Ort', 'Ref'];
        const centerX = 150, centerY = 150, radius = 120;
        const angleStep = (2 * Math.PI) / keySkills.length;
        
        let svgHTML = `<svg width="300" height="300" viewBox="0 0 300 300" class="fmp-radar-svg">`;
        
        // Draw grid circles
        for (let i = 1; i <= 5; i++) {
            const circleRadius = radius * (i / 5);
            svgHTML += `<circle cx="${centerX}" cy="${centerY}" r="${circleRadius}" fill="none" stroke="#e0e0e0" stroke-width="1"/>`;
        }
        
        // Draw axis lines and labels
        keySkills.forEach((skill, index) => {
            const angle = index * angleStep - Math.PI / 2;
            const x = centerX + radius * Math.cos(angle);
            const y = centerY + radius * Math.sin(angle);
            
            // Axis line
            svgHTML += `<line x1="${centerX}" y1="${centerY}" x2="${x}" y2="${y}" stroke="#e0e0e0" stroke-width="1"/>`;
            
            // Skill label
            const labelX = centerX + (radius + 15) * Math.cos(angle);
            const labelY = centerY + (radius + 15) * Math.sin(angle);
            const textAnchor = Math.cos(angle) > 0.1 ? 'start' : Math.cos(angle) < -0.1 ? 'end' : 'middle';
            
            svgHTML += `<text x="${labelX}" y="${labelY}" text-anchor="${textAnchor}" dominant-baseline="middle" font-size="10" fill="#333">${skill}</text>`;
        });
        
        // Draw player data
        const colors = ['#ff4444', '#4444ff', '#44ff44', '#ffff44', '#ff44ff'];
        
        playerIds.forEach((id, playerIndex) => {
            const player = players[id];
            const color = colors[playerIndex] || '#888888';
            let points = [];
            
            keySkills.forEach((skill, skillIndex) => {
                const angle = skillIndex * angleStep - Math.PI / 2;
                const value = player.skills[skill] || 0;
                const scaledRadius = radius * (value / 100);
                const x = centerX + scaledRadius * Math.cos(angle);
                const y = centerY + scaledRadius * Math.sin(angle);
                points.push(`${x},${y}`);
            });
            
            // Draw polygon
            svgHTML += `<polygon points="${points.join(' ')}" fill="${color}30" stroke="${color}" stroke-width="2"/>`;
            
            // Draw data points
            points.forEach(point => {
                const [x, y] = point.split(',').map(Number);
                svgHTML += `<circle cx="${x}" cy="${y}" r="3" fill="${color}"/>`;
            });
        });
        
        svgHTML += `</svg>`;
        
        // Legend
        let legendHTML = '<div class="fmp-radar-legend">';
        playerIds.forEach((id, index) => {
            const player = players[id];
            const color = colors[index] || '#888888';
            legendHTML += `<div class="fmp-legend-item">
                <span class="fmp-legend-color" style="background-color: ${color}"></span>
                <span class="fmp-legend-name">${player.name}</span>
            </div>`;
        });
        legendHTML += '</div>';
        
        return `<div class="fmp-radar-container">${svgHTML}${legendHTML}</div>`;
    }
    
    function createCustomBarChart(players, containerId) {
        const playerIds = Object.keys(players);
        if (playerIds.length === 0) return '';
        
        const categories = [t.technical, t.physical, t.mental];
        const categorySkills = {
            [t.technical]: skillCategories.technical,
            [t.physical]: skillCategories.physical,
            [t.mental]: skillCategories.mental
        };
        
        const colors = ['#ff4444', '#4444ff', '#44ff44', '#ffff44', '#ff44ff'];
        let html = '<div class="fmp-bar-chart">';
        
        categories.forEach(category => {
            html += `<div class="fmp-bar-category">
                <div class="fmp-bar-label">${category}</div>
                <div class="fmp-bars-container">`;
            
            playerIds.forEach((id, index) => {
                const player = players[id];
                const skills = categorySkills[category];
                const total = skills.reduce((sum, skill) => sum + (player.skills[skill] || 0), 0);
                const average = skills.length > 0 ? total / skills.length : 0;
                const color = colors[index] || '#888888';
                
                html += `<div class="fmp-bar-wrapper">
                    <div class="fmp-bar" style="width: ${average}%; background-color: ${color};">
                        <span class="fmp-bar-value">${Math.round(average)}</span>
                    </div>
                    <span class="fmp-bar-player">${player.name}</span>
                </div>`;
            });
            
            html += `</div></div>`;
        });
        
        html += '</div>';
        return html;
    }

    // ===== STYLES =====
    GM_addStyle(`
        .fmp-primary-skill { background-color: #ffd700 !important; color: #000 !important; font-weight: bold !important; border-radius: 4px; }
        .fmp-secondary-skill { background-color: #b0e0e6 !important; color: #000 !important; font-weight: bold !important; border-radius: 4px; }
        #playerdata .skilltable th, #playerdata .skilltable td { padding: 2px 4px !important; }
        
        /* Modal Styles */
        .fmp-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
            visibility: hidden;
            opacity: 0;
            transition: opacity 0.3s, visibility 0.3s;
        }
        .fmp-modal-overlay.active {
            visibility: visible;
            opacity: 1;
        }
        .fmp-modal {
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
            width: 95%;
            max-width: 1400px;
            max-height: 90vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            transform: translateY(-20px);
            transition: transform 0.3s;
        }
        .fmp-modal.active {
            transform: translateY(0);
        }
        .fmp-modal-header {
            background: linear-gradient(to right, #007bff, #0056b3);
            color: white;
            padding: 15px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            user-select: none;
        }
        .fmp-modal-title {
            font-size: 18px;
            font-weight: bold;
            margin: 0;
        }
        .fmp-modal-close {
            background: none;
            border: none;
            color: white;
            font-size: 24px;
            cursor: pointer;
            line-height: 1;
            padding: 0;
            width: 30px;
            height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            transition: background-color 0.2s;
        }
        .fmp-modal-close:hover {
            background-color: rgba(255, 255, 255, 0.2);
        }
        .fmp-modal-content {
            padding: 20px;
            overflow-y: auto;
            flex-grow: 1;
        }
        
        /* Tabs */
        .fmp-tabs {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #ddd;
        }
        .fmp-tab {
            padding: 10px 20px;
            cursor: pointer;
            border: 1px solid transparent;
            border-bottom: none;
            border-radius: 5px 5px 0 0;
            margin-right: 5px;
            background: #f8f9fa;
            transition: all 0.3s;
        }
        .fmp-tab.active {
            background: #007bff;
            color: white;
            border-color: #007bff;
        }
        .fmp-tab-content {
            display: none;
        }
        .fmp-tab-content.active {
            display: block;
        }
        
        /* Comparison Table */
        #fmp-compare-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 10px;
            font-size: 0.95em;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        #fmp-compare-table th, #fmp-compare-table td {
            border: 1px solid #b0c4de;
            padding: 8px;
            text-align: center;
        }
        #fmp-compare-table th {
            background-color: #d8e6f7;
            font-weight: bold;
            text-transform: uppercase;
        }
        .fmp-diff-positive { color: green; font-weight: bold; background-color: #e6ffe6; }
        .fmp-diff-negative { color: red; font-weight: bold; background-color: #ffe6e6; }
        .fmp-clear-btn { 
            background-color: #dc3545; 
            color: white; 
            border: none; 
            padding: 8px 15px; 
            border-radius: 4px; 
            cursor: pointer; 
            margin-left: 10px; 
            font-size: 0.9em;
            transition: background-color 0.2s;
        }
        .fmp-clear-btn:hover {
            background-color: #c82333;
        }
        
        /* Custom Radar Chart */
        .fmp-radar-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin: 20px 0;
        }
        .fmp-radar-svg {
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            background: #fafafa;
        }
        .fmp-radar-legend {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 15px;
            margin-top: 15px;
        }
        .fmp-legend-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .fmp-legend-color {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            display: inline-block;
        }
        .fmp-legend-name {
            font-size: 12px;
            font-weight: 500;
        }
        
        /* Custom Bar Chart */
        .fmp-bar-chart {
            margin: 20px 0;
        }
        .fmp-bar-category {
            margin-bottom: 20px;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 5px;
        }
        .fmp-bar-label {
            font-weight: bold;
            margin-bottom: 10px;
            color: #333;
        }
        .fmp-bars-container {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .fmp-bar-wrapper {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .fmp-bar {
            height: 25px;
            min-width: 30px;
            border-radius: 3px;
            position: relative;
            transition: width 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: flex-end;
            padding: 0 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        }
        .fmp-bar-value {
            color: white;
            font-weight: bold;
            font-size: 12px;
            text-shadow: 1px 1px 1px rgba(0,0,0,0.3);
        }
        .fmp-bar-player {
            min-width: 120px;
            font-size: 12px;
            font-weight: 500;
        }
        
        /* Chart Container */
        .fmp-chart-container {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 20px;
        }
        .fmp-chart-wrapper {
            flex: 1;
            min-width: 300px;
            background: white;
            border-radius: 8px;
            padding: 15px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            border: 1px solid #e0e0e0;
        }
        .fmp-chart-title {
            font-size: 16px;
            font-weight: bold;
            margin-bottom: 15px;
            text-align: center;
            color: #333;
        }
        
        /* Player Cards */
        .fmp-player-cards {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 20px;
        }
        .fmp-player-card {
            flex: 1;
            min-width: 200px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.1);
        }
        .fmp-player-card h4 {
            margin: 0 0 10px 0;
            font-size: 16px;
        }
        .fmp-player-card p {
            margin: 5px 0;
            font-size: 14px;
        }
        
        /* Buttons */
        #fmp-save-player-btn, #fmp-open-modal-btn {
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
            font-size: 14px;
            margin-left: 10px;
            white-space: nowrap;
            transition: background-color 0.2s;
        }
        #fmp-save-player-btn {
            background-color: #007bff;
        }
        #fmp-save-player-btn:hover {
            background-color: #0056b3;
        }
        #fmp-open-modal-btn {
            background-color: #28a745;
        }
        #fmp-open-modal-btn:hover {
            background-color: #218838;
        }
    `);

    // ===== CORE FUNCTIONS =====
    
    function getPlayerMainPosition() {
        const posElement = document.querySelector('.pitch-position');
        if (posElement) return posElement.textContent.trim();

        const positionElementB = document.querySelector('#playerdata .playerpos b');
        if (positionElementB) {
            const text = positionElementB.textContent.trim();
            return text.split(' ')[0].replace(/[^a-zA-Z]/g, '').toUpperCase();
        }
        return t.unknownPosition;
    }

    function highlightSkills() {
        const mainPosition = getPlayerMainPosition();
        if (!positionSkills[mainPosition]) return;

        const config = positionSkills[mainPosition];
        const skillTable = document.querySelector('#playerdata .skilltable');
        if (!skillTable) return;

        const headers = skillTable.querySelectorAll('th');
        headers.forEach((th, index) => {
            const skillName = th.textContent.trim();
            const isPrimary = config.primary.includes(skillName);
            const isSecondary = config.secondary.includes(skillName);

            if (isPrimary || isSecondary) {
                if (isPrimary) th.classList.add('fmp-primary-skill');
                if (isSecondary) th.classList.add('fmp-secondary-skill');

                const parentRow = th.parentElement;
                const valueRow = parentRow.nextElementSibling;
                if (valueRow && valueRow.children[index]) {
                    const valueCell = valueRow.children[index];
                    const numSpan = valueCell.querySelector('.num');
                    const styleClass = isPrimary ? 'fmp-primary-skill' : 'fmp-secondary-skill';
                    if (numSpan) numSpan.classList.add(styleClass);
                    else valueCell.classList.add(styleClass);
                }
            }
        });
    }

    function extractPlayerName() {
        let nameElement = $('.lheader h3');
        if (nameElement.length === 0) nameElement = $('h1');
        if (nameElement.length === 0) nameElement = $('.lheader').find(':header').first();

        if (nameElement.length > 0) {
            let rawText = nameElement.contents().filter(function() {
                return this.nodeType === 3;
            }).text().trim();

            if (!rawText) rawText = nameElement.text().trim();
            let cleanName = rawText.replace(/^\d+\.\s*/, '').trim();
            if (cleanName) return cleanName;
        }
        return t.unknownPlayer;
    }

    async function extractPlayerData() {
        const player = {};
        const urlParams = new URLSearchParams(window.location.search);
        player.id = urlParams.get('id');
        player.name = extractPlayerName();
        player.position = getPlayerMainPosition();

        if (!player.id) return null;

        // Extract skills from table
        player.skills = {};
        const skillTable = $('#playerdata .skilltable');
        const headers = skillTable.find('th');
        const values = skillTable.find('td');

        headers.each((index, th) => {
            const skillName = $(th).text().trim();
            const skillValueText = $(values[index]).text().trim();
            const skillValue = parseInt(skillValueText.match(/(\d+)/)?.[0], 10) || 0;
            if(skillName) {
                player.skills[skillName] = skillValue;
            }
        });

        // Extract additional data from JSON
        try {
            const response = await fetch(`/Team/Player?handler=PlayerData&playerId=${player.id}`);
            if (response.ok) {
                const json = await response.json();
                if (json && json.player) {
                    if (json.player.age) player.age = `${json.player.age.years} ${t.age} ${json.player.age.months}M`;
                    if (json.player.wage) player.salary = json.player.wage.toLocaleString();
                    if (json.player.rating) {
                        player.rating = json.player.rating;
                        player.lastRating = json.player.rating;
                    }
                    if (json.player.qi) player.qi = json.player.qi;
                }
            }
        } catch (e) {
            console.error("JSON data fetch failed:", e);
        }

        // Fallback data extraction
        const infoText = $('.infotable').text();
        if (!player.age) {
            const ageMatch = infoText.match(/(Yaş|Age|Alter|Edad|Âge|Età)\s*(\d+)[.,](\d+)/);
            player.age = ageMatch ? `${ageMatch[2]} ${t.age} ${ageMatch[3]}M` : t.unknownPosition;
        }
        if (!player.salary || player.salary === t.unknownPosition) {
            const wageMatch = infoText.match(/(Maaş|Wage|Gehalt|Salario|Salaire|Stipendio)\s*ⓕ\s*([\d,.]+)/);
            player.salary = wageMatch ? wageMatch[2] : t.unknownPosition;
        }

        player.value = t.unknownPosition;
        player.timestamp = new Date().toLocaleString();
        player.lang = currentLang;
        return player;
    }

    async function savePlayer() {
        const player = await extractPlayerData();
        if (!player) {
            alert('Player data could not be fetched. Please refresh the page.');
            return;
        }

        let savedPlayers = await GM_getValue('fmp_saved_players', {});
        if (Object.keys(savedPlayers).length >= 5 && !savedPlayers[player.id]) {
            alert(t.maxPlayers);
            return;
        }

        savedPlayers[player.id] = player;
        await GM_setValue('fmp_saved_players', savedPlayers);
        alert(player.name + t.playerSaved);
        await updateCompareModal();
    }

    // ===== MODAL FUNCTIONS =====
    
    function createModal() {
        if ($('#fmp-modal-overlay').length) return;

        const modalHTML = `
            <div class="fmp-modal-overlay" id="fmp-modal-overlay">
                <div class="fmp-modal" id="fmp-modal">
                    <div class="fmp-modal-header" id="fmp-modal-header">
                        <h3 class="fmp-modal-title">${t.modalTitle}</h3>
                        <button class="fmp-modal-close" id="fmp-modal-close">×</button>
                    </div>
                    <div class="fmp-modal-content" id="fmp-modal-content">
                        <div class="fmp-tabs">
                            <div class="fmp-tab active" data-tab="table">${t.tableView}</div>
                            <div class="fmp-tab" data-tab="chart">${t.chartView}</div>
                        </div>
                        <div id="fmp-tab-table" class="fmp-tab-content active">
                            <p>${t.noPlayers}</p>
                        </div>
                        <div id="fmp-tab-chart" class="fmp-tab-content">
                            <p>${t.noPlayers}</p>
                        </div>
                    </div>
                </div>
            </div>
        `;

        $('body').append(modalHTML);

        // Close modal functionality
        $('#fmp-modal-close, #fmp-modal-overlay').on('click', function(e) {
            if (e.target === this) {
                $('#fmp-modal-overlay').removeClass('active');
                $('#fmp-modal').removeClass('active');
            }
        });

        // Tab functionality
        $('.fmp-tab').on('click', function() {
            const tabId = $(this).data('tab');
            $('.fmp-tab').removeClass('active');
            $('.fmp-tab-content').removeClass('active');
            $(this).addClass('active');
            $(`#fmp-tab-${tabId}`).addClass('active');
        });

        makeModalDraggable();
    }

    function makeModalDraggable() {
        const modal = document.getElementById('fmp-modal');
        const header = document.getElementById('fmp-modal-header');
        
        let isDragging = false;
        let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0;
        
        header.addEventListener("mousedown", dragStart);
        document.addEventListener("mousemove", drag);
        document.addEventListener("mouseup", dragEnd);
        
        function dragStart(e) {
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;
            if (e.target === header || header.contains(e.target)) {
                isDragging = true;
            }
        }
        
        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                xOffset = currentX;
                yOffset = currentY;
                setTranslate(currentX, currentY, modal);
            }
        }
        
        function setTranslate(xPos, yPos, el) {
            el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
        }
        
        function dragEnd(e) {
            initialX = currentX;
            initialY = currentY;
            isDragging = false;
        }
    }

    async function updateCompareModal() {
        const players = await GM_getValue('fmp_saved_players', {});
        const playerIds = Object.keys(players);

        let tableHTML = '';
        let chartHTML = '';

        if (playerIds.length === 0) {
            tableHTML = `<p>${t.noPlayers}</p>`;
            chartHTML = `<p>${t.noPlayers}</p>`;
        } else {
            tableHTML = `
                <div style="margin-bottom: 15px;">
                    <button class="fmp-clear-btn" id="fmp-clear-btn">${t.clearList}</button>
                    <span style="margin-left: 10px; font-size: 0.9em; color: #666;">
                        ${t.savedCount}: ${playerIds.length}/5
                    </span>
                </div>
                ${createCompareTable(players)}
            `;
            
            chartHTML = `
                <div style="margin-bottom: 15px;">
                    <button class="fmp-clear-btn" id="fmp-clear-btn-chart">${t.clearList}</button>
                    <span style="margin-left: 10px; font-size: 0.9em; color: #666;">
                        ${t.savedCount}: ${playerIds.length}/5
                    </span>
                </div>
                ${createPlayerCards(players)}
                <div class="fmp-chart-container">
                    <div class="fmp-chart-wrapper">
                        <div class="fmp-chart-title">${t.skillsChart}</div>
                        ${createCustomRadarChart(players, 'fmp-radar-chart')}
                    </div>
                    <div class="fmp-chart-wrapper">
                        <div class="fmp-chart-title">Skill Categories</div>
                        ${createCustomBarChart(players, 'fmp-bar-chart')}
                    </div>
                </div>
            `;
        }

        $('#fmp-tab-table').html(tableHTML);
        $('#fmp-tab-chart').html(chartHTML);

        $('#fmp-clear-btn, #fmp-clear-btn-chart').on('click', async () => {
            if (confirm(t.confirmClear)) {
                await GM_setValue('fmp_saved_players', {});
                await updateCompareModal();
            }
        });
    }

    function createPlayerCards(players) {
        const playerIds = Object.keys(players);
        if (playerIds.length === 0) return '';

        let html = '<div class="fmp-player-cards">';
        playerIds.forEach((id, index) => {
            const player = players[id];
            const colors = ['#667eea', '#764ba2', '#f093fb', '#4facfe', '#43e97b'];
            
            html += `
                <div class="fmp-player-card" style="background: linear-gradient(135deg, ${colors[index] || '#667eea'} 0%, ${darkenColor(colors[index] || '#667eea', 20)} 100%);">
                    <h4>${player.name}</h4>
                    <p><strong>${t.quality}:</strong> ${player.rating || 'N/A'}</p>
                    <p><strong>${t.position}:</strong> ${player.position}</p>
                    <p><strong>${t.age}:</strong> ${player.age || 'N/A'}</p>
                </div>
            `;
        });
        html += '</div>';
        return html;
    }

    function darkenColor(color, percent) {
        const num = parseInt(color.replace("#", ""), 16);
        const amt = Math.round(2.55 * percent);
        const R = (num >> 16) - amt;
        const G = (num >> 8 & 0x00FF) - amt;
        const B = (num & 0x0000FF) - amt;
        return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
            (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
            (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
    }

    function openModal() {
        $('#fmp-modal-overlay').addClass('active');
        $('#fmp-modal').addClass('active');
    }

    function createOpenModalButton() {
        if ($('#fmp-open-modal-btn').length) return;
        const $headerCell = $('.lheader h3').parent();
        if ($headerCell.length) {
            const $btn = $(`<button id="fmp-open-modal-btn">${t.compareWindow}</button>`);
            $btn.on('click', openModal);
            $('#fmp-save-player-btn').after($btn);
        }
    }

    function createCompareTable(players) {
        const playerIds = Object.keys(players);
        const allSkills = new Set();
        playerIds.forEach(id => {
            if(players[id].skills) {
                Object.keys(players[id].skills).forEach(skill => allSkills.add(skill));
            }
        });
        const sortedSkills = Array.from(allSkills).sort();

        let html = '<table id="fmp-compare-table"><thead><tr>';
        html += `<th style="width:10%">${t.feature}</th>`;

        playerIds.forEach(id => {
            const displayName = (players[id].name && players[id].name !== t.unknownPlayer) ? players[id].name : `Player ${players[id].id}`;
            html += `<th colspan="2" style="background-color:#2c5a8a; color:white;">${displayName.toUpperCase()} <br><small>(${players[id].position})</small></th>`;
        });
        html += '</tr><tr><th>&nbsp;</th>';
        playerIds.forEach(() => { html += `<th>${t.points}</th><th>${t.difference}</th>`; });
        html += '</tr></thead><tbody>';

        const infoKeys = [
            { label: t.age, key: 'age' },
            { label: t.salary, key: 'salary' },
            { label: `${t.quality} (${t.rating})`, key: 'rating' },
            { label: 'QI', key: 'qi' }
        ];

        infoKeys.forEach(info => {
            html += `<tr class="fmp-key-info-row"><td><b>${info.label}</b></td>`;
            playerIds.forEach(id => {
                let val = players[id][info.key] || '-';
                html += `<td colspan="2" style="font-weight:bold;">${val}</td>`;
            });
            html += '</tr>';
        });

        sortedSkills.forEach(skill => {
            html += `<tr><td style="text-align:left;font-weight:bold;">${skill}</td>`;
            const refVal = players[playerIds[0]].skills[skill] || 0;

            playerIds.forEach((id, idx) => {
                const val = players[id].skills[skill] || 0;
                let diffHtml = '';

                if (idx > 0) {
                    const diff = val - refVal;
                    if (diff > 0) diffHtml = `<span class="fmp-diff-positive">+${diff}</span>`;
                    else if (diff < 0) diffHtml = `<span class="fmp-diff-negative">${diff}</span>`;
                    else diffHtml = '<span style="color:gray">-</span>';
                }

                html += `<td>${val}</td><td>${diffHtml}</td>`;
            });
            html += '</tr>';
        });

        html += '</tbody></table>';
        return html;
    }

    function createSaveButton() {
        if ($('#fmp-save-player-btn').length) return;
        const $headerCell = $('.lheader h3').parent();
        if ($headerCell.length) {
            const $btn = $(`<button id="fmp-save-player-btn">${t.savePlayer}</button>`);
            $btn.on('click', savePlayer);
            $('.lheader h3').after($btn);
        }
    }

    // ===== INITIALIZATION =====
    setTimeout(async () => {
        createSaveButton();
        createModal();
        createOpenModalButton();
        highlightSkills();
        await updateCompareModal();
    }, 1000);

})();