Fiche STE-G3 — Niveau N2 — Modèle prévu (modèle de l'agent lp-impl) | utilisé claude-opus-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat (préchargés), sk-change-safety (invoqué) # STE-G3-IMPL : `NotifyCoupleTeleported()`, reprise de `LastSafe` après une téléportation par CAM (2026-10-14) **Résultat : réussi (écrit, non compilé).** Fonction publique ajoutée à `ULPShowCoupleSubsystem` : une seule écriture de `LastSafe` à la place actuelle du meneur, aucun balayage, gardes avant toute écriture. Compilation confiée au build COMMUN-11 (fiche § 3). | Mesure | Avant (2026-10-14, entrée de fiche) | Après (2026-10-14) | |---|---|---| | MD5 `Source\LifePlay\LPShowCoupleSubsystem.h` | `57dc8cd468d2fb5de231bfbe25346389` | `44c721ee9e82bdfdf7571e493af70e39` | | MD5 `Source\LifePlay\LPShowCoupleSubsystem.cpp` | `35cf128c8c3e059130e7cf48d9fa0454` | `3cdd572d78b691eef0a3f02b1b37a486` | | Fins de ligne (`git ls-files --eol`) | `i/crlf w/crlf` (les deux) | `i/crlf w/crlf` (les deux) | Commande des MD5 : `certutil -hashfile "" MD5`. Mesuré, `git -C "D:/Game/LifePlay-master 5.8" diff --stat -- Source/LifePlay/LPShowCoupleSubsystem.h Source/LifePlay/LPShowCoupleSubsystem.cpp` : ``` Source/LifePlay/LPShowCoupleSubsystem.cpp | 19 +++++++++++++++++++ Source/LifePlay/LPShowCoupleSubsystem.h | 4 ++++ 2 files changed, 23 insertions(+) ``` ## 1. Fichiers modifiés - `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPShowCoupleSubsystem.h` l. 28-30 (section `public`, après `Deinitialize`) : commentaire de deux lignes et déclaration `void NotifyCoupleTeleported();`. - `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPShowCoupleSubsystem.cpp` l. 806-824 (après `KeepTogether`) : définition. Diff résumé de la définition : ```cpp void ULPShowCoupleSubsystem::NotifyCoupleTeleported() { ACharacter* Who = Lead.Get(); if (!bEngaged || !Who || !Partner.Get() || !GetWorld()) { return; } const FVector Now = Who->GetActorLocation(); if (!(Now - LastSafe).IsNearlyZero()) { UE_LOG(LogLifePlay, Warning, TEXT("Show couple : place reprise apres teleportation %s (ecart %.0f)"), *Now.ToString(), FVector::Dist(LastSafe, Now)); } LastSafe = Now; LastBlocker.Reset(); } ``` (plus deux lignes de commentaire en tête du corps, non recopiées.) ### 1.1 Pour CAM : nom, signature, en-tête, appel - **Nom** : `ULPShowCoupleSubsystem::NotifyCoupleTeleported`. - **Signature** : `void NotifyCoupleTeleported();` (publique, non `const`, sans paramètre, sans retour). - **En-tête** : `#include "LPShowCoupleSubsystem.h"` (même module `LifePlay`, classe `LIFEPLAY_API`, `LPShowCoupleSubsystem.h:20`). - **Appel depuis le C++, pas depuis un Blueprint** : B2p-F est « C++ `LPCameraModeSubsystem`, aucun asset » (`CAM-COMMUN9-DIAG.md` l. 129 et l. 215) ; G3 est « appelée par B2p-D après une pose libre validée par G1 » (l. 191-193) ; le glisser B2p-D est du C++ (`LPCameraModeSubsystem.cpp:1064-1068`). Donc **pas de `UFUNCTION`** (fiche § 3 : seulement si un Blueprint l'appelle). - Forme d'appel conseillée (même accès que `LPCameraModeSubsystem.cpp:287`, `UGameInstance::GetSubsystem` `GameInstance.h:440`), **après** la boucle qui déplace le couple (`LPCameraModeSubsystem.cpp:1065-1068`) et seulement si G1 a validé la place : ```cpp if (ULPShowCoupleSubsystem* Show = GI->GetSubsystem()) { Show->NotifyCoupleTeleported(); } ``` ### 1.2 Grandeur reprise (point amont de la fiche § 3) - `KeepTogether` écrit `LastSafe = Who->GetActorLocation();` avec `Who = Lead.Get()` (`.cpp:752`, `.cpp:804`) ; `Engage` écrit la même grandeur (`LastSafe = InLead->GetActorLocation();`, `.cpp:400`). La fonction reprend exactement celle-là : la place de l'acteur meneur (`Lead`), pas la boîte ni le partenaire. Établi. - Effet sur l'image suivante : `KeepTogether` calcule `Delta = Now - LastSafe` (`.cpp:760-761`) ; si CAM n'a plus rien bougé, `Delta` est nul et `KeepTogether` sort sans balayer (`.cpp:762-765`) ; sinon il balaie depuis la place libre posée par CAM (`ResolveCoupleMove(…, LastSafe, Delta, …)`, `.cpp:792-793`). Établi par lecture. ### 1.3 États remis à zéro - `LastBlocker` (« dernier bloquant ») : vidé. Il n'est lu que pour dédoublonner le journal (`.cpp:781` coque, `.cpp:794` « bloque par ») ; aucun effet sur le mouvement. Conséquence : le prochain blocage (ou la coque) est réécrit une fois au journal, utile pour l'essai CAM-74. Établi (toutes les occurrences lues : `.h:53`, `.cpp:781, 790, 794, 798, 823`). - Aucun autre état de balayage dans le sous-système : profondeur, normale et bloquant sont des variables locales du résultat `Move` (`.cpp:792-799`). `Box`, `FittedPair`, `RejectedPair`, `LoggedExtent` (mesure de la boîte), `bPawnLogged`, `SavedFurniture`, `SavedSeats`, `SavedPartnerMove` (collisions, mouvement gelé) ne sont pas des états de balayage : non touchés. Établi par lecture de l'en-tête (`.h:45-75`). - Résolveur partagé : `Grep "static "` sur `Plugins\LPAnimCore\Source\LPAnimCore\LPCoupleSimCore.cpp` → 1 résultat, `static const TCHAR* Excluded[]` (l. 46), table constante : pas d'état gardé entre deux appels. `LPCoupleSim.h` cherché seulement avec le motif `static … =` (0 résultat) : absence d'autre état statique **estimée**. ### 1.4 Gardes, appel répété, appel hors scène - Gardes avant toute lecture ou écriture : `bEngaged` faux (hors scène, ou `Release` passé : `.cpp:668-674`), meneur absent ou détruit (`Lead.Get()` nul), partenaire absent, monde absent (`GetWorld()` nul) → retour sans rien changer, sans plantage (aucun déréférencement avant les gardes). - Pas de garde sur la boîte (`Box`) : non nécessaire pour reprendre `LastSafe`. - **Appel répété** : sans danger. `LastSafe` reçoit la même valeur (idempotent) ; `LastBlocker` est revidé ; la ligne de journal n'est écrite que si la place a bougé de plus de `UE_KINDA_SMALL_NUMBER` (`Vector.h:600`). - **Appel hors scène** : sans danger, la garde `bEngaged` sort avant toute écriture. Au premier tour d'une scène, `Engage` repose de toute façon `LastSafe` sur le meneur (`.cpp:400`). - Contrat pour CAM : n'appeler qu'après une pose **validée par G1**. Si la place posée recouvrait un meuble, la fonction en ferait la nouvelle place sûre et `KeepTogether` ne dépénètrerait pas tant que le meneur ne bouge pas (`Delta` nul, `.cpp:762-765`). Estimé par lecture. ### 1.5 API moteur relues (UE 5.8, `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\`) - `TWeakObjectPtr::Get()` : `Runtime\Core\Public\UObject\WeakObjectPtrTemplates.h:141` (`[[nodiscard]]`, valeur utilisée). - `UObject::GetWorld() const` : `Runtime\CoreUObject\Public\UObject\Object.h:711` (déjà appelé par le fichier, `.cpp:289`, `.cpp:755` ; `UGameInstanceSubsystem` ne le redéfinit pas : `GameInstanceSubsystem.h` et `Subsystem.h` sans `GetWorld`). - `AActor::GetActorLocation() const` : `Runtime\Engine\Classes\GameFramework\Actor.h:2502`. - `TVector::IsNearlyZero(T Tolerance = UE_KINDA_SMALL_NUMBER) const` : `Runtime\Core\Public\Math\Vector.h:600`. - `TVector::ToString() const` : `Vector.h:861` ; `TVector::Dist(const TVector&, const TVector&)` statique, rend `T` = `double` pour `FVector` : `Vector.h:1015` (format `%.0f`, comme `.cpp:796`). - `FString::Reset(int32 NewReservedSize = 0)` : `Runtime\Core\Public\Containers\UnrealString.h.inl:332`. - `UGameInstance::GetSubsystem() const` (pour CAM) : `Runtime\Engine\Classes\Engine\GameInstance.h:440`. - `LogLifePlay` : `Source\LifePlay\LifePlay.h:9`, inclus par `.cpp:24`. ## 2. Écarts à la SPEC - E1 : `sk-change-safety` invoqué **après** les deux Edit et non avant. Instantané pris par l'orchestrateur et vérifié ensuite : `D:\Game\LifePlay_Backup_5.5\STE-G3\Source\LifePlay\LPShowCoupleSubsystem.h` MD5 `57dc8cd468d2fb5de231bfbe25346389`, `.cpp` MD5 `35cf128c8c3e059130e7cf48d9fa0454` (identiques à l'entrée de fiche), `manifest.csv` présent. Sans effet sur le résultat. - E2 : la ligne `UE_LOG` est conditionnelle (écrite seulement si la place a changé), pour ne pas écrire une ligne par appel sans déplacement. Elle reste une seule ligne, catégorie `LogLifePlay`, niveau `Warning` comme le reste du fichier. - E3 : pas de `UFUNCTION` : l'appelant est du C++ (§ 1.1), conforme à la fiche § 3. - E4 : la fiche ne dit pas où placer la fonction dans le `.cpp` ; placée après `KeepTogether`, à la fin du fichier. - Budget suivi : rubrique 9 de la fiche (20 tours), et non le budget N3 du message de lancement. ## 3. Instantané Pris par l'orchestrateur (fiche § 2) : `D:\Game\LifePlay_Backup_5.5\STE-G3\` (2 fichiers + `manifest.csv`), relu par MD5 le 2026-10-14 (§ 2 E1). Retour arrière (proposé, non lancé) : recopier les deux fichiers de l'instantané sur `Source\LifePlay\` (`Tools\Restore.ps1 -Lot STE-G3`, à décider par l'orchestrateur). ## 4. Ce qui n'a pas été fait - Aucune compilation (build COMMUN-11, `lp-builder`), aucun éditeur, MCP, commandlet ni jeu (fiche § 6). - L'appel côté CAM (B2p-F) n'est pas écrit : fichier CAM, hors périmètre. - Ligne STE-G3 du registre `STE-TACHES.md`, entrée CHG, commit : orchestrateur et gardien. - Le mode « tourner » de la prise (`CoupleGrabMode == 2`, `LPCameraModeSubsystem.cpp:1071-1085`) déplace aussi le meneur autour du pivot ; le DIAG ne prévoit l'appel qu'après le glisser. Question pour CAM, non traitée. ## 5. Points à confirmer en VAL - Diff limité aux deux fichiers : `git diff --stat` = 2 fichiers, 23 insertions, 0 suppression. - `LastSafe` écrit une seule fois, à `Lead.Get()->GetActorLocation()`, même grandeur que `.cpp:804`. - Aucun appel à `ResolveCoupleMove` ni à un balayage ou recouvrement dans `NotifyCoupleTeleported`. - Gardes `bEngaged`, meneur, partenaire, monde placées avant toute lecture ou écriture. - `LastBlocker` seul état remis à zéro, lu seulement pour le journal (`.cpp:781`, `.cpp:794`). - Déclaration en section `public` (`.h:30`), sans `UFUNCTION`. - Fins de ligne CRLF conservées (`git ls-files --eol` : `w/crlf` pour les deux fichiers). - Aucun nom local qui masque un membre (`Who`, `Now` : pas de membre de ce nom, `.h:45-75`). - Build COMMUN-11 : `LPShowCoupleSubsystem.cpp` compilé, 0 erreur, 0 avertissement nouveau (C4458, C4456 compris). - En jeu, CAM-74 : couple coincé, glisser vers le milieu de la pièce → ligne `Show couple : place reprise apres teleportation …` puis plus de `bloque par … profondeur 3` à chaque image. - En jeu : volume de la ligne « place reprise » pendant un glisser continu (une ligne par image déplacée si CAM appelle à chaque image). ## 6. Fichiers à commiter Dépôt jeu `D:\Game\LifePlay-master 5.8`, branche `ue5.8` : - M Source/LifePlay/LPShowCoupleSubsystem.h - M Source/LifePlay/LPShowCoupleSubsystem.cpp - A Docs/Migration/etat/ST/STE/STE-G3-IMPL.md Tours : 15 / 20