您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Carrega dados de 15 temporadas, filtra jogadores por país e 5 estrelas, calcula o R5 e marca jogadores em times inativos/banidos.
// ==UserScript== // @name Trophy Manager - R5 + Filtro + Status (Definitivo) // @namespace http://tampermonkey.net/ // @version 14.0 // @description Carrega dados de 15 temporadas, filtra jogadores por país e 5 estrelas, calcula o R5 e marca jogadores em times inativos/banidos. // @author Gemini & User & Kraft FC // @match https://*.trophymanager.com/history/league/* // @match https://*.trophymanager.com/history/club/transfers/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect trophymanager.com // @icon https://www.google.com/s2/favicons?sz=64&domain=trophymanager.com // ==/UserScript== (function() { 'use strict'; // --- SEÇÃO DE ESTILOS PARA MARCAR O NOME DO JOGADOR --- const playerStatusStyles = { inactive: { css: { 'color': '#007bff' }, title: 'Este jogador está em um clube INATIVO' }, banned: { css: { 'color': '#DC143C', 'font-weight': 'bold' }, title: 'Este jogador está em um clube BANIDO' } }; // --- SEÇÃO DE CÁLCULO DE R5 (sem alterações na lógica de cálculo) --- const weightR4=[[.51872935,.29081119,.57222393,.89735816,.84487852,.5088794,.5088794,.13637928,.05248024,.09388931,.57549122,0,0,0],[.45240063,.31762087,.68150374,.77724031,.74690951,.50072196,.45947168,.17663123,.23886264,.18410349,.46453393,0,0,0],[.43789335,.31844356,.53515723,.63671706,.59109742,.51311701,.53184426,.32421168,.06318165,.27931537,.50093723,.19317517,.07490902,0],[.42311032,.32315966,.62271745,.53932111,.51442838,.49835997,.47896659,.26434782,.22586124,.32182902,.45537227,.23961054,.09291562,0],[.3184988,.36581214,.50091016,.31726444,.2802902,.5202217,.55763723,.60199246,.10044356,.51811057,.38320838,.38594825,.14966211,0],[.35409971,.34443972,.64417234,.30427501,.27956082,.49925481,.46093655,.32887111,.38695101,.47884837,.37465446,.39194758,.15198852,0],[.32272636,.35024067,.48762872,.22888914,.19049636,.52620414,.57842512,.53330409,.07523792,.55942740,.39986691,.53866926,.20888391,0],[.36311066,.33106245,.61831416,.19830147,.17415753,.50049575,.47737842,.28937553,.34729042,.5283421,.39939218,.55684664,.21593269,0],[.40622753,.29744114,.39446722,.09952139,.07503885,.50402399,.5850585,.36932466,.05210389,.5367799,.51998862,.83588627,.32413803,0],[.37313433,.37313433,.37313433,.74626866,.52238806,.74626866,.52238806,.52238806,.37313433,.2238806,.2238806]]; const weightR5=[[.41029304,.18048062,.56730138,1.06344654,1.02312672,.40831256,.58235457,.12717479,.05454137,.0908983,.42381693,.04626272,.02199046,0],[.42126371,.18293193,.60567629,.91904794,.89070915,.40038476,.56146633,.15053902,.15955429,.15682932,.42109742,.09460329,.03589655,0],[.23412419,.32032289,.62194779,.63162534,.63143081,.45218831,.47370658,.55054737,.17744915,.39932519,.26915814,.16413124,.07404301,0],[.27276905,.26814289,.61104798,.39865092,.42862643,.43582015,.46617076,.44931076,.25175412,.46446692,.2998635,.43843061,.21494592,0],[.2521926,.25112993,.56090649,.18230261,.1837649,.45928749,.53498118,.59461481,.09851189,.6160195,.31243959,.65402884,.29982016,0],[.28155678,.24090675,.60680245,.19068879,.20018012,.45148647,.48230007,.42982389,.26268609,.57933805,.31712419,.65824985,.29885649,0],[.22029884,.2922969,.63248227,.09904394,.10043602,.47469498,.52919791,.7755588,.10531819,.71048302,.27667115,.56813972,.21537826,0],[.21151292,.3580471,.88688492,.14391236,.13769621,.46586605,.34446036,.51377701,.59723919,.75126119,.16550722,.29966502,.12417045,0],[.3547978,.14887553,.4327338,.00023928,.00021111,.46931131,.57731335,.41686333,.05607604,.62121195,.45370457,1.03660702,.43205492,0],[.45462811,.30278232,.45462811,.90925623,.45462811,.90925623,.45462811,.45462811,.30278232,.15139116,.15139116]]; const weightRb=[[.10493615,.05208547,.07934211,.14448971,.13159554,.06553072,.07778375,.06669303,.05158306,.02753168,.1205517,.01350989,.02549169,.0388755],[.07715535,.04943315,.11627229,.11638685,.12893778,.07747251,.06370799,.03830611,.10361093,.06253997,.09128094,.0131411,.02449199,.03726305],[.08219824,.08668831,.07434242,.09661001,.08894242,.08998026,.09281287,.08868309,.04753574,.06042619,.05396986,.05059984,.05660203,.03060871],[.06744248,.06641401,.09977251,.08253749,.09709316,.09241026,.08513703,.06127851,.1027552,.07985941,.0461896,.0392727,.05285911,.02697852],[.07304213,.08174111,.07248656,.08482334,.07078726,.09568392,.09464529,.09580381,.04746231,.07093008,.4595281,.05955544,.07161249,.03547345],[.06527363,.0641027,.09701305,.07406706,.08563595,.09648566,.08651209,.06357183,.10819222,.07386495,.03245554,.05430668,.06572005,.03279859],[.07842736,.07744888,.0720115,.06734457,.05002348,.08350204,.08207655,.11181914,.03756112,.07486004,.06533972,.07457344,.09781475,.02719742],[.06545375,.06145378,.10503536,.06421508,.07627526,.09232981,.07763931,.07001035,.11307331,.07298351,.04248486,.06462713,.07038293,.02403557],[.07738289,.05022488,.07790481,.01356516,.01038191,.06495444,.07721954,.07701905,.02680715,.07759692,.12701687,.15378395,.12808992,.03805251],[.07466384,.07466384,.07466384,.14932769,.10452938,.14932769,.10452938,.10344411,.0751261,.04492581,.04479831]]; const funFix2 = i => (i !== null && i !== undefined) ? (Math.round(i * 100) / 100).toFixed(2) : '-'; const getPosition = pos => ({'gk':9,'dc':0,'dr':1,'dl':1,'dmr':3,'dml':3,'dmc':2,'mr':5,'ml':5,'mc':4,'omr':7,'oml':7,'omc':6,'fc':8}[pos.toLowerCase().replace(/ /g, '')]); const calculateRemainders=(player,positionIndex,skills,SI)=>{let weight=26353376e4;9===positionIndex&&(weight=48717927500);let rec=0,ratingR=0,skillSum=0;for(let i=0;i<skills.length;i++)skillSum+=parseInt(skills[i]);let remainder=Math.round(10*(Math.pow(2,Math.log(weight*SI)/Math.log(Math.pow(2,7)))-skillSum))/10,remainderWeight=0,remainderWeight2=0,not20=0;weightR4[positionIndex].forEach((value,index)=>{rec+=skills[index]*weightRb[positionIndex][index];const weight=ratingType==="R5"?weightR5:weightR4;ratingR+=skills[index]*weight[positionIndex][index],20!=skills[index]&&(remainderWeight+=weightRb[positionIndex][index],remainderWeight2+=weight[positionIndex][index],not20++)});(0.9<remainder/not20||!not20)&&(9===positionIndex?not20=11:not20=14,remainderWeight=1,remainderWeight2=5),rec=funFix2((rec+remainder*remainderWeight/not20-2)/3);return[remainder,Math.round(remainderWeight2),not20,ratingR,rec]}; const calculateRERECOld=(player,positionIndex,skills,SI,rou)=>{const remainders=calculateRemainders(player,positionIndex,skills,SI);let rou2=0.03*(100-100*Math.pow(Math.E,-0.035*rou));const remainder=remainders[0]*remainders[1]/remainders[2];let ratingR=remainders[3]+remainder;return Number(funFix2(ratingR+5*rou2))}; const calculateREREC=(player,positionIndex,skills,SI,rou)=>{let ratingR4=calculateRERECOld(player,positionIndex,skills,SI,rou);let rou2=0.03*(100-100*Math.pow(Math.E,-0.035*rou));const remainders=calculateRemainders(player,positionIndex,skills,SI);var goldstar=0;var skillsB=[];for(let j=0;j<2;j++)for(let i=0;i<skills.length;i++)0==j&&20==skills[i]&&goldstar++,1==j&&(20!=skills[i]?skillsB[i]=1*skills[i]+remainders[0]/(skills.length-goldstar):skillsB[i]=skills[i]);var skillsB_rou=[];for(let i=0;i<skills.length;i++)1==i||(9===positionIndex&&i>2)?skillsB_rou[i]=skillsB[i]:skillsB_rou[i]=1*skills[i]+rou2;var headerBonus=12<skillsB_rou[10]?funFix2(0.8*(Math.pow(Math.E,(skillsB_rou[10]-10)**3/1584.77)-1)+0.15*Math.pow(Math.E,0.007*skillsB_rou[0]*skillsB_rou[0]/8.73021)+0.05*Math.pow(Math.E,0.007*skillsB_rou[6]*skillsB_rou[6]/8.73021)):0,fkBonus=funFix2(Math.pow(Math.E,0.002*Math.pow(skillsB_rou[13]+skillsB_rou[12]+0.5*skillsB_rou[9],2))/327.92526),ckBonus=funFix2(Math.pow(Math.E,0.002*Math.pow(skillsB_rou[13]+skillsB_rou[8]+0.5*skillsB_rou[9],2))/983.6577),pkBonus=funFix2(Math.pow(Math.E,0.002*Math.pow(skillsB_rou[13]+skillsB_rou[11]+0.5*skillsB_rou[9],2))/1967.31409),gainBase=funFix2((skillsB_rou[0]**2+0.5*skillsB_rou[1]**2+0.5*skillsB_rou[2]**2+skillsB_rou[3]**2+skillsB_rou[4]**2+skillsB_rou[5]**2+skillsB_rou[6]**2)/6/22.9**2),keepBase=funFix2((0.5*skillsB_rou[0]**2+0.5*skillsB_rou[1]**2+skillsB_rou[2]**2+skillsB_rou[3]**2+skillsB_rou[4]**2+skillsB_rou[5]**2+skillsB_rou[6]**2)/6/22.9**2),posGain=[0.3*gainBase,0.3*gainBase,0.9*gainBase,0.6*gainBase,1.5*gainBase,0.9*gainBase,0.9*gainBase,0.6*gainBase,0.3*gainBase],posKeep=[0.3*keepBase,0.3*keepBase,0.9*keepBase,0.6*keepBase,1.5*keepBase,0.9*keepBase,0.9*keepBase,0.6*keepBase,0.3*keepBase],allBonus=11==skills.length?0:Number(headerBonus)+Number(fkBonus)+Number(ckBonus)+Number(pkBonus);return 9===positionIndex?ratingR4=funFix2(Number(ratingR4)+allBonus):ratingR4=funFix2(Number(ratingR4)+allBonus+posGain[positionIndex]+posKeep[positionIndex]),ratingR4}; let ratingType = 'R5'; function getFullPlayerInfo(playerID) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "https://trophymanager.com/ajax/tooltip.ajax.php", data: `player_id=${playerID}&type=player`, headers: { "Content-Type": "application/x-www-form-urlencoded" }, onload: res => { try { const responseData = JSON.parse(res.responseText); const playerData = responseData.player; const clubData = responseData.club; const skillsArray = playerData.skills; if (!Array.isArray(skillsArray)) throw new Error("Skills data is not an array."); const checkSkills = skillsArray.filter(skill => skill.value); let orderedSkills = []; if (playerData.favposition.toLowerCase().includes('gk')) { orderedSkills = [checkSkills[0].value, checkSkills[2].value, checkSkills[4].value, checkSkills[1].value, checkSkills[3].value, checkSkills[5].value, checkSkills[6].value, checkSkills[7].value, checkSkills[8].value, checkSkills[9].value, checkSkills[10].value]; } else { orderedSkills.push(...checkSkills.filter((s, i) => i % 2 === 0).map(s => s.value)); orderedSkills.push(...checkSkills.filter((s, i) => i % 2 !== 0).map(s => s.value)); } const finalSkills = orderedSkills.map(skill => { if (typeof(skill) === 'string') { if (skill.includes('silver')) return 19; const match = skill.match(/title='(\d+)'/); if (match && match[1]) return parseInt(match[1]); return 20; } return skill; }); const playerInfo = { ...playerData, skills: finalSkills, asi: parseInt(playerData.skill_index.replace(/,/g, ''), 10), xp: parseFloat(playerData.routine) || 0, }; resolve({ playerInfo, clubInfo: clubData }); } catch (e) { reject(e); } }, onerror: reject }); }); } function filtrarJogadores() { const countryCode = document.getElementById('tm-country-input').value.toLowerCase().trim(); if (!countryCode) { alert('Por favor, insira um código de país (ex: br, ao, pt).'); return; } const countrySelector = `a.country_link[href="/national-teams/${countryCode}/"]`; let rows = document.querySelectorAll('table.zebra.hover.tablesorter tbody tr'); rows.forEach(row => { if (row.classList.contains('season-separator')) { row.style.display = ''; return; } let isMatchingCountry = row.querySelector(countrySelector) !== null; let starsTd = row.querySelector('td:nth-child(2)'); let isFiveStars = starsTd && starsTd.querySelectorAll('img[src="/pics/star.png"]').length === 5; if (isMatchingCountry && isFiveStars) { row.style.display = ''; } else { row.style.display = 'none'; } }); } function mostrarTodos() { let rows = document.querySelectorAll('table.zebra.hover.tablesorter tbody tr'); rows.forEach(row => row.style.display = ''); } function criarInterfaceFiltro() { if (document.getElementById('tm-filter-container')) return; let isFiltered = false; // Move state here const container = document.createElement('div'); container.id = 'tm-filter-container'; container.style.cssText = "position: fixed; top: 10px; right: 10px; z-index: 9999; background: rgba(0,0,0,0.7); padding: 8px; border-radius: 5px; display: flex; flex-direction: column; align-items: flex-end; gap: 10px;"; const filterGroup = document.createElement('div'); filterGroup.style.cssText = "display: flex; align-items: center; gap: 10px;"; const label = document.createElement('label'); label.textContent = 'País:'; label.style.color = 'white'; label.style.fontWeight = 'bold'; const countryInput = document.createElement('input'); countryInput.id = 'tm-country-input'; countryInput.type = 'text'; countryInput.placeholder = 'ex: br'; countryInput.style.cssText = "width: 40px; padding: 5px; border: 1px solid #ccc; border-radius: 3px;"; countryInput.value = GM_getValue('tm_filter_country', 'ao'); const filterButton = document.createElement('button'); filterButton.id = 'tm-filter-button'; filterButton.style.cssText = "padding: 5px 10px; color: #fff; border: none; border-radius: 3px; cursor: pointer;"; const updateButtonText = () => { const countryCode = countryInput.value.toUpperCase(); filterButton.textContent = `Filtrar ${countryCode} 5 Estrelas`; }; countryInput.addEventListener('change', () => { const newCountry = countryInput.value.toLowerCase().trim(); GM_setValue('tm_filter_country', newCountry); updateButtonText(); }); filterButton.addEventListener('click', function() { isFiltered = !isFiltered; if (isFiltered) { filtrarJogadores(); filterButton.textContent = 'Remover Filtro'; filterButton.style.backgroundColor = '#FF5733'; calculateR5ForFiltered(); } else { mostrarTodos(); updateButtonText(); filterButton.style.backgroundColor = '#4CAF50'; } }); filterGroup.appendChild(label); filterGroup.appendChild(countryInput); filterGroup.appendChild(filterButton); const loadSeasonsButton = document.createElement('button'); loadSeasonsButton.id = 'tm-load-seasons-button'; loadSeasonsButton.textContent = 'Carregar 15 Temporadas'; loadSeasonsButton.style.cssText = "padding: 5px 10px; color: #fff; border: none; border-radius: 3px; cursor: pointer; background-color: #337ab7; width: 100%;"; loadSeasonsButton.addEventListener('click', () => carregarTemporadasAnteriores(isFiltered)); container.appendChild(loadSeasonsButton); container.appendChild(filterGroup); document.body.appendChild(container); updateButtonText(); filterButton.style.backgroundColor = '#4CAF50'; } async function carregarTemporadasAnteriores(filterIsActive) { const button = document.getElementById('tm-load-seasons-button'); button.disabled = true; const urlMatch = window.location.href.match(/\/transfers\/(\d+)/); if (!urlMatch) { alert('Não foi possível determinar a temporada atual a partir da URL.'); button.disabled = false; return; } const currentSeason = parseInt(urlMatch[1]); const baseUrl = window.location.href.replace(/\/transfers\/\d+/, ''); const seasonsToLoad = 14; const boughtTableBody = Array.from(document.querySelectorAll('h3')).find(h3 => h3.textContent.trim() === 'Bought')?.nextElementSibling?.querySelector('tbody'); const soldTableBody = Array.from(document.querySelectorAll('h3')).find(h3 => h3.textContent.trim() === 'Sold')?.nextElementSibling?.querySelector('tbody'); for (let i = 1; i <= seasonsToLoad; i++) { const seasonToFetch = currentSeason - i; if (seasonToFetch < 1) break; button.textContent = `Carregando ${i}/${seasonsToLoad} (Temp. ${seasonToFetch})...`; try { const fetchUrl = `${baseUrl}/transfers/${seasonToFetch}`; const response = await fetch(fetchUrl); const html = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const separatorHtml = `<tr class="season-separator" style="background-color: #3a3a3a; font-weight: bold; text-align: center;"><td colspan="4">--- Temporada ${seasonToFetch} ---</td></tr>`; const fetchedBoughtBody = Array.from(doc.querySelectorAll('h3')).find(h3 => h3.textContent.trim() === 'Bought')?.nextElementSibling?.querySelector('tbody'); if (fetchedBoughtBody && boughtTableBody) { boughtTableBody.insertAdjacentHTML('beforeend', separatorHtml); Array.from(fetchedBoughtBody.children).forEach(row => boughtTableBody.appendChild(row)); } const fetchedSoldBody = Array.from(doc.querySelectorAll('h3')).find(h3 => h3.textContent.trim() === 'Sold')?.nextElementSibling?.querySelector('tbody'); if (fetchedSoldBody && soldTableBody) { soldTableBody.insertAdjacentHTML('beforeend', separatorHtml); Array.from(fetchedSoldBody.children).forEach(row => soldTableBody.appendChild(row)); } } catch (error) { console.error(`Falha ao carregar temporada ${seasonToFetch}:`, error); button.textContent = `Erro na Temp. ${seasonToFetch}`; await new Promise(resolve => setTimeout(resolve, 2000)); } } button.textContent = 'Dados Carregados'; button.style.backgroundColor = '#5cb85c'; button.disabled = true; if (filterIsActive) { filtrarJogadores(); calculateR5ForFiltered(); } } async function calculateR5ForFiltered() { const tables = document.querySelectorAll('table.zebra.hover.tablesorter'); if (tables.length === 0) return; for (const table of tables) { const rows = Array.from(table.querySelectorAll('tbody tr')).filter(row => row.style.display !== 'none' && !row.classList.contains('season-separator')); const batchSize = 5; for (let i = 0; i < rows.length; i += batchSize) { const batch = rows.slice(i, i + batchSize); const promises = batch.map(async (row) => { const playerLink = row.querySelector('td:first-child a[href*="/players/"]'); const playerCell = playerLink ? playerLink.closest('td') : null; if (!playerLink || !playerCell || playerCell.querySelector('span.r5-span')) return; const match = playerLink.href.match(/\/players\/(\d+)/); if (!match || !match[1]) return; const playerID = match[1]; const loadingSpan = document.createElement('span'); loadingSpan.className = 'r5-span'; loadingSpan.textContent = ' (R5: ...)'; loadingSpan.style.cssText = "color: grey; font-size: 0.9em;"; playerLink.after(loadingSpan); try { const { playerInfo, clubInfo } = await getFullPlayerInfo(playerID); let highestR5 = 0; let positions = playerInfo.favposition.split(','); positions.forEach(pos => { let positionIndex = getPosition(pos); if (positionIndex !== undefined) { let r5 = calculateREREC(playerInfo, positionIndex, playerInfo.skills, playerInfo.asi, playerInfo.xp); if (r5 > highestR5) highestR5 = r5; } }); loadingSpan.textContent = ` (R5: ${highestR5})`; loadingSpan.style.color = 'yellow'; if (clubInfo) { let status = null; if (clubInfo.status === 'b') status = 'banned'; else if (clubInfo.created === 'inactive') status = 'inactive'; if (status) { Object.assign(playerLink.style, playerStatusStyles[status].css); playerLink.title = playerStatusStyles[status].title; } } } catch (error) { console.error(`[TM SCRIPT] Falha ao processar ${playerID}:`, error); loadingSpan.textContent = ` (R5: Erro)`; loadingSpan.style.color = 'red'; } }); await Promise.all(promises); if (rows.length > batchSize) { await new Promise(resolve => setTimeout(resolve, 250)); } } } } window.addEventListener('load', function() { criarInterfaceFiltro(); }); })();