183 lines
33 KiB
Markdown
183 lines
33 KiB
Markdown
|
|
Fiche CAM-B2p-G-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-G-VAL : validation statique de la partie C++ (recadrage, suivi du couple, C3, témoin G3) (2026-10-14)
|
|||
|
|
|
|||
|
|
Portée : VAL **statique** (aucune compilation, aucun éditeur, aucun commandlet, aucun MCP, aucune écriture git).
|
|||
|
|
Copie `D:\Game\LifePlay-master 5.8`, branche `ue5.8`, UE 5.8 (BuildSettingsVersion V7 : `/we4456 /we4458 /we4459`).
|
|||
|
|
Les fichiers `Source/LifePlay/LPCameraModeSubsystem.cpp` et `.h` sont **non suivis** par git (`git status --short` : `??`) : il n'existe donc
|
|||
|
|
pas de tag `CAM-B2p-G-avant` ; le diff est pris contre l'instantané `D:\Game\LifePlay_Backup_5.5\CAM-B2p-G\` par `git diff --no-index`.
|
|||
|
|
**Deux passes** : la VAL initiale (sections 1 à 6, `.cpp` `6D88EDDA…`) et la **Reprise** (section finale, `.cpp` `A5F041FF…`) qui revérifie la correction de N1 et N2. Le verdict final est celui de la Reprise.
|
|||
|
|
|
|||
|
|
## 1. Fiches amont relues
|
|||
|
|
- `Docs/Migration/etat/CAM/CAM-B2p-G-SPEC.md` (92 lignes, lue en entier).
|
|||
|
|
- `Docs/Migration/etat/CAM/CAM-B2p-G-IMPL.md` (72 lignes, lue en entier ; seule IMPL du lot, partie C++). La correction de N1 et N2 n'a pas de livrable IMPL propre : elle est vérifiée dans le fichier source (Reprise).
|
|||
|
|
- Amont de la base : `Docs/Migration/etat/ST/STG/STG-COMMUN11-BUILD.md` (lignes 21, 30, 134, 163-164 : le build COMMUN-11 a compilé
|
|||
|
|
`LPCameraModeSubsystem.cpp` `03D85D28…` sans avertissement et contient la chaîne `Show couple : place reprise`).
|
|||
|
|
- Lu en lecture seule : `Source/LifePlay/LPShowCoupleSubsystem.cpp` (l. 285-393, 750-824), `LPShowCoupleSubsystem.h`.
|
|||
|
|
- Gabarit VAL lu : `D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md`.
|
|||
|
|
|
|||
|
|
## 2. Points à confirmer des IMPL (IMPL § 4, un par ligne)
|
|||
|
|
| # | Point de l'IMPL | Verdict | Preuve |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| P1 | Lignes « vue recadree » à l'entrée en Scene et en C3 | PASS-STATIC (code) ; en jeu : reporté V7 | `.cpp` l. 841-850 (deux appels), ligne de journal en fin de `RecenterSceneCamera` ; format 6 `%` pour 6 arguments |
|
|||
|
|
| P2 | Flèches et molette : la caméra suit le couple | PASS-STATIC (code) ; en jeu : reporté V7 (CAM-79) | `.cpp` l. 851-858 (avant reprise) ; appel avant la mise à jour de `LastCouplePivot` (l. 766-767, 828-829) |
|
|||
|
|
| P3 | Distance de 900 cm au plus | PASS-STATIC (code) ; en jeu : reporté V7 | `.cpp` constante `FollowMaxDistance`, bloc de suivi |
|
|||
|
|
| P4 | Ligne « G3 NotifyCoupleTeleported appele » à chaque pose qui déplace le meneur | PASS-STATIC (code) ; en jeu : reporté V7 | bloc « relâcher » de `TickCoupleMouse` ; une ligne par appel, condition `> 1.0` inchangée |
|
|||
|
|
| P5 | Compilation | **BLOCKED-compilation** (VAL statique : aucune compilation autorisée). Relecture C++ UE 5.8 sans défaut trouvé (§ 3, contrôles 3 et 4, puis Reprise) | à confier à BUILD |
|
|||
|
|
| P6 | Essais CAM-76 à CAM-80 | reporté (V7, en jeu) | aucune exécution possible ici |
|
|||
|
|
|
|||
|
|
## 3. Critères rejoués (commande → résultat) — VAL initiale
|
|||
|
|
| # | Contrôle | Commande / lecture | Résultat brut | Verdict |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| 1a | MD5 | `md5sum` des quatre fichiers | `.cpp` `6d88edda0033e12d6891821d47f1267f` ; `.h` `00d439937dbceab44f459c4a834f34f0` ; instantané `.cpp` `03d85d282a6306e6d2494d7dc2c253fc`, `.h` `4ae6599447750371cff7e0aac74eb93e` | PASS : égalent l'IMPL (état d'avant la Reprise) |
|
|||
|
|
| 1b | Lignes | `wc -l` | `.cpp` 1570 (instantané 1437) ; `.h` 141 (instantané 132) | PASS (1437+133 = 1570 ; 132+9 = 141) |
|
|||
|
|
| 1c | CR | `tr -cd '\r' < f \| wc -c` (sans `grep -c`) sur les quatre fichiers | 0, 0, 0, 0 ; premiers octets `2f 2f 20` (pas de BOM), dernier octet `0a` | PASS : LF, sans BOM, fin de ligne finale |
|
|||
|
|
| 2a | Diff compte | `git diff --no-index --numstat` | `.cpp` : `136 3` ; `.h` : `9 0` | PASS : 136 = 10 (constantes) + 11 (fin de sexe) + 1 + 1 (deux appels) + 100 (deux fonctions et ligne vide) + 8 (témoin G3) + 5 (étiquette) ; 3 retraits = 2 lignes `if` de G3 + 1 ligne `return` de l'étiquette |
|
|||
|
|
| 2b | Périmètre | diff lu en entier (6 hunks `.cpp`, 1 hunk `.h`) | uniquement : constantes B2p-G ; `TickCoupleBounds` (remise à zéro hors sexe, deux appels) ; `TickSceneCamera` et `RecenterSceneCamera` ; bloc « relâcher » de `TickCoupleMouse` ; `ComputeLabel` ; membres du `.h` | PASS : aucun ajout non demandé |
|
|||
|
|
| 2c | `const AActor* View` existants | diff + `Grep` des déclarations de `View` | déclarations aux l. 611, 1317, 1458 (`const AActor*`) et 989 (`AActor*`) : aucune ligne de diff n'y touche ; les nouvelles `AActor* View` (l. 837) sont dans `TickSceneCamera` seulement | PASS |
|
|||
|
|
| 3 | Relecture C++ UE 5.8 | `Grep` dans `D:\Game\Launcher\Engine\UE_5.8\Engine\Source` (détail ci-dessous) | aucune erreur de type, de const ni de format trouvée | PASS-STATIC |
|
|||
|
|
| 4 | Ombrage C4456/C4457/C4458/C4459 | lecture de chaque portée + `Grep` des membres et des noms globaux (détail ci-dessous) | aucun ombrage trouvé | PASS-STATIC |
|
|||
|
|
| 5 | Logique (a) à (g) | lecture (détail ci-dessous) | conforme, trois réserves non bloquantes (N1 à N3) | PASS-STATIC |
|
|||
|
|
| 6 | G3 | lecture de `LPShowCoupleSubsystem.cpp` | voir § « G3 » ci-dessous | PASS-STATIC |
|
|||
|
|
| 7 | Cohérence SPEC/IMPL | lecture croisée | voir § « SPEC / IMPL » ci-dessous | PASS avec écarts acceptables |
|
|||
|
|
|
|||
|
|
### Contrôle 3, API UE 5.8 (sources lues dans `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime`)
|
|||
|
|
- `AActor::AddActorWorldOffset(FVector, bool=false, FHitResult*=nullptr, ETeleportType=None)` : `Engine/Classes/GameFramework/Actor.h:1756`. Appel conforme.
|
|||
|
|
- `SetActorLocationAndRotation(FVector, FRotator, …)` et surcharge `FQuat` : `Actor.h:1701-1702` ; `(Target - Desired).Rotation()` rend `FRotator` (`Core/Public/Math/Vector.h:832`) : pas d'ambiguïté. `SetActorLocation(const FVector&, …)` : `Actor.h:1636`.
|
|||
|
|
- `FVector::Normalize(T Tolerance=UE_SMALL_NUMBER)` rend `bool` : `Vector.h:630`. `FVector::ForwardVector` déjà utilisé dans le module (`LPSecondScreenSubsystem.cpp:101`).
|
|||
|
|
- `FVector(0.0, 0.0, float)` : constructeur non gabarit `TVector(T InX, T InY, T InZ)` (`Vector.h:168`), conversion `float` vers `double` implicite. `Flat * float` : `operator*(FArg)` arithmétique (`Vector.h:311-312`) ; `-Couple[0]->GetActorForwardVector()` : moins unaire sur le résultat de l'appel.
|
|||
|
|
- `TWeakObjectPtr<AActor>` : `LastSexView = View` (AActor*) même forme que `GrabCamera = View` (déjà compilé) ; `LastSexView.Get() != SceneCamera` compare `AActor*` à `AActor*` ; `View == SceneCamera` : deux `AActor*` non const. Dans `ComputeLabel` les comparaisons existantes sur `const` restent inchangées.
|
|||
|
|
- `FCollisionQueryParams(FName, bool, const AActor*)`, `SCENE_QUERY_STAT` (nom unique `LPRecenter`, une seule occurrence dans `Source`), `AddIgnoredActors(const TArray<AActor*>&)`, `AddIgnoredActor(const AActor*)` : `Engine/Public/CollisionQueryParams.h:39, 231, 243, 257`. Même forme que le code déjà compilé de `TickCoupleMouse`. `Cast<AActor>(ReadObject(...))` sans déréférence nulle.
|
|||
|
|
- `LineTraceSingleByChannel` sur `const UWorld*` : même usage que dans `TickCoupleMouse` (`World` const, déjà compilé). `FHitResult()` appelle `Init()` qui fait `Memzero` (`Engine/Classes/Engine/HitResult.h:148-176`) : `Hit.bBlockingHit` vaut faux après une trace sans impact.
|
|||
|
|
- `FMath::Clamp` : tous les arguments sont `double` (`Desired.X/Y/Z` double, `Area.Min.X + Margin` double, `Interior.FloorTop + 30.0` double, `FMath::Max(double, double)`). `FMath::Min(LowestRoot, cond ? double : double)`. Bornes du `Clamp` Z : minimum `FloorTop+30`, maximum `Max(FloorTop+30, Ceiling-20)` donc minimum inférieur ou égal au maximum, y compris plafond bas.
|
|||
|
|
- Formats `UE_LOG` :
|
|||
|
|
- recadrage : `%s`, `%.0f`, `%.0f`, `%.0f`, `%.0f`, `%s` = 6 ; arguments `Reason`, `FVector::Dist` (double), `Target.X/Y/Z` (double), ternaire `TEXT(…) : TEXT("")` = 6. `%s` reçoit un `const TCHAR*` (les deux littéraux de tailles différentes se réduisent en pointeur dans le ternaire, usage courant) ; `Hit.bBlockingHit` (champ de bits) en condition : valide.
|
|||
|
|
- G3 : `%s`, `%.0f` = 2 ; arguments : ternaire de `TEXT` et `LeadMoved` (double) = 2. Condition du ternaire : pointeur `ShowCouple`, conversion contextuelle en `bool`.
|
|||
|
|
- Const-correctness : `ReadBool(const UObject*, FName)` reçoit `const APlayerController* PC` et `APlayerController* EndPC` ; `WriteBool(UObject*, …)` reçoit `EndPC` non const. `ReadObject(const UObject*, …)` reçoit `AActor* Room` et `UGameInstance* GI`. `ComputeRoomInterior(const AActor*, …)` reçoit `AActor* Room`. `GetFirstPlayerController()` et `GetViewTarget()` appelables sur `const` (mêmes formes que dans le code déjà compilé).
|
|||
|
|
- Qualification : tous les appels aux fonctions et constantes de l'espace de noms sont préfixés `LPCameraModeLocal::` (relu ligne à ligne) ; `PCSexC3`, `RecenterDistance`, `RecenterHeight`, `RecenterTargetAboveRoot`, `RecenterWallMargin`, `FollowMaxDistance`, `FollowMinStep` définies dans l'espace de noms.
|
|||
|
|
- Signatures : `.h` l. 127 et 129 identiques aux définitions `.cpp`. Aucun paramètre ni variable inutilisé. `GI` n'est jamais nul dans `TickSceneCamera` (retour anticipé si `!GI`).
|
|||
|
|
|
|||
|
|
### Contrôle 4, ombrage de tout le fichier
|
|||
|
|
Méthode : lecture de chaque fonction touchée, plus `Grep` (a) des membres du `.h` (l. 49-140), (b) des déclarations globales et des espaces de noms anonymes de `Source/LifePlay` (espaces de noms nommés ou anonymes dans 25 fichiers ; aucune déclaration de portée espace de noms nommée comme un des nouveaux noms ; les occurrences de `Desired`, `Hit`, `Query`, `World`, `PC`, `View`, `Mesh`, `Character`, `Person`, `Reason` hors module sont des locales de fonction ou un membre de struct : `LPBonusRewardSubsystem.h:79`, `LPSaveRelaunch.cpp:369`, etc.).
|
|||
|
|
| Nouveau nom | Portée | Conflit |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `EndWorld`, `EndPC` | bloc de retour anticipé de `TickCoupleBounds` | aucun : pas d'homonyme dans la fonction (`World` et `PC` des l. 748-749 sont dans un bloc `if` sans lien) |
|
|||
|
|
| `World`, `PC`, `View`, `SceneCamera`, `bC3`, `bOnScene`, `Moved`, `Away` (`TickSceneCamera`) | corps de fonction ; `Moved` et `Away` dans le bloc `else if` | paramètres `GI`, `Room`, `Couple`, `Pivot`, `bGrabEnded` : aucun homonyme (C4457 exclu) ; membres `LastSexView`, `bSexC3Was`, `LastCouplePivot`, `bHasLastPivot`, `CoupleGrabMode` : aucun homonyme local (C4458 exclu) |
|
|||
|
|
| `World`, `LowestRoot`, `Person`, `Character`, `Mesh`, `Target`, `Flat`, `Desired`, `Interior`, `Area`, `Margin`, `Query`, `Wall`, `CamRoom`, `Hit` (`RecenterSceneCamera`) | `Person`, `Character`, `Mesh` dans la boucle `for` ; `Area` et `Margin` dans le bloc `if` ; `Wall`, `CamRoom` en initialisation de `if` | paramètres `GI`, `Room`, `SceneCamera`, `Couple`, `Pivot`, `Reason` : aucun homonyme (C4457 exclu) ; aucun nom répété dans une portée englobante (C4456 exclu) ; aucun membre homonyme (C4458 exclu) |
|
|||
|
|
| `LeadMoved`, `ShowCouple` (`TickCoupleMouse`) | bloc `!bHeld`, bloc `if (LeadMoved > 1.0)` | `ShowCouple` existait déjà en initialisation de `if` (baseline) : aucun autre `ShowCouple` ni `LeadMoved` dans la fonction (paramètres `GI`, `PC`, `View`, `Room`, `Couple`, `Pivot`) |
|
|||
|
|
| C4459 (globaux visibles en unité unifiée) | — | aucun nom nouveau de portée globale ; les constantes ont le préfixe d'espace de noms nommé ; seul global proche : `CVarShowCouple` (`LPShowCoupleSubsystem.cpp:28`), nom différent de `ShowCouple` |
|
|||
|
|
La baseline `03D85D28…` a été compilée sans avertissement (`STG-COMMUN11-BUILD.md:30`) ; les noms déjà présents (`Margin`, `Area`, `Interior`, `LowestRoot`, `Moved`, `Query`, `Hit`, `Wall`, `CamRoom`, `Target`, `SceneCamera`, `bC3`, `World`, `PC`, `View`) restent sans conflit global ; seuls `EndWorld`, `EndPC`, `bOnScene`, `Away`, `Flat`, `Desired`, `LeadMoved`, `Reason` étaient nouveaux et ne figurent dans aucune portée globale (`Grep` ci-dessus). `Axis` et `Reach`, ajoutés à la Reprise, sont traités dans la Reprise.
|
|||
|
|
|
|||
|
|
### Contrôle 5, logique
|
|||
|
|
- (a) G-b : `LastSexView` remis à zéro hors sexe ; en sexe, `bOnScene && LastSexView.Get() != SceneCamera` déclenche un recadrage une fois, puis `LastSexView = View` ; plus de recadrage tant que la vue reste `SceneCamera`. Retour depuis le dernier POV : la vue change donc nouvelle entrée, nouveau recadrage. PASS-STATIC.
|
|||
|
|
- (b) G-d : `else if (bOnScene && bC3 && !bSexC3Was)` ; `bSexC3Was = bC3` chaque image. Sans variable `SexC3` dans le PC, `ReadBool` rend faux (`FindPropertyByName` nul) : `bC3` faux, rien ne change ; l'étiquette reste « Camera: Scene ». Priorité du POV conservée dans `ComputeLabel`. PASS-STATIC.
|
|||
|
|
- (c) G-c : condition `bOnScene && bHasLastPivot && CoupleGrabMode == 0 && !bGrabEnded` ; vecteur `Pivot - LastCouplePivot` ; appel avant `LastCouplePivot = Pivot` (pièce cachée et chemin normal) ; `Pivot` déjà corrigé par E1 (`Pivot += Correction` précède l'appel du chemin normal). La distance de 900 cm est évaluée à chaque image du bloc. Enchaînement exclusif en `else if` : une image recadrée ne suit pas. L'ordre de tick est établi : `TickSwitchKey` (intervalle 0) appelle `TickSexCamera` (qui contient `TickCoupleMouse`, donc `bGrabJustEnded` et `CoupleGrabMode` de la même image) puis `TickCoupleBounds`. PASS-STATIC.
|
|||
|
|
- (d) `RecenterSceneCamera` : garde `!World || Couple.Num() == 0` ; bornes F1 seulement si `!Room->IsHidden() && ComputeRoomInterior` ; bornes XY seulement si la pièce fait plus de deux marges ; clamp Z toujours cohérent ; tracé Visibility cible vers caméra avec couple, `SceneCamera`, `Wall` et `CamRoom` ignorés ; rapprochement devant l'obstacle (version corrigée : Reprise) ; rotation vers la cible. PASS-STATIC.
|
|||
|
|
- (e) Hors sexe : `PC.SexC3` remis à faux s'il était vrai ; `bSexC3Was` et `LastSexView` remis à zéro. `WriteBool` ne fait rien si la propriété est absente. PASS-STATIC.
|
|||
|
|
- (f) Témoin G3 : `LeadMoved` calculé une fois, condition `> 1.0` identique à l'ancienne ; une ligne par appel avec le texte `appele` ou `non appele, sous-systeme absent` ; l'appel `NotifyCoupleTeleported` reste conditionné par `ShowCouple` non nul. PASS-STATIC.
|
|||
|
|
- (g) Déréférences : `GI` non nul ; `Room` valide ; `PC` testé avant `GetViewTarget` ; `SceneCamera` utilisé seulement si `bOnScene` (donc valide et non nul) ; `Couple[0]` après `Couple.Num() == 0` ; `Mesh` testé avant usage ; `ReadBool(PC, …)` accepte `PC` nul. PASS-STATIC.
|
|||
|
|
|
|||
|
|
### G3 (contrôle 6)
|
|||
|
|
- `KeepTogether` (`LPShowCoupleSubsystem.cpp:750-805`) écrit `LastSafe = Who->GetActorLocation();` en **l. 804**, à la fin de chaque passage qui va au bout. Deux retours anticipés existent : l. 756-758 (meneur, partenaire, volume ou monde absent) et l. 762-765 (`Delta.IsNearlyZero()`, auquel cas `LastSafe` égale déjà `Now` à la tolérance de `IsNearlyZero`). La formulation de l'IMPL « se termine à chaque image » est donc exacte à ce détail près ; le résultat est le même : `LastSafe` suit la place du meneur à chaque image où la scène est engagée. `KeepTogether` est appelée l. 388 (par `AfterActors`, branché sur `FWorldDelegates::OnWorldPostActorTick`, l. 275-285).
|
|||
|
|
- `NotifyCoupleTeleported` (l. 807-824) : retour sans effet si `!bEngaged || !Who || !Partner || !GetWorld()` (l. 812-814) ; `Show couple : place reprise apres teleportation` n'est écrite (l. 819-820) **que si** `!(Now - LastSafe).IsNearlyZero()` (l. 817) ; l. 822-823 : `LastSafe = Now` et `LastBlocker.Reset()` toujours.
|
|||
|
|
- Ordre dans l'image (établi, `Launch/Private/LaunchEngineLoop.cpp` 5.8) : `GEngine->Tick` (l. 5859) précède `FTSTicker::GetCoreTicker().Tick` (l. 6103). `OnWorldPostActorTick` (donc `KeepTogether`) s'exécute dans le tick du monde ; les tickers de `LPCameraModeSubsystem` (`FTSTicker`, l. 527-529) s'exécutent après. Au relâcher, le meneur n'a donc pas bougé depuis le dernier passage de `KeepTogether`, qui avait déjà remis `LastSafe` à sa place : `Now − LastSafe` est nul et **aucune ligne** n'est écrite.
|
|||
|
|
- Conclusion sur COMMUN-11 : **l'absence de ligne « place reprise » au journal COMMUN-11 est attendue et ne prouve pas que G3 manque**. Le build COMMUN-11 contient G3 (établi : `STG-COMMUN11-BUILD.md:21` compile `LPShowCoupleSubsystem.cpp` et `LPCameraModeSubsystem.cpp` ; l. 134 la chaîne `Show couple : place reprise` passe de 0 à 1 ; l. 163 la CAM `03D85D28…`, état B2p-F + G3). Le critère d'essai de `COMMUN-11-TESTS.md:38` (« place reprise après chaque pose qui déplace le couple ») suppose à tort que la ligne paraît à chaque pose ; elle ne paraît que si `KeepTogether` a laissé `LastSafe` en arrière (pas bloqué ou ramené d'un pas). Le nouveau témoin (P4) tranchera en jeu que l'appel part. Niveau de preuve : ordre des tickers et résultat **établis par lecture** ; comportement observé en jeu **non rejoué**.
|
|||
|
|
|
|||
|
|
### SPEC / IMPL (contrôle 7)
|
|||
|
|
- Conforme à la SPEC : G-a (cible, distance 300, hauteur 120, azimut gardé, F1 à 20 cm, plafond, tracé Visibility, regard, ligne de journal), G-b, G-c (même vecteur, figé pendant le glisser, 900 cm), G-d côté C++ (front montant, étiquette « Camera: C3 Couple », remise à faux hors sexe), G-e (Room non touché : aucun asset ni Blueprint dans le périmètre).
|
|||
|
|
- Écarts acceptables, tous annoncés par l'IMPL : partie PC de G-d (variable `SexC3` et deux branches) laissée à MCP sous UE-GO ; le recadrage d'entrée en Scene remplace le cadrage `pickPreset` à chaque retour de POV (voulu, SPEC G-b) ; témoin G3 côté appelant (demande de Big Boss, hors SPEC mais cité et sans effet de comportement) ; la ligne de journal du recadrage est plus riche que la ligne minimale de la SPEC (ajout de distance, cible, obstacle : acceptable).
|
|||
|
|
- Écart de gabarit (non bloquant) : voir Constats C1 et C2.
|
|||
|
|
|
|||
|
|
## 4. Diff lu en entier
|
|||
|
|
| Fichier | Instantané → actuel | Hunks | Compte brut | Lecture |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| `Source/LifePlay/LPCameraModeSubsystem.cpp` | 1437 → 1570 lignes (avant Reprise) ; 1437 → 1575 (Reprise) | 6 : constantes ; fin de sexe ; deux appels ; `TickSceneCamera` et `RecenterSceneCamera` ; G3 ; `ComputeLabel` | `136 3` (avant Reprise) ; `141 3` (Reprise) | entier, deux fois |
|
|||
|
|
| `Source/LifePlay/LPCameraModeSubsystem.h` | 132 → 141 lignes | 1 : deux déclarations et deux membres (l. 125-133) | `9 0` (inchangé) | entier |
|
|||
|
|
Aucun autre fichier du dossier de l'instantané (`manifest.csv` plus les deux fichiers). `LPShowCoupleSubsystem.cpp/.h` (`M` dans `git status`, travail STE) : lus seulement, rien écrit par ce lot (non prouvable par instantané, qui ne les contient pas ; l'IMPL § 2 annonce « rien écrit »).
|
|||
|
|
|
|||
|
|
## 5. Verdict
|
|||
|
|
**VAL initiale : PASS-STATIC**, aucun défaut bloquant. Ce verdict ne couvre ni la compilation (P5, BLOCKED-compilation, à confier à BUILD), ni le comportement en jeu (P1 à P4 et P6, reportés à V7). **Verdict final après la Reprise : PASS-STATIC** (N1 et N2 levés ; voir la section Reprise pour une réserve nouvelle, N7, non bloquante).
|
|||
|
|
|
|||
|
|
## 6. Points à confirmer par l'utilisateur (cases)
|
|||
|
|
- [ ] BUILD : compilation de `LifePlay` (module touché doit figurer dans les unités compilées) avec 0 erreur et 0 avertissement nouveau sous V7 (P5).
|
|||
|
|
- [x] N1 : levé à la Reprise (seuil 0,01 cm). Reste à confirmer en jeu que la caméra ne tremble pas.
|
|||
|
|
- [x] N2 : levé à la Reprise (distance minimale de 50 cm). Compromis noté (N2b).
|
|||
|
|
- [ ] N3 : accepter qu'un recadrage déclenché pendant une prise soit défait par la caméra figée, ou demander de le différer.
|
|||
|
|
- [ ] N7 : décider du renommage de `Reach` (voir Reprise), ou laisser BUILD trancher.
|
|||
|
|
- [ ] Décision sur le critère `COMMUN-11-TESTS.md:38` (« place reprise après chaque pose ») à reformuler à la lumière du G3 ci-dessus.
|
|||
|
|
- [ ] Essais en jeu CAM-76 à CAM-80 et lignes « vue recadree » / « G3 NotifyCoupleTeleported » (V7), après la partie PC de G-d.
|
|||
|
|
|
|||
|
|
Tours : 29 / 40 (VAL initiale 22, Reprise 7)
|
|||
|
|
|
|||
|
|
## Constats
|
|||
|
|
### Défauts bloquants
|
|||
|
|
Aucun (preuves : contrôles 1 à 7 ci-dessus et Reprise).
|
|||
|
|
|
|||
|
|
### Défauts non bloquants
|
|||
|
|
| # | Ligne (VAL initiale) | Constat | Niveau de preuve | État |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| N1 | `.cpp` 855 | Le suivi ignorait tout pas de pivot de 0,5 cm ou moins par image, et `LastCouplePivot` est réécrit à chaque image : un mouvement lent n'était jamais suivi. | estimé | **levé à la Reprise** |
|
|||
|
|
| N2 | `.cpp` 926 | Si le tracé touchait à moins de 20 cm de la cible, la caméra passait de l'autre côté de la cible. | estimé | **levé à la Reprise** (N2b : compromis) |
|
|||
|
|
| N3 | `.cpp` 844-850, 866 ; 1304-1306 | Un recadrage déclenché pendant une prise (touche C avec le bouton de souris tenu, ou entrée en Scene en pleine prise) est défait à l'image suivante par `TickCoupleMouse` qui remet la pose de prise (`GrabCameraLocation`) ; `bSexC3Was` est déjà à vrai, donc pas de nouvelle tentative, alors que la ligne « vue recadree » a été écrite. De même, un front montant de `SexC3` pendant lequel la vue n'est pas `SceneCamera` est perdu. | estimé (lecture, non rejoué) | ouvert |
|
|||
|
|
| N4 | `.cpp` 719-726 | Hors sexe, la lecture par réflexion de `PC.SexC3` s'exécute à chaque image ; même ordre de coût que `ReadBool(GI, SexOn)`. Sans importance mesurable attendue. | estimé | ouvert, négligeable |
|
|||
|
|
| N5 | `.cpp` 859-864 | La borne de 900 cm n'est évaluée que hors prise ; une prise suivie d'un relâcher peut laisser la caméra au-delà de 900 cm pendant la prise, puis la ramener d'un saut. La pièce fait 7,25 × 7 m (SPEC § 1) : cas pratiquement impossible. | estimé | ouvert, négligeable |
|
|||
|
|
| N6 | `.cpp` 840-841 | Si le jeu change la vue avec un fondu (`SetViewTargetWithBlend` à durée non nulle), `GetViewTarget()` ne rend la nouvelle cible qu'en fin de fondu et le recadrage d'entrée se fait alors ; comportement de Room non relu. | incertain (essai : CAM-78) | ouvert |
|
|||
|
|
| N7 | `.cpp` 930 (Reprise) | Nouveau nom local `Reach` homonyme de `const float Reach` dans l'espace de noms anonyme de `LPDirectControlSubsystem.cpp:58` : C4459 possible (erreur sous `/we4459`) **si** ce fichier précédait `LPCameraModeSubsystem.cpp` dans une unité unifiée. Voir Reprise. | estimé (ordre alphabétique constaté dans `Module.LifePlay.cpp`) | ouvert, non bloquant |
|
|||
|
|
|
|||
|
|
### Écarts de gabarit des livrables amont (écart = section absente)
|
|||
|
|
- **C1, IMPL** : le gabarit IMPL exige la tête commune (`Fiche … — Niveau … — Modèle prévu … | utilisé … — Agent … — Skills …`), absente (la ligne 1 est le titre) ; les sections ne suivent pas l'ordre 1 Fichiers modifiés (chemins, lignes), 2 Écarts à la SPEC, 3 Instantané, 4 Ce qui n'a pas été fait, 5 Points à confirmer en VAL, 6 Fichiers à commiter : l'IMPL a « 1. Écritures », « 2. G3 », « 3. Écarts », « 4. Points à confirmer en V7 », « 5. Fichiers à commiter » ; l'instantané n'est pas une section (sans manifeste cité ; le dossier contient `manifest.csv`), « Ce qui n'a pas été fait » est fondu dans § 3 ; la ligne `Tours :` est absente ; le dépôt n'est pas précisé dans « Fichiers à commiter ». Non bloquant ; le contenu est exact (MD5, lignes, périmètre recomptés). La correction N1/N2 n'est pas consignée dans l'IMPL (MD5 annoncé de l'IMPL : `6D88EDDA…`, périmé) : à mettre à jour.
|
|||
|
|
- **C2, SPEC** : le gabarit SPEC exige 1 Objectif mesurable, 2 Fichiers touchés et ordre, 3 Repli, 4 Risques R# et questions Q#, 5 Interdits, 6 Valeurs moteur lues (`chemin:ligne`), 7 Critères de VAL (commande → attendu), plus la tête commune. La SPEC a DIAG, Conception, Estimation, Risques : sans objectif mesurable, sans interdits, sans valeurs moteur, sans critères de VAL. Les critères de cette VAL viennent donc de la consigne de l'orchestrateur. Non bloquant.
|
|||
|
|
- **C3, IMPL** : § 2 de l'IMPL écrit « KeepTogether … se termine à chaque image par LastSafe » : voir le détail des retours anticipés (G3 ci-dessus). Imprécision, sans conséquence sur la conclusion.
|
|||
|
|
|
|||
|
|
### Non mesuré
|
|||
|
|
Compilation et avertissements ; exécution du moindre essai en jeu ; journal de la session COMMUN-11 relu (seul le rapport de build a été lu) ; vitesse des flèches et de la molette ; comportement du Blueprint Room au changement de vue (N6) ; vérification de l'affirmation du commentaire « l'animation ne bouge pas le pivot » (donc absence de tremblement avec le seuil de 0,01 cm) ; `LPTestStringCompat` et porte G-U3 (sans objet : aucun changement de chaîne, de configuration ni de compilation dans ce lot).
|
|||
|
|
|
|||
|
|
### Fichiers à commiter (VAL)
|
|||
|
|
A `Docs/Migration/etat/CAM/CAM-B2p-G-VAL.md` (dépôt du jeu, branche `ue5.8`). Fichiers modifiés par la VAL : ce livrable seulement.
|
|||
|
|
|
|||
|
|
### À verser
|
|||
|
|
`lp-validate` § 1 : un fichier source non suivi par git n'a pas de tag `<lot>-avant` ; comparer à l'instantané par `git diff --no-index --numstat` (code 1 = différences). `lp-build`/`lp-ue-compat` : ordre de tick établi en 5.8 — `GEngine->Tick` (LaunchEngineLoop.cpp:5859) avant `FTSTicker::GetCoreTicker().Tick` (l. 6103), donc `OnWorldPostActorTick` précède les tickers `FTSTicker` des sous-systèmes. `lp-validate` § 4 (C4459) : l'unité unifiée est lisible dans `Intermediate/Build/Win64/x64/UnrealEditor/Development/LifePlay/Module.LifePlay.cpp` (ordre alphabétique insensible à la casse des `#include` ; `LPCameraModeSubsystem.cpp` en est absent quand il est modifié, compilé seul `[NoUba]`) ; vérifier l'ordre avant de conclure sur un nom homonyme d'un espace de noms anonyme d'un autre fichier du module.
|
|||
|
|
|
|||
|
|
## Reprise (2026-10-14) : correction de N1 et N2
|
|||
|
|
Statique, aucune écriture hors ce livrable. Entrée : message de l'orchestrateur (`.cpp` annoncé `A5F041FFA8562F9E75560EAD01725E2B`, 1575 lignes ; `.h` inchangé `00D43993…`).
|
|||
|
|
|
|||
|
|
### R1. Contrôles rejoués
|
|||
|
|
| Contrôle | Commande | Résultat brut | Verdict |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| MD5 | `md5sum` | `.cpp` `a5f041ffa8562f9e75560ead01725e2b` ; `.h` `00d439937dbceab44f459c4a834f34f0` | PASS : égalent l'annonce |
|
|||
|
|
| Lignes | `wc -l` | `.cpp` 1575 ; `.h` 141 | PASS (1570 + 5) |
|
|||
|
|
| CR | `tr -cd '\r' < f \| wc -c` | 0 et 0 ; premiers octets `2f 2f 20`, derniers `7d 0a` | PASS : LF, sans BOM |
|
|||
|
|
| Diff contre l'instantané | `git diff --no-index --numstat` puis diff `-U2` lu en entier | `.cpp` `141 3` (avant : `136 3`) ; `.h` `9 0` | PASS : +5 = +2 (ligne de constantes : commentaire et `RecenterMinDistance` ajoutés autour de `FollowMinStep`) +3 (bloc d'obstacle : 1 ligne remplacée par commentaire, `Axis`, `Reach`, `Desired`) ; les 3 retraits sont inchangés ; tous les autres hunks sont identiques à ceux de la VAL initiale |
|
|||
|
|
| Périmètre | lecture | seules les constantes de suivi et le bloc `if (LineTraceSingleByChannel…)` de `RecenterSceneCamera` diffèrent de la VAL initiale | PASS : aucun ajout non demandé |
|
|||
|
|
|
|||
|
|
### R2. N1 : constante de suivi
|
|||
|
|
- Lignes (diff) : `const float FollowMinStep = 0.01f;` précédée d'un commentaire (« Le pivot est la place des acteurs (l'animation ne la bouge pas) : tout pas compte, meme lent »), et `const float RecenterMinDistance = 50.f;` (cm). Utilisation inchangée : `Moved.Size() > LPCameraModeLocal::FollowMinStep` (`double` contre `float`, sans avertissement).
|
|||
|
|
- Effet : le seuil par image tombe de 0,5 à 0,01 cm, soit 0,6 cm/s à 60 i/s : tout mouvement volontaire (flèches, molette, borne E1) est suivi, y compris à 240 i/s. **N1 levé** (PASS-STATIC).
|
|||
|
|
- Réserve non mesurée : si un acteur du couple dérivait de plus de 0,01 cm par image sans intervention (bruit numérique, recalage du jeu), la caméra suivrait cette dérive ; c'est le comportement voulu par G-c (« le jeu »), mais l'absence de tremblement en jeu n'est pas rejouée. L'affirmation du commentaire sur l'animation n'est pas vérifiée ici.
|
|||
|
|
|
|||
|
|
### R3. N2 : bloc d'obstacle
|
|||
|
|
Code lu (`.cpp` l. 926-932) :
|
|||
|
|
```
|
|||
|
|
if (World->LineTraceSingleByChannel(Hit, Target, Desired, ECC_Visibility, Query))
|
|||
|
|
{
|
|||
|
|
const FVector Axis = (Desired - Target).GetSafeNormal();
|
|||
|
|
const double Reach = FMath::Max(FVector::Dist(Target, Hit.Location) - LPCameraModeLocal::RecenterWallMargin, static_cast<double>(LPCameraModeLocal::RecenterMinDistance));
|
|||
|
|
Desired = Target + Axis * Reach;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- Types : `FVector::Dist` rend `T` = `double` (`Core/Public/Math/Vector.h:1015, 2464`) ; `Hit.Location` (`FVector_NetQuantize`, dérivée de `FVector`) se lie à `const TVector<double>&` par conversion vers la base (même emploi que `LPDirectControlSubsystem.cpp:1582`) ; `double - float` donne `double` ; `static_cast<double>(float)` : second argument `double` ; `FMath::Max(double, double)` : même type, même forme qu'à `.cpp` l. 909 (`FMath::Max(Interior.FloorTop + 30.0, Interior.Ceiling - Margin)`) et à `TickCoupleMouse` (`FMath::Max(OutZ, FloorBox.Max.Z)`) ; `Axis * Reach` : `FVector * double` (`operator*(FArg)` arithmétique, `Vector.h:311-312`) ; `Target + …` : `FVector` ; `Desired` non const. **Aucune erreur de type.** PASS-STATIC.
|
|||
|
|
- Ordre d'évaluation : `Axis` est calculé avant l'affectation de `Desired` et ne dépend que de `Desired` d'avant ; la ligne remplacée de la VAL initiale n'existe plus (`Grep` : un seul `Desired =` dans le bloc de trace).
|
|||
|
|
- Logique : le tracé est non dégénéré quand il touche (une trace de longueur nulle ne touche pas), donc `Desired - Target` n'est pas nul et `Axis` est un vecteur unitaire de la cible vers l'ancienne position visée : **la caméra reste du côté de la cible d'où vient `Desired`**. `Reach = Max(distance au point d'impact − 20 cm, 50 cm)` : **jamais moins de 50 cm** de la cible (établi par la forme de l'expression). La caméra ne peut plus passer de l'autre côté de la cible (défaut de N2). **N2 levé** (PASS-STATIC).
|
|||
|
|
- N2b, compromis non bloquant : si l'impact est à moins de 50 cm de la cible, la caméra est posée à 50 cm, donc au-delà de l'obstacle, de 50 − d cm (d = distance cible-impact, d < 50). Pour d entre 50 et 70 cm elle reste devant l'obstacle (d − 20 < 50 ≤ d). Si la distance visée initiale est elle-même inférieure à 50 cm (pièce très étroite, bornes F1), `Reach` peut dépasser la distance visée. Les deux cas sont rares (obstacle collé au couple, hors couple et caméras) et l'exigence « au moins 50 cm » les accepte ; le journal l'écrit (`rapprochee devant un obstacle` si l'impact existe). Aucun test en jeu rejoué.
|
|||
|
|
- Ligne de journal et rotation : `Hit.bBlockingHit` et `FVector::Dist(Desired, Target)` inchangés ; la distance écrite est désormais celle de la position finale.
|
|||
|
|
|
|||
|
|
### R4. Ombrage C4456 à C4459 pour `Axis` et `Reach`
|
|||
|
|
Méthode : `Grep` de `\b(Axis|Reach)\b` dans `Source/LifePlay` (`*.cpp`, `*.h`), lecture des portées, lecture de l'unité unifiée générée.
|
|||
|
|
- Portée : `Axis` et `Reach` sont déclarés dans le bloc `if` du tracé (l. 929-930) de `RecenterSceneCamera`. Paramètres de la fonction : `GI`, `Room`, `SceneCamera`, `Couple`, `Pivot`, `Reason` ; locales englobantes : `World`, `LowestRoot`, `Target`, `Flat`, `Desired`, `Interior`, `Query`, `Hit` : aucun `Axis` ni `Reach` : **C4456 et C4457 exclus**. Membres du `.h` (l. 49-140) : aucun `Axis` ni `Reach` : **C4458 exclu**.
|
|||
|
|
- `Axis` : les autres occurrences sont `FName Axis` en **paramètre de fonction** de `LPCameraModeLocal::AxisValue` (`.cpp` l. 241, 251), `FName Axis` de `AxisValue` dans `LPDirectControlSubsystem.cpp:168, 178`, une boucle `int32 Axis` locale (`LPDirectControlSubsystem.cpp:1154`) et `FVector Axis` locale (`LPOutdoorRoomSubsystem.cpp:433-438`). Ce sont des paramètres ou des locales, pas des noms de portée globale ni d'espace de noms : **aucun C4459 pour `Axis`** (confirmé). La preuve par l'existant : `LPOutdoorRoomSubsystem.cpp:433` déclare `FVector Axis` en local dans la même unité unifiée sous V7 sans conflit, donc aucun `Axis` global n'existe dans les en-têtes de l'unité.
|
|||
|
|
- `Reach` : **point d'attention.** `LPDirectControlSubsystem.cpp:58` déclare `const float Reach = 150.f;` dans un espace de noms **anonyme** (l. 53). La nouvelle locale `const double Reach` (l. 930) a le même nom. Établi : `Module.LifePlay.cpp` (unité unifiée, `Intermediate/Build/Win64/x64/UnrealEditor/Development/LifePlay/`) inclut les sources dans l'ordre alphabétique insensible à la casse (l. 31-57 : `LifePlay.cpp`, `LifePlayGameModeBase.cpp`, `LPAssetRegistryHelpers.cpp`, `LPColorGradeSubsystem.cpp`, … `LPDirectControlSubsystem.cpp` l. 37, … ) ; `LPCameraModeSubsystem.cpp` est absent de la copie générée (compilé seul, `[NoUba]`, comme dans `STG-COMMUN11-BUILD.md:21`), mais à l'ordre alphabétique il se placerait **avant** `LPDirectControlSubsystem.cpp`. Une déclaration locale ne peut masquer qu'un nom **déjà déclaré** à ce point de l'unité : ici l'anonyme `Reach` n'est pas encore vu, donc **pas de C4459 dans l'ordre actuel** (estimé : l'ordre alphabétique est constaté dans le fichier généré, pas garanti par le moteur ; la sensibilité exacte de C4459 aux noms d'un espace anonyme n'a pas été rejouée, faute de compilation). Une autre combinaison d'unités (ordre différent, fichier d'une unité à part qui inclurait `LPDirectControlSubsystem.cpp` avant, ou renommage de fichiers) pourrait en faire une erreur sous `/we4459`. **N7 (non bloquant).** Remède sans risque, à l'appréciation de l'orchestrateur : renommer la locale `Reach` en un nom unique (par exemple `ArmLength`) ; non fait par la VAL. Sinon BUILD le tranche (compilation de l'unité complète, pas seulement du fichier `[NoUba]`).
|
|||
|
|
- Autres noms globaux possibles : `Reach` ne figure dans aucun autre fichier de `Source` ; pas de lecture des en-têtes moteur pour un `Reach` global (incertain, faible probabilité).
|
|||
|
|
|
|||
|
|
### R5. Verdict final et suite
|
|||
|
|
- **PASS-STATIC** (N1 et N2 levés par lecture ; aucun défaut bloquant ; N7 non bloquant nouveau ; N3 reste ouvert comme avant). Non couverts : compilation (BLOCKED-compilation, BUILD), comportement en jeu (V7).
|
|||
|
|
- Cases à jour : voir § 6. L'IMPL doit être mise à jour (MD5 `A5F041FF…`, 1575 lignes, correction N1/N2) ; la ligne « Fichiers à commiter » de l'IMPL ne change pas.
|