Static catalog data for the soccer game's player cards. Defines curated ratings, ages, heights, preferred foot and portrait values keyed by player name, and appends a batch of professional players (ids 25-73) to the catalog with generated details.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Socer;
public static partial class PlayerCatalog
{
// Original approximate game profiles informed by EA's role/stat structure.
// These values are deliberately authored rather than represented as a live licensed database.
static Dictionary<string,string> faceData;
static Dictionary<string,string> Faces=>faceData??=new(StringComparer.Ordinal){
["Gianluigi Donnarumma"]="90,84,73,91,52,87|27|196|Right",
["Alisson Becker"]="86,85,85,87,56,89|33|193|Right",
["Achraf Hakimi"]="93,76,82,82,79,78|27|181|Right",
["Trent Alexander-Arnold"]="75,72,90,80,78,73|27|175|Right",
["Virgil van Dijk"]="77,60,71,72,90,86|35|193|Right",
["Antonio Rüdiger"]="81,54,68,65,87,86|33|190|Right",
["Jude Bellingham"]="80,86,83,88,78,84|23|186|Right",
["Rodri"]="66,80,86,84,87,85|30|191|Right",
["Mohamed Salah"]="89,88,83,88,45,76|34|175|Left",
["Bukayo Saka"]="85,83,83,88,60,73|25|178|Left",
["Harry Kane"]="64,91,84,83,49,82|33|188|Right",
["Erling Haaland"]="87,92,71,80,47,89|26|195|Left",
["Thibaut Courtois"]="89,89,76,90,46,89|34|200|Left",
["Manuel Neuer"]="83,80,88,82,52,85|40|193|Right",
["Theo Hernández"]="91,71,79,82,78,83|28|184|Left",
["Alphonso Davies"]="95,65,77,84,75,74|25|185|Left",
["Rúben Dias"]="62,39,66,68,89,87|29|187|Right",
["William Saliba"]="79,39,70,72,88,83|25|192|Right",
["Kevin De Bruyne"]="67,86,92,86,63,75|35|181|Right",
["Pedri"]="77,70,86,90,67,64|23|174|Right",
["Vinícius Júnior"]="95,84,81,91,29,69|26|176|Right",
["Lamine Yamal"]="85,80,83,90,23,52|19|180|Left",
["Lionel Messi"]="76,86,90,91,33,63|39|170|Left",
["Kylian Mbappé"]="96,91,80,92,29,76|27|178|Right",
["Jan Oblak"]="84,88,74,85,43,87|33|188|Right",
["Marc-André ter Stegen"]="86,84,89,85,47,85|34|187|Right",
["Emiliano Martínez"]="83,84,76,85,45,84|34|195|Right",
["David Raya"]="82,81,88,84,50,82|31|183|Right",
["Diogo Costa"]="82,80,83,85,51,81|27|186|Right",
["Unai Simón"]="81,83,78,83,48,83|29|190|Right",
["Andy Robertson"]="77,58,81,77,81,75|32|178|Left",
["Federico Dimarco"]="79,75,86,80,76,74|28|175|Left",
["Jeremie Frimpong"]="94,67,76,84,72,61|25|171|Right",
["Dani Carvajal"]="77,56,75,77,83,80|34|173|Right",
["Gabriel Magalhães"]="68,38,61,58,85,85|28|190|Left",
["Alessandro Bastoni"]="73,43,77,73,86,82|27|190|Left",
["Marquinhos"]="75,54,74,70,87,79|32|183|Right",
["Matthijs de Ligt"]="64,51,64,60,83,86|27|189|Right",
["Nico Schlotterbeck"]="75,55,71,65,82,82|26|191|Left",
["Declan Rice"]="71,65,77,77,85,85|27|188|Right",
["Federico Valverde"]="89,81,84,81,78,83|28|182|Right",
["Bruno Fernandes"]="70,83,89,82,67,71|32|179|Right",
["Martin Ødegaard"]="70,78,89,89,63,65|27|178|Left",
["Florian Wirtz"]="81,80,86,89,50,60|23|177|Right",
["Jamal Musiala"]="84,81,80,92,58,64|23|184|Right",
["Cole Palmer"]="75,85,83,86,45,63|24|189|Left",
["Raphinha"]="89,83,82,86,52,68|29|176|Left",
["Nico Williams"]="93,76,78,85,37,64|24|181|Right",
["Ousmane Dembélé"]="92,84,84,90,36,59|29|178|Left",
["Lautaro Martínez"]="81,87,75,85,48,85|29|174|Right",
["Alexander Isak"]="85,85,72,84,35,77|27|192|Right",
["Victor Osimhen"]="87,84,65,77,41,82|27|186|Right",
["Cristiano Ronaldo"]="77,87,75,80,34,77|41|187|Right",
// Famous expansion: authored football archetypes on the same 99-point
// game scale. Identity/role references are recorded in Art/Portraits/
// famous-expansion-v3.json; these are not EA or live-season ratings.
["Ederson"]="85,82,93,86,64,84|33|188|Left",
["Sergio Ramos"]="70,68,72,66,86,85|40|184|Right",
["Jules Koundé"]="82,44,76,78,86,80|27|181|Right",
["João Cancelo"]="84,70,87,87,82,72|32|182|Right",
["Joshua Kimmich"]="64,70,90,83,85,76|31|177|Right",
["Luka Modrić"]="70,74,94,91,68,61|41|172|Right",
["N'Golo Kanté"]="78,56,80,80,90,78|35|168|Right",
["Frenkie de Jong"]="78,69,87,87,77,77|29|181|Right",
["Neymar"]="85,83,86,94,36,59|34|175|Right",
["Son Heung-min"]="88,89,81,86,43,71|34|183|Right",
["Rafael Leão"]="94,82,78,87,31,74|27|188|Right",
["Bernardo Silva"]="73,76,88,92,60,62|32|171|Left",
["Robert Lewandowski"]="70,91,77,82,44,83|38|185|Right",
["Karim Benzema"]="72,89,85,86,38,79|38|185|Right",
["Luis Suárez"]="68,88,79,81,45,80|39|175|Right",
["Antoine Griezmann"]="77,87,86,87,58,69|35|176|Left",
};
static void CuratedFace(PlayerCard card)
{
if(!Faces.TryGetValue(card.Name,out string data))return;
var pieces=data.Split('|');var a=pieces[0].Split(',').Select(int.Parse).ToArray();
card.Age=int.Parse(pieces[1]);card.HeightCm=int.Parse(pieces[2]);card.Foot=pieces[3];
if(PlayerPositions.Card(card)=="GK")
{
card.Pace=a[4];card.Keeping=(int)Math.Round((a[0]+a[1]+a[3]+a[5])/4d);
card.Passing=a[2];card.Shooting=14;card.Dribbling=29;card.Defence=19;card.Physical=68;card.Endurance=47;
}
else
{
card.Pace=a[0];card.Shooting=a[1];card.Passing=a[2];card.Dribbling=a[3];card.Defence=a[4];card.Physical=a[5];
card.Endurance=Math.Clamp(card.Physical+8,45,94);card.Keeping=12;
}
card.Composure=Math.Clamp(card.Overall+2,60,95);card.Aggression=Math.Clamp(card.Physical-1,40,92);
}
public static void KeeperReference(PlayerCard card,PlayerDetailStats stats)
{
if(PlayerPositions.Card(card)!="GK" || !Faces.TryGetValue(card.Name,out string data))return;
var a=data.Split('|')[0].Split(',').Select(int.Parse).ToArray();
var original=Find(card.CatalogId);
int development=original==null?0:card.Keeping-original.Keeping;
int C(int n)=>Math.Clamp(n+development,1,99);
stats.GkDiving=C(a[0]);stats.GkHandling=C(a[1]);stats.GkKicking=Math.Clamp(a[2]+(original==null?0:card.Passing-original.Passing),1,99);
stats.GkReflexes=C(a[3]);stats.GkPositioning=C(a[5]);
}
static void AddMoreProfessionals(List<PlayerCard> result)
{
Add("Jan Oblak","Slovenia","SI","GK",25);
Add("Marc-André ter Stegen","Germany","DE","GK",26);
Add("Emiliano Martínez","Argentina","AR","GK",27);
Add("David Raya","Spain","ES","GK",28);
Add("Diogo Costa","Portugal","PT","GK",29);
Add("Unai Simón","Spain","ES","GK",30);
Add("Andy Robertson","Scotland","SCT","LB",31);
Add("Federico Dimarco","Italy","IT","LB",32);
Add("Jeremie Frimpong","Netherlands","NL","RB",33);
Add("Dani Carvajal","Spain","ES","RB",34);
Add("Gabriel Magalhães","Brazil","BR","CB",35);
Add("Alessandro Bastoni","Italy","IT","CB",36);
Add("Marquinhos","Brazil","BR","CB",37);
Add("Matthijs de Ligt","Netherlands","NL","CB",38);
Add("Nico Schlotterbeck","Germany","DE","CB",39);
Add("Declan Rice","England","GB","CDM",40);
Add("Federico Valverde","Uruguay","UY","CM",41);
Add("Bruno Fernandes","Portugal","PT","CAM",42);
Add("Martin Ødegaard","Norway","NO","CAM",43);
Add("Florian Wirtz","Germany","DE","CAM",44);
Add("Jamal Musiala","Germany","DE","CAM",45);
Add("Cole Palmer","England","GB","CAM",46);
Add("Raphinha","Brazil","BR","RW",47);
Add("Nico Williams","Spain","ES","LW",48);
Add("Ousmane Dembélé","France","FR","RW",49);
Add("Lautaro Martínez","Argentina","AR","ST",50);
Add("Alexander Isak","Sweden","SE","ST",51);
Add("Victor Osimhen","Nigeria","NG","ST",52);
Add("Cristiano Ronaldo","Portugal","PT","ST",53);
// 54-57 are the historical sources of the existing red editions.
// Append normal identities without renumbering any owned player.
Add("Ederson","Brazil","BR","GK",58);
Add("Sergio Ramos","Spain","ES","CB",59);
Add("Jules Koundé","France","FR","RB",60);
Add("João Cancelo","Portugal","PT","LB",61);
Add("Joshua Kimmich","Germany","DE","CDM",62);
Add("Luka Modrić","Croatia","HR","CM",63);
Add("N'Golo Kanté","France","FR","CDM",64);
Add("Frenkie de Jong","Netherlands","NL","CM",65);
Add("Neymar","Brazil","BR","LW",66);
Add("Son Heung-min","South Korea","KR","LW",67);
Add("Rafael Leão","Portugal","PT","LW",68);
Add("Bernardo Silva","Portugal","PT","CAM",69);
Add("Robert Lewandowski","Poland","PL","ST",70);
Add("Karim Benzema","France","FR","ST",71);
Add("Luis Suárez","Uruguay","UY","ST",72);
Add("Antoine Griezmann","France","FR","ST",73);
void Add(string name,string country,string code,string pos,int id)
{
var card=new PlayerCard{CatalogId=$"PRO-{id:00}",Name=name,Country=country,CountryCode=code,
Position=pos=="GK"?"ВР":pos=="CB"?"ЦЗ":pos is "LB" or "RB"?"КЗ":pos is "LW" or "RW"?"КП":pos=="ST"?"НП":"ЦП",
PreferredPosition=pos,Pace=70,Shooting=70,Passing=70,Dribbling=70,Defence=60,Physical=70,Keeping=12,
PortraitSkin=2,PortraitHair=0,PortraitStyle=id%5,PortraitBeard=2,WeakFoot=3,SkillMoves=pos=="GK"?1:pos is "LW" or "RW" or "CAM"?4:2};
CuratedFace(card);
if(id==53){card.WeakFoot=4;card.SkillMoves=5;card.PortraitSkin=1;}
if(id>=58)FamousPlayerIdentity(card,id);
CardDevelopment.Initialize(card);card.Details=PlayerCardDetails.Generate(card);result.Add(card);
}
}
static void FamousPlayerIdentity(PlayerCard card,int id)
{
// Stamina, temperament and technique retain different play patterns:
// ball-winners can repeat recoveries, creators control rather than race,
// and older finishers rely on position and composure.
(card.Endurance,card.Aggression,card.Composure,card.WeakFoot,card.SkillMoves)=id switch
{
58=>(55,49,90,3,1),59=>(76,92,87,3,2),60=>(86,79,85,3,3),61=>(81,67,86,4,4),
62=>(88,78,89,4,3),63=>(70,56,93,4,4),64=>(92,81,84,3,2),65=>(84,66,88,4,4),
66=>(68,58,90,5,5),67=>(84,54,88,5,4),68=>(78,45,81,4,4),69=>(91,60,90,3,4),
70=>(76,72,93,4,3),71=>(72,58,93,4,4),72=>(69,86,89,4,3),73=>(85,68,90,3,4),
_=>(card.Endurance,card.Aggression,card.Composure,card.WeakFoot,card.SkillMoves)
};
(card.PortraitSkin,card.PortraitHair,card.PortraitBeard)=id switch
{
58=>(2,0,2),59=>(1,1,0),60=>(4,0,2),61=>(2,0,0),62=>(0,3,2),63=>(1,1,2),
64=>(5,0,2),65=>(0,3,2),66=>(2,0,0),67=>(1,0,2),68=>(4,0,2),69=>(1,1,0),
70=>(0,0,2),71=>(2,0,0),72=>(2,0,0),73=>(0,1,0),_ => (2,0,2)
};
card.Flag=card.CountryCode switch {
"BR"=>"🇧🇷","ES"=>"🇪🇸","FR"=>"🇫🇷","PT"=>"🇵🇹","DE"=>"🇩🇪","NL"=>"🇳🇱",
"HR"=>"🇭🇷","KR"=>"🇰🇷","PL"=>"🇵🇱","UY"=>"🇺🇾",_=>card.Flag};
}
}