您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
FMP oyuncu sayfasını tam analiz eder: Mevki puanı, gizli yeteneklere göre revize edilmiş potansiyel tahmini ve antrenman tavsiyesi sunar. Artık yıldız derecelendirmesi ve hata bildirim özelliği içerir.
// ==UserScript== // @name FMP Yetenek Analizcisi // @namespace kalenderadam // @version 1.3 // @description FMP oyuncu sayfasını tam analiz eder: Mevki puanı, gizli yeteneklere göre revize edilmiş potansiyel tahmini ve antrenman tavsiyesi sunar. Artık yıldız derecelendirmesi ve hata bildirim özelliği içerir. // @description:tr FMP oyuncu sayfasını tam analiz eder: Mevki puanı, gizli yeteneklere göre revize edilmiş potansiyel tahmini ve antrenman tavsiyesi sunar. Artık yıldız derecelendirmesi ve hata bildirim özelliği içerir. // @author Kalenderadam (ve AI Asistanı) // @match https://*.footballmanagerproject.com/Team/Player?id=* // @icon https://www.google.com/s2/favicons?sz=64&domain=footballmanagerproject.com // @grant none // @license MIT // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // --- AYARLAR --- const YOUR_FMP_USER_ID = "16531"; const skillWeights = { DC: { "Markaj": 1.0, "Top Kapma": 1.0, "Kafa": 1.0, "Pozisyon Alma": 0.9, "Dayanıklılık": 0.8, "Uzaktan Şut": 0.6, "Teknik": 0.5, "Pas": 0.5, "Hız": 0.5, "Orta": 0.3, "Bitiricilik": 0.2 }, DL: { "Top Kapma": 1.0, "Orta": 1.0, "Dayanıklılık": 0.8, "Markaj": 0.6, "Teknik": 0.6, "Pas": 0.6, "Pozisyon Alma": 0.6, "Uzaktan Şut": 0.6, "Kafa": 0.6, "Hız": 0.5, "Bitiricilik": 0.4 }, DR: { "Top Kapma": 1.0, "Orta": 1.0, "Dayanıklılık": 0.8, "Markaj": 0.6, "Teknik": 0.6, "Pas": 0.6, "Pozisyon Alma": 0.6, "Uzaktan Şut": 0.6, "Kafa": 0.6, "Hız": 0.5, "Bitiricilik": 0.4 }, DMC: { "Top Kapma": 0.9, "Kafa": 0.9, "Pozisyon Alma": 0.8, "Dayanıklılık": 0.8, "Markaj": 0.7, "Pas": 0.7, "Uzaktan Şut": 0.7, "Teknik": 0.6, "Hız": 0.5, "Bitiricilik": 0.4, "Orta": 0.3 }, DML: { "Top Kapma": 1.0, "Orta": 1.0, "Dayanıklılık": 0.8, "Pas": 0.7, "Teknik": 0.6, "Pozisyon Alma": 0.6, "Uzaktan Şut": 0.6, "Kafa": 0.6, "Hız": 0.5, "Markaj": 0.5, "Bitiricilik": 0.4 }, DMR: { "Top Kapma": 1.0, "Orta": 1.0, "Dayanıklılık": 0.8, "Pas": 0.7, "Teknik": 0.6, "Pozisyon Alma": 0.6, "Uzaktan Şut": 0.6, "Kafa": 0.6, "Hız": 0.5, "Markaj": 0.5, "Bitiricilik": 0.4 }, MC: { "Pas": 1.0, "Teknik": 0.8, "Pozisyon Alma": 0.8, "Dayanıklılık": 0.8, "Top Kapma": 0.7, "Uzaktan Şut": 0.7, "Kafa": 0.6, "Markaj": 0.5, "Hız": 0.5, "Bitiricilik": 0.5, "Orta": 0.4 }, ML: { "Orta": 1.0, "Pas": 0.8, "Dayanıklılık": 0.8, "Pozisyon Alma": 0.7, "Uzaktan Şut": 0.7, "Teknik": 0.6, "Top Kapma": 0.6, "Kafa": 0.6, "Bitiricilik": 0.6, "Hız": 0.5, "Markaj": 0.4 }, MR: { "Orta": 1.0, "Pas": 0.8, "Dayanıklılık": 0.8, "Pozisyon Alma": 0.7, "Uzaktan Şut": 0.7, "Teknik": 0.6, "Top Kapma": 0.6, "Kafa": 0.6, "Bitiricilik": 0.6, "Hız": 0.5, "Markaj": 0.4 }, AMC: { "Pas": 1.0, "Uzaktan Şut": 1.0, "Bitiricilik": 0.9, "Dayanıklılık": 0.8, "Teknik": 0.7, "Pozisyon Alma": 0.7, "Kafa": 0.7, "Hız": 0.5, "Orta": 0.4, "Markaj": 0.3, "Top Kapma": 0.3 }, AML: { "Orta": 1.0, "Dayanıklılık": 0.8, "Pas": 0.8, "Pozisyon Alma": 0.8, "Teknik": 0.7, "Uzaktan Şut": 0.7, "Bitiricilik": 0.6, "Kafa": 0.6, "Top Kapma": 0.5, "Hız": 0.5, "Markaj": 0.3 }, AMR: { "Orta": 1.0, "Dayanıklılık": 0.8, "Pas": 0.8, "Pozisyon Alma": 0.8, "Teknik": 0.7, "Uzaktan Şut": 0.7, "Bitiricilik": 0.6, "Kafa": 0.6, "Top Kapma": 0.5, "Hız": 0.5, "Markaj": 0.3 }, FC: { "Bitiricilik": 1.0, "Kafa": 1.0, "Uzaktan Şut": 0.8, "Dayanıklılık": 0.8, "Teknik": 0.7, "Pas": 0.7, "Pozisyon Alma": 0.7, "Top Kapma": 0.5, "Hız": 0.5, "Orta": 0.4, "Markaj": 0.2 }, GK: { "Elle Kontrol": 1.0, "Refleks": 1.0, "Bire Bir": 0.8, "Pozisyon Alma": 0.7, "Hava Hakimiyeti": 0.6, "Zıplama": 0.6, "Hız": 0.6, "Sıçrama": 0.5, "Degaj": 0.5, "Elle Pas": 0.5, "Dayanıklılık": 0.5 } }; const skillAbbrToFullName = { Tec: 'Tecrübe', Day: 'Dayanıklılık', Hız: 'Hız', Mrkj: 'Markaj', TpK: 'Top Kapma', Poz: 'Pozisyon Alma', Pas: 'Pas', Ort: 'Orta', Tek: 'Teknik', Kaf: 'Kafa', Bit: 'Bitiricilik', Uza: 'Uzaktan Şut', ElK: 'Elle Kontrol', '1e1': 'Bire Bir', Ref: 'Refleks', HH: 'Hava Hakimiyeti', Sçr: 'Sıçrama', Deg: 'Degaj', Zıp: 'Zıplama', ElP: 'Elle Pas' }; function scoreToStars(score) { const fullStar = '★'; const halfStar = '½'; const emptyStar = '☆'; let stars = score / 5; let fullStars = Math.floor(stars); let halfStars = (stars - fullStars >= 0.5) ? 1 : 0; let emptyStars = 5 - fullStars - halfStars; return fullStar.repeat(fullStars) + halfStar.repeat(halfStars) + emptyStar.repeat(emptyStars); } function safeQuerySelector(selector) { return document.querySelector(selector); } // --- YENİLENMİŞ YETENEK OKUMA FONKSİYONU --- function getSkillsFromPage() { const skills = {}; const skillTable = safeQuerySelector('.skilltable'); if (!skillTable) return skills; // Sayfadaki TÜM yetenek başlıklarını (th) ve değerlerini (td > span.num) bul const allHeaders = skillTable.querySelectorAll('th'); const allValues = skillTable.querySelectorAll('td > span.num'); // Kısaltmalardan tam isimlere bir harita oluştur const abbrMap = new Map(); allHeaders.forEach(th => { const abbr = th.textContent.trim(); if (skillAbbrToFullName[abbr]) { abbrMap.set(th, skillAbbrToFullName[abbr]); } }); // Değerleri doğru başlıklarla eşleştir allValues.forEach(valueSpan => { // Bir değerin başlığını bulmak için en yakın <th> elementini ara const parentRow = valueSpan.closest('tr'); if (parentRow) { const valueIndex = Array.from(parentRow.children).indexOf(valueSpan.parentElement); const headerCell = parentRow.querySelector(`th:nth-child(${valueIndex})`) || parentRow.previousElementSibling?.querySelector(`th:nth-child(${valueIndex})`); if (headerCell && abbrMap.has(headerCell)) { const fullName = abbrMap.get(headerCell); skills[fullName] = parseInt(valueSpan.textContent, 10); } } }); // Eğer ilk yöntemle 11'den az yetenek bulunduysa (kaleci gibi), daha genel bir arama yap if(Object.keys(skills).length < 11) { allHeaders.forEach((th, index) => { const abbr = th.textContent.trim(); const fullName = skillAbbrToFullName[abbr]; if(fullName && allValues[index]){ skills[fullName] = parseInt(allValues[index].textContent, 10); } }); } return skills; } function analyzePlayer() { try { const positionEl = safeQuerySelector('.infotable .pitch-position'); const position = positionEl ? positionEl.textContent.trim().replace('KL', 'GK') : null; const ageEl = safeQuerySelector('.infotable .age_year'); if (!position || !ageEl) throw new Error("Temel oyuncu bilgileri (yaş/pozisyon) bulunamadı."); const age = parseInt(ageEl.textContent.trim(), 10); const skills = getSkillsFromPage(); if (Object.keys(skills).length === 0) throw new Error("Oyuncu yetenekleri okunamadı."); // ... (geri kalan analiz mantığı aynı) const weights = skillWeights[position]; if (!weights) throw new Error(`'${position}' mevkisi için ağırlık formülü bulunamadı.`); let weightedSum = 0, totalWeight = 0; const skillDetails = []; for (const skillName in weights) { if (skills.hasOwnProperty(skillName)) { weightedSum += skills[skillName] * weights[skillName]; totalWeight += weights[skillName]; skillDetails.push({ name: skillName, value: skills[skillName], weight: weights[skillName] }); } } if (totalWeight === 0) throw new Error("Yetenek ağırlıkları hesaplanamadı."); const positionalScore25 = (weightedSum / totalWeight / 20) * 25; let hiddenTalents = { professionalism: null, fitness: null }; let trainingAnalysis = { text: "Antrenman bilgisi bulunamadı.", color: "#ccc" }; const reportContainer = safeQuerySelector('#ScoutInfo'); if (reportContainer) { const hiddenTalentContainer = Array.from(reportContainer.querySelectorAll('.skilldev')).pop(); if (hiddenTalentContainer) { hiddenTalentContainer.querySelectorAll('div > span').forEach(span => { const match = span.textContent.match(/\((\d)\/8\)/); if (match && match[1]) { if (span.textContent.includes('Profesyonellik')) hiddenTalents.professionalism = parseInt(match[1]); else if (span.textContent.includes('Fitness')) hiddenTalents.fitness = parseInt(match[1]); } }); } } const trainingElement = safeQuerySelector('#TrainingBoard table tr:nth-child(2)'); if (trainingElement?.cells?.[0]) { const trainingText = trainingElement.cells[0].textContent; const importantSkills = skillDetails.sort((a, b) => b.weight - a.weight).slice(0, 3).map(s => s.name); let matchCount = importantSkills.filter(skill => trainingText.toLowerCase().includes(skill.toLowerCase().substring(0, 3))).length; trainingAnalysis = matchCount >= 2 ? { text: "Doğru antrenmanı alıyor.", color: "#a6f704" } : { text: "Mevkisi için daha uygun bir antrenman seçilebilir.", color: "#ff7422" }; } let potentialEstimate25, potentialSource = ""; const latestReportElement = safeQuerySelector('#ScoutInfo .rec'); if (latestReportElement) { potentialEstimate25 = parseFloat(latestReportElement.textContent); potentialSource = "Gözlemci Raporu"; if (hiddenTalents.professionalism !== null) { const professionalismBonus = (hiddenTalents.professionalism - 4) * 0.3; const fitnessBonus = (hiddenTalents.fitness !== null) ? (hiddenTalents.fitness - 4) * 0.2 : 0; potentialEstimate25 = Math.min(Math.max(potentialEstimate25 + professionalismBonus + fitnessBonus, 0), 25.0); potentialSource = "Revize Edilmiş Rapor"; } } else { let ageMultiplier = age < 18 ? 1.35 : age < 21 ? 1.25 : age < 24 ? 1.15 : age < 28 ? 1.05 : 1.0; potentialEstimate25 = Math.min(positionalScore25 * ageMultiplier, 25.0); potentialSource = "Yaş Tahmini"; } displayReport({ position, positionalScore25, potentialEstimate25, age, skillDetails, potentialSource, trainingAnalysis }); } catch (error) { console.error("FMP Analiz Betiği Hatası:", error); handleError(error); } } function handleError(error) { let oldReport = document.getElementById('aiScoutReport'); if (oldReport) oldReport.remove(); const errorPanel = document.createElement('div'); errorPanel.id = 'aiScoutReport'; errorPanel.style.cssText = `position: fixed; top: 100px; right: 20px; width: 300px; background-color: #4d2121; border: 2px solid #dc3545; border-radius: 8px; padding: 15px; z-index: 10000; color: #fff; font-family: 'Roboto', sans-serif; box-shadow: 0 0 15px rgba(0,0,0,0.5);`; const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; closeButton.style.cssText = `position: absolute; top: 5px; right: 10px; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer;`; closeButton.onclick = () => errorPanel.remove(); const title = document.createElement('h3'); title.textContent = 'Analiz Hatası!'; title.style.cssText = `color: #ffc107; margin: 0 0 10px 0; border-bottom: 1px solid #dc3545; padding-bottom: 5px;`; const errorMessage = document.createElement('p'); errorMessage.textContent = `Hata Mesajı: ${error.message}`; errorMessage.style.margin = '10px 0'; const reportLink = createReportLink(error); const linkParagraph = document.createElement('p'); linkParagraph.textContent = 'Bu hatayı geliştiriciye bildirmek için aşağıdaki linke tıklayın. Mesaj otomatik olarak oluşturulacaktır.'; linkParagraph.style.marginTop = '15px'; linkParagraph.appendChild(reportLink); errorPanel.appendChild(closeButton); errorPanel.appendChild(title); errorPanel.appendChild(errorMessage); errorPanel.appendChild(linkParagraph); document.body.appendChild(errorPanel); } function createReportLink(error) { const url = new URL(window.location.href); const playerId = url.searchParams.get('id'); const subject = encodeURIComponent(`Yetenek Analizcisi Hata Raporu (Oyuncu ID: ${playerId})`); const body = encodeURIComponent( `Merhaba,\n\nFMP Yetenek Analizcisi betiği bir hata ile karşılaştı. Hata detayları aşağıdadır:\n\n` + `Hata Mesajı: ${error.message}\n` + `Oyuncu Sayfası: ${window.location.href}\n` + `Betiğin Sürümü: 1.4\n\n` + // Sürüm güncellendi `Stack Trace:\n${error.stack}` ); const mailUrl = `/Team/Mail?mode=New&recipientId=${YOUR_FMP_USER_ID}&subj=${subject}&body=${body}`; const reportLink = document.createElement('a'); reportLink.href = mailUrl; reportLink.textContent = "Hata Raporu Gönder"; reportLink.target = "_blank"; reportLink.style.cssText = `display: block; text-align: center; margin-top: 10px; padding: 8px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px;`; return reportLink; } function displayReport({ position, positionalScore25, potentialEstimate25, age, skillDetails, potentialSource, trainingAnalysis }) { let oldReport = document.getElementById('aiScoutReport'); if (oldReport) oldReport.remove(); const reportPanel = document.createElement('div'); reportPanel.id = 'aiScoutReport'; reportPanel.style.cssText = `position: fixed; top: 100px; right: 20px; width: 300px; background-color: #2a3128; border: 2px solid #53a12c; border-radius: 8px; padding: 15px; z-index: 10000; color: #fff; font-family: 'Roboto', sans-serif; box-shadow: 0 0 15px rgba(0,0,0,0.5);`; const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; closeButton.style.cssText = `position: absolute; top: 5px; right: 10px; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer;`; closeButton.onclick = () => reportPanel.remove(); const title = document.createElement('h3'); title.textContent = 'AI Gözlemci Raporu'; title.style.cssText = `color: #a6f704; margin: 0 0 10px 0; border-bottom: 1px solid #53a12c; padding-bottom: 5px;`; const scoresDiv = document.createElement('div'); scoresDiv.innerHTML = ` <p style="margin: 5px 0; display: flex; justify-content: space-between; align-items: center;"> <strong>Mevki Puanı (${position.replace('GK', 'KL')}):</strong> <span style="color: #a6f704; font-size: 1.2em;"> ${scoreToStars(positionalScore25)} (${positionalScore25.toFixed(1)}) </span> </p> <p style="margin: 5px 0; display: flex; justify-content: space-between; align-items: center;"> <strong>Potansiyel Tahmini (${potentialSource}):</strong> <span style="color: #e1d919; font-size: 1.2em;"> ${scoreToStars(potentialEstimate25)} (${potentialEstimate25.toFixed(1)}) </span> </p> `; skillDetails.sort((a, b) => b.weight - a.weight); const keySkills = skillDetails.slice(0, 3).map(s => `<li style="color: #a6f704;">${s.name} (${s.value})</li>`).join(''); const weakSkills = skillDetails.slice(-3).map(s => `<li style="color: #ff7422;">${s.name} (${s.value})</li>`).join(''); const detailsDiv = document.createElement('div'); detailsDiv.innerHTML = ` <h4 style="margin-top: 15px; color: #fff;">Mevki İçin Önemli Yetenekler:</h4> <ul style="margin: 0; padding-left: 20px;">${keySkills}</ul> <h4 style="margin-top: 10px; color: #fff;">Mevki İçin Az Önemli Yetenekler:</h4> <ul style="margin: 0; padding-left: 20px;">${weakSkills}</ul> <h4 style="margin-top: 15px; color: #fff;">Antrenman Analizi:</h4> <p style="margin: 5px 0; color: ${trainingAnalysis.color};">${trainingAnalysis.text}</p> `; reportPanel.appendChild(closeButton); reportPanel.appendChild(title); reportPanel.appendChild(scoresDiv); reportPanel.appendChild(detailsDiv); document.body.appendChild(reportPanel); } function addButton() { if (document.getElementById('aiScoutButton')) return; const actionsContainer = document.querySelector('#ActionsBoard table'); if (actionsContainer) { const newRow = document.createElement('tr'); const newHeader = document.createElement('th'); const newCell = document.createElement('td'); newCell.className = 'td-cell'; const button = document.createElement('div'); button.id = 'aiScoutButton'; button.textContent = 'AI Analizi'; button.className = 'fmp-btn btn-yellow'; button.style.width = '90px'; button.onclick = analyzePlayer; const descriptionDl = document.createElement('dl'); const dt = document.createElement('dt'); dt.textContent = 'Yetenek Analizi'; const dd = document.createElement('dd'); dd.textContent = 'Oyuncunun mevcut yeteneklerine göre sayısal bir analiz yapar.'; descriptionDl.appendChild(dt); descriptionDl.appendChild(dd); newHeader.appendChild(button); newCell.appendChild(descriptionDl); newRow.appendChild(newHeader); newRow.appendChild(newCell); actionsContainer.prepend(newRow); } } const observer = new MutationObserver((mutationsList, observer) => { const actionsContainer = document.querySelector('#ActionsBoard table'); if (actionsContainer && !document.getElementById('aiScoutButton')) { addButton(); } }); observer.observe(document.body, { childList: true, subtree: true }); })();