# S4-IMPL-H — Découpe de `LPPairSandbox.cpp` (fiche S4-T2) - **Date :** 2026-10-06 - **Fiche :** S4-T2 | **Agent :** A-IMPL-H | **Rôle :** Implémentation - **Modèle prévu :** `claude-opus-5-thinking-high` (M3) - **Amont :** `S4/S4-CART.md` (S4-T1, lecture seule) - **Statut :** **compilé** — `LPSandboxEditor` **et** `LPSandbox` Win64 Development, 0 erreur / 0 avertissement - **Commit addon :** `32ad09c` — `S4-T2: decoupe de LPPairSandbox.cpp en 7 unites de traduction` > **Le repo jeu n'est pas modifié par cette fiche, hors ce document.** Tout le code vit dans > `D:\Game\LifePlay-Sandbox\Plugins\LPSandbox\Source\LPSandbox\`. > `LPInteractionSandbox.*` (legacy, 441 LOC) **n'est pas touché** : sa suppression est le lot S4-T3. --- ## 1. Résultat : LOC par fichier Avant : `LPPairSandbox.cpp` = **4274 lignes**, `LPPairSandbox.h` = 322. | Fichier | LOC | Contenu | |---|---:|---| | `LPPairSandbox.h` | **332** | UCLASS inchangées + 3 déclarations privées + 4 déclarations anticipées | | `LPPairSandboxMenu.h` | **73** | `FPairSandboxMenu`, `PairSandboxMenu::FindChoice` / `MakeCombo` | | `LPSandboxActionVM.h` | **55** | `PairSandbox::FAction` / `FPerson`, `extern` des globales, 4 prototypes | | `LPSandboxRoomBuilder.cpp` | **316** | `.lpworld` (`Token`, `FProp`, `LoadWorldFile`), `Stage::Build` (mobilier, murs, éclairage) | | `LPCoupleSim.cpp` | **477** | `PlayIdle`, `ConfigureCapsule`, `Weld`, `Apply`, `Toggle*`, `PlayPair`, `MoveCouple`, `FitCoupleBox`, `LoadQuaternius`, `SelectBody`, `ApplyBody`, `RefreshPose`, `PlayWalk` | | `LPSandboxActionVM.cpp` | **647** | `Tokenize`, `FWhoEval`, `WhoAllows`, `WhereHas`, `LoadActions`, `ReadInteractKey`, `CollectActions` | | `LPSandboxInteraction.cpp` | **493** | `LoadChat`, `BeginInteraction`, `ApplyFilters`, `ApplyInteractionView`, `ShowChat` / `HideChat`, pastille de touche, `BuildFilterPanel` | | `LPSandboxMeasure.cpp` | **837** | `EndMeasure`, `BeginMeasure`, `ReplayMeasure`, `ShowBestModel`, caméras fixes et libre, `DrawMeasureFigures`, `ScoreMeasureViews`, `BuildMeasurePanel` | | `LPSandboxMeasureTour.cpp` | **743** | `PairSandboxAuto`, `Toggle{Raw,Formula,T}`, `SetAutoRun`, `TickAuto`, `PairSandboxTour`, clips et navigation, `StartTour` / `TickTour`, vignettes, planches, `TickMeasureInput` | | `LPPairSandbox.cpp` | **807** | constructeurs, `Possess`, `SetStage`, `EndPlay`, `ResetView`, `Select*`, `ShowAnimMenu`, `Tick`, `GameMode` | - **Plus gros fichier : 837 LOC** (`LPSandboxMeasure.cpp`), sous l'objectif de ~900. - Trois fichiers dépassent le seuil souple de 800 (837, 807) ; les ramener sous 800 imposerait de couper `BeginMeasure` (374 lignes) et `ShowAnimMenu` (277 lignes après extraction) au-delà du découpage « pragmatique » demandé. - Total du module : 4274 → **4320 lignes de code déplacé + 240 lignes d'en-têtes et d'includes** répétés. ## 2. Écart assumé avec la fiche : 7 unités au lieu de 5 (+1) La fiche demandait 5 unités, avec autorisation de scinder Mesure en deux si elle dépassait 900. **Deux scissions ont été nécessaires**, pour une raison arithmétique : | Unité telle que définie par la fiche | LOC si non scindée | |---|---:| | Mesure (`BeginMeasure` … `draw` + panneau + touches) | **~1570** → scindée en 837 + 743 (prévu par la fiche) | | Interaction (VM `lpaction` + catalogue + chat + mode + filtres) | **~1140** → scindée en 647 (`ActionVM`) + 493 (`Interaction`) | La scission d'Interaction suit le fichier que la fiche nommait elle-même comme en-tête optionnel (`LPSandboxActionVM.h`) : l'en-tête a été créé **et** son `.cpp`, parce que le corps de l'évaluateur (`Tokenize` + `FWhoEval` + filtrage du catalogue, 592 lignes) n'a aucune raison d'être `inline`. La frontière est nette : `LPSandboxActionVM.*` ne connaît ni UE Slate, ni les classes de la sandbox — c'est du code libre, testable isolément. Les 5 noms de fichiers demandés existent tous et portent le contenu demandé. ## 3. Ce qui n'a pas bougé - **Aucune UCLASS déplacée.** Les 3 classes restent déclarées dans `LPPairSandbox.h`, donc UHT ne voit aucun changement de réflexion (`Reflection code generated` : 1 fichier, comme avant). - **Aucun corps de fonction réécrit.** Le découpage a été fait par extraction de plages de lignes : un diff ligne à ligne entre l'original et l'union des nouveaux fichiers ne remonte que 13 lignes retirées et 33 ajoutées, toutes listées au §4. - `LPSandbox.Build.cs` est inchangé : UBT ramasse les nouveaux `.cpp` du dossier plat. - `LPInteractionSandbox.*` (doublon connu de `LoadChat` / `ShowChat` / `HideChat`) est intact : lot S4-T3. ## 4. Les seules modifications de code (liste exhaustive) ### 4.1. Trois méthodes privées ajoutées à `LPPairSandbox.h` ```cpp TSharedRef BuildMeasurePanel(TSharedPtr& OutHolder); TSharedRef BuildFilterPanel(TSharedPtr& OutHolder); void TickMeasureInput(APlayerController* PC, ALPPairSandboxStage* Room, float DeltaSeconds, float DX, float DY); ``` Elles sortent trois blocs contigus de `ShowAnimMenu` (576 lignes) et de `Tick` (301 lignes), qui à eux seuls auraient laissé `LPPairSandbox.cpp` à ~1220 LOC. Elles restent **membres** : le panneau des filtres construit des cases à cocher sur des **pointeurs sur membres privés** (`&ALPPairSandboxCharacter::bWho1Married`), ce qu'une fonction libre ne peut pas faire (risque n° 3 de la cartographie). Déclarations anticipées ajoutées : `APlayerController`, `SBox`, `SVerticalBox`. Les corps appelants gardent la forme d'origine : | Avant (ligne d'origine) | Après | |---|---| | `SAssignNew(MeasureHolder, SVerticalBox) …` (3706-3822) | `BuildMeasurePanel(MeasureHolder)` | | `SAssignNew(FilterHolder, SBox) …` (3845-3878) | `BuildFilterPanel(FilterHolder)` | | `if (bMeasure && Room) { … }` (4125-4211) | `if (bMeasure && Room) { TickMeasureInput(PC, Room, DeltaSeconds, DX, DY); }` | Les holders restent des variables locales de `ShowAnimMenu`, recopiées ensuite dans `Menu->MeasureRow` / `Menu->FilterRow` comme avant : l'ordre d'évaluation de l'arbre Slate est identique. ### 4.2. Deux lambdas locales devenues fonctions libres `FindChoice` et `MakeCombo` (lignes 3512-3545) servaient à 9 listes déroulantes, dont 7 partent dans `BuildFilterPanel`. Elles deviennent `PairSandboxMenu::FindChoice` / `MakeCombo` dans `LPPairSandboxMenu.h`, corps inchangé. `MakeCombo` capturait `Self` sans l'utiliser : la capture inutile disparaît. Les 9 appels sont simplement qualifiés. ### 4.3. Globales du catalogue `PairSandbox::Catalog`, `PlaceChoices`, `WhenChoices` restent définies une seule fois (`LPSandboxActionVM.cpp`) et sont déclarées `extern` dans `LPSandboxActionVM.h` (risque n° 2 de la cartographie). `ShowAnimMenu` les lit toujours directement. ### 4.4. Namespaces de constantes `PairSandboxAuto` et `PairSandboxTour` **n'ont pas eu besoin d'un en-tête partagé** (risque n° 1 de la cartographie) : après ce découpage, leurs quatre consommateurs (`TickAuto`, `StartTour`, `TickTour`, `SaveVignette` / `WritePlanche`) vivent tous dans `LPSandboxMeasureTour.cpp`. C'est ce qui a dicté la frontière Mesure / MesureTour : `LPSandboxMeasure.cpp` ne référence aucune des deux. Le `static bool bPoseWritten` de `ShowFormulaDefault` reste unique, dans le même fichier (risque n° 4). ## 5. Compilation | Cible | Mode d'unité | Résultat | |---|---|---| | `LPSandboxEditor Win64 Development` | **non-unity** (7 `.cpp` exclus du blob, car dans le working set git) | succès, 0 avertissement | | `LPSandbox Win64 Development` (Game) | non-unity | succès, `Binaries/Win64/LPSandbox.exe` | | `LPSandboxEditor Win64 Development` | **unity** (`-Rebuild` après commit) | succès, 0 avertissement | Les deux modes ont été vérifiés (risque n° 5 de la cartographie) : la première compilation, faite avant le commit, a traité chaque nouveau `.cpp` **isolément**, ce qui prouve que les blocs `#include` de chaque unité se suffisent à eux-mêmes ; le `-Rebuild` après commit valide le chemin unity. Aucun include n'a dû être corrigé : la compilation est passée du premier coup. ## 6. Vérification « zéro changement de comportement » Contrôle automatisé : comparaison du multiensemble des lignes de code (hors `#include`, commentaires, accolades et crochets seuls) entre l'original et l'union des 9 fichiers produits. - **13 lignes de l'original absentes** : les 2 déclarations de lambdas `FindChoice` / `MakeCombo`, les 9 lignes d'appel à ces lambdas (requalifiées), les 2 `SAssignNew(…Holder, …)` (devenus `SAssignNew(OutHolder, …)`). - **33 lignes nouvelles** : les 9 appels requalifiés, 2 `TWeakObjectPtr Self(this)`, les 3 signatures des méthodes extraites et leurs 3 appels, 2 `#pragma once`, 2 ouvertures de `namespace`, et les 12 lignes de déclaration de `LPSandboxActionVM.h` / `LPPairSandboxMenu.h`. Tout le reste du fichier d'origine — y compris l'ordre des blocs de `Tick` (souris, déplacement, interaction, Échap, mesure, libellés), qui était le risque n° 7 — est recopié à l'octet près. ## 7. Reste à faire - **S4-T3** : supprimer `LPInteractionSandbox.*` (441 LOC), doublon de `LoadChat` / `ShowChat` / `HideChat` désormais isolés dans `LPSandboxInteraction.cpp`. Penser au `GameMode` `/Script/LPSandbox.LPInteractionGameMode` référencé par `Tools/debug/LifePlay_interaction_sandbox.bat`. - **Validation d'exécution (S4-VAL)** : non faite ici. Lancer les 3 modes (Pair, Interaction, Mesure) et comparer `Saved/Measurement/tour.csv` et une vignette avant / après, comme le recommande `S4-CART.md` §6.7. - Si le seuil souple de 800 LOC devient contraignant : découper `BeginMeasure` (`RunMeasureClip`, `SpawnMeasureCameras`) et `ShowAnimMenu`, ce qui ferait passer Mesure et l'orchestrateur sous 700.