LifePlay-Revival/Docs/Migration/etat/CAM/CAM-COMMUN3-DIAG.md

341 lines
26 KiB
Markdown
Raw Normal View History

# CAM-COMMUN3-DIAG : D8 KO, caméra collée à la nuque (2026-10-13)
Demande de Big Boss : quel `Wall`, pourquoi la tête est dans sa collision, quel correctif parmi (a), (b), (c). Lecture
seule, sans UE ni MCP. Aucune écriture avant GO.
Sources :
- journal partiel `D:\Game\LifePlay_Backup_5.5\COMMUN-3\essais\LifePlay_commun3_partiel_2030_BOSS.log` (MD5
`d42c53f9…`, heures UTC = locale − 2 h) ;
- dump Room `CAM-B2f-VAL_LPDumpFunction_20261013-195332.txt` ;
- composants Person `CAM-T4_LPDumpComponents_20261011-004910.txt` ;
- carte `ST/STE/STE-D_LPDumpComponents_20261010-113200.txt` ;
- StreetMap_Static `ST/STA/dumps/StreetMap_Static__EventGraph.txt` ;
- `CAM-B1-IMPL.md` (B1-12).
## 1. Relevé du témoin (23 lignes `Temoin C2`, 18:25 à 18:29 UTC)
| Objet touché par les deux sondes (Camera et Visibility, toujours identiques) | Lignes | Pièces (largeur de boîte X × Y) |
|---|---|---|
| `StaticMeshComponent0` de `Wall` (caché), **départ déjà dedans** | 16 | 665 × 640 (15), 847 × 815 (1) |
| `StaticMeshComponent0` de `Wall` (caché), à 61-131 cm de la tête | 4 | 257 × 1515 (rue, 2), 1028 × 990, 665 × 640 |
| rien | 3 | rue 257 × 1515 (2), 847 × 815, marge de boîte 19 à 37 cm |
Aucune ligne ne touche un personnage (`Person_C` : 0).
## 2. DIAG
**Quel `Wall` (prouvé).** Ce n'est pas un mur de Room (`Wall1-4`, ignorés avec Room par `bIgnoreSelf`). C'est l'acteur
`StaticMeshActor` **`PersistentLevel.Wall`** de CentralLondon :
- il fait partie des `ShowOnlyActors` des SceneCapture (`STE-D_LPDumpComponents`, l. 11) ;
- `GI.Wall` le référence ;
- c'est le **panneau de fond** qui montre l'image du lieu : `StreetMap_Static.UpdateWall(Amenity)` lui donne la
texture `AmenityBackgrounds` (`WallDM`, paramètre `PNG`).
Il est **caché dès qu'une pièce est affichée** : Room B1-12, `GI.Wall` caché = NON(`Room.bHidden`), nœud 88 ; et
`Wall` caché en C1, nœud 438. Le témoin le donne toujours `cache=1`.
**Pourquoi la tête est dedans (prouvé pour le fond, estimé pour la position).**
- `Wall` est fixe dans le niveau. La pièce, elle, est toujours posée au même point (`Room loc = (-3131543,
-14967213, -115)` dans tous les relevés du journal COMMUN-2) mais change d'échelle selon le lieu (3,5 × 18,
8 × 8, 12 × 12, 45 × 45). Le panneau traverse donc l'intérieur de certaines pièces : 4 pièces différentes ici.
- Le joueur le traverse sans être arrêté : la capsule de `Person` **ignore WorldStatic** (profil `Custom`, écarts
Visibility / WorldStatic / Pawn Ignore, dump T4). `Wall` est un `StaticMeshActor`, donc WorldStatic (type par
défaut, estimé).
- Sa collision, invisible mais active, bloque Camera **et** Visibility. Les sondes partent de la douille `head` déjà
dans le panneau : impact à 0 cm, `bStartPenetrating`. La caméra se place alors à 20 cm de la tête, à hauteur 0
(à 18:28-18:29, joueur immobile, 13 lignes de suite).
**Correction du DIAG de COMMUN-2.** La cause « PNJ cachés en collision » était **fausse**. Le témoin ne voit aucun
personnage, et le passage au canal Visibility (B2f) n'a rien changé, puisque `Wall` bloque aussi ce canal. La vraie
cause de « caméra trop près autour des meubles » est très probablement `Wall` : le joueur debout près d'un meuble se
trouve dans la bande du panneau. Le passage à Visibility reste neutre et peut rester en place.
Les 3 lignes « rien » (marge de boîte de 19 à 37 cm, rue étroite surtout) relèvent de la boîte B2e. C'est l'effet
voulu, « toujours dans la pièce ».
## 3. Les 3 pistes
| Piste | Effet sur les 23 lignes | Coût | Risque | Avis |
|---|---|---|---|---|
| (a) `GI.Wall` dans les acteurs ignorés des sondes | les 20 lignes `Wall` (16 dedans + 4 à distance) | Room : `add_node_pin` sur `MakeArray_5` + `connect_pins` `VariableGet_260.Wall` (getter `GI.Wall` déjà présent) → nouvelle entrée [2] ; 2 écritures, aucun nœud créé | nul hors caméra : seule la liste `ActorsToIgnore` des 3 sondes (459, 499, 505) change ; `GI.Wall` nul → entrée ignorée | **retenue** |
| (b) ignorer un impact « départ déjà dedans » | 16 lignes sur 20 : les 4 `Wall` à distance restent | ≈ 4-6 nœuds par sonde (BreakHitResult, NOT, AND) × 3 | masque aussi les vrais cas « dedans » ; ne traite pas la cause | non |
| (c) collision des murs cachés en Ignore (Camera, Visibility) | les 20 lignes | 3 voies de masquage de `Wall` (PC, Room, GI) ou un sous-système C++ ; touche un acteur du niveau | change le jeu hors caméra (clics, scènes) ; hors périmètre CAM | non pour CAM ; voir § 5 |
## 4. Correctif proposé : B2g (Room par `lp-mcp-bp`, plus le témoin C++)
1. Instantané `.\Tools\Snapshot.ps1 -Lot CAM-B2g` (Room `F2B082D2911D9F7C7BE60C2CF30EE6D5` attendu).
2. Room :
- `get_node_infos` sur `K2Node_MakeArray_5` (entrées [0] PlayerCharacter et [1] CamRoom ; sortie vers 459, 499 et
505) et sur `K2Node_VariableGet_260` (`Wall`, self ← `VariableGet_257.GI`) ;
- `add_node_pin` sur `MakeArray_5`, puis `connect_pins` `260.Wall` → nouvelle entrée [2] ;
- relecture, `is_dirty` = vrai, compilation stricte, `save_assets` de Room seul, MD5 différent.
3. Témoin C++ (`LPCameraModeSubsystem.cpp`, `WitnessC2`) : ajouter `GI.Wall` aux acteurs ignorés, pour qu'il reste le
miroir de la sonde de Room (2 lignes : lecture de `Wall` sur GI, `AddIgnoredActor`).
- VAL :
- V5 : `MakeArray_5` a 3 entrées, [2] ← `260.Wall`, aucune autre différence ;
- V6 = référence ;
- V7 au prochain build commun.
- Essai : rejouer D8 dans la même chambre (665 × 640), près du meuble, debout immobile. La caméra reste derrière, à sa
distance. Les lignes `Temoin C2` ne doivent plus citer `Wall`.
- Barème :
- Room : a0 b1 c0 d0 e1 f0 = 2 → N1, ajusté à N3 (MCP par `lp-mcp-bp`) ; S = C3 1 + f 0 = 1 : une fiche ;
- C++ : a0 b0 c0 d0 e1 f0 = 1 → N1 ; S = 0.
## 5. Hors CAM, à signaler (avis seulement)
`Wall` caché garde une collision qui bloque Visibility au milieu de certaines pièces. Les clics à la souris
(`GetHitResultUnderCursor`, canal Visibility par défaut) pourraient donc buter sur ce panneau invisible, dans la même
bande. C'est un défaut probable du jeu d'origine, non mesuré ici. S'il est confirmé, la piste (c), par exemple
`Wall` en Ignore pour Visibility et Camera pendant qu'il est caché, relève du lot qui tient les clics ou le décor, pas
de CAM.
## 6. Second cas (18:30:11, 18:30:17, 18:30:23 UTC ; lignes relayées par Big Boss, après la copie partielle)
Signature : « caméra à 23 cm de la tête (hauteur +22) ; sonde Camera : rien ; sonde Visibility : rien ; boîte C2 :
marge 149 cm (largeur X 847, Y 815) ». À 18:30:05, c'était encore le cas « `Wall`, départ déjà dedans ».
**Hypothèses de Big Boss écartées (dump de Room après B2f)** :
- **distance restée au minimum après le repli** : non. `C2Target` n'a pas de mémoire. `UpdateRoomCam` appelle
`ComputeC2Target` (467) à chaque passage de la minuterie, et la cible est recalculée depuis la tête (61, 62, puis
66 en repli). Seule la poursuite `VInterpTo` (468, vitesse 8) lisse le trajet : environ 0,3 s pour combler 90 %,
pas 12 s.
- **molette** : non. `C2Dist` est borné entre −75 et +125 autour de 200 cm, donc au moins 125 cm, jamais 23.
- **boîte B2e** : non. La marge est de 149 cm.
**Cause estimée : le même `Wall`, cette fois juste derrière la tête et non autour d'elle.**
1. La sonde principale 459 va de la tête vers la cible, derrière la rotation de contrôle. Elle touche `Wall` à quelques
centimètres. La cible devient l'impact + normale × 20 (61), donc à moins de 80 cm.
2. Le repli s'enclenche (95, seuil 80). La sonde 505 va de la tête vers « tête + 100 en Z + 60 cm vers l'arrière »
(24, 23, 25). Elle traverse encore le panneau, juste derrière. La cible devient l'impact + normale × 20 (27, 66) :
un point à environ 20 cm de la tête et un peu au-dessus. C'est exactement la signature : 23 cm, hauteur +22.
3. Le témoin ne voit rien parce qu'il sonde **vers la caméra**, à plat. La caméra est presque à la verticale de la
tête, légèrement ramenée vers l'avant par la normale × 20. La sonde part donc à l'opposé du panneau. Le témoin
n'est pas faux, mais il est aveugle à ce cas : il ne refait pas le trajet de la sonde 459.
- **Statut : estimé.** Il est cohérent avec la position du joueur juste après le cas « dedans » de 18:30:05 : il en
sort de quelques centimètres et le panneau reste derrière lui. Il est à confirmer sur le journal définitif, ou par
le témoin corrigé ci-dessous.
**Effet du correctif (a)** : les 3 sondes (459, 499, 505) partagent `MakeArray_5`. Ignorer `GI.Wall` couvre donc aussi
ce second cas.
**Complément au témoin C++ (B2g)** pour que le cas devienne prouvable :
- sonder aussi **dans l'axe de la sonde 459** : à plat, vers l'arrière de la rotation de contrôle du PC, sur 250 cm ;
- journaliser la distance tête → `Room.C2Target`.
Une cible proche avec une sonde vers l'arrière qui touche désigne l'obstacle. Une cible lointaine avec une caméra
proche désignerait la poursuite. Barème inchangé : N1, S = 0.
## 7. KO C1 : « la souris ne fait plus tourner la vue » (vers 18:37:50 UTC)
**DIAG (prouvé par le dump de Room après B2f et par l'historique git).**
- **Ce n'est pas une régression de B2d, et l'orientation vers la marche n'est pas de B2d.**
- `bOrientRotationToMovement = true` et `bUseControllerRotationYaw = false` sont posés dans
`LPDirectControlSubsystem::Walk` depuis le commit d'origine `5e901be` (Proximité).
`git log -S bOrientRotationToMovement` ne donne que ce commit. Le diff non commité de STG (3a, 3b) n'y touche pas.
- En C1, la souris n'a **jamais** tourné la vue. C'est la décision Q11 de la SPEC (« C1 masque cheveux, soleil,
mur ; pas de regard à la souris »).
- Dans Room, `CircleCamera` (souris) et `LeftRight` (flèches) ne tournent la rotation de contrôle **qu'en C2**
(`IfThenElse_96` / `_97`, condition PO_8 : vue = `CamRoom`). Hors C2, sans constructeur, sexe ni espace
(`IfThenElse_26`, OR 19), la branche `else` est **vide**. Elle l'était déjà avant B2d.
- **Ce que voit le joueur en C1** :
- la vue est attachée au maillage, au niveau de la tête (B1-3) : elle tourne seulement avec le corps ;
- le corps se tourne vers la marche (`bOrientRotationToMovement`) ;
- la marche suit le lacet de la rotation de contrôle (`Walk`, l. 908), qui ne change qu'en C2 (souris, flèches,
`PlaceC2`).
En C1, on ne choisit donc la direction du regard qu'en marchant. Après Q ou D, le corps regarde de côté, mais Z
ramène vers l'axe de contrôle.
- **La piste « rétablir `bUseControllerRotationYaw = true` » ne marcherait pas (estimé fort).** Le joueur n'est **pas possédé** par
le PC : la marche tourne en « physique sans contrôleur », `bRunPhysicsWithNoController` (`Walk`, l. 875-879 ; la marche ne fonctionne que grâce à ce réglage).
`bUseControllerRotationYaw` n'agit que par `FaceRotation`, appelée par le contrôleur du pion possédé. Sans
contrôleur, le corps ne suivrait pas la souris.
- Autre point : `Walk` remet `bOrientRotationToMovement` à vrai à chaque passage où il est faux (l. 888-892). Le
couper ailleurs, dans Room par exemple, serait annulé à l'image suivante.
**Correctif proposé : regard horizontal à la souris en C1, comme un jeu à la première personne.** Il se fait en deux
parties qui vont ensemble.
1. **Room (CAM, `lp-mcp-bp`)** : la souris et les flèches gauche/droite tournent la rotation de contrôle en C1 aussi.
- (N) `BooleanOR` : A ← `PromotableOperator_8` (vue = `CamRoom`), B ← `VariableGet_269.CamC1On` ;
- sortie → `IfThenElse_96.Condition` et `IfThenElse_97.Condition` (remplace PO_8 sur ces deux seules entrées) ;
- PO_8 reste relié à 91, 98, 99 et 100 : molette et flèches haut/bas inchangées hors C2.
1 nœud, 4 liens. Barème : a0 b1 c0 d0 e1 f1 = 3 → N1, ajusté à N3 (MCP) ; S = C3 1 + f 1 = 2 < 3. Peut passer dans
la même ouverture d'éditeur que B2g.
2. **`LPDirectControlSubsystem::Walk` (fichier STG, correctif exact)** : en C1, le corps suit le lacet de contrôle et
n'est plus orienté par la marche. En sortant de C1, on revient à l'orientation vers la marche.
```cpp
// dans Walk, a la place du bloc « if (!Move->bOrientRotationToMovement) { ... } » (l. 888-892)
const bool bFirstPerson = FirstPersonView(GI, Pawn, PC); // C1 : Room.CamC1On et vue = le joueur
if (Move->bOrientRotationToMovement == bFirstPerson)
{
Move->bOrientRotationToMovement = !bFirstPerson;
Character->bUseControllerRotationYaw = false; // sans effet sans controleur ; garde l'etat d'origine
}
if (bFirstPerson)
{
// C1 : le corps (et la vue attachee a la tete) suit le lacet de la souris, a chaque image.
Character->SetActorRotation(FRotator(0.f, PC->GetControlRotation().Yaw, 0.f));
}
// espace anonyme, a cote de ScenePlaying
bool FirstPersonView(const UObject* GI, const APawn* Pawn, const APlayerController* PC)
{
const FObjectPropertyBase* RoomProp = GI ? CastField<FObjectPropertyBase>(GI->GetClass()->FindPropertyByName(TEXT("Room"))) : nullptr;
const UObject* Room = RoomProp ? RoomProp->GetObjectPropertyValue_InContainer(GI) : nullptr;
const FBoolProperty* C1 = IsValid(Room) ? CastField<FBoolProperty>(Room->GetClass()->FindPropertyByName(TEXT("CamC1On"))) : nullptr;
return C1 && C1->GetPropertyValue_InContainer(Room) && PC && PC->GetViewTarget() == Pawn;
}
```
- En entrant en C1 par C, le corps prend le lacet de contrôle, donc la direction où regardait C2. La vue reste
ainsi dans l'axe de la caméra précédente. Aucun alignement n'est nécessaire.
- Pas de regard vertical (tangage) dans ce correctif : il faudrait tourner `FirstPersonSM`. C'est à décider à part.
- Barème STG : a0 b1 c0 d1 e1 f1 = 4 → N1 ; S = 0 (code exact) + 1 = 1.
- Essai :
- en C1, la souris et les flèches gauche/droite tournent la vue sur place ;
- Z avance là où l'on regarde, Q et D font un pas de côté sans tourner la vue ;
- en C2, rien ne change ;
- en revenant en C1, la vue regarde dans l'axe de la caméra C2.
- **Dépendance** : sans la partie 1, la partie 2 fige le corps sur un lacet que rien ne change en C1. Les deux parties
doivent entrer dans le même build.
## 8. Troisième cas (session 2, joueur assis, 18:54:08-20) et géométrie de `Wall` (prouvée)
**Géométrie de `Wall` (prouvée, deux sources concordantes)** :
- relevé de la carte `portage55/L6f/L6f_LPDumpComponents_20260930-112932.txt` (`-Filter=Wall`) :
`StaticMesh = /Engine/BasicShapes/Cube`, `BodyInstance = (ObjectType=ECC_WorldStatic, CollisionProfileName="BlockAll")`,
`RelativeLocation = (-3131490, -14967219, 26)`, `RelativeScale3D = (0.1, 12.5625, 5.75)` ;
- boîte journalisée en jeu par ShowCouple (« coque Wall ignorée », journal COMMUN-2) :
`Min = (-3131495, -14967847.1, -261.5)`, `Max = (-3131485, -14966590.9, 313.5)`.
C'est une **plaque verticale de 10 cm d'épaisseur** (X), longue de 12,56 m (Y) et haute de 5,75 m. Elle est fixe et
se trouve à **53 cm du centre de la pièce** (Room toujours en X = -3131543), parallèle à Y. Elle traverse donc
**toutes les pièces**, pas seulement certaines (correction du § 2). Elle est cachée, mais `BlockAll` bloque Camera,
Visibility et tout le reste. Le joueur la traverse, car sa capsule ignore WorldStatic. ShowCouple l'ignorait déjà
comme « coque ».
**Recoupement avec le témoin (session 1, MD5 `27e654cc…`)** :
- la longue série « départ déjà dedans » de 18:28:08 à 18:29:21 a lieu joueur immobile en X = -3131499,6 : dans
la plaque, à 5 cm de la face X = -3131495 ;
- à 18:30:11-23 (« rien », 23 cm, +22) puis 18:30:29 (« dedans », même caméra), le joueur est immobile en
X = -3131522,9 : à 28 cm de la face, la tête oscille au bord de la plaque ;
- les autres lignes ont une position relevée jusqu'à 5 s plus tôt (vidage périodique), le joueur étant en marche :
elles ne se comparent pas.
**Le troisième cas est le même mécanisme que le § 6 : la plaque juste derrière la tête.**
- La sonde 459 touche la plaque à quelques centimètres derrière, et la cible passe sous 80 cm.
- Le repli 505 (haut 100, arrière 60) touche encore la plaque. Si la face est à environ 35 cm derrière la tête, la
sphère de 20 cm la touche à environ 15 cm en arrière et 25 cm en haut. Avec la normale × 20 vers le joueur, on
obtient environ 25 cm de la tête, hauteur +25 : la signature « 26 cm, +26 » est retrouvée.
- Assis, la tête est plus basse, mais le calcul part de la douille `head` du maillage (455, et pareil pour le témoin).
Les pistes « point de tête lu sur la capsule », « position assise décalée » et « tangage de C2 » sont donc écartées.
Le tangage n'existe pas : `MakeRotator(0, 0, lacet)`.
- Le témoin dit « rien » parce qu'il sonde à plat **vers la caméra**. Ici, elle est presque à la verticale de la tête
et un peu en avant, à cause de la normale × 20 : la sonde part à l'opposé de la plaque.
- **Contrôle à faire au journal définitif de la session 2** : X du joueur à 18:54 entre environ -3131560 et
-3131420 (plaque à ±70 cm, selon la direction du regard). Si X sort nettement de cette bande, l'hypothèse tombe et
le cas reste ouvert.
**Le correctif (a) couvre les trois cas.** Les 3 sondes (459, 499, 505) partagent `MakeArray_5`. Avec `GI.Wall`
ignoré, ni la sonde principale ni le repli ne voient plus la plaque. Rien d'autre que la plaque ne bloque Visibility
dans la pièce : les personnages l'ignorent, et Room est ignoré par `bIgnoreSelf`.
- Variante plus robuste, au choix de Big Boss : ignorer **tout acteur caché** dans les sondes. Il faudrait un filtre
sur `HitActor.bHidden` et un nouveau balayage, ce qui fait bien plus de nœuds. Non retenue : `Wall` est le seul cas
relevé.
**Conséquence hors CAM (renforce le § 5)** : une paroi invisible et `BlockAll` coupe chaque pièce en deux, à 53 cm
du centre. Pour la marche, le joueur ne la sent pas (WorldStatic ignoré). En revanche, tout tracé Visibility, comme
les clics, ou WorldStatic, comme la sonde de ShowCouple qui l'ignore déjà, peut buter dessus. Il faudrait la passer
en `NoCollision`, puisqu'elle ne sert que de fond aux SceneCapture, mais c'est une décision hors CAM : acteur du
niveau, lot de décor ou STG.
## 9. Finalisation sur les journaux définitifs (bilan `etat\BOSS\COMMUN-3-BILAN.md`)
Journaux lus :
- session 1 : `LifePlay_commun3_session1_definitif_BOSS.log` (`27e654cc…`, 275 relevés `Room loc`) ;
- session 2 : `LifePlay_commun3_session2_ferme_BOSS.log` (`61e37cf3…`, 164 relevés `Room loc`).
Dans les deux sessions, Room est toujours en (-3131543, -14967213, -115).
### D8 cas 1 et 2 : prouvés, cause `GI.Wall`
Voir le § 8. La plaque est fixe : aucun Blueprint relevé ne la déplace. GI, PC, Room et StreetMap ne font que la
cacher, changer sa visibilité ou son matériau (dumps lus). Les cas « dedans » relevés loin de la plaque ont une
position vieille de 1 à 4 s, joueur en marche ou changement de pièce (18:56:54-59 en session 2) : ils ne s'y
opposent pas.
### D8 cas 3 (session 2, 18:53:20 à 18:54:38 UTC, assis) : mécanisme prouvé par élimination, objet probable
- Le joueur (Bridget) est immobile pendant 80 s, en X = -3131579,5, Y = -14967396 (`fnt_chair06-f-0-sit`, `_48`).
La caméra reste stable, à 26-27 cm de la tête et hauteur +26 (14 lignes).
- **Une sonde a forcément touché.** Sans impact, la cible est à au moins 125 cm (`C2Dist` borné) et la caméra la
rejoint en environ 0,3 s. `C2Scripted` n'est jamais mis à vrai (B4 non fait, seule écriture = faux). La boîte a une
marge de 135 cm. Le repli sans impact place la cible à environ 117 cm. Seul un impact suivi du repli donne
« 26 cm, +26 ».
- **Ce qui peut bloquer Visibility à moins de 80 cm** :
- Room : ignoré par `bIgnoreSelf` ;
- le joueur et `CamRoom` : ignorés ;
- les personnages : capsule et maillage ignorent Visibility, et le plus proche, Laura, visible, est à 187 cm ; les
PNJ cachés sont à 212 cm et plus ;
- il ne reste que la plaque `Wall`, à 84 cm du centre de la capsule, dans l'axe X.
- **Point non mesuré** : la tête du personnage assis est-elle décalée d'environ 50 cm vers la plaque par rapport à la
capsule ? C'est la condition géométrique pour obtenir 26 cm, +26. Le journal ne donne pas la position de la tête.
- Le témoin dit « rien », car il sonde vers la caméra, presque à la verticale, et pas dans l'axe de la sonde 459.
- **Statut : probable.** Le correctif (a) le couvre si l'objet est bien `Wall`. Le témoin complété (§ 10, C) le
tranchera à COMMUN-4 : position de la tête, décalage par rapport à la capsule, distance de la cible et sonde dans
l'axe de 459.
### C1, souris : voir le § 7 (prouvé). Correctif en deux parties, Room (CAM) et `Walk` (STG).
## 10. Correctifs exacts pour COMMUN-4 (aucune écriture avant GO)
| Id | Fichier | MD5 attendu avant écriture | Écrit par | Jeton |
|---|---|---|---|---|
| A. B2g (D8) | `Content/BPs/Room.uasset` | `F2B082D2911D9F7C7BE60C2CF30EE6D5` | `lp-mcp-bp` | UE |
| B. B2h (C1, partie Room) | `Content/BPs/Room.uasset` (même ouverture que A) | idem, puis MD5 après A et B | `lp-mcp-bp` | UE |
| C. témoin C2 complété | `Source/LifePlay/LPCameraModeSubsystem.cpp` | `1DE73AA932AF2A056E1B8528CED4DCAB` (`.h` `84F9BA30…` inchangé) | session CAM | aucun (compilé au build) |
| D. C1, partie `Walk` | `Source/LifePlay/LPDirectControlSubsystem.cpp` (STG) | `827F19C0B7A78BDF578ACF4AE6C2289D` (relevé du 2026-10-13, état STG non commité) | STG | aucun (compilé au build) |
GI (`E64B5B37…`) et Person (`8668A47C…`) ne sont pas touchés.
**A. B2g : `GI.Wall` ignoré par les 3 sondes de C2.** Instantané `.\Tools\Snapshot.ps1 -Lot CAM-B2g -Path
Content\BPs\Room.uasset`. Préfixe `/Game/BPs/Room.Room:EventGraph.`, un appel MCP par message :
1. `get_node_infos` `K2Node_MakeArray_5` : entrées [0] ← `VariableGet_261.PlayerCharacter`, [1] ←
`VariableGet_268.CamRoom` ; sortie `Array` → 459, 499, 505 `.ActorsToIgnore`.
Et `K2Node_VariableGet_260` : `Wall`, self ← `VariableGet_257.GI`, sortie → 88 et 438 `.self`.
2. `add_node_pin` `K2Node_MakeArray_5` → nouvelle entrée [2] (relever son PinID).
3. `connect_pins` `260.Wall` (sortie) → `MakeArray_5.[2]`.
4. Relecture des 3 entrées et des 3 sorties de `MakeArray_5`, puis de 88 et 438 (liens de 260 conservés).
B ne crée ni ne supprime de nœud. Si elle vient seule, faire la séquence `create_node` / `delete_node` d'une Branche
isolée avant l'enregistrement (amendement B2f-a), si `is_dirty` est faux.
**B. B2h : en C1, la souris et les flèches gauche/droite tournent la rotation de contrôle** (§ 7, partie 1).
5. `create_node` du OU booléen. Même type que `K2Node_CommutativeAssociativeBinaryOperator_19` (`BooleanOR`) :
prendre le `type_id` par `find_node_types` (filtre « OR »). 2 entrées A et B.
6. `connect_pins` `PromotableOperator_8.ReturnValue` → OR.A ; `VariableGet_269.CamC1On` → OR.B.
7. `connect_pins` OR.ReturnValue → `IfThenElse_96.Condition`, puis → `IfThenElse_97.Condition`.
`connect_pins` remplace le lien d'entrée existant (PO_8) sur ces deux seules conditions.
8. Relecture :
- PO_8 reste relié à 91, 98, 99, **100** et OR.A, et ne l'est plus à 96 ni 97 (amendement E1 : 100 = `DownUp`, hauteur de C2, omis dans la première liste ; écritures inchangées) ;
- 269 garde ses liens (77, 82, 84) et ajoute OR.B ;
- 96.then → 516 et 97.then → 518 sont inchangés.
9. Pour A et B : `is_dirty` = vrai, compilation stricte, `save_assets` de Room seul, MD5 différent de `F2B082D2`.
VAL : V5, avec seulement ces différences (`MakeArray_5` [2], 1 nœud OR, conditions de 96 et 97), Room = 1261 nœuds ;
V6 = référence.
Barème A + B : a0 b1 c0 d0 e1 f1 = 3 → N1, ajusté à N3 (MCP) ; S = C3 1 + f 1 = 2 < 3 : une fiche.
**C. Témoin C2 complété** (`LPCameraModeSubsystem.cpp`, code exact) :
```cpp
// LPCameraModeLocal
const FName GIWall(TEXT("Wall"));
const FName RoomC2Target(TEXT("C2Target"));
// WitnessC2, apres Query.AddIgnoredActor(Cam) : meme liste que MakeArray_5 apres B2g
if (AActor* Wall = Cast<AActor>(LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIWall)))
{
Query.AddIgnoredActor(Wall);
}
// Probe prend la direction en parametre : auto Probe = [&](ECollisionChannel Channel, const FVector& Direction) -> FString
const FString CameraText = Probe(ECC_Camera, Dir);
const FString VisibilityText = Probe(ECC_Visibility, Dir);
// Axe de la sonde 459 : a plat, derriere la rotation de controle du PC.
const APlayerController* PC = World->GetFirstPlayerController();
const FVector Back = PC ? -FRotationMatrix(FRotator(0.f, PC->GetControlRotation().Yaw, 0.f)).GetUnitAxis(EAxis::X) : -Player->GetActorForwardVector();
const FString BackText = Probe(ECC_Visibility, Back);
FVector Target;
const FString TargetText = LPCameraModeLocal::ReadVector(Room, LPCameraModeLocal::RoomC2Target, Target) ? FString::Printf(TEXT("%.0f cm"), FVector::Dist(Head, Target)) : FString(TEXT("illisible"));
const FVector Offset = Head - Player->GetActorLocation();
UE_LOG(LogLifePlay, Log, TEXT("Temoin C2 : camera a %.0f cm de la tete (hauteur %+.0f) ; cible C2 a %s ; tete (%.0f, %.0f), decalage capsule %.0f cm ; vers la camera, Camera : %s, Visibility : %s ; vers l'arriere, Visibility : %s ; boite C2 : %s"),
Distance, CamLoc.Z - Head.Z, *TargetText, Head.X, Head.Y, Offset.Size2D(), *CameraText, *VisibilityText, *BackText, *BoxText);
```
Sauvegarde `Backup_5.5\CAM-B2g\Source\LifePlay\` avant modification. Barème : N1 ; S = 0.
**D. `Walk`, partie STG** : le code exact est au § 7. Le bloc à remplacer est maintenant aux **l. 935-939** de la
version `827F19C0` : `if (!Move->bOrientRotationToMovement) { … }`. La lecture de `GI` et `PC` se trouve en tête de
`Walk` (l. 908 et suivantes). **D n'a de sens qu'avec B** : il faut les mettre dans le même build.
**Cas de test pour COMMUN-4** :
- D8 : rejouer la chambre et le joueur assis. Aucune ligne `Temoin C2` sous 100 cm hors des rues étroites. Si une
ligne reste, elle donne la cible, la tête et la sonde vers l'arrière.
- C1 : la souris et les flèches gauche/droite tournent la vue ; Z avance là où l'on regarde ; C2 est inchangé.