FMP Position Analyzer and Comparator

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

目前為 2025-11-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);

})();