PBDATA_EU.PAC holds every real person the game knows: 27,950 players,
3,000 managers and coaches, and 1,000 scouts. That covers names,
nationality, age, height, weight, positions, preferred shirt number, a
required status, personality traits, skills, play styles and 64 ability
ratings per player. The squads in
OTEAMMEMBER.TBB (INITTEAM_FORMAT.md) refer to
players by their index here.
SRC/pbdata.py reads it. python SRC/initteam.py squads uses it to put
names on the squads.
| Address | Symbol | What it shows |
|---|---|---|
0x110d50, 0x11034c |
PwkCallbackCommand_BpDataReadFile and its caller |
loads the pack and calls plBp_Create(work, entry0, entry1, entry2, entry3) with the four BINPAC entries in order |
0x20dd78, 0x20c5d8 |
plBp_Create, PlBpinfoTask::init |
entry 0 is the header, entry 1 the records, entry 2 and entry 3 are kept at 0x390670 / 0x390674. If the header's first byte is '0', it isn't parsed |
0x20da58 |
PlBpinfoTask::initBpmaster |
the header layout below |
0x20c700 |
getPbase(id) |
player id (0 to count−1) is at records + id × 98. It is decoded by plBits_DecPlPbaseEx. Ids from 0x7cce are the edit player imported from Virtua Pro Football, held in the save (SAVE_FORMAT.md) |
0x20c808 |
getMbase(id) |
ids from 0x6d2e, at records + players×98 + (id − 0x6d2e) × 81, decoded by plBits_DecPlMbaseEx |
0x20c9b8 |
getSbase(id) |
ids from 0x78e6, decoded by plBits_DecPlSbaseEx |
0x20bd28 |
PlBitsClass::readBits(n) |
reads n bits, most significant first. Bits are taken from each byte starting at 0x80, and bytes in order |
0x20bdc8 |
PlBitsClass::readBitsStr(buf, n) |
n 8-bit characters: the name |
0x2e8568 |
plBits_DecPlPbaseEx |
the player fields below, in order. Afterwards it adds 16, 150 and 45 to +0x28, +0x29 and +0x2a, replaces +0x34 with STATUS[v & 0xf], and maps each of the 64 abilities through 0x2e8540 |
0x2e8a70 |
plBits_DecPlMbaseEx |
the manager fields. The two groups of 9-bit values are sign-extended from bit 8 into s32 at +0x4c and +0x54 (pointers set up at 0x2e8b64) |
0x2e8ec8 |
plBits_DecPlSbaseEx |
the scout fields. The last 45 of its 49 seven-bit values go through 0x2e8540 |
0x2e8540, 0x55b950 |
ability mapping | ABILITY[min(v, 31)] with ABILITY = 38, 40, … 96, 98, 99 |
0x55b970 |
status table (required status) | 0, 200, 1000, 3000, 5000, 7500, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000 |
0x2175e0, 0x2176a8 |
plPinfo_IsForeigner, plPinfo_IsEU |
player +0x14 is the nationality (PlNati). Bit 1 of +0x63 is an EU passport on top of it |
0x218748 |
plPinfo_IsSkill |
+0x64 is a bit mask, one bit per PlPlayerSkill |
0x20d908 |
getPinfoRank |
for ids ≥ 0x63f7 the rank is +0x18. Below that it is 15 − the row of 16 {first, last} ranges at 0x5eac08 (filled at run time) that holds entry 3's value for the player |
0x20d9a0 |
getPinfoApos0 |
the main position is +0x1c. For ids below 0x63f7 it is the column of the group table at 0x52fbf8 (row 15 − rank) whose range holds entry 3's value (Entries 2 and 3) |
0x20d470 |
PlBpinfoTask::serchPinfo |
searches the ranking: entry 2 (0x390670) through the group table at 0x52fbf8 |
0x24d450, 0x24d480, 0x33c5d8 |
pwkOteam_InitNonresident modes 0 and 2, _InitCommon |
the fixed squads of the id blocks (Player id blocks) |
0x2734c8 |
pwkTeam_SetUnumberOpinfo |
+0x2b is the player's preferred shirt number, 1–99 |
0x218728 |
plPinfo_IsSkill (PlPinfo overload) |
reads the skills at PlPinfo +0x1fc, so the game's copy of the record starts at PlPinfo +0x198 |
0x24d8a8 |
pwkPlayStyle_Init |
PlPinfo +0x1f6 (record +0x5e) holds 5 play styles: it copies the non-zero ones into the style list at +0x27c, then adds random styles 1–22 (slti 0x17) for the player's position. The first stored style becomes the current one (+0x278) |
0x289e50, 0x52f708 |
CDetailManager::AddPlate_PLAYSTYLE, Msg::GetString's base ids |
the current style is drawn as GetString(5, style). Type 5's base id is 150, and types below 0x29 use global slot 0, category 1. So style n is message 1:150 + n |
0x246884 |
match growth (in the function before pwkGUtl_AddExp's caller at 0x246930) |
abilities 45–52 (0x2d + k) gain experience only when k is the system of the club's current team style (my-team data +0x4204); abilities 33–41 only for the player's own position row |
0x215a00 |
plMisc_Nati2NatiTeam |
nation → national team: PLRESOURCECOMMON.PAC entry 3, table 2, one u16 per nation. pbdata.py names nations through it, because the team names are known (message category 3) |
| Offset | Type | Value | Meaning |
|---|---|---|---|
0x00 |
char[4] | 1000 |
version text. Only checked for a leading '0' |
0x04 |
u32 | 1 | not read |
0x08 |
u32 × 3 | 27,950 / 3,000 / 1,000 | record counts: players, managers, scouts |
0x14 |
u32 | 3,058 | stored at +0x34 of the task. Use not traced |
0x18 |
u16 (+2) × 3 | 98 / 81 / 71 | record sizes in bytes. Each is followed by ff ff |
0x24 |
u16 | 64 | stored at +0x50. Use not traced |
0x26 |
8 bytes | 2e 02 26 a5 00 00 30 30 |
not read by initBpmaster |
27,950 × 98 + 3,000 × 81 + 1,000 × 71 = 3,053,100 bytes, exactly the
size of entry 1 in PBDATA_EU.PAC.
Each record starts with a char[19] name (cp850, zero-padded), followed
by bit fields read MSB first. The widths, order and destination offsets
are all confirmed from the decoders. The destination offset is where
the game keeps the value in memory. Meanings marked empirical come
from the data alone. Player field names from +0x34 on are the
developers' own labels (below);
confirmed on such a row means the game code that reads the field was
also found. Every field also has the developers' source name
(The developers' field names), which
names the fields the editor labels leave out.
| Bits | × | Offset | Name | Meaning |
|---|---|---|---|---|
| 8 | 1 | +0x14 |
nation | nationality, 1–144. confirmed |
| 5 | 1 | +0x18 |
rank | 0–15. confirmed (read directly only for ids ≥ 0x63f7) |
| 4 | 3 | +0x1c |
position | main and up to two more positions: a cell of the detail screen's pitch grid, 0–12, with 13 meaning none. See Positions. confirmed |
| 7 | 1 | +0x28 |
age | stored − 16. Empirical meaning: 16–40, and it matches the real players in 2005. Computer-team players don't show this age: their squad slot's age from OTEAMMEMBER.TBB is used instead (INITTEAM_FORMAT.md). Tested in game: a database age of 16 didn't change Terry's shown age |
| 8 | 1 | +0x29 |
height | stored − 150, in cm (158–205). Empirical. The decoder stores the sum in a byte (sb at 0x2e8a14), so heights above 255 wrap. Tested in game: 313 cm shows as 57 cm |
| 7 | 1 | +0x2a |
weight | stored − 45, in kg (48–100). Empirical |
| 7 | 1 | +0x2b |
shirt | preferred shirt number. confirmed |
| 3 | 1 | +0x2c |
leg | 0–3. Bit 0 set means right-footed, clear means left. Empirical: every well-known left-footer tested (Robben, Ashley Cole, Giggs, Messi, Roberto Carlos, Duff, Cech) has it clear. Bit 1 is set for famously two-footed players (Maldini, Henry, Rooney, Duff), and VPF's Player Edit screen shows Rooney (leg 3) as "Both(R)", so bit 1 is two-footed. The developers' editor names the four values 左, 右, 両左, 両右 (left, right, both-left, both-right). The detail screen shows a message from 100–103 (LEFT, RIGHT, LEFT, RIGHT), which fits 100 + leg, but the copy into PlPinfo +0x1c4 hasn't been traced. Tested in PCSX2: Van der Sar (250) set to 2 shows LEFT, as 100 + leg predicts; the screen doesn't show two-footedness |
| 16 | 1 | +0x30 |
castno | always 0. source name castno; no reader found |
| 16 | 1 | +0x32 |
face | face number, 0–17,172. confirmed: CDetailManager passes it to CDetailFace::Request (0x2869b4) |
| 16 | 1 | +0x34 |
req_status | required status: band 0–15, looked up in the status table (0x55b970). confirmed: pwkTeam_UpdatePlayerCandidates (0x260cf8) leaves a player out of a candidate list when it is above the club's pwkTeam_Status(). Managers (+0x24) and scouts (+0x22) have the same field. The BPINFO CHECK screen labels the 10,000 cutoff "1mil" (SQB_FORMAT.md) |
| 3 | 1 | +0x36 |
tone | speech tone, 0–3. Read by the event code in SIMPRG.REL; what each value sounds like isn't traced |
| 2 | 8 | +0x37 |
dissatis | sensitivity to 8 causes of dissatisfaction, 0–3. confirmed; see Personality |
| 4 | 1 | +0x3f |
professionalism | picks the starting power range. confirmed |
| 4 | 1 | +0x40 |
pressure | pressure resistance. Read by the match engine (GAMEPRG.REL), not traced |
| 4 | 1 | +0x41 |
loyalty | club loyalty; sets a promoted youth player's contract years. confirmed |
| 4 | 1 | +0x42 |
star | star quality; scales popularity changes. confirmed |
| 4 | 4 | +0x43 |
explodetype | source name explodetype, 0–15 ×4: probably meant for the growth spurts (pwkExplo_*, see explodetype). No label in the developers' editor. Mostly 0 (15,718, 24,808, 27,428 and 27,917 players), more often set for low-ranked players. Kept in the save but read by nothing: only the save's serializers touch PlPinfo +0x1db–+0x1de (its game copy) in the executable or any overlay |
| 3 | 1 | +0x47 |
moti_type | motivation type, 0–7. confirmed |
| 3 | 1 | +0x48 |
cond_type | condition type, 0–7. No reader found outside the debug and save code |
| 5 | 1 | +0x49 |
potential | 0–7; read by plPinfo_InitEditAbil (0x21b2b8) |
| 4 | 3 | +0x4a |
growth | physical, skill and mental growth types, 0–15. confirmed (pwkGUtl_GetPhysical/Skill/MentalGrowCoe, 0x245ef8–0x245f58) |
| 3 | 1 | +0x4d |
travel | travel tolerance, 0–7. confirmed |
| 3 | 1 | +0x4e |
injury_res | injury resistance, 0–7. confirmed |
| 3 | 1 | +0x4f |
recovery | recovery, 0–7. confirmed |
| 4 | 1 | +0x50 |
foul_avoid | foul avoidance, 0–15. Read by the match engine, not traced |
| 4 | 1 | +0x51 |
weak_foot | weak-foot accuracy, 0–15. Read by the match engine, not traced. 163 of the 499 two-footed players have 8 or more, against 324 of the other 27,451 |
| 5 | 1 | +0x52 |
policy | the player's policy type, 0–24. confirmed (pwkTeamType_PolicyInit 0x27033c, also plCombi_* and pwkPromise_*) |
| 3 | 1 | +0x53 |
adapt | adaptability, 0–6. confirmed: pwkTeamType_GetFit (0x270624) looks it up in a table at 0x3996d8 (30, 33, 35, 38, 40, 45, 50) |
| 4 | 1 | +0x54 |
intelligence | always 0 |
| 2 | 1 | +0x55 |
ball_touch | ball-touch type, 0–2. Becomes match parameter 139, which nothing in the match reads |
| 2 | 1 | +0x56 |
dribble_style | 0–3: which of four sets of dribble motions the player uses. confirmed; see The tendencies |
| 2 | 1 | +0x57 |
sleeves | 0 by season, 1 short, 2 long. confirmed; always 0. See Kit style |
| 3 | 1 | +0x58 |
wristband | 0 none, 1–4 a colour. confirmed; 0 in 27,256 players |
| 3 | 1 | +0x59 |
gloves | outfield gloves in cold weather: 0 none, 1–4 a glove texture. confirmed; 0 in 27,187 players |
| 2 | 1 | +0x5a |
gk_cap | the goalkeeper's cap, 0–3: source name nicknack_gkcap. No reader was found, and value 3 showed no cap in PCSX2. 1 in 46 players |
| 3 | 1 | +0x5b |
gk_gloves | goalkeeper's gloves, 0–5. confirmed |
| 1 | 1 | +0x5c |
gk_pants | 1: a goalkeeper wears long pants. confirmed |
| 4 | 1 | +0x5d |
boots | boot colours, 0–14. confirmed |
| 5 | 5 | +0x5e |
style | play styles, 1–22, 0 for none. confirmed. See Play styles |
| 3 | 1 | +0x63 |
flags | bit 1: EU passport. confirmed. Set in 19,333 players |
| 16 | 1 | +0x64 |
skills | bit mask. confirmed. All 16 bits are used; see Skills |
| 3 | 11 | +0x66 |
tendency | how readily the match AI picks each kind of play, 0–4 with 2 neutral: dribble, shoot, carry, feed pass, side change, back pass, killer pass, one-two, clear, press, goalkeeping. confirmed; see The tendencies. Source name ai; no label in the developers' editor. +0x67 is also the row of the affinity table (see Personality). Team combos shift the other ten (pwkPCombo_Add, see explodetype and team combos) |
| 5 | 64 | +0x74 |
ability | 64 ratings (PlAbilNo 0–63), each mapped to 38–99. confirmed. See Abilities |
The 5 bits after the last field are zero in every record.
The last 2,359 player records (25,591–27,949) aren't in any club's
starting squad (OTEAMMEMBER.TBB, teams 3–441). Confirmed:
pwkOteam_InitNonresident (0x24d568) gives them to teams through
_InitCommon(record, first team, last team, first player, last player, per team) (0x33c5d8). It hands out the ids in order and takes each
player's shirt number from +0x2b (or his place in the block if that
is 0):
| Ids | Called from | Teams | What |
|---|---|---|---|
| 25,591–26,040 | mode 0, 0x24d450, at new-game setup (0x110ae4) |
442–459, 25 each | made-up players for the 18 clubs without an OTEAMMEMBER squad, three per league nation: England, France, Germany, Italy, Spain, Netherlands (empirical: each block of 25 has one nationality). They are the third-tier clubs of the tutorial's playoff (England's 442–444 Gillingham, Nottingham and Rotherham, LMASTER_MOD.md). England's first 18 and 16 from 25,623 are the built-in default club (TEAMINIT_FORMAT.md) |
| 26,041–27,949 | mode 2, 0x24d480, from PwkCallbackCommand_VS_Start (0x111478) |
460–542, 23 each | the national teams' fixed squads, used in VS mode. Every block has one nationality |
A national-team record repeats a club player under the same name, as a separate record: Gianluigi Buffon is 3,175 at Juventus and 26,110 (shirt
- in Italy's block, John Terry 101 at Chelsea and 26,046 (shirt 6) in England's. The two can differ (that Terry's height, abilities and age aren't the club Terry's). Nexxus identified these as national-team players.
Tested in PCSX2: with record 26,046 (England's Terry) renamed
NT.Terry.VS, England's starters in a VS match listed DF 6 NT.Terry.VS,
in a 4-4-2 with the rest of the block's names (Robinson, A. Cole,
G. Neville, Ferdinand, Beckham, Gerrard, J. Cole, Wright-Phillips, Owen,
Rooney).
In a career the fixed squads aren't loaded: mode 3 (0x24d4b0) only
sets the 83 team numbers, and squads are called up from the club players
(National team call-ups). Mode 1 (0x24d4d8,
from PwkCallbackCommand_PromotionEnd) isn't traced.
char[19] name, then (bits × count at offset): 8 +0x14 (nationality,
empirical: same range and position as the players'), 5 +0x18 (rank),
3 +0x1c (job, see below), 16 +0x20 (face), 6 +0x22 (age, see
below), 16 +0x24 (required status, confirmed
table lookup), 4 ×4 +0x26 (appeartype), 2 ×5 +0x2a (comptype), 3 ×4
+0x2f, 2 +0x33,
6 +0x34, 3 ×8 +0x35, 8 ×3 +0x3d, 3 ×7 +0x40, 5 ×5 +0x47,
signed 9 ×2 +0x4c, signed 9 ×4 +0x54, 1 ×2 +0x64, and 48 abilities
(5 bits, mapped to 38–99) at +0x66. The 6 bits left over are zero.
The fields from +0x2f on are named in
The developers' staff editors.
char[19] name, then 8 +0x14 (nationality, empirical), 8 +0x18
(age, see below), 5 +0x1c (rank), 16 +0x20 (face), 16 +0x22
(required status), 4 ×4 +0x24 (appeartype), 1 +0x28,
and 49 × 7 bits at +0x29. The first 4 are special searches and the last
45 are abilities, clamped to 31 and mapped to 38–99. The 3 bits left over
are zero. See
The developers' staff editors.
Manager +0x22 and scout +0x18 are the age, stored as is (no offset,
unlike the players' +0x28). Without TEAM_INIT_DATA.TBB,
pwkTeam_Init2 copies manager byte 0x22 (0x25eff4) and scout byte
0x18 (0x25f038) into the slot where the table otherwise puts the
age. Where the table gives it, it equals these fields in all 180 manager
and 108 scout records. Tested in PCSX2: the game shows that value as
the staff member's age, in the Coach and Scout Candidate Lists and for
the club's own staff (TEAMINIT_FORMAT.md). Ages
run 35–55 for managers and 35–58 for scouts.
DEBUGPRG.REL holds the developers' editors for players, managers and
scouts (Param::PinfoEditorTask, MinfoEditorTask, SinfoEditorTask),
with Japanese (cp932) labels. The retail game never loads this overlay
(SNR2_FORMAT.md), but its code and labels name most
of the record.
Abilities (confirmed). A table at DEBUGPRG.REL 0x11128 holds 64
pairs {label, ability number}, numbers 0–63 in order:
| Ability | Label | Ability | Label | |
|---|---|---|---|---|
| 0 | ドリブルスピード dribble speed | 32 | 集中力 concentration | |
| 1 | ドリブル精度 dribble accuracy | 33 | GK適正 GK aptitude | |
| 2 | シュート精度 shot accuracy | 34 | SB適正 | |
| 3 | シュートテクニック shot technique | 35 | CB適正 | |
| 4 | ショートパス精度 short pass accuracy | 36 | WB適正 | |
| 5 | ロングパス精度 long pass accuracy | 37 | DM適正 | |
| 6 | クロスボール精度 cross accuracy | 38 | SM適正 | |
| 7 | ヘディング精度 heading accuracy | 39 | OM適正 | |
| 8 | トラップ trap | 40 | WG適正 (wing) | |
| 9 | キープ力 ball keeping | 41 | FW適正 | |
| 10 | タックル tackle | 42 | 中央適正 centre | |
| 11 | インターセプト intercept | 43 | 左サイド適正 left side | |
| 12 | ボール奪取 ball winning | 44 | 右サイド適正 right side | |
| 13 | マーキング marking | 45–52 | 3-4-3 … 5-4-1 理解度 system understanding | |
| 14 | プレイスキック精度 placekick accuracy | 53 | 速攻理解度 counterattack | |
| 15 | セービング saving | 54 | ポゼッション理解度 possession | |
| 16 | キャッチング catching | 55 | サイド攻撃理解度 side attack | |
| 17 | ハイボール high balls | 56 | 中央攻撃理解度 centre attack | |
| 18 | 飛び出し rushing out | 57 | オフサイド理解 offside | |
| 19 | スピード speed | 58 | プレス理解 pressing | |
| 20 | ダッシュ dash | 59–63 | 攻撃パターンA–E理解度 attack patterns A–E | |
| 21 | ジャンプ力 jump | – | – | |
| 22 | レスポンス response | – | – | |
| 23 | スタミナ stamina | – | – | |
| 24 | キック力 kick strength | – | – | |
| 25 | 当たりの強さ contact strength | – | – | |
| 26 | 統率力 leadership | – | – | |
| 27 | 度胸 nerve | – | – | |
| 28 | 攻撃意識 attack minded | – | – | |
| 29 | 守備意識 defence minded | – | – | |
| 30 | サポート意識 supportiveness | – | – | |
| 31 | 視野の広さ vision | – | – |
This agrees with every ability the game code names (the bars, the
position grid and the system growth) and with all four VPF players
(Ability names). The 34–41 labels name the grid cells:
SB/CB, WB/DM, SM/OM and WG/FW are the side and centre of each row. 59–63
are the five attack patterns the tactics screen calls Left Flank, Right
Flank, Centre Drive, Counter Attack and Possession (messages 1:620–649),
but nothing in the data or the code tells the five apart, so which
letter is which pattern makes no difference
(Ability names). No call to the
three functions that take an ability number (plPinfo_Abil2PSM,
pwkGUtl_GetGrowCoe, pwkGUtl_AddExp) passes 59–63 as a constant;
the match doesn't copy them (The match engine's player
parameters);
and every practice grows all five equally. Their two other readers are
loops over all five: the hexagon window's player rating, and the
travelling salesman's boost for flag 336 (0x21070c, raising each by
40% for the match), which no event sets in the retail game
(EVSDATABIN_FORMAT.md).
A scan for loads at PlPinfo +0x16c, +0x172, +0x178, +0x17e and
+0x184 missed it because it reads them from the team data (+0x18c
= squad slot 0's +0x16c). Besides that, only the hexagon table reads
them, weighting 60 alone. Training's combinations A–E
(practices 12–16) are a different set: they pick their row from the
club's five pattern slots, and rows 12–16 train the left side, right
side, centre, counterattack and possession, the tactics screen's order
(TRAINING_FORMAT.md).
Record fields (confirmed). The player editor draws each field's label
and then reads that field, a few instructions later, in record order
(DEBUGPRG.REL 0x5ad0–0x60a8): 利き足 leg +0x2c, 身長 height, 体重
weight, 基本背番号 shirt, 得意ポジション position, 必要ステータス
required status +0x34, 口調 speech tone +0x36, プロフェッショナル意識
professionalism +0x3f, プレッシャー耐性 pressure resistance +0x40,
クラブ忠誠心 club loyalty +0x41, スター性 star quality +0x42,
モチベーションタイプ motivation type +0x47, コンディションタイプ
condition type +0x48, 将来性 potential +0x49, フィジカル/スキル/精神成長タイプ
growth types +0x4a–+0x4c, 移動耐性 travel tolerance +0x4d,
怪我の少なさ injury resistance +0x4e, 回復力 recovery +0x4f,
ファールしにくさ foul avoidance +0x50, 逆足精度 weak-foot accuracy
+0x51, ポリシー policy +0x52, 環境適応度 adaptability +0x53,
インテリジェンス intelligence +0x54, ボールタッチタイプ ball-touch type
+0x55, ドリブルスタイル dribble style +0x56, 固有プレイスタイル native
play styles +0x5e. The manager and scout editors read their +0x24
and +0x22 under 必要ステータス too.
The editor has no label for +0x30, +0x37 (shown on its dissatisfaction
page), +0x43–+0x46, +0x57–+0x5d or +0x66–+0x70. The retail
game's own labels name +0x57–+0x5d (Kit style).
The manager editor (MinfoEditorTask, title 監督エディット) and the scout
editor (SinfoEditorTask, スカウトエディット) work like the player editor:
each page draws a label and then reads the field. Both take a PlMinfo or
PlSinfo (+0x910 and +0x3ac of the task), which is 4 bytes and then
the record (PlMbase / PlSbase). Offsets below are record offsets.
Manager abilities (confirmed). The table at DEBUGPRG.REL 0x103d8
holds 48 pairs {label, ability number}, numbers 0–47 in order. Nearly
all labels end in 指導力 "coaching ability", left out below:
| Ability | Label | Ability | Label | |
|---|---|---|---|---|
| 0 | モチベーションケア能力 motivation care | 24 | DF | |
| 1 | フィジカルケア能力 physical care | 25 | DM | |
| 2 | 人望 respect | 26 | OM | |
| 3 | 不満ケア能力 dissatisfaction care | 27 | FW | |
| 4 | 選手目利き力 judging players | 28 | 中央適正 centre aptitude | |
| 5 | 能力開発 ability development | 29 | 左サイド適正 left side aptitude | |
| 6 | ユース選手 youth players | 30 | 右サイド適正 right side aptitude | |
| 7 | 若手選手 young players | 31–38 | 3-4-3 … 5-4-1 (the 8 systems) | |
| 8 | 中堅選手 mid-career players | 39 | 速攻 counter attack | |
| 9 | ベテラン選手 veterans | 40 | ポゼッション possession | |
| 10 | ドリブル dribble | 41 | サイド攻撃 side attack | |
| 11 | シュート shot | 42 | 中央攻撃 centre attack | |
| 12 | パス pass | 43 | オフサイド offside | |
| 13 | ヘディング heading | 44 | プレス pressing | |
| 14 | インターセプト intercept | 45 | 攻撃パターン attack patterns | |
| 15 | マーキング marking | 46 | 連携 teamwork | |
| 16 | セービング saving | 47 | セットプレイ set plays | |
| 17 | 飛び出し rushing out | – | – | |
| 18 | スピード speed | – | – | |
| 19 | スタミナ stamina | – | – | |
| 20 | フィジカル physical | – | – | |
| 21 | メンタル mental | – | – | |
| 22 | 攻守意識 attack/defence awareness | – | – | |
| 23 | GK | – | – |
All 31 abilities behind the manager and coach bars (Manager, coach and scout bars) agree with these labels: MOTIV 0, PHYSC 1, POPUL 2, COMMU 3, ASSES 4, TRAIN 5, DRIBB 10 … MARK 15, SAVIN 16, HND 17 (rushing out), SPEED 18 … MENTA 21, ATKDF 22, CENTA 28, FLANK 29/30, FASTB 39 … CLOSD 44, ATTST 45, TEAMW 46, FK 47.
Coach type (confirmed). The editor prints the job (PlMinfo +0xa0,
from record +0x1c) under コーチタイプ "coach type", through the list at
0x10940:
| Job | Label |
|---|---|
| 0 | アシスタントバランス balanced assistant |
| 1 | アシスタント攻撃 attacking assistant |
| 2 | アシスタント守備 defensive assistant |
| 3 | フィジカルコーチ physical coach |
| 4 | GKコーチ GK coach |
| 5 | 監督 manager |
| 6 | ユース監督 youth manager |
This matches what the game does with the jobs (5 and 6 are set at run time, 0–2 share the coaching bars) and the job averages below.
Manager fields (confirmed). The page drawn at DEBUGPRG.REL 0x1c98–0x1f2c and the two after it (0x1f30–0x2214) read these
fields. The first page (0x1560–0x1c94) shows only run-time state
(popularity, promises, age, contract, salary):
| Offset | Bits | Label | Values in the database |
|---|---|---|---|
+0x24 |
16 | 必要ステータス required status | (as before) |
+0x2f |
3 | モチベーションタイプ motivation type | 0–7 |
+0x30 |
3 | 選手起用方針 player selection policy | 0–7 |
+0x31 |
3 | 試合進行方針 match policy | 0–7 |
+0x32 |
3 | 育成方針 training policy | 0–7 |
+0x33 |
2 | 休養方針 rest policy | 0–3 |
+0x34 |
6 | ポリシー policy | 0–24 |
+0x35 |
3 ×4 | 許容範囲 accepted range | 1–7 |
+0x39 |
3 ×4 | 得意範囲 best range | 2–7 |
+0x3d |
8 ×3 | フォーメーション formations | 0–34, list at 0x10868 |
+0x40 |
3 | 攻撃志向 attacking | 1–5 |
+0x41 |
3 | ポゼッション志向 possession | 1–5 |
+0x42 |
3 | 攻撃展開中央サイド attack through centre or sides | 1–5 |
+0x43 |
3 | 攻撃展開左右 attack left or right | 1–5 |
+0x44 |
3 | プレス開始位置 where pressing starts | 1–5 |
+0x45 |
3 | プレス強度 pressing strength | 1–5 |
+0x46 |
3 | オフサイド強度 offside trap strength | 1–5 |
+0x47 |
5 ×5 | 監督攻撃パターンセット attack pattern set | 0–30, list at 0x107e8 |
+0x4c |
9 ×2 | 監督指導可能練習 drills he can teach as manager | all −1 (none) |
+0x54 |
9 ×4 | コーチ指導可能練習 drills he can teach as coach | 52–138 or −1 |
+0x64 |
1 | 実名? real name? (Yes/No) | all 0 |
+0x65 |
1 | モデルパターン model pattern | 0 (1,526), 1 (1,474) |
What the four policies (+0x30–+0x33) and the seven tactical values
(+0x40–+0x46) do in the game is below, after the affinity reader.
The affinity reader confirms +0x2f as well: pwkDissatis_GetAffintyType
takes a manager's column from PlMinfo +0x33.
Where the game keeps the manager (confirmed). PlMinfo is 4 bytes
and then the record, so record offset n is PlMinfo +n+4. My club's
manager is the PlMinfo at PlTeamData +0x4854 (pwkTeam_GetManager,
0x269d08, copies 0xbc bytes from there), so the four policies are at
PlTeamData +0x4888–+0x488b and the seven tactical values at
+0x4898–+0x489e. A computer club's PlTeamData is built by
pwkOteam_GetTeamData with its manager from the database
(pwkOteam_GetManager, 0x24be40), and the match takes a whole
0x49a0-byte copy (PlGiTask::GetTeamData, 0x211258), so the same
offsets hold everywhere.
The four policies (confirmed). The developers call them
plan_appoint, plan_game, plan_grow and prac_off (field table,
python SRC/sles_disasm.py ISO/SLES_541.51 fields plan_ prac_off). The
manager detail screen shows all four: CDetailManager::ConvertManager
(0x2871a0) copies PlMinfo +0x34–+0x37, and
SetupDetailManagerPage1 (0x28b9e0–0x28baf0) prints message
10300 + n, 10310 + n, 10320 + n and 10330 + n of category 201.
The Japanese names, and what PAL's English shows where it differs:
| Value | Selection (+0x30) |
Match (+0x31) |
Training (+0x32) |
Rest (+0x33) |
|---|---|---|---|---|
| 0 | バランス重視 balanced | 積極的に動く act boldly | 中央攻撃重視 centre attack | あまり休まない rarely rest |
| 1 | 選手能力重視 player ability | 戦術変更を多用 change tactics a lot | サイド攻撃重視 side attack | たまに休む rest now and then |
| 2 | チーム力重視 team strength ("Team player") | 選手交代を多用 use subs a lot | ラインディフェンス重視 line defence | 適度に休む rest moderately |
| 3 | 若手重視 young players ("Talented youth") | 臨機応変に対応 adapt as needed | マンマークディフェンス重視 man-marking defence | よく休む rest often |
| 4 | 経験重視 experience | 戦術変更を好む like tactic changes | フィジカル重視 physical | – |
| 5 | コンディション重視 condition ("Tactically-Fit") | 選手交代を多用 use subs a lot (the same as 2) | フォーメーション重視 formation | – |
| 6 | モチベーション重視 motivation | 選手交代を好む like subs | コンディション重視 condition | – |
| 7 | チームフィット重視 team fit ("Suitablity") | あまり動かない rarely change | 実戦重視 match practice | – |
In PBDATA_EU.PAC every value occurs (selection 185–477 managers each,
match 213–455, training 141–546, rest 496–1,027). pbdata.py and the
editor show these names.
Selection. plTeam_RegSel (0x2320a0) picks the starting eleven,
and its fifth argument is always PlTeamData +0x4888: at the start of
the season (pwkTeam_Init2), in pwkTeam_UpdateEquip, for each
computer club (pwkOteam_GetTeamData, SIMPRG.REL 0x130c64), before a
match for the side that isn't my club (PlGiTask::SetTeamData,
0x210a8c), on the tactics screen's automatic line-up
(CTacticsBase::setTeamTacticsAuto, 0x2a7738), and through
plTeam_RegReset (plTeam_ChangeTeamStyleCom, 0x22f2a8). The policy
reaches CalcuStamemPoint (0x22f910), which scores a player for a
position. The policy picks a row of 8 floats at 0x53a4f8:
| Policy | Position fit | Rank × 10 | Physical | Skill | Mental | + condition | + motivation | + fit |
|---|---|---|---|---|---|---|---|---|
| 0 balanced | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
| 1 player ability | 2 | 1.5 | 1 | 1 | 1 | 0 | 0 | 0 |
| 2 team strength | 2 | 1 | 1 | 1 | 1 | 0.2 | 0.2 | 0.3 |
| 3 young players | 2 | 1 | 1.5 | 1 | 1 | 0 | 0 | 0 |
| 4 experience | 2 | 1 | 1 | 1 | 1.5 | 0 | 0 | 0 |
| 5 condition | 2 | 1 | 1 | 1 | 1 | 0.5 | 0 | 0 |
| 6 motivation | 2 | 1 | 1 | 1 | 1 | 0 | 0.5 | 0 |
| 7 team fit | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0.5 |
The first five weights multiply plPinfo_GetPositionFitValue, the
rank (PlPinfo +0x1b0, record +0x18) × 10, and the three growth
stages: _GetPhysical/Skill/MentalGrowTypeValue (0x22f650–0x22f690)
look up the player's age against his growth types (+0x4a–+0x4c),
and the result goes through {0, 2, 4, 6, 8, 10, 13, 16, 19, 22, 25}
(0x53a4e8). Their sum is then multiplied by three factors, each a
curve value plus the row's last three weights: condition (PlPinfo +0x240) and motivation (+0x242), in 10 steps of 6,553, through
0.70–1.15 (0x53a488); T-FIT (+0x29a) in steps of 10 through
0.75–1.10 (0x53a4b0). Last comes GetBasePositionPower, and the
score is halved when PlPinfo +0x20c has a bit of 0x300 set. So
"young players" favours the physical growth stage, "experience" the
mental one, and 5–7 lean on condition, motivation and T-FIT.
The match reads the same byte. GAMEPRG.REL 0xd96e8 (called from
match::MatchManager) takes the period of the match from 0xaacc0
(limits 1,800, 2,700, 3,600, 4,200, 4,800, 5,400 and 7,200 seconds,
the ends of the 30th to 120th minutes) and the goal difference from
0xaadd8 (−3 to +3). Policies 0–1, 2–4 and 5–7 pick one of three 8 × 7
tables (0x249500, 0x249538, 0x249570) for a mode 0–4 (2 when level
or early; 0 when behind late, 4 when ahead late; the third table moves
later and only to 1 or 3). The mode adds a step (0x2495ad, 5 × 5) to
five tactics sliders of each of the side's three formations: when
behind (mode 0), +2 to sliders 0, 1, 4 and 5; when ahead (mode 4), −2,
−2, −1 and −1 there and −1 to the offside trap. GAMEPRG.REL 0xf3cb0
also switches on it, picking one of three 8 × 7 word tables by period
and score (policies 0, 2 and 5 0x2495d8; 1, 3 and 6 0x2496a0; 4 and
7 0x249768) for what looks like the substitutions. Both groupings fit
the match policy's names (act boldly and change tactics a lot together;
"rarely change" in the mildest group) better than the selection's,
yet both read +0x4888.
Match. Nothing reads +0x31 (PlMinfo +0x35, PlTeamData +0x4889)
except the detail screen and the save code (SAVEPRG.REL). The match
reads the selection byte where this one's names would fit (above), so
it may be a slip in GAMEPRG.REL; not tested.
Training and rest. The Training screen's coach plan
(SIMPRG.REL 0xf06a8, in WP::CTrainingTask; called from
0xecbe8, the branch that sets task +0xf48 to 1) fills the 8 slots
at task +0x198–+0x1b4 from my club's manager: the training policy
picks a row of 7 practices from SIMPRG.REL 0x228cc0 (6 rows; policies
6 and 7 use row 5), and a 0 there means the current formation's system.
Slot 4 is always 27 (rest).
| Row | Practices (PlPracNo, TRAINING_FORMAT.md) |
|---|---|
| 0 | possession, centre attack, power, stamina, set plays, practice match, teamwork |
| 1 | direct play, side attack, offside trap, stamina, set plays, practice match, teamwork |
| 2 | pressing, pressing, covering, offside trap, teamwork, practice match, covering |
| 3 | speed, power, stamina, speed, power, stamina, practice match |
| 4 | system, system, pressing, offside trap, practice match, teamwork, system |
| 5 | practice match, teamwork, practice match, practice match, set plays, set plays, teamwork |
The rows don't follow the names from 3 on: man-marking (3) gets the
physical row, physical (4) the formation row, and formation, condition
and match practice (5–7) the match row. The rest policy changes the last
two slots: 0 keeps the row, 1 puts 26 (conditioning) in the last, 2
puts 27 (rest) in the last, and 3 puts 26 in the one before and 27 in
the last. No other reader of +0x32 or +0x33 was found, so the
policies don't change how a computer club trains.
The tactical values (confirmed). plTeam_SetStyleTeamParam
(0x22ed28) sets the 7 tactics sliders of each formation
(PlTeamData +0x4260 + 0x70 × formation, tactics ×7, 0–6) to the
manager's value (+0x4898–+0x489e: attacking, possession, centre or
sides, left or right, where pressing starts, pressing strength, offside
trap) plus a step from the table at 0x534438. The step's column comes
from the strength ratio, own plTeam_GetStrength (+5 for the home side)
over the opponent's: up to 0.79 column 0, up to 0.89 column 1, up to
1.09 column 2, up to 1.19 column 3, and above 1.19 column 0 again, so
column 4 is never used.
| Slider | Column 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| attacking | +2 | +1 | 0 | −1 | −2 |
| possession | −1 | 0 | 0 | 0 | +1 |
| centre or sides | +2 | +1 | 0 | −1 | −2 |
| left or right | 0 | 0 | 0 | 0 | 0 |
| pressing starts | −1 | 0 | 0 | 0 | +1 |
| pressing strength | +1 | 0 | 0 | 0 | −1 |
| offside trap | −1 | 0 | 0 | 0 | +1 |
It runs with plTeam_RegSel at the same places (season start,
pwkTeam_UpdateEquip, each computer club, and before a match for the
side that isn't mine). Which end of a slider means more attacking isn't
traced, so whether the clubs much stronger than their opponent are
meant to get column 4 is open. pbdata.py allows 1–5; the sliders take
0–6.
The policies (confirmed). The 25 policies are the cells of a 5 × 5
grid with two axes: counter to possession, and individual to
organisation (the player editor's カウンター~ポゼッション and
タレント~チーム). A player's own point on the grid is in the save
(PlPinfo +0x296, +0x298,
SAVE_FORMAT.md). The table at
0x555340 (pwkTeamType_GetPolicyBseExp) gives each policy's centre:
policy p is in row p ÷ 5 and column p mod 5, one cell is 16,383 on
each axis.
| Row (policy ÷ 5) | Counter-to-possession point (+0x296) |
Column (policy mod 5) | Individual-to-organisation point (+0x298) |
|
|---|---|---|---|---|
| 0 (0–4) | 65,535, counter | 0 | 0, individual | |
| 1 (5–9) | 49,151 | 1 | 16,383 | |
| 2 (10–14) | 32,767 | 2 | 32,767 | |
| 3 (15–19) | 16,383 | 3 | 49,151 | |
| 4 (20–24) | 0, possession | 4 | 65,535, organisation |
So policy 0 is counter and individual, 12 the middle, 24 possession and
organisation. Policy 25 (an entry at the end of the table, at policy
10's point) makes pwkTeamType_GetFit return 100 for any manager; no
record in the database uses it (players and managers use 0–24, each
value 859–1,441 players and 92–184 managers). pwkTeamType_Exp2Policy
(0x270260) turns a point back into a policy.
The manager's two sets of 4 range values (+0x35 accepted, +0x39
best) draw a diamond around his policy's centre. In
pwkTeamType_CheckPolicyAllow (0x2708f0) each value v reaches
v × 16,383 ÷ 2, half a cell per unit: the 1st toward counter, the 2nd
toward possession, the 3rd toward individual and the 4th toward
organisation. A point is inside when it is within those reaches on both
axes and below the straight line joining the two reaches of its
quadrant. The readers:
| Address | Symbol | What it does with the ranges |
|---|---|---|
0x2703d0 |
pwkTeamType_GetFit |
a player's fit with the manager (the T-FIT bar): 75 if his point is inside the best diamond, else 25 if inside the accepted diamond, else 0; plus (131,070 − the distance from the manager's centre, both axes added) ÷ 2,621, up to 50; at most 100 |
0x23a620 |
pwkDissatis_MPolicy |
the share of your squad whose points are inside the manager's accepted diamond, as a percentage, feeding the manager's dissatisfaction |
0x270770 |
pwkTeamType_PolicyMov |
moves a player's point and recomputes his policy with Exp2Policy |
Most managers' best values are smaller than their accepted ones (5,721 of the 12,000 pairs smaller, 3,630 equal, 2,649 larger; averages about 2.85 and 3.15), so the best diamond is usually the inner one.
The lists the indices go into (confirmed, the editor prints the entry):
- Attack patterns,
0x107e8: 0 なし none, then 6 each ofLS00–LS05,RS,CT,CA,PO(labels likeLS00_00). The letters fit the tactics screen's five kinds: left flank, right flank, centre, counter attack and possession. In the database, slots 0–2 are always set and 3 and 4 are often 0 (601 and 2,153 managers). - Formations,
0x10868: 35 labels such as4-4-2 dv 3and4-3-3 tv 1, grouped by system 3-4-3 … 5-4-1. Entry 34 repeats entry 31's label (5-3-2 tv 1), probably a slip for 5-4-1. - Drills,
0x105b8: 139 training menu items: 0–7 the systems, 8–28 team training, 29–31 growth policies (長所を伸ばす, 平均的に, 短所を補足), 32–51 forward, defender, goalkeeper, physical and mental training, 52–73 play styles, 74–138 individual drills (シュート, 1000本セーブ, 呼吸法 …). The database's coaches use only 52–138.
Scout abilities (confirmed). The table at 0x13240 pairs 45 labels
with numbers 4–48, the index into the 49 seven-bit values at +0x29. So
scout ability n is value n + 4:
| Ability | Label | Ability | Label |
|---|---|---|---|
| 0 | クラブ交渉能力 club negotiation | 8–20 | GK, LSB, RSB, CB, LWB, RWB, DM, LSM, RSM, OM, LWG, RWG, FW |
| 1 | 選手交渉能力 player negotiation | 21 | MC (managers) |
| 2 | 金銭交渉能力 money negotiation | 22 | AC (assistant coaches) |
| 3 | 現役選手探索能力 search: professionals | 23 | PC (physical coaches) |
| 4 | ユース探索能力 search: youth | 24 | GC (GK coaches) |
| 5 | 新人探索能力 search: newcomers | 25 | YM (youth managers) |
| 6 | 中堅探索能力 search: mid-career | 26–31 | England, France, Germany, Italy, Spain, Netherlands |
| 7 | ベテラン探索能力 search: veterans | 32–44 | Western, Central, Eastern, Northern Europe, South America A, B, North, West, East and South Africa, North/Central America and Caribbean, East Asia, South Asia and Middle East, Oceania |
The labels of 8–44 end in SAbil. The 11 scout bars agree (CLB 0 …
VETER 7, MANAG 21 … GCOAC 24), and the 12th, unlabelled value the screen
computes is 25, youth managers. Empirical check: for each of the 14
most common nationalities, the scouts' best region on average is their
own (England 85 against 60 for the other regions, Brazil South America A,
Argentina South America B, Japan East Asia, Scotland, Belgium and
Portugal Western Europe).
Scout fields (confirmed). The editor (0x97a0–0x98b0) reads 実名?
real name? at +0x28 (all 0 in the database) and 必要ステータス at
+0x22. Under 特殊検索 "special search" it loops over +0x29–+0x2c
and prints each value below 36 from the list at 0x13428, or なし. These
36 are play-style types: Centre Forward, Moving, Post Player, Attacker,
Dynamo, Crusher, Covering, Central MF, Side Attacker, Cut-in, Defensive
Side, Sweeper, Libero, Stopper, Centre Back, Orthodox, Libero GK,
Playmaker, Second Striker, Operaio, Shadow Striker, Wing, Estremo, Dash
Out, Last Fort, High Tower, Attacking GK, Ace Striker, Crosser, Speed
Star, Line Conductor, All-rounder, Ace Killer, Wall, Super Dribbler,
Regista. In the database the first search is always set (0–35), and 322,
579 and 816 scouts have none (36) in the other three.
Not labelled by either editor, but named by the developers' field
table (below): manager +0x18 rank
(0–15), +0x20 faceno, +0x26 appeartype (×4, always 0) and
+0x2a–+0x2e comptype (×5, 0–3 each, like the players' eight
dissatisfaction sensitivities comptype +0x37), and scout +0x1c
rank (0–15), +0x20 faceno and +0x24 appeartype (×4, always 0).
Empirical: the face numbers run 17,173–20,172 for the 3,000 managers
in order and 20,173–21,172 for the 1,000 scouts, continuing the players'
faces (0–17,172). Which code reads the staff ranks, faces, appear types
and manager comptypes isn't traced. The players' +0x30 (castno) is 0
in every record.
SLES_541.51 holds the field table of the developers' structure editor
(PwkEditTask, reached from pwkEdit_CallPwkEdit 0x23c3f8): rows of
0x2c bytes naming the members of about 170 of the game's structures, with
each member's offset, size, setter and getter (0x23c420–0x242bc8),
range and count. Confirmed from the table: the player, manager and
scout records start at 0x53dfe8, 0x53fdd8 and 0x53f900, and every
offset and count matches the decoders. Two members group several of
pbdata.py's fields: the manager's _policyset and the scout's abil
×49 (4 special searches, then 45 abilities). pbdata.py's names follow
the editors' labels where there are any, and these names otherwise:
| Record | Offset | Source name | Range ×count | pbdata.py |
|---|---|---|---|---|
| player | +0x14, +0x18, +0x1c |
nationality, rank, apos0 | 0–146, 0–16, 0–13 ×3 | nation, rank, position |
| player | +0x28–+0x2c |
age0, height, weight, unumber, leg | – | age, height, weight, shirt, leg |
| player | +0x30 |
castno | – | castno |
| player | +0x32, +0x34 |
faceno, needstatus | – | face, req_status |
| player | +0x36 |
tone | 0–6 | tone |
| player | +0x37 |
comptype | 0–3 ×8 | dissatis |
| player | +0x3f–+0x42 |
professional, pressure, loyalty, star | 0–15 | the same |
| player | +0x43 |
explodetype | 0–15 ×4 | explodetype |
| player | +0x47–+0x49 |
motitype, condtype, futuretype | 0–7, 0–7, 0–31 | moti_type, cond_type, potential |
| player | +0x4a–+0x4c |
pgrowtype, sgrowtype, mgrowtype | 0–15 | growth |
| player | +0x4d–+0x51 |
awaytype, weaktype, tiredtype, wildtype, reverseaccuracy | 0–7 ×3, 0–15 ×2 | travel, injury_res, recovery, foul_avoid, weak_foot |
| player | +0x52–+0x56 |
policy, envtype, intelligence, balltouch, dribblestyle | 0–31, 0–7, 0–15, 0–2, 0–3 | policy, adapt, intelligence, ball_touch, dribble_style |
| player | +0x57–+0x5d |
nicknack_sleeve, _wristband, _gloves, _gkcap, _gkglove, _pants, _spike | 0–2, 0–4, 0–4, 0–3, 0–5, 0–1, 0–14 | sleeves, wristband, gloves, gk_cap, gk_gloves, gk_pants, boots |
| player | +0x5e, +0x63, +0x64 |
_pstyle_native, core_flag, skill_flag | 0–22 ×5, 0–7 | style, flags, skills |
| player | +0x66, +0x74 |
ai, abil | 0–4 ×11, 0–100 ×64 | tendency, ability |
| manager | +0x18, +0x1c, +0x20, +0x22 |
rank, coachtype0, faceno, age | 0–16, 0–7, –, 0–32 | rank, job, face, age |
| manager | +0x26, +0x2a |
appeartype, comptype | 0–9 ×4, 0–3 ×5 | appeartype, comptype |
| manager | +0x2f–+0x33 |
motitype, plan_appoint, plan_game, plan_grow, prac_off | – | moti_type, select_policy, match_policy, training_policy, rest_policy |
| manager | +0x34, +0x3d |
_policyset (a structure: policy, okrange ×4, goodrange ×4), _formation | ×3 | policy, policy_range, policy_best, formation |
| manager | +0x40–+0x46 |
offensetype, possession, offensecenterside, offenseside, presspos, presshard, offsidehard | 0–7 | attacking … offside |
| manager | +0x47, +0x4c, +0x54 |
_favoriteattackid, _mpraclist, _praclist | 0–29 ×5, −1–139 ×2, ×4 | attack_pattern, manager_drill, coach_drill |
| manager | +0x64–+0x66 |
real_name_flag, model_pattern, abil | 0–1, 0–1, 0–100 ×48 | real_name, model_pattern, ability |
| scout | +0x1c, +0x20, +0x24 |
rank, faceno, appeartype | 0–16, –, 0–10 ×4 | rank, face, appeartype |
| scout | +0x29 |
abil | 0–100 ×49 | search, ability |
The table's ranges are the developers' editor limits, not checks the game
makes: the database stays inside them, often well inside (tone 0–3,
policy 0–24). The same table names many other structures (PlPinfo,
the team data, the club's finances, the schedule), so it is worth reading
before decoding a new one.
python SRC/sles_disasm.py ISO/SLES_541.51 fields # every structure, one block each
python SRC/sles_disasm.py ISO/SLES_541.51 fields explodetype ai # rows by nameConfirmed from the readers in SLES_541.51 (each reads the field at
PlPinfo +0x198 + its record offset):
| Field | Reader | What the value does |
|---|---|---|
dissatis +0x37–+0x3e |
pwkDissatis_Money, _Player, _Staff, _Position, _Match, _Policy, _CompeEnd, _Facility (0x237420, 0x2376a8, 0x237af8, 0x237fbc, 0x238350, 0x238558, 0x23a930, 0x238a84) |
each scales that cause of dissatisfaction by 1.0, 1.1, 1.2 or 1.3 (tables such as 0x391150) |
professionalism +0x3f |
plPinfo_InitPower (0x21af98) |
power (+0x24c, 0–1000) starts at a random value in a range from the table at 0x532a38: 800–900 for 0, rising to 980–1000 for 15 |
loyalty +0x41 |
pwkTeam_GetYouthPromoteConyear (0x2714cc) |
value / 2 picks the row for a promoted youth player's contract years |
star +0x42 |
plPinfo_GameCheck, MonthEndCheck, YearEndCheck, ClubTitleCheck, UpDownKeymanCaptaion |
added to the rank, capped at 30, picks the size of each popularity change (plPinfo_ChangePop) |
moti_type +0x47 |
plPinfo_CalcMotiYear (0x21bfe4) |
yearly motivation gain from the table at 0x390860: 3000, 4500, 1500, 4500, 1500, 1500, 3000, 4500 |
moti_type, tendency +0x67 |
pwkDissatis_GetAffintyType (0x2370d0) |
how two people get on: an 8 × 8 table of u16 at 0x390fd0, row = one person's +0x67, column = the other's +0x47 (a manager's comes from his PlMbase +0x2f). Values 0–4 |
travel +0x4d |
plPinfo_CalcMatchMovePowerDecline (0x2197dc) |
with the age, the power lost to travelling for a match (the effect is read from the function's name) |
injury_res +0x4e |
plPinfo_CheckKega (0x21aa64) |
with the age, fatigue, power and motivation, the injury check |
recovery +0x4f |
plPinfo_CalcGTired, CalcPracTired, CalcRecover |
picks a pair of multipliers from 0x532870: (1.1, 0.9), (1.1, 1.0), (1.0, 0.9), (1.0, 1.0), (1.0, 1.1), (0.9, 0.9), (0.9, 1.0), (0.7, 0.9) |
The match engine converts some of these fields into its own player
parameters (below). What the
match then does with them isn't traced. +0x57–+0x5d are the kit
style (below).
Before a match, GAMEPRG.REL 0x143908–0x143968 builds a 0x108-byte
record for each of a team's 25 players (team +0x20 + 0x108 × slot).
0x1435a8 and the helpers it calls fill a byte array at record +0x37:
parameter k is record +0x37 + k. 0x146480 reads parameter k and
returns 0 for a value of 100 or more. A record field +n sits at
PlPinfo +n+0x198, and ability a at PlPinfo +0xa + 6a (a halfword; the
conversions add 15 to it).
Confirmed from the code below. Three tables of 16-byte rows
{s32 parameter, s32 ability or −1, u32 add, s32 max} turn abilities
into parameters (ability + add, then clamped to max), and the rest is
written directly:
| Parameters | Source | Address |
|---|---|---|
| 13 | ability 23 (stamina) + 15 | 0x1435c0 |
| 26–42, 46–57 | abilities 0–25 + 15, in order, except: 32 and 33 both ability 6, 41 ability 22, 42 ability 14, 54 ability 25 | table 0x28bd08, 0x142ee0 |
| 43, 44, 45 | 0.7 × ability 14 (placekick) + 0.3 × ability 5 (long pass) + 15 | 0x142f60 |
| 58 | 7 − injury_res (+0x4e) |
0x142fd0 |
| 59 | weak_foot (+0x51) / 2 |
0x142fdc |
| 60 | recovery (+0x4f) |
0x142fe8 |
| 61, 67–70 | abilities 26, 28–31 + 15 | table 0x28bf38, 0x143038 |
| 62 | (15 − foul_avoid (+0x50)) / 2 |
0x1430c0 |
| 63 | professionalism (+0x3f) / 2 |
0x143100 |
| 64 | pressure (+0x40) / 2 |
0x14310c |
| 65, 66 | ability 32 (concentration) / 14, ability 27 (nerve) / 14 | 0x143118 |
| 71 | 1 if professionalism ≥ 3 |
0x143138 |
| 72, 75, 76 | abilities 53, 58, 57 + 15 | table 0x28bf38 |
| 73 | 0.7 × ability 55 + 0.3 × ability 54 + 15 | 0x143148 |
| 74 | 0.7 × ability 56 + 0.3 × ability 54 + 15 | 0x143180 |
| 77 | PlPinfo +0x294 (clamped to 25) |
0x1431c4 |
| 78–89 | 0.7 × an aptitude + 0.3 × a system fit (ability 45 + the third argument; 0x1435a8 passes 4, the 4-4-2) + 15. The aptitudes, in parameter order: 33 GK, 35 CB, 34 SB, 36 WB, 37 DM, 39 OM, 38 SM, the larger of 40 WG and 41 FW, 40 WG, 42 centre, 43 left side, 44 right side |
table 0x28c048, 0x143228 |
| 90–111 | 99 for the play style held in PlPinfo +0x278 (compared with 1–22; presumably the style the player plays), 0 for the others |
0x1433e8 |
| 112–127 | skill bits 0–15 (plPinfo_IsSkill), 0 or 1 |
0x143430 |
| 128–138 | the 11 tendency values (+0x66), a value of 5 or more becomes 4 |
0x143490 |
| 139 | ball_touch (+0x55), 3 becomes 2 |
0x1434d8 |
| 140 | dribble_style (+0x56), 4 or more becomes 2 |
0x1434f8 |
| 141–149 | 1 if ability 1 (dribble skill) is at least 0x50 | 0x143518 |
| 153–155, 159–162 | the kit style: boots, gloves, gk_cap, wristband, gk_gloves, gk_pants, sleeves |
0x143558 |
Parameters 150–152 and 156–158 are set to 0. Abilities 59–63 (the attack
patterns) and 45–52 (the system fits, except through 78–89) are not
copied. Neither are tone, loyalty, star, explodetype, moti_type,
cond_type, potential, growth, travel, policy, adapt and
intelligence; those are only used outside the match.
The team-combo bonuses (pwkPCombo_Add) reach the match through
parameters 128–138.
python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408 # Beckham: every field by name
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x5ad0 400 --sles ISO/SLES_541.51Confirmed from the code (GAMEPRG.REL):
| Address | What it shows |
|---|---|
0x97b28 |
copies the 163 parameters (record +0x37 on) to +0x4 of the match's per-player object. +0xa7 holds a modifier for each, cleared by 0x97af8 and changed by 0x97aa8 |
0x9bdb8 |
the getter the match uses: base (+0x4 + k) plus modifier (+0xa7 + k), plus situation bonuses for some parameters, clamped to 0–99. It has 240 calls, nearly all with a constant k |
0xa84a0 |
at match start, copies a tuning table of 1,951 8-byte entries (u32 kind × 2²⁴ + index, then the value) from 0x270000 to the buffer at *0x249194. 0xa8578 returns entry n's value |
The record's own getter 0x146480 (above) is only used by its 13 calls
in 0x146530–0x146c00, where 0x146560 picks a player's best play
style from parameters 0x5a + n. Every parameter from 26 to 140 except
61, 64, 113 and 139 is read through 0x9bdb8
(below). Parameter 139, the ball touch, has
no reader.
python SRC/snr2.py xref ISO/DLL/GAMEPRG.REL 0x9bdb8 # the 240 calls
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x9bdb8 120 --sles ISO/SLES_541.51python SRC/snr2.py callers ISO/DLL/GAMEPRG.REL 0x9bdb8 fb lists the 240
calls with the parameter each passes (236 pass a constant) and the
fb:: class whose code makes the call, or reaches it through other
calls. The classes come from their run-time type information
(SNR2_FORMAT.md). That a class reads a
parameter is confirmed; what the class does is read from its name,
and how much the value changes the outcome isn't traced. "Helper" is a
PlayerAI function reached only through virtual calls. 109 calls have no
fb:: class this way, and 45 no class at all.
Four shared helpers do much of the reading:
| Helper | What it is | Parameters |
|---|---|---|
0x98a30 |
rates a kick by its kind (the byte at +0x1a of its argument, kinds −1 to 41, jump table 0x26e280; see The kick kinds); behind every DecisionFactor*_NonInterceptionRate and _NonDFBlockRate class, the chance that a pass, cross or one-two isn't intercepted and a shot isn't blocked. Its sub-helpers 0x98f50–0x99280 each read one skill. Several kinds share one call (0x98e24), so callers credits it only with 30 |
5, 28 shot skill, 29 shot technique, 30 short pass, 31 long pass, 32/33 cross, 34 header, 37 tackle, 42 placekick, 46 saving, 59 weak foot; skills 116–118, 120, 123–125, 127 |
0x9bc38, 0x9bcf8 |
probably a duel: the first reads ball keeping and the second tackle, both with the two contact strength parameters | 36 ball keeping, 37 tackle, 54 and 57 contact strength |
0x99520 |
the player's value for play style n + 1: argument n (0–21) reads parameter 90 + n, −1 returns 0 (a switch, jump table 0x26e3a0) |
90–111 |
0x99340, 0x99468 |
the aptitude for a position: 0x992c8 maps it to one of 78–89 |
78–89 |
A kick request carries its kind as a signed byte at +0x1a. Two
switches give each kind a meaning (confirmed from the code):
0x58000(kind, variant) returns an entry of the kind's variant list, 12-byte entries{char *name, ...}. The variant is byte+0x25of theGAMEDATA.BINrecord the request names at+0x1c(0x45f44–0x45f84,GAMEDATA_FORMAT.md). Most kinds list the foot and body surfaces (INSIDE, OUTSIDE, HEEL, SOLE, TOE, INSTEP, INFRONT, OUTFRONT, CHEST, HEAD_STRONG, HEAD_WEAK, SLIDING, CHIP), but some lists name the kind.0x98a30rates the kick from the kicker's parameters, by kind + 1 through the jump table at0x26e280.
| Kind | Variant list | Rated by |
|---|---|---|
| −1, 0, 5, 10, 27, 34, 36, 38, 41 | surfaces (10: UNDERHAND, OVERHAND, a goalkeeper's throw; 27: KICKOFF) | short pass (30) |
| 1, 2 | surfaces | short pass × the playmaker skill (127) |
| 3, 4, 11, 35 | surfaces (11: INSTEP, SOUTH_AMERICAN, a goalkeeper's kick) | long pass (31) |
| 6–9 | surfaces (9: UNDERHAND, OVERHAND) | cross: 32 and 33 blended 0.9/0.1, 0.4/0.6, 0.7/0.3, 0.4/0.6. Both hold the cross ability, so the blends differ only by the parameters' match modifiers |
| 12 | SINGLE_HAND, DOUBLE_HAND and their _PK forms: a catch |
saving (46) × the one-on-one keeper skill (117) |
| 13 | HAND, BODY, KICK and their _PK forms: a save |
the same |
| 14–18 | surfaces plus OVERHEAD, JUMPING_VOLLEY, HEEL_VOLLEY: shots | shot skill (28) × the factors of the one-on-one finisher, acrobatic shot, one-touch shot and goal machine skills (116, 123–125), kept to 0–99; kind 14 averages it with short pass, kinds 15, 16 and 18 with shot technique (29), kind 17 uses it alone |
| 19–21 | surfaces | tackle (37) |
| 22, 23, 30, 31, 33 | surfaces; 30, 31, 33: THROWIN | always 99 |
| 32 | LONGTHROW | long pass × the long throw skill (118) |
| 24–26 | surfaces; 25: LOST; 26: COLLISION | parameter 5 (source not traced) |
| 28, 37, 39, 40 | 28: KICKOFF; 40: four foot surfaces only | placekick (42) |
| 29 | surfaces plus WEAK | shot skill (28) alone |
After the switch, unless the kind has flag 0x40 (the throw-ins,
below), the rating is scaled by how the ball is
struck, read from the same
GAMEDATA.BIN record (+0xc bits 16–18: 0 the right foot, 1 the left,
2 the head, 3 the chest): parts 2 and up by min(header (34) + 20, 100) /
100, so a chest touch counts as a header too, and a kick with the
player's weak foot by 0.14 × parameter 59 (weak_foot / 2, 0–7).
Confirmed: 0x97be0 maps the player's leg value through
0x26e060 (0, 1, −1, −1) to the weak foot's part, so a left-footer
(leg 0) is weak with part 0 and a right-footer with part 1, and a
two-footed player (leg 2, 3) is never scaled; 0x97c00 maps it through
0x26e070 (1, 0, 2, 2) to the strong foot. The multiplications use the
R5900's three-operand mult (0x98ed0, 0x98f08).
Two kinds are stored as constants (confirmed): kind 38 by the
corner and free-kick plays' code (0x6f260, reached from PlayCKKick
and PlayFKKick), and kind 25 by fb::Player's code (0x8083c,
0x8b78c). Kinds 4 and 10 are stored at 0x6001c–0x601dc and
0x6af40–0x6b170, in code reached from the pass, clear and cross
intentions. Most kinds are copied from a candidate record (+0x18, a
halfword) that the intentions choose from.
Confirmed. A candidate gets its kind from the first stack argument of one of six builder functions, and small wrapper functions call each builder with a constant kind. The wrappers' kinds and their callers:
| Builder | Kind of kick | Kinds passed | Called from |
|---|---|---|---|
0x58f00, 0x5b148 |
short pass | 27, 30, 34, 36, 38 | the restart wrappers below |
0x60738, 0x63350 |
through pass | 1 (0x630e8), 2 (0x647f8), 33 |
IntentionFrontPass, IntentionKillerPass |
0x5c4f8, 0x5eec8 |
long pass | 3 (0x5ece8), 11 (0x6b310, 0x6b440), 35, 37, 39 |
IntentionFeedPass, IntentionKillerPass, IntentionSideChangePass; 11 from PlayEffector |
0x650d0, 0x661a8 |
cross | 6, 7 (0x66030, 0x66f80), 8, 9 (0x67448, 0x67920), 32 |
6 and 7 IntentionPinPointCross, 8 and 9 IntentionLinerCross |
0x67c20 |
shot | 14, 16 (0x68c10, 0x69090), 15, 17 (0x68e50, 0x692d0), 18 (0x69510), 40 |
14 and 16 IntentionTargetShoot, 15 and 17 IntentionPowerShoot, 18 IntentionLoopShoot |
0x6b580 |
keeper | 12, 13 (0x6c548, 0x6c3f0) |
the intentions' shared code |
IntentionTargetShoot picks 14 or 16 by a weighted random draw
(0x97910 against 0.5, 0xed394–0xed578), so the two are the same
shot in two forms; the other pairs are probably chosen the same way.
The restarts (confirmed):
| Play | Field | Kinds |
|---|---|---|
PlayCKKick |
+0x98, set by 0x105700 from the play's code at +0x58 |
36 for codes 0xd01 and 0xd03–0xd06, else 37. 0x1055c0 sends 36 to the short-pass wrapper (0x1062e8 → 0x6de30) and 37 to the long-pass wrapper (0x6ec30), aimed with the placekick mixes (0x8e818) |
PlayFKKick |
+0x98, set by 0x105df8 |
39 for code 0xe7f or 0xf00, 38 for 0xf01–0xf05 and the rest, and 40 when +0x5c (masked with 0xfffeffff) is 0xb or more. 40 goes to the shot wrapper 0x6f758 |
PlayTIThrow |
– | 0x107e90 calls the kind 30 wrappers (0x6c7c8, 0x6c900) and the kind 32 ones (0x6cbb0, 0x6cce8). Kind 33's wrapper (0x6d0d0) is reached only from the intentions |
So 36 is a short corner and 37 a corner cross; 38 a free kick passed short, 39 a free kick crossed or played long, 40 a shot from a free kick; 30 a short throw-in, 32 a long throw and 33 a throw-in played through.
Kinds 27, 34 and 35 also have wrappers (0x6d380; 0x6d5e8/0x6d738;
0x6d9e8/0x6db18), but these are reached only through two switches
that re-plan a request by its kind (0x4bcb0, table 0x267e10,
kinds −1 to 41; and 0x53954–0x53a1c), so the request already has
its kind. 28, 29 and 31 have no wrapper: only a case in 0x4bcb0
(0x52040, 0x52238, 0x50390). Where these six kinds are first set
wasn't found. No constant 27, 28, 29, 31, 34 or 35 is stored at +0x1a
or +0x18, and the kick-off, goal kick and penalty plays (PlayKOKick,
PlayGKKick, PlayPKKick) only send the player commands
(0x113d98–0x113dc0, 0x113cb0/0x113d70, 0x114320).
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x58000 12 # the variant switch (table 0x268010)
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x98a30 260 # the rating switch and the scalingConfirmed: GAMEPRG.REL 0x29f680 holds 41 entries of 12 bytes,
{s32 kind, f32, u32 flags}, one per kind. The table is zero in the
file. The overlay's static initialiser 0x704f0 fills it in kind order:
it builds kinds 0–4, 6–10, 14–18 and 30–40 in code, and copies kinds 5,
11–13 and 19–29 from preset records at 0x2696cc–0x269774. The float
is either 1/(0.5994 × mg::video_vsync_multiplier) or a much smaller
value (0.0417 at a multiplier of 1); what it scales wasn't traced. About
150 places in the match code read the flags with the kind as the index.
| Kind | Flags | Kind | Flags | Kind | Flags | Kind | Flags |
|---|---|---|---|---|---|---|---|
| 0 | 0x3001 |
11 | 0x2100 |
22 | 0x5200 |
33 | 0x00c4 |
| 1 | 0x3004 |
12 | 0x3d20 |
23 | 0x5200 |
34 | 0x2081 |
| 2 | 0x2104 |
13 | 0x3c20 |
24 | 0x3200 |
35 | 0x2182 |
| 3 | 0x2102 |
14 | 0x3810 |
25 | 0x1e00 |
36 | 0x0081 |
| 4 | 0x2102 |
15 | 0x3810 |
26 | 0x3e00 |
37 | 0x018a |
| 5 | 0x3920 |
16 | 0x3910 |
27 | 0x2081 |
38 | 0x2081 |
| 6 | 0x2108 |
17 | 0x3810 |
28 | 0x2890 |
39 | 0x218a |
| 7 | 0x2008 |
18 | 0x3910 |
29 | 0x2890 |
40 | 0x2890 |
| 8 | 0x2108 |
19 | 0x6e20 |
30 | 0x00c1 |
||
| 9 | 0x2008 |
20 | 0x6e20 |
31 | 0x00c1 |
||
| 10 | 0x2001 |
21 | 0x6e20 |
32 | 0x01ca |
What the bits mean. Where a class is named, it reads that bit (confirmed). Each meaning comes from the kinds that carry the bit, their variant lists and their ratings (empirical):
| Bit | Kinds | Meaning |
|---|---|---|
0x1 |
0, 10, 27, 30, 31, 34, 36, 38 | a short pass (rated by short pass, or a throw) |
0x2 |
3, 4, 32, 35, 37, 39 | a long ball (rated by long pass, the long throw or placekick) |
0x4 |
1, 2, 33 | a through pass (rated by short pass × the playmaker skill) |
0x8 |
6–9, 32, 37, 39 | a cross: read by DecisionFactorCross_PositionNearnessToEndLine, _NearGoalTeammates, IntentionPinPointCross and IntentionLinerCross |
0x10 |
14–18, 28, 29, 40 | a shot (rated by shot skill or placekick) |
0x20 |
5, 12, 13, 19–21 | – (the keeper's catch and save, the tackles, kind 5) |
0x40 |
30–33 | a throw-in (the THROWIN and LONGTHROW variant lists); turns off the scaling above |
0x80 |
27–40 | a restart: kick-off, throw-ins, set pieces |
0x100 |
2–6, 8, 11, 12, 16, 18, 32, 35, 37, 39 | probably a ball in the air: of each pair of otherwise equal kinds, only one has it (crosses 6 and 8, not 7 and 9; shots 16 and 18, not 14, 15 and 17; through pass 2, not 1) |
0x4000 |
19–23 | the tackles and the two kinds rated 99 |
0x200, 0x400, 0x800, 0x1000, 0x2000 |
many | not worked out |
fb::RoleCombination (0x128de8) treats a kick with any of bits
0x1–0x10 the same way: a pass, a cross or a shot (confirmed).
fb::PlayEffector's handlers send a different event to 0x94018 for
kinds 36 and 37 (0x68, 0x69) and for each throw-in kind 30–33
(0x6a–0x6d) (confirmed). What those events start wasn't traced.
So the kinds are, combining the table above with the ratings:
| Kind | What it is | How sure |
|---|---|---|
| 0 | short pass | flags and rating |
| 1, 2 | through pass, on the ground and in the air; made by the front and killer pass intentions | flags, rating and code |
| 3, 4 | long pass in the air. Their flags are the same; 3 is made by the feed, killer and side-change pass intentions, 4 is the kind the pass, clear and cross intentions' shared code stores | flags, rating and code |
| 5 | – (rated by short pass; flags like a keeper's) | – |
| 6 / 7 | pinpoint cross, in the air / low | flags and code |
| 8 / 9 | liner cross, in the air / low | flags and code |
| 10 | goalkeeper's throw | variant list and flags |
| 11 | goalkeeper's kick, in the air (made by PlayEffector) |
variant list (INSTEP, SOUTH_AMERICAN), flags and code |
| 12, 13 | goalkeeper's catch, save | variant lists |
| 14, 16 | target (placed) shot; 16 in the air | flags, rating and code |
| 15, 17 | power shot; neither has the air bit | flags and code |
| 18 | loop (chipped) shot, in the air | flags and code |
| 19–21 | tackles | rating and flags |
| 22, 23 | always rated 99; flags like the tackles | – |
| 24–26 | 25 the ball lost, 26 a collision | variant lists |
| 27 | kick-off pass | variant list and flags |
| 28, 29 | shots at a restart; 28 rated by placekick (its variant list is KICKOFF), 29 by shot skill. Probably a penalty and a shot from a kick-off or drop ball, not confirmed | flags and rating |
| 30 / 31 / 32 / 33 | throw-ins: short (made by PlayTIThrow) / short, never made by a wrapper / long (PlayTIThrow, rated by the long throw skill) / through |
flags, rating and code |
| 34, 35 | short and long pass at a restart: by elimination probably the goal kick, not confirmed | flags and rating |
| 36, 37 | corner: short pass, cross (PlayCKKick) |
code |
| 38, 39, 40 | free kick: short pass, cross or long ball, shot (PlayFKKick) |
code |
Still open: what tells 3 from 4 and the members of each in-play pair apart beyond the random draw, the three tackles, and where kinds 27–29, 31, 34 and 35 are first set (kick-off, penalty, goal kick).
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x704f0 700 # builds the kind table
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x128de8 40 # RoleCombination: bits 0x1-0x10The other readers:
| Parameters | Field | Read by |
|---|---|---|
| 26, 27, 35 | dribble pace, dribble skill, trap | IntentionRWTB, IntentionKeep, PlayADribble, PlayRWTB; 27 also by helpers 0x91438, 0x9b560, 0x9b768 |
| 38, 39, 41, 52, 53 | intercept, ball winning, agility, jump, agility | intercept by match::PlayerManipulator; ball winning and agility (41) by helper 0x9b650; jump by helper 0x9b7e0; agility (53) by many roles (RoleBallCarrierMarker, RoleFreeRunCommon, …) |
| 40 | marking | RoleMarker, RoleBallCarrierMarker, helper 0x9b650, the set-piece assignment (CSetPlayAssign) |
| 42, 43–45 | placekick, the placekick mixes | penalties (PlayPKKick) and free kicks and corners (PlayFKKick, PlayCKKick; 0x8e818 picks 44 or 45) |
| 46–49, 115, 117, 122 | saving, catching, aerial ability, rushing out; skills penalty stopper, one-on-one keeper, reflex saves | saving by PlayPKKeeper and the kick helper; catching and aerial ability by PlayGoalKeep; rushing out and one-on-one keeper by RoleBallCarrier; penalty stopper by PlayPKKeeper; reflex saves by match::MatchManager (0x190e40) |
| 50, 51, 55, 56 | pace, acceleration, stamina, kick strength | pace by the pass, cross and dribble rate classes; acceleration by RoleBallCarrier; stamina by fb::Player's 0x7a898; kick strength by the pass, clear and shot intentions and PlayFKKick |
| 58, 60 | 7 − injury_res, recovery |
match::MatchManager (0x9b220, 0x9b360; 0x19db10) |
| 62 | (15 − foul_avoid) / 2 |
PlayBallCarrierMark (fouls when challenging) and 0x9b020 |
| 63 | professionalism / 2 |
20 reads, 19 of them in the out-of-play roles' code (0x125980–0x128450, after RoleOutOfPlay) |
| 65, 66 | concentration / 14, nerve / 14 | the kick rate helpers; nerve also PlayPKKick |
| 67, 68 | attack minded, defence minded | 12 and 15 reads: the positioning roles (RoleSAAttacker, RoleSASupport, RoleFreeRunner, RoleMarker, RoleMarkAssist, …), PlayEffector and the phases (PhaseInPlay, PhaseSP, PhaseThrowIn) |
| 69, 70, 75, 76 | supportiveness, vision, pressing, line DF | RoleSupporter and RoleSASupport; RoleBallCarrier; RoleMarkAssist; RoleSpaceMarker |
| 71 | professionalism ≥ 3 |
match::MatchManager (0x169670) |
| 72–74 | counterattack, the possession mixes | fb::Combination's code (0xc16b4), the team's combination plays; 73 also RoleSASupport |
| 77 | PlPinfo +0x294 |
collision code (0x31998) |
| 93, 97, 99, 100, 106–108 | styles Dash out, Shadow striker, Dynamo, Man marker, Sweeper, Defensive Sweeper, Stopper | besides 0x99520: 93, 97 and 99 by 0x99720 and 0x10df80 (roles); Man marker, the sweepers and Stopper by RoleBallCarrierMarker |
| 112, 114, 118–121, 126 | skills covering, penalty taker, long throw, super sub, through balls, positioning, poacher | covering by RoleBallCarrierMarker; penalty taker by PlayPKKick; long throw by the throw-in taker RoleTIThrower; super sub by match::StatisticTask (0x190c00); through balls by RoleFKKicker; positioning by RoleMarker and CSetPlayAssign; poacher by helper 0x9a310 |
| 128–138 | the tendencies | see below |
| 140 | dribble_style |
helper 0x9b540 (see below) |
These fit the names in pbdata.py and the skill and
style names closely (long throw read by the throw-in taker, penalty
taker by the penalty kick, super sub by the statistics task), which is a
check on those names. Also read, from outside the table above:
parameter 16 (0x193ce8) and 24 (0x9c518), whose sources aren't
traced, and 164 (0xc1d00, fb::Combination's code), past the 163
copied parameters: its base byte +0xa8 is parameter 1's modifier.
Never read through 0x9bdb8 (confirmed, no call passes them):
61 leadership, 64 pressure / 2, 113 skill offside line, 139
ball_touch, 141–149 (dribble skill ≥ 0x50), and the kit style and
zeros 150–162. The kit style reaches the player model another way
(Kit style). Unless match code reads them straight from
the player object, which wasn't searched for, leadership, pressure, the
offside-line skill and the ball touch do nothing in a match.
The match AI's decision classes read the 11 values at +0x66. Each class
gives its name through its run-time type information (the fb:: classes
in GAMEPRG.REL, found from the vtable of each reading function). Most
classes turn the level into one of five entries of the tuning table,
which is then added to that kind of play's score, or multiplies it. Level
2 is neutral in every set that is added. Confirmed from the code:
| Item | Offset | Parameter | Read by | Tuning entries for levels 0–4 |
|---|---|---|---|---|
| 0 dribble | +0x66 |
128 | IntentionRWTB (0xe4320, 0xe46b0), IntentionADribble (0xe7390), IntentionKeep (0xe6050), PlayBallKeep (0xf95c8), and 0xe4948, which the pass, cross and shoot intentions also call |
0x52d: −0.15, −0.05, 0, +0.05, +0.15 (RWTB); 0x537: −0.3, −0.1, 0, +0.05, +0.15 (ADribble); 0x532: × 0.75, 0.9, 1.0, 3.0, 10.0 (0xe4948); 0x53c: 20, 5, 2, 0.5, 0.01 (BallKeep) |
| 1 shoot | +0x67 |
129 | 0xec508, called 10 times from the shoot intentions' code |
0x546: × 0.6, 0.9, 1.0, 1.1, 1.35 |
| 2 carry | +0x68 |
130 | IntentionRWTB, IntentionKeep, IntentionADribble; and 0x1176d8 (through 0x117480), inside a helper (0x116ee8) that most roles call, on or off the ball (below) |
0x55a: 0, 0, 0, +0.05, +0.15; 0x555: × 0.2, 0.6, 1.0, 1.5, 3.0 (0x1176d8) |
| 3 feed pass | +0x69 |
131 | IntentionFeedPass (0xde6e0) |
0x519: −0.1, −0.05, 0, +0.05, +0.15 |
| 4 side change | +0x6a |
132 | IntentionSideChangePass (0xe08b0) |
0x51e: −1.0, −0.5, 0, +0.05, +0.15 |
| 5 back pass | +0x6b |
133 | IntentionBackPass (0xe2688) |
0x523: 0, 0, 0, +0.05, +0.1 |
| 6 killer pass | +0x6c |
134 | IntentionKillerPass (0xdc1d8) and IntentionLinerCross (0xeafc0) |
0x528: 0, 0, 0, +0.05, +0.15 |
| 7 one-two | +0x6d |
135 | IntentionOneTwo (0xe8300) |
0x542–0x545: 0, 0, +0.05, +0.1 for levels 1–4 (level 0's entry isn't traced) |
| 8 clear | +0x6e |
136 | IntentionClear (0xf0448, and 0xf0ab0 from it) |
0x550: 0, 0, 0, +0.05, +0.15; 0x54b: × 1.0, 1.0, 1.0, 1.2, 1.5 |
| 9 press | +0x6f |
137 | PlayBallCarrierMark (0xfcb48, through 0x9a418 and 0x9a4c8) |
no tuning entries. The level is capped by a team value (match data +0x9764: 1 caps it at 0, 2 at 1, anything else at 4) and picks from the floats at 0x26e400 (rows 80, 70, 60, 50, 50 / 60, 50, 45, 30, 30 / 30 …) and 0x26e440 (5, 5, 10, 15, 20) |
| 10 goalkeeping | +0x70 |
138 | PlayGoalKeep (0xff080, through 0x1002b0 and 0x100c28) |
0x594–0x59c, every other entry: −0.3, −0.2, −0.1, +0.1, +0.3 |
The item names are the ones pbdata.py uses. They come from the class
names, not from the game's text: no screen in the game shows the
tendencies (Nexxus's report). "RWTB" is probably "run with the ball".
The classes for dribble, shoot, clear, press and goalkeeping act for one
player, the one on the ball or marking it. The pass intentions (feed,
side change, back, killer and the liner cross) and the one-two read the
passer's value: each takes the intention's own player id (intention
+4 → +4 → +0), turns it into a team slot with 0x73df8 on match
data +0x8e84, and reads that slot's player
(GAMEPRG.REL 0xdeed8, 0xe110c, 0xe2ec8, 0xddf40, 0xebd84,
0xe8554).
Carry in the roles (confirmed). The helper 0x116ee8 is called by
nearly every role class (RoleFreeRunCommon, RoleBallCarrierMarker,
RoleSupporter, RoleMarker, RoleMarkAssist, RoleSpaceMarker,
RoleSpaceMarkCover, RoleGoalKeeper, RoleSAAttacker, RoleSASupport
and more; python SRC/snr2.py callers ISO/DLL/GAMEPRG.REL 0x116ee8).
Through 0x117480 it reaches 0x1176d8, which reads the carry value
of the role's own player: it takes the id at role +4 → +0 (the
same chain as the pass intentions, one level shorter), turns it into a
team slot with 0x73df8 and reads that slot's player (0x117800–
0x1178a8). The level picks a multiplier (0x555–0x559: × 0.2, 0.6,
1.0, 1.5, 3.0). The player's current play (found by 0xf19d0 from role
+0x1b60) has three scores for its current step (play +8, +0x18,
+0x28, indexed by +0x38). Each is multiplied by the factor and
compared with a threshold, a set of three picked by a match value
(+0x1738, 0–4, the team's situation, below) from tuning entries
0x78b–0x799 (jump table 0x27a690; any other value uses the first
set). The play is kept only when all three pass
(0x117b8c–0x117bd8). So a player with a higher carry value keeps
running with a plan once he has one; a low one drops it sooner. In one
branch the factor is raised to at least 1.0 first (max.s at
0x117b44).
| Situation | Thresholds |
|---|---|
| 0 | 0.3, 0.4, 0.8 |
| 1 | 0.3, 0.55, 0.6 |
| 2 | 0.2, 0.4, 0.3 |
| 3 | 0.1, 0.25, 0.15 |
| 4 | 0.1, 0.2, 0.9 |
The team's situation (confirmed, GAMEPRG.REL). The value at
+0x1738 belongs to the team AI object that the role reaches through
+0xc (the one that also holds the current phase at +0x1708). Each
update asks the phase for a situation (its virtual method at vtable
+0x50) and stores it with 0xab100:
| Phase | Situation |
|---|---|
| Kick-off, goal kick, out of play | 0 |
Attack (PhaseAttack) |
1 |
| Penalty kick | 4 for the side 0xa9e20 picks, 3 for the other |
| Corner, free kick, other set plays | 3 or 4 by the set play's step (jump table 0x278840), otherwise as in play |
| In play, build-up, goal, throw-in | worked out from the ball (0xf5608, below) |
In open play 0xf5608 compares the line of play (match data
+0x1065c, described with the press floats below) with two points
between the team's own goal line (y −527.81, turned round for the
side) and the opponents' defensive line (match data +0x10400, the
other side's entry). The first point is a fraction f of the way
there, the second halfway from that point to the opponents' line.
Behind the first point the situation is 0, before the second 1, and
from the second on 2. Two checks then override it: 3 when 0xf5728
is true, 4 when 0xf5c18 is; what they test isn't traced. So 0 to 2
are the ball in your own part of the pitch, in the middle, and near
the opponents' back line.
f is the team's +0x179c, set at each kick-off (0xaafd0,
0xaaff0) from 0xa9e58: 0.3 + v / 15 for a value v of 0–6,
so 0.3 to 0.7. v (0xbc538) is a tactics value of the current
formation (word +0x10 of the record 0x14af00 returns; which of the
7 sliders that is isn't traced), plus a step for the match situation:
+1 when behind late and −1 when ahead late (0x2741a0; the mode 0–4
from the time and score, as for the
match policy), or +3 and −3 when
0x9ce78 is true. It is clamped to 0–6.
A move into or out of situation 3 or 4 is held back: when match data
+0x104d4 is set, 0xab100 only switches after the new value has
come 5 updates running (counter +0x1740, pending value +0x173c).
What the press floats measure (confirmed, GAMEPRG.REL). Match
positions are in tenths of a metre
(BPB_FORMAT.md), so the table values are distances:
0x9a418(called four times byPlayBallCarrierMark,0xfd498–0xfd5a0) returns row r of0x26e400at the press level: row 0 8, 7, 6, 5, 5 m, row 1 6, 5, 4.5, 3, 3 m, row 2 3 m at every level. The result replaces the second of four distances (15, 10, 8 and 4 m by default,0x278a4c) that the marker works with, so it is how far from the ball carrier he takes up his position. Row 0 is used when bit 0 of the play's+0x80is set. Otherwise the row follows a distance along the pitch: the line of play (below) minus a side's defensive line (the side in the player id0x86298returns), both turned round for that side. From 30 m (tuning0x5c9) it is row 1, up to 10 m (0x5ca) row 2, and in between a blend of the two. Then 1.5 m (tuning0x5cb) is taken off when an angle worked out from one goal (0xa61d8, picked by the side; the goal centre at y ±527.81) is over 90 degrees (0xfd648–0xfd68c).0x9a4c8(once,0xfcd68) returns 0.5, 0.5, 1, 1.5 or 2 m from0x26e440, or 0 when the side's press byte is 1. The value is squared and compared with a squared distance, worked out in VU0 code that the disassembler can't decode (0xfcdbc–0xfcf18); inside it, the mark sets a flag (0xfcf60). Probably the radius within which the marker counts as on the ball carrier.
The two positions both rows use (confirmed):
- The line of play, match data
+0x1065c, is a y position that0x3e038keeps up to date. While a player has the ball (match data+0xc) it follows his y, while a pass is on its way it follows the receiver's (+0x14), and while nobody has it, the ball's (0x41da8). It runs up to 5 m ahead of or behind that player by a factor from0x41ca8, and never past the goal line (y ±527.81). - The defensive lines, match data
+0x10400+ side × 4, are set by0x37540from the players sorted by y: for each side, the y of its second player counted from its own goal, the goalkeeper usually being the first. That is the side's offside line.+0x10408and+0x1040chold each side's front player in the same way.0x3e4a8reads the first pair and0x3e4c8the second.
So a higher press level means standing closer to the ball carrier (rows 0 and 1 shrink from 8 or 6 m at level 0 to 5 or 3 m at levels 3–4) and a wider closing-in radius. With the press cap at 2 (below), every level from 1 up gets level 1's values.
The press cap stays at 2. The team value is byte 0 of a 4-byte
record per side at match data +0x8e84 + 0x8e0 + side × 4
(+0x9764, +0x9768). Its constructor (0x74320, called at
0x7399c) sets bytes 0 and 1 to 2 (confirmed). 0x74380 would
fill byte 0 from match data +0x976d (always 2, GAMEPRG.REL 0x26f0),
but only 0x73c10 calls it, from 0x161438, and in a career match it
doesn't run: with 0x74380 patched to load 4, both records still read
02 02 07 00 over PINE in a German league match (2026-10-31). No other
code writes byte 0. Both press readers clamp the player's level with
the EE's pmaxw and pminw (0x9a48c, 0x9a53c) to 0–cap, the cap
set to 4 and then, in the delay slot, to 1 for a 2 (movz at
0x9a474 and 0x9a52c). So in every career match a press tendency of
2, 3 or 4 acts as 1: only 0 against anything higher makes a
difference, which in the second table (0x26e440, 5 and 5) is none at
all. Read from a mid-match save state (England v England) too: both
sides' records were 02 02 07 00. Byte 1 is set from a menu during the
match (0x1e4afc, values 4, 3, 2, 1 from 0x29ab00) and isn't traced
further. --mod press-tendency turns both movz into nop
(LMASTER_MOD.md).
Empirical, over all 27,950 players: every item uses 0–4, mostly 1–3,
and items 0–9 each have 2,000–3,600 players at 4. Averaged by main
position, the values fit the names. Goalkeepers average about 0.3 for
dribble, shoot, killer pass and one-two, and about 2 for goalkeeping.
Other players average about 0.45 for goalkeeping. Forwards (cells 10–12)
average about 1 for clear and press, and defenders (cells 1–3) about 1.5
for shoot and killer pass. PBDATA_JP.PAC has some values of 5 and 6,
which the match reads as 4.
dribble_style (parameter 140) has one reader, 0x9b540, called from
0x8afdc. The value goes to 0x13d710/0x13d780, which pick one of four
rows in the tables at 0x285888, 0x285a08 and 0x285ab8. These hold
motion numbers (967–1026 in the first table): the four rows share their
first 12 entries and differ in the dribbling ones. So the style chooses
the player's set of dribble animations. Which style looks like what
hasn't been checked in game.
python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408 # tendency, item 0 first
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0xdef30 40 --sles ISO/SLES_541.51 # feed pass: entries 0x519-0x51d
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x9a418 60 --sles ISO/SLES_541.51 # pressThe 64 player abilities are finer-grained than anything the game shows. The detail screen's 14 bars and the 6-axis "Evaluation" hexagon are worked out from them.
Groups (confirmed, plPinfo_Abil2PSM 0x216f10). Abilities 0–18
return 1, 19–25 return 0, and 26–63 return 2. plPinfo_InitAbil
(0x21b470) uses the group to pick which growth byte (+0x4a, +0x4b,
+0x4c) scales an ability's random start offset. From the bars below,
0–18 are skills, 19–25 physical and 26–63 mental, which fits the name
(P/S/M).
Bars (confirmed, WP::CDetailManager::ConvertPlayer_Bar 0x285380).
Each bar is the integer average of the listed abilities' levels, and it
is drawn as (value + 10) / 99 of full width. The labels are detail
messages 0x2774+. The last 7 bars depend on whether the player's main
position (PlPinfo +4) is 0, the goalkeeper:
| Bar | Abilities | Field bar | Abilities | GK bar | Abilities | ||
|---|---|---|---|---|---|---|---|
| SPEED | 0, 20, 22 | DRIBB | 0, 1 | SAVIN | 15 | ||
| PHYSI | 24, 25 | SHOT | 2, 3, 24 | HANDL | 16 | ||
| STAMI | 23 | PASS | 4, 5, 6 | CROSS | 17 | ||
| MENTA | 26, 27 | FK | 14 | GO FW | 18 | ||
| SUPPO | 30, 31 | HEAD | 7, 25, 21 | DISTR | 4, 5, 24 | ||
| SYSTE | 0–7 | INTER | 11 | AGILI | 22 | ||
| TACTI | 0–10 | MARK | 13 | JUMP | 21 |
Only abilities 0–32 feed the bars. Where a bar has a single source, it names that ability: 11 intercept, 13 marking, 14 free kick, 15 saving, 16 handling, 17 crosses, 18 going out, 21 jumping, 22 agility, 23 stamina. The labels SYSTE and TACTI (messages 200:10105/10106, "SYS" and "TAC" in Japanese) suggest the systems and tactics (45–52 and 53–63, see Ability names), but the code averages abilities 0–7 and 0–10. A check with well-known players fits (database values, before the random start offset): Terry has MARK 98, INTER 94 and HEAD 92, Pirlo has PASS 93 and FK 94, Henry has SPEED 94 and DRIBB 95, and Cech and Buffon have SAVIN 98.
Hexagon (confirmed computation, plPinfo_CalcHexagon 0x217ce0,
plPinfo_CalcHexAbil 0x217850). PLRESOURCECOMMON.PAC entry 2,
table 0 has 8 bytes per ability. Each 4-byte half holds two
{u8 hexagon, u8 weight} pairs: +4 is the field-player variant, and
+0 is the goalkeeper variant. Hexagon h is the weighted average of
every ability with a pair naming h. CalcHexagonNG fills all six from
the field variant, then CalcHexagon recomputes 0 and 1 with the
goalkeeper variant for goalkeepers. The screen's labels are messages
670–675 of category 1 (Attacking, Physical, Teamwork, Defence, Attitude,
Skills, clockwise from the top). Which index goes to which label is
empirical. Hexagons 0 and 3 are told apart by Virtua Pro Football's
categories (Ability names): 0's main abilities are its
"Attack" page, and 3's are its "Skill" page.
| Hexagon | Main abilities (weight 80) | Label |
|---|---|---|
| 0 | 2, 4–7, 14 (shot skill, passes, cross, header, placekick) | Attacking |
| 1 | 10–13 (15–18 for goalkeepers) | Defence (defenders score ~90) |
| 2 | 33, 42–44, 53–58 (59–63 at weight 16) | Teamwork |
| 3 | 1, 3, 8, 9 (dribble skill, shot technique, trap, ball keeping) | Skills |
| 4 | 0, 19–25 | Physical |
| 5 | 26–32 | Attitude |
python SRC/pbdata.py show prints both the bars and the hexagon, and
csv adds the bars as columns.
The developers' label table (above)
names all 64 abilities. This section is the independent check that came
first: Virtua Pro Football's English names and values. pbdata.py uses
VPF's English name where VPF has the same parameter.
Virtua Pro Football (VPF) runs on the same engine, and its Player Edit screen names its parameters on 8 pages. Nexxus supplied screenshots of that screen for four players in this database: Maik Taylor (player 0, a goalkeeper), Wayne Rooney (258, a forward), David Beckham (4408, a right midfielder), and Lucio (2102, a defender; Physical page only). VPF's pages follow this game's ability order, and most values match within a point or two. VPF has parameters this game lacks (cross technique, 1 on 1 response, balance and some tactical ones), so the lists are aligned by value, not by position.
The values column gives this game / VPF, for Taylor (T), Rooney (R) and Beckham (B). The evidence column says where each name comes from: code (the bars, the position grid or match growth above), VPF + data (VPF's name, and the players' values or a trend across the whole database fit), or VPF (VPF's name, with the values as shown).
| Ability | Name | Values T, R, B | Evidence |
|---|---|---|---|
| 0 | dribble pace | 38/31, 88/92, 76/72 | VPF + data (SPEED and DRIBB bars) |
| 1 | dribble skill | 38/38, 90/90, 78/77 | VPF + data (DRIBB bar) |
| 2 | shot skill | 38/35, 86/91, 84/83 | VPF + data (SHOT bar) |
| 3 | shot technique | 38/35, 88/87, 82/81 | VPF + data (SHOT bar). VPF's "Skill" page |
| 4 | short pass | 54/54, 84/83, 86/85 | VPF + data (PASS bar) |
| 5 | long pass | 64/63, 82/81, 98/99 | VPF + data (PASS bar) |
| 6 | cross | 40/39, 80/80, 99/99 | VPF + data (PASS bar) |
| 7 | header | 38/35, 82/82, 70/70 | VPF + data (HEAD bar) |
| 8 | trap | 60/55, 88/88, 84/80 | VPF + data. VPF's "Skill" page |
| 9 | ball keeping | 38/36, 82/82, 74/73 | VPF + data. VPF's "Skill" page |
| 10 | tackle | 48/48, 66/65, 70/70 | VPF + data |
| 11 | intercept | 38/38, 46/46, 70/69 | code (INTER bar) |
| 12 | ball winning | 38/37, 48/48, 72/72 | VPF + data |
| 13 | marking | 38/35, 50/49, 76/76 | code (MARK bar) |
| 14 | placekick | 52/51, 80/80, 99/99 | code (FK bar) |
| 15–18 | saving, catching, aerial ability, rushing out | 78/77, 78/77, 82/81, 78/78 for T | code (goalkeeper bars SAVIN, HANDL, CROSS, GO FW) |
| 19 | pace | 74/70, 86/94, 80/76; Lucio 80/82 | VPF + data. Lucio settles the pair: VPF gives him pace 82 and acceleration 71 |
| 20 | acceleration | 74/69, 92/94, 78/74; Lucio 76/71 | VPF + data |
| 21–23 | jump, agility, stamina | 76/71, 88/83, 62/57 for T; 82/82, 88/88, 84/84 for Lucio | code (JUMP, AGILI, STAMI bars) |
| 24 | kick strength | 74/73, 88/88, 86/86 | VPF + data (SHOT and DISTR bars) |
| 25 | contact strength | 80/80, 90/91, 80/80 | VPF + data (PHYSI and HEAD bars) |
| 26 | leadership | 40/40, 70/70, 90/90 | VPF + data (MENTA bar) |
| 27 | nerve | 40/51, 94/45, 94/66 | developer label 度胸. VPF's "consistency" sits in this slot, but the values don't fit (MENTA bar) |
| 28 | attack minded | 52/51, 96/96, 94/93 | VPF + data: averages 45 for goalkeepers, 80 for forwards |
| 29 | defence minded | 84/84, 54/70, 72/72 | VPF + data: averages 80 for goalkeepers, 45 for forwards |
| 30 | supportiveness | 40/40, 88/79, 90/89 | VPF + data (SUPPO bar) |
| 31 | vision | 60/59, 76/75, 99/99 | VPF + data (SUPPO bar) |
| 32 | concentration | T 78, R 72, B 86 | developer label 集中力; no VPF counterpart (Attitude hexagon) |
| 33–44 | position aptitudes | see below | code (Positions) |
| 45–52 | fit with systems 3-4-3, 3-5-2, 3-6-1, 4-3-3, 4-4-2, 4-5-1, 5-3-2, 5-4-1 | – | code for "system k" (match growth); the order of the systems is empirical: the formation names (messages 801:0–7, 1:530–537) and their descriptions (700:270–293) are listed in this order |
| 53 | counterattack | 82/82, 88/87, 82/81 | VPF + data |
| 54 | possession | T 42, R 88, B 46 | developer label ポゼッション理解度; no VPF counterpart (Teamwork hexagon) |
| 55 | attacks down wings | 80/79, 62/78, 40/39 | VPF + data (Rooney is the one outlier) |
| 56 | attacks through middle | 38/34, 86/86, 56/55 | VPF + data |
| 57 | line DF | 64/64, 58/57, 64/63 | VPF + data. Beckham settles the pair (line DF 63, pressing 59) |
| 58 | pressing | 68/68, 58/57, 60/59 | VPF + data |
| 59–63 | attack patterns A–E | – | developer labels 攻撃パターンA–E理解度. The game only ever handles the five as a set (below), so which letter is which makes no difference |
The attack patterns (confirmed). Nothing in the game reads one of abilities 59–63 on its own. Every use takes all five:
| Where | What it does with them |
|---|---|
PLRESOURCECOMMON.PAC 2.0, the hexagon weights |
all five the same: Teamwork (2) weight 16 and Attacking (0) weight 20, for field players and goalkeepers |
GP::PlTeamData2HEX_DATA (0x27b458, at 0x27b70c) |
adds their average to a player's rating in the GP::HEX_DATA hexagon window, with the averages of 42–44 and 53–58 |
TRAINING_LIST.TBB |
every practice grows the five by the same amount (+1, +4, +8 or +15) |
--mod salesman-items (flag 336) |
raises all five in the match's copy of your squad (above) |
The match doesn't copy them into its player parameters
(The match engine's player parameters).
A scan of the executable and every overlay for halfword loads at their
PlPinfo offsets (+0x16c–+0x184), and for offsets or indices built
from 59, found no other reader (the hits are virtual-call slots at the
same offsets in other objects, and a per-player stride of 0x16c).
So the letters are just names: which one is which doesn't change
anything in the retail game. The developers may have meant them for the
club's five pattern slots (PlTeamData +0x489f), but no code ties a
slot to one of them.
VPF's aptitude page names the position cells. Rooney and Beckham agree on 39 = OM (86/85, 72/70), 42 = centre (94/93, 88/87), 43 = left (82/82, 54/53) and 44 = right (54/53, 98/97), and Beckham has 38 = SM (96/95). Rooney also fits 34 = SB (40/39), 35 = CB (44/44), 36 = WB (46/46) and 41 = FW (96/95). Taylor has 33 = GK (82/82). The defensive cells don't fit Beckham, whose VPF values there are a flat 48 (56, 70, 76 and 92 here for SB, CB, WB and DM). 40, the forward line's sides, has no VPF counterpart.
Across the database, abilities 26, 27, 32 and 45–63 average 51 for every position and play style. They are only higher across the board for stronger players (about 62 for the holders of any skill), which suggests filler values. That is why those names rest on the VPF players.
+0x57–+0x5d say how the player wears the kit. The retail game has an
editor for them, and its labels are in message category 2000: a
"Dressing" page (1070) with Sleeves, Gloves, Wristband and Spikes
(1074–1077), and a "GK Style" page (1071) with Hat, Pants, Gloves,
Wristband and Spikes (1078–1082). The help texts (3074–3082) explain each
one: sleeves are "Short all year", "Long all year" or "Change length
according to season", outfield gloves are worn "during cold weather", and
the goalkeeper's leg wear is "Normal" or "Long". Edit players carry the
same settings; the database players mostly have 0.
Confirmed from the game code:
| Address | Symbol | What it shows |
|---|---|---|
GAMEPRG.REL 0x1e468 |
(called from 0x1e67c, 0x1e7c8, after get_player_uniform_style_info_ptr) |
fills a 16-byte player::SPlayerUniformStyleInfo from PlPinfo: bytes 0–6 are record +0x57–+0x5d, byte 7 the shirt number (+0x2b), +8 a pointer to the record (name and number for the shirt), +0xc a flag from 0x1e518, which compares the player with the one picked by team +0x41eb (probably the captain). Byte 2 (+0x59) is copied only when plGi_Get() +0x1f538 is set, and is 0 otherwise |
0x2130d4 |
PlGiTask::InitStadium |
sets plGi +0x1f538 to IsLongSleeveUniformFromTempareture (0x21fbc8: temperature below 10) of plCity_GetTemperature for the player's club's city (pwkTeam_GetMyTeamData +4) on the match date. PlGiTask::init (0x210ffc) clears it. The only caller is ScheCallbackCommand_MatchBranch (0x113db8), the career's match step, so a VS match leaves it 0 |
GAMEPRG.REL 0xe12c |
(before set_team_uniform_style_info) |
copies the same flag into the team style's long-sleeve byte (+0x14), so outfield gloves show exactly when "by season" sleeves are long |
0x103594–0x10364c |
CFaceLoader::LoadRequest |
for an edit player (plPinfo_IsEdit, id from 0x7cce) it copies the edit work (pwkTeam_GetEditWork, record at work +4) and passes record +0x5a ≠ 0 to CEditFaceObject::RequestCreate (0x1507c8), which keeps it at +0x288. Other players take LoadRequestNormal (0x1036a8), which has no cap |
0x150d04 |
CEditFaceObject::CopyHair |
when +0x288 is set, adds the face pack's cap texture (GetCapTextureImage, vtable slot 0x10c) to the hair model |
YRSTPRG.REL 0xc598, 0xf250, 0xf464 |
PLAYEREDIT_MODULE |
the retail player editor's face preview passes record +0x5a to CEditFaceEdit::ChangeHairType (0x14ff48) as its cap flag, which SetupHairModel (0x1503a0) uses the same way |
0x2cd9c8 |
PlayerAssemblerBase::set_player_uniform_style_info |
clamps bytes 0–6 to at most 2, 4, 4, 3, 5, 1 and 14, then stores the 16 bytes at assembler +0x8460 + 16 × slot. Slot 0 is the goalkeeper (prepare_player_one sets up the GK textures for it) |
0x2cf3a0 |
set_default_uniform_style |
the defaults: all 0 except byte 4 (+0x5b) = 1 and the shirt number 10 |
0x2cdd68 |
refresh_uniform_style |
byte 0 picks the arm models: 1 short, 2 long, 0 the team's choice (team style +0x14, set by the season, IsLongSleeveUniformFromTempareture 0x21fbc8). Byte 1 non-zero adds the wristband meshes, only when the sleeves are short. Byte 5 (slot 0 only) swaps the goalkeeper's pants model for the long one |
0x2c9c28 |
set_hand |
hand models: the goalkeeper always has gloved hands; an outfield player gets gloved hands when byte 2 is non-zero |
0x2cb240, 0x2cb2b0 |
prepare_player_one |
glove textures: the goalkeeper's from byte 4 through the table at 0x3a1de0 (common-pack entries 93–98, named gkglv_00–gkglv_05), an outfield player's from byte 2 through 0x3a1dc8 (1–4 → entries 89–92, fpglv_00–fpglv_03) |
0x2cdad8 |
setup_player_uniform_style |
byte 6 picks a pair of palettes from the table at 0x3a2238 (15 pairs of entries 116–122) for common-pack texture 115, named spk_00 (the boots). Byte 1 picks the palette of texture 123, named wristband (4 palettes, entries 124–127) |
The common pack is PLAYER/PLAYERDATACOMMONPACK1.MRG; the texture names
come from its .svm entries (python SRC/svr.py info).
| Offset | Byte | Name | Values |
|---|---|---|---|
+0x57 |
0 | sleeves | 0 by season, 1 short all year, 2 long all year |
+0x58 |
1 | wristband | 0 none, 1–4 a colour (shown only with short sleeves) |
+0x59 |
2 | gloves | outfield players, only in a career match when the club's city is below 10 degrees (below): 0 none, 1–4 glove texture fpglv_00–03 |
+0x5a |
3 | gk_cap | edit players only: non-zero puts a cap on the head (below). The source name is nicknack_gkcap, the GK Style page's Hat. The match's style byte 3 has no reader; the cap goes through the face loader instead |
+0x5b |
4 | gk_gloves | goalkeeper's gloves gkglv_00–05 |
+0x5c |
5 | gk_pants | goalkeeper only: 0 normal, 1 long |
+0x5d |
6 | boots | 0–14, one of 15 two-palette colourings |
Tested in PCSX2 (a VS match with England's fixed squad, records 26,041–26,055 edited and renamed after their settings):
- Boots: Gerrard (1), Joe Cole (7) and Wright-Phillips (14) each wore differently coloured boots.
- Sleeves: Ashley Cole with sleeves 2 wore long sleeves.
- Wristband: Neville (1) and Ferdinand (4), both with short sleeves, wore wristbands.
gk_cap= 3 showed no cap, neither on the keeper (Robinson) nor on an outfield player (Owen). The code explains it: both are database players with fixed faces, and only an edit player's face reads the cap.- Outfield gloves (Terry 4, Beckham 1) didn't show in a night match. The code explains it: a VS match never sets the cold-weather flag.
- The keeper (
gk_pants1,gk_gloves5) wore long pants and gloves that looked unlike the usual keeper gloves.
The Dressing and GK Style pages share bytes: both have a Wristband and Spikes item, and there is only one wristband and one boots byte.
Empirical: across all 27,950 players the values stay within the
clamps (pbdata.py info prints the ranges and marks a value above them
with !!). Sleeves are always 0. Boots value 1 is held by several
goalkeepers (Dudek, Kiely, Hoult, Howarth); the other values look
random, with no link to nationality or position.
Also read by the dissatisfaction code. pwkDissatis_PlayerResign
(0x239a30) and pwkDissatis_StaffResign (0x23a018) use +0x58 and
+0x59 as indexes into multiplier tables of 1.0, 1.1, 1.2, 1.3 (0x3917c8,
0x3919d0), like the eight dissatis causes. The data looks like kit
settings, not sensitivities: 26,524 players have both at 0, and only 31
have both non-zero, where the eight dissatis causes are spread
over 0–3. So a wristband or gloves also makes a player a little more upset
when a teammate or a staff member he likes or dislikes leaves.
Gloves and cold weather (confirmed, table above). plGi_Get() +0x1f538 is the career match's long-sleeve flag: the temperature of
the player's club's city on the match date is below 10. That is the
month's average for the city's climate zone plus −2 to +2
(PLRESOURCESIM_FORMAT.md). The European
zones average below 10 from about November to March. The flag counts for
both teams, home or away, and only in career matches; in a VS match it
is always 0, so no outfield player wears gloves there.
The cap (confirmed, table above). gk_cap is read only when an edit
player's head is built from face parts: a non-zero value adds the cap
texture to the hair model. Database players use their stored faces, so
their gk_cap is never read. The match's copy of the byte (style byte 3)
has no reader. So the GK Style page's Hat shows in the retail editor's
face preview and on that edit player in a match, whatever his position.
Values above 1 look the same as 1: both paths keep only whether the
byte is non-zero.
No code was found that reads +0x43–+0x46. The decoder keeps them
(plBits_DecPlPbaseEx stores four 4-bit values at PlPbase +0x43), and
the developers' field table names them explodetype, 0–15, four of them
(The developers' field names). A scan of
every byte load at their PlPinfo offsets (+0x1db–+0x1de) in
SLES_541.51 and the overlays, and at +0x43–+0x46 in the executable,
found only other structures (the manager record's tactics in the
developers' editor, a youth candidate record at 0x25fd70). A scan for
the offsets as immediates (0x1db–0x1de, which also finds indexed
reads) adds only the save's serializers, which write and read the four
values with every player (SAVEPRG.REL 0xeed0, 0x1043c). So they are
kept in the save and read by nothing.
"Explode" is the game's word for a growth spurt: the pwkExplo_*
functions (0x243348–0x2441c0) check monthly and yearly triggers
(pwkExplo_Month 0x243b80), then pwkExplo_SetExploExp (0x243348)
adds experience to all 64 abilities. Whether a player can have one
depends on his age against a table picked by the skill growth type
+0x4b (0x243020, table 0x54d6e0), and on PlPinfo +0x270/+0x271,
which plPinfo_InitExplo clears. So explodetype looks like a planned
input to this system that the retail game doesn't use.
Outside the match, only +0x67 of the 11 tendencies at +0x66 is read
(the affinity row).
pwkPCombo_Add (0x24e2e8) adds a bonus to all 11 and to the 64
abilities when the club reaches one of 45 "combos" (conditions at
0x54fbe8, 0x34 bytes each, checked by pwkPCombo_Recalc 0x24e0e8;
bonuses at 0x550510, 0x98 bytes each), clamped to 0–4. The bonus to
+0x67 is always 0. The match reads all 11
(The tendencies).
The tendencies are not the tactics screen's combination lines, nor the
heart, "…" and skull icons it shows next to each player when one is
selected. Both are per pair of players, stored in the save: the levels,
their thresholds and the five icons are in
SAVE_FORMAT.md ("Pair
combinations"). The lines follow the same levels: in one screenshot the
only red line joined the selected player to the one player with the big
red heart, and a substitute who had just come on had only "…" icons and
white lines. Which colour (white, yellow, orange, red) is which level is
empirical, from those screenshots. How fast a pair grows
(plCombi_GetCombinationGrow, 0x20fb90) depends on both players'
PlPinfo +0x1ea, through an 8 × 8 table at 0x390698, not on +0x66.
python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408 # +0x57-+0x5d by name
python SRC/sles_disasm.py ISO/SLES_541.51 dis set_player_uniform_style_info set_hand__Q2 setup_player_uniform_style__Q2
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x1e468 44 --sles ISO/SLES_541.51
python SRC/sles_disasm.py ISO/SLES_541.51 dis pwkPCombo_Add+0x64 holds one bit per PlPlayerSkill (plPinfo_IsSkill, 0x218748).
Bit n is described by message 6000 + n of category 2000. The short
labels are summaries of those texts, as pbdata.py prints them:
| Bit | Label | Description (message 2000:6000 + bit) | VPF name |
|---|---|---|---|
| 0 | covering | superb covering, bails the team out | Covering |
| 1 | offside line | holds the defensive line, works the offside trap | Line Control |
| 2 | penalty taker | superb penalty taker | PK Taker |
| 3 | penalty stopper | puts pressure on the penalty taker (a keeper) | PK Goal Keeper |
| 4 | one-on-one finisher | cool in one-on-ones with the keeper | Shot on 1 on 1 |
| 5 | one-on-one keeper | saves one-on-ones | GK on 1 on 1 |
| 6 | long throw | very long throw-ins | Long Throw |
| 7 | super sub | swings a match when brought on | Super Sub |
| 8 | through balls | vision for through balls | Ball Feeding |
| 9 | positioning | exquisite positioning | Positioning |
| 10 | reflex saves | miraculous reactions | Fine Saving |
| 11 | acrobatic shot | shoots even off balance | Acrobatic Play |
| 12 | one-touch shot | one-touch finishing | Volleys |
| 13 | goal machine | pin-point shooting | Controlled Shot |
| 14 | poacher | pounces on loose balls in the box | Good Positioning |
| 15 | playmaker | leads the team with killer passes | Through Ball |
VPF's Special Skill page lists its 16 skills in this order, and each
name fits the description of the same bit, which backs up the bit order.
VPF marks Rooney with Shot on 1 on 1, Acrobatic Play and Controlled
Shot, and his +0x64 has exactly bits 4, 11 and 13. Beckham's PK Taker,
Ball Feeding and Controlled Shot are exactly his bits 2, 8 and 13.
That bit n goes with message 6000 + n is empirical, from who holds
which bit. Of 2,465 goalkeepers, 90, 91 and 86 hold bits 3, 5 and 10, and
of 14,395 defenders and forwards only 1 holds any of the three. Bits 0 and
1 go mostly to defenders (237 of 238), and bits 2, 4 and 11–14 mostly to
forwards. The code confirms one: the substitutions screen tests bit 7, the
super sub (CTacticsMenuSubstitutionsImplement::Update_MessDisplay,
0x2f6558). In the match, the skills are parameters 112–127; which
classes read each is in Who reads each parameter,
and how much they change a play isn't traced.
Confirmed (pwkPlayStyle_Init 0x24d8a8, AddPlate_PLAYSTYLE
0x289e50). +0x5e holds up to 5 play styles, 1–22, with 0 for an
empty slot. When a player is set up, the game lists the stored styles,
then adds random ones that suit the player's position until the
position's count (table at 0x398e98) is reached. The first stored
style is the one the detail screen shows. Style n is named by message
1:150 + n:
| Style | Name (message 1:150 + n) | VPF name | Players |
|---|---|---|---|
| 1 | Centre Forward | Centre Forward | forwards |
| 2 | Moving | Moving | forwards |
| 3 | Postplayer | Target man | forwards (Crouch) |
| 4 | Dash out | Darting run | forwards (Inzaghi, Shevchenko) |
| 5 | Second Striker | Second Attacker | forwards (Totti, Del Piero) |
| 6 | Wing | Wings | forwards and attacking midfielders |
| 7 | Play maker | Playmaker | midfielders (Pirlo, Zidane) |
| 8 | Shadow striker | Shadow Striker | midfielders (Lampard) |
| 9 | Attacker | Attacker | attacking midfielders (Robben, Ronaldinho) |
| 10 | Dynamo | Dynamo | midfielders (Gattuso, Vieira) |
| 11 | Man marker | Hard Marker | defensive midfielders (Makelele, Gattuso) |
| 12 | Covering | Anchor | defensive midfielders (Makelele) |
| 13 | Centre MF | Central Midfielder | midfielders (Lampard, Beckham) |
| 14 | Winger | Side Attacker | wide players (Beckham, Roberto Carlos) |
| 15 | Threaten to cut in | Wing Forward | wide players (Henry, Cafu) |
| 16 | Full back | Wing half | full-backs (Cafu) |
| 17 | Sweeper | Sweeper | defenders |
| 18 | Defensive Sweeper | Libero | defenders |
| 19 | Stopper | Stopper | defenders (Puyol, Nesta) |
| 20 | CB | Centre Back | defenders (Puyol, Nesta) |
| 21 | GK | Orthodox | goalkeepers only (Cech, Lehmann) |
| 22 | Attacking GK | Libero GK | goalkeepers only (Buffon, Barthez) |
Across the database (empirical), 19,403 players have at least one
style. Styles 21 and 22 are held only by goalkeepers (841 and 220), and
1–5 almost only by forwards. VPF shows its 22 styles in the same order,
each with a rating. This game stores only which styles a player has.
Beckham's three styles here (14, 13, 7) are his three best in VPF (Side
Attacker 80, Central Midfielder 70, Playmaker 92); Rooney's 2 and 5 are
his two best (Moving 87, Second Attacker 85), but his 9 (Attacker) is 48
there. The style names were already checked in game through a save
(SAVE_FORMAT.md).
Tested in PCSX2: John Terry (101) has styles 20, 19, 18 (CB,
Stopper, Defensive Sweeper). pbdata.py set ... 101 style.0=7 makes them
7, 19, 18. On a disc patched with patch_disc.py (with
--skip-tutorial), his detail screen in a new career showed "Play maker"
on its STYLE line, with three style icons. So the first stored style is
the one shown, and the database value reaches the game unchanged.
Confirmed, plPinfo_CalcAptPos (0x217f70),
plPinfo_GetPositionFitValue (0x217e48), and the table at 0x532610.
The detail screen's pitch grid has 13 cells, and they are the 13
position numbers. For each cell, the game computes a fit value, a
weighted sum of up to four abilities:
| Cell | Name | Fit |
|---|---|---|
| 0 | GK | 33 |
| 1, 2, 3 | DF left, right, centre | 0.7 × 34 + 0.3 × 43 (left) or 44 (right); centre 0.7 × 35 + 0.2 × 42 + 0.05 × (43 + 44) |
| 4, 5, 6 | DM left, right, centre | the same with 36 (sides) and 37 (centre) |
| 7, 8, 9 | AM left, right, centre | 38 and 39 |
| 10, 11, 12 | FW left, right, centre | 40 and 41 |
So abilities 33–44 are position aptitudes: 33 goalkeeper, 34–41 each row's sides and centre, and 42/43/44 a leaning to the centre, the left and the right. The rows go from the goal upwards. The names DF/DM/AM/FW are descriptive, not the game's. Which of 43 and 44 is the left is empirical: players who play on the left (Robben, Edu) are higher in 43, and Tevez's top-right cell lights up with 44 = 88.
Each cell's fit is converted to a level from 0 to 4 against the
player's best cell, using the table at 0x5327b0. A cell gets level
4, 3, 2 or 1 for the first row where fit ≥ 70/60/50/40 and either
fit ≥ 1.0/0.95/0.9/0.8 × best, or fit ≥ 80/70/60/50. Then each listed
position (+0x1c, up to three) gets +1, up to 4.
Tested in the game: Terry with abilities alternating 38 and 99 has
99 in 33, 35, 37, 39 and 41. His grid lit the goalkeeper box and the
whole centre column, as the formula predicts. The vanilla Terry's grid
(his centre-back cell, the centre cell in front of it and one side cell)
also matches the computed levels. His two side cells score 66 and 68,
just under the level-3 threshold, and the game's random start offset
(plPinfo_InitAbil) can push one of them over. Which levels the screen
draws, and in which colour, hasn't been traced.
python SRC/pbdata.py show prints the grid (forwards at the top), and
list names the positions.
Confirmed, WP::CDetailManager::CalcManagerAbil (0x286cd0).
PlMinfo is 4 bytes followed by the PlMbase (plMinfo_InitDb
0x2e9720 copies it to +4), so PlMinfo +0x6a is ability 0 and
+0xa0 is the job at PlMbase +0x1c. Every manager and coach screen has
12 shared bars, then a set picked by the job through the jump table at
0x557630. All are single abilities except FLANK:
| Bar | Ability | Bar | Ability | Job | Extra bars (abilities) |
|---|---|---|---|---|---|
| ATTST | 45 | FLANK | avg 29, 30 | 0, 1, 2 | DRIBB 10, SHOT 11, PASS 12, HEAD 13, INTER 14, MARK 15 |
| TEAMW | 46 | MOTIV | 0 | 3 | SPEED 18, PHYSI 20, STAMI 19, MENTA 21 |
| FK | 47 | PHYSC | 1 | 4 | SAVIN 16, HND 17 |
| TRAIN | 5 | COMMU | 3 | 5+ | FASTB 39, SLOWB 40, WINGP 41, DIREC 42, OFFSI 43, CLOSD 44 |
| ATKDF | 22 | POPUL | 2 | – | – |
| CENTA | 28 | ASSES | 4 | – | – |
The labels match the in-game help pages: 0–2 are assistant coaches, 3 physical coaches, 4 goalkeeper coaches. The database holds jobs 0 (439), 1 (786), 2 (714), 3 (678) and 4 (383), and no 5. A staff member hired as manager gets the job-5 bars. In a save, C. Collin (job 0 in the database) shows the manager bars, and they match abilities 39–44. So the job is changed at run time. The shared bars for Collin and M. Boismortier match their detail screens exactly.
What the jobs are. The developers' manager editor names them (confirmed, see The developers' staff editors): 0 balanced, 1 attacking and 2 defensive assistant, 3 physical coach, 4 GK coach, 5 manager, 6 youth manager. Averaging the coaching bars over each job in the database (empirical) agrees:
| Job | Count | Coaching bars (DRIBB SHOT PASS HEAD INTER MARK) | Is |
|---|---|---|---|
| 0 | 439 | 68 68 68 60 68 68 | a balanced assistant: all even. Hired as manager it becomes 5, as youth manager 6 |
| 1 | 786 | 80 80 80 68 55 55 | an attacking coach |
| 2 | 714 | 68 55 61 76 80 80 | a defensive coach |
| 3 | 678 | physical bars | physical coach |
| 4 | 383 | saving bars | goalkeeper coach |
In a save, M. Eulenburg and S. Saioni (job 0 in the database) are the manager (5) and youth manager (6), and the coaches keep jobs 1–4.
Where jobs 5 and 6 are set (confirmed). The job is the word at
PlMinfo +0xa0; your club's manager is at team data +0x4854, so his
job is at +0x48f4. pwkTeam_SignManager (0x269e20) copies the
record as it is and sets only the contract years at +0x9e, so the job
is set before it is called:
| Address | Symbol | What it shows |
|---|---|---|
0x26a240 |
pwkTeam_GetManagerCandidates |
builds each manager candidate's PlMinfo from the database (plMinfo_InitDb) and writes 5 into +0xa0. Every staff member hired from the manager list arrives with job 5, whatever his database job |
SIMPRG.REL 0x78998 |
WP::CManagerChangeTask |
the manager change screen lists your manager and up to 4 of your coaches (pwkTeam_GetManager, pwkTeam_GetCoaches, 0x771b0, 0x77270). Picking a coach writes 5 into his +0xa0 and calls pwkTeam_SignManager; this is how a coach becomes your manager |
SIMPRG.REL 0x78c3c |
WP::CManagerChangeTask |
sets 5 only while the detail screen shows a coach on that list, so it shows the manager bars, then puts the old job back |
0x26bd7c |
pwkTeam_SetYManager |
writes 6 into the youth manager's +0xa0 after copying him in |
SIMPRG.REL 0xd64d4 |
(next to pwkTeam_GetYManager and pwkTeam_GetCoachCandidates calls) |
writes 6 into a staff record it lists; which screen this is isn't traced |
The manager candidates come from the manager search
(pwkTeam_UpdateManagerCandidates 0x261500) and the automatic fill
(pwkTeam_AutoListUpManagerCandidates 0x263650), both through
pwkTeam_AddManagerCandidate (0x269658). They take staff from the
whole staff database by id, and no test of the database job was found
in them, so coaches can be found and hired as managers.
Former players become coaches or scouts (confirmed). A player of
yours can be asked to change career. The talk module
(TALK_MODULE::CTalkModule, SIMPRG.REL 0x11af8, 0x11b60) first asks
pwkTeam_IsPossibleJobChange (0x269c28), which allows it when the
player is not an edit player, his PlPinfo +0x21c (life,
ECONOMY.md) is at least 5, pwkTeam_GetCompAverage is at
most 45,000, a coach or scout job-change slot is free, and fewer than 3
of your players already have a job change set. The answer goes into
PlPinfo +0x210 through plPinfo_SetChangeJob (SIMPRG.REL 0x12150):
choice 0 takes a value from his position (the table at SIMPRG.REL 0x1cd288, by plMisc_Apos2Epos), and choices 1–3 store 4–6.
+0x210 |
Becomes | Set by choice |
|---|---|---|
| 0 | nothing | – |
| 1 | coach, job 0 (balanced assistant) | 0, for a WB, DM or SM |
| 2 | coach, job 1 (attacking assistant) | 0, for an OM, WG or FW |
| 3 | coach, job 2 (defensive assistant) | 0, for a GK, SB or CB |
| 4 | coach, job 3 (physical coach) | 1 |
| 5 | coach, job 4 (GK coach) | 2 |
| 6 | scout | 3 |
At the start of the next season ZinziYearStart::calcYearStartSecondHalf
(YRSTPRG.REL 0x17164–0x17240) walks your squad. For each player with
a job change it turns +0x210 into a coach job through the table at
YRSTPRG.REL 0x33288 (7, 0, 1, 2, 3, 4, 7; 7 is none) and makes him a
coach in one of the 3 coach job-change slots (0x1a698, which builds the
PlMinfo with that job; EVS::SetChangeJobCoach), or a scout for 6
(0x1a5e8). When that succeeds the player leaves the squad
(ZinziMyTeam::DiscardPlayer, reason 6).
pwkTeam_PutCoachJobChangeWork (0x26bc88) then puts each new coach
with job 0–4 on your coach candidate list (pwkTeam_AddCoachCandidate,
source 8), where he can be hired. As a coach he can later become the
manager through the manager change screen above. So no player becomes a
manager directly: former players become coaches first.
The coach page's title (SetupDetailCoachPageCommon, jump table
0x5577d0) is the same string for jobs 0, 1 and 2 (Msg::GetString(0x18, 3)), with 4 for goalkeeper coaches and 5 for physical coaches. Those
match messages 553 "Assistant Coach", 554 "GK Coach" and 555 "Physical
Coach" of category 100001. Checked in game: S. Daerden (job 1) and
L. Lynch (job 2) are both titled "Assistant Coach", and their comment
lines call them an "outstanding forwards coach" and a "very good defence
coach", which fits the averages above. So the game gives jobs 0–2 one
title; what tells them apart is which abilities they are good at, which
the comment line puts into words.
GP::ConvertStaff (0x27ee68) likewise gives 0–2 one icon.
Confirmed, WP::CDetailManager::ConvertScout (0x287c60).
PlSinfo is 4 bytes followed by the PlSbase (plSinfo_InitDb
0x21eb80). The 11 scout bars are single abilities (the 45 mapped
scout abilities, starting at PlSbase +0x2d): CLB 0, PLAYE 1, FINDP 3,
YOUTH 4, YOUNG 5, OLDER 6, VETER 7, MANAG 21, ACOAC 22, PCOAC 23,
GCOAC 24. A 12th value, ability 25, is computed too but not labelled.
Staff and scout bars are drawn as value / 99. Player bars add 10
first.
python SRC/pbdata.py show prints these bars for managers and scouts
(the job's own set, plus the manager set for coaches), and csv adds
them as columns.
The abilities no bar shows (confirmed, 2026-10-30). Most of them do something; the readers:
| Abilities | Read by | What they do |
|---|---|---|
| Manager 23–27 (GK, DF, DM, OM, FW coaching), 31–38 (the 8 systems) | the practice growth code, through table 9 of TRAINING_LIST.TBB (TRAINING_FORMAT.md): SIMPRG.REL 0x159b68 (my club's manager, team data +0x48be + n), 0x15ab58 (the youth team's coach) |
each coaches the matching aptitudes: GK coaching the GK aptitude, DF the side and centre backs, DM the wing backs and defensive midfield, OM the side and attacking midfield, FW the wingers and forwards, each system coaching that system's aptitude. From 50 up it adds (value × 0.2 − 1) % to the growth |
| Manager 7, 8, 9 (young, mid-career, veteran players) | SIMPRG.REL 0x15a428, for the first team (called at 0x15864c and 0x159654) |
by the player's age (PlPinfo +8): under 23 ability 7, under 30 ability 8, else 9 (team data +0x48c5–+0x48c7); returns (value − 50) ÷ 100 as a growth factor, so 99 is +49 % and 38 −12 % |
| Manager 7, for the youth team | SIMPRG.REL 0x15abe8 (called at 0x15a5c4), youth data +0x3f71 |
the same factor for every youth player, from the youth team's coach |
| Manager 6 (youth players) | none found | – |
| Scout 2 (money negotiation) | pwkMoney_GetMoveResearchMoney, GetMoveScoutListMoney, GetRentalResearchMoney, GetRentalScoutListMoney (0x2484d8–0x248938), pwkMoney_GetScoutExpectSalary (0x249e80), plSinfo_CalcHexagon (0x218a30) |
what the scout's research and scout lists cost for transfers and loans, and his expected salary |
| Scout 8–20 (the 13 positions) | plSinfo_CalcAptPos (0x218bd8, PlSinfo +0x39) |
each to a level 0–4: below 55, 65, 75, 85, and above |
| Scout 32–44 (the 13 regions) | plSinfo_GoodRegion (0x218b48, PlSinfo +0x51) |
the scout's best region |
| Scout 26–31 (England … the Netherlands) | none found | – |
(plSinfo_GoodAbil, 0x218b88, returns the best of all 49 values from
PlSinfo +0x2d, the special searches included.) So the youth team's
coach is judged on "young players" (7), and nothing reads "youth players"
(6), which may have been meant there: 0x15abf8 reads +0x3f71 where
+0x3f70 would be ability 6. Nothing reads a scout's six nations either.
Each is 27,950 u16. Entry 2 is a ranking of the player ids: players
0–25,590 sorted by rank (highest first), then main position, then
nationality, followed by ids 25,591–27,949 in order. Entry 3 is its
inverse, each player's place in the ranking. Both hold for all 27,950
players (empirical, sorted by the stored +0x18, +0x1c and
+0x14; pbdata.py info checks it).
Confirmed from the code. A table of 16 × 13 s32 at 0x52fbf8, fixed
in the executable, gives the first place of each group: row 15 − rank,
column = main position, −1 for an empty group. Row 0 has two groups
(place 0 in column 9, places 1–2 in column 12), and the last group starts
at 25,585 (row 15, column 12). The readers:
serchPinfo(0x20d470) finds a group's range of places and reads the players through entry 2 (0x390670). The last range ends at0x63f7. Given a nation, it narrows the range to that nationality, which is why each group is sorted by it.getPinfoRank(0x20d908) andgetPinfoApos0(0x20d9a0) find the row and column that hold a player's entry-3 place. So for players below0x63f7, the game's rank and main position come from the place, not from+0x18/+0x1c.
Nexxus's report: the game never shows a player's rank (no number, class or
stars), so an edit to it can only be seen through what it controls: the
Transfer List bands
(PLRESOURCESIM_FORMAT.md), national
call-ups and popularity changes.
Confirmed: the run-time rank ranges at 0x5eac08 are built from the
group table when the database is set up (0x20c4a8, after
initPlCoreInfoSub): each row runs from its first group's start to one
before the next row's first start, the last to 0x63f6. Entries 2 and 3
are read only through getPlno (0x20c2b8), checkPlno (0x20c2f0),
serchPinfo, getPinfoRank and getPinfoApos0, and the group table
exists once on the disc. Inside a group there is no order by id or by
ability (empirical: about half of the 18,920 neighbouring pairs go
each way).
For editing: changing the rank or main position in a record alone has
no effect in the game for ids below 25,591, and changing the nationality
alone moves the player out of his nation's call-up search. pbdata.py set and import with --sles <SLES_541.51> <out SLES> re-sort entries 2
and 3 (keeping each group's order, so only moved players change place)
and write a copy of the executable with the group table to match. They
check first that the input executable's table belongs to the input pack.
Without --sles they say when an edit leaves the ranking out of order.
pbdata.py roundtrip --sles checks the rebuild: from the disc's records
it gives back entries 2 and 3 and the executable's table exactly. An
emulation of getPinfoRank and getPinfoApos0 on the result agrees
with the records for all 25,591 players, before and after an edit.
Tested in PCSX2: free agent A.Rankin (12,804, goalkeeper, stored
rank 9) was lowered to rank 5 with --sles and renamed
RANKFIX.Rankin, and put in free-agent slot 627 (the slot where rank-5
players showed before, with slot 50 given a rank-14 player so the same
slots pass the filter). In a new career he showed on the new club's
Transfer List in 2006–07 (GK, 22, ENG, no team), which a rank-9 free
agent can't
(PLRESOURCESIM_FORMAT.md),
and his detail page opened normally. A main-position change goes through
the same table but wasn't tested on its own.
Confirmed, SIMPRG.REL and SLES_541.51. In a career a national
squad is picked from the club players when it is called up:
| Address | Symbol | What it does |
|---|---|---|
SIMPRG.REL 0x160b68 |
jmNT_CallCheck |
on a day where pwkSche_IsNationalConvene is true, calls up every squad (jmNT_CallAll) and sets event flags 0x13f–0x141 |
SIMPRG.REL 0x1603c8, 0x160210 |
jmNT_CallAll, jmNT_makeNationList |
lists the nations with an international competition on the schedule and runs plTeam_CreateNationTeam(team, squad, 23) for each |
SIMPRG.REL 0x15fbe8 |
jmNT_NewCall(team) |
the same for one team |
0x226338 |
(unnamed) | the squad's shape: counts the positions in the national manager's three formations (PlMbase +0x3d, see the staff editors) and scales them to 25 places over the 13 positions |
0x227470 |
plTeam_CreateNationTeam |
for each position, walks the ranking best first (serchPinfo with rank, position and the team's nation) and fills a pool of up to 38 |
0x534148 |
per-position limit | at most 1 goalkeeper and 3 players per other position from one club |
0x2272f8, 0x227200 |
_sortBaseListFromNationalTeamPoint, _getNationalTeamPoint |
sort the pool by a national-team score before the squad is kept |
plTeam_CreateNationTeam takes only players whose current team (bits 22
and up of the run-time core info) is your club (1), another club
(2–459) or team 465. From your club it takes at most 8, and none
with flag 0x300 in PlPinfo +0x20c. The search stops at 0x63f7, so
the fixed national records (26,041+) are never called up.
So to change who plays for a country in a career, edit the club players (their nationality and their place in the ranking, see above). The fixed blocks only change VS mode.
Nexxus's report (PCSX2): with record 26,046 renamed NT.Terry.VS, the
career national squads list each player's club, so they are drawn only
from club players. The renamed fixed record doesn't appear, as the
search limit above says.
What _getNationalTeamPoint scores, why team 465 is accepted (it is
the Netherlands' national team,
PLRESOURCECOMMON_FORMAT.md),
and what flag 0x300 means aren't traced.
The same header and identical entries 2 and 3, but entry 1 is empty (0
bytes). The European disc carries no Japanese player records. pbdata.py info reports this as a note, not a problem.
pbdata.py can write the database back out. Each record is re-encoded
from its stored field values in readBits order: the 19 name bytes as
they were, every field at its width, then the padding bits as they were.
Entry 1 is then replaced in a copy of the pack. Records are fixed-size,
so the entry keeps its offset and size and nothing else in the file
moves. pbdata.py roundtrip does this for all 31,950 records without
changes, and the result is byte-identical to the disc's file (a
regress.py check).
Edits are given as the values show prints, and are converted back:
addfields subtract their offset (age 30 is stored as 14).- Abilities must be one of the 32 table values, 38–99.
req_statusmust be one of the 16 table values, stored as its index. The game reads only the low 4 bits of the 16-bit field, so an unchanged value keeps its stored bits.- Signed manager fields must fit 9 bits.
- Names are cp850, at most 18 bytes, zero-padded to 19 (every name on the disc has a terminator).
The limits are mostly the field widths, not what the game considers
sensible. The exception is age, height and weight: the decoder adds
their offset and stores the sum in a byte, so pbdata.py refuses values
above 255. Height is the only one where that matters (the field alone
would allow up to 405 cm). Otherwise the game takes whatever fits.
Tested in the game (PCSX2, new game, patched with patch_disc.py):
Terry renamed J.Terry.MOD, 96 kg, left-footed, with abilities
alternating 38 and 99. The name, weight, leg and the bar pattern all
showed as predicted by pbdata.py show (low SPEED and FK, high STAMI,
HEAD, INTER and MARK). A height of 313 cm showed as 57 cm, which is what
led to the byte limit above. A second test with 255 cm showed 255 cm.
python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out.PAC 101 age=30 ability.13=99 name=J.Terry
python SRC/pbdata.py csv DAT/PARAM/PBDATA_EU.PAC players players.csv # edit in a spreadsheet
python SRC/pbdata.py import DAT/PARAM/PBDATA_EU.PAC out.PAC players players.csv
# rank, main position or nationality: re-sort the ranking, patch the executable
python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out.PAC 101 rank=14 position.0=12 --sles ISO/SLES_541.51 out_SLES_541.51
python SRC/patch_disc.py patch game.iso out.iso PARAM/PBDATA_EU.PAC=out.PAC disc:SLES_541.51=out_SLES_541.51 --copies --dat DATimport writes only the values that differ from the pack, so an unedited
CSV gives an identical file. The bar and entry2/entry3 columns are
derived and ignored. patch_disc.py puts the edited pack on a disc
(REBUILD.md).
The editor's People tab (SRC/editor.py) edits players, managers and
scouts through the same code: pbdata.edit_spec gives each named field
its range or named values, unnamed fields stay read-only, and a save
that changes the ranking also writes the executable, as --sles does.
Tested in PCSX2: Van der Sar (250) edited only in the People tab
showed 255 cm, 45 kg, LEFT, full bars and every position cell lit on his
detail screen; his age and shirt came from OTEAMMEMBER.TBB, as the
tab's note says (INITTEAM_FORMAT.md).
- Whether anything reads
castno(+0x30, always 0); no reader was found. - Tendencies (The tendencies): what the two checks
that make the team's situation 3 or 4 test (
0xf5728,0xf5c18), which tactics slider sets where situation 1 starts, which side0xa9e20picks at a penalty, which side's defensive line the press row uses, which distance the second press float is compared with (VU0 code), and what byte 1 of the per-side record at match data+0x9764(set from a match menu) does. No screen shows the tendencies (Nexxus's report), so an edit can only be seen in how a player plays; reading the match's parameter copy over PINE during a match would at least confirm that an edited value reaches it. - Kit style: whether the dissatisfaction code's use of
+0x58/+0x59is intended (Kit style). The cap on an edit player and the gloves in a cold career match are traced in the code but not yet seen in the game. - How much each parameter changes what its readers do
(Who reads each parameter gives the
readers): what tells apart the kick kinds that share a builder
(The kind table: 3 and 4, the in-play pairs, the
tackles), where kinds 27–29, 31, 34 and 35 (kick-off, penalty, goal
kick) are first set, flag bits
0x200–0x2000and kind 5, the 45 calls with no class, and the sources of parameters 5, 16 and 24. - What each speech tone and condition type value is, what the ball touch does (match parameter 139 has no reader), and which dribble style looks like what.
- The hexagon labels are matched to indices from the data and VPF's
pages. The label order in
GP::CHexWindowBase::DrawString(0x27c700) comes from a screen layout and hasn't been traced. - What the play styles do in a match, beyond which abilities grow and which roles read them (Who reads each parameter).
- The code that turns the leg field into
PlPinfo +0x1c4. - The job change: what
pwkTeam_GetCompAveragemeasures (it must be at most 45,000), and which screen writes job 6 atSIMPRG.REL 0xd64d4. - What reads the staff fields only the source table names (rank, face,
appeartype, and the manager's comptype), and what the model pattern
(
+0x65) does. - Whether the match's two readers of the selection policy
(
GAMEPRG.REL 0xd96e8and0xf3cb0,PlTeamData +0x4888) were meant to read the match policy (+0x4889), which nothing reads; what the word tables at0xf3cb0decide (probably the substitutions). An in-game test could tell: change a computer club's manager's selection or match policy and watch its tactics and substitutions (The four policies). - Which end of a tactics slider (0–6) means more attacking, and so
whether
plTeam_SetStyleTeamParamgiving a strength ratio above 1.19 column 0 (the same as up to 0.79) instead of column 4 is intended. - Whether anything reads manager ability 6 (youth players) or the scouts' nation abilities 26–31; no reader was found (Manager, coach and scout bars), and whether the youth coach's growth factor was meant to read ability 6 instead of 7.
- Header
+0x14,+0x24and the last 8 header bytes. - What
_getNationalTeamPointscores.
python SRC/pbdata.py info DAT/PARAM/PBDATA_EU.PAC DAT/PARAM/PBDATA_JP.PAC
python SRC/pbdata.py list DAT/PARAM/PBDATA_EU.PAC --find terry
python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 100 m:0 s:0 # every field
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x1c98 340 --sles ISO/SLES_541.51 # manager editor pages
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x97a0 70 --sles ISO/SLES_541.51 # scout editor, special searches
python SRC/pbdata.py csv DAT/PARAM/PBDATA_EU.PAC players players.csv
python SRC/initteam.py squads DAT/PARAM 7 # Chelsea, with names
python SRC/sles_disasm.py ISO/SLES_541.51 dis initBpmaster getPbase readBits__Q25Param11PlBitsClassi
python SRC/sles_disasm.py ISO/SLES_541.51 dis plBits_DecPlPbaseEx plBits_DecPlMbaseEx plBits_DecPlSbaseEx