LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2b-IMPL-F7.md

273 lines
17 KiB
Markdown
Raw Normal View History

Fiche CAM-B2b-F7 — Niveau N3 — Modèle prévu opus | utilisé claude-opus-5-5 (Opus 5.5) — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot
# CAM-B2b-F7-IMPL : pilotage de C2 par `DownUp` (B2b-6, fin), relecture globale, compilation stricte, enregistrement de Room (2026-10-13)
Consignes : `CAM-B2b-FICHES.md` § « Rubriques communes » (l. 23-63) et § « F7 » (l. 134-142). Seul `DownUp` restait à
faire : `RotateCamera` a été traité en F6b. SPEC : `CAM-SPEC.md` § « CAM-B2b », ligne B2b-6 (l. 380). Modèle suivi :
`CAM-B2b-IMPL-F6b.md`, partie `RotateCamera` (même schéma).
Contexte : UE-GO de Big Boss B2b du 2026-10-13, cité par l'orchestrateur. Éditeur headless PID 10708, port 8000,
connexion `unreal-mcp` partagée (MCPA-T1). Room chargé, F1 à F6b en mémoire. Un seul `call_tool` par message.
**24 appels MCP** :
- 3 `get_node_infos`, 6 `create_node`, 1 `break_pins`, 9 `connect_pins` et 3 `set_pin_value` ;
- 1 `compile_blueprint` et 1 `save_assets`.
Aucun `delete_node`, `write_graph_dsl` ni `ProgrammaticToolset`. Aucune écriture par HyperAIStudio.
## 1. Fichiers modifiés
- `Content/BPs/Room.uasset` : **enregistré** par `save_assets` (§ 1.8). Il contient F1 à F7. MD5 avant
`a5fa970e…` (= instantané) ; après `e0517c63541f249bd9dbda5c39815ba5`. Taille passée de 6 060 745 à 6 318 319 octets.
- Graphe `/Game/BPs/Room.Room:EventGraph`, événement `DownUp` (CE8, x 5520, y 3952) : nœuds neufs en x 5700-6550,
y 4250-4550.
- Aucun autre asset n'a été ouvert en écriture ni enregistré (§ 3).
### 1.1 Lecture avant écriture (`get_node_infos` groupé, 3 nœuds existants)
La lecture est conforme aux faits de la fiche :
- `K2Node_CustomEvent_8` (`AddEvent|Custom|DownUp`) :
- `then` out 1 → `IfThenElse_8`.execute 0 ;
- `AxisValue` out 2 (`Float (double-précision)`) → `CommutativeAssociativeBinaryOperator_3`.0, `_5`.0 et `_8`.0.
- `K2Node_IfThenElse_8` : execute ← CE8.1 ; Condition ← `CommutativeAssociativeBinaryOperator_17`.0 ; then 0 →
`IfThenElse_17`.0 ; else 1 libre.
- `K2Node_PromotableOperator_8` (`Utilitaires|Equal(Object)`) : A ← `CallFunction_439`.0, B ← `VariableGet_268`.0 ; out 0
(`Booléen`) → `IfThenElse_91`.1, `_96`.1, `_97`.1, `_98`.1 et `_99`.1.
### 1.2 Nœuds créés (préfixe `refPath` : `/Game/BPs/Room.Room:EventGraph.`)
| Rôle | `refPath` (suffixe) | `type_id` demandé → relu | Broches relues | Position |
|---|---|---|---|---|
| **Branche** C2 | `K2Node_IfThenElse_100` | `Utilitaires\|ContrôleDeFlux\|Branche` → idem | execute 0, Condition 1 ; then 0, else 1 | 5750, 4250 |
| Obtenir C2Height | `K2Node_VariableGet_296` | `Variables\|Pardéfaut\|ObtenirC2Height` → `\|GetC2Height` | C2Height out 0 (double) | 5700, 4450 |
| AxisValue × 3 | `K2Node_PromotableOperator_35` | `Utilitaires\|Opérateurs\|Multiplier` → `Math\|Flottant\|float*float` | A 0, B 1 ; out 0 (double) | 5900, 4550 |
| C2Height + produit | `K2Node_PromotableOperator_36` | `Utilitaires\|Opérateurs\|Ajouter` → `Math\|Flottant\|float+float` | A 0, B 1 ; out 0 (double) | 6100, 4450 |
| Clamp | `K2Node_CallFunction_511` | `Math\|Flottant\|Clamp(Float)` → idem | Value 0, Min 1, Max 2 ; ReturnValue out 0 (double) | 6300, 4500 |
| **Définir C2Height** | `K2Node_VariableSet_71` | `Variables\|Pardéfaut\|DéfinirC2Height` → `\|SetC2Height` | execute 0, C2Height 1 ; then 0, Output_Get 1 | 6550, 4400 |
Comme en F3 à F6b, les opérateurs naissent avec un `type_id` trompeur et des broches `Caractère de remplacement` :
- PO35 : `Utilitaires|Gestiondutemps|Seconds*FrameRate` ;
- PO36 : `…|FrameNumber+Int`.
À la relecture finale, ils sont typés `float*float` et `float+float`, en double précision. À la création, le Clamp avait
Min `0.0` et Max `1.0`. PO8 est réutilisé sans copie (nœud pur).
### 1.3 Lien coupé (`break_pins`, 1, `{"returnValue":null}`)
| # | Sortie | Entrée |
|---|---|---|
| C1 | CustomEvent_8.then 1 | IfThenElse_8.execute 0 |
Aucun autre lien n'a été coupé, et rien d'autre n'a été touché dans `DownUp`.
### 1.4 Liens faits (`connect_pins`, 9, tous `{"returnValue":null}`)
| # | Sortie | Entrée |
|---|---|---|
| L1 | CustomEvent_8.then 1 | IfThenElse_100.execute 0 |
| L2 | IfThenElse_100.else 1 | IfThenElse_8.execute 0 |
| L3 | PromotableOperator_8.0 | IfThenElse_100.Condition 1 |
| L4 | CustomEvent_8.AxisValue 2 | PromotableOperator_35.A 0 |
| L5 | VariableGet_296.0 (`C2Height`) | PromotableOperator_36.A 0 |
| L6 | PromotableOperator_35.0 | PromotableOperator_36.B 1 |
| L7 | PromotableOperator_36.0 | CallFunction_511.Value 0 |
| L8 | CallFunction_511.0 | VariableSet_71.C2Height 1 |
| L9 | IfThenElse_100.then 0 | VariableSet_71.execute 0 |
`VariableSet_71.then` reste libre : c'est la fin de l'événement côté C2, comme dans F6a et F6b.
### 1.5 Valeurs (`set_pin_value`, 3, tous `{"returnValue":null}`, relues)
| Broche | Avant (relu à la création) | Après (relu) |
|---|---|---|
| PromotableOperator_35.B 1 | (neuf, `""`, joker) | `3.0` (`Float (double-précision)`) |
| CallFunction_511.Min 1 | `0.0` | `-40.0` |
| CallFunction_511.Max 2 | `1.0` | `150.0` |
`PO35.B` a été posé après L4, qui type l'opérateur. La Condition de la Branche 100 garde sa valeur par défaut `true`, sans
effet puisque la broche est liée (L3).
### 1.6 Relecture globale (`get_node_infos` groupé, 23 nœuds)
Nœuds relus : les 6 nœuds créés, `IfThenElse_8`, PO8, CE1, CE4, CE8, CE19, CE55, `CallFunction_459`, `_467`, `_474`,
`_499`, `_505`, `VariableSet_61`, `_62`, `_66`, `CustomEvent_49` et `CallFunction_494`. Chaque lien a été comparé au
rapport de la fiche qui l'a posé :
**DownUp (F7)**
- Les 9 liens L1 à L9 apparaissent des deux côtés, et les 3 valeurs sont relues.
- `IfThenElse_8` : execute ← `IfThenElse_100`.1 ; Condition ← `CommutativeAssociativeBinaryOperator_17` ; then →
`IfThenElse_17` ; else libre.
- CE8.AxisValue → CABO_3.0, _5.0, _8.0, plus PO35.0.
**PO8** : sortie → `IfThenElse_91`, `_96`, `_97`, `_98`, `_99` et `_100` (6 branches).
**Événements de déplacement (F6a, F6b)**
- CE1 `CircleCamera` : then → `IfThenElse_96` ; DeltaSeconds → CABO_26.1, _16.1, _27.1, plus PO28.1 (F6a L1, L5).
- CE4 `LeftRight` : then → `IfThenElse_97` ; AxisValue → CABO_47.0, _13.0, _24.0, plus PO29.0 (F6a L8, L11).
- CE19 `RotateCamera` : then → `IfThenElse_99` ; DeltaSeconds → CABO_45.1, _12.1, _15.1, plus PO33.0 (F6b L10, L13).
- CE55 `ScrollDownUp` : then → `IfThenElse_98` ; Values → CABO_2.0, CF_26.2, CABO_20.0, _10.0, _22.0, plus PO31.0
(F6b L1, L4).
**`ComputeC2Target` et `ComputeRoomBox` (F2, F3, F5)**
- 494 `|ComputeRoomBox` : execute ← CE47.1 ; then → 459.0 (F3 L1, L2).
- 459 (trace de `Desired`) :
- execute ← 494, then → `IfThenElse_90`, ReturnValue → `IfThenElse_90`.1, OutHit → 460 ;
- Start ← 455, End ← 495 (F3 L6), ActorsToIgnore ← `MakeArray_5` ;
- Radius `20.0` (F3), TraceChannel `TraceTypeQuery2`.
- `VariableSet_61` : execute ← `IfThenElse_90`.0 ; C2Target ← 496 (F3 L11) ; then → `IfThenElse_95` (F5 L1).
- `VariableSet_62` : execute ← `IfThenElse_90`.1 ; C2Target ← 495 (F3 L7) ; then → `IfThenElse_95` (F5 L2).
- 505 (trace du repli) :
- execute ← `IfThenElse_95`.0, then → `VariableSet_66`, OutHit → 506, ReturnValue → 507.2 ;
- Start ← 455, End ← 504, ActorsToIgnore ← `MakeArray_5` ;
- Radius `20.0`, `TraceTypeQuery2` (F5 L14 à L18, L24, L29).
- `VariableSet_66` : execute ← 505 ; C2Target ← 508 (F5 L28) ; then libre.
- CE49 `ComputeRoomBox` : then → `VariableSet_64` (F2 L21).
**Poursuite (F4)**
- 467 `|ComputeC2Target` : execute ← `IfThenElse_93`.1 ; then → 499 (F4 L1).
- 499 (trace de la poursuite) :
- execute ← 467, then → 474, OutHit → 500, ReturnValue → 501.2 ;
- Start ← 455, End ← 468, ActorsToIgnore ← `MakeArray_5` ;
- Radius `20.0`, `TraceTypeQuery2` (F4 L1 à L6, L12).
- 474 : execute ← 499 (F4 L2) ; self ← `VariableGet_268` ; NewLocation ← 502 `Safe` (F4 L16) ; NewRotation ← 469 ; bSweep
`false`.
**Aucune divergence.** Chaîne obtenue pour `DownUp` : CE8 → Branche 100 (`GetViewTarget(PC) == CamRoom`).
- Vrai : `Définir C2Height` (71) := `Clamp(C2Height + AxisValue × 3, −40, 150)`, puis fin.
- Faux : `IfThenElse_8`, chaîne d'origine inchangée.
### 1.7 Compilation
`compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true}` a renvoyé
`{"returnValue":null}`, sans exception : c'est le critère de la fiche.
Deux contrôles n'ont pas été faits :
- `hyper_blueprint_debug_inspect`, absent du serveur le 2026-10-10 (mémoire `unreal-mcp-reconnect`, non revérifié) ;
- le journal de l'éditeur, faute du chemin `-abslog` : il n'est pas dans `Saved\Logs`, dont la fin de la liste ne montre
que `UnrealPak*.log`, `cef3.log` et `portage`.
### 1.8 Enregistrement
`editor_toolset.toolsets.asset.AssetTools` / `save_assets {"asset_paths":["/Game/BPs/Room"]}` a renvoyé
`{"returnValue":true}`. Il a été appelé une seule fois, pour Room seul, après la compilation stricte.
## 2. Écarts à la SPEC
- **Aucun écart de câblage.** La ligne B2b-6 (`CAM-SPEC.md:380`) et la fiche (`CAM-B2b-FICHES.md:137`) sont réalisées
telles quelles pour `DownUp`. Seul le lien CE8.then → `IfThenElse_8` a été coupé.
- **Formule** : `C2Height := Clamp(C2Height + AxisValue × 3, −40, 150)`. A de PO36 = C2Height, B = produit, relu.
- **Typage** : `AxisValue` est en double précision, comme `Clamp(Float)` : aucune conversion implicite.
- **Relecture** : la liste de la fiche (l. 139) a été élargie, à la demande de l'orchestrateur, à 499, 505,
`VariableSet_66`, CE49 et 494, plus `IfThenElse_8` et PO8.
- **Garde `Accessed None`** : aucune `IsValid` ajoutée, car ni la fiche ni la SPEC n'en demandent, et les nœuds neufs ne
lisent que des flottants. PO8 (→ 439 `GetViewTarget`, self ← `VariableGet_262` ; → `VariableGet_268` `CamRoom`) est
désormais évalué à chaque appel de `DownUp`. Même réserve qu'en F6a et F6b. PO8 alimente maintenant 6 branches.
- **Comportement** : quand C2 n'est pas la vue, il est identique à l'ancien (`else` → `IfThenElse_8`). Quand C2 est la
vue, la chaîne d'origine **n'est plus exécutée** : c'est le changement voulu par la SPEC.
- **Positions** : choisies près de CE8, sans prescription (cosmétique). Elles chevauchent visuellement les nœuds
`CircleCamera` de F6a (x 5650-6050, y 4250-4450), sans effet sur le graphe.
## 3. Instantané
L'orchestrateur l'a fait avant F1 (`D:\Game\LifePlay_Backup_5.5\CAM-B2b\`) ; il n'a pas été refait, selon la consigne.
Mesures avant et après `save_assets` :
```
date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; grep -i "Room.uasset" "/d/Game/LifePlay_Backup_5.5/CAM-B2b/manifest.csv"; git -C "D:\Game\LifePlay-master 5.8" status --short -- Content Source Config
2026-10-13T16:10:25+02:00
a5fa970e821dc2d0d9bf220fd43b9de6 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
"Content\BPs\Room.uasset","True","6060745","A5FA970E821DC2D0D9BF220FD43B9DE6","2026-10-13T15:25:45"
M Content/BPs/Person.uasset
M Content/BPs/Room.uasset
M Source/LifePlay/LPDebugSubsystem.cpp
M Source/LifePlay/LPDebugSubsystem.h
```
(save_assets → true)
```
date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; stat -c '%s %y' "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; git -C "D:\Game\LifePlay-master 5.8" status --short -- Content Source Config; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset"
2026-10-13T16:10:34+02:00
e0517c63541f249bd9dbda5c39815ba5 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
6318319 2026-10-13 16:10:30.045597500 +0200
M Content/BPs/Person.uasset
M Content/BPs/Room.uasset
M Source/LifePlay/LPDebugSubsystem.cpp
M Source/LifePlay/LPDebugSubsystem.h
b239e1035cb028b78fbd4bb9f0c5f70a */d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset
stat -c '%n %s %y' ".../Content/BPs/Person.uasset" ".../Content/BPs/Room.uasset"
/d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset 20808711 2026-10-13 14:35:14.930487700 +0200
/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset 6318319 2026-10-13 16:10:30.045597500 +0200
```
Lecture des mesures :
- Le MD5 de Room diffère de l'instantané `A5FA970E` : l'enregistrement a bien eu lieu.
- `Person.uasset` date de 14:35:14, avant B2b : ce n'est pas l'enregistrement de F7. `LPDebugSubsystem.*` viennent
d'autres lots ; aucun n'a été touché.
- Room figurait **déjà** en `M` avant l'enregistrement, avec le MD5 de l'instantané. L'instantané de 15:25 diffère donc
déjà de HEAD. Estimé : enregistrement d'un lot antérieur (B2) non encore commité. À trancher par le gardien avant le
commit.
Retour arrière, sur décision de l'orchestrateur et jamais lancé ici :
1. fermer l'éditeur ;
2. `.\Tools\Snapshot.ps1 -Lot CAM-B2b-retour -Path Content\BPs\Room.uasset` ;
3. `.\Tools\Restore.ps1 -Lot CAM-B2b -Only Content\BPs\Room.uasset -WhatIf`, puis sans `-WhatIf`.
Retour attendu à l'octet : MD5 `A5FA970E…`, 6 060 745 octets.
## 4. Ce qui n'a pas été fait
- `hyper_blueprint_debug_inspect` et journal de l'éditeur : non consultés (§ 1.7).
- `CompileAllBlueprints`, éditeur fermé : il revient à l'orchestrateur (`lp-mcp` § 1, règle 3).
- `IfThenElse_96`, `_97`, `_98`, `_99` n'ont pas été relus ici. Leurs `else` → `IfThenElse_26`, `_9`, `_10`, `_25` sont
établis par les relectures finales de F6a et F6b, pas par celle de F7. `CallFunction_473` (Start ← 502, F4 L17) n'a pas
été relu non plus.
- Comportement à l'exécution : non mesuré (aucune PIE, aucun build), seulement déduit du câblage.
- Preuve en jeu (build empaqueté + `LogStats`), V5 et V6 : hors rôle ; c'est la suite de l'orchestrateur.
- Fermeture de l'éditeur : non faite, selon la consigne. PID 10708 reste ouvert.
## 5. Points à confirmer en VAL
1. **Vue autre que C2** : `DownUp` prend `else` → `IfThenElse_8`, avec la chaîne d'origine inchangée (Condition ←
CABO_17, then → `IfThenElse_17`). Établi par le câblage ; à confirmer en jeu (hauteur de l'ancienne caméra identique).
2. **Vue C2** : `DownUp` ajoute `AxisValue × 3` à `C2Height`, borné à [−40, 150]. L'effet passe par `ComputeC2Target`
(F3) au prochain appel. Signe et gain sont à juger à l'essai.
3. **`LogStats`** : aucun `Accessed None` nouveau sur `CallFunction_439` / `VariableGet_262`. PO8 est évalué à chaque
`DownUp`, probablement à chaque image tant que l'axe est actif (estimé, appelant non lu).
4. **Cas limite (estimé, déjà noté en F6a et F6b)** : si `GetViewTarget` et `CamRoom` valent tous deux `None`, PO8 vaut
`true`, et `DownUp` écrit `C2Height` au lieu de piloter l'ancienne caméra.
5. **V5 (`CAM-SPEC.md:382`)** : pour les 5 événements, `then` → nouvelle branche et `else` → branche d'origine :
- CE1 → 96, `else` → 26 ;
- CE4 → 97, `else` → 9 ;
- CE19 → 99, `else` → 25 ;
- CE8 → 100, `else` → 8 ;
- CE55 → 98, `else` → 10.
Également : `SphereTraceSingle` 3 (459, 499, 505), `Vector_BoundedToBox` ≥ 4, `ComputeRoomBox` 1.
6. **Valeurs sérialisées**, à relire par dump (`LPDumpFunction`) sur le fichier enregistré :
- PO35.B = 3.0 ; 511 Min −40.0, Max 150.0 ;
- celles de F1 à F6b, déjà listées dans leurs IMPL (par exemple PO31.B 15.0, 509 [−75, 125], PO33.B 3.0,
510 [−40, 150], Radius 20 de 459, 499 et 505).
7. **Compilation** : jugée sur la seule absence d'exception de `compile_blueprint {warnings_as_errors:true}`. À
recontrôler par `CompileAllBlueprints`, éditeur fermé, avec 0 erreur et 0 avertissement nouveau sur Room.
8. **État git de Room** : il était déjà `M` avant F7 (§ 3). Le gardien doit établir ce que contient le diff par rapport à
HEAD (B2 et B2b ?) avant le commit.
## 6. Fichiers à commiter
Dépôt du jeu, branche `ue5.8` :
- `A Docs/Migration/etat/CAM/CAM-B2b-IMPL-F7.md`
- `M Content/BPs/Room.uasset`, après VAL (V5, V6), avec la réserve du point 8 du § 5.
`Content/BPs/Person.uasset` et `Source/LifePlay/LPDebugSubsystem.*` n'appartiennent pas à ce lot : ne pas les joindre.
Tours : 30 / 50 (ce Write et la remise du compte rendu compris ; 24 appels MCP, un par message)
## Constats
- Le schéma F6b se rejoue tel quel pour `DownUp` : 6 créations, 1 coupure, 9 liaisons et 3 valeurs, plus 2 lectures
groupées, soit 21 appels pour l'événement.
- Les index de broches sont identiques à F6b (Branche, `Clamp(Float)`, `|SetC2Height`) ; `|GetC2Height` → out 0.
- Dans le graphe, les numéros des nœuds créés suivent ceux de F6b : `IfThenElse_100`, `VariableGet_296`,
`PromotableOperator_35` et `_36`, `CallFunction_511`, `VariableSet_71`.
- `save_assets` sur `/Game/BPs/Room` renvoie `{"returnValue":true}` (booléen) ; les écritures de graphe renvoient
`null`.
- À verser : lp-mcp § 3 — `save_assets {asset_paths:["/Game/BPs/X"]}` (toolset `editor_toolset.toolsets.asset.AssetTools`)
renvoie `true`. Avant d'enregistrer, mesurer le MD5 et `git status` : un asset peut déjà être `M` du fait d'un lot
antérieur, ce qui change ce que le gardien commitera.