LightningFlik
It's not feature complete yet but here's a beta version of my FM 24 scouting tool for Windows: https://github.com/PhilipArmstead/Yet-another-Scouting-Tool/releases/tag/v24.0.139-beta

A config file will be created after you run it; see here for a primer on how to configure the weights (but I think it will be straightforward). The app hot reloads after modifying the config so it should make it easy to tweak scales and such.

This was built with speed and convenience in mind so there's no absurd wait time when starting the app. Just run it, load a save and get cracking. (Once you see the number of players cached in the bottom left corner, it's fully primed. Blink and you'll miss it).
GeorgeFloydOverdosed said: Interesting that the coach rating decreased with lower current rep. Wouldn't have thought that.

I've always suspected that the coach report is vibes-based, really. It makes sense to me that their current reputation (and possibly even form) influences it, as well as the relative strength of the league and team-mates. I don't for one minute think the game is telling me that a 4.5 star player is unquestionably better than a 4 star player.

GeorgeFloydOverdosed said: Club rep and manager rep doesn't matter, would have expected somewhat otherwise.

This surprised me too. Anecdotally I am convinced that my manager's reputation is factored in when talking to players one-on-one (my manager is always bullied in conversations with players when starting from the bottom; after a few seasons though I can reliably threaten to sell any player I want to keep them in line).

I'm going to do more investigation on this tonight; I think maybe the function I was looking at last night was actually determining how long the player wants his contract to be. It returns a number 1, 2 or 3 and I thought it was grading your bargaining position, but now I think it's how long the player wants.

I should be spending time finishing my scouting tool but it would actually be incredibly useful reverse engineering the mechanisms behind this so I can search the entire database for players who are interested in joining.
johnconnerson said: Have you (or anyone else) done research into how wage demand is calculated? I'm curious if there is a way to estimate how much a player "should" make (based on age, CA, reputation, or whatever else affects it). This would be helpful for quickly understanding whether or not I can afford a player/if a player is undervalued by the market.

I'm intrigued enough by the question to look.

Here's a very quick test. There are three metrics for reputation in the game:

- Home
- Current
- World

Other websites can probably explain (or guess) what they do but I'm just here to see if they affect contract negotiations. (How could they not?) I'll play with the values (and use my unchanged player as a baseline) and record them below.

| Home rep | Current rep | World rep | Salary demand
| 3,115        | 4,372      | 1,1911    | Important player, £53k p/a
| 10,000      | 4,372      | 1,1911    | Important player, £55k p/a (plus small bonus increases)
| 0                | 4,372      | 1,1911    | Same as baseline
| 3,115        | 10,000    | 1,1911    | Wants to talk but thinks I can't afford him (1)
| 3,115        | 0              | 1,1911    | Important player, £36k p/a (also his bonus demands have halved and the coach's estimation of his ability has decreased)
| 3,115        | 4,372      | 10,1000  | Important player, £86k p/a (plus large bonus increases)
| 3,115        | 4,372      | 0            | Important player, £51.5k p/a (plus small bonus decreases)

(1) This was true even after I adjusted my budget sliders and tried again and after I adjusted the club's reputation to be 10,000

N.b. after changing reputations I advance a few in-game hours so the system registers it

Other notes
1. I saw no change after setting my wage budget as low as it would go (£428k p/a) and as high as it would go (£1.2m p/a) even for the 10,000 world rep experiment. (But if someone on Reddit said it did then I'm not going to dispute that.)
2. World reputation is the only thing that changed the player's reputation on his Personal Information page. It was 5 grey stars ("Minimal";) at 0 and 5 gold stars ("Exceptional";) at 10,000.
3. It makes sense that Current Reputation would have such a big impact on demands + your star rating if it's meant to be the sense of how hot you are as a player.
4. I also tested the baseline player when the club had a reputation of 0 and 10,000.

0: Important player, £58k p/a
2,920 (baseline): Important player, £53k p/a
3,115 (player's home rep): Important player, £54k p/a
10,000: Important player, £64k p/a

5. I also tried setting the CA/PA of the player to various values:

| CA  | PA  | Demands
| 67  | 120 | £53k p/a (baseline)
| 67  | 200 | Unchanged
| 67  | 67  | Unchanged
| 120 | 120 | Not willing to discuss!
| 1  | 200 | Unchanged

6. I changed the manager's Home, Current and World reputations to 10,000 and it made no difference (although it made people like me more).

Here's a dump of screenshots showing (most) of the above. I know a lot of this might not be new information but it's a start.

Baseline


CA 120/120


Club rep 0


Club rep 10,000


Player current rep 0


Player current rep 10,000


Player home rep 0


Player home rep 10,000


Player world rep 10,000


EDIT: From reading the game's code I can tell you that the reputation of the league you're going to be playing in next season is a factor (or the current league, if next season's isn't decided yet).
It's a minimal impact though, seemingly. If I tell Isaac that we're going to the Premier League next season instead of League Two, he asks for a tiny bit more money and his coach summary rating drops an entire star.



From the looks of the code, it seems like a few things are summed (reputations) and then scored (given a grade?) That is, I don't think changes are linear; I think you get bucketed in to discrete grades, which represent how strong your bargaining position is. After enough reputation points gained/lost, you might find yourself in the next bucket.

This has taken _far_ longer than I anticipated and I'm knackered so I might look at the actual calculations tomorrow (no promises though).

EDIT again: I can't help myself.

If you have a "Front End" Sugar Daddy, the player's demands increase. (+15 to the score it keeps track of).
GeorgeFloydOverdosed said: How did you get it to display the phrases as single lines like that? Maybe its cause I didn't let it fully finish processing I guess.
Maybe Analysis -> One Shot -> ASCII Strings?

I know I did that at the start. Then I use Window -> Defined Strings to search.
I just can't find where that code above is executed. I've decided to search for "weig" in hopes that I can find somewhere else in the code where that logic is performed. In doing so, I've found the nuttiest string references in the binary. What the actual hell is in this game?





EDIT: one more. I actually googled the strings in this one and found a medical website with the exact same strings. I'd love to talk to one of the people who worked on this game and ask exactly what directory they've accidentally included in their build pipeline on this project.

Medical website

GeorgeFloydOverdosed said: Very interesting findings

How do you feel about the premise that different positions require different attributes? I know that flies in the face of your theory of a generalised make-up (last I checked, anyway). I thought for a while that the positional requirements of attributes was overblown because FM always punishes me for expecting too much of it.

Now I'm unsure, but I've had great success promoting Warrington up the leagues using your old, old weights.
I assume it makes no difference to the results but I'm pretty sure that custom goalkeeper metric is wrong.

It says "+10% means the keeper conceded 10% fewer than expected."

If I faced 10 goals and only conceded 9 then I've conceded 10% fewer than expected, In this formula though...

(1 expected goal prevented + 9 conceded) / 9 conceded
10 / 9
11.11%

That's not 10%. To get 10% he'd need to divide goals prevented by xG: 1 / (1 + 9). Not sure which one of these he actually wanted though. Still, it's interesting so far.
GeorgeFloydOverdosed said: So just to clarify, it is the case that 0x239 = acceleration, right?

Correct. The game stores various objects in memory (Person, Player, Staff, Club, Team, Nation, Competition and so on) and 0x239 is the offset from the start of any given Player object where acceleration is stored. You can trust the offsets defined in my constants file.

GeorgeFloydOverdosed said: What led you to 0x0144b40180 anyway

I opened Cheat Engine, went to the memory address of a known player (which my scouting app told me) and added a "Tell me what read from this address" breakpoint on the player's acceleration. One of the instructions was the one I quoted in my code snippet here. I noticed the function actually hit a lot of offsets and learned it was combining and multiplying them in various ways.

GeorgeFloydOverdosed said: Would be interested in seeing the suspected role location

This is something you led me to last night, with this comment:

GeorgeFloydOverdosed said: I spent a while trying to find attribute combos in the code using this info. I came across a few intriguing ones, such as Reflexes interacting with CommandOfArea for some reason, but no clear formula I'm looking for.

I wondered what you were talking about so I searched the FM binary for "Command", "CommandOfArea" and "Command_of_Area".

"Command_Of_Area" is a hard-coded string at memory location 0x145c48e51 and it is used in only one location: 0x144380f6c. I can't copy and paste the whole thing but as soon as I saw it, I was intrigued.



It looks like it's taking a bunch of attribute names, feeding them in to a function and then getting a value back from them. Reading player attributes? Surely not, they're stored in a Player object in memory and accessed through offsets, not by string.

I looked at what called that function and got one hit: 0x14231dcd7.



It's a very boring looking function but then I noticed some strange looking memory addresses on lines 16, 33 and 50. I looked closer and realised they're not memory addresses, they're the words "posi", "attr" and "weig" represented as numbers (i.e. the character codes for each letter have been concatenated to make a 4 byte number). This intrigued me further.

I went back to the previous function (the one that read in attributes and did something with them) and noticed the bottom: the game reads in string references to player positions (e.g. DL, AMRL, SW) and it takes those plus the values read in above and feeds them in to another function.

Given all this, I'm convinced it is determining some sort of relationship between positions and attributes. Problem is, I can't find when the code is executed. I've put breakpoints all around it and they never fire. Is the code unreachable and unused? Does it fire when the game first loads, before I get a chance to attach Cheat Engine to it? (I can test this by launching the game with a debugger already attached, which I will do after work). Is it legacy code? (This wouldn't necessarily surprise me, I've found some legacy stuff already (around a mechanic called Match Boost in training modules that never seems to get used)).
Yes that's right. Everything in constants.h is right, that's from my app which scouts players in memory. I'm saying the names I gave to the attribute groups were made up by me.

Also I may have found the location in code where role/position suitability is calculated, but I can't get the code to fire in the course of the game, so testing it is proving difficult. It looks like that's what it's doing though.
GeorgeFloydOverdosed said: Although what you discovered isn't exactly the golden ticket we are looking for, it's nonetheless a significant insight for me.

Perhaps it's something obvious to a programmer, but as someone who doesn't know programming and couldn't make heads or tails of the code before, finding out what the attributes are called in the code gives me a solid foothold to work with.


I'm sorry to say that all the names in my post were given by me. There's nothing in the code I shared that names attributes or the groups they belong to (although you could infer them by reading the exact scout report detail they lead to).

The names in this post came straight from the qme_stat_relevant_attribute_data file in simatch.fmf though.
I figured someone else would have gone digging before now. Cheers.
I was digging around in the simatch.fmf file and found the attributes which are used to determine how often a player succeeds at a given task. I don't know if this is useful to anybody but it does present the opportunity for a fun experiment where you mod the file to replace everything with something like "Flair" and then give yourself a bunch of Flair: 20 players to see if that makes you incredibly OP. (If not, then it probably means this data can't be trusted anyway).

| Statistic | Encoded attributes |
| --- | --- |
| Passes attempted | Passing, Work Rate |
| Pass completion | Passing, Decisions |
| Key passes | Passing, Vision |
| Crosses attempted | Crossing, Work Rate |
| Cross completion | Crossing, Vision, Technique |
| Tackles attempted | Tackling, Positioning, Work Rate |
| Tackle completion | Tackling, Positioning |
| Interceptions | Anticipation, Positioning |
| Blocks | Anticipation, Positioning, Bravery |
| Clearances | Positioning |
| Headers attempted | Positioning, Jumping |
| Header completion | Heading, Strength, Jumping |
| Fouls made | Dirtiness, Aggression |
| Dribbles | Dribbling, Flair, Pace, Acceleration |
| Offsides | Movement, Work Rate, Acceleration, Concentration |
| Distance run | Work Rate, Stamina, Pace |
| Sprints | Work Rate, Stamina |
| Pressures attempted | Work Rate, Anticipation |
| Pressure completion | Work Rate, Anticipation, Acceleration |
| Progressive passes | Passing, Vision |
Are there CA-hungry attributes that people feel don't really contribute to actual results (e.g. dual-footedness)?

I have a theory that a player's CA is a multiplier for all other attributes when it comes to determining their overall strength. So given two players whose only difference is CA + whatever inconsequential attribute bumps it takes to justify the increased CA, the player with the higher CA will perform better.

An entire team of such players should outperform the same team without boosted CA. That is, a team of 99 CA players should be consistently outperformed by a team of 100 CA players even if their attributes are almost 100% identical.

If I'm wrong then bumping a useless attribute to the point where it increases CA by 1 should make no difference.
I want to share something of potential interest.

I've been disassembling the "fm.exe" application for FM 24, which means I've turned the machine code back in to something resembling C so that I can look at it. I'm doing this because I want to find where shortlists/search results are stored in RAM so I can read them in my scouting tool but I might have found something else.

There's a function beginning at memory address 0x0144b40180 (at least in my Windows, Steam copy of FM 24.4.2+2081827), which appears to be grouping together player attributes and scoring them. I'll share the approximated C code here (feel free to ask Chat GPT what it means) as well as what I think it's doing. If you do feed this in to an LLM to decipher, give it this file too. That will map the offset codes (e.g. 0x23d, Pace) to the player's actual attributes.

From what I can see, this code is reading player attributes in groups, multiplying them together and creating a score for each group. It then returns the highest scoring group and the group itself.

For example, starting on line 65 of the code snippet:

Spoiler sVar1 = (short)((uint)(*(char *)(param_1 + 0x239) * 0x6667 + 0xccce) >> 0x10);
  uVar8 = (sVar1 >> 1) - (sVar1 >> 0xf);
  if ((short)uVar8 < 2) {
    uVar8 = 1;
  }
  uVar6 = (uint)uVar8;
  if (0x13 < uVar6) {
    uVar6 = 0x14;
  }
  sVar1 = (short)((uint)(*(char *)(param_1 + 0x23d) * 0x6667 + 0xccce) >> 0x10);
  uVar8 = (sVar1 >> 1) - (sVar1 >> 0xf);
  if ((short)uVar8 < 2) {
    uVar8 = 1;
  }
  uVar11 = 0x14;
  if (uVar8 < 0x14) {
    uVar11 = (uint)uVar8;
  }
  if ((int)(short)uVar2 < (int)((uVar11 + uVar6) * 5)) {
    uVar2 = (short)(uVar11 + uVar6) * 5;
    *param_2 = uVar2;
    uVar3 = CONCAT71((int7)((ulonglong)uVar3 >> 8),9);
  }


- The lines beginning sVar1 = (short)((uint)(*(char *) read a different attribute from the Player entity (Acceleration and Pace in this example (0x239 and 0x23d)).
- The lines uVar8 = (sVar1 >> 1) - (sVar1 >> 0xf); divide the attribute by 2, rounding down.
- The next two if conditions ensure the number is between 1-20 inclusive.
- The final if condition sums uVar11 (which is Pace / 2) and uVar6 (which is Acceleration / 2) and then multiplies the value by 5. If that result is > uVar2 (which is the highest rating it has seen so far), then it sets that to be the highest rating (*param_2 = uVar2) and sets the return value of the function to be the category ID (9 in this case).

Now here's the exciting part: the attributes it groups together and how it scores them.

Group 1: Set pieces

((Corners + Free Kicks + Penalties + Long Throws) * 10) / 4

Group 2: Attacking technique

(Crossing + Finishing + Long Shots + Passing + Technique) * 2

Group 3: Attacking intelligence

((Anticipation + Vision + Decisions + Positioning) * 10) / 4

(Yes, positioning.)

Group 4: Leadership

((Bravery + Work Rate + Teamwork) * 10) / 3

Group 5: Defensive intelligence

((Tackling + Marking + Concentration + Positioning) * 10) / 4

Group 6: Technical control

(Flair + Dribbling + First Touch + Composure + Balance) * 2

Group 7: Aerial Ability

(Heading + Jumping Reach) * 5

Group 8: Physique

((Strength + Natural Fitness + Stamina + Agility) * 10) / 4

Group 9: Speed

(Acceleration + Pace) * 5

Now I'm not saying that the game uses the same aggregation/scaling in the match engine--the only place that I've found it make a difference is the coach report (see below; this is me hacking the return value from the function so it thinks groups 9, 8 and 3 are this player's best)--but it's worth bearing in mind. I'm going to keep this line of enquiry up and I'll report back anything I learn.



@GeorgeFloydOverdosed As part of your season-long experiments, would it be valuable to see at a glance all teams in a given league with the average rating of their players?

I'm thinking something like this:

| Man City  | 84.75% |
| Liverpool | 83.60% |
| Arsenal  | 82.13% |
| Chelsea  | 80.44% |

And so on.

I'm not sure what your tests look like but I assume you'd expect the final order of computer-controlled teams in your leagues to roughly correlate to the ratings of the players inside? or else it would mean a tweak is needed to the ratings? Right now it seems like you're testing tactics but this way you could test your weights with all the teams in the league, not just one.
GeorgeFloydOverdosed said: Yes, I'm still keen to see your go at it. The more options the better.

I guess with FM27 coming soon, its 50/50 whether people will move on entirely from FM24 or not. But if FM27 turns out to be crap 2.0, then I think these new tools will help keep FM24 alive by injecting a bit of novelty into it.


With that in mind, how flexible a weighting system do you think is needed to really assess players? Is a simple Pace: 20, Acceleration: 19.2 system sufficient or do you need interdependence between attributes?
@GeorgeFloydOverdosed Are you in the market for an FM 24 scouting tool with customisable weights? or do you have one already? Mine is nearly done but there's been so many released recently, I can't even keep track.
OpticFawn said: please make a version for mac, only one i can use is fmrte and its kinda bad imo for scouting
This is the one version I can't reliably make. I don't have a Mac. It would require porting about 7 OS-specific functions but I can't test them. It'll be open source though if someone else wants to add the feature.

**EDIT:** maybe I could send you test builds but it would be slow going.
@mavali

I wanted to alert you to something I'm currently developing. It's yet another FM scouting tool. It's for FM 24, written in C, open-source and free forever. It doesn't use any of your code but I have heavily relied on your tool as far as look-and-feel go (since I don't have a visually creative bone in my body and I am hamstrung by design any time I create anything.)

This is actually a tool I've been making on and off for a while (see a post here from May where I showed off the Node version of it, before deciding to rewrite it in C because shipping a Node app for people to use would be an awful experience).

I mention all this by way of asking for your blessing to rely so heavily on your app's aesthetic. I don't want you to hear about another scouting tool that looks similar to yours and wonder if you've been ripped off.

I did consider putting this down and porting your tool to FM 24 (the only one I play) and then adding in missing features I care about, but when I saw that your backend bindings were written in C#, I decided to leave it alone.

Let me know if you're all right with this; if not then I guess we'll figure something out.

EDIT: I meant to attach a screenshot.