# Foundational test — live custom XI via Freeze Lineup **Status: PENDING — test has not yet been run.** This is build-order step 1 from `docs/direction.md`: the test everything else in the direction pivot depends on. ## What changed since the first draft of this doc The first version of this test guessed at a "selection bias" DB field and a candidate squad/lineup table name, based on general FIFA-modding precedent that turned out not to hold for FLE's documented API — no such field appears anywhere in FLE's actual Lua API docs or its own example scripts. While researching an unrelated hotkey issue, a **confirmed, FLE-documented** mechanism for forcing a starting XI turned up instead: the **Formation Editor's "Freeze Lineup" feature** (FLE wiki, `Formation-Editor.md`): > This feature can be used in player career mode if you want to manage the > starting lineup of your team. Can be also used in manager career mode to > manually manage your next opponent's starting lineup. Steps (GUI, no scripting): open Formation Editor for a team → arrange players on the pitch → tick **Freeze Lineup** → `Data → Save`. This is real and documented, but it's GUI-only — there is no Lua function for it, and what DB write it actually performs under the hood is undocumented. This test is now two phases: confirm the GUI feature works at all, then reverse the DB write it makes so it can be replicated programmatically (required for the app→game bridge in build-order step 2, which needs this driven from outside the game, not from a person clicking checkboxes). Also fixed in this pass: `EditDBTableField`'s real signature, confirmed from FLE's own docs and `lua/scripts/99ovr_99pot.lua`, is `EditDBTableField(cell)` where `cell` is `row["fieldname"]` with `.value` mutated in place — **not** `EditDBTableField(table, row_index, field, value)` as originally (incorrectly) written into the first draft of the injector script. ## What this test settles Whether a *specific, externally-chosen* 11 players can be forced into a career (or Kick-Off) match's starting lineup, live, with no restart — and whether the mechanism that does it (Freeze Lineup's underlying DB write) can be driven by a script instead of a person clicking through the Formation Editor UI. If Freeze Lineup itself doesn't actually hold under match start (the wiki doesn't show it being tested against a live match, only "you should be able to see... when you play against them"), the whole bridge architecture in `docs/direction.md` §3 needs rethinking — there is no other documented mechanism for forcing a lineup. ## Prerequisites - FIFA 23 launched normally (FLE injected, EAAC neutralized — same baseline as `track-c-fut-table-test.md`) - A career save loaded (Freeze Lineup is documented for career mode specifically — confirm separately whether it does anything in Kick-Off, don't assume it does) - Note 11 player IDs from your club (`tools/squad-exporter/export_squad.lua` output, `playerid` field) that are NOT currently your starting XI ## Phase 1 — confirm Freeze Lineup actually holds into a match This has zero scripting and should be done first since everything else is wasted effort if it fails. 1. Open the Live Editor overlay (F9, or `Windows → Settings` from the overlay's own menu bar if the hotkey isn't registering — see the umu/Wine hotkey note below). 2. `Features → Teams` → find your team → `Edit`. 3. `Team → Formation` to open the Formation Editor. 4. Swap players around on the pitch so the XI differs from your current actual starting XI in some checkable way (e.g. swap two outfield players' positions, or bench/start a specific player). 5. Tick **Freeze Lineup**. 6. `Data → Save`. 7. Hide Live Editor (F9), save your career **on a new slot** (don't overwrite your main save in case this corrupts something), exit to main menu, reload that save, and check the team's lineup screen / play a match and watch who starts. **Record in the Results table below whether the frozen lineup actually took the pitch.** If not, stop here — Phase 2 is moot. ## Phase 2 — find the underlying DB write Only proceed if Phase 1 confirmed Freeze Lineup works. 1. In FLE's Lua Engine, run `tools/squad-injector/snapshot_lineup_tables.lua`. This dumps every DB table whose name contains `squad`, `lineup`, `formation`, `tactic`, `teamsheet`, `selection`, `players`, or `teams` to `C:\FIFA 23 Live Editor\openfut_snapshot_.json`. Note this filename — this is your **before** snapshot. 2. Without restarting or reloading, repeat the Formation Editor steps from Phase 1 (steps 2–6 only — open Formation Editor, change the lineup, tick Freeze Lineup, `Data → Save`). Don't save/reload the career between snapshot and this step — keep it to a single live session so the diff isn't polluted by other state changes. 3. Run `snapshot_lineup_tables.lua` again. This is your **after** snapshot. 4. Copy both JSON files out of the Wine prefix (same path pattern as `track-c-fut-table-test.md`: `~/Games/umu/.../drive_c/FIFA 23 Live Editor/`) and run: ```bash python3 tools/squad-injector/diff_snapshots.py before.json after.json ``` 5. The output shows exactly which table(s) and field(s) changed. This is the real, confirmed write Freeze Lineup performs — record it in the Results table below. ## Phase 3 — replicate the write via script 1. Open `tools/squad-injector/apply_lineup_write.lua` and fill in `TARGET_TABLE` and `TARGET_FIELDS` using Phase 2's diff output. 2. Edit `C:\FIFA 23 Live Editor\openfut_test_xi.json`: ```json { "team_id": 12345, "xi": [ { "player_id": 111111, "position": 0 }, { "player_id": 222222, "position": 5 } ] } ``` Use 11 entries. Position codes are **confirmed numeric 0–27** (`GK=0, SW=1, RWB=2, RB=3, RCB=4, CB=5, LCB=6, LB=7, LWB=8, RDM=9, CDM=10, LDM=11, RM=12, RCM=13, CM=14, LCM=15, LM=16, RAM=17, CAM=18, LAM=19, RF=20, CF=21, LF=22, RW=23, RS=24, ST=25, LS=26, LW=27`) — from `lua/scripts/export_season_stats.lua`'s `get_pos_name` table in FLE's own repo, not a guess. 3. Run `apply_lineup_write.lua` from FLE's Lua Engine. 4. Repeat the save-to-new-slot / reload / check-lineup verification from Phase 1, but this time without ever opening the Formation Editor — the write was made entirely from the script. ## Classification criteria ### "Confirmed — full mechanism works" Phase 1 holds, Phase 2 finds a clean diff, Phase 3's scripted write produces the same in-match result as the manual GUI path. **Verdict:** Build-order step 1 done. Proceed to step 2 (bridge transport) in `docs/direction.md`. ### "GUI works, script doesn't" Phase 1 holds but Phase 3's replicated write doesn't stick, even though the diffed fields matched what changed in Phase 2. **Verdict:** Freeze Lineup likely does more than a single DB field write (e.g. an internal engine call beyond `EditDBTableField`'s reach, or a second write the diff missed because it happened in a table outside the `KEYWORDS` filter in `snapshot_lineup_tables.lua` — widen the filter and redo Phase 2). ### "Freeze Lineup doesn't hold at all" Phase 1 fails — the lineup reverts to the game's own AI-picked XI regardless. **Verdict:** No confirmed mechanism exists for forcing a lineup. This kills the bridge architecture as designed in `direction.md` §3 and needs a return to first principles — there is no fallback documented anywhere in FLE's wiki for this specific case. ## A note on the umu/Wine F9/F11 hotkey issue If FLE's F9 (hide/show) hotkey isn't registering under umu, this is plausibly a Wine keyboard-hook limitation (FLE's global hotkey detection likely uses a low-level hook that doesn't translate cleanly through Wine's input layer) — not something documented anywhere in FLE's own troubleshooting docs, which don't mention Linux/Wine at all. F11 specifically has **no documented FLE function** — F9 is the only documented toggle. Workaround: click directly into the FLE overlay window (it should still be visible/clickable even if the hotkey doesn't fire) and use its own menu bar instead of relying on the hotkey. ## Results *(To be filled in after the test is run.)* | Field | Value | |---|---| | Date run | — | | Phase 1: Freeze Lineup holds into a match? | — | | Phase 2: table(s)/field(s) changed | — | | Phase 3: scripted write reproduces Phase 1 result? | — | | **Classification** | **PENDING** |