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

268 lines
42 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-J-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-J-VAL : validation statique du correctif C++ J1 à J6 et contre-vérification de la DIAG (2026-10-14)
Copie `D:\Game\LifePlay-master 5.8`, branche `ue5.8`. Aucune compilation, aucun jeu, aucun éditeur, aucun commandlet, aucune écriture git. Seul fichier écrit dans le projet : ce livrable. Scripts de lecture et diffs dans le scratchpad (hors projet) : `check1.ps1`, `hunks.ps1`, `diff_cpp.txt`, `diff_h.txt`, `diff_cpp_u0.txt` (première passe) ; `check2.ps1`, `diff2_cpp.txt`, `diff2_h.txt` (reprise). Aucune modification de code, de config ou de doc validée : rien à inventorier sur une copie jetable.
**Statut courant : voir la section « Reprise » en fin de fichier (verdict global mis à jour : PASS-STATIC). Les sections 1 à 6 et les Constats ci-dessous sont la première passe (verdict d'alors : FAIL sur D1), conservés tels quels.**
## 1. Fiches amont relues
- `CAM-B2p-J-IMPL.md` (DIAG § 1, correctif § 2, PC § 3, proposé § 4, essais § 5, `5 bis` vide « à compléter » à la première passe), lu en entier.
- `CAM-B2p-I-IMPL.md` (base : I0, I1, reprise D1 de la VAL) et `CAM-B2p-I-VAL.md` (verdicts et défauts D1 à D3 de B2p-I), lus pour le contexte ; défauts D1 à D3 de B2p-I recoupés avec le diff (voir 2, point J5).
- `CAM-B2p-G-PC-IMPL.md` et `CAM-B2p-G-PC-FICHE.md` (nœuds 134, 408, 277, 40 de G-g (2)), `CAM-B2p-R-IMPL.md` (O1 de Room).
- Journal d'essai `D:\Game\LifePlay_Backup_5.5\COMMUN-12\essais\commun12_LifePlay.log` (24 682 lignes, UTC, relu aux passages cités). Dump `CAM-B2p-G-PC-VAL_LPDumpFunction_20261014-135527.txt`. Moteur `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime`. `Config\DefaultInput.ini`.
- Conformité de l'IMPL à son gabarit (lp-doc/gabarits.md) : **écart** (D5) : pas de tête commune (`Fiche … — Niveau …`), pas de section « Écarts à la SPEC », pas de « Points à confirmer en VAL » (remplacés par § 5 « Essais »), pas de ligne `Tours : …`, § 5 bis vide.
## 2. Points à confirmer des IMPL (un par ligne)
Le § 5 de l'IMPL ne liste que des essais (V7). Les points ci-dessous suivent la fiche de la VAL (1 à 9) et les essais CAM-91 à CAM-95, CAM-86 à CAM-90.
| # | Point | Verdict | Preuve |
|---|---|---|---|
| 1 | DIAG § 1, faits établis (poses identiques, rotations +0, aucune ligne de refus, bascules `LogViewport`) | **PASS** | § 3.1 |
| 1b | DIAG § 1.1, cause « GameOnly fige le curseur » | **estimée, non établie, partiellement contredite** (prises 2 et 3 de 12:38) | § 3.1, D2 |
| 1c | DIAG § 1.3, +22 cm par prise = molette de Room | **estimée, non étayée** (la bascule du panneau coïncide) ; +9 et Maj + molette **établis** | § 3.1, D6 |
| 1d | Autres causes (canal Pawn H3, G-f/H4, sélection) | **écartées pour la rotation (établi), restent possibles pour le glisser avant J3 (estimé)** | § 3.1 |
| 2 | J1 `KeepMouseFree` : conditions, API, absence de boucle, clavier | **PASS-STATIC**, réserves D2 (couverture) et D4 (drapeau de curseur) | `.cpp:1249-1285`, moteur § 3.2 |
| 3 | J2 `GrabCursor` : position réelle, deltas, signe de Y, `TraceScreen`, seuil, rotation | **FAIL** : D1 **bloquant** (échelle des deltas) ; reste PASS-STATIC (Y, tracé, seuil, `bMouse &&` retiré, non-régression) | `.cpp:1590-1610, 1678-1681`, `DefaultInput.ini:16-17` |
| 4 | J3 : lignes de refus et témoins | **PASS-STATIC** (formats, cadences, témoin hors CAM), libellé « deltas bruts » inexact (D1) | § 3.4 |
| 5 | J4 Alt | **PASS-STATIC** (note N1) | `.cpp:1208-1247` |
| 6 | J5 restauration | **PASS-STATIC** pour le front, le premier passage, `SetConsumeCaptureMouseDown`, la signature ; **réserves D3, D4** (état d'entrée rendu hors du sexe) | `.cpp:1059-1071, 1287-1357` |
| 7 | J6 texte d'aide, `ActionPause` | **PASS-STATIC** | `.cpp:1964-1985` |
| 8 | Transverse : `/we4456 /we4458 /we4459`, C4244, includes, `.h`/`.cpp` | **PASS-STATIC** ; compilation **BLOCKED-compilation** (à BUILD) | § 3.5 |
| 9 | § 3 PC : recette qui défait G-g (2) seule | **PASS-STATIC** | § 3.6 |
| E-91 | CAM-91 glisser et rotation panneau fermé, ligne `souris liberee` | chemin de code lu ; **reporté V7** (risque D1, D2) | |
| E-92 | CAM-92 lit posé, commode refusée | non touché par J ; **reporté V7** | |
| E-93 | CAM-93 Alt bref / Alt tenu ; Espace après le PC | code **PASS-STATIC** ; Espace **reporté** (PC, UE-GO) | |
| E-94 | CAM-94 ligne `fin de scene … touches inchangees` | format **PASS-STATIC** ; comportement **reporté V7** (D3, D4) | |
| E-95 | CAM-95 ligne `deplace pendant la prise hors CAM` | format **PASS-STATIC** ; **reporté V7** | |
| E-86..90 | CAM-86 à CAM-90 (B2p-I) | inchangés par J (hunks hors `AttachHelpIcon`, `ToggleControlsHelp`) ; **reporté V7** | |
## 3. Critères rejoués (commande → résultat)
### 3.0 Entrée, fins de ligne, base
Commande : `pwsh -NoProfile -File …\scratchpad\check1.ps1` (octets lus par `[IO.File]::ReadAllBytes`, MD5 par `Get-FileHash`, comptes d'octets 10 et 13). `FileCheck.ps1` non rejoué (les fichiers sont non suivis par git, pas de manifeste comparable) : remplacé par ce comptage.
| Fichier | Attendu (fiche) | Obtenu | Verdict |
|---|---|---|---|
| `.cpp` final | MD5 `9D9291021BBB44BD8AD49448494FAB3A`, 2197 lignes, LF | MD5 `9D9291021BBB44BD8AD49448494FAB3A`, 96 476 octets, LF 2197, CR 0, BOM non | PASS |
| `.h` final | MD5 `9D85465E284F626D00212FFAA331C4BB`, 203 lignes, LF | MD5 `9D85465E284F626D00212FFAA331C4BB`, 11 109 octets, LF 203, CR 0, BOM non | PASS |
| `.cpp` base `CAM-B2p-I3` | `0F383EF7…`, 1976 lignes | MD5 `0F383EF777DED71608347E17D4A0861F`, LF 1976, CR 0 | PASS |
| `.h` base `CAM-B2p-I3` | `6EDBBCCF…`, 179 lignes | MD5 `6EDBBCCF61759B8C6EE4890DF9BC5FE7`, LF 179, CR 0 | PASS |
`git status --short -- Source` : `LPCameraModeSubsystem.cpp` et `.h` sont `??` (non suivis) : pas de `git diff <tag>` possible, diff par `git diff --no-index` contre l'instantané (code 1 = différences).
### 3.1 Contre-vérification de la DIAG § 1 (journal, dump, moteur)
| Affirmation | Source relue | Résultat | Niveau |
|---|---|---|---|
| Prises existantes `couple pris (deplacement)` 12:36:24, 12:37:39, 12:38:15 | journal 4412, 5160, 5475 | confirmées (heures exactes) | établi |
| Chaque pose à la même place | paires pris/pose : 4412/4414, 4416/4456, 4457/4459, 5160/5162, 5208/5210, 5475/5480, 5481/5520, 5973/5975, 6022-6028, 6190/6192, 6256/6295, 7268/7309, 21239/21241, 21591/21593 | coordonnées identiques dans les 14 paires relevées (2 sessions : 12:35 et 12:49) | établi |
| Rotations de +0 : 12:36:32, 12:37:41, 12:38:20, 12:38:26 | 4464 (« -0 »), 5166, 5527, 5589 | confirmées ; 21 lignes `couple tourne de ±0` au total | établi |
| Aucune ligne `surface refusee` ni `place refusee` | Grep des deux motifs sur tout le journal | 0 ligne | établi |
| Bascules `LogViewport` GameOnly puis relâché | 5473-5474 (12:38:14.280 `DoNotLock → LockOnCapture`, `CaptureDuringMouseDown → CapturePermanently`, juste après `panneau bascule` 5472) ; 5533-5534 (12:38:21.540) ; retour 5478-5479 (12:38:16.701, au premier relâché) | confirmées. Nouveau : le retour à `CaptureDuringMouseDown` vient **du jeu, au premier relâché, sans ligne de bascule du panneau** (même motif 4410-4411, 5158-5159, 5768-5769) | établi |
| `FInputModeGameOnly::ApplyInputMode` : haute précision et verrou | `PlayerController.cpp:6439-6451` : `UseHighPrecisionMouseMovement`, `SetUserFocus`, `LockMouseToWidget`, `LockOnCapture`, `CapturePermanently` (ou `…IncludingInitialMouseDown` si `bConsumeCaptureMouseDown` faux) | confirmé mot pour mot | établi |
| Voie d'ouverture du panneau | dump 1034-1041 : `SetInputMode_GameAndUIEx`, `InMouseLockMode='DoNotLock'`, `bHideCursorDuringCapture='false'`, focus sur `CursorREF` ; dump 1171 : `CallFunction_1394` `SetInputMode_GameOnly` | confirmé (le dump 1171 est bien `K2Node_CallFunction_1394`) | établi |
| Molette = +9 à 12:37:57 | journal 5286 : `deplace hors prise de (+0, +0, +9) ; fleches=+0/+0 Maj=0 molette=+1.0` | confirmé, **sans Maj** | établi |
| Maj + molette vers Z 165 à 171 | 5676 (12:38:37, Z 171, `Maj=1 MoveObject=1`), 5717 (147), 5718 (165), 5894, 5974 (170) | confirmé | établi |
| Borne racine ≤ plafond − 120 | 3786 `sol -102 plafond 283 (marge 80)`, 4739 `racine au plus 163` (283 − 120 = 163) ; sol −102 : 163 + 102 = 265 cm | confirmé | établi |
| `prise=0 MoveCamera=1` « 12:41 à 12:43 » | Grep `MoveCamera=1 ; boutons` : 3829 (12:35:26) et toutes les 5 à 10 s depuis | exact mais **incomplet** : présent dès 12:35:26 (clic droit hors du couple, orbite normale) | établi (imprécision) |
**Cause du § 1.1 : estimée, non établie.** Établis : les symptômes, la voie GameOnly du PC, les faits moteur. Non établi : « le curseur ne bouge pas pendant l'appui » (aucune position écrite dans le journal) et le lien GameOnly vers position figée.
Contradiction partielle : le retour à `CaptureDuringMouseDown` / `DoNotLock` se produit au premier relâché (5478-5479, 12:38:16.701). Les prises **2** (`pris` 5481, 12:38:17.088, `pose` 5520, même place) et **3** (rotation, 5521 à 5527, `+0`) ont eu lieu dans cet état, avec `HideCursorDuringCapture` resté vrai (dernier changement avant : 5368, 12:38:03.277 `False → True` ; suivant : 5525, 12:38:20.437). Soit l'utilisateur n'a pas bougé la souris (rien ne l'infirme), soit une autre cause fige le curseur : `SceneViewport.cpp:756-758, 780-783` cache le curseur et passe en haute précision à l'appui quand `HideCursorDuringCapture()` est vrai, ce qui figerait la position même sans GameOnly. L'état qui marchait (COMMUN-11) est `DoNotLock` **et** `HideCursorDuringCapture` faux (dump 1040-1041).
Autres causes : H4 et G-f ne sont pas atteintes (aucune ligne de refus ; elles écrivent). H3 (canal Pawn) : le glisser ne l'atteint qu'après `bGrabMoved`, et avant J3 un échec de `TraceCursor` était **muet** (l'ancien `if (bGrabMoved && TraceCursor(Aim))` n'écrivait rien) : possible pour le glisser, **estimé**. Pour la rotation, aucune dépendance au tracé : 4 rotations à `+0` avec `bMouse` vrai à la prise indiquent que `MouseX` n'a pas varié (curseur figé ou souris immobile), **estimé fort**. La sélection fonctionne (les prises existent).
**+22 cm par prise (§ 1.3, estimé par l'IMPL)** : les deux épisodes de 12:38 coïncident chacun avec **un `panneau bascule` à l'intérieur de la prise** (5524 à 12:38:20.437 puis G3 à 5528 ; 5540 à 12:38:22.765 puis G3 à 5544), tandis que la prise de rotation 5584-5589, sans bascule, ne bouge rien. 22 n'est pas un multiple de 9. Le témoin hors prise de 12:35:42 donne `+25` avec `molette=-1.0` (3986), sens contraire du -9 de 6132. La molette de Room n'est donc **pas établie** comme source ; la bascule du panneau (clic molette pendant la prise) est un autre candidat. Le témoin J3 écrit `molette=` : il tranchera (D6).
### 3.2 Moteur 5.8 : API de J1 et de J5
| API | Source | Constat |
|---|---|---|
| `UGameViewportClient::GetMouseCaptureMode() const` | `GameViewportClient.h:786` | existe, public |
| `HideCursorDuringCapture() const` | `GameViewportClient.h:831` | existe (utile pour D2) |
| `FInputModeGameAndUI::SetLockMouseToViewportBehavior`, `SetHideCursorDuringCapture` | `PlayerController.h:191, 194` | existent |
| `FInputModeGameOnly::SetConsumeCaptureMouseDown` | `PlayerController.h:219` | existe |
| `FInputModeGameAndUI::ApplyInputMode` | `PlayerController.cpp:6398-6414` | `SetFocusAndLocking(…WidgetToFocus…)` : **pas de focus déplacé sans widget** (`6315-6318`), `ReleaseMouseLock` si `DoNotLock`, `ReleaseMouseCapture`, `SetHideCursorDuringCapture`, `CaptureDuringMouseDown` : le clavier reste à la vue, établi |
| `FInputModeGameOnly::ApplyInputMode` | `PlayerController.cpp:6439-6451` | `consume ? CapturePermanently : CapturePermanently_IncludingInitialMouseDown` : le mappage de J5 (`Avant == CapturePermanently`) est exact ; ne touche **pas** `HideCursorDuringCapture` (D4) |
| `APlayerController::SetInputMode` | `PlayerController.cpp:6454-6468` | un appel, sans événement Blueprint : pas de boucle avec le PC |
| `UPlayerInput::GetKeyValue` | `PlayerInput.cpp:2199-2204` rend `KeyState->Value.X` ; `Value.X = MassageAxisInput(…)` (`1264`) | **valeur traitée**, voir D1 |
| `UPlayerInput::GetRawKeyValue` | `PlayerInput.h:760`, `PlayerInput.cpp:2206-2211` | existe, rend `RawValue.X` (non traitée) |
| `DeprojectScreenPositionToWorld(float, float, …)` | `PlayerController.cpp:2197-2200` | même chemin que `DeprojectMousePositionToWorld` (`2182-2195`, `UGameplayStatics::DeprojectScreenToWorld` avec la position souris) : équivalent à écran identique |
| Signe de Y | `SceneViewport.cpp:928-931` : `MouseDelta.X += …`, `MouseDelta.Y -= CursorDelta.Y` | `MouseY` positif = vers le haut ; `-GetKeyValue(MouseY)` donne un Y d'écran vers le bas : **signe correct** |
### 3.3 J1 `KeepMouseFree` (`.cpp:1249-1285`, appel `1129`)
- **Conditions** : appelée dans `TickSexCamera` après le retour `!bSexOn || bMenuOnScreen` (`1112-1122`) : sexe vrai, hors menu (menu relevé toutes les 0,1 s, `Tick` → `bMenuOnScreen`). Aucun bouton tenu (`1259-1260`). Seulement `CapturePermanently` ou `CapturePermanently_IncludingInitialMouseDown` (`1265`). `TickSexCamera` est appelé à chaque image par `TickSwitchKey` (ticker à 0 s, `641`, `814`) : exact (pas à 0,1 s).
- **Sexe** : la fiche dit « conditions (sexe …) » : le sexe est le front de `GI.SexOn`, pas un test du sexe des personnes ; la condition est donc « pendant la scène », sans limite aux modes Scene (aussi en POV).
- **Pas de boucle avec le PC** : après l'appel, le mode est `CaptureDuringMouseDown` (`6412`), la condition `1265` n'est plus vraie ; le PC ne réagit pas à `SetInputMode`. Si le PC remet GameOnly (fermeture du panneau, dump 1171), J1 le refait à l'image suivante : au plus une application par fermeture. Journal limité : une ligne toutes les 2 s (`MouseFreeCooldown`, `1255, 1279-1284`), `%d`/`%d` pour 2 `int32` : conforme.
- **Clavier** : voir 3.2, pas de `WidgetToFocus` : le focus posé par GameOnly (`6446`) reste. Établi.
- **Réserves** : D2 (J1 ne voit pas `CaptureDuringMouseDown` + `HideCursorDuringCapture` vrai), D4 (drapeau non rendu).
### 3.4 J2 et J3 (`TickCoupleMouse`, `.cpp:1367-1702`)
- **`GrabCursor`** : initialisé au clic à `GrabMouseStart` (`1505-1509`) ; à chaque image : position réelle si elle a bougé de 0,5 px au moins (`1594-1597`), sinon accumulation de `RawDelta` (`1598-1602`) ; `GrabLastReal` mis à jour seulement avec `bMouse` (`1603-1606`). Pas de double comptage (branches exclusives). Mélange réel/brut sans saut tant que l'échelle brute vaut 1 pixel : **faux ici (D1)**.
- **D1 (bloquant)** : `RawDelta` lit `Input->GetKeyValue(EKeys::MouseX/MouseY)` (`1593`). Cette valeur est `MassageAxisInput` (`PlayerInput.cpp:2305-2387`) : `NewVal *= AxisProps->Sensitivity` (`2337`), puis FOV (`2352`) et lissage (`2366`, activé : `DefaultInput.ini:78 bEnableMouseSmoothing=True`). Le projet fixe `Sensitivity=0.070000` pour `MouseX` et `MouseY` (`DefaultInput.ini:16-17`, déjà en `8-9`). Un déplacement de 100 px de souris donne donc ≈ 7 « px » dans `GrabCursor` (facteur 14 environ). Conséquences : seuil de glisser `CoupleDragThreshold = 5` (`:136`) franchi après ≈ 71 px de souris ; point visé `TraceScreen(GrabCursor)` 14 fois plus lent que la main ; rotation `CoupleTurnPerPixel = 0.3` (`:137`) réduite à ≈ 0,02° par pixel. Le filet de J2, présenté comme « filet si J1 ne passe pas » (IMPL § 2 J2), ne tient donc pas ses promesses dans les états où le curseur reste figé (dont les états non couverts par J1, D2). Correction : `GetRawKeyValue` (valeur en pixels de la fenêtre, `SceneViewport.cpp:928-931`, cumulée dans `RawValueAccumulator`, copiée en `RawValue` à chaque `EvaluateKeyMapState`, `PlayerInput.cpp:1354-1361`), ou division par la sensibilité. Le libellé « deltas bruts » des lignes J3 est inexact en l'état.
- **Signe de Y** correct (3.2). **`TraceScreen`** : même tracé que `TraceCursor` (Visibility, mêmes ignorés, `CursorTraceLength`) avec la position passée (`1399-1411`) ; appelé à la prise avec `GrabMouseStart` (`1522`) et au glisser avec `GrabCursor` (`1612`) : cohérents. **Seuil** : `Distance(GrabMouseStart, GrabCursor) >= 5` (`1610`). **Rotation** : `Turn = (GrabCursor.X - GrabLastMouseX) * 0.3`, `GrabLastMouseX` mis à `float(GrabCursor.X)` (`1680-1681`), initial `MouseX` (`1510`) : continu. **`bMouse &&`** : retiré des deux branches (`1608`, `1678`) ; la sélection exige toujours `bMouse` (`1456-1464`, ligne `curseur sans position` conservée). Aucun `bMouse &&` ne bloque plus le glisser ni la rotation.
- **Non-régression B2p-D à H** (diff lu, 22 retraits tous recoupés, voir 4) : pose (`1620-1675` inchangé hors `GrabPivotAfterCam`), G-e (`TraceDown`, `GrabHeight` : intacts), G-f (`1648-1658` intact), G1 (`1659-1670`), N2 (`LastSurfaceRefused.Reset()` `1669`, conservé), caméra figée (`1695-1700` intact), G3 (`1553-1567` intact, `GrabLeadStart`). PASS-STATIC.
- **J3 formats `%` contre arguments** (relus un à un) : `1282` 2/2 ; `1495-1496` 4 `%` (`%.0f %.0f %s %.0f`) pour `MouseX, MouseY, chaîne, rayon` ; `1540-1541` 4/4 (`%.0f %.0f %d %s`) ; `1549-1550` 3/3 (`%.0f %d %s`) ; `1583-1585` 5/5 (`%+.0f ×3 %+.1f %d`) ; `1616` 2/2 ; `1237` 2/2 ; `1293` 1/1 ; `1353-1356` 7/7 (`%d %d %d %s %s %s %s`). Les `%s` conditionnels de tableaux de longueurs différentes décroissent en `const TCHAR*` (motif déjà présent `1048-1049`, `1237`).
- **Cadences** : `clic hors du couple` 2 s au plus (`SelectMissLogTime`, `1492`) ; `deplace pendant la prise hors CAM` 1 s au plus (`1579`) ; `ne vise aucune surface` une fois par prise (`bGrabAimWarned` remis à faux à la prise `1508`) ; `glisser sans effet` et `rotation sans effet` une fois par prise (au relâché).
- **Témoin « hors CAM » ne compte pas les pas de CAM** : `GrabPivotAfterCam = Pivot` à la prise (`1509`), `= Pivot + Delta` après chaque déplacement du glisser (`1667`, avec le même `Delta` que `SetActorLocation`) ; la rotation tourne autour du pivot (moyenne des positions : conservée, tolérance 1 cm `1576`). Le témoin met à jour `GrabPivotAfterCam` même quand la ligne est retenue par la cadence (`1587`). PASS-STATIC.
### 3.5 J4, J5, J6, transverse
- **J4** (`1208-1247`) : `bAltKey = IsPressed(LeftAlt) ou IsPressed(RightAlt)` (`1213`), `bMiddle = bouton ou Alt` ; `bPanelByAlt = bAltKey && !bMiddleButton` à l'appui (`1220`) ; seuils inchangés `PanelClickMaxHold 0.2 s` et `PanelClickMaxMove 5 px` (`:186-187`, `1232`) ; fonction appelée seulement pendant le sexe hors menu (`1112-1128`) ; `bMiddleWasDown` remis à faux hors sexe ou menu (`1076, 1116`). Appui tenu ou glissée : pas de bascule (`MiddleHeld > 0.2` ou `bMiddleMoved`). `RotateCamera` = clic molette, `LeftAlt`, `RightAlt` (`DefaultInput.ini:107-109`) : ouvert aux deux. Ligne `panneau bascule (%s, %.2f s)` 2/2. Note N1.
- **J5 front** : `bSexStateWas` initial faux (`.h:180`) ; au premier passage avec `SexOn` faux : égalité, aucun appel (premier passage correct, pas de fin de scène fantôme) ; avec `SexOn` vrai dès le départ : `BeginSexScene` (`1060-1071`). **`BeginSexScene`** : signature des mappages, remise à zéro de `bMouseFreed`, `MouseFreeCount`, `MouseFreeCooldown`. **`MappingSignature`** (`318-337`) : `GetActionMappings` et `GetAxisMappings`, `FCrc::StrCrc32(*Joined)` (`uint32`) avec `%08X`, bitfields `bShift/bCtrl/bAlt` convertis en `int` ; sans modification des mappages (lecture seule). **`EndSexScene`** (`1296-1357`) rend : prise levée (`ReleaseCoupleGrab`), `bMiddleWasDown`, `bHelpBoxOpen`, boîte d'aide et icône repliées, **opacité de `TextBlock_1`** (`SetRenderOpacity(FadedLinesOpacity)` puis `Reset`, D3 de B2p-I traité), `MoveCamera` à faux **si aucun bouton souris n'est tenu**, `SexC3` à faux (**déjà fait hors sexe par `TickCoupleBounds`, `837-839`** : aucun état nouveau), mode d'entrée GameOnly si `bMouseFreed` et vue encore en `CaptureDuringMouseDown` et `!MenuOnScreen(GI)` (`1335-1350`), avec `SetConsumeCaptureMouseDown(MouseCaptureBefore == CapturePermanently)` : mappage exact (3.2). Ligne finale 7/7. Réserves : **D3** et **D4** ; note N2 (`MoveCamera` avec Alt tenu).
- **J6** (`1964-1985`) : `Printf` à **7 `%s`** (CAMERA : 1 + 2 + 2 ; COUPLE : 2) pour **7 arguments** (`1981-1984`). « Mouse wheel: raise / lower the couple » : étayé par le journal (5286, `Maj=0`, +9). « Alt or middle click (short press) » : conforme à J4. `ActionPause` : Grep `ActionPause|"Pause"` sur `LPCameraMode*` = 0 ligne : retiré sans reste (déclaration `201` et argument `1984`).
- **Fins de ligne et includes** : LF, 0 CR ; aucun nouvel `#include` nécessaire (`FInputModeGameAndUI`, `FInputModeGameOnly` par `GameFramework/PlayerController.h:26`, `FCrc`, `FPlatformTime` par `CoreMinimal.h`, `UTextBlock`/`UButton` déjà inclus `7, 12`) ; le diff n'ajoute aucun include. `.h` : 24 lignes, un seul bloc contigu (l. 172-195), tout en `private`, aucun `UPROPERTY` ni `UFUNCTION` (pas d'effet UHT).
- **`/we4456 /we4458 /we4459`** : (a) membres de la classe (`.h:37-203`) contre les nouveaux locaux et paramètres : aucun homonyme (`Capture`, `Input`, `GI`, `ViewportClient`, `FreeMouse`, `Lines`, `HelpIcon`, `World`, `PC`, `Now`, `Nearest`, `Outside`, `Screen`, `RealCursor`, `RawDelta`, `CursorTravel`, `ScreenGap`, `bAim`, `Joined`, `Settings`, `Map`, `InputText`, `LinesText`, `MappingsNow`, `bStillFreed`, `bButtonHeld`, `GameOnly`) ; (b) locaux englobants : `Now` est déclaré dans deux blocs frères (`1491` dans `if (!bOnCouple)`, `1578` dans `if (Dist…)`), `Aim` dans deux blocs frères (`1521`, `1611`, ancien), aucun nouveau local ne masque `Input`, `World`, `MouseX/Y`, `bMouse` du niveau de la fonction ; (c) noms globaux ou d'espace anonyme de `Source\LifePlay` : Grep sur 21 identifiants en ligne d'indentation 0 ou 1 de tous les `.cpp` et `.h` : **aucun** nom de portée fichier ni d'espace anonyme n'est homonyme. Les `Now` (`LPBonusRewardSubsystem.cpp:811`, `LPDebugSubsystem.cpp:717`, `LPHairstyleSubsystem.cpp:169`, `LPModInfoSubsystem.cpp:215`) sont des locaux de fonction, et `Capture` (`LPSecondScreenSubsystem.h:34`) un membre d'une autre classe : sans effet de masquage. L'auteur avait donc raison. **Non vérifié** : les noms globaux des en-têtes du moteur (impossible par lecture exhaustive) : à BUILD.
- **Conversions (C4244)** : `static_cast<float>` partout où un `double` va vers un `float` (`1404`, `1680-1681`) ; `GrabLastMouseX = MouseX` (float vers float) ; `Nearest` en `double` ; `FVector2D(float, float)` élargit sans perte ; `%.0f` reçoit des `double` ou des `float` promus. Aucune conversion rétrécissante détectée.
### 3.6 § 3 de l'IMPL : recette PC (dump `…135527.txt`)
| Nœud | Dump | Constat |
|---|---|---|
| `IfThenElse_40` | 1546-1550 | `then → IfThenElse_134.execute`, `else → IfThenElse_41` ; `execute ← 63.else`, `Condition ← CallFunction_125` |
| `IfThenElse_134` | 6513-6517 | `execute ← 40.then`, `Condition ← VariableGet_22.SexOn`, `then → CallFunction_408`, `else → CallFunction_277` |
| `CallFunction_408` | 6509-6511 | `LeftMouseSexOn()`, `execute ← 134.then`, `then` vide |
| `CallFunction_277` | 4423-4426 | `PlayClick()`, `execute ← 134.else`, `then → VariableSet_0` |
| `VariableGet_22` | 986-988 | `SexOn → IfThenElse_15.Condition, IfThenElse_134.Condition` |
| `IfThenElse_15` | 989-993 | `Condition ← VariableGet_22`, `then` vide, `else → CallFunction_160` (inchangé) |
| Avant G-g (2) | `CAM-B2p-G-PC-IMPL.md:35-36, 128-131` | `40.then → 277`, `277.then → VariableSet_0` |
| En-tête | dump 6518 | `PC :: EventGraph (1447 noeuds)` |
La recette (instantané ; `connect_pins 40.then → 277.execute` : une sortie d'exécution n'a qu'un lien, il remplace `40 → 134`, comme à l'aller `G-g` (appels 38-40) ; suppression de 134, qui emporte `22 → 134`, `134.then → 408`, `134.else → 277` ; suppression de 408, orphelin) restitue exactement `40.then → 277`, garde `22 → 15` et ne touche pas les coupures G-g (1) (nœuds 387 et voisins). Nœuds supprimés : 134 et 408, soit 1447 → 1445 : cohérent. Relecture de 40, 277, 22, 15 : bien les quatre nœuds dont un voisin change. **PASS-STATIC** ; exécution = UE-GO.
## 4. Diff lu en entier
| Fichier | Base (`CAM-B2p-I3`) | Final | `git diff --no-index --numstat` | Recompte indépendant |
|---|---|---|---|---|
| `LPCameraModeSubsystem.cpp` | 1976 lignes | 2197 | `243 22` | `hunks.ps1` : 29 hunks, `+243 -22` ; lignes `+`/`-` de `-U0` : `plus=243 minus=22` |
| `LPCameraModeSubsystem.h` | 179 | 203 | `24 0` | 1 hunk (`@@ -169,6 +169,30 @@`), +24, 0 retrait |
Répartition par point (nouveau `.cpp` ; les hunks mixtes J2/J3 sont partagés à l'estime, la somme J2 + J3 est exacte : 85 ajoutées, 13 retirées) :
| Point | Lignes `.cpp` | + | − |
|---|---|---|---|
| J1 | `1129` ; `1249-1286` | 39 | 0 |
| J2 | `1398-1399`, `1404`, `1505-1506`, `1522`, `1590-1610`, `1612`, `1678-1681` (part) | ≈ 33 | ≈ 9 |
| J3 | `1468`, `1477-1485`, `1490-1497`, `1507-1509`, `1533-1551`, `1574-1588`, `1613-1617`, `1667` (part) | ≈ 52 | ≈ 4 |
| J4 | `1210-1214`, `1220`, `1237` | 7 | 2 |
| J5 | `318-338` (21) ; `1059-1071` (13) ; `1287-1358` (72) | 106 | 0 |
| J6 | `200` (retrait de `ActionPause`) ; `1966-1968`, `1978`, `1980`, `1984` | 6 | 7 |
| **Total** | | **243** | **22** |
Les 22 retraits recoupés un à un : `ActionPause` (1), `bMiddle` (1), `UE_LOG` du panneau (1), déclaration du lambda (1), `DeprojectMousePositionToWorld` (1), condition de projection (2), `bOnCouple = true; break;` déplacés (2), `GrabOffset` (1), `if (bMouse && CoupleGrabMode == 1)` (1), `bGrabMoved |=` (1), `if (bGrabMoved && TraceCursor(Aim))` (1), `else if (bMouse && CoupleGrabMode == 2)` (1), `Turn` et `GrabLastMouseX` (2), commentaire d'aide (2), format et arguments d'aide (4) : total 22. **Aucune ligne de B2p-C à I n'est retirée hors de ces 22**. Ajouts non annoncés : aucun (tous rattachés à J1 à J6 ; la part « clic hors du couple » et `Nearest` relève de J3).
Aucun fichier de code hors `LPCameraModeSubsystem.*` n'est dans le périmètre de J ; les autres `M`/`??` de `git status` (LPDebug, LPDirectControl, …) ne sont pas ce lot et n'ont pas été lus.
## 5. Verdict
| Point | Verdict |
|---|---|
| DIAG § 1, faits | **PASS** |
| DIAG § 1.1, cause | **estimée** (partiellement contredite, D2) ; autre cause possible : `HideCursorDuringCapture` vrai ; H3 muet avant J3 |
| J1 | **PASS-STATIC**, réserves D2, D4 |
| J2 | **FAIL** (D1 bloquant) |
| J3 | **PASS-STATIC** |
| J4 | **PASS-STATIC** |
| J5 | **PASS-STATIC**, réserves D3, D4 |
| J6 | **PASS-STATIC** |
| Transverse (warnings, includes, cohérence) | **PASS-STATIC** ; compilation **BLOCKED-compilation** (BUILD) |
| § 3 PC | **PASS-STATIC** (exécution : UE-GO) |
| **Global (première passe)** | **FAIL** : 1 défaut bloquant (**D1**). Non couvert : la compilation (à BUILD), tout comportement en jeu (V7). Les 5 autres défauts (D2 à D6) ne bloquent pas, mais D2 à D4 sont à corriger avec D1 pour que J1 et J5 tiennent leurs critères. **Mis à jour par la section « Reprise » : PASS-STATIC.** |
## 6. Points à confirmer par l'utilisateur (cases)
- [ ] D1 : reprise de l'IMPL (raw en pixels) avant BUILD. (faite, voir Reprise)
- [ ] D2 : élargir le déclencheur de J1 à `HideCursorDuringCapture()` vrai, ou accepter que J2 seul couvre ces états. (fait, voir Reprise)
- [ ] D3 et D4 : état d'entrée à rendre en fin de scène : mode, verrouillage et drapeau de curseur d'avant la scène (relevés à `BeginSexScene`), ou ne rien rendre. (fait, voir Reprise)
- [ ] D6 : suivre à V7 le témoin `molette=` de CAM-95 et la ligne `panneau bascule` pour attribuer le +22 cm.
- [ ] UE-GO pour le PC (§ 3 de l'IMPL), puis V5 et V6 par un validateur distinct.
- [ ] V7 (COMMUN-13) : CAM-91 à CAM-95 et CAM-86 à CAM-90 après BUILD.
Tours : 35 / 40
## Constats
### Défauts bloquants
- **D1. J2 lit des valeurs traitées, pas des pixels.** `LPCameraModeSubsystem.cpp:1593` : `Input->GetKeyValue(EKeys::MouseX/MouseY)`. `PlayerInput.cpp:2199-2204` + `1264` + `2337` : sensibilité appliquée ; `DefaultInput.ini:16-17` : `Sensitivity=0.070000`. Effet : deltas ≈ 14 fois trop petits (seuil de glisser ≈ 71 px de souris, rotation ≈ 0,02°/px), donc le filet de J2 est quasi inopérant là où le curseur reste figé. Correction minimale : `GetRawKeyValue` (`PlayerInput.h:760`) pour les deux axes ; corriger le libellé des lignes J3 (« deltas bruts » ne l'est qu'après ce changement). Rien d'autre à changer dans J2.
### Défauts non bloquants
- **D2. J1 ne couvre pas l'état observé après le premier relâché.** Journal 5478-5479 (12:38:16.701) : `CaptureDuringMouseDown` / `DoNotLock`, `HideCursorDuringCapture` resté vrai (5368 puis 5525). Les prises 2 et 3 (5481 à 5527) n'ont rien fait dans cet état, que `.cpp:1265` ignore. `SceneViewport.cpp:756-758, 780-783` : curseur caché et haute précision à l'appui quand ce drapeau est vrai. Proposition : déclencher aussi si `ViewportClient->HideCursorDuringCapture()` (`GameViewportClient.h:831`) est vrai.
- **D3. `EndSexScene` rend GameOnly alors que l'état d'avant la scène est `CaptureDuringMouseDown` / `DoNotLock`.** Dernier changement avant la scène du 12:35:21 : journal 2549-2550 (12:34:54.870) puis `Hide` vrai (3125). `.cpp:1342-1343` impose `CapturePermanently` (premier clic consommé, curseur verrouillé) si J1 a agi une fois, même si le jeu a lui-même replacé `CaptureDuringMouseDown` (cas observé à chaque premier relâché). Le critère de la fiche « hors du sexe, rien d'autre ne change » n'est tenu que si l'état rendu est celui d'avant la scène.
- **D4. `HideCursorDuringCapture` reste à faux après la scène.** `.cpp:1277` le met à faux ; `FInputModeGameOnly::ApplyInputMode` (`PlayerController.cpp:6439-6451`) ne le rétablit pas. Journal : vrai avant et pendant la plupart des scènes. À relever et rendre avec D3.
- **D5. IMPL hors gabarit** (§ 1) et imprécision « 12:41 à 12:43 » (en réalité dès 12:35:26) ; § 5 bis vide.
- **D6. Cause du +22 cm non établie.** § 3.1 : coïncidence avec une bascule du panneau dans la prise ; +25 avec `molette=-1.0` à 12:35:42. L'IMPL le dit « estimé » : correct ; la mention « seule voie connue » est trop affirmative.
### Notes (sans défaut)
- N1. `bAltKey` : Alt + Entrée (plein écran) ou Alt + Tab vifs (moins de 0,2 s, moins de 5 px) pourraient basculer le panneau pendant le sexe. À observer à V7.
- N2. `EndSexScene` met `MoveCamera` à faux si aucun bouton souris n'est tenu : un Alt tenu (action `RotateCamera`) à cet instant la remettra à vrai par le PC au besoin ; effet négligeable.
- N3. La ligne `clic hors du couple` s'écrit à chaque clic d'orbite hors couple (une fois par 2 s) : bruit attendu.
- N4. Les trois répartitions J2/J3 des hunks mixtes sont estimées (§ 4).
### Non mesuré
Compilation (`Build.ps1 -FromLog` sans objet : aucun build de ce lot), `LPTestStringCompat`, comportement en jeu (CAM-86 à CAM-95), noms globaux des en-têtes du moteur contre `/we4459`, rôle exact du jeu qui replace `CaptureDuringMouseDown` au premier relâché (aucun `SetInputMode` correspondant dans le dump du PC : seuls 1034, 1171 et 2734 existent), position réelle du curseur pendant les prises du journal (jamais écrite).
### Fichiers à commiter
A `Docs/Migration/etat/CAM/CAM-B2p-J-VAL.md` (ce livrable). Aucun autre fichier écrit dans le projet.
### À verser
`lp-validate § 1` ou skill CAM : `UPlayerInput::GetKeyValue(MouseX/MouseY)` rend une valeur traitée (sensibilité 0,07 du projet, FOV, lissage), `GetRawKeyValue` rend les pixels ; ne jamais s'en servir pour un déplacement en pixels. Et : le jeu repasse en `CaptureDuringMouseDown` au premier relâché après un GameOnly du PC (journal COMMUN-12), donc « rendre GameOnly » n'est pas un retour à l'état d'avant.
---
## Reprise (2026-10-14) : validation statique de la reprise, instantané `CAM-B2p-I4` vers final
### R1. Fiches amont relues
`CAM-B2p-J-IMPL.md` § 5 bis (l. 124-140), relu ; la première passe de cette VAL ; instantané `D:\Game\LifePlay_Backup_5.5\CAM-B2p-I4\Source\LifePlay\`. Mêmes règles : aucune compilation, aucune écriture hors de ce livrable.
### R2. Entrée et diff
Commande : `pwsh -NoProfile -File …\scratchpad\check2.ps1` (octets, MD5, `git diff --no-index --numstat` et `-U3`, Select-String `MouseCaptureBefore`).
| Fichier | Attendu | Obtenu | Verdict |
|---|---|---|---|
| `.cpp` base `I4` | `9D929102…`, 2197 lignes | MD5 `9D9291021BBB44BD8AD49448494FAB3A`, LF 2197, CR 0 | PASS (= entrée de la première passe) |
| `.h` base `I4` | `9D85465E…`, 203 lignes | MD5 `9D85465E284F626D00212FFAA331C4BB`, LF 203, CR 0 | PASS |
| `.cpp` final | `1F10631F904E0EAB74BAA896571F0A57`, 2220 lignes, LF | MD5 `1F10631F904E0EAB74BAA896571F0A57`, 98 554 octets, LF 2220, CR 0, BOM non | PASS |
| `.h` final | `9AEE8D04CF35C0C6EEDF226E3B0A86C3`, 206 lignes, LF | MD5 `9AEE8D04CF35C0C6EEDF226E3B0A86C3`, 11 242 octets, LF 206, CR 0, BOM non | PASS |
| Reste de `MouseCaptureBefore` | 0 | `Select-String` sur les deux fichiers : 0 ligne (grep d'homonymes : seules les 4 nouvelles variables `.h:184-187` et leurs usages) | PASS |
`numstat` : `.cpp` `44 21` (6 hunks), `.h` `5 2` (1 hunk). Recompte à la main des signes du `-U3` : `.cpp` +44 −21 (hunks : KeepMouseFree +6 −6 ; journal de J1 +2 −2 ; `BeginSexScene` +12 −1 ; `bButtonHeld` +2 −1 ; `EndSexScene` +20 −10 ; `RawDelta` +2 −1), `.h` +5 −2. Concordant. Répartition : D1 +2 −1 ; D2 +8 −8 ; D3/D4 +32 −11 ; Alt dans `bButtonHeld` +2 −1 ; total +44 −21. Aucun hunk hors de ces cinq objets : **aucun ajout non annoncé**, aucune ligne de J1 à J6 hors de ces zones n'est touchée.
### R3. Hunk par hunk
1. **D1** (`.cpp:1612-1613`, hunk 6) : `Input->GetRawKeyValue(EKeys::MouseX)` et `-Input->GetRawKeyValue(EKeys::MouseY)`. `GetRawKeyValue` : `PlayerInput.h:760`, `PlayerInput.cpp:2206-2211` (`KeyState->RawValue.X`). `RawValue` est copié de l'accumulateur à chaque `EvaluateKeyMapState` (`PlayerInput.cpp:1354-1361`), et `MouseX`/`MouseY` portent `UpdateAxisWithoutSamples` (`InputCoreTypes.cpp:535-536`) : la valeur retombe à 0 sans mouvement, en pixels (`SceneViewport.cpp:928-931`). Signe de Y inchangé et correct. Aucune sensibilité, aucun FOV, aucun lissage. **D1 levé.**
2. **D2** (hunks 1 et 2) : `bPermanent` (capture permanente) ou `bHiddenOnPress = ViewportClient->HideCursorDuringCapture()` (`GameViewportClient.h:831`, `const`, public) déclenche la libération ; l'ancien premier retour est conservé pour ce qui précède (viewport, `PlayerInput`, bouton tenu). `bMouseFreed = true` inconditionnel ; `MouseFreeCount` toujours incrémenté. Pas de boucle : après `FInputModeGameAndUI` avec `SetHideCursorDuringCapture(false)` (`PlayerController.cpp:6411`) et `CaptureDuringMouseDown` (`6412`), `bPermanent` et `bHiddenOnPress` sont faux à l'image suivante ; il ne se redéclenche que lorsque le jeu repose lui-même GameOnly ou `Hide` vrai (journal : à chaque fermeture du panneau, 5530, 5545). Format du journal : `%d` `%d` `%d` pour `Capture`, `bHiddenOnPress ? 1 : 0`, `MouseFreeCount` : 3/3. **D2 levé** (réserve N5).
3. **D3/D4, `BeginSexScene`** (hunk 3) : `const UGameInstance* GI`, `const UGameViewportClient* ViewportClient` ; `GetMouseCaptureMode() const` (`GameViewportClient.h:786`), `GetMouseLockMode() const` (`821`, définition `GameViewportClient.cpp:3495-3498`), `HideCursorDuringCapture() const` : tous appelables sur un pointeur `const`. Stockage en `uint8` (`EMouseCaptureMode : uint8`, `EMouseLockMode : uint8`, `EngineBaseTypes.h:40, 56`) et `bool`. Pas de viewport : `bSavedMouseState = false`, rien n'est rendu. Journal : 4 `%` (`%s %d %d %d`) pour `*MappingsAtSexStart`, 2 `static_cast<int32>`, `bSavedHideCursor ? 1 : 0` : 4/4. Relevé fait au front, c'est-à-dire l'état d'avant la scène observé à la première image (journal COMMUN-12 : `CaptureDuringMouseDown`, `DoNotLock`, `Hide` vrai avant la scène du 12:35:21, 2549-2550 puis 3125).
4. **D3/D4, `EndSexScene`** (hunks 5 et 4) : rendu seulement si `bMouseFreed && bSavedMouseState`, `PC`, hors menu, et **vue encore dans l'état posé par CAM** : `CaptureDuringMouseDown` **et** `DoNotLock` **et** `!HideCursorDuringCapture()` (plus strict qu'avant : un `Hide` vrai remis par le jeu empêche toute écriture). Ordre : `SetInputMode(GameOnly)` d'abord et seulement si la capture sauvée était permanente (`Consume` vrai seulement pour `CapturePermanently`, exact selon `PlayerController.cpp:6450`), puis `SetMouseCaptureMode`, `SetMouseLockMode`, `SetHideCursorDuringCapture` : ces trois méthodes ne font qu'écrire un champ avec journal `LogViewport` (`GameViewportClient.cpp:3458-3511`), sans opération Slate ; elles viennent donc après `ApplyInputMode` et ne sont pas écrasées par lui. `GameOnly` ne touchant pas `Hide` (`6439-6451`), le troisième réglage rend bien le drapeau (D4). Si l'état de départ n'est pas permanent (cas du journal), aucun `SetInputMode` : seulement les trois champs, qui valent déjà `CaptureDuringMouseDown`, `DoNotLock` et le `Hide` d'origine. `static_cast<EMouseCaptureMode>(uint8)` et `static_cast<EMouseLockMode>(uint8)` sont licites (enums de base `uint8`). Journal : `FString::Printf` `%d %d %d` / 3 arguments ; la ligne finale (`7/7`) n'est pas modifiée par le diff, `InputText` reste une `FString` affichée par `*InputText`. `bButtonHeld` compte `LeftAlt` et `RightAlt` (note N2 levée). **D3 et D4 levés.**
5. `MouseCaptureBefore` : retiré du `.h` et du `.cpp` (R2, 0 reste).
### R4. Homonymes C4456 / C4458 / C4459 des nouveaux noms
Nouveaux : `bPermanent`, `bHiddenOnPress` (locaux de `KeepMouseFree`), `GI` et `ViewportClient` (locaux de `BeginSexScene`, fonction sans autre local de ces noms), `CaptureBefore` (local d'un bloc de `EndSexScene`), membres `bSavedMouseState`, `SavedCaptureMode`, `SavedLockMode`, `bSavedHideCursor`.
- Membres de la classe (`.h:37-206`) : aucun homonyme des locaux ; les 4 nouveaux membres n'ont pas de local du même nom.
- Locaux englobants : `GI` et `ViewportClient` de `BeginSexScene` sont seuls dans la fonction ; dans `EndSexScene`, `CaptureBefore` n'est déclaré qu'une fois et `ViewportClient` (bloc interne, ancien) n'a pas d'homonyme englobant.
- Globaux et espaces anonymes de `Source\LifePlay` : Grep des sept noms dans tous les `.cpp` et `.h` : seules occurrences dans `LPCameraModeSubsystem.*` (`.h:184-187`, `.cpp:1267-1366`). Aucun homonyme global.
- Non vérifié : noms globaux des en-têtes du moteur (à BUILD).
Conversions : aucune nouvelle conversion rétrécissante (`uint8` depuis un enum de base `uint8`, `GetRawKeyValue` en `float`).
### R5. Défauts D1 à D6 : décision
| Défaut | État | Raison |
|---|---|---|
| D1 (bloquant) | **levé** | R3-1 |
| D2 | **levé** | R3-2, réserve N5 |
| D3 | **levé** | R3-3, R3-4 : l'état rendu est celui d'avant la scène, pas un GameOnly imposé |
| D4 | **levé** | R3-4 : `Hide` rendu ; cas résiduel N6 |
| D5 | **maintenu, non bloquant** | § 5 bis ne corrige que le fond de la DIAG ; il n'ajoute ni tête (`Fiche … — Niveau …`), ni « Écarts à la SPEC », ni « Points à confirmer en VAL », ni ligne `Tours` (relu l. 124-143), et ne restitue pas l'intervalle exact (`MoveCamera=1` dès 12:35:26) alors qu'il le nomme. À régler à la rédaction de clôture (DOC) |
| D6 | **levé** | § 5 bis déclare la cause du § 1.1 « estimée forte, non établie, aucune position au journal » et le +22 cm « non établi », coïncidant avec une bascule du panneau, tranché par le témoin `hors CAM` : c'est l'évaluation de cette VAL (« estimée » et « non étayée »). Le corps de la DIAG (§ 1.1 « Cause, établie » ; § 1.3 « seule voie connue ») n'a pas été réécrit : la correction est une note de lecture, jugée suffisante tant que le § 5 bis est cité avec la DIAG |
### R6. Notes nouvelles (sans défaut)
- N5. J1 s'applique maintenant dès que `Hide` est vrai hors bouton tenu : état par défaut du jeu (journal), donc au début de presque toute scène et à chaque retour de `Hide` à vrai. Le relevé `bMenuOnScreen` a jusqu'à 0,1 s de retard : à l'ouverture d'un menu, J1 peut poser une fois `GameAndUI` (sans focus) avant que `bMenuOnScreen` ne passe à vrai. Effet attendu faible ; `EndSexScene` ne rend rien si un menu est ouvert. À regarder à V7 (menu ouvert pendant la scène).
- N6. Rendu en fin de scène : `SetMouseLockMode` et `SetMouseCaptureMode` n'écrivent que le champ du viewport (pas d'opération Slate). Pour un état d'avant-scène non permanent mais verrouillé (`LockAlways`, `LockOnCapture` sans GameOnly), le verrou Slate levé par `ReleaseMouseLock` ne serait pas reposé. Cas absent du journal (état de départ `DoNotLock`) ; à surveiller si le menu principal laisse `LockAlways` (journal 914) à l'entrée d'une scène. Si le jeu replace lui-même GameOnly après la détection de fin de scène, `Hide` peut rester à faux (le rendu est alors sauté : `bStillFreed` faux) ; cas sans témoin.
- N1 (Alt + Entrée / Alt + Tab) inchangée. N2 levée (Alt compté).
### R7. Verdict global mis à jour
| Point | Verdict |
|---|---|
| DIAG § 1, faits | **PASS** (inchangé) ; cause § 1.1 : **estimée forte** (accord avec § 5 bis) |
| J1 | **PASS-STATIC** (D2, D4 levés) |
| J2 | **PASS-STATIC** (D1 levé) |
| J3, J4, J6 | **PASS-STATIC** (inchangés ; libellé « deltas bruts » désormais exact) |
| J5 | **PASS-STATIC** (D3, D4 levés ; N2 levée) |
| Transverse | **PASS-STATIC** ; compilation **BLOCKED-compilation** (à BUILD) |
| § 3 PC | **PASS-STATIC** (inchangé) |
| **Global** | **PASS-STATIC** : aucun défaut bloquant ouvert. Ouvert non bloquant : D5 (gabarit), notes N1, N5, N6. Non couvert : compilation (BUILD), tout comportement en jeu (V7) ; UE-GO du PC. |
### R8. Points à confirmer par l'utilisateur (cases)
- [ ] BUILD de `LPCameraModeSubsystem.cpp/.h` (MD5 `1F10631F…` / `9AEE8D04…`), puis V7 (COMMUN-13) : CAM-91 à CAM-95, CAM-86 à CAM-90 ; lire en plus la ligne `debut de scene … souris capture … verrou … curseur cache` et `fin de scene … mode d'entree rendue (…)`.
- [ ] V7 : menu ouvert pendant une scène (N5) ; fin de scène avec le panneau fermé juste avant.
- [ ] D5 : tête, « Écarts à la SPEC », « Points à confirmer » et `Tours` de l'IMPL à la clôture.
- [ ] UE-GO du PC (§ 3 de l'IMPL).
Tours (reprise) : 6 / 40 ; cumul avec la première passe : 41 (la reprise est une fiche distincte, budget de 40 respecté pour chacune).
### Fichiers à commiter (reprise)
A `Docs/Migration/etat/CAM/CAM-B2p-J-VAL.md` (ce livrable, réécrit en entier avec la section Reprise). Aucun autre fichier écrit dans le projet.