483 lines
45 KiB
Markdown
483 lines
45 KiB
Markdown
|
|
Fiche STE-H5b — Niveau N3 — Modèle prévu (modèle de l'agent lp-cart) | utilisé claude-opus-5-5 — Agent lp-cart — Skills sk-protocol, ue-cpp-build, lp-commandlets, lp-paths (préchargés), lp-mcp (lu, § 1-4), lp-doc (gabarits)
|
|||
|
|
# STE-H5b-SPEC : reposer la variable de scène `AI` avant chaque routine d'IA (correctif F-a) (2026-10-10)
|
|||
|
|
|
|||
|
|
Lecture seule stricte (aucun éditeur, commandlet, build ni écriture hors de ce fichier). Gabarit SPEC :
|
|||
|
|
`D:\Game\LifePlay-master 5.8\.claude\skills\lp-doc\gabarits.md:36-38`. Décision de l'utilisateur : Q-H5-1 = « Corriger (F-a) »
|
|||
|
|
(`STE-TACHES.md:23`). **Statut : complet, sous un préalable** : le dump 5.8 d'`AddVariable` avec broches d'exécution
|
|||
|
|
(P3, commande au § 2.0) n'existe pas. Le reste de la SPEC est exécutable ; P3 contrôle 3 points (A1-A3) avant l'IMPL.
|
|||
|
|
|
|||
|
|
Abréviations (chemins absolus) :
|
|||
|
|
- **EG** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\dumps\GI__EventGraph_apres-GARDES.txt` (`-Fn=EventGraph -Pins -Exec`,
|
|||
|
|
17:43:39, après les gardes, EG:1-4 ; 6233 nœuds, EG:120) ;
|
|||
|
|
- **L6f** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\portage55\L6f\L6f_dump_GI.txt` (GI **5.5** du 2026-09-30, `-Fn=* -Pins`,
|
|||
|
|
**sans** broches d'exécution : le flux de données seulement, `L6f_LPDumpFunction_20260930-102202.txt:1`) ;
|
|||
|
|
- **L2** = `D:\Game\LifePlay_Build_5.8_STB-JEU2_Development\Windows\LifePlay\Saved\Logs\LifePlay.log` (avant les gardes) ;
|
|||
|
|
- **L3** = `D:\Game\LifePlay_Build_5.8_STF-STE-LISIB_Development\Windows\LifePlay\Saved\Logs\LifePlay.log` (J3, état actuel B8B97C46) ;
|
|||
|
|
- **H5D** = `STE-H5-DIAG.md`, **H3S** = `STE-H3-SPEC.md`, **H3bS** = `STE-H3b-SPEC.md`, **GIMPL** = `STE-GARDES-IMPL.md`
|
|||
|
|
(dossier `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\`) ;
|
|||
|
|
- **UE** = `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\` (moteur installé, lu).
|
|||
|
|
|
|||
|
|
État du Blueprint (rejoué) : `md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/GI.uasset"` → `b8b97c46b825a6af942baabd5bf74c56` ;
|
|||
|
|
`git status --short -- Content/BPs/GI.uasset` → vide ; dernier commit du fichier `5c55db4` (STE-R5). EG a été pris après
|
|||
|
|
l'IMPL des gardes (GIMPL § 1 : B8B97C46 à 17:33), donc **EG décrit le GI actuel** (établi).
|
|||
|
|
|
|||
|
|
## 1. Objectif mesurable
|
|||
|
|
|
|||
|
|
Que chaque `PlayScene` d'un fichier `.lpai` lancé par la boucle d'IA (`MacroInstance_45`) soit précédé, **dans la même
|
|||
|
|
exécution synchrone**, de `AddVariable('AI', [AIDummy])`. Un vidage de `Variables` par `UpdateCheckForScenes`
|
|||
|
|
(`CallArrayFunction_5412`, EG:6616-6623) ne peut se produire qu'à une reprise latente (`Delay(0)` de la macro `Async`,
|
|||
|
|
§ 6) ; il ne peut donc plus tomber entre la pose de `AI` et la routine qui la lit.
|
|||
|
|
|
|||
|
|
Mesures :
|
|||
|
|
- statique : EG après IMPL = 6234 nœuds (+1), liens du § 2.4 relus, compilation sans diagnostic (§ 7, V1-V3) ;
|
|||
|
|
- en jeu (J4, même parcours que J3 plus une rafale) : `hours by 0` = 0 ; routines valides en hausse par rapport à J3
|
|||
|
|
(ratios du § 7, V5) ; aucune famille d'avertissements nouvelle.
|
|||
|
|
|
|||
|
|
Volume : **1 nœud créé, 1 `break_pins`, 6 `connect_pins`, 2 `set_pin_value` (+1 relecture), 0 suppression**, un seul
|
|||
|
|
graphe (`EventGraph`).
|
|||
|
|
|
|||
|
|
Ce que l'utilisateur verra (effet accepté par Q-H5-1) :
|
|||
|
|
- en temps accéléré ou après un déplacement (rafale de routines), la routine d'un PNJ (repas, travail, sortie…)
|
|||
|
|
s'applique à ce PNJ : il change de lieu (`setCurrentLocation`) et reçoit sa prochaine échéance (`AI_Untils[slot]`) ;
|
|||
|
|
aujourd'hui, une partie de ces routines tourne « à vide » (sans personne, effets masqués par les gardes G4-G10) et le
|
|||
|
|
PNJ est repris à la seconde suivante ;
|
|||
|
|
- moins de routines jouées par PNJ : la boucle s'arrête au premier `.lpai` qui pose une échéance future (condition C de
|
|||
|
|
`BooleanAND_65`, EG:24880-24887, 24792-24796) ;
|
|||
|
|
- les compteurs du PNJ (`ActorVars`, ex. `entertainment_today`, `entertainment.lpai:19-20`) avancent au bon moment, ce qui
|
|||
|
|
évite des répétitions ; on croise plus souvent un PNJ au lieu où sa routine l'envoie ;
|
|||
|
|
- journal : plus de lignes `(test0104) for … hours by <n>` et `[AI] <n> moves to …` pendant les rafales ;
|
|||
|
|
- rien d'autre : scènes du joueur, interface, PNJ hors routine inchangés (§ 4, R5).
|
|||
|
|
|
|||
|
|
## 2. Fichiers touchés, ordre
|
|||
|
|
|
|||
|
|
Un seul fichier : `Content\BPs\GI.uasset`, graphe `EventGraph` (événement `UpdateAI`, corps de `MacroInstance_45`). Ni
|
|||
|
|
`UpdateCheckForScenes`, ni `CallFunction_739` / `_779`, ni les fonctions `AddVariable` / `CallFunction`, ni les gardes
|
|||
|
|
G1-G11, ni le C++, ni le contenu des modules.
|
|||
|
|
|
|||
|
|
### 2.0 Préalables (dans l'ordre, avant tout `create_node`)
|
|||
|
|
- **P1.** Décision Q-H5-1 = F-a : acquise (`STE-TACHES.md:23`). Q-H5b-1 (preuve, § 4) : à demander avant J4, pas avant l'IMPL.
|
|||
|
|
- **P2.** MD5 de `GI.uasset` = `b8b97c46…` et `git status` vide sur ce fichier. Sinon : nouveau dump
|
|||
|
|
`.\Tools\RunCommandlet.ps1 -Lot STE-H5b -Commandlet LPDumpFunction -Args '-BP=GI -Fn=EventGraph -Pins -Exec'` et
|
|||
|
|
comparaison des nœuds du tableau 2.2 ; tout écart arrête l'IMPL.
|
|||
|
|
- **P3 (à compléter, bloquant)** — dump 5.8 d'`AddVariable` avec flux d'exécution, éditeur fermé, BUILD-GO de Big Boss
|
|||
|
|
(une session à la fois sur la copie UE, `STE-TACHES.md:41-45`) :
|
|||
|
|
`.\Tools\RunCommandlet.ps1 -Lot STE-H5b -Commandlet LPDumpFunction -Args '-BP=GI -Fn=AddVariable -Pins -Exec'`
|
|||
|
|
(≈ 1 min, comme EG:3). Le flux de données 5.5 est lu dans L6f:10070-10273 ; le dump 5.8 doit confirmer :
|
|||
|
|
- **A1** remplacement par nom : boucle sur `Variables` (`MacroInstance_2707`, L6f:10098-10101), égalité **sans casse**
|
|||
|
|
du nom (`EqualEqual_StriStri` `CallFunction_34376`, L6f:10112-10118) → `Array_Set` à l'indice trouvé
|
|||
|
|
(`CallArrayFunction_4346`, L6f:10119-10124) et `Replaced = true` (L6f:10139-10145) ; sinon `Array_Add` à la fin
|
|||
|
|
(`CallArrayFunction_3373`, L6f:10083-10087). Attendu : jamais deux entrées `AI` ;
|
|||
|
|
- **A2** la branche `destroyDummy` (`CallFunction_0`, L6f:10212-10215) n'est atteinte que pour l'entrée remplacée, si
|
|||
|
|
elle est valide (`MacroInstance_1960` IsValid, L6f:10168-10169), si `SaveSlot == 0` (`IfThenElse_4367`,
|
|||
|
|
L6f:10170-10179) et si le OU `BooleanOR_236` est faux (ancienne = joueur, non cachée, **ancienne = nouvelle**
|
|||
|
|
`CallFunction_1`, a des `SavedClothes` ; L6f:10188-10224, 10262-10273) ;
|
|||
|
|
- **A3** le `SwitchString_75` sur `Name` (L6f:10149-10151, cas `OptionChosen…` → `VariableSet_14216`) laisse `AI` sur le
|
|||
|
|
chemin par défaut (même chemin que `CallFunction_739` aujourd'hui).
|
|||
|
|
Un point non confirmé → arrêt, rapport à l'orchestrateur, pas d'IMPL. Facultatif (non bloquant) :
|
|||
|
|
`... -Args '-BP=GI -Fn=Async -Pins -Exec'` (la macro est un graphe de `GetAllGraphs`, donc trouvée par `-Fn`, § 6).
|
|||
|
|
- **P4.** Gel annoncé à toutes les sessions (lp-mcp § 1.7) ; instantané
|
|||
|
|
`.\Tools\Snapshot.ps1 -Lot STE-H5b -Path Content\BPs\GI.uasset` (paramètres `-Lot`, `-Path` obligatoires,
|
|||
|
|
`Tools\Snapshot.ps1:17-22`) ; tag d'entrée `STE-H5b-avant` par le gardien si l'orchestrateur le souhaite ; éditeur
|
|||
|
|
headless (lp-mcp § 2) ; `AssetTools.load_asset` `/Game/BPs/GI`.
|
|||
|
|
- **P5.** `get_node_infos {nodes:[{refPath:"/Game/BPs/GI.GI:EventGraph.K2Node_<Nom>"}]}` pour chaque nœud du tableau 2.2 :
|
|||
|
|
les liens doivent égaler la colonne « avant » ; relever au passage la position (`pos`) de `IfThenElse_148` et de
|
|||
|
|
`CallFunction_743`, et les indices de broches.
|
|||
|
|
- **P6.** `find_node_types {graph:{refPath:"/Game/BPs/GI.GI:EventGraph"}, type_id_filter:"AddVariable", context_pins:[]}` :
|
|||
|
|
attendu `Scène|AddVariable` (relu en STE-H3b pour `CallFunction_360`, GIMPL:33, 39). Autre résultat → arrêt.
|
|||
|
|
|
|||
|
|
### 2.0 bis P3 contrôlé sur le dump 5.8 — complément A-CART pour l'orchestrateur LOT STE (2026-10-10, après STE-D5)
|
|||
|
|
|
|||
|
|
Auteur : agent lp-cart (A-CART), claude-opus-5-5, skills sk-protocol, ue-cpp-build, lp-commandlets, lp-paths ; lecture
|
|||
|
|
seule, seul ce fichier et `STE-H3c-SPEC.md` écrits. Dumps (sous `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\dumps\`) :
|
|||
|
|
**AV** = `GI__AddVariable_D5.txt` (`-BP=GI -Fn=AddVariable -Pins -Exec`, lot STE-D5, 20:51:17, code 0,
|
|||
|
|
`=== GI :: AddVariable (49 noeuds) ===` AV:120 ; le bloc AV:367-416 est l'écho tronqué du même dump) ; **AS** =
|
|||
|
|
`GI__Async_D5.txt` (`-Fn=Async`, 20:52:03, code 0, 20 nœuds, AS:120 ; écho AS:210-259). `GI.uasset` : dernière écriture
|
|||
|
|
17:33:20 (`stat -c '%y %s'` → `2026-10-10 17:33:20 … 52540467`), MD5 `b8b97c46…`, `git status` vide : AV et AS décrivent
|
|||
|
|
l'état B8B97C46 (établi). Tous les nœuds cités par P3 existent dans AV sous le même nom que dans L6f.
|
|||
|
|
**P3 est satisfait : A1, A2 et A3 sont confirmés ; la SPEC est exécutable** (P1, P2, P4-P6 restent à faire à l'IMPL).
|
|||
|
|
|
|||
|
|
- **A1 confirmé** (établi, sauf la sortie de `Break`, estimée forte). Entrée `FunctionEntry_3606.then` → `SwitchString_75`
|
|||
|
|
(AV:122) ; `Default` → `MacroInstance_2707` (`ForEachLoopWithBreak` sur `Variables`, AV:151-158, 219). `LoopBody` →
|
|||
|
|
`IfThenElse_3951` (AV:174-178), condition `EqualEqual_StriStri` (`CallFunction_34376`, AV:169-173 : `A` ← paramètre
|
|||
|
|
`Name` par `VariableGet_19198`, `B` ← `BreakStruct_1527.Name_2_…`) : comparaison sans casse ; `else` vide → élément
|
|||
|
|
suivant. `then` → `MacroInstance_1960` (IsValid), dont toutes les issues finissent sur `Array_Set`
|
|||
|
|
(`CallArrayFunction_4346`, AV:179-186 : `Index` ← `MacroInstance_2707.Array Index`, `Item` ← `MakeStruct_614`) →
|
|||
|
|
`VariableSet_3566` `Replaced = true` (AV:201-205) → `Knot_488` → `MacroInstance_2707.Break` (AV:154, 215-217).
|
|||
|
|
`Completed` → `IfThenElse_4283` sur `Replaced` (AV:206-212) : `then` vide, `else` → `Array_Add` (`CallArrayFunction_3373`,
|
|||
|
|
AV:134-140). `Replaced` est une variable locale (ses nœuds n'ont pas de broche `self`, AV:201-205, 211-212, au contraire
|
|||
|
|
des variables de GI, AV:133, 229 ; estimé fort), remise à sa valeur par défaut à chaque appel (H3bS:100-102). Conclusion :
|
|||
|
|
la première entrée de même nom est remplacée sur place et la boucle s'arrête ; `Array_Add` seulement si aucune entrée ne
|
|||
|
|
porte ce nom. `AddVariable` ne crée jamais une deuxième entrée `AI`. Si `Break` ne menait pas à `Completed` (macro
|
|||
|
|
standard, non dumpée), il n'y aurait pas d'ajout non plus : A1 tient dans les deux cas.
|
|||
|
|
- **A2 confirmé** (établi). `destroyDummy` (`CallFunction_0`, AV:297-302) n'est atteint que par : `IfThenElse_3951.then` →
|
|||
|
|
`MacroInstance_1960` `Is Valid` (ancienne `Person[0]` = `GetArrayItem_2453`, AV:238-246) → `IfThenElse_4367` sur
|
|||
|
|
`SaveSlot == 0` (`CallFunction_27614`, AV:247-259) `then` → `IfThenElse_4819` (AV:278-282) `else`, condition `BooleanOR`
|
|||
|
|
`CommutativeAssociativeBinaryOperator_236` (AV:283-289) de : A ancienne `== PlayerCharacter` (`CallFunction_24845`,
|
|||
|
|
AV:270-274) ; B `Not(ancienne.bHidden)` (`CallFunction_565`, AV:290-296) ; C ancienne `==` nouvelle `Person[0]`
|
|||
|
|
(`CallFunction_1` ← `GetArrayItem_0` du paramètre `Person`, AV:303-311) ; D `Length(ancienne.SavedClothes) > 0`
|
|||
|
|
(`CallFunction_5`, AV:355-366). Suite de la branche : `PrintString` « DestroyDummy test1903 via AddVariable »
|
|||
|
|
(`CallFunction_2`) → `Array_RemoveItem(PersonsUsedThisScene)` (`CallArrayFunction_5493`) → `destroyDummy(Dummy = ancienne,
|
|||
|
|
KeepAlreadyGot = false)` → `PrintString` « (test0304) DestroyDummy was from AddVariable » (`CallFunction_606`,
|
|||
|
|
`bPrintToScreen = false`) → `Array_Set` (AV:263-269, 297-333). Pour H5-A, cas attendu : ancienne `AI` = `[AIDummy]` posée
|
|||
|
|
par `CallFunction_739` juste avant la boucle, même objet que la nouvelle : `SaveSlot != 0` (seule condition d'entrée du
|
|||
|
|
`else` de `IfThenElse_148`, § 2.5) ferme la branche dès `IfThenElse_4367`, et le terme C la fermerait aussi. Cas d'un autre
|
|||
|
|
objet (si `AIDummy` changeait entre deux images ; écrivain hors GI, R3) : la branche ne s'ouvre que pour une ancienne
|
|||
|
|
personne à `SaveSlot == 0`, cachée, hors joueur, sans `SavedClothes` ; c'est le comportement d'origine d'`AddVariable`,
|
|||
|
|
déjà possible avec `_739` / `_779` (incertain, non observé ; V5 compte `DestroyDummy was from AddVariable`). R2 : levé
|
|||
|
|
pour le cas attendu.
|
|||
|
|
- **A3 confirmé, libellé corrigé** (établi). `SwitchString_75` (AV:218-223) : `Selection` ← paramètre `Name` ; un seul cas,
|
|||
|
|
la broche **`choice`** (et non « `OptionChosen…` » : L6f:10149-10155, sans broches d'exécution, ne montrait pas le nom du
|
|||
|
|
cas ; `OptionChosenIndex` est la variable écrite) → `VariableSet_14216` `OptionChosenIndex = Round(Float[0])`
|
|||
|
|
(`CallFunction_71442`, `GetArrayItem_8856`, AV:224-237), dont `then` est **vide** (AV:226) : pour `Name = choice`, rien
|
|||
|
|
n'est enregistré dans `Variables`. Broche cachée `NotEqual_StriStri` (AV:222) : comparaison sans casse (estimé, nom de la
|
|||
|
|
fonction). `AI` ≠ `choice` → `Default` → A1 (établi). Le `PrintString` `CallFunction_4` (« <Name> Add Variable <objet> »)
|
|||
|
|
a `execute` vide (AV:344-345) : jamais exécuté (cohérent avec 0 ligne ` Add Variable` dans L3, R2).
|
|||
|
|
- Autres constats utiles à l'IMPL (établis sur AV) : aucun nœud latent ni `FunctionResult` parmi les 49 nœuds (types,
|
|||
|
|
AV:121-366) : H5-A rend la main de façon synchrone, puis `H5-A.then` → `CallFunction_743` ; `Float[0]` n'est lu que sur le
|
|||
|
|
cas `choice` (`GetArrayItem_8856`) : le `MakeArray_5` vide de H5-A ne lève pas de F2 ; `Person[0]` de l'ancienne entrée
|
|||
|
|
(`GetArrayItem_2453`) n'est lu qu'après un nom égal : ancienne `AI` de longueur 1, pas de F2 (lecture que G12 garde,
|
|||
|
|
`STE-H3c-SPEC.md` § 2.2 bis).
|
|||
|
|
|
|||
|
|
**§ 2.3 confirmé sans changement** : `type_id` `Scène|AddVariable` (relu en 5.8, GIMPL:39 ; P6 le revérifie) ; broches de
|
|||
|
|
H5-A = paramètres d'`AddVariable` `Name, Class, Array, Bool, Float, String, Person, Building` (AV:121-130), même ordre que
|
|||
|
|
`CallFunction_739` (EG:25149-25160, qui montre aussi `self`, EG:25152 : l'indice exact reste à relire, § 2.1) ; valeurs
|
|||
|
|
`Name = 'AI'`, `Class = 'Person'`, `Array = 'false'` copiées telles quelles dans la structure (`MakeStruct_614` ←
|
|||
|
|
`VariableGet_18510` à `_18516`, `Building` ← entrée, AV:141-150, 187-200 ; `Class` et `Array` ne sont testés nulle part).
|
|||
|
|
Volume inchangé : 1 nœud, 1 `break_pins`, 6 `connect_pins`, 2 `set_pin_value` (+1 relecture) ; EG après = 6234 nœuds.
|
|||
|
|
|
|||
|
|
**Macro `Async` (AS), sémantique relue** (établi sauf mention) :
|
|||
|
|
- `Tunnel_0.execute` → `IfThenElse_0` sur l'entrée `Condition` (`BooleanAND_65` pour `MacroInstance_45`, EG:25423) ;
|
|||
|
|
`else` → `Knot_3` → `Completed` (AS:129-133, 178-180) : **condition fausse = fin de la boucle**, pas de pause.
|
|||
|
|
- `then` → `IfThenElse_2` sur `SkipAsync` (variable de GI, AS:181-188) : vrai → `SkipAsync = false`, `Local = -1`,
|
|||
|
|
`Delay(0)` (AS:189-198) ; faux → `IfThenElse_1` sur `(Local % ItersPerTck == 0) AND (Local >= 0)` (AS:141-155, 200-209).
|
|||
|
|
- `IfThenElse_1.then` → `Knot_1` → `ExecutionSequence_0` : `then_0` → `Loop Body`, puis `then_1` → `Knot_2` (AS:163-172) ;
|
|||
|
|
`IfThenElse_1.else` → `Delay(0)` (`CallFunction_2`, `Duration = 0`, AS:156-162), `then` → `Knot_2`.
|
|||
|
|
- `Knot_2` → `AssignmentStatement_0` `Local = Local + 1` → retour à `IfThenElse_0` (AS:134-138, 173-177). `Local`
|
|||
|
|
(`TemporaryVariable_0`, AS:139-140) n'est pas remis à zéro à l'entrée.
|
|||
|
|
Conséquences :
|
|||
|
|
1. Le corps (`IfThenElse_148` → H5-A → `PlayScene`) s'exécute en entier sur `then_0`, avant `then_1` ; le seul point latent
|
|||
|
|
de la macro est le `Delay` (AS:156) : pas de reprise entre la pose de `AI` et la routine (objectif du § 1, établi).
|
|||
|
|
2. **Correction du § 2.5** (« jusqu'à `len(LPAI)` itérations à 3 par image ») : le corps ne part que si `Local % 3 == 0`
|
|||
|
|
(`ItersPerTck = '3'`, EG:25424) ; les deux valeurs suivantes de `Local` passent chacune par un `Delay(0)`. Le gestionnaire
|
|||
|
|
d'actions latentes ne traite un objet qu'une fois par passe (`bProcessedThisFrame`, UE
|
|||
|
|
`Runtime\Engine\Private\LatentActionManager.cpp:223-231, 250-257`) : environ un `.lpai` toutes les deux images (estimé
|
|||
|
|
fort). Entre deux `.lpai` d'un même PNJ, il y a **toujours** au moins une reprise latente, donc une fenêtre de vidage :
|
|||
|
|
F-a reste nécessaire à chaque itération (rien à changer au § 2.3).
|
|||
|
|
3. Ligne F-b du § 2.6 et « Incertain » des Constats : la sortie d'`Async` quand la condition devient fausse est établie
|
|||
|
|
(`Completed`, fin de boucle) ; F-b arrêterait la boucle d'IA au lieu de la suspendre (verdict « plus invasive » confirmé).
|
|||
|
|
|
|||
|
|
Corrections de renvois : V4 (§ 7) joint `dumps\GI__AddVariable_D5.txt` (et non `GI__AddVariable.txt`) ; R1 et R2 (§ 4) sont
|
|||
|
|
levés par ce complément ; la commande P3 du § 2.0 n'est plus à lancer.
|
|||
|
|
|
|||
|
|
Tours du complément (A-CART, commun au § 2.7 et à `STE-H3c-SPEC.md` § 2.2 bis) : 19 / 50.
|
|||
|
|
|
|||
|
|
### 2.1 Conventions (reprises de H3S § 2.1, H3bS § 2.1 et GIMPL § 2)
|
|||
|
|
- `refPath` d'un nœud : `/Game/BPs/GI.GI:EventGraph.K2Node_<Nom>` (lp-mcp § 3) ; G-H2 a déjà été posée dans ce graphe
|
|||
|
|
(GIMPL:35).
|
|||
|
|
- Indices de broches par direction. Établis : `IfThenElse` sortie `then` 0 / `else` 1 (lp-mcp § 3). **Tous les autres se
|
|||
|
|
relisent par `get_node_infos` avant `connect_pins`.** Attendus (estimés) pour le nouvel `AddVariable` : entrées
|
|||
|
|
`execute` 0, `Name` 1, `Class` 2, `Array` 3, `Bool` 4, `Float` 5, `String` 6, `Person` 7, `Building` 8, sortie `then` 0,
|
|||
|
|
si la broche cachée `self` n'est pas comptée (cas observé : IsValidIndex `TargetArray` = 0, GIMPL:38) ; sinon +1 sur
|
|||
|
|
chaque entrée après `execute`. `CallFunction_743` : entrée `execute` 0. `MakeArray_3` à `_6` : sortie `Array` 0.
|
|||
|
|
- Une sortie de données peut alimenter plusieurs entrées (les liens existants restent) ; une entrée d'exécution peut
|
|||
|
|
recevoir plusieurs liens (H3bS:78-79).
|
|||
|
|
- Après le graphe : `compile_blueprint` avec avertissements en erreurs (`hyper_blueprint_debug_inspect` absent du
|
|||
|
|
serveur, GIMPL:44-46), puis `save_assets {asset_paths:["/Game/BPs/GI"]}`.
|
|||
|
|
|
|||
|
|
### 2.2 Nœuds existants (colonne « avant », EG)
|
|||
|
|
|
|||
|
|
| Nœud | Rôle | Liens avant (EG) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `K2Node_MacroInstance_45` | macro `Async`, `ItersPerTck = 3` | `Loop Body` → `IfThenElse_148.execute` ; `Completed` → `VariableSet_280` ; `Condition` ← `BooleanAND_65` (EG:25422-25428) |
|
|||
|
|
| `K2Node_IfThenElse_148` | `AIDummy.SaveSlot == 0` | `execute` ← `MacroInstance_45.Loop Body` ; `Condition` ← `CallFunction_811` ; `then` → `VariableSet_292.execute` ; `else` → `CallFunction_743.execute` (EG:25750-25754) |
|
|||
|
|
| `K2Node_CallFunction_811` / `VariableGet_576` | `SaveSlot` de `VariableGet_529.AIDummy` `== 0` | EG:25742-25749 |
|
|||
|
|
| `K2Node_CallFunction_743` | `PlayScene(LPAI[CheckingPkgIndex])` | `execute` ← `MacroInstance_43.LoopBody`, `IfThenElse_148.else` ; `then` → `MacroInstance_47` ; `FilePath` ← `GetArrayItem_51.Output` (EG:24891-24895) |
|
|||
|
|
| `K2Node_MacroInstance_43` | `WhileLoop` mort | `execute` (vide), `LoopBody` → `CallFunction_743.execute` (EG:24975-24979) |
|
|||
|
|
| `K2Node_CallFunction_739` | `AddVariable` existant, modèle | `execute` ← `VariableSet_259.then` ; `then` → `VariableSet_281.execute` ; `Name = 'AI'`, `Class = 'Person'`, `Array = 'false'` ; `Bool` ← `MakeArray_4`, `Float` ← `MakeArray_5`, `String` ← `MakeArray_6`, `Person` ← `MakeArray_3`, `Building` (vide) (EG:25149-25160) |
|
|||
|
|
| `K2Node_MakeArray_4` / `_5` / `_6` | tableaux vides `Bool` / `Float` / `String` | `Array` → `CallFunction_739.Bool` / `.Float` / `.String` (EG:25161-25166) |
|
|||
|
|
| `K2Node_MakeArray_3` | `[AIDummy]` | `Array` → `CallFunction_739.Person` ; `[0]` ← `VariableGet_525.AIDummy` (EG:25167-25169) |
|
|||
|
|
| `K2Node_VariableGet_525` | `AIDummy` | `AIDummy` → `MakeArray_3.[0]` (EG:25170-25172) |
|
|||
|
|
|
|||
|
|
### 2.3 Étapes MCP (une par appel, dans l'ordre ; le nouveau nœud est noté **H5-A**)
|
|||
|
|
1. `create_node {graph:{refPath:"/Game/BPs/GI.GI:EventGraph"}, type_id:"Scène|AddVariable", pos:{x,y}}` ; position
|
|||
|
|
libre près de `IfThenElse_148` (sans effet sur l'exécution).
|
|||
|
|
2. `get_node_infos` de H5-A : appel `GI_C::AddVariable`, mêmes broches que `CallFunction_739` (EG:25149-25160) ; noter
|
|||
|
|
son nom (`K2Node_CallFunction_<n>`) et les indices réels (§ 2.1). Sinon arrêt et repli (§ 3).
|
|||
|
|
3. `break_pins` : `IfThenElse_148.else` (sortie 1) / `CallFunction_743.execute` (entrée 0).
|
|||
|
|
4. `connect_pins` : `IfThenElse_148.else` (1) → `H5-A.execute`.
|
|||
|
|
5. `connect_pins` : `H5-A.then` → `CallFunction_743.execute` (0). Le lien mort `MacroInstance_43.LoopBody` reste.
|
|||
|
|
6. `connect_pins` : `MakeArray_3.Array` → `H5-A.Person`.
|
|||
|
|
7. `connect_pins` : `MakeArray_4.Array` → `H5-A.Bool`.
|
|||
|
|
8. `connect_pins` : `MakeArray_5.Array` → `H5-A.Float`.
|
|||
|
|
9. `connect_pins` : `MakeArray_6.Array` → `H5-A.String`.
|
|||
|
|
10. `set_pin_value` : `H5-A.Name` = `AI` (valeur relue sur `CallFunction_739`, EG:25153).
|
|||
|
|
11. `set_pin_value` : `H5-A.Class` = `Person` (EG:25154).
|
|||
|
|
12. Relire `H5-A.Array` : doit valoir `false` (EG:25155) ; ne le poser que s'il diffère. `Building` : rien.
|
|||
|
|
13. `get_node_infos` de H5-A, `IfThenElse_148`, `CallFunction_743`, `CallFunction_739`, `MakeArray_3` à `_6` : liens = § 2.4.
|
|||
|
|
14. `compile_blueprint` (avertissements en erreurs) : 0 erreur, 0 avertissement ; puis `save_assets`.
|
|||
|
|
15. Fermeture (lp-mcp § 2 : `Stop-Process`, `.\Tools\UeState.ps1` = 0), MD5 après relevé, `git status --short -- Content Config Source`.
|
|||
|
|
|
|||
|
|
Repli documenté de l'étape 6 seulement : si `connect_pins` refuse `MakeArray_3.Array` → `H5-A.Person`, créer un Make Array
|
|||
|
|
(`type_id` `Utilitaires|Array|Faireuntableau`, GIMPL:39), câbler d'abord sa sortie `Array` → `H5-A.Person` (typage,
|
|||
|
|
H3bS:83-84), puis `VariableGet_525.AIDummy` → `[0]` (2 nœuds au lieu de 1, EG 6235). Tout autre refus → arrêt et repli.
|
|||
|
|
|
|||
|
|
### 2.4 Après (liens attendus, relus à l'étape 13 et dans le dump V1)
|
|||
|
|
- `IfThenElse_148.else` → `H5-A.execute` (seul lien) ;
|
|||
|
|
- `H5-A` : `execute` ← `IfThenElse_148.else` ; `then` → `CallFunction_743.execute` ; `Name = 'AI'`, `Class = 'Person'`,
|
|||
|
|
`Array = 'false'` ; `Bool` ← `MakeArray_4.Array`, `Float` ← `MakeArray_5.Array`, `String` ← `MakeArray_6.Array`,
|
|||
|
|
`Person` ← `MakeArray_3.Array`, `Building` (vide) ;
|
|||
|
|
- `CallFunction_743.execute` ← `MacroInstance_43.LoopBody`, `H5-A.then` ;
|
|||
|
|
- `MakeArray_3` / `_4` / `_5` / `_6` : `Array` → `CallFunction_739.<broche>` **et** `H5-A.<broche>` ;
|
|||
|
|
- aucun autre nœud de EG modifié.
|
|||
|
|
|
|||
|
|
### 2.5 Comportement avant / après
|
|||
|
|
- Avant : `AI` posée une fois par PNJ (`CallFunction_739`, EG:25149-25171), puis jusqu'à `len(LPAI)` itérations à 3 par
|
|||
|
|
image (`MacroInstance_45`, EG:25422-25428) ; un vidage entre deux images retire `AI` pour les `.lpai` suivants (H5D § 3-4).
|
|||
|
|
- Après : à chaque itération qui joue un `.lpai` (`SaveSlot != 0`), `AI` est reposée juste avant `PlayScene`. Si `AI`
|
|||
|
|
existe déjà avec le même `AIDummy`, `AddVariable` la remplace par elle-même (A1) ; cette situation existe déjà à chaque
|
|||
|
|
PNJ (`CallFunction_779` pose `AI` dans `LoopUpdateAI`, EG:25017-25037, puis `CallFunction_739` la repose dans `UpdateAI`).
|
|||
|
|
- Validité de `AIDummy` au point d'insertion (point (a) de la fiche) : le `else` de `IfThenElse_148` n'est pris que si
|
|||
|
|
`AIDummy.SaveSlot != 0` ; une lecture sur une référence nulle rend 0 (UE `ScriptCore.cpp:3234-3245`), donc `AIDummy`
|
|||
|
|
n'est jamais nul quand H5-A s'exécute (établi par construction). Même source (`VariableGet_525`) que l'appel actuel.
|
|||
|
|
- Le `.lpai` s'exécute en entier dans l'appel `PlayScene` : boucle `WhileLoop` `MacroInstance_963` sur les lignes, tant
|
|||
|
|
que `Not(WaitingScene)` (EG:6027-6031, 6212-6223) ; `WaitingScene` ne passe à vrai qu'après une ligne « lourde »
|
|||
|
|
(`LastHeavyFunction`, EG:20369-20390) = ligne commençant par `generate` ou `dress` (`GI__ReadSceneLine.txt:635-652`) ;
|
|||
|
|
les 17 `.lpai` du jeu n'en ont pas (Grep `\.(generate|dress)|^\s*\w+\s*=\s*generate|\bdress\(`, `-i`, `*.lpai` sous
|
|||
|
|
`Content\Modules` : 0).
|
|||
|
|
- Gardes G1-G11 : avec `AI` = `[AIDummy]` valide, les tests `IsValidIndex` / `IsValid` des gardes qui lisent `AI` (G1, G2,
|
|||
|
|
G3, G4, G5, G6, G7, G8, G9, G10, G11 selon la commande) passent par `then` : le chemin d'origine s'exécute. Le cas B ne
|
|||
|
|
les déclenche plus ; elles restent utiles au cas A (conditions de `.lpscene`) et aux autres variables (ex. `SO` de
|
|||
|
|
`so_follows.lpai:8-17`).
|
|||
|
|
|
|||
|
|
### 2.6 Alternatives comparées (fiche, point (c) ; aucune n'est tranchée ici)
|
|||
|
|
|
|||
|
|
| Variante | Où | Nœuds / coupures | Effet hors IA | Verdict de lecture |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| **F-a** (décidée) | corps de `MacroInstance_45` | 1 / 1 | aucun | la plus petite ; couvre tout `.lpai` synchrone |
|
|||
|
|
| F-a′ : déplacer `CallFunction_739` dans la boucle | idem | 0 / 3 | aucun | même effet, plus de coupures, sans gain |
|
|||
|
|
| F-b : exclusion mutuelle (`nextAi` ou `CheckingForScenes` dans une condition `Async`) | `MacroInstance_39` ou `_45` | 1-2 | cadence des scènes du joueur ou des routines ; une condition fausse fait sortir la macro (branche `IfThenElse_0` sur `Condition`, L6f:49499-49505 ; sortie exacte à lire avec `-Fn=Async -Exec`) au lieu de la mettre en pause | plus invasive |
|
|||
|
|
| F-c : ne pas vider `Variables` pendant la boucle d'IA (Branch sur `nextAi` avant `Array_Clear`) | `UpdateCheckForScenes` | 2 / 1 | la vérification d'une scène candidate garderait les variables de la candidate précédente et des `.lpai` (ex. `Today`, `Current`, `Dest`) : appariement `WHO` des scènes du joueur modifié | risque visible sur les scènes du joueur |
|
|||
|
|
| F-c′ : sauver puis reposer `AI` autour du `Array_Clear` | `UpdateCheckForScenes` | ≥ 4 (GetVariable, IsValidIndex + Branch, AddVariable, Make Array) | chemin chaud (5 490 `CHECKING:` en J3, `STE-VAL.md:364`) ; `AI` visible des candidates, sans lecteur (Grep `(^\|[^A-Za-z_])AI\.` sur `*.lpscene` : 0) | plus de nœuds, aucun gain pour le contenu actuel |
|
|||
|
|
|
|||
|
|
Lecture : aucune alternative n'est plus sûre que F-a sans effet de bord ; F-a reste conforme à la décision.
|
|||
|
|
|
|||
|
|
### 2.7 Témoin temporaire (Q-H5b-1) — complément A-CART pour l'orchestrateur LOT STE (2026-10-10)
|
|||
|
|
|
|||
|
|
Décision de l'utilisateur : Q-H5b-1 = témoin (`STE-TACHES.md:27`) : un `PrintString` temporaire sur le chemin « personne
|
|||
|
|
vide » de G5, dans un build de diagnostic **non commité**, retiré avant le commit. Il remplace la variante (b) du § 4 (texte
|
|||
|
|
et préfixe ci-dessous) ; la mesure « avant F-a » de cette variante devient la contre-épreuve C0 (fin de section).
|
|||
|
|
|
|||
|
|
Nœuds existants, graphe `CallFunction` de GI (**CFG** = `dumps\GI__CallFunction_apres-GARDES.txt`, 1563 nœuds, CFG:120 ;
|
|||
|
|
état B8B97C46, graphe que l'IMPL H5b ne touche pas) :
|
|||
|
|
|
|||
|
|
| Nœud | Rôle | Liens (CFG) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `K2Node_IfThenElse_14` | G5-B1 : `IsValidIndex(BreakStruct_54.Person_24_…, 0)` (`CallArrayFunction_9`) | `execute` ← `CallFunction_189.then` ; `then` → `IfThenElse_15.execute` ; `else` **(vide)** (CFG:7619-7628) |
|
|||
|
|
| `K2Node_IfThenElse_15` | G5-B2 : `IsValid(GetArrayItem_75.Output)` (`CallFunction_356`) | `execute` ← `IfThenElse_14.then` ; `then` → `CallFunction_190.execute` (`Map_Add(AI_Untils)`) ; `else` **(vide)** (CFG:7629-7637) |
|
|||
|
|
| `K2Node_CallFunction_193` | modèle : `PrintString` « (test0104) for … hours by <clé> » après `_190` | `bPrintToScreen = 'true'`, `bPrintToLog = 'true'`, `WorldContextObject` (vide) (CFG:5586-5596) |
|
|||
|
|
|
|||
|
|
Nœuds créés (noms réels `K2Node_CallFunction_<n>`, notés à la création) :
|
|||
|
|
- **T1** = `PrintString` sur `IfThenElse_14.else`, `InString` = `STE-H5b-TEMOIN G5-B1 setUntil Person vide` (variable
|
|||
|
|
absente ou sans personne : le cas B que F-a supprime) ;
|
|||
|
|
- **T2** = `PrintString` sur `IfThenElse_15.else`, `InString` = `STE-H5b-TEMOIN G5-B2 setUntil Person0 nul` (variable
|
|||
|
|
présente, `Person[0]` nul : autre cause, hors F-a) ;
|
|||
|
|
- pour les deux : `bPrintToScreen` = `false` (comme `CallFunction_606`, AV:329), `bPrintToLog` = `true` (défaut, relu) ;
|
|||
|
|
`TextColor`, `Duration`, `Key`, `WorldContextObject` : défaut / vide (comme `CallFunction_193`). Texte ASCII seul ; préfixe
|
|||
|
|
unique : `STE-H5b-TEMOIN` → 0 dans L3 (Grep, mode `count`), témoin de lecture du même fichier
|
|||
|
|
`LogBlueprintUserMessages: \[GI_C_\d+\] \(test0104\)` → 46 ;
|
|||
|
|
- ligne attendue : `LogBlueprintUserMessages: [GI_C_<n>] STE-H5b-TEMOIN G5-B1 setUntil Person vide` (préfixe `[objet]` quand
|
|||
|
|
`Kismet.bLogPrintStringSource` est vrai, UE `Runtime\Engine\Private\KismetSystemLibrary.cpp:497-506`, observé pour les
|
|||
|
|
messages existants de GI) ;
|
|||
|
|
- `type_id` du `PrintString` : **non relu en 5.8** (absent de lp-mcp § 3 et de GIMPL § 2) ; il se lit à l'étape T-2.
|
|||
|
|
|
|||
|
|
Étapes MCP (une par appel ; `refPath` `/Game/BPs/GI.GI:CallFunction.K2Node_<Nom>`) :
|
|||
|
|
- T-1. `get_node_infos` de `IfThenElse_14` et `IfThenElse_15` : liens = tableau ci-dessus (les deux `else` vides) ; sinon arrêt.
|
|||
|
|
- T-2. `find_node_types {graph:{refPath:"/Game/BPs/GI.GI:CallFunction"}, type_id_filter:"PrintString", context_pins:[]}` ;
|
|||
|
|
sans résultat, filtres `Print` puis `Imprimer`. Retenir l'entrée dont le nœud créé appelle `KismetSystemLibrary::PrintString`
|
|||
|
|
(broches `InString`, `bPrintToScreen`, `bPrintToLog`, `TextColor`, `Duration`, `Key`), pas `PrintText` (`InText`). Aucune
|
|||
|
|
entrée conforme → arrêt, pas de témoin, preuve par la variante (a) (V5 seul), rapport à l'orchestrateur.
|
|||
|
|
- T-3. `create_node` T1 (position libre près de `IfThenElse_14`) ; `get_node_infos` T1 : nom, indices de `execute`,
|
|||
|
|
`InString`, `bPrintToScreen` (attendu `execute` entrée 0 ; les autres relus, `self` / `WorldContextObject` cachés possibles).
|
|||
|
|
- T-4. `connect_pins` : `IfThenElse_14.else` (sortie 1) → `T1.execute`.
|
|||
|
|
- T-5. `set_pin_value` : `T1.InString` = `STE-H5b-TEMOIN G5-B1 setUntil Person vide`.
|
|||
|
|
- T-6. `set_pin_value` : `T1.bPrintToScreen` = `false`.
|
|||
|
|
- T-7 à T-10. Idem pour T2 sur `IfThenElse_15.else` (sortie 1), `InString` = `STE-H5b-TEMOIN G5-B2 setUntil Person0 nul`.
|
|||
|
|
- T-11. `get_node_infos` de T1, T2, `IfThenElse_14`, `IfThenElse_15` : `else` → T1 / T2 seuls, `then` inchangés, `T1.then`
|
|||
|
|
et `T2.then` vides, textes relus à l'octet.
|
|||
|
|
- T-12. `compile_blueprint` (avertissements en erreurs) : 0 / 0 ; `save_assets {asset_paths:["/Game/BPs/GI"]}`.
|
|||
|
|
Volume : 2 nœuds, 0 `break_pins`, 2 `connect_pins`, 4 `set_pin_value` ; `CallFunction` 1563 → 1565 nœuds (dump facultatif,
|
|||
|
|
éditeur fermé : `.\Tools\RunCommandlet.ps1 -Lot STE-H5b -Commandlet LPDumpFunction -Args '-BP=GI -Fn=CallFunction -Pins -Exec'`).
|
|||
|
|
Comportement : T1 et T2 sont sur des `else` vides et n'ont aucune sortie de données ; seule différence, une ligne de journal
|
|||
|
|
(établi par construction).
|
|||
|
|
|
|||
|
|
Procédure (décision de l'utilisateur ; une session sur la copie, gel annoncé, BUILD-GO de Big Boss) :
|
|||
|
|
1. IMPL H5b finie (§ 2.3, étapes 1-15) : GI enregistré, éditeur fermé (`.\Tools\UeState.ps1` = 0), MD5 **M1** relevé (V3),
|
|||
|
|
dump V1 (EventGraph, 6234 nœuds) pris sur M1.
|
|||
|
|
2. Instantané dédié : `.\Tools\Snapshot.ps1 -Lot STE-H5b-temoin -Path Content\BPs\GI.uasset` (lot distinct de `STE-H5b`, qui
|
|||
|
|
garde B8B97C46 : la première sauvegarde d'un lot fait référence, `Tools\Snapshot.ps1:7-9`) ; MD5 de la copie
|
|||
|
|
`D:\Game\LifePlay_Backup_5.5\STE-H5b-temoin\Content\BPs\GI.uasset` = M1, sinon arrêt.
|
|||
|
|
3. Éditeur headless (lp-mcp § 2), `load_asset` `/Game/BPs/GI`, T-1 à T-12, fermeture, `UeState.ps1` = 0, MD5 **M2** (≠ M1,
|
|||
|
|
relevé, jamais commité).
|
|||
|
|
4. Build Development de diagnostic (mêmes commandes que `STF-T5-J-BUILD.md:8-10`) :
|
|||
|
|
`.\Tools\Build.ps1 -Lot STE-H5b -Target LifePlay -Config Development -ExtraArgs "-NoUBA"` puis
|
|||
|
|
`.\Tools\BuildDebug.ps1 -Config Development -ArchiveDir "D:\Game\LifePlay_Build_5.8_STE-H5b-TEMOIN_Development" -SkipBuild`
|
|||
|
|
(dossier absent avant). `PrintString` est compilé en Development (retiré seulement en Shipping / Test, UE
|
|||
|
|
`KismetSystemLibrary.cpp:458`).
|
|||
|
|
5. J4 sur cette archive (parcours de V5, avec rafale) ; journal
|
|||
|
|
`D:\Game\LifePlay_Build_5.8_STE-H5b-TEMOIN_Development\Windows\LifePlay\Saved\Logs\LifePlay.log`.
|
|||
|
|
6. Retrait, éditeur fermé : `.\Tools\Restore.ps1 -Lot STE-H5b-temoin` (`-Lot` obligatoire, `Tools\Restore.ps1:19-24`) ;
|
|||
|
|
MD5 = M1 ; `git status --short -- Content Config Source` = ` M Content/BPs/GI.uasset` seul ; V2 (`CompileAllBlueprints`)
|
|||
|
|
sur M1. Seulement alors : ticket au gardien (commit de GI à M1). MD5 ≠ M1 → arrêt, pas de commit.
|
|||
|
|
7. L'archive `..._STE-H5b-TEMOIN_Development` est gardée (jamais supprimée), marquée « diagnostic, GI M2 non commité » dans
|
|||
|
|
le livrable J4 ; elle ne sert de référence à aucun autre lot.
|
|||
|
|
Interdit pendant la fenêtre 2-6 : toute autre écriture de `GI.uasset` (G12 de STE-H3c en particulier), que la restauration
|
|||
|
|
défairait. Si G12 doit être mesurée dans la même J4, son IMPL se place avant l'étape 2 (l'instantané couvre alors H5b + G12).
|
|||
|
|
Écart assumé : J4 se joue sur M2, le commit porte M1 ; la seule différence est T1 / T2 (ci-dessus).
|
|||
|
|
|
|||
|
|
Critère (Grep en mode `count` sur le journal de J4) :
|
|||
|
|
- `STE-H5b-TEMOIN` → **0** ; détail `STE-H5b-TEMOIN G5-B1` (cas B, critère de F-a) et `STE-H5b-TEMOIN G5-B2` ;
|
|||
|
|
- non-vacuité, même journal : `hours by [1-9]` ≥ 1 (G5 atteinte avec une personne) et `LogBlueprintUserMessages: \[GI_C_\d+\]`
|
|||
|
|
≥ 1 (les `PrintString` de GI arrivent au journal) ; rafale présente (V5) ;
|
|||
|
|
- reste > 0 : rapport (compte, trois lignes de contexte par occurrence), pas de correctif. Attribution : `setUntil` est
|
|||
|
|
appelé sur `AI` par les 17 `.lpai`, mais aussi par 6 `.lpscene` (`Actor.setUntil` × 5 sous `vin_Incest\Scenes\proactive`,
|
|||
|
|
`CurrentCompanion.setUntil` dans `goodbye_companion.lpscene:11` ; Grep `\b\w+\.setUntil\b` `-i -o` sous `Content\Modules` :
|
|||
|
|
23 lignes) ; la boucle d'IA ne tourne jamais pendant une scène du joueur (R5) : une ligne témoin pendant une scène n'est
|
|||
|
|
pas imputable à F-a (contexte à lire, incertain).
|
|||
|
|
|
|||
|
|
Contre-épreuve sans F-a (**C0, jugée utile**) : depuis G5 (Q2), le cas B est muet et J3 ne dit pas combien il y en a eu ; un
|
|||
|
|
0 en J4 seul ne distingue pas « F-a agit » de « parcours sans cas B » (sa fréquence dépend des rafales : 24 `by 0` dans L2,
|
|||
|
|
H3S:151-152). C0 montre, avec le même témoin et un parcours comparable, que le cas B se produit sans F-a. Attendu C0 :
|
|||
|
|
`STE-H5b-TEMOIN G5-B1` ≥ 1 (rapporté aussi au nombre de `CHECKING:`) ; J4 est alors lue à parcours comparable (`CHECKING:`
|
|||
|
|
du même ordre). Coût : une écriture de GI, un build, une session et une restauration de plus (les 2 builds de la variante (b)
|
|||
|
|
du § 4). Ordre conseillé, qui laisse intacte la procédure ci-dessus : juste après P4 (instantané `STE-H5b` = B8B97C46) et
|
|||
|
|
avant l'IMPL H5b : T-1 à T-12 sur B8B97C46 (mêmes `refPath` : H5b ne touche pas `CallFunction`), build vers
|
|||
|
|
`D:\Game\LifePlay_Build_5.8_STE-H5b-C0_Development`, session C0, `.\Tools\Restore.ps1 -Lot STE-H5b` éditeur fermé, MD5 =
|
|||
|
|
`b8b97c46…`, puis IMPL H5b. Décision à l'utilisateur (elle ajoute un build à sa procédure) ; sans C0, le 0 de J4 est
|
|||
|
|
rapporté « sans contrôle positif ».
|
|||
|
|
|
|||
|
|
## 3. Repli
|
|||
|
|
|
|||
|
|
- Avant écriture : instantané P4 (`LifePlay_Backup_5.5\STE-H5b`, état `b8b97c46…`).
|
|||
|
|
- Échec de création, lien relu différent, refus non prévu (§ 2.3) ou compilation avec diagnostic : arrêt,
|
|||
|
|
`.\Tools\Restore.ps1 -Lot STE-H5b` éditeur fermé (GI entier), contrôle MD5 = `b8b97c46b825a6af942baabd5bf74c56`.
|
|||
|
|
- Après commit (gardien) : `git revert` du commit du ticket STE-H5b, ou tag `STE-H5b-avant` (ticket RESTORE).
|
|||
|
|
- Indépendant des gardes : le repli de H5b ne défait aucune garde (instantané pris sur l'état B8B97C46).
|
|||
|
|
|
|||
|
|
## 4. Risques R#, questions Q#
|
|||
|
|
|
|||
|
|
- **R1** (A1) : sémantique d'`AddVariable` lue sur un dump 5.5 sans exécution (L6f:10070-10273). Attendu : remplacement par
|
|||
|
|
nom sans casse. Indice concordant : si chaque appel ajoutait une entrée, chaque PNJ aurait deux `AI` (`_779` puis
|
|||
|
|
`_739`) sans effet visible ; non probant seul. P3 tranche.
|
|||
|
|
- **R2** (A2) : la branche `destroyDummy` d'`AddVariable` est vivante en 5.8 : 3 déclenchements dans L3 (Grep
|
|||
|
|
`via AddVariable|DestroyDummy was from AddVariable| Add Variable` → 6 lignes, L3:8403-8404, 19888-19889, 20263-20264 ;
|
|||
|
|
0 dans L2). Pour `AI` elle ne doit pas se déclencher : ancienne personne = nouvelle (`AIDummy`), et `SaveSlot != 0` au
|
|||
|
|
point d'insertion (§ 2.5). Estimé fort ; P3 confirme ; en J4, ce compte ne doit pas croître (V5).
|
|||
|
|
- **R3** (point (a)) : l'écrivain de `GI.AIDummy` n'est pas trouvé. EG : seuls `VariableSet_258` et `_276`, sur des chaînes
|
|||
|
|
sans entrée d'exécution (EG:25011-25016, 25362-25376) ; L6f (GI 5.5 complet) : aucun autre `VariableSet` d'`AIDummy`
|
|||
|
|
(Grep `AIDummy` : 39 lignes, hors EG seulement `destroyDummy` L6f:42252-42258, `destroySlot` L6f:43747-43750 et un nom
|
|||
|
|
de variable de scène `'AIDummy'` dans `GenerateHomeAndWork`, L6f:43339, 43459) ; `PC__EventGraph.txt` : 0. Il est donc
|
|||
|
|
posé hors de GI (autre Blueprint ou niveau, non dumpé). Non bloquant : `AIDummy` est non nul au point d'insertion
|
|||
|
|
(§ 2.5), c'est la même lecture que `CallFunction_739`, et les routines valides l'impriment sous le nom d'objet `AI`
|
|||
|
|
(`[AI] <n> moves to …` : 48 lignes dans L3, 9 dans L2 ; estimé : message de `Person_C::setCurrentLocation` sur `AIDummy`).
|
|||
|
|
- **R4** : une ligne « lourde » (`generate…`, `dress…`) dans un futur `.lpai` suspendrait la routine (minuterie
|
|||
|
|
`ContinueScene`) ; un vidage pourrait alors tomber entre deux lignes, hors de portée de F-a. Aujourd'hui : 0 cas (§ 2.5).
|
|||
|
|
- **R5** (masquage, fiche § 5) : seule l'entrée nommée `AI` est remplacée (A1). Aucun contenu n'écrit `AI =` (Grep
|
|||
|
|
`^\s*AI\s*=` `-i` sous `Content\Modules` : 0) ; aucune `.lpscene` ne lit `AI.` (0) ; les 17 `.lpai` la lisent (95 lignes).
|
|||
|
|
La condition de `MacroInstance_45` contient `Not(SceneOn)` (`CallFunction_748`, EG:25586-25592) et `Len(followUp) == 0`
|
|||
|
|
(`CallFunction_828`, EG:25979-25990) : H5-A ne s'exécute jamais pendant une scène du joueur. Chaque candidate de
|
|||
|
|
`UpdateCheckForScenes` commence par `Array_Clear` : `AI` n'entre pas dans une vérification.
|
|||
|
|
- **R6** (comportement modifié, accepté) : routines appliquées, moins de routines par PNJ, compteurs `ActorVars` avancés (§ 1).
|
|||
|
|
- **R7** (preuve) : depuis les gardes (Q1 = A), une routine sans personne est **muette** (G4 et G5 sautent
|
|||
|
|
`setCurrentLocation` et `setUntil` sans message) : le journal ne peut plus montrer directement « aucun vidage suivi de
|
|||
|
|
routine sans personne ». La preuve directe est statique (§ 2.5) ; la preuve en jeu est statistique (V5) — voir Q-H5b-1.
|
|||
|
|
- **R8** : variables laissées dans `Variables` par la vérification des scènes (après un vidage) et variables temporaires
|
|||
|
|
des `.lpai` (`Today`, `Current`, `Dest`, `Until`) : comportement d'origine inchangé ; chaque `.lpai` recrée ses
|
|||
|
|
temporaires avant de les lire (ex. `entertainment.lpai:7-16`, estimé pour les 16 autres).
|
|||
|
|
- **R9** : réemploi de `MakeArray_3` à `_6` par deux appels : précédent G10 (`CallFunction_360` câblé sur les Make Array de
|
|||
|
|
`CallFunction_203`, H3bS:159-160, GIMPL:33 ; compilé, J3 PASS). Un nœud pur est réévalué pour chaque appel qui le lit
|
|||
|
|
(estimé fort, non relu dans `KismetCompiler.cpp`).
|
|||
|
|
- **R10** : coût : un parcours de `Variables` par `.lpai` joué (≤ 17 par PNJ) ; négligeable (estimé).
|
|||
|
|
|
|||
|
|
**Q-H5b-1 (utilisateur, avant J4)** — preuve en jeu : (a) statistique seule, comparaison J4 / J3 (V5, défaut) ; ou
|
|||
|
|
(b) témoin temporaire dans un build de diagnostic **non commité** : un `PrintString` « (test0104) setUntil sans personne »
|
|||
|
|
sur `IfThenElse_14.else` (G5-B1, `CallFunction`, GIMPL:28), mesuré avant F-a (attendu > 0 dans une rafale) puis après
|
|||
|
|
(attendu 0), retiré par `Restore.ps1` avant le commit (2 builds de plus, une écriture GI de plus).
|
|||
|
|
|
|||
|
|
## 5. Interdits
|
|||
|
|
|
|||
|
|
- Aucune modification hors du § 2.3 : pas de `delete_node`, pas de retouche de `CallFunction_739` / `_779`, de
|
|||
|
|
`UpdateCheckForScenes` (`CallArrayFunction_5412`), des conditions des macros `Async`, des gardes G1-G11, du C++, du
|
|||
|
|
contenu (`*.lpai`, `*.lpscene`).
|
|||
|
|
- Aucun nœud AND / OR / Select ; aucune écriture HyperAIStudio (lp-mcp § 1.1) ; outils Epic seulement.
|
|||
|
|
- Pas d'IMPL sans P1 à P6 (P3 compris) ; pas d'éditeur pendant un commandlet ou un build (lp-mcp § 1.7).
|
|||
|
|
- Pas de commit par l'agent ni par l'IMPL : ticket au gardien (lp-repo) ; numéro CHG à réserver par l'orchestrateur
|
|||
|
|
(`etat\REPO\NUMEROS.md`).
|
|||
|
|
|
|||
|
|
## 6. Valeurs moteur lues dans le moteur installé (UE 5.8)
|
|||
|
|
|
|||
|
|
- `Delay(0)` : `FDelayAction::UpdateOperation` retire le temps écoulé et ne déclenche la suite que lorsque
|
|||
|
|
`TimeRemaining <= 0` (UE `Runtime\Engine\Public\DelayAction.h:27-30`) : la reprise est latente, traitée par le
|
|||
|
|
gestionnaire d'actions latentes, jamais au milieu d'une chaîne d'exécution synchrone. La macro `Async` n'a pas d'autre
|
|||
|
|
point de suspension que ce `Delay` (`CallFunction_2`, `Duration = 0`, L6f:49523-49527).
|
|||
|
|
- Référence nulle : l'expression est sautée et la valeur rendue effacée (UE `Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:3234-3245`,
|
|||
|
|
message `Accessed None` l. 3208-3216) : `AIDummy.SaveSlot` vaudrait 0 sur un nul, donc `IfThenElse_148.then`.
|
|||
|
|
- `UBlueprint::GetAllGraphs` parcourt aussi `MacroGraphs` (UE `Runtime\Engine\Private\Blueprint.cpp:1969-1991`) ;
|
|||
|
|
`LPDumpFunction` filtre ce tableau par `Graph->GetName()` (`Source\LifePlayEditorTools\LPDumpFunctionCommandlet.cpp:131-158`) :
|
|||
|
|
`-Fn=Async` est accepté (précédent : `=== GI :: Async (20 noeuds) ===`, L6f:49498).
|
|||
|
|
|
|||
|
|
## 7. Critères de VAL (commande → attendu)
|
|||
|
|
|
|||
|
|
1. **V1** — dump après IMPL :
|
|||
|
|
`.\Tools\RunCommandlet.ps1 -Lot STE-H5b -Commandlet LPDumpFunction -Args '-BP=GI -Fn=EventGraph -Pins -Exec'` →
|
|||
|
|
`=== GI :: EventGraph (6234 noeuds) ===` (6235 avec le repli de l'étape 6) ; diff avec EG : seulement le bloc H5-A et
|
|||
|
|
les lignes de `IfThenElse_148.else`, `CallFunction_743.execute`, `MakeArray_3` à `_6` (`Array`), conformes au § 2.4.
|
|||
|
|
2. **V2** — `compile_blueprint` sans diagnostic (IMPL) ; `CompileAllBlueprints` éditeur fermé : 0 erreur, 0 avertissement
|
|||
|
|
nouveau (lp-validate).
|
|||
|
|
3. **V3** — MD5 de `GI.uasset` ≠ `b8b97c46…`, relevé au CHG ; `git status --short -- Content Config Source` = ` M Content/BPs/GI.uasset` seul.
|
|||
|
|
4. **V4** — P3 conforme (A1, A2, A3) et joint au lot (`dumps\GI__AddVariable.txt`).
|
|||
|
|
5. **V5** — J4 en jeu (build Development, parcours de `STE-J3.md:10-16` **plus une rafale** : déplacement par la carte vers
|
|||
|
|
un autre lieu, lignes `UPDATE TIME ARRIVED` / `LOAD ROOM`, et une action de plusieurs heures), Grep en mode `count` sur
|
|||
|
|
le `LifePlay.log` de J4, J3 (L3) comme référence :
|
|||
|
|
|
|||
|
|
| Motif | Attendu J4 | J3 (L3) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `hours by 0` | 0 | 0 (`STE-VAL.md:360`) ; L2 : 24 |
|
|||
|
|
| `hours by [^0]` / `CHECKING:` | ≥ 46 / 5 490 (0,0084) | 46 / 5 490 (`STE-VAL.md:363-364`) |
|
|||
|
|
| `LogBlueprintUserMessages: \[AI\] ` / `CHECKING:` | ≥ 48 / 5 490 (0,0087) | 48 (rejoué) |
|
|||
|
|
| `DestroyDummy was from AddVariable` | même ordre que J3 (pas de rafale liée aux routines) | 3 (rejoué) |
|
|||
|
|
| `Person_24_4B712C904750AD4CE1580DBEBD9E7396' of length 0` / `CHECKING:` | ≤ 0,042 | 232 / 5 490 (`STE-VAL.md:361`) |
|
|||
|
|
| `property Person in`, `CallFunc_Array_Get_Item_3[0-3] `, `_41 ` | 0 | 0 (`STE-VAL.md:356-358`) |
|
|||
|
|
| `DestroyDummy was from CheckScenes, WHO unmatched` | > 0 (des vidages ont eu lieu pendant le parcours) | 106 (rejoué) |
|
|||
|
|
|
|||
|
|
Puis `& .\Tools\LogStats.ps1 -Compare <journal J4>,<L3>` : aucune famille d'erreur ou d'avertissement nouvelle. Les
|
|||
|
|
ratios dépendent du parcours (R7) ; un ratio en baisse est rapporté, pas corrigé. Selon Q-H5b-1 (b) : témoin = 0.
|
|||
|
|
6. **V6** — à l'œil (utilisateur) : comportement des PNJ en ville après un déplacement et en temps accéléré ; scènes du
|
|||
|
|
joueur et interface inchangées (§ 1, « Ce que l'utilisateur verra »).
|
|||
|
|
|
|||
|
|
Fichiers à commiter (dépôt jeu, branche `ue5.8`, ticket au gardien) :
|
|||
|
|
- A `Docs/Migration/etat/ST/STE/STE-H5b-SPEC.md`
|
|||
|
|
|
|||
|
|
Tours : 48 / 60
|
|||
|
|
|
|||
|
|
## Constats
|
|||
|
|
|
|||
|
|
- Établi : nœuds et liens du § 2.2 (EG) ; MD5 et propreté git de `GI.uasset` (commandes en tête) ; condition de
|
|||
|
|
`MacroInstance_45` (5 termes, dont `Not(SceneOn)` et `Len(followUp) == 0`) ; `.lpai` exécutés dans `PlayScene` par une
|
|||
|
|
`WhileLoop` sans suspension pour le contenu actuel ; flux de données d'`AddVariable` et de `Async` en 5.5 (L6f) ;
|
|||
|
|
comptes rejoués sur L2 et L3 (ci-dessous).
|
|||
|
|
- Estimé : sémantique d'exécution d'`AddVariable` (A1-A3, P3) ; nom d'objet `AI` de `AIDummy` ; réévaluation des nœuds purs ;
|
|||
|
|
absence de ligne lourde dans `random.lpai:10` (la seule correspondance de `generate|dress` dans les `.lpai` avec le motif
|
|||
|
|
large est une liste de lieux, `beauty.lpai:12` contient `hairdresser`).
|
|||
|
|
- Incertain : écrivain de `GI.AIDummy` (hors GI, non cherché dans les autres Blueprints : aucun dump) ; sortie exacte
|
|||
|
|
d'`Async` quand la condition devient fausse (utile à F-b seulement).
|
|||
|
|
- Comptes (Grep, mode `count`) : L2 ` Add Variable|via AddVariable|DestroyDummy was from AddVariable` → 0 ; L2
|
|||
|
|
`hours by|WHO unmatched|index SPAWN` → 339 (lecture du fichier vérifiée) ; L2 `LogBlueprintUserMessages: \[AI\] ` → 9 ;
|
|||
|
|
L3 même motif → 48 ; L3 `via AddVariable|DestroyDummy was from AddVariable| Add Variable` → 6 ; L3 `WHO unmatched` → 106 ;
|
|||
|
|
L3 `hours by [1-9]` → 46 ; `Content\Modules` : 17 `.lpai` (Glob), `AI.` dans `*.lpai` → 95, dans `*.lpscene` → 0,
|
|||
|
|
`^\s*AI\s*=` → 0.
|
|||
|
|
- Dumps 5.8 manquants : `AddVariable` (P3, bloquant), `Async` (facultatif), `GetVariable` (non nécessaire : F-a n'en dépend
|
|||
|
|
pas ; flux 5.5 en L6f:10274). Aucun dump 5.8 n'a de broches d'exécution pour ces graphes (Glob `**/*AddVariable*`,
|
|||
|
|
`**/*Async*` sous `etat\` : 0 ; seul L6f les contient, sans `-Exec`).
|
|||
|
|
- Écart à la fiche : aucun. Point (b) traité par P3 (préalable bloquant) plutôt que par un arrêt de la SPEC, parce que
|
|||
|
|
l'appel ajouté est identique à `CallFunction_739`, déjà exécuté à chaque PNJ.
|