LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2p-C-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

147 lines
22 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-C-VAL — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate
# CAM-B2p-C-VAL : ZQSD pour la caméra pendant le sexe, molette en réserve, témoin des touches (VAL statique, 2026-10-14, reprise incluse)
Verdict : **PASS-STATIC**, confirmé après la reprise (voir « Reprise » en fin de document). Aucune compilation, aucun éditeur, aucun MCP, aucun jeu : tout est lu. Défauts bloquants : 0.
Défauts non bloquants : 6 à la première passe ; après la reprise, 3 sont levés (2, 3, 4) et 3 restent (1, 5, 6). La compilation et les essais V7 restent à faire sous jeton UE.
Les numéros de ligne des § 2 à 6 et des Constats valent pour la **première version** (`.cpp` `E5DA8299…`, 817 lignes). Dans la version actuelle (`8E35E07A…`, 820 lignes), ils sont décalés de +1 après la ligne 116 et de +3 après la ligne 484.
## 1. Fiches amont relues
| Fiche | Lue | Note |
|---|---|---|
| `CAM-B2p-SPEC.md` (P1, P2, P3, P3 bis, § 3 à 6) | oui, lignes 1-160 | P3 aux lignes 73-91, P3 bis aux lignes 51-55 |
| `CAM-B2p-C-IMPL.md` | oui, en entier, puis relue à la reprise (lignes 44-65) | l'IMPL n'a pas les sections « Points à confirmer en VAL » et « Fichiers à commiter » du gabarit IMPL : écart de gabarit (Constats 5) |
| `CAM-B2p-R-FICHE.md` | non relue en détail | pour info seulement (Room, MCP) ; hors périmètre de cette VAL |
| `CAM-B2r-VAL.md` (état d'avant) | non rouverte | l'état B2r est établi par l'égalité MD5 de l'instantané avec les valeurs annoncées (§ 3, contrôle 1) |
## 2. Points à confirmer des IMPL
L'IMPL ne numérote pas de points à confirmer. Je reprends ses déclarations vérifiables, une par ligne.
| Id | Déclaration de l'IMPL | Résultat |
|---|---|---|
| C-1 | MD5 avant `124336D3…` / `61A3E062…`, après `E5DA8299…` / `9E20981F…` | PASS (§ 3, contrôle 1) ; après la reprise : `8E35E07A…` / `9E20981F…`, PASS (Reprise) |
| C-2 | `.cpp` 662 → 817 lignes, `.h` 77 → 84 lignes | PASS (`wc -l` : 662, 817, 77, 84) ; après la reprise : 820 et 84, PASS |
| C-3 | Fins de ligne LF, 0 CR | PASS (§ 3, contrôle 3) ; reprise : 0 CR, PASS |
| C-4 | Écart 1 : `MenuOnScreen` gardé dans `bMenuOnScreen` (0,1 s) au lieu d'`IsMenuOpen` seul | PASS : `MenuOnScreen` appelle `IsMenuOpen` en premier (`.cpp:248`). Écart acceptable, plus large donc plus sûr. Contrepartie : jusqu'à 0,1 s de retard à l'ouverture d'un menu : acceptable |
| C-5 | Écart 2 : P3 bis écrit mais désactivé (`bSexWheelZoom = false`) | PASS (`.cpp:114` ; `Wheel` vaut 0 à `.cpp:498`) |
| C-6 | « Sans P1 (Room), ZQSD n'agit pas en mode Scene ; le POV marche sans P1 » | PASS par lecture : en Scene, la condition `View == SceneCamera` (`.cpp:513`) est fausse si la vue est `CamRoom`. Effet réel : reporté (V7, avec B2p-R) |
| C-7 | « Compilation et B2p-R non faites » | confirmé : aucun journal de build ; reporté (BUILD sous jeton UE) |
| C-8 | Signe « D tourne vers la droite (angle = −côté × vitesse × dt) » | voir § 3, contrôle 5 : le signe est juste pour « la caméra se déplace vers sa droite autour du pivot » ; le libellé était ambigu pour le lacet, désormais déclaré dans l'IMPL (Reprise) ; ressenti reporté (V7-1) |
## 3. Critères rejoués
| N° | Critère | Commande / lecture | Résultat brut | Verdict |
|---|---|---|---|---|
| 1 | MD5 avant et après | `md5sum` sur l'instantané `D:\Game\LifePlay_Backup_5.5\CAM-B2p-C\Source\LifePlay\` et sur `Source/LifePlay/` | avant : `124336d3293ef06befcf076d10956234` (.cpp), `61a3e06280bf7226adb52ecfdd9173de` (.h). Après (première version) : `e5da82992ac18471e51acf1798b7f0e2` (.cpp), `9e20981f8c768371a0935e2c7ce9e134` (.h) | PASS : les quatre égalent les valeurs annoncées |
| 2 | Diff dans le périmètre | `diff -u` instantané → actuel, lu en entier (190 lignes pour le .cpp, 17 pour le .h) | .cpp : 4 hunks, +156 / −1 (net +155 = 817 − 662). La seule suppression est `(void)DeltaTime;` remplacée par `TickSexCamera(DeltaTime);` (`.cpp:477`). .h : 1 hunk, +7 / 0 (net +7 = 84 − 77) | PASS : voir le détail au § 4 |
| 3 | Fins de ligne | `tr -cd '\r' < fichier \| wc -c` | .cpp actuel : 0 ; .h actuel : 0 ; .cpp instantané : 0 ; .h instantané : 0 | PASS |
| 4 | Relecture C++ UE 5.8 | lecture du .cpp et des en-têtes du moteur (`D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime`) | voir ci-dessous | PASS-STATIC |
| 5 | Logique (a) à (h), signe de D | lecture de `TickSexCamera` (`.cpp:481-561`) | voir ci-dessous | PASS-STATIC |
| 6 | Cohérence SPEC / IMPL | SPEC lignes 51-55 et 73-91 contre l'IMPL | voir ci-dessous | PASS avec écarts mineurs (déclarés depuis) |
### Contrôle 4 : C++ UE 5.8 (preuves)
| Point | Preuve | Résultat |
|---|---|---|
| `FVector::RotateAngleAxis` | `Core/Public/Math/Vector.h:769` : `RotateAngleAxis(const T AngleDeg, const TVector<T>& Axis) const` ; `Angle` est un `float`, converti en `double` à l'appel (`.cpp:519`) | OK |
| `FMath::Clamp` avec `double` | `.cpp:524-525` : premier argument `Distance - float*float*float - float*float` donc `double` (car `Distance` est `double`), bornes en `static_cast<double>` ; trois `double`, un seul `T` | OK |
| `FMath::Clamp` du tangage | `.cpp:543-544` : `Rotation.Pitch` (`double` en LWC) `+ float`, bornes `-static_cast<double>(...)` et `static_cast<double>(...)` | OK |
| `FRotator` en `double` | `Rotation.Yaw += Angle` (`:531`) et `+= Right * ...` (`:542`) : `double += float`, sans rétrécissement | OK |
| `SetActorLocationAndRotation` | `Engine/Classes/GameFramework/Actor.h:1701` : `(FVector, FRotator, bool=false, FHitResult*=nullptr, ETeleportType=None)`. `View` est un `AActor*` non const (`.cpp:491`) | OK |
| `USceneComponent::SetWorldRotation` | `Components/SceneComponent.h:575-576` : surcharges `FRotator` et `FQuat` ; l'argument est un `FRotator` : pas d'ambiguïté. `Look` est un `USceneComponent*` non const | OK |
| `FIntProperty` | `UnrealType.h:1663` : `GetPropertyValue_InContainer(void const*, int32=0) const` renvoie `int32 const&` ; `Prop ? ... : -1` donne `int` | OK |
| `FScriptArrayHelper`, `FObjectPropertyBase` | `ReadActors` (`.cpp:165-184`) reprend le motif de `ArrayContains` (`.cpp:220-237`, B2r, déjà validé) | OK |
| `PlayerInput->IsPressed` / `GetKeyValue` sur un PC const | `PlayerInput.h:745` et `:757`, les deux `const` ; `PlayerInput` est un pointeur membre : sur `const APlayerController*` il reste `UPlayerInput* const`. Même motif déjà présent dans `TickSwitchKey` (`.cpp:461-465`) | OK |
| `AxisValue` | corps identique à `LPDirectControlSubsystem.cpp:167-186` (relu côte à côte) | OK |
| `EKeys` : include nécessaire ? | `EKeys::LeftShift` / `RightShift` : `InputCore/Classes/InputCoreTypes.h:455-456` ; `PlayerInput.h:14` inclut `InputCoreTypes.h`, et `PlayerInput.h` est inclus (`.cpp:17`) | OK, aucun include à ajouter |
| `TArray<AActor*>::Contains(AActor*)` | `Couple` est `const TArray<AActor*>` ; `Contains` est `const` et prend le type de l'élément ; `View` est un `AActor*` (`.cpp:535`) | OK |
| `View == SceneCamera` | `AActor*` contre `const AActor*` : comparaison de pointeurs valable (`.cpp:513`) | OK |
| `GetViewTarget` sur un PC const | `PlayerController.h:1911` : `virtual AActor* GetViewTarget() const override` | OK |
| `Pivot /= Couple.Num()` | `TVector::operator/=(T)` ; `int32` converti en `double`. Gardé par `Couple.Num() > 0` (`.cpp:507-509`) | OK |
| `GetAxisMappings`, `IsDigital` | `InputSettings.h:318` ; `InputCoreTypes.h:81` | OK |
| `UE_LOG` (`.cpp:557-560`) | 13 spécificateurs : `%+.0f` ×4, `%d` ×3, `%s` ×1, `%d` ×2, `%.0f` ×3. 13 arguments : `Forward`, `Right`, `AxisValue` ×2 (quatre `float`, promus en `double`) ; `bShift ? 1 : 0`, deux `ReadBool ? 1 : 0` (`int`) ; `*GetNameSafe(View)` (`TCHAR*`) ; `ReadInt` (`int32`) ; `Couple.Num()` (`int32`) ; `Pivot.X`, `.Y`, `.Z` (`double`) | OK : types et nombre concordent |
| Ombrage / redéfinition | `Rotation` est déclaré dans deux blocs disjoints (`.cpp:530` et `:541`) ; aucun membre de la classe ne porte les noms `View`, `Room`, `Pivot`, `Couple`, `Look`, `Forward`, `Right` (`.h` relu en entier) | OK |
| Avertissement sur condition constante | `bSexWheelZoom ? ... : 0.f` (`.cpp:498`) : ternaire sur constante ; MSVC C4127 coupé (`VCToolChain.cs:338`), C4702 coupé dans le cas courant (`VCToolChain.cs:1396-1407`) | OK, incertain à la compilation (Constats 6) |
| Collision de noms en compilation unitaire | `Grep AxisValue` dans `LPCameraModeSubsystem.cpp` : définition `:187` dans `namespace LPCameraModeLocal` (ouvert `:30`, fermé `:300`) ; appels `:496`, `:497`, `:498`, `:550` (×2), `:558` (×2), tous préfixés `LPCameraModeLocal::`. `ReadInt` : appel `:560`, préfixé. `ReadActors` : appel `:501`, préfixé. Les nouveaux noms (`AxisMoveForward`, `GISexPersons`, `RoomMoveObject`, etc.) ne sont utilisés que préfixés. Aucun appel non qualifié | OK : pas de C2668 attendu |
### Contrôle 5 : logique
| Point | Lecture | Verdict |
|---|---|---|
| (a) rien ne bouge si `SexOn` faux ou menu | `.cpp:485-488` : retour avant toute lecture de touche ou écriture si `!GI`, `SexOn` faux ou `bMenuOnScreen`. Seul `SexKeysCooldown` est décrémenté avant (`:483`), sans effet visible | OK |
| (b) mode Scene seulement si vue = `Room.SceneCamera` et couple non vide | `.cpp:513` : `bKeys && Couple.Num() > 0 && View == SceneCamera`. `Room` nul : `ReadObject(nullptr)` renvoie `nullptr` (`.cpp:127`) donc `SceneCamera` nul, et `View` valide ne l'égale jamais | OK |
| (c) bornes seulement avec Z/S (ou molette) | `.cpp:522-526` : `Clamp` dans `if (Forward != 0 \|\| Wheel != 0)`. L'orbite seule garde `Distance = Offset.Size()` (une rotation conserve la norme) | OK, mais voir Constats 1 (saut au premier appui) |
| (d) direction nulle protégée | `.cpp:527-533` : `GetSafeNormal()` puis `if (!Direction.IsZero())` ; sinon ni position ni lacet ne changent | OK |
| (e) POV | `.cpp:535-547` : `Couple.Contains(View)`, `Look` lu par `ReadObject(View, FirstPersonSM)` puis `IsValid(Look)` ; tangage `Clamp(..., -80, +80)` | OK |
| (f) P3 bis désactivé | `bSexWheelZoom = false` (`.cpp:114`) donc `Wheel = 0.f` (`:498`) ; `AxisScroll` n'est pas lu | OK |
| (g) témoin au plus une ligne par seconde | `.cpp:550-557` : retour si `SexKeysCooldown > 0` ou aucune touche (`:552-555`) ; sinon `SexKeysCooldown = 1.f` (`SexKeysLogInterval`, `:556`) puis une seule ligne. Le décompte se fait à chaque image (`:483`) | OK |
| (h) déréférencements nuls | `GI` testé (`:485`) ; `World` testé (`:489`) ; `PC` et `View` testés (`:490-492`, `IsValid(View)`) ; `Room` : `ReadObject` / `ReadBool` acceptent un `nullptr` ; `PlayerInput` testé dans `AxisValue` (`:190`) et au témoin (`:551`) ; `Person` : `ReadActors` ne garde que des `IsValid` (`:178`) ; `Look` : `IsValid` (`:539`) | OK |
| Signe de D | Voir ci-dessous | confirmé pour le déplacement ; libellé ambigu pour le lacet (déclaré depuis dans l'IMPL) |
**Signe de D (raisonnement vectoriel).**
- Entrée : `MoveRight` vaut +1 sur D et −1 sur Q (`Config/DefaultInput.ini:158-160`). `MoveForward` vaut +1 sur Z/W et −1 sur S (`:155-157`). Z réduit bien la distance (`Distance − Forward × …`, `.cpp:524`) : cohérent.
- Rotation : `RotateAngleAxisRad` (`Vector.h:1339-1362`), axe (0,0,1) : `x' = C·x − S·y`, `y' = S·x + C·y`. Un angle positif envoie +X vers +Y. En UE (X avant, Y droite, Z haut), c'est le sens horaire vu de dessus, le même que le lacet croissant. L'angle appliqué à la position (`:519`) et au lacet (`:531`) est le même : le cadrage est gardé.
- Cas test : caméra à l'ouest du pivot, `Offset = (−d, 0)`, qui regarde +X ; sa droite est +Y. Pour D, `Angle = −vitesse × dt < 0`, donc `S < 0` et `y' = S·(−d) = −d·S > 0` : la caméra se déplace vers +Y, donc **vers sa droite**, autour du pivot. Son lacet diminue (la caméra se tourne vers la gauche pour garder le couple dans le champ), ce qui est normal pour une orbite.
- Conclusion : le signe `Angle = −Right × vitesse × dt` est **juste** si « D » signifie « la caméra se déplace vers sa droite » (comme un pas de côté). Il est **inverse** si « D tourne vers la droite » signifie « le lacet de la caméra augmente » ; c'est ce que fait le POV (`Yaw += Right × …`, `:542`, regard vers la droite). À l'écran, les deux modes donnent le même effet (la scène défile vers la gauche quand on appuie sur D). À confirmer par l'essai V7-1 (sens ressenti), pas bloquant.
### Contrôle 6 : SPEC / IMPL
| Élément SPEC | IMPL | Verdict |
|---|---|---|
| P3 : conditions `SexOn`, menu fermé, PC et vue valides | tenues (`:485-492`) ; menu par `bMenuOnScreen` (écart déclaré, acceptable) | OK |
| Orbite 90°/s, dolly 200 cm/s, bornes 80 à 800 cm, pivot = moyenne de `SexPersons`, tableau vide = rien | constantes `.cpp:107-110` ; moyenne `:502-510` ; tableau vide : pas de Scene, pas de POV (`Contains` faux) | OK |
| POV : `FirstPersonSM`, lacet Q/D, tangage Z/S à 90°/s, ±80° | tenu (`:535-547`). Écart : la SPEC dit `AddWorldRotation(0, lacet, 0)` et un tangage « autour de l'axe droit du composant ». L'IMPL fixe des angles d'Euler monde (`GetComponentRotation` puis `SetWorldRotation`). Équivalent tant que le roulis est nul | écart mineur, déclaré depuis dans l'IMPL (Reprise) |
| Témoin : format `Z=%d Q=%d S=%d D=%d fleches=… couple a (…)` | format réel : `avant=%+.0f cote=%+.0f fleches=…/… Maj MoveObject MoveCamera ; vue ; index POV ; couple N a (x, y, z)` | écart mineur, déclaré depuis dans l'IMPL (Reprise) |
| `.h` : « 2 membres (dernier temps de témoin, vitesse) » | 1 méthode + 2 membres (`bMenuOnScreen`, `SexKeysCooldown`), pas de vitesse (constante locale) | OK |
| P3 bis désactivé par constante | tenu | OK |
| « Environ 120 lignes » | +156 lignes dans le `.cpp`, +7 dans le `.h` (dont constantes et commentaires) | OK |
| Aucune lecture `Accessed None` | réflexion avec contrôle de nullité partout (voir (h)) | OK |
## 4. Diff lu en entier
| Fichier | Compte | Contenu |
|---|---|---|
| `Source/LifePlay/LPCameraModeSubsystem.cpp` | 4 hunks, +156 / −1 (première version) | H1 `@@ -93` : constantes et noms dans `LPCameraModeLocal` ; H2 `@@ -133` : `ReadInt`, `ReadActors`, `AxisValue` dans l'espace nommé ; H3 `@@ -247` : `bMenuOnScreen = bMenu;` dans `Tick` (`:322`) ; H4 `@@ -401` : `TickSexCamera(DeltaTime);` à la place de `(void)DeltaTime;` dans `TickSwitchKey` (`:477`), puis le corps de `TickSexCamera` (`:481-561`) |
| `Source/LifePlay/LPCameraModeSubsystem.h` | 1 hunk, +7 / 0 | `TickSexCamera`, `bMenuOnScreen`, `SexKeysCooldown` et leurs commentaires (`.h:70-76`) |
- Tout est dans le périmètre de l'IMPL. **Aucun ajout non demandé.** Le code B2r (C1, C2, occlusion, CharGen, `LogSwitchKey`, `ArrayContains`, `MenuOnScreen`) n'a aucune ligne modifiée : la seule ligne `-` du diff est `(void)DeltaTime;`.
- Les deux fichiers sont **non suivis par git** (`git status --short` : `??`). Un `git diff` contre un tag n'est donc pas possible : la référence est l'instantané. Le gardien devra les ajouter (`A`).
- Non fait ici (hors périmètre VAL statique) : `Build.ps1`, `LPTestStringCompat`, ouverture de l'éditeur, essais en jeu.
## 5. Verdict
**PASS-STATIC**, première passe puis reprise. Le code correspond à l'IMPL et à la SPEC, aucune erreur de compilation n'est attendue à la lecture, aucune dérive hors périmètre, B2r inchangé. Il ne vaut pas PASS : rien n'a été compilé ni exécuté.
## 6. Points à confirmer par l'utilisateur
- [ ] V7-1 : en Scene (une fois B2p-R posé), D déplace la caméra vers sa droite autour du couple ; Q vers sa gauche. Le sens ressenti convient-il ? Sinon, inverser le signe de `Angle` (`.cpp:517`, `:520` dans la version actuelle).
- [ ] V7-2 : en POV, D tourne le regard vers la droite, Z lève le regard. Le sens convient-il ?
- [ ] V7-3 : Z/S en Scene : la caméra se rapproche ou s'éloigne sans saut brusque au premier appui (Constats 1).
- [ ] V7-4 : après une scène, ZQSD ne fait rien hors sexe (la marche reprend), et rien pendant un menu.
- [ ] V7-5 : le journal contient `Sexe : touches …` au plus une fois par seconde ; fournir ces lignes pour le KO Maj + souris.
- [ ] BUILD : compilation de `LifePlayEditor` sous jeton UE (0 erreur ; relever tout nouvel avertissement sur `LPCameraModeSubsystem.cpp`).
- [ ] Décision de l'utilisateur : P3 bis (molette) reste désactivé tant que l'essai de P1 n'a pas montré que le zoom d'origine (nœud 125) ne convient pas.
Tours : 16 / 40
## Constats
Aucun défaut bloquant. Défauts non bloquants (première passe) :
1. **Saut de distance au premier appui** (`.cpp:524-525`). Si la caméra posée par le jeu est à plus de 800 cm (ou à moins de 80 cm) du pivot, le premier appui sur Z ou S ramène la distance à la borne en une seule image (saut visible). Conforme à la SPEC (bornes pendant l'avance et le recul) ; un correctif possible serait de n'appliquer la borne que dans le sens qui rapproche de l'intervalle. Laissé en l'état par l'IMPL : décision de l'orchestrateur. **Reste ouvert.**
2. **`DeltaTime` non borné** (`.cpp:477`, `:517`, `:524`). Après un à-coup d'une seconde, l'orbite fait 90° d'un coup et l'avance 200 cm. **Levé à la reprise** (voir Reprise).
3. **Écarts SPEC/IMPL non déclarés** : format du témoin, angles d'Euler monde au lieu de `AddWorldRotation`. **Levé : déclarés dans l'IMPL** (voir Reprise).
4. **Libellé ambigu** de l'IMPL (« D tourne vers la droite »). **Levé : précisé dans l'IMPL** (voir Reprise).
5. **Gabarit IMPL incomplet** : les sections « Points à confirmer en VAL » et « Fichiers à commiter » manquent. Fichiers à commiter attendus : `A Source/LifePlay/LPCameraModeSubsystem.cpp` et `A Source/LifePlay/LPCameraModeSubsystem.h` (non suivis dans git, § 4), plus l'IMPL et la présente VAL. **Reste ouvert** (je n'ai pas revu cette section à la reprise : elle n'apparaît pas dans les lignes 44-65 relues).
6. **Incertain, non mesuré** : un avertissement MSVC sur `bSexWheelZoom ? … : 0.f` (condition constante) n'est pas exclu à la compilation ; C4127 et C4702 sont coupés par la chaîne d'outils d'après `VCToolChain.cs`, mais seul `Build.ps1` le prouve. Si la molette est un jour activée, le sens de `ScrollDownUp` (+1 sur `MouseWheelAxis`, `DefaultInput.ini:154`) et l'échelle par cran seront à régler à l'essai. **Reste ouvert.**
## Reprise (2026-10-14) : borne du pas de temps (constat 2)
Objet : revérifier la seule modification faite après la VAL. Toujours statique : ni compilation, ni éditeur, ni MCP.
| Contrôle | Commande / lecture | Résultat brut | Verdict |
|---|---|---|---|
| MD5 | `md5sum` sur `Source/LifePlay/LPCameraModeSubsystem.cpp` et `.h` | `.cpp` `8e35e07ae08f3f208a24dc4cf2b3338f` (annoncé `8E35E07AE08F3F208A24DC4CF2B3338F`) ; `.h` `9e20981f8c768371a0935e2c7ce9e134` (annoncé `9E20981F…`, inchangé) | PASS |
| Lignes | `wc -l` | `.cpp` 820 (annoncé 820), `.h` 84 | PASS |
| Fins de ligne | `tr -cd '\r' < fichier \| wc -c` | `.cpp` CR=0, `.h` CR=0 | PASS |
| Diff contre l'instantané | `diff -u` instantané → actuel : 4 hunks, 153 lignes `+` non vides, 1 ligne `-` (`(void)DeltaTime;`). Comparaison avec mon diff de la première passe (`diff` des deux diffs, en-têtes et numéros de hunk mis à part) | Les seules différences avec la première passe sont : (1) `+ const float SexMaxStep = 0.05f; // pas de temps maximal pris en compte (s)` (`.cpp:117`) ; (2) deux lignes dans `TickSexCamera` : un commentaire et `const float Step = FMath::Min(DeltaTime, LPCameraModeLocal::SexMaxStep);` (`.cpp:486`) ; (3) quatre substitutions `DeltaTime` → `Step` : `Angle` (orbite), `Distance - Forward * SexDollySpeed * Step` (avance ou recul), `Rotation.Yaw += Right * SexLookSpeed * Step` (lacet POV), `Rotation.Pitch + Forward * SexLookSpeed * Step` (tangage POV). Les numéros de hunk bougent de 1 à 3 lignes, rien d'autre | PASS : exactement 3 lignes ajoutées et 4 substitutions, aucune autre modification |
| `DeltaTime` restant | `grep -n "DeltaTime\|Step"` sur la zone | dans `TickSexCamera`, `DeltaTime` n'apparaît plus qu'à la signature, au calcul de `Step` et à `SexKeysCooldown = FMath::Max(0.f, SexKeysCooldown - DeltaTime)` : le témoin garde le temps réel (une ligne par seconde réelle) | PASS |
| Compilation à l'œil | lecture des lignes ajoutées | `SexMaxStep` : `const float` dans l'espace nommé `LPCameraModeLocal`, appelée qualifiée. `FMath::Min(float, float)` donne un `float`, `Step` est un `float` ; il remplace un `float` : mêmes types dans les produits `float`, puis `double` dans le `Clamp` comme avant. `Step` est déclaré avant son premier usage (`:486`, usages plus bas) ; aucun membre ni variable locale homonyme (`SexWheelStep` est un autre nom). Aucun nouvel include nécessaire (`FMath` déjà utilisée) | PASS-STATIC |
| Effet voulu | lecture | Un à-coup d'image est borné à 0,05 s : au plus 4,5° d'orbite, 10 cm d'avance, 4,5° de regard par image. Au-dessus de 20 images par seconde, aucun changement de comportement | PASS-STATIC |
| IMPL relu | `CAM-B2p-C-IMPL.md`, lignes 12-13 et 46-63 | Tableau : `.cpp` `8E35E07AE08F3F208A24DC4CF2B3338F`, 820 lignes ; `.h` inchangé. § Écarts : les constats 3 et 4 sont **déclarés** (format du témoin `avant/cote` et `couple N a` ; angles monde en POV au lieu d'`AddWorldRotation` ; sens de D, caméra vers sa droite en Scene et regard vers la droite en POV, ressenti à confirmer en V7). La reprise (constat 2) et le constat 1 laissé en l'état y sont notés | PASS |
Constats après la reprise : 2 levé (borne ajoutée et revérifiée), 3 et 4 levés (déclarés dans l'IMPL). Restent ouverts, non bloquants : 1 (saut à la borne), 5 (sections « Points à confirmer en VAL » et « Fichiers à commiter » : non revues), 6 (avertissement MSVC possible). Un nouveau point, mineur : avec la borne à 0,05 s, sous 20 images par seconde la caméra va plus lentement que les vitesses annoncées (comportement voulu pour un jeu qui rame).
**Verdict final de la reprise : PASS-STATIC.** Défauts bloquants : 0. MD5 final : `.cpp` `8e35e07ae08f3f208a24dc4cf2b3338f`, `.h` `9e20981f8c768371a0935e2c7ce9e134`.
Tours (reprise comprise) : 16 / 40. Fichier écrit : `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\CAM\CAM-B2p-C-VAL.md` (à commiter : `A`).