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

254 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 exige `bGrabMoved && TraceCursor` (l. 734) | PASS-STATIC |
| (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
- [x] 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