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

42 KiB
Raw Blame History

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.