LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2p-D-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-D-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-D-VAL : couple à la souris pendant le sexe, VAL statique (2026-10-14, reprise D-1 incluse)

Hors gabarit : section « 7. Reprise » ajoutée en fin de livrable (revérification de la seule modification faite après la VAL initiale). Périmètre : VAL statique (lecture seule). Aucune compilation, aucun éditeur, aucun commandlet, aucun MCP, aucun jeu, aucune écriture git. Seul fichier écrit : ce livrable.

Numéros de ligne des sections 2 à 6 : ils se rapportent au .cpp de la VAL initiale (MD5 3DB310DC…, 1042 lignes). Après la reprise (MD5 D6063264…, 1044 lignes), toute ligne à partir de la 660 est décalée de + 2 (§ 7).

1. Fiches amont relues

Fiche Lue Remarque
Docs/Migration/etat/CAM/CAM-B2p-SPEC.md § 7 à § 7.11 (l. 161-331) oui, en entier § 7.3 (hauteur) et § 7.4 (rotation) remplacés par § 7.11
Docs/Migration/etat/CAM/CAM-B2p-D-IMPL.md oui, en entier, y compris le paragraphe « Reprise après la VAL (D-1) » (l. 66-77) seule IMPL du lot B2p-D ; § 3 = points à confirmer repris en § 2
Instantané D:\Game\LifePlay_Backup_5.5\CAM-B2p-D\ (manifest.csv, sous-arbre Source\LifePlay) oui MD5 du manifeste = MD5 annoncés « avant »
Source/LifePlay/LPCameraModeSubsystem.cpp et .h (état actuel) oui, en entier pour les zones modifiées (l. 1-260, 470-790 du .cpp ; .h entier)

B2r et B2p-C (validés) ne sont pas rouverts : seul leur inchangé hors points déclarés est contrôlé (§ 3, contrôle 2).

2. Points à confirmer des IMPL

Source : CAM-B2p-D-IMPL.md § 3.

Id Point (IMPL § 3) Résultat Preuve ou motif
PC-1 Compilation : signature GetMousePosition(float&, float&) PASS-STATIC PlayerController.h:1623 (surcharge double& en 1624, la float& du code est exacte) ; implémentation PlayerController.cpp:6166
PC-2 DeprojectMousePositionToWorld(FVector&, FVector&) const PASS-STATIC PlayerController.h:731
PC-3 ProjectWorldLocationToScreen(FVector, FVector2D&, bool) const PASS-STATIC PlayerController.h:742 ; même espace de coordonnées que la souris (pixels de vue : GameplayStatics.cpp:3319-3343, GameViewportClient.cpp:1094-1110)
PC-4 FCollisionQueryParams::AddIgnoredActors(TArray<AActor*>) PASS-STATIC CollisionQueryParams.h:257 : const TArray<AActor*>&, correspondance exacte avec Couple (const ref)
PC-5 FMath::PointDistToSegment PASS-STATIC UnrealMathUtility.h:2017 : (const FVector&, const FVector&, const FVector&) rend float ; FVector(FVector2D, T) explicite en Vector.h:176, appelé en initialisation directe
PC-6 Compilation réelle (COMMUN-8) BLOCKED-compilation (reporté COMMUN-8) VAL statique, jeton UE tenu par STE
PC-7 En jeu : position du curseur en mode Scene ; clic gauche sur le couple et SelectUnderCursor ; menu au clic droit d'un widget de scène ; hauteur sur le lit juste ; borne au sol ; aucune ligne Accessed None nouvelle reporté V7 (COMMUN-8) non vérifiable sans jeu ; la ligne « curseur sans position » (l. 666-670) répondra à la première question
PC-8 Essais CAM-58 à CAM-63 (SPEC § 7.11) reporté V7 non rejoués ; chaque essai est relié à son code en § 3, contrôle 4
PC-9 Reprise D-1 (IMPL l. 66-68) : avec Maj tenu, aucune prise PASS-STATIC § 7

3. Critères rejoués (VAL initiale, .cpp 3DB310DC…)

Contrôle 1 : empreintes, lignes, fins de ligne

Commande : md5sum, wc -l, tr -cd '\r' | wc -c (actuel et instantané), puis Tools/FileCheck.ps1 -Path ....

3db310dc025f50b9bc5b0681be84b15e *LPCameraModeSubsystem.cpp   1042   CR=0
6585d7f67a7be3aea377b8e93cfa14d8 *LPCameraModeSubsystem.h     112   CR=0
instantané : 8e35e07ae08f3f208a24dc4cf2b3338f  .cpp 820 CR=0 ; 9e20981f8c768371a0935e2c7ce9e134  .h 84 CR=0
FileCheck : LF 0 1042 0 - 3DB310DC...  alertes=0 (.cpp) ; LF 0 112 0 - 6585D7F6...  alertes=0 (.h) ; fichiers=2 LF=2 alertes=0
Élément IMPL Recompté Verdict
MD5 .cpp après 3DB310DC025F50B9BC5B0681BE84B15E 3DB310DC025F50B9BC5B0681BE84B15E confirmé (état initial ; état final : § 7)
MD5 .h après 6585D7F67A7BE3AEA377B8E93CFA14D8 6585D7F67A7BE3AEA377B8E93CFA14D8 confirmé
MD5 .cpp / .h avant 8E35E07A… / 9E20981F… identiques au manifest.csv et au sous-arbre de l'instantané confirmé
Lignes 820 → 1042 ; 84 → 112 820 → 1042 ; 84 → 112 confirmé
CR / BOM 0 CR, LF 0 CR, pas de BOM, aucune alerte FileCheck confirmé
Suivi git « non suivis jusqu'ici » git status --short : ?? pour les deux ; git ls-files : vide confirmé (A)

Contrôle 2 : diff complet instantané → actuel, périmètre

Commandes : diff -u (lu en entier, .h puis .cpp) ; git diff --no-index --numstat.

228   6   LPCameraModeSubsystem.cpp     (820 + 228 - 6 = 1042)
 28   0   LPCameraModeSubsystem.h      (84 + 28 = 112)

diff -q rend 1 (différent), comme attendu. Hunks du .cpp : 7 (constantes ; garde début de TickSexCamera ; PC non const ; ReleaseCoupleGrab si vue invalide ; bGrabbing + 2 conditions !bGrabbing ; témoin ; deux fonctions nouvelles). Hunks du .h : 2 (déclarations anticipées, bloc de membres).

Hunk Dans le périmètre de l'IMPL § 1
Constantes PelvisSocket, RoomFloor, 120 px, 5 px, 0,3, 30 cm, 5000, +150, −400 (l. 119-129) oui
TickSexCamera : ReleaseCoupleGrab (l. 503, 515), remise de bCursorWarned (l. 504-507), PC non const (l. 511), TickCoupleMouse avant ZQSD (l. 535), !bGrabbing sur les deux branches (l. 537, 559) oui
Témoin : bButtons, G, D, prise, lacet, ligne quand seul un bouton est tenu (l. 576-587) oui
ReleaseCoupleGrab (l. 590-596), TickCoupleMouse (l. 598-786) oui
.h : class AActor;, class UGameInstance;, 2 fonctions, 14 membres oui

Inchangé : toutes les autres lignes de B2r (CharGen, témoins C1, C2, occlusion, touche C) et de B2p-C (formules d'orbite, distances, POV) ; les deux seules modifications de leurs branches sont les !bGrabbing déclarés. Aucun ajout non demandé : Query.AddIgnoredActor(View) (l. 620), en plus de la liste de la SPEC (couple, Wall, CamRoom), est inoffensif et cohérent (la caméra en vue ne doit pas être touchée par son propre tracé).

Contrôle 3 : relecture C++ UE 5.8

Sources lues : D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\….

Élément Preuve moteur Verdict
GetMousePosition(float&, float&) const PlayerController.h:1623 PASS-STATIC
DeprojectMousePositionToWorld(FVector&, FVector&) const PlayerController.h:731 PASS-STATIC
ProjectWorldLocationToScreen(FVector, FVector2D&, bool) const PlayerController.h:742 PASS-STATIC
AddIgnoredActors(const TArray<AActor*>&) CollisionQueryParams.h:257 (autres surcharges : const AActor*, TWeakObjectPtr<const AActor>, sans ambiguïté) PASS-STATIC
AddIgnoredActor(const AActor*) avec View, Wall, CamRoom CollisionQueryParams.h:243 PASS-STATIC
SCENE_QUERY_STAT(LPCoupleDrag) + true CollisionQueryParams.h:39 : deux arguments (FName, TStatId) ; même forme que LPDirectControlSubsystem.cpp:1024 PASS-STATIC
UWorld::LineTraceSingleByChannel(...) const World.h:2161, appelé via const UWorld* PASS-STATIC
FMath::PointDistToSegment UnrealMathUtility.h:2017 (rend float, comparé à const float) PASS-STATIC
FVector(FVector2D, 0.f) Vector.h:176 explicite, 0.f → double PASS-STATIC
FVector MousePoint(float, float, float) Vector.h:168 TVector(T,T,T) PASS-STATIC
FVector2D::Distance Vector2D.h:397 statique, rend T PASS-STATIC
EKeys::LeftMouseButton/RightMouseButton InputCoreTypes.h via GameFramework/PlayerInput.h (déjà inclus, l. 17 ; LeftShift déjà utilisé de la même façon) PASS-STATIC
UPrimitiveComponent / Bounds / FBox Bounds est public (SceneComponent.h:127-129) ; GetBox() const (BoxSphereBounds.h:226) ; PrimitiveComponent.h arrive par Components/SkeletalMeshComponent.h (l. 9) ; Box est un UBoxComponent (LPOutdoorRoomSubsystem.cpp:32, 233) PASS-STATIC
Cast<ACharacter>(const AActor*) Casts.h:89 : qualificatifs recopiés, rend const ACharacter* ; précédent LPSecondScreenSubsystem.cpp:36 PASS-STATIC
Os pelvis existe dans le squelette (STD-SCN-DIAG.md l. 50 ; LPDebugSubsystem.cpp:523) ; sinon GetSocketLocation rend l'origine du maillage (pas de plantage) PASS-STATIC
.h sans include d'AActor TWeakObjectPtr<AActor> avec déclaration anticipée : même schéma que TWeakObjectPtr<UGameViewportClient> Viewport (.h l. 108, déjà compilé en B2c) ; GrabCamera.Get/Reset/= sont dans le .cpp où AActor est complet (GameFramework/Actor.h, l. 13) PASS-STATIC
Membre non UPROPERTY dans UCLASS + GENERATED_BODY accepté : UHT ignore ce qui n'est pas balisé ; tous les membres de ce .h sont déjà dans ce cas (Root, Viewport, bShown…) PASS-STATIC
UGameInstance / UObject dans une signature privée class UGameInstance; ajoutée ; UObject vient de Subsystems/GameInstanceSubsystem.h PASS-STATIC
Lambdas [&] captures World, PC, Query, Pivot (référence const) : durée de vie ≥ lambda, jamais stockées ; Out, OutZ en référence ; -> bool explicite PASS-STATIC
Types float/double GrabHeight, SurfaceZ, Target, Delta en double ; MouseX/Y, GrabTurned, GrabLastMouseX en float ; FMath::Clamp aux 3 arguments double (Target.X, Box.Min.X + Margin, Box.Max.X - Margin) ; ternaire du témoin double : double PASS-STATIC
Const-correctness PC passe non const (nécessaire pour WriteBool(UObject*), l. 784) ; Room const UObject* vers ReadObject(const UObject*) ; boucle de déplacement AActor* sur const TArray<AActor*>& (éléments AActor* const) ; Mesh const : GetSocketLocation est const PASS-STATIC
Ombrages de noms (C4456/C4458) SurfaceZ, Aim, Rotation, Person, Hit redéclarés seulement dans des portées sœurs, jamais imbriquées ; aucun local ne porte le nom d'un membre PASS-STATIC
Aides de LPCameraModeLocal qualifiées Grep des noms d'aides et de constantes précédés d'autre chose que :: : seules l. 43-253 (définitions dans l'espace) et l. 673 (commentaire) ; aucun appel non qualifié ; PelvisSocket, RoomFloor, CursorTraceLength, CoupleSelectRadius n'existent dans aucun autre fichier de Source/ ; namespace LPCameraModeLocal défini une seule fois (l. 30) : pas d'ambiguïté en compilation unitaire avec LPDirectControlSubsystem.cpp PASS-STATIC

Formats UE_LOG (spécificateurs comptés dans la chaîne, arguments comptés dans l'appel) :

Ligne du journal Spécificateurs Arguments
« Sexe : touches … » (l. 581-587) 17 (%+.0f×4, %d×6, %s, %d×2, %.0f×4 ; le lacet est le 17e) 17 : Forward, Right, 2 AxisValue, bShift, MoveObject, MoveCamera, bLeftWasDown, bRightWasDown, CoupleGrabMode, *GetNameSafe, ReadInt, Couple.Num(), Pivot.X/Y/Z, Yaw égal
« couple pris (%s) a (%.0f ×3), hauteur … %s » 5 5 : ternaire TEXT, X, Y, Z, ternaire *FString::Printf(...) / TEXT égal ; le temporaire FString vit jusqu'à la fin de l'expression complète de l'appel de journal ; les deux branches du ternaire sont const TCHAR*
« couple pose a (%.0f ×3), lacet %.0f » 4 4 égal
« couple tourne de %+.0f degres, lacet %.0f » 2 2 (GrabTurned float, Yaw double) égal
« curseur sans position … (vue %s) » 1 1 égal

Contrôle 4 : logique et gardes de Big Boss

Id Garde Preuve (ligne) Verdict
(a) Rien hors sexe ou pendant un menu, prise levée TickSexCamera l. 500-509 : !SexOn ou bMenuOnScreen → ReleaseCoupleGrab() puis retour (aucune lecture de la souris) ; vue invalide l. 513-517 idem. ReleaseCoupleGrab (l. 590-596) remet mode, caméra figée, bLeftWasDown, bRightWasDown à zéro ; bCursorWarned remis à faux hors sexe seulement (l. 504-507) : « une ligne par scène » PASS-STATIC
(b) Mode Scene seulement l. 609 : Couple.Num() > 0 && !Couple.Contains(View) && !ReadBool(PC, POVOn) ; sinon CoupleGrabMode = 0, caméra relâchée, return false (l. 610-614). L'état des boutons est mis à jour avant ce test (l. 603-606) : passer de POV à Scene bouton tenu ne crée pas de front PASS-STATIC
(c) Aucun clic consommé TickCoupleMouse ne lit que PlayerInput->IsPressed (l. 599-602) ; la seule écriture sur le PC est WriteBool(PC, MoveCamera, false) (l. 784), jamais exécutée hors prise (tous les return false de la fonction la précèdent : l. 614, 662, 671, 693, 727). Aucun Consume, SetInputMode, SetIgnore… PASS-STATIC
(d) Borne au sol de Room.Box avec marge l. 737-749 : AABB de Floor->Bounds (l. 741), marge 30 cm, Clamp en X et Y ; Floor absent ou trop petit (≤ 2 marges) → pas de borne. Réserve D-3 PASS-STATIC
(e) Rien avant 5 px l. 732 : `bGrabMoved = Distance(début, souris) >= 5(verrou une fois franchi) ; le déplacement exigebGrabMoved && TraceCursor` (l. 734)
(f) Hauteur par tracé vertical, garde si pas d'impact à la prise : TraceDown(Pivot, SurfaceZ) → bGrabHasHeight, GrabHeight = Pivot.Z - SurfaceZ (l. 704-705) ; au glisser : Target.Z = SurfaceZ + GrabHeight seulement si bGrabHasHeight && TraceDown(Target, …) (l. 751-754) ; sinon Z actuel gardé. Rayon du curseur sans impact → TraceCursor faux → le couple ne bouge pas à cette image (SPEC § 7.11a) PASS-STATIC
(g) Rotation autour du pivot l. 762-775 : Offset.RotateAngleAxis(Turn, UpVector) autour de Pivot (moyenne des positions, invariante par rotation) et Yaw += Turn pour chaque personne, Turn = ΔX × 0,3 ; sens cohérent avec le lacet (rotation positive autour de Z = lacet positif) PASS-STATIC
(h) Caméra figée pendant la prise, libérée au relâcher et hors sexe l. 780-783 : pose mémorisée (l. 700-702) remise à chaque image tant que la prise dure ; relâcher : GrabCamera.Reset() (l. 726) ; hors sexe, hors Scene, vue invalide : Reset (l. 593, 613) ; ZQSD coupé par !bGrabbing (l. 537, 559). Réserves D-5 (ordre d'image) et D-6 (caméra remplacée en cours de prise) PASS-STATIC
(i) Aucune déréférence nulle PC->PlayerInput testé (Input &&, l. 599-601) ; GI non nul par l'appelant (l. 500) ; World testé (l. 610) ; View valide par l'appelant (l. 513) ; Room nul accepté par ReadObject ; Mesh testé (l. 679-681) ; Floor par IsValid (l. 739) ; GrabCamera.Get() testé (l. 780). Couple[0] : l. 716 (branche « relâcher ») n'est atteinte qu'après le return false de l. 610-614, donc bScene vrai, donc Couple.Num() > 0 (l. 609) ; l. 587 gardé par Couple.Num() > 0 ?. Les éléments de Couple sont déjà filtrés IsValid par ReadActors (l. 191). Les boucles l. 676, 756 et 769 n'itèrent qu'un tableau non vide. Wall et CamRoom testés par if (AActor* … = Cast<AActor>(…)) (l. 621, 625) PASS-STATIC
(j) Conflits avec flèches (P2) et Maj + molette seulement pendant une prise aucun code ne touche aux flèches ni à la molette (bSexWheelZoom reste faux) ; pendant un glisser, la position est réécrite à chaque image, donc remplace un appui de flèche (déclaré, IMPL § 2). Hors prise : aucune écriture. Réserve D-1 (Maj + clic) levée à la reprise : § 7 PASS-STATIC

Rattachement aux essais V7 (non rejoués) : CAM-58 = (e)(f) ; CAM-59 = (g)(h) ; CAM-60 = (d) ; CAM-61 = clic hors couple (l. 691-693 : return false sans prise, aucune écriture) ; CAM-62 = (b) ; CAM-63 = (a) (aucun mouvement après la sortie : le déplacement n'est atteint que dans TickCoupleMouse, que TickSexCamera n'appelle plus hors sexe).

Contrôle 5 : cohérence SPEC / IMPL

Exigence SPEC § 7.11 IMPL / code Verdict
Sélection par projection, 120 px, segment bassin-tête, sans tracé de collision l. 673-694 ; Mesh absent → position de l'acteur conforme
Tracé vertical à la prise (Visibility, complexe ; couple, Wall, CamRoom ignorés), dz gardé l. 618-627, 704-705 conforme (View ignoré en plus)
Glisser : rayon du curseur, nouveau XY = impact + décalage, borné, Z = surface + dz l. 706-707, 730-757 conforme
Pas de mouvement sans impact l. 734 conforme
Clic droit = rotation 0,3°/px autour du pivot, caméra figée l. 762-775, 780-784 conforme
Clic hors couple : comportement actuel inchangé l. 691-693 conforme
Maj + molette reste disponible IMPL § 2 : non joué pendant une prise de déplacement écart déclaré, acceptable
« La rotation d'origine (Maj + bouton + souris) reste » (§ 7.11b) initialement : MoveCamera forcé à faux sans test de Maj (D-1) ; après reprise : conforme (§ 7) levé
Retour automatique à la sortie, sélection levée sans pose l. 500-509 conforme
Volume ≈ 200 lignes +228 / −6 (.cpp), +28 (.h) conforme

4. Diff lu en entier

Fichier Compte Statut git Lecture
Source/LifePlay/LPCameraModeSubsystem.cpp VAL initiale : +228 / −6 ; 820 → 1042. Après reprise : +230 / −6 ; 820 → 1044 ?? (à ajouter, A) diff -u entier lu, 7 hunks, aucun hors périmètre
Source/LifePlay/LPCameraModeSubsystem.h +28 / −0 ; 84 → 112 (inchangé par la reprise) ?? (A) diff -u entier lu, 2 hunks

Recompte par une seconde méthode (git diff --no-index --numstat) égal au recompte wc -l : 228 − 6 = +222 = 1042 − 820 ; 28 = 112 − 84. Fins de ligne LF identiques à l'instantané ; aucune trace de modification par script dans le diff.

5. Verdict

PASS-STATIC (final, après reprise). Aucun défaut bloquant. D-1 est levé (§ 7). Réserves non bloquantes D-2 à D-9 ci-dessous ; la compilation (COMMUN-8) et les essais CAM-58 à CAM-63 restent à faire, aucun n'est compté comme passé.

Défauts bloquants : aucun.

Défauts non bloquants :

Id Ligne (état initial) Constat Suite proposée (non appliquée)
D-1 598-786, 784 LEVÉ à la reprise (§ 7). Maj + bouton tenu sur le couple déclenchait aussi une prise et mettait MoveCamera à faux : la rotation d'origine (Maj + bouton + souris, SPEC § 7.1) était masquée près du couple, contre § 7.11b sans objet
D-2 784 MoveCamera forcé à faux : la voie « Manual + souris → AnimatingSpeed quand MoveCamera est faux » (SPEC § 7.11b) est morte au dump, mais n'a pas été revérifiée V7 : AnimatingSpeed inchangé pendant un glisser
D-3 737-749 Si le couple part hors de l'AABB de Room.Box, le premier pas le ramène d'un coup au bord (− 30 cm) V7 CAM-60, dans une pièce et dans l'extérieur
D-4 707 Rayon du curseur sans impact à la prise : décalage nul, puis le couple saute sous le curseur au premier pas cas rare (viser le ciel) ; V7
D-5 535, 780-783 Le ticker passe avant le tick du monde : le PC peut tourner la caméra une image avant que la pose soit remise (image de décalage possible au clic) V7 CAM-58/59 : tremblement de caméra à l'appui
D-6 700, 780-783 Si la vue change de caméra sans passer par POV pendant une prise, la caméra mémorisée est remise à sa pose, pas la nouvelle vue négligeable ; levée au relâcher
D-7 590-596, 603-606 ReleaseCoupleGrab remet les « était enfoncé » à faux : un bouton déjà tenu à la fermeture d'un menu compte comme un nouveau front négligeable (les boutons de menu agissent au relâcher)
D-8 618-627, 704, 733-734 Tracés Visibility complexes : le rayon du curseur et le départ du tracé vertical (pivot + 150) peuvent toucher un plafond ou un baldaquin avant le lit V7 CAM-58 ; la ligne « hauteur sur la surface N cm » donne dz
D-9 576-587 Le témoin écrit une ligne par seconde dès qu'un bouton de souris est tenu pendant le sexe, même hors couple déclaré (IMPL § 1) ; bruit de journal seulement

Établi / estimé / incertain :

  • Établi : MD5, lignes, CR, périmètre du diff, signatures et surcharges du moteur 5.8 (lignes citées), comptes de spécificateurs, absence d'appel non qualifié, gardes (a) à (j) par lecture, reprise D-1 (§ 7).
  • Estimé : compilation propre (aucune erreur trouvée par lecture ; MSVC non lancé) ; sens de rotation du lacet.
  • Incertain : tout comportement en jeu (D-2 à D-8), absence de collision de la sélection avec un clic d'interface, Room.Box couvrant le sol des pièces jouées. Essai qui trancherait : COMMUN-8 puis CAM-58 à CAM-63.

6. Points à confirmer par l'utilisateur

  • D-1 : Maj + clic sur le couple : levé (aucune prise avec Maj ; la rotation d'origine garde la main). À observer tout de même en V7 : Maj + bouton + souris tourne le couple près du couple.
  • V7 : « curseur sans position » absent du journal en mode Scene (PC-7).
  • V7 : un clic gauche sur le couple ouvre-t-il aussi une interaction de Room ? Un clic droit ouvre-t-il un menu de widget ?
  • V7 : CAM-58 (hauteur sur le lit, caméra immobile), CAM-59 (rotation sur place), CAM-60 (arrêt à 30 cm du bord), CAM-61 (orbite hors couple), CAM-62 (touche C), CAM-63 (sortie bouton tenu).
  • COMMUN-8 : compilation (0 erreur, avertissements nouveaux listés) avant tout essai en jeu.

7. Reprise : revérification de D-1 (Maj + clic), 2026-10-14

Annonce du coordinateur : .cpp D60632646BF1CF7D200E3BEA833D8D72, 1044 lignes ; .h inchangé 6585D7F6….

Recompte (md5sum, wc -l, tr -cd '\r' | wc -c, git diff --no-index --numstat) :

d60632646bf1cf7d200e3bea833d8d72 *LPCameraModeSubsystem.cpp   1044   CR=0
6585d7f67a7be3aea377b8e93cfa14d8 *LPCameraModeSubsystem.h     112   CR=0
numstat instantané → actuel : .cpp 230 / 6 (VAL initiale : 228 / 6, soit + 2) ; .h : diff -u de 45 lignes, MD5 inchangé
Élément Annoncé Recompté Verdict
MD5 .cpp D60632646BF1CF7D200E3BEA833D8D72 D60632646BF1CF7D200E3BEA833D8D72 confirmé
Lignes .cpp 1044 (avant 1042) 1044 confirmé
MD5 / lignes .h 6585D7F6…, 112 6585D7F67A7BE3AEA377B8E93CFA14D8, 112 confirmé
CR 0 0 (.cpp et .h) confirmé

Le diff ne diffère du précédent que par ces lignes : preuve par reconstruction. Sur le fichier actuel, retrait des l. 660-661 et remise de la l. 662 à if (!bLeftPressed && !bRightPressed) (sed en lecture, sortie vers md5sum, aucun fichier écrit) :

3db310dc025f50b9bc5b0681be84b15e *-      (= MD5 de la VAL initiale, 3DB310DC…)

La reprise se réduit donc exactement à ces trois lignes (l. 660-662) ; tout le reste, y compris le diff lu en § 4, est identique octet pour octet à l'état déjà validé.

Lignes ajoutées (cat -A, tabulations seules, pas de CR), dans TickCoupleMouse, branche CoupleGrabMode == 0 :

660  // Maj tenu : la voie d'origine (Maj + bouton + souris, Room) garde la main, aucune prise.
661  const bool bShiftHeld = Input && (Input->IsPressed(EKeys::LeftShift) || Input->IsPressed(EKeys::RightShift));
662  if ((!bLeftPressed && !bRightPressed) || bShiftHeld)

Conforme à l'annonce (commentaire, bShiftHeld, condition).

Compilation à l'œil (PASS-STATIC) :

  • Input est le UPlayerInput* déclaré en tête de fonction (l. 599) : dans la portée.
  • UPlayerInput::IsPressed(FKey) const et EKeys::LeftShift/RightShift : mêmes appels que TickSexCamera l. 575 (déjà compilés en B2p-C).
  • Input && (bool || bool) : pointeur converti en bool par &&, résultat bool pour un const bool. Pas de macro, pas de virgule exposée.
  • Parenthèses de la condition équilibrées ; bShiftHeld n'est déclaré qu'une fois dans TickCoupleMouse (aucun ombrage : le bShift de TickSexCamera est une autre fonction).
  • Variable utilisée : pas d'avertissement « variable inutilisée ».

D-1 levé (lecture de la fonction, état final) :

  • Maj tenu et front montant d'un bouton avec CoupleGrabMode == 0 : le return false de la l. 664 est pris avant toute écriture. Aucune prise, donc ni CoupleGrabMode, ni GrabCamera, ni mémorisation de la pose de la caméra.
  • MoveCamera n'est écrit qu'à la fin de la fonction (l. 786 après décalage), après les return false : il n'est donc pas touché. La chaîne d'origine (bouton → StartMoveCamera → MoveCamera → Room avec Maj → rotation) reste intacte.
  • La caméra n'est pas figée. ZQSD n'est pas coupé (bGrabbing faux).
  • Les « était enfoncé » sont mis à jour avant (l. 603-606) : relâcher Maj bouton tenu ne crée pas de front tardif, donc pas de prise en cours de geste.
  • Maj + clic hors couple : inchangé (aucune prise, comme avant).
  • Sans Maj : comportement identique à la VAL initiale (le test ajouté vaut faux).
  • Cas limite (V7, non bloquant) : Maj appuyé après le début d'une prise. Le test n'existe que pour CoupleGrabMode == 0, donc la prise continue (le glisser reste cohérent, MoveCamera reste à faux). La rotation d'origine ne démarre alors pas, ce qui est le comportement voulu pour un geste commencé sans Maj.

Section « Reprise » de CAM-B2p-D-IMPL.md (l. 66-77) relue : elle décrit « deux lignes de plus dans la sélection » et liste D-2 à D-9 comme laissés en l'état. Cohérent avec le diff : 2 lignes de plus (la condition est modifiée en place), D-2 à D-9 non touchés (leurs lignes sont identiques, décalées de + 2 après la l. 660). Le tableau MD5 en tête de l'IMPL n'a pas été recontrôlé au-delà de ce qui est donné ici.

Numéros de ligne : après la l. 660, les lignes citées en §§ 2 à 6 sont à lire + 2 (ex. WriteBool(PC, MoveCamera, false) l. 784 devient l. 786, fin de TickCoupleMouse l. 786 devient 788).

Verdict de la reprise : PASS-STATIC. D-1 levé. Verdict final du lot : PASS-STATIC, aucun défaut bloquant ; D-2 à D-9 non bloquants à observer en V7 ; compilation (COMMUN-8) et essais CAM-58 à CAM-63 à faire.

Fichiers à commiter (dépôt : jeu, branche ue5.8) :

  • A Source/LifePlay/LPCameraModeSubsystem.cpp (MD5 D6063264…)
  • A Source/LifePlay/LPCameraModeSubsystem.h
  • A Docs/Migration/etat/CAM/CAM-B2p-D-IMPL.md
  • A Docs/Migration/etat/CAM/CAM-B2p-D-VAL.md (ce livrable)

Tours : 28 / 40