// ==UserScript==
// @name TM Opponent Scouting
// @version 2.1
// @description Trophy Manager: Show match events for the last 5 games of next opponent. Now fetches fixtures directly from the club page for efficiency.
// @author Scunny Club ID: 4464490 (Modificado por Kraft FC ID: 970949)
// @namespace https://trophymanager.com
// @include https://trophymanager.com/home/
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
console.log('TM Last Five Match Reports Enhanced script starting...');
// Configuration
const LAST_MATCHES = 5;
// Constants
const FIXTURES = '/ajax/fixtures.ajax.php';
// Event types and mappings
const MENTALITY_MAP = new Map()
.set("1", "Very Defensive")
.set("2", "Defensive")
.set("3", "Slightly Defensive")
.set("4", "Normal")
.set("5", "Slightly Attacking")
.set("6", "Attacking")
.set("7", "Very Attacking");
const STYLE_MAP = new Map()
.set("1", "Balanced")
.set("2", "Direct")
.set("3", "Wings")
.set("4", "Shortpassing")
.set("5", "Long Balls")
.set("6", "Through Balls");
const GOAL_STYLE_MAP = new Map()
.set("p_s", "Penalty")
.set("kco", "GK Counter")
.set("klo", "GK Kick")
.set("doe", "Corner")
.set("cou", "Counter/Direct")
.set("dir", "Freekick")
.set("win", "Wing Attack")
.set("sho", "Short Pass")
.set("lon", "Long Ball")
.set("thr", "Through Ball");
const FOCUS_MAP = new Map()
.set("1", "Balanced")
.set("2", "Left")
.set("3", "Center")
.set("4", "Right");
const EVENT_TYPE = {
GOAL: 'goal',
YELLOW_CARD: 'yellow',
RED_CARD: 'red',
MENTALITY: 'mentality',
STYLE: 'style',
POSITION: 'position',
SUBSTITUTION: 'substitution',
INJURY: 'injury'
};
// Add status indicator
function addStatusIndicator(message) {
let statusDiv = document.getElementById('tm-reports-status');
if (!statusDiv) {
statusDiv = document.createElement('div');
statusDiv.id = 'tm-reports-status';
statusDiv.style.cssText = 'position: fixed; top: 10px; right: 10px; background: #333; color: white; padding: 10px; border-radius: 5px; z-index: 9999; font-size: 12px;';
document.body.appendChild(statusDiv);
}
statusDiv.textContent = message;
console.log('Status: ' + message);
}
// Get current team info from SESSION data
function getCurrentTeamInfo() {
try {
// SESSION data is available globally on Trophy Manager pages
if (typeof SESSION !== 'undefined') {
return {
id: SESSION.id || SESSION.main_id,
name: SESSION.clubname
};
}
} catch (e) {
console.error('Error accessing SESSION data:', e);
}
return null;
}
// *** MODIFIED FUNCTION ***
// Search for fixture/opponent and return OPPONENT's ID and name
function getOpponentInfo() {
const currentTeam = getCurrentTeamInfo();
if (!currentTeam) {
console.error('Could not identify current team from SESSION data');
return null;
}
console.log('Current team:', currentTeam.name, '(ID:', currentTeam.id + ')');
const fixtureElements = document.querySelectorAll('[class*="fixture"], [class*="opponent"], [class*="match"], [class*="next"]');
for (let el of fixtureElements) {
const clubLinks = el.querySelectorAll('a[href*="/club/"]');
for (let clubLink of clubLinks) {
const match = clubLink.href.match(/\/club\/(\d+)/);
if (match) {
const teamId = match[1];
const teamName = clubLink.textContent.trim();
// Skip if this is the current team
if (teamId === currentTeam.id.toString() || teamName === currentTeam.name) {
console.log('Skipping current team:', teamName);
continue;
}
// This must be the opponent
console.log('Found opponent Club ID:', teamId, 'Name:', teamName);
return { id: teamId, name: teamName };
}
}
}
console.error('Could not find opponent team');
return null;
}
// *** REMOVED a large block of code here (getLeagueUrlFromAnotherPage) as it's no longer needed ***
function applyResults(data) {
let fixtures = filterFixtures(data);
}
// Filter fixtures to get completed matches only
function filterFixtures(data) {
let months = [];
let matches = [];
for (const key in data) {
if (data.hasOwnProperty(key)) {
months.push(data[key]);
}
}
for (let index = 0; index < months.length; index++) {
const thisMonth = months[index];
matches = matches.concat(thisMonth.matches.filter(function testUnPlayed(match) {
return match.result != null;
}));
}
return matches;
}
// Get team's last matches
function getTeamMatches(matches, teamName) {
console.log('Looking for matches for team: ' + teamName);
let teamMatches = [];
for (let index = matches.length - 1; index >= 0 && teamMatches.length < LAST_MATCHES; index--) {
const match = matches[index];
const homeMatch = match.hometeam_name === teamName;
const awayMatch = match.awayteam_name === teamName;
if (homeMatch || awayMatch) {
let matchLink = '';
try {
if (typeof $ !== 'undefined') {
matchLink = $(match.match_link).attr('href') || match.match_link;
} else {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = match.match_link;
const linkElement = tempDiv.querySelector('a');
matchLink = linkElement ? linkElement.href : match.match_link;
}
} catch (e) {
console.error('Error getting match link:', e);
continue;
}
let matchData = {
date: match.date,
homeTeam: match.hometeam_name,
awayTeam: match.awayteam_name,
result: match.result,
matchLink: matchLink
};
teamMatches.unshift(matchData);
console.log('Added match: ' + match.hometeam_name + ' vs ' + match.awayteam_name);
}
}
console.log('Found ' + teamMatches.length + ' team matches');
return teamMatches;
}
// Process match events and return filtered events for a specific team
function processMatchEvents(matchData, targetTeamName) {
const report = matchData.report;
if (Object.keys(report).length <= 3) {
return []; // No match data available
}
const homeClubName = matchData.club.home.club_name;
const awayClubName = matchData.club.away.club_name;
const homeClubId = matchData.club.home.id;
const awayClubId = matchData.club.away.id;
console.log('Team comparison - Target:', targetTeamName);
console.log('Home club name:', homeClubName);
console.log('Away club name:', awayClubName);
const isTargetHome = homeClubName === targetTeamName;
const isTargetAway = awayClubName === targetTeamName;
console.log('Is target home?', isTargetHome, 'Is target away?', isTargetAway);
if (!isTargetHome && !isTargetAway) {
console.log('Target team not found in match:', targetTeamName, 'vs', homeClubName, '&', awayClubName);
return [];
}
const homeLineup = matchData.lineup.home;
const awayLineup = matchData.lineup.away;
const homePlayerIds = Object.getOwnPropertyNames(homeLineup);
const awayPlayerIds = Object.getOwnPropertyNames(awayLineup);
const homePlayer = new Map();
const awayPlayer = new Map();
homePlayerIds.forEach((playerId) => {
homePlayer.set(playerId, homeLineup[playerId].name);
});
awayPlayerIds.forEach((playerId) => {
awayPlayer.set(playerId, awayLineup[playerId].name);
});
let eventReport = [];
if (isTargetHome) {
const homeStartStyle = matchData.match_data.attacking_style.home === "0" ? "1" : matchData.match_data.attacking_style.home;
const homeStartMentality = matchData.match_data.mentality.home.toString();
const homeFocus = matchData.match_data.focus_side.home;
eventReport.push({
minute: "0",
type: "tactics",
content: `Starting - Mentality: ${MENTALITY_MAP.get(homeStartMentality)}, Style: ${STYLE_MAP.get(homeStartStyle)}, Focus: ${FOCUS_MAP.get(homeFocus)}`
});
} else if (isTargetAway) {
const awayStartStyle = matchData.match_data.attacking_style.away === "0" ? "1" : matchData.match_data.attacking_style.away;
const awayStartMentality = matchData.match_data.mentality.away.toString();
const awayFocus = matchData.match_data.focus_side.away;
eventReport.push({
minute: "0",
type: "tactics",
content: `Starting - Mentality: ${MENTALITY_MAP.get(awayStartMentality)}, Style: ${STYLE_MAP.get(awayStartStyle)}, Focus: ${FOCUS_MAP.get(awayFocus)}`
});
}
Object.keys(report).forEach(function (minute) {
const minuteArr = report[minute];
for (let i = 0; i < minuteArr.length; i++) {
const paramArr = minuteArr[i].parameters;
if (paramArr) {
for (let j = 0; j < paramArr.length; j++) {
const paramObj = paramArr[j];
if (paramObj.goal) {
const isTargetTeamGoal = (isTargetHome && homePlayer.has(paramObj.goal.player)) ||
(isTargetAway && awayPlayer.has(paramObj.goal.player));
if (isTargetTeamGoal) {
let goalStyle = "";
let chanceType = minuteArr[i].type;
if (chanceType) {
chanceType = chanceType.substring(0, 3);
if (GOAL_STYLE_MAP.has(chanceType)) {
goalStyle = GOAL_STYLE_MAP.get(chanceType);
}
}
const playerMap = isTargetHome ? homePlayer : awayPlayer;
const scorer = playerMap.get(paramObj.goal.player);
const assister = paramObj.goal.assist ? playerMap.get(paramObj.goal.assist) : null;
eventReport.push({
minute: minute,
type: "goal",
content: `[Goal] ${scorer}${assister ? ` (${assister})` : ""} ${goalStyle ? `[${goalStyle}]` : ""} ${paramObj.goal.score[0]} - ${paramObj.goal.score[1]}`
});
}
} else if (paramObj.yellow) {
const isTargetTeamCard = (isTargetHome && homePlayer.has(paramObj.yellow)) ||
(isTargetAway && awayPlayer.has(paramObj.yellow));
if (isTargetTeamCard) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "yellow",
content: `[Yellow card] ${playerMap.get(paramObj.yellow)}`
});
}
} else if (paramObj.red) {
const isTargetTeamCard = (isTargetHome && homePlayer.has(paramObj.red)) ||
(isTargetAway && awayPlayer.has(paramObj.red));
if (isTargetTeamCard) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "red",
content: `[Red card] ${playerMap.get(paramObj.red)}`
});
}
} else if (paramObj.yellow_red) {
const isTargetTeamCard = (isTargetHome && homePlayer.has(paramObj.yellow_red)) ||
(isTargetAway && awayPlayer.has(paramObj.yellow_red));
if (isTargetTeamCard) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "red",
content: `[Red card] (2 yellow cards) ${playerMap.get(paramObj.yellow_red)}`
});
}
} else if (paramObj.mentality_change) {
const targetClubId = isTargetHome ? homeClubId : awayClubId;
if (paramObj.mentality_change.team == targetClubId) {
if (paramObj.mentality_change.mentality) {
eventReport.push({
minute: minute,
type: "mentality",
content: `[Tactics] Mentality change to ${MENTALITY_MAP.get(paramObj.mentality_change.mentality)}`
});
} else if (paramObj.mentality_change.style) {
eventReport.push({
minute: minute,
type: "style",
content: `[Tactics] Attacking style change to ${STYLE_MAP.get(paramObj.mentality_change.style)}`
});
}
}
} else if (paramObj.player_change) {
const isTargetTeamChange = (isTargetHome && homePlayer.has(paramObj.player_change.player)) ||
(isTargetAway && awayPlayer.has(paramObj.player_change.player));
if (isTargetTeamChange) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "position",
content: `[Position] ${playerMap.get(paramObj.player_change.player)} change to ${paramObj.player_change.position ? paramObj.player_change.position.toUpperCase() : ""}`
});
}
} else if (paramObj.sub && paramObj.sub.player_in && paramObj.sub.player_out) {
const isTargetTeamSub = (isTargetHome && homePlayer.has(paramObj.sub.player_in)) ||
(isTargetAway && awayPlayer.has(paramObj.sub.player_in));
if (isTargetTeamSub) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "substitution",
content: `[Substitution] ${playerMap.get(paramObj.sub.player_in)} replace ${playerMap.get(paramObj.sub.player_out)}${paramObj.sub.player_position ? ` and play ${paramObj.sub.player_position.toUpperCase()}` : ""}`
});
}
} else if (paramObj.injury) {
const isTargetTeamInjury = (isTargetHome && homePlayer.has(paramObj.injury)) ||
(isTargetAway && awayPlayer.has(paramObj.injury));
if (isTargetTeamInjury) {
const playerMap = isTargetHome ? homePlayer : awayPlayer;
eventReport.push({
minute: minute,
type: "injury",
content: `[Injury] ${playerMap.get(paramObj.injury)}`
});
}
}
}
}
}
});
return eventReport;
}
// Enhanced display function with dropdown functionality
function displayEnhancedMatchList(matches, opponentTeamName) {
let html = '<div id="tm-match-reports" style="margin: 1px 0; padding: 1px; border: 2px solid #333; background: #333333;">';
html += '<h2 style="text-align: center; color: #ffffff; margin: 5px; margin-bottom: 8px; font-size: 16px;">Next Opponent Last ' + matches.length + ' Matches</h2>';
matches.forEach((match, index) => {
const matchId = `match-${index}`;
const dropdownId = `dropdown-${index}`;
html += '<div style="margin: 2px 0; padding: 1px; border: 1px solid #ccc; background: #008000; border-radius: 1px; font-size: 14px; line-height: 14px;">';
html += '<div style="display: flex; justify-content: space-between; align-items: center; padding: 5px;">';
html += '<div>';
html += '<p style="margin: 0; padding-bottom: 5px;">M ' + (index + 1) + ': ' + match.homeTeam + ' ' + match.result + ' ' + match.awayTeam + '</p>';
html += '<p style="margin: 0; padding-bottom: 5px; font-size: 12px;">Date: ' + match.date + ' | <a href="' + match.matchLink + '" target="_blank">View Match</a></p>';
html += '</div>';
html += '<button onclick="toggleMatchEvents(\'' + matchId + '\', \'' + dropdownId + '\', \'' + match.matchLink + '\', \'' + opponentTeamName.replace(/'/g, "\\'") + '\')" ';
html += 'style="background: #006600; color: white; border: 1px solid #004400; padding: 8px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; line-height: 1.2; text-align: center; min-width: 50px;">';
html += '<div>Show</div><div>Events</div></button>';
html += '</div>';
html += '<div id="' + dropdownId + '" style="display: none; padding: 10px; background: #004d00; border-top: 1px solid #ccc; margin-top: 5px;">';
html += '<div id="' + dropdownId + '-content">Click "Show Events" to load match details...</div>';
html += '</div>';
html += '</div>';
});
html += '</div>';
const targetElement = document.querySelector('.column2_a .box_footer');
if (targetElement) {
targetElement.insertAdjacentHTML('beforebegin', html);
console.log('Inserted enhanced match list with dropdowns');
} else {
console.error('Could not find the target element to insert before');
}
}
function toggleMatchEvents(matchId, dropdownId, matchUrl, opponentTeamName) {
const dropdown = document.getElementById(dropdownId);
const button = event.target.closest('button');
if (dropdown.style.display === 'none') {
dropdown.style.display = 'block';
button.innerHTML = '<div>Hide</div><div>Events</div>';
const contentDiv = document.getElementById(dropdownId + '-content');
contentDiv.innerHTML = '<div style="color: yellow;">Loading match events...</div>';
loadMatchEvents(matchUrl, opponentTeamName, contentDiv);
} else {
dropdown.style.display = 'none';
button.innerHTML = '<div>Show</div><div>Events</div>';
}
}
function loadMatchEvents(matchUrl, opponentTeamName, contentDiv) {
const urlParts = matchUrl.split('/');
let matchId = '';
if (matchUrl.includes('/nt/')) {
matchId = 'nt' + urlParts[urlParts.length - 2];
} else {
for (let i = urlParts.length - 1; i >= 0; i--) {
if (urlParts[i] && !isNaN(urlParts[i])) {
matchId = urlParts[i];
break;
}
}
}
console.log('Extracted match ID:', matchId, 'from URL:', matchUrl);
const ajaxUrl = 'https://trophymanager.com/ajax/match.ajax.php?id=' + matchId;
const xhr = new XMLHttpRequest();
xhr.open('GET', ajaxUrl, true);
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
try {
const data = JSON.parse(this.responseText);
const events = processMatchEvents(data, opponentTeamName);
displayMatchEvents(events, contentDiv);
} catch (e) {
console.error('Error parsing match data:', e);
contentDiv.innerHTML = '<div style="color: red;">Error loading match data</div>';
}
} else if (this.readyState == 4) {
contentDiv.innerHTML = '<div style="color: red;">Failed to load match data (Status: ' + this.status + ')</div>';
}
};
xhr.send();
}
function displayMatchEvents(events, contentDiv) {
if (events.length === 0) {
contentDiv.innerHTML = '<div style="color: #cccccc;">No events found for this team in this match.</div>';
return;
}
let html = '<div style="color: white; font-size: 13px;">';
events.forEach((event, index) => {
let color = '#ffffff';
switch (event.type) {
case 'goal': color = '#00ffff'; break;
case 'yellow': color = '#ffff00'; break;
case 'red': color = '#ff4444'; break;
case 'mentality': color = '#ff9900'; break;
case 'style': color = '#6666ff'; break;
case 'position': color = '#99ff99'; break;
case 'substitution': color = '#ff99ff'; break;
case 'injury': color = '#996633'; break;
case 'tactics': color = '#ffcc99'; break;
}
html += '<div style="color: ' + color + '; margin: 3px 0; padding: 2px;">';
html += event.minute + "': " + event.content;
html += '</div>';
});
html += '</div>';
contentDiv.innerHTML = html;
}
window.toggleMatchEvents = toggleMatchEvents;
window.loadMatchEvents = loadMatchEvents;
window.displayMatchEvents = displayMatchEvents;
// *** REWRITTEN FUNCTION ***
// Main function to get match history
async function generateMatchReports() {
addStatusIndicator('Starting opponent scouting...');
const opponentInfo = getOpponentInfo();
if (!opponentInfo) {
addStatusIndicator('Error: Could not find opponent info.');
return;
}
addStatusIndicator('Found opponent: ' + opponentInfo.name);
addStatusIndicator('Fetching fixtures for ' + opponentInfo.name + '...');
// New, direct request data using the club ID
const requestData = {
'type': 'club',
'var1': opponentInfo.id
};
if (typeof $ !== 'undefined') {
// Use jQuery
$.post(FIXTURES, requestData, function(data) {
handleFixturesResponse(data, opponentInfo.name);
}, 'json').fail(function(xhr, status, error) {
console.error('AJAX request failed:', status, error);
addStatusIndicator('Error: Failed to fetch fixtures.');
});
} else {
// Use vanilla JS for broader compatibility
const xhr = new XMLHttpRequest();
xhr.open('POST', FIXTURES, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
const formData = Object.keys(requestData)
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(requestData[key]))
.join('&');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const data = JSON.parse(xhr.responseText);
handleFixturesResponse(data, opponentInfo.name);
} catch (e) {
console.error('Error parsing JSON:', e);
addStatusIndicator('Error: Invalid response data.');
}
} else {
console.error('Request failed with status:', xhr.status);
addStatusIndicator('Error: Failed to fetch fixtures.');
}
}
};
xhr.send(formData);
}
}
function handleFixturesResponse(data, opponentName) {
console.log('Fixtures data received:', data);
if (data != null) {
addStatusIndicator('Processing fixtures...');
const fixtures = filterFixtures(data);
const teamMatches = getTeamMatches(fixtures, opponentName);
if (teamMatches.length === 0) {
addStatusIndicator('No completed matches found for ' + opponentName);
return;
}
addStatusIndicator('Displaying ' + teamMatches.length + ' matches...');
displayEnhancedMatchList(teamMatches, opponentName);
addStatusIndicator('Scouting complete!');
setTimeout(() => {
const statusDiv = document.getElementById('tm-reports-status');
if (statusDiv) statusDiv.remove();
}, 5000);
} else {
console.error('No fixtures data received');
addStatusIndicator('Error: No fixtures data received.');
}
}
function waitForReady() {
console.log('Waiting for page to be ready...');
addStatusIndicator('Page loading, please wait...');
setTimeout(generateMatchReports, 2000); // Reduced wait time as it might not be needed
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', waitForReady);
} else {
waitForReady();
}
console.log('TM Last Five Match Reports Enhanced script initialized');
})();