LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2p-F-VAL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

26 KiB
Raw Blame History

Fiche CAM-B2p-F-VAL — Niveau N1 — Modèle prévu sonnet | utilisé sonnet 5.5 (claude-sonnet-5-5) — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate

CAM-B2p-F-VAL : intérieur réel de la pièce, pose libre, focus clavier, VAL statique (2026-10-14, reprise G3 incluse)

VAL statique seulement : aucune compilation, aucun éditeur, aucun commandlet, aucun MCP, aucun jeu. Copie D:\Game\LifePlay-master 5.8, branche ue5.8 (relevée par git branch --show-current). Moteur lu : D:\Game\Launcher\Engine\UE_5.8\Engine\Source. Les deux fichiers sont non suivis par git (git status --short : ??) ; la base de comparaison est l'instantané D:\Game\LifePlay_Backup_5.5\CAM-B2p-F\ (manifeste : 2 fichiers seulement). Les sections 1 à 6 valident l'état .cpp 8C6A3D71… / .h 3DF4B511… ; l'ajout G3 fait ensuite est validé dans la section « Reprise G3 » (état final .cpp 03D85D28…, .h 4AE65994…). Le verdict final est celui de la section « Reprise G3 ».

1. Fiches amont relues

  • Docs/Migration/etat/CAM/CAM-B2p-F-IMPL.md (lu en entier).
  • Docs/Migration/etat/CAM/CAM-COMMUN9-DIAG.md § 6 (6.1 à 6.6), lignes 103-224 lues en entier.
  • Comparaison de règle : Source/LifePlay/LPShowCoupleSubsystem.cpp l. 38-62 (IsBedMesh) et l. 770-778 (IsIgnoredShell), lecture seule.
  • Pour la reprise G3 : Source/LifePlay/LPShowCoupleSubsystem.h (en entier) et .cpp l. 805-824 (NotifyCoupleTeleported), lecture seule ; Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSim.h.
  • Pas d'autre IMPL dans ce lot (B2p-A à B2p-E déjà validés, hors périmètre).

2. Points à confirmer des IMPL (IMPL § 3)

Identifiant Point Résultat
P1 Ligne F2 : source « murs Wall* », bornes X et Y dans celles de Box, plafond trouvé ou non reporté (COMMUN-11, journal de jeu). Statique : le format de la ligne est correct (16 spécificateurs, 16 arguments, § 3 C3e)
P2 G1 : lits non refusés, meubles et murs refusés reporté (COMMUN-11, CAM-73). Statique : la règle des lits est identique à IsBedMesh (§ 3 C5e)
P3 G1 : couple coincé se dégage vers une place libre (complet avec G3) reporté (CAM-74) ; la partie CAM est PASS-STATIC (§ 3 C5e) ; l'appel G3 est maintenant livré et validé statiquement (Reprise G3) ; l'effet réel reste à voir en jeu
P4 H1 et H2 : ZQSD remarche après un clic dans la vue ; un clic dans le champ y remet le focus reporté (CAM-75). Statique : logique conforme (§ 3 C5g) ; l'effet de SetAllUserFocusToGameViewport et le fait que le PlayerInput ne voie pas un clic pris par l'interface ne se prouvent qu'en jeu
P5 Compilation (/we4458, etc.) BLOCKED-pas de build (VAL statique demandée) ; relecture et contrôle d'ombrage PASS-STATIC (§ 3 C3, C4 ; Reprise G3). À rejouer par Build.ps1 + Build.ps1 -FromLog à la prochaine compilation
P6 Essais CAM-69 à CAM-75 reporté (COMMUN-11)

3. Critères rejoués

# Contrôle Commande ou lecture Résultat brut Verdict
C1 MD5, lignes, CR md5sum, wc -l, tr -cd '\r' | wc -c sur le .cpp et le .h .cpp 8c6a3d719fbc60900cc35bad46bf6d51, 1426 l., 0 CR ; .h 3df4b51155a42bcc0f99fcc26cfd4f6e, 130 l., 0 CR. Instantané : .cpp fff0fbb45d493b931b08780bf09547c8 (1163 l., 0 CR), .h 0edc1f76b7eec0d135ea046da2269be0 (121 l.) PASS : identique à l'IMPL (casse mise à part)
C2 Diff complet instantané → actuel git diff --no-index --numstat puis -U3 du .cpp, -U2 du .h, lus en entier .cpp 288 ajouts, 25 retraits (1163 + 288 − 25 = 1426) ; .h 9 ajouts, 0 retrait (121 + 9 = 130). Hunks : 2 includes, constantes (CoupleHeadroom, bloc F/G), FRoomInterior + ComputeRoomInterior + CoupleSpotFree + FocusedEditable, TickCoupleBounds (3 hunks), TickSexCamera (1), TickCoupleMouse (3 : LastSpotBlocker.Reset, bGrabJustEnded, F3 + G1). .h : 5 membres + 4 commentaires PASS : uniquement le périmètre de l'IMPL. Aucun ajout non demandé (voir D4 : un retour anticipé mort, sans effet)
C3a Types moteur Lecture de Box2D.h, Vector2D.h, Box.h, OverlapResult.h, World.h, CollisionQueryParams.h, CollisionShape.h (PhysicsCore), UnrealType.h, Actor.h, SceneComponent.h, SlateApplication.h, SEditableText.h, SharedPointer.h TBox2(EForceInit) explicite (Box2D.h:56), TBox2(const TVector2<T>&, const TVector2<T>&) (:67), Min/Max publics (:36-39), GetCenter() const (:272) ; explicit TVector2(const TVector<T>&) (Vector2D.h:132) : l'appel explicite FVector2D(Out.FloorBox.Min) convient ; TBox<T>::GetSize() (Box.h:419) et GetCenter() (:374) const ; FOverlapResult::bBlockingHit (OverlapResult.h:49), GetComponent() const (:39) ; UWorld::OverlapMultiByChannel(...) const (World.h:2418) ; AddIgnoredActors(const TArray<AActor*>&) (CollisionQueryParams.h:257), AddIgnoredComponent(const UPrimitiveComponent*) (:267) ; MakeBox(const FVector&) (CollisionShape.h:284) ; GetKeyboardFocusedWidget() const (SlateApplication.h:1653), SetAllUserFocusToGameViewport(EFocusCause = SetDirectly) (:649) ; SEditableText::GetText() const public (SEditableText.h:232) ; GetComponentScale() const (SceneComponent.h:1084) ; GetRootComponent() const (Actor.h:2481) ; GetComponents(TArray<T*, A>&, bool) const (Actor.h:4024) ; TSharedPtr(FNullTag* = nullptr) non explicite (SharedPointer.h:706) ; Engine/OverlapResult.h existe (Glob) PASS-STATIC : toutes les signatures utilisées existent avec la bonne constance
C3b FScriptArrayHelper sur const AActor* UnrealType.h:805-816 : surcharges ContainerPtrToValuePtr(UObject const*) et (void const*) ; FScriptArrayHelper(const FArrayProperty*, const void*) (:4285). Comparaison : ReadActors (cpp l. 215) fait la même chose avec const UObject* const AActor* → UObject const* (conversion dérivé vers base) est préférée à void const* : pas d'ambiguïté. Même motif que LPModDailies.cpp:137 (Room const aussi) PASS-STATIC
C3c Types float/double, constance Relecture ligne à ligne de ComputeRoomInterior (l. 337-440), CoupleSpotFree (l. 445-480), FocusedEditable (l. 483-491) FMath::Min/Max toujours à deux arguments de même type (double/double, int32/int32) ; FRotator(0.0, Yaw, 0.0) en double ; Margin double, constantes float élargies sans perte ; ComputeRoomInterior(const AActor*, FRoomInterior&), CoupleSpotFree(const UWorld*, const AActor*, const UPrimitiveComponent*, const TArray<AActor*>&, const FVector&, double, FString&) : les appels (l. 752, 1108, 1130) passent AActor*, const AActor*, const UWorld*, const UPrimitiveComponent* : conformes. FocusedEditable : cond ? Focused : nullptr converti en TSharedPtr<SWidget> (même motif que LPDirectControlSubsystem.cpp:951) PASS-STATIC
C3d Lambdas l. 354-390 (UseStructure, [&]) Captures par référence de Out, Floor, Walled, Center : toutes déclarées avant la lambda, durée de vie couvrant l'appel (la lambda ne sort pas de la fonction). Type de retour explicite -> bool, tous les chemins retournent PASS-STATIC
C3e Format UE_LOG Grep -o '%[-+.0-9]*[dfsi]' sur le fichier, lignes 764, 801, 848, 1142 Ligne 764 (F2) : 16 spécificateurs (7 %.0f, %s, %d, %d, 4 %.0f, 2 %.2f) pour 16 arguments (Area 4, FloorTop, Ceiling, Margin, *Source, Walls, bCeilingFound?1:0, FloorBox 4, Scale 2) : 16 et non 15, accord complet ; types : doubles pour %f, int32 pour %d, TCHAR* pour %s. Ligne 801 : 7 pour 7. Ligne 848 : 2 %s (Why est un const TCHAR*, *Editable->GetTypeAsString()) pour 2. Ligne 1142 : 1 %s pour *Blocker PASS-STATIC
C3f Qualification des aides Grep des appels Tous les appels aux aides passent par LPCameraModeLocal:: dans les fonctions membres (l. 752, 1108, 1130, 830, 850) ; dans l'espace, ComputeRoomInterior appelle ReadObject non qualifié (même espace) PASS-STATIC
C4 Ombrage de tout le fichier Membres du .h à jour (liste de 41 noms dont les 5 nouveaux) contre les locaux, paramètres et captures ; Grep des noms nouveaux (Interior, Area, Margin, Editable, Why, Blocker, bClick, bGiveBack, bSexOn, bGrabEnded, FocusWorld, FocusPC, LeadMesh, LeadCharacter, RootOffset) ; relecture des scopes Aucun nom de membre (bGrabJustEnded, bBoundsLogged, LastSpotBlocker, bSceneFocusChecked, bFocusClickWasDown, Root, Text, Viewport, …) repris par un local ou un paramètre. TickCoupleBounds : Floor (l. 736) précède Interior, pas de doublon ; Person en trois boucles sœurs (préexistant). TickCoupleMouse : Interior, Area, Margin, Blocker, Delta, Person dans des blocs imbriqués sans homonyme englobant ; les deux SurfaceZ (l. 1071, 1119) et les deux Aim (l. 1074, 1102) sont dans des blocs frères. TickSexCamera : bSexOn unique, Editable/Why/bClick/bGiveBack/FocusWorld/FocusPC dans le bloc else seulement, World/PC du reste de la fonction déclarés après ce bloc et sous d'autres noms. ComputeRoomInterior : locaux Floor, Center, RootComponent, Walled, UseStructure, Components, Prop, Inner, Array, Index, Location, HalfX, HalfY ; lambda : paramètre Comp, locaux Bounds, Size ; la boucle for (… Comp : Components) vient après la lambda, donc pas de masquage ; aucun homonyme paramètre/local (Room, Out). CoupleSpotFree : paramètres World, Room, Floor, Couple, RootSpot, Yaw, OutBlocker ; locaux Query, Overlaps, Center, Overlap, Comp, MeshComp, Mesh, MeshName : aucun doublon. FocusedEditable : Focused. C4459 (erreur aussi : UBT CppCompileWarnings.cs:89 donne /we4456 /we4458 /we4459 pour ShadowVariableErrors) : aucun local nouveau ne reprend un nom de portée d'espace de LPCameraModeLocal (liste des constantes et fonctions lue l. 34-511) ; Grep des déclarations de portée espace dans Source/** : seul MeshName existe ailleurs (LPDirectControlSubsystem.cpp:780, fonction), déclaré dans un fichier qui suit LPCameraModeSubsystem.cpp dans l'ordre alphabétique d'une unité unifiée, donc non visible ici PASS-STATIC : 0 masquage trouvé (C4456, C4457, C4458, C4459). Estimé pour C4459 inter-fichiers (dépend de la composition des unités unifiées)
C5a F1 : murs, replis, incohérence, plafond l. 337-440 Murs : composants de Room dont le nom commence par Wall (l. 397, insensible à la casse), critères haut > 100 cm et petit côté ≤ quart du grand, borne resserrée selon le côté par rapport au centre du sol. Repli 1 AllComponents[0..5] (l. 403, RoomStructureCount = 6) seulement si 0 mur ; repli 2 demi-côté d'échelle × 50 cm autour de Room (l. 421-433), dans la boîte du sol. Incohérence (largeur ou profondeur ≤ 1 cm) : retour à la boîte du sol, source sol (murs incoherents). Plafond : composant mince en Z (< 50 cm) et à plus de 150 cm au-dessus du sol, borne = Min.Z le plus bas, défaut sol + 350 PASS-STATIC (voir D1, D2)
C5b E1 : intérieur + marge 80, pièce étroite, plafond − 120 l. 758-790 Marge CoupleWallMargin = 80 ; si largeur ≤ 2 × marge, la correction va au centre de l'axe ; borne haute Ceiling − CoupleHeadroom (120). Z bas inchangé. Ligne de correction : plafond .. (racine au plus ..) PASS-STATIC
C5c F2 : une ligne par scène l. 704, 760-768 bBoundsLogged posé avant le log, remis à faux dans la branche « hors sexe » (l. 704). Placé après le retour « pièce cachée » : la ligne sort à la première image où la borne s'applique PASS-STATIC (voir D6)
C5d F3 : même intérieur dans le glisser l. 1105-1117 Même fonction, même marge ; condition de largeur identique. Une pièce étroite n'est pas recentrée par le glisser (E1 le fait, image suivante) : comportement préexistant PASS-STATIC
C5e G1 : refus, lits, ligne, place encombrée au départ, nullité l. 445-480, 1124-1143 Recouvrement bloquant (bBlockingHit) d'un composant dont le propriétaire est Room (Comp->GetOwner() != Room → ignoré), sol (Floor) et couple ignorés. Règle des lits : bed (IgnoreCase), fnt_ext011 (Contains), fnt_new045 et fnt_new235 (Equals) : identique caractère pour caractère à IsBedMesh (LPShowCoupleSubsystem.cpp:56-59) ; maillage nul : non lit dans les deux (STE : false ; ici nom vide donc bloquant). Boîte demi-étendue 78 × 46 × 50, centre à racine + 58 (bas à +8 cm), lacet du meneur. Ligne de refus : écrite seulement si Blocker != LastSpotBlocker. La place de départ n'est jamais testée (seule la cible l'est) : une prise qui part d'une place encombrée peut aller vers toute place libre. Nullité : Couple[0] (l. 1126-1131) : bScene exige Couple.Num() > 0 (l. 962) et la fonction retourne avant (l. 963-968) ; déjà utilisé l. 1084. RoomActor nul : CoupleSpotFree retourne vrai (l. 447) ; ComputeRoomInterior(nullptr, …) retourne faux (l. 340). Blocker est une FString (pas de pointeur) PASS-STATIC (voir D3, D7)
C5f F4 : bGrabJustEnded l. 688-689, 698-699, 723, 1093 Appelant : TickSexCamera (qui appelle TickCoupleMouse) puis TickCoupleBounds dans la même image. Pose au relâcher (l. 1093), lecture puis remise à faux avant toute sortie anticipée (l. 698-699), donc jamais de drapeau périmé ; l'image du relâcher n'est pas comptée par le témoin (l. 723). Le déplacement visé est celui que le résolveur de STE applique après les acteurs, vu à l'image où le mode passe à 0 PASS-STATIC
C5g H1 et H2 l. 815-852 Hors sexe : remise à faux des deux drapeaux, aucun appel Slate. H1 : Editable valide, bouton gauche ou droit enfoncé, front (!bFocusClickWasDown) → focus rendu au viewport, bMenuOnScreen recalculé. H2 : seulement à l'image où bSceneFocusChecked est faux, seulement si le type est exactement SEditableText et GetText().IsEmpty(). bSceneFocusChecked = true posé à chaque première image, même sans champ (une fois par scène). Editable déréférencé dans le log seulement si bGiveBack, vrai seulement si Editable.IsValid() dans les deux branches. GI non nul dans le bloc else (bSexOn l'implique) PASS-STATIC
C5h Déréférences nulles l. 337-491, 698-806, 815-852, 1105-1143 Room nul (ComputeRoomInterior, CoupleSpotFree) géré ; RootComponent nul géré (échelle 1) ; Floor testé par IsValid ; Comp testé dans la boucle et la lambda ; LeadCharacter/LeadMesh nuls gérés (RootOffset = 0) ; FocusPC->PlayerInput testé PASS-STATIC
C6 Cohérence DIAG / IMPL DIAG § 6.2 à 6.6 contre IMPL § 1 et § 2 Écarts déclarés ou visibles, tous acceptables : (i) format de la ligne F2 (DIAG : murs trouves n/6 ; réalisé : source …, murs n, marge ajoutée) : le DIAG § 6.4 impose les murs par nom, donc n/6 n'a plus de sens ; (ii) marge 80 au lieu de 30 : DIAG 6.4 G2 ; (iii) OverlapMultiByChannel à la place de OverlapAnyTestByChannel : nécessaire pour nommer le composant refusé ; (iv) G3 non appelé à l'époque, nom et signature proposés à STE : déclaré IMPL § 2, conforme à DIAG 6.4 (appel livré ensuite : Reprise G3) ; (v) CoupleFloorMargin laissé inutilisé (IMPL § 2) ; (vi) plafond − 120 : sol + 230 si plafond estimé (IMPL § 2). DIAG 6.2 voulait le repli d'échelle « aussi » comme borne ; réalisé en repli seulement si 0 mur (voir D1) PASS-STATIC
C7 Fichiers modifiés hors périmètre git status --short des deux fichiers ; manifeste de l'instantané (2 lignes) Seuls .cpp et .h figurent au manifeste ; git diff --no-index --stat instantané → arbre : les 25 retraits sont ceux du .cpp PASS

4. Diff lu en entier

  • Fichiers : Source/LifePlay/LPCameraModeSubsystem.cpp (+288 −25, 11 hunks) et Source/LifePlay/LPCameraModeSubsystem.h (+9 −0, 1 hunk) pour l'état validé en première passe ; l'ajout G3 est lu dans « Reprise G3 ».
  • Le compte recalculé (1163 + 288 − 25 = 1426 ; 121 + 9 = 130) égale celui de l'IMPL.
  • Aucune modification par script détectée (pas de fins de ligne mixtes : 0 CR dans les deux fichiers, LF comme l'instantané).
  • UE_VERSION_OLDER_THAN : non utilisé dans ces fichiers, sans objet.

5. Verdict

PASS-STATIC. Aucun défaut bloquant. Le critère « compile sous /we4456 /we4458 /we4459 » est établi par lecture seule (estimé) : il reste à rejouer par une compilation (Build.ps1 puis Build.ps1 -FromLog) et LPTestStringCompat n'a pas été rejoué (aucun build lancé). Verdict final après la reprise G3 : voir la section « Reprise G3 » (inchangé : PASS-STATIC).

6. Points à confirmer par l'utilisateur

  • COMMUN-11, ligne F2 : source « murs Wall* », murs n ≥ 1 (idéalement 4), bornes X et Y à l'intérieur de Box, plafond trouvé ou non (D1, D2).
  • CAM-69 à CAM-72 : le couple s'arrête à 80 cm des murs (glisser, flèches), plafond − 120 cm (Maj + molette).
  • CAM-73, CAM-74 : refus d'un meuble ou d'un mur sans refus d'un lit ; couple coincé dégagé (avec G3 : appel livré, effet à voir en jeu, voir R2 de la reprise).
  • CAM-75 : focus clavier rendu par un clic dans la vue, repris par un clic dans le champ ; ZQSD remarche (D5).
  • Scène d'extérieur (pièce cachée) : un glisser n'est pas refusé à tort par G1 (D7).
  • Compilation et journal d'avertissements au prochain build.

Tours : 24 / 40

Reprise G3 (appel du contrat STE, vérifié après la première VAL)

Même méthode, toujours statique. Instantané de l'état validé : D:\Game\LifePlay_Backup_5.5\CAM-B2p-F-G3\ (manifeste de 2 fichiers).

# Contrôle Commande ou lecture Résultat brut Verdict
G-1 MD5, lignes, CR md5sum, wc -l, tr -cd '\r' | wc -c .cpp 03d85d282a6306e6d2494d7dc2c253fc, 1437 l., 0 CR ; .h 4ae6599447750371cff7e0aac74eb93e, 132 l., 0 CR. Annonce : 03D85D28… 1437, 4AE65994… 132. Instantané G3 : .cpp 8c6a3d71… (1426 l.), .h 3df4b511… (130 l.) = l'état validé en première passe PASS : conforme
G-2 Diff contre l'instantané G3 git diff --no-index -U3 (.cpp) et -U2 (.h), lus en entier .cpp : 3 hunks, +11 lignes (1426 + 11 = 1437) : #include "LPShowCoupleSubsystem.h" (l. 20) ; GrabLeadStart = Couple[0]->GetActorLocation(); à la prise (l. 1072) ; commentaire de 2 lignes + bloc if (FVector::Dist(Couple[0]->GetActorLocation(), GrabLeadStart) > 1.0) { if (ULPShowCoupleSubsystem* ShowCouple = GI->GetSubsystem<ULPShowCoupleSubsystem>()) { ShowCouple->NotifyCoupleTeleported(); } } avant bGrabJustEnded = true; (l. 1095-1104). .h : 1 hunk, +2 lignes (130 + 2 = 132) : commentaire et FVector GrabLeadStart = FVector::ZeroVector; (l. 123-124). Aucun retrait PASS : exactement les changements attendus, rien d'autre
G-3 Contrat STE Read de LPShowCoupleSubsystem.h en entier ; Grep NotifyCoupleTeleported dans Source/LifePlay ; MD5 void NotifyCoupleTeleported(); déclaré en section public: (h l. 24-30), défini .cpp l. 807-824 : retourne sans effet si pas engagé, sans meneur, sans partenaire ou sans monde ; sinon LastSafe = position du meneur et LastBlocker.Reset(). MD5 en lecture seule : .h 44c721ee9e82bdfdf7571e493af70e39, .cpp 3cdd572d78b691eef0a3f02b1b37a486 (attendus 44C721EE…, 3CDD572D…). Les seuls appels dans Source : celui de CAM (l. 1101) PASS-STATIC
G-4 Un appel au plus par prise, rien par image Lecture l. 1063-1107 et 1082-1107 L'initialisation de GrabLeadStart est dans le bloc CoupleGrabMode == 0 qui crée la prise (une fois par prise). L'appel est dans le bloc if (!bHeld) qui termine la prise : il remet CoupleGrabMode = 0 et retourne false (l. 1105-1107), donc la prise suivante repart du bloc de création. Aucun appel ni déclaration dans la boucle du glisser (l. 1110-1160). L'appel est conditionnel : le meneur doit s'être déplacé de plus d'1 cm (FVector::Dist renvoie un double) depuis le début de la prise ; un simple clic ou une rotation sans déplacement du meneur n'appelle rien PASS-STATIC
G-5 Nullité et portée Lecture Couple[0] : bScene exige Couple.Num() > 0 et la fonction retourne avant (l. 963-968) ; Couple[0]->GetActorRotation() est déjà lu quelques lignes plus haut dans le même bloc (l. 1085). GrabLeadStart est posé à chaque création de prise avant que le mode passe à non nul puis qu'il serve : jamais lu non initialisé. GI : paramètre de TickCoupleMouse, déjà déréférencé au début (GI->GetWorld(), l. 961) ; l'appelant (TickSexCamera) n'y arrive qu'avec bSexOn (donc GI non nul). GetSubsystem rend un pointeur testé par le if d'initialisation PASS-STATIC
G-6 Compilation, types Lecture ULPShowCoupleSubsystem complet par l'include ; méthode publique non const appelée sur ULPShowCoupleSubsystem* ; GI->GetSubsystem<T>() sur UGameInstance* (modèle de GetSubsystem<ULPKeyboardSubsystem>() en l. 287). L'include LPShowCoupleSubsystem.h est du même module LifePlay que LPKeyboardSubsystem.h ; il tire LPCoupleSim.h du plugin LPAnimCore, déjà inclus par LPShowCoupleSubsystem.cpp dans ce module (même chemin d'inclusion). .generated.h à la charge d'UHT (aucune nouvelle UCLASS dans ce lot) PASS-STATIC
G-7 Ombrage C4456 à C4459 Grep \bShowCouple\b|\bGrabLeadStart\b dans Source/** ; liste des déclarations de LPShowCoupleSubsystem.h ; Grep des déclarations de portée globale de LPCoupleSim.h GrabLeadStart : une seule déclaration (membre, .h l. 124) ; aucun local ni paramètre homonyme (C4458 écarté). ShowCouple : une seule occurrence en identifiant, local à l'if d'initialisation (l. 1099) ; hors Source, lp.ShowCouple n'est qu'une chaîne (LPShowCoupleSubsystem.cpp:29). LPShowCoupleSubsystem.h apporte la classe ULPShowCoupleSubsystem (membres privés Lead, Partner, Box, LastSafe, … propres à cette classe, non visibles de ULPCameraModeSubsystem), les déclarations avancées ACharacter, UAnimSequence, UBoxComponent, UPrimitiveComponent. LPCoupleSim.h (Plugins/LPAnimCore/Source/LPAnimCore/) ne déclare à portée globale que des déclarations avancées de classes et namespace LPCoupleSim : aucun nom de variable ni de fonction globale, et le .cpp de CAM n'a pas de using namespace. Les noms de portée d'espace de LPCameraModeLocal (relevés en C4) ne sont pas touchés. Pas de macro ajoutée PASS-STATIC : 0 masquage (C4456, C4457, C4458, C4459). Estimé pour la visibilité en unité unifiée : LPShowCoupleSubsystem.cpp suit LPCameraModeSubsystem.cpp dans l'ordre alphabétique

Défauts de la reprise : aucun bloquant.

  • R1 (info) : le commentaire du .cpp parle d'une rotation « autour d'un pivot hors du meneur » : la condition Dist > 1.0 couvre bien ce cas (le meneur bouge) sans appel inutile sinon.
  • R2 (l. 1095-1104, incertain) : l'appel a lieu au relâcher, pas à chaque pas validé par G1 comme le prévoyait l'IMPL § 2 (« après un déplacement validé »). Pendant le glisser, le résolveur de STE balaie encore depuis l'ancien LastSafe à chaque image ; un couple coincé dont le premier pas est refusé par ce balayage ne serait donc dégagé qu'à partir du moment où le résolveur laisse passer le meneur. Conforme à ce que la reprise demande (un appel au plus par prise) ; l'effet se juge au CAM-74 en jeu.
  • R3 (info) : au relâcher, Couple[0]->GetActorLocation() est lu après les corrections du résolveur de l'image précédente : LastSafe reprend la place réelle du meneur, ce que NotifyCoupleTeleported documente (« la place du meneur »).

Verdict final : PASS-STATIC. Les 6 contrôles de la reprise passent par lecture ; rien n'a été compilé ni joué. Restent à faire : compilation (Build.ps1 + -FromLog, 0 erreur sous /we4456 /we4458 /we4459), COMMUN-11 (F2, CAM-69 à CAM-75, dont CAM-74 pour l'effet de G3 et R2).

Tours : 24 / 40 (première passe 18, reprise 6)

Constats

Aucun défaut bloquant. Défauts non bloquants (avec ligne du .cpp ; numéros de la première passe, l. 1126-1143 etc. décalées de +11 après l. 1072 dans l'état final) :

  • D1 (l. 403, 421) : les replis ne s'appliquent qu'avec 0 mur nommé Wall*. Si seulement une partie des murs porte ce nom, les côtés non reconnus restent à la boîte du sol (trop large, cause de COMMUN-10), sans plafonnement par l'échelle. La ligne F2 (murs n) le montre ; le DIAG 6.2 prévoyait la borne d'échelle « au cas où les murs ne seraient pas reconnus ». Estimé : à lire en COMMUN-11.
  • D2 (l. 397, 403) : le plafond n'est cherché que parmi les composants Wall*, ou parmi AllComponents[0..5] si aucun mur n'est nommé. Si le plafond a un autre nom et que des murs sont trouvés, la F2 affichera plafond trouve 0 et la borne sera sol + 230. Conservateur, sans danger.
  • D3 (l. 1139-1141 avant G3 ; 1150-1152 après) : LastSpotBlocker n'est remis à vide qu'à la prise, pas après une pose réussie : un second refus par le même composant, après une pose libre, n'écrit pas de ligne. Conforme à « une ligne par changement de composant », mais une ligne manquera dans le journal.
  • D4 (l. 752-757) : retour anticipé si ComputeRoomInterior rend faux : absent de l'IMPL, et chemin mort (le sol a déjà été validé l. 736-747). Sans effet.
  • D5 (l. 828-851) : H1 suppose que le PlayerInput ne voit pas un clic absorbé par l'interface. Non prouvable statiquement (incertain) : CAM-75.
  • D6 (l. 760) : la ligne F2 sort après le retour « pièce cachée » ; en scène d'extérieur, pas de ligne F2 (cohérent avec « pas de borne »).
  • D7 (l. 1130 avant G3 ; 1141 après) : avec une pièce cachée, bFloorKnown est faux : le sol n'est pas passé à CoupleSpotFree et la Room cachée reste testée ; si ses composants chevauchent la place visée en extérieur, la pose sera refusée. Incertain : essai d'un glisser en extérieur.
  • D8 (l. 128) : CoupleFloorMargin inutilisé. MSVC n'avertit pas sur une constante de portée d'espace ; un compilateur Clang le ferait (-Wunused-const-variable). À retirer au nettoyage.
  • D9 (l. 156) : la boîte de test (demi-étendue 78 × 46) tournée de 45° dépasse la marge de 80 cm aux coins (≈ 90 cm) : près d'un mur, G1 peut refuser une place que le clamp autorise. Conservateur, aucun passage de mur.

Compte rendu : l'écart entre « 15 spécificateurs » de la consigne et 16 relevés est une erreur de la consigne (16 spécificateurs, 16 arguments, accord vérifié).

Fichiers à commiter : A Docs/Migration/etat/CAM/CAM-B2p-F-VAL.md (seul fichier que cette VAL a écrit, réécrit en entier à la reprise) ; les .cpp et .h du sous-système restent à commiter par le gardien (non suivis).