Fiche CAM-B2b-F4 — 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-F4-IMPL : trajectoire protégée de la poursuite (B2b-4) (2026-10-13) Consignes : `CAM-B2b-FICHES.md` § « Rubriques communes » (l. 23-63) et § « F4 » (l. 106-113) ; SPEC `CAM-SPEC.md` § « CAM-B2b », ligne B2b-4 (l. 378). UE-GO / BUILD-GO de Big Boss du 2026-10-13 (cité par l'orchestrateur). Éditeur headless PID 10708, port 8000, connexion `unreal-mcp` partagée (MCPA-T1) ; Room chargé avec F1 à F3 en mémoire. Un seul `call_tool` par message ; 33 appels MCP au total (6 `create_node`, 3 `break_pins`, 17 `connect_pins`, 3 `set_pin_value`, 3 `get_node_infos`, 1 `compile_blueprint`). Aucun `delete_node`, `save_assets`, `write_graph_dsl` ni `ProgrammaticToolset`. ## 1. Fichiers modifiés - `Content/BPs/Room.uasset` : modifié **en mémoire de l'éditeur seulement**, non enregistré (pas de `save_assets` avant F7). Preuve : MD5 disque avant et après F4 = MD5 de l'instantané (§ 3). - Graphe `/Game/BPs/Room.Room:EventGraph`, zone de la poursuite (`UpdateRoomCam`, 467 en 31050, 5100) ; nœuds neufs en x 31450-33050, y 4500-6350. ### 1.1 Lecture avant écriture (`get_node_infos` groupé, 8 nœuds existants) Conforme à la fiche : `CallFunction_467` (`|ComputeC2Target`, execute ← `IfThenElse_93`.1) then 0 → `474.execute` 0 ; `CallFunction_468` (`Math|Interpolation|VInterpTo`, Current ← 471, Target ← `VariableGet_280`, DeltaTime ← 470, InterpSpeed 8.0) sortie 0 → `473.Start` 0 et → `474.NewLocation` 2 ; `CallFunction_474` (`Transformation|SetActorLocationAndRotation`, self ← `VariableGet_268`, NewRotation ← 469, bSweep false) ; `CallFunction_473` (`FindLookAtRotation`, Target ← 455, sortie → 469.1) ; `CallFunction_455` (`GetSocketLocation`, self ← `VariableGet_264`, `head`) sortie 0 → 457, 459, 464, 473 ; `MakeArray_5` ([0] ← 261, [1] ← 268) → 459.6 ; `VariableGet_286` (`|GetC2BoxMin`) → 495.1, 496.1 ; `VariableGet_287` (`|GetC2BoxMax`) → 495.2, 496.2. ### 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 | |---|---|---|---|---| | **Trace** de la poursuite | `K2Node_CallFunction_499` | `Collision\|SphereTraceByChannel` → idem | execute 0, Start 1, End 2, Radius 3, TraceChannel 4, bTraceComplex 5, ActorsToIgnore 6, DrawDebugType 7, bIgnoreSelf 8, TraceColor 9, TraceHitColor 10, DrawTime 11 ; then 0, OutHit 1, ReturnValue 2 | 31450, 4500 | | `BreakHitResult` | `K2Node_CallFunction_500` | `Collision\|BreakHitResult` → idem | Hit 0 ; Location out 4, Normal out 6 (18 sorties) | 31900, 6100 | | Normal × 20 | `K2Node_PromotableOperator_20` | `Utilitaires\|Opérateurs\|Multiplier` → `Math\|Vecteur\|vector*vector` (après liaison A) | A 0, B 1 ; out 0 | 32250, 6350 | | Location + (Normal × 20) | `K2Node_PromotableOperator_21` | `Utilitaires\|Opérateurs\|Ajouter` → `Math\|Vecteur\|vector+vector` (après liaison A) | A 0, B 1 ; out 0 | 32500, 6150 | | `SelectVector` | `K2Node_CallFunction_501` | `Math\|Vecteur\|SelectVector` → idem | A 0, B 1, bPickA 2 ; out 0 | 32750, 5950 | | **`Safe`** = borne | `K2Node_CallFunction_502` | `Math\|Vecteur\|VectorBoundedtoBox` → `Math\|Vecteur\|Vector_BoundedToBox` | InVect 0, InBoxMin 1, InBoxMax 2 ; out 0 | 33050, 5950 | Les deux opérateurs ont à la création le `type_id` trompeur déjà vu en F3 (`Utilitaires|Gestiondutemps|Seconds*FrameRate`, `…|FrameNumber+Int`) et des broches `Caractère de remplacement` ; relus typés `Vector` à la relecture finale. Réutilisés sans copie (nœuds purs) : 455, 468, `MakeArray_5`, `VariableGet_286`, `VariableGet_287`. ### 1.3 Liens coupés (`break_pins`, 3, tous `{"returnValue":null}`) | # | Sortie | Entrée | Point de la fiche | |---|---|---|---| | C1 | CallFunction_467.then 0 | CallFunction_474.execute 0 | 1 | | C2 | CallFunction_468.ReturnValue 0 | CallFunction_474.NewLocation 2 | 3 | | C3 | CallFunction_468.ReturnValue 0 | CallFunction_473.Start 0 | 3 | ### 1.4 Liens faits (`connect_pins`, 17, tous `{"returnValue":null}`) | # | Sortie | Entrée | |---|---|---| | L1 | CallFunction_467.then 0 | CallFunction_499.execute 0 | | L2 | CallFunction_499.then 0 | CallFunction_474.execute 0 | | L3 | CallFunction_455.0 (tête) | CallFunction_499.Start 1 | | L4 | CallFunction_468.0 (`VInterpTo`) | CallFunction_499.End 2 | | L5 | MakeArray_5.0 ([MC, CamRoom]) | CallFunction_499.ActorsToIgnore 6 | | L6 | CallFunction_499.OutHit 1 | CallFunction_500.Hit 0 | | L7 | CallFunction_500.Normal 6 | PromotableOperator_20.A 0 | | L8 | CallFunction_500.Location 4 | PromotableOperator_21.A 0 | | L9 | PromotableOperator_20.0 | PromotableOperator_21.B 1 | | L10 | PromotableOperator_21.0 | CallFunction_501.A 0 | | L11 | CallFunction_468.0 | CallFunction_501.B 1 | | L12 | CallFunction_499.ReturnValue 2 | CallFunction_501.bPickA 2 | | L13 | CallFunction_501.0 | CallFunction_502.InVect 0 | | L14 | VariableGet_286.0 (`C2BoxMin`) | CallFunction_502.InBoxMin 1 | | L15 | VariableGet_287.0 (`C2BoxMax`) | CallFunction_502.InBoxMax 2 | | L16 | CallFunction_502.0 (`Safe`) | CallFunction_474.NewLocation 2 | | L17 | CallFunction_502.0 (`Safe`) | CallFunction_473.Start 0 | ### 1.5 Valeurs (`set_pin_value`, 3, toutes `{"returnValue":null}`, relues) | Broche | Avant (relu à la création) | Après (relu) | |---|---|---| | CallFunction_499.Radius 3 | `0.0` | `20.0` | | CallFunction_499.TraceChannel 4 | `TraceTypeQuery1` | `TraceTypeQuery2` | | PromotableOperator_20.B 1 | (neuf, `""`) | `20.0,20.0,20.0` | Valeurs non prescrites de 499, laissées par défaut et relues **identiques à 459** (trace de `ComputeC2Target`) : bTraceComplex `false`, DrawDebugType `None`, bIgnoreSelf `true`, TraceColor `(R=1,G=0,B=0,A=1)`, TraceHitColor `(R=0,G=1,B=0,A=1)`, DrawTime `5.000000`. ### 1.6 Relecture et chaîne obtenue `get_node_infos` final groupé sur les 14 nœuds touchés ou réutilisés (6 créés + 467, 468, 474, 473, 455, MakeArray_5, 286, 287) : les 3 coupures sont absentes des deux côtés (467.then ne sort plus que vers 499 ; 468.0 ne sort plus que vers 499.End et 501.B ; 474.NewLocation et 473.Start ne reçoivent plus que 502) ; les 17 liens apparaissent des deux côtés ; les 3 valeurs relues ; types : vecteurs `Vector`, `bPickA` `Booléen`, `Radius` `Float (simple précision)`. Liens non touchés relus intacts : 467.execute ← IfThenElse_93.1 ; 468 ← 471, 280, 470 (InterpSpeed 8.0) ; 474.self ← 268, 474.NewRotation ← 469, bSweep `false` ; 473.Target ← 455, 473.0 → 469.1 ; 455 → 457, 459, 464, 473 (plus 499) ; MakeArray_5 → 459.6 (plus 499.6), [0] ← 261, [1] ← 268 ; 286 / 287 → 495, 496 (plus 502). Aucune divergence. Chaîne de la poursuite : `IfThenElse_93.else` → `ComputeC2Target` (467 ; recalcule la boîte par `ComputeRoomBox`, F3) → `SphereTraceByChannel(tête → VInterpTo, r 20, TraceTypeQuery2, ignorer [MC, CamRoom] + self)` (499) → `SetActorLocationAndRotation(CamRoom, Safe, LookAt(Safe → tête))` (474), avec `Safe = Bound(touché ? Location + Normal × 20 : VInterpTo, C2BoxMin, C2BoxMax)`. ### 1.7 Compilation `compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true}` → `{"returnValue":null}`, aucune exception (critère de la rubrique « Fin de chaque fiche »). `hyper_blueprint_debug_inspect` non appelé (absent du serveur le 2026-10-10, mémoire `unreal-mcp-reconnect` ; non revérifié) ; journal de l'éditeur non lu (chemin `-abslog` non fourni). Pas de `save_assets` (F7). ## 2. Écarts à la SPEC - Aucun écart de câblage : la ligne B2b-4 (`CAM-SPEC.md:378`) et la fiche F4 (`CAM-B2b-FICHES.md:106-113`) sont réalisées telles quelles, points 1 à 3 dans l'ordre ; aucun nœud hors fiche touché. - `Hit.Normal × 20` (SPEC, scalaire) est réalisé par `vector*vector` avec B = (20,20,20) (fiche) : équivalent composante par composante. - « Location » = sortie `Location` (4) de `BreakHitResult`, centre de la sphère au contact, et non `ImpactPoint` (5) : comme `460.Location → PO7.A` dans `ComputeC2Target` (lu en F3). - Garde `Accessed None` : aucune `IsValid` ajoutée (aucune demandée). Les nœuds neufs ne lisent aucune référence d'objet (vecteurs, structure de touche, booléen). `SelectVector` est pur et évalue A et B : ni l'un ni l'autre ne déréférence d'objet, donc pas de cascade de branches requise. En revanche, 455 (`GetSocketLocation`, self ← `VariableGet_264`) est désormais évalué une fois de plus par passage (499.Start, en plus de 473.Target) ; si sa cible est nulle, un `Accessed None` déjà présent serait compté deux fois (point 3 du § 5). - Ordre des écritures : `set_pin_value` de PO20.B posé juste après la liaison A (typage), comme en F3 ; typage et valeur confirmés à la relecture finale (§ 1.6). - Positions des nœuds : choisies hors des nœuds existants (cosmétique, non prescrites). ## 3. Instantané Fait par l'orchestrateur avant F1 (non refait, consigne). Manifeste relu : ``` grep -i "Room.uasset" "/d/Game/LifePlay_Backup_5.5/CAM-B2b/manifest.csv" "Content\BPs\Room.uasset","True","6060745","A5FA970E821DC2D0D9BF220FD43B9DE6","2026-10-13T15:25:45" ``` Disque mesuré : ``` md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset" a5fa970e821dc2d0d9bf220fd43b9de6 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset (2026-10-13T15:47:51+02:00, avant F4) a5fa970e821dc2d0d9bf220fd43b9de6 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset (2026-10-13T15:50:09+02:00, après F4) ``` = instantané : rien d'enregistré. Retour arrière avant F7 : fermer l'éditeur sans enregistrer (perd F1 à F4) ; après F7 : `Tools\Restore.ps1 -Lot CAM-B2b -Only Content\BPs\Room.uasset`, éditeur fermé, sur décision de l'orchestrateur (jamais lancé ici). ## 4. Ce qui n'a pas été fait - F5 et suivantes (repli vers le haut, pilotage, enregistrement) : hors périmètre. - `hyper_blueprint_debug_inspect`, journal de l'éditeur : non consultés (§ 1.7). - `save_assets` : interdit avant F7 ; F1 à F4 vivent en mémoire de l'éditeur PID 10708 (une fermeture ou un plantage avant F7 les perd ; l'instantané reste intact). - Comportement à l'exécution : non mesuré (aucune PIE, aucun build) ; seulement déduit du câblage. - Preuve en jeu (build empaqueté + `LogStats`) : hors rôle. ## 5. Points à confirmer en VAL 1. Cas « pas de touche, cible dans la boîte » : `Safe = 468` (estimé par le câblage), donc trajectoire identique à B2. Hypothèse : 468 (`VInterpTo`, pur) rend la même valeur à ses trois évaluations du passage (499.End, 501.B via 474 et 473) car Current (471), Target (280) et DeltaTime (470) ne changent pas entre la trace et 474 (estimé). 2. Changement voulu : touche → `Location + Normal × 20` ; toujours borné par `C2BoxMin` / `C2BoxMax`. À confirmer en jeu : la caméra de poursuite ne sort jamais de la pièce et ne passe pas derrière un obstacle du canal Camera (`TraceTypeQuery2`, à rapprocher du canal Camera de `DefaultEngine.ini`, non relu ici). 3. `LogStats` : aucun `Accessed None` nouveau ; en particulier sur `VariableGet_264` (cible de 455), désormais lu deux fois par passage de la poursuite. 4. Boîte valide au moment de la trace : `ComputeRoomBox` est appelé par `ComputeC2Target` (467) juste avant 499 (F3) ; une boîte dégénérée (min > max sur un axe) ferait renvoyer une borne fixe par `Vector_BoundedToBox` (incertain : non lu dans le moteur ; relève de F1-F2). 5. Valeurs sérialisées après `save_assets` (F7) : `499.Radius = 20`, `499.TraceChannel = TraceTypeQuery2`, `PO20.B = (20,20,20)`, par dump (`LPDumpFunction`). 6. Compilation stricte jugée sur l'absence d'exception seulement ; recontrôle en F7 et par `CompileAllBlueprints`. 7. V5 (`CAM-SPEC.md:382`) : `SphereTraceSingle` attendu 3 après B2b ; F4 en ajoute 1 (459 + 499 + celle de F5 prévue). ## 6. Fichiers à commiter - `A Docs/Migration/etat/CAM/CAM-B2b-IMPL-F4.md` (dépôt du jeu, branche `ue5.8`). - `Content/BPs/Room.uasset` : rien à commiter pour F4 (non enregistré) ; il le sera après F7 et la VAL. Tours : 39 / 60 ## Constats - `Collision|SphereTraceByChannel` neuf : Radius `0.0`, TraceChannel `TraceTypeQuery1` par défaut ; les autres entrées par défaut sont celles de 459 (bIgnoreSelf `true`, DrawDebugType `None`). - `Collision|BreakHitResult` : Hit 0 ; sorties bBlockingHit 0, bInitialOverlap 1, Time 2, Distance 3, Location 4, ImpactPoint 5, Normal 6, ImpactNormal 7, PhysMat 8, HitActor 9, HitComponent 10, HitBoneName 11, BoneName 12, HitItem 13, ElementIndex 14, FaceIndex 15, TraceStart 16, TraceEnd 17. - `Math|Vecteur|SelectVector` : A 0, B 1, bPickA 2 (défaut `false`) ; out 0. - `Utilitaires|Opérateurs|Ajouter` relié en A à un vecteur devient `Math|Vecteur|vector+vector`. - Économie : créer tous les nœuds, une lecture groupée (6 neufs + 459 pour comparer les défauts), câbler et poser les valeurs, une relecture finale (14 nœuds) : 33 appels pour 6 nœuds, 3 coupures, 17 liens, 3 valeurs.