290 lines
26 KiB
Markdown
290 lines
26 KiB
Markdown
Fiche STE-H3 — Niveau N3 — Modèle prévu (modèle de l'agent lp-cart) | utilisé claude-opus-5-5 — Agent lp-cart — Skills sk-protocol, lp-mcp (§ 3-4), lp-commandlets, lp-paths, ue-cpp-build (préchargés), lp-doc (gabarits)
|
||
# STE-H3-SPEC : gardes F1 / F3 des lectures vides de personnages du moteur de scènes (2026-10-10)
|
||
|
||
Lecture seule stricte (aucun éditeur, commandlet, build ni écriture hors de ce fichier). Copie `D:\Game\LifePlay-master 5.8`,
|
||
branche `ue5.8`. Gabarit SPEC : `D:\Game\LifePlay-master 5.8\.claude\skills\lp-doc\gabarits.md:36-38`.
|
||
**Statut : partiel.** 6 gardes spécifiées (G1 à G6) ; la 4e lecture F3 (`CallFunc_Array_Get_Item_41`) n'est **pas dans
|
||
`CallFunction`** mais dans `CallFunctionReturnBool`, fonction sans dump 5.8 : écart au DIAG, rapporté sans improviser (§ 4, É1).
|
||
G5 dépend de Q2 ; toute l'IMPL dépend de Q1.
|
||
|
||
Abréviations (chemins absolus) :
|
||
- **D** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\dumps\` (dumps 5.8 après STC, `STE-D-INV.md`) ;
|
||
- **RSN** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STA\dumps\GI__ReturnStatIfNeeded.txt` (dump STA, sans en-tête, antérieur à STB et STC) ;
|
||
- **EGB** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STB\dumps\GI__EventGraph.txt` (antérieur à STC ; à reconfirmer sur STE-D2) ;
|
||
- **L2** = `D:\Game\LifePlay_Build_5.8_STB-JEU2_Development\Windows\LifePlay\Saved\Logs\LifePlay.log` (journal STB-JEU2 du DIAG) ;
|
||
- **JM** = `D:\Game\LifePlay-master 5.8\Docs\Journal_Modifications.md` (déplacé par DOCMV, CHG-300 : JM:1135) ; modèle de garde CHG-282 (JM:1153), CHG-283 (JM:1152) ;
|
||
- **UE** = `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\` (moteur installé, lu).
|
||
|
||
État du Blueprint : MD5 de `Content\BPs\GI.uasset` = `d93785fca90e3342575fc0b601cc2e93` (commande
|
||
`md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/GI.uasset"`), égal à celui relevé à la fin de STE-D (`STE-D-INV.md:84`) ;
|
||
`git status --short -- Content/BPs/GI.uasset` vide ; dernier commit `bc4d91f` (STC, CHG-296). Les dumps **D** décrivent
|
||
donc le GI actuel (établi).
|
||
|
||
## 1. Objectif mesurable
|
||
|
||
Supprimer, sans changer ce que le jeu fait, les avertissements des lectures de personnage vide dans GI, sur le parcours
|
||
de STB-JEU2 rejoué (L2 comme référence, comptes par Grep, rejouables) :
|
||
|
||
| Famille (motif Grep sur le journal de jeu) | L2 | Attendu après IMPL |
|
||
|---|---|---|
|
||
| F1 ciblée, multiligne `property Person in not an UClass\r?\n[^\n]*\r?\n\s+Function /Game/BPs/GI\.GI_C:(getActorVar\|setActorVar\|ReturnStatIfNeeded):` | 267 | **0** |
|
||
| F3 de `CallFunction`, `property CallFunc_Array_Get_Item_3[123] ` | 153 (`_31` 96, `_32` 24, `_33` 33) | **0** |
|
||
| F3 de `CallFunctionReturnBool`, `property CallFunc_Array_Get_Item_41 ` | 14 | inchangé (hors SPEC, É1) |
|
||
| F2, `Person_24_4B712C904750AD4CE1580DBEBD9E7396' of length 0` | 351 | en baisse par `CHECKING:` (résidu attendu, § 4 R7) |
|
||
|
||
Gardes spécifiées par fonction : `setActorVar` 1 (G1), `getActorVar` 1 (G2), `ReturnStatIfNeeded` 1 en cascade de
|
||
2 branches (G3), `CallFunction` 3 en cascade (G4 `setCurrentLocation`, G5 `setUntil`, G6 `getBuilding`),
|
||
`CallFunctionReturnBool` 0 (bloquée, É1). Total : 20 nœuds créés, 7 `break_pins`, 42 `connect_pins`, 0 suppression.
|
||
|
||
## 2. Fichiers touchés, ordre
|
||
|
||
Un seul fichier : `Content\BPs\GI.uasset` (graphes de fonction `setActorVar`, `getActorVar`, `ReturnStatIfNeeded`,
|
||
`CallFunction` ; ni l'EventGraph, ni le C++, ni le contenu des modules). Ordre d'exécution : G1, G2, G3, G4, G6, puis G5
|
||
(selon Q2).
|
||
|
||
### 2.0 Préalables (dans l'ordre, avant tout `create_node`)
|
||
- P1. Réponses de l'utilisateur à Q1 (et Q2 pour G5).
|
||
- P2. MD5 de `GI.uasset` = `d93785fc…` ; sinon (STE-GI passe avant H3 d'après `STE-TACHES.md:26-28`) refaire les dumps
|
||
`LPDumpFunction -BP=GI -Fn=<f> -Pins -Exec` des 4 fonctions et comparer les nœuds cités ici (lp-commandlets).
|
||
- P3. Reconfirmer G3 sur un dump 5.8 neuf : RSN date d'avant STB/STC (aucune de ces fonctions n'est citée par CHG-281 à 296,
|
||
JM:1136, 1143-1154 ; `STD-SCN-DIAG.md:74-77`) : `.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=ReturnStatIfNeeded -Pins -Exec'`, à joindre à STE-D2.
|
||
- P4. Gel annoncé (lp-mcp § 1.7), `.\Tools\Snapshot.ps1 -Lot STE-H3` (GI.uasset), `AssetTools.load_asset` `/Game/BPs/GI`.
|
||
- P5. Pour chaque nœud existant cité : `get_node_infos {nodes:[{refPath:"/Game/BPs/GI.GI:<Graphe>.K2Node_<Nom>"}]}` ;
|
||
les liens doivent égaler la colonne « avant » ; tout écart arrête l'IMPL.
|
||
|
||
### 2.1 Conventions communes
|
||
- `type_id` (lp-mcp § 3) : Branch = `Utilitaires|ContrôleDeFlux|Branche` ; IsValid (fonction pure, nœud `K2Node_CallFunction`
|
||
comme en CHG-290) = `Utilitaires|IsValid` ; IsValidIndex = `Utilitaires|Tableau|IsValidIndex`.
|
||
- Indices de broches : par direction, dans l'ordre des broches du nœud (règle déduite de lp-mcp § 3 : Branch entrées
|
||
exécution 0 / Condition 1, sorties then 0 / else 1 ; VariableSet entrées exécution 0 / valeur 1, sortie then 0).
|
||
Branch : indices établis par lp-mcp. **Tous les autres** (IsValid : `Object` en entrée, `ReturnValue` en sortie ;
|
||
IsValidIndex : `TargetArray`, `Index` en entrée, `ReturnValue` en sortie ; broches des nœuds existants) : le nom est
|
||
donné ici, l'indice se **relit par `get_node_infos`** avant `connect_pins` (lp-mcp § 3 « vérifier les autres »), la broche
|
||
`self` cachée des fonctions statiques pouvant compter. Valeurs attendues (estimées, ordre des dumps) : IsValid `Object`
|
||
entrée 1, `ReturnValue` sortie 0 ; IsValidIndex `TargetArray` entrée 1, `Index` entrée 2, `ReturnValue` sortie 0.
|
||
- IsValidIndex : `Index` laissé à `0` (valeur par défaut, à relire) ; `TargetArray` prend le type `Person` au câblage
|
||
(relu en CHG-282 : « tableaux typés Person »).
|
||
- Cascades de `Branch`, jamais de AND / OR / Select purs (lp-mcp § 1.4 et § 3-4). Une sortie de données peut alimenter
|
||
plusieurs entrées : les liens existants restent.
|
||
- Après chaque fonction : `compile_blueprint` + `hyper_blueprint_debug_inspect {blueprintAssetPath:"/Game/BPs/GI.GI", graphName:"<Graphe>"}` = 0 diagnostic ;
|
||
`save_assets {asset_paths:["/Game/BPs/GI"]}` à la fin.
|
||
|
||
### 2.2 G1 — `setActorVar` (F1, 32 dans L2) — D `GI__setActorVar.txt`
|
||
Avant : `K2Node_FunctionEntry_0.then` → `K2Node_IfThenElse_2.execute` (l. 121-122) ; `FunctionEntry_0.Person` →
|
||
`VariableGet_0.self`, `VariableGet_1.self`, `VariableGet_8.self` (l. 123) ; `IfThenElse_2.Condition` ← `BooleanOR`
|
||
(`CommutativeAssociativeBinaryOperator_1`, l. 253-257) de `SaveSlot > 0` (`CallFunction_5`, l. 243-247) et `isPlayer`
|
||
(`VariableGet_8`, l. 258-260) ; `IfThenElse_2.else` (vide) (l. 252).
|
||
Après : `create_node` IsValid `G1-V`, Branch `G1-B` ; `break_pins` (`FunctionEntry_0.then`, `IfThenElse_2.execute`) ;
|
||
`connect_pins` : `FunctionEntry_0.then` → `G1-B.execute` (0) ; `FunctionEntry_0.Person` → `G1-V.Object` ;
|
||
`G1-V.ReturnValue` → `G1-B.Condition` (1) ; `G1-B.then` (0) → `IfThenElse_2.execute`. `G1-B.else` (1) : rien.
|
||
Comportement : personne nulle → aujourd'hui `SaveSlot` lu 0 et `isPlayer` lu faux (valeur effacée,
|
||
UE `Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:3242-3244`), OR faux, `else` vide : rien n'est écrit. Après : rien
|
||
n'est écrit. **Identique** (établi), sans les deux lectures.
|
||
|
||
### 2.3 G2 — `getActorVar` (F1, 142 dans L2) — D `GI__getActorVar.txt`
|
||
Écart au DIAG (`STD-SCN-DIAG.md:161`) : `getActorVar` ne lit pas `Variable.Person[0]` ; `Person` est son paramètre
|
||
d'entrée (l. 121-124). La garde est donc `IsValid(Person)` seul. Même constat pour `setActorVar` (G1).
|
||
Avant : `FunctionEntry_0.then` → `VariableSet_0.execute` (l. 122) ; `Person` → `VariableGet_0.self` (`Prefix`),
|
||
`VariableGet_1.self` (`SaveSlot`) (l. 123) ; `FunctionResult_1.execute` ← `MacroInstance_1.Completed`, `Float = '0.0'` (l. 125-127).
|
||
Après : IsValid `G2-V`, Branch `G2-B` ; `break_pins` (`FunctionEntry_0.then`, `VariableSet_0.execute`) ;
|
||
`connect_pins` : `FunctionEntry_0.then` → `G2-B.execute` ; `FunctionEntry_0.Person` → `G2-V.Object` ;
|
||
`G2-V.ReturnValue` → `G2-B.Condition` ; `G2-B.then` → `VariableSet_0.execute` ; `G2-B.else` → `FunctionResult_1.execute`.
|
||
Comportement : personne nulle → aujourd'hui clé = `"" + "0"` = `"0"` (`Concat_0`, l. 138-142), `Map_Find(ActorVars, "0")`
|
||
(l. 143-148) ; clé absente → tableau vide → `Completed` → `FunctionResult_1` → 0.0. Après : 0.0 directement.
|
||
**Identique si `ActorVars` n'a pas de clé `"0"`** (R1).
|
||
|
||
### 2.4 G3 — `ReturnStatIfNeeded` (F1, 93 dans L2 ; F2 si tableau vide) — RSN (à reconfirmer, P3)
|
||
Avant : `K2Node_CallFunction_81822` (`GetVariable`, Name ← `Split.LeftS`) `.then` → `VariableSet_1019.execute` (RSN:140-145) ;
|
||
`BreakStruct_2805.Person_24_…` → `GetArrayItem_10794.Array` (RSN:146-155), index 0, `Output` → `VariableSet_1019.Person`
|
||
(RSN:156-159) ; `VariableSet_1019.then` → `CallFunction_7798.execute`, `Output_Get` → `7798.self` (RSN:39-43) ;
|
||
`7798` = `Person_C::QueryStat`, `then` → `FunctionResult_7`, `Value` → `FunctionResult_7.Float` (RSN:160-171).
|
||
Les nœuds cités par le DIAG (`_81822` → `_7798`) sont trouvés (établi).
|
||
Après : IsValidIndex `G3-I`, Branch `G3-B1`, IsValid `G3-V`, Branch `G3-B2` ; `break_pins` (`81822.then`,
|
||
`VariableSet_1019.execute`) et (`VariableSet_1019.then`, `7798.execute`) ; `connect_pins` (10) :
|
||
`81822.then` → `G3-B1.execute` ; `BreakStruct_2805.Person_24_4B712C904750AD4CE1580DBEBD9E7396` → `G3-I.TargetArray` ;
|
||
`G3-I.ReturnValue` → `G3-B1.Condition` ; `G3-B1.then` → `VariableSet_1019.execute` ; `G3-B1.else` → `FunctionResult_7.execute` ;
|
||
`VariableSet_1019.then` → `G3-B2.execute` ; `VariableSet_1019.Output_Get` → `G3-V.Object` ; `G3-V.ReturnValue` →
|
||
`G3-B2.Condition` ; `G3-B2.then` → `7798.execute` ; `G3-B2.else` → `FunctionResult_7.execute`.
|
||
Comportement : aujourd'hui `QueryStat` sur nul est sauté (ScriptCore.cpp:3234-3237) et `FunctionResult_7` rend la
|
||
sortie `Value` jamais écrite dans ce cadre, mise à zéro à l'appel (ScriptCore.cpp:987-993) : 0.0 (`STD-SCN-DIAG.md:161`).
|
||
Après : même `FunctionResult_7`, `7798` non exécuté dans ce cadre : 0.0. **Identique** (estimé fort : lecture du VM).
|
||
Pas de `FunctionResult` neuf (aucun `type_id` connu en lp-mcp § 3).
|
||
|
||
### 2.5 G4 — `CallFunction`, commande `setCurrentLocation` (F3 `_32`) — D `GI__CallFunction.txt`
|
||
Avant : cas `SwitchString_22.setCurrentLocation` → `CallFunction_184` (`GetVariable`, l. 5452-5457) ; `BreakStruct_52.Person_24_…`
|
||
→ `GetArrayItem_73` (l. 5460-5473), `Output` → `CallFunction_185.self` ; `CallFunction_186` (`GetVariable(Parameters[0])`)
|
||
`.then` → `CallFunction_185.execute` (l. 5479-5484) ; `185` = `Person_C::setCurrentLocation`, `then` (vide) (l. 5474-5478).
|
||
Après : IsValidIndex `G4-I`, Branch `G4-B1`, IsValid `G4-V`, Branch `G4-B2` ; `break_pins` (`186.then`, `185.execute`) ;
|
||
`connect_pins` (7) : `186.then` → `G4-B1.execute` ; `BreakStruct_52.Person_24_…` → `G4-I.TargetArray` ; `G4-I.ReturnValue`
|
||
→ `G4-B1.Condition` ; `G4-B1.then` → `G4-B2.execute` ; `GetArrayItem_73.Output` → `G4-V.Object` ; `G4-V.ReturnValue` →
|
||
`G4-B2.Condition` ; `G4-B2.then` → `185.execute`. Les deux `else` : rien.
|
||
Comportement : aujourd'hui `186` s'exécute, `185` est sauté, fin. Après : idem. **Identique** (établi).
|
||
|
||
### 2.6 G5 — `CallFunction`, commande `setUntil` (F3 `_31`) — **soumise à Q2**
|
||
Avant : `CallFunction_187` (`GetVariable`, cas `setUntil`, l. 5501-5506) ; `BreakStruct_54.Person_24_…` → `GetArrayItem_75`
|
||
(l. 5509-5522), `Output` → `VariableGet_139.self` (`Prefix`) et `VariableGet_140.self` (`SaveSlot`) (l. 5523-5528) →
|
||
`Concat_5` (l. 5529-5533) → `CallFunction_190.Key` (`Map_Add(AI_Untils)`, l. 5567-5573) et `Concat_1.D` → `PrintString`
|
||
`CallFunction_193` « (test0104) for … hours by <clé> » (l. 5586-5603) ; `CallFunction_189` (`GetVariable(Parameters[0])`)
|
||
`.then` → `190.execute` (l. 5538-5543) ; `190.then` → `193.execute`.
|
||
Après : IsValidIndex `G5-I`, Branch `G5-B1`, IsValid `G5-V`, Branch `G5-B2` ; `break_pins` (`189.then`, `190.execute`) ;
|
||
`connect_pins` (7) : `189.then` → `G5-B1.execute` ; `BreakStruct_54.Person_24_…` → `G5-I.TargetArray` ; `G5-I.ReturnValue`
|
||
→ `G5-B1.Condition` ; `G5-B1.then` → `G5-B2.execute` ; `GetArrayItem_75.Output` → `G5-V.Object` ; `G5-V.ReturnValue` →
|
||
`G5-B2.Condition` ; `G5-B2.then` → `190.execute`. Les deux `else` : rien.
|
||
Comportement : **modifié**. Aujourd'hui, personne nulle : `Map_Add(AI_Untils, "0", Until)` puis la ligne « by 0 ». Après :
|
||
ni l'un ni l'autre. Le chemin d'origine ne peut pas être gardé : le `PrintString` relit `Prefix` / `SaveSlot` du nul (Q2, R2).
|
||
|
||
### 2.7 G6 — `CallFunction`, commande `getBuilding` (F3 `_33`)
|
||
Avant : cas `SwitchString_22.getBuilding` → `CallFunction_178` (`GetVariable`, l. 312, 5339-5344) ; `BreakStruct_48.Person_24_…`
|
||
→ `GetArrayItem_71` (l. 5347-5360), `Output` → `CallFunction_179.self` ; `178.then` → `179.execute` ; `179` =
|
||
`Person_C::getBuilding`, `then` → `CallFunction_180.execute`, `Building` → `180.Building` (l. 5361-5366) ; `180` =
|
||
`AddVariable(VariableName, Class 'Building')` (l. 5373-5384).
|
||
Après : IsValidIndex `G6-I`, Branch `G6-B1`, IsValid `G6-V`, Branch `G6-B2` ; `break_pins` (`178.then`, `179.execute`) ;
|
||
`connect_pins` (9) : `178.then` → `G6-B1.execute` ; `BreakStruct_48.Person_24_…` → `G6-I.TargetArray` ; `G6-I.ReturnValue`
|
||
→ `G6-B1.Condition` ; `G6-B1.then` → `G6-B2.execute` ; `GetArrayItem_71.Output` → `G6-V.Object` ; `G6-V.ReturnValue` →
|
||
`G6-B2.Condition` ; `G6-B2.then` → `179.execute` ; `G6-B1.else` → `180.execute` ; `G6-B2.else` → `180.execute`.
|
||
Comportement : aujourd'hui `179` est sauté et `180` définit la variable avec `Building` nul (sortie jamais écrite, cadre
|
||
mis à zéro, ScriptCore.cpp:987-993). Après : `180` reçoit la même sortie non écrite. **Identique** (estimé fort).
|
||
|
||
### 2.8 Correspondance nom compilé → nœud (F3)
|
||
Le suffixe `_N` de `CallFunc_Array_Get_Item_N` ne se lit pas dans un dump : il vient de l'ordre de compilation (§ 6). La
|
||
correspondance est établie par empreinte dans L2 :
|
||
- les 24 « (test0104) for … hours by 0 » viennent de `setUntil` (`CallFunction` l. 5599-5603 : `by` + `Prefix` + `SaveSlot`,
|
||
soit `"" + "0"` pour un nul ; les PNJ valides impriment `by 1` à `by 4`, 7 lignes `hours by [^0]` dans L2) ;
|
||
- `_31` = 96 = 4 × 24 (`setUntil` évalue deux fois le getter, pour `Map_Add` puis `PrintString`, et lit deux propriétés :
|
||
L2:20353-20387) ; `_32` = 24 = 1 × 24 (`setCurrentLocation`, un appel par routine) ; `_33` = 33 ≥ 24 (`getBuilding`, placé
|
||
avant les branches des routines, deux fois dans `meal.lpai`) ;
|
||
- ordre dans L2:20255-20411 : 3 × `getActorVar`, `_33`, `_32`, `_31` × 4, « by 0 », `setActorVar`, soit la suite de
|
||
`D:\Game\LifePlay-master 5.8\Content\Modules\vin_Base\AI\entertainment.lpai:7-20`.
|
||
Statut : estimé fort (comptes rejouables) ; la VAL le confirme (comptes à 0, § 7).
|
||
|
||
## 3. Repli
|
||
|
||
- Avant écriture : instantané `.\Tools\Snapshot.ps1 -Lot STE-H3` (P4).
|
||
- Échec de compilation, diagnostic ou lien relu différent : arrêt, `.\Tools\Restore.ps1 -Lot STE-H3` éditeur fermé (GI entier,
|
||
pas de retour partiel), puis contrôle MD5 = `d93785fc…`.
|
||
- Après commit (gardien) : `git revert` du commit du ticket STE-H3.
|
||
- G5 se retire seule (refus à Q2) : G4 et G6 restent valables. G1 à G3 sont indépendantes de G4 à G6.
|
||
|
||
## 4. Risques R#, questions Q#
|
||
|
||
Écart É1 (arrêt partiel, fiche § 9) : `CallFunc_Array_Get_Item_41` (14 dans L2) est en tête de pile
|
||
`GI_C:CallFunctionReturnBool` (L2:21377-21397 ; Grep multiligne : 153 lignes F3 sous `CallFunction:` et 14 sous
|
||
`CallFunctionReturnBool:`), sous `PlayScene` → `CheckConditionsOther` → `CheckEachCondition`. Le DIAG la range dans
|
||
`CallFunction` (`STD-SCN-DIAG.md:163`). Aucun dump de `CallFunctionReturnBool` : garde non spécifiée. Commande proposée
|
||
(à joindre à STE-D2) : `.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=CallFunctionReturnBool -Pins -Exec'`.
|
||
Empreinte relevée : 1 F2 puis 2 « Accessed None » par lecture (même forme que `setUntil`).
|
||
|
||
**Q1 (utilisateur) — « acteur vide = état normal ? » (fiche, point (a))** : non tranchable par les seuls dumps. Établi :
|
||
- Cas A, conditions des `.lpscene` (`CheckConditionsOther`, ex. `meet_new_person.lpscene:5`
|
||
`isWithCompanion() && Random(0, 200) < CurrentCompanion:interpersonal`, `STD-SCN-DIAG.md:33-37`) : l'absence est
|
||
prévue par le contenu, qui la teste ; le calcul évalue tous les termes. **État normal** : concerne G3.
|
||
- Cas B, routines d'IA `.lpai` lancées par `PlayScene` (24 démarrages « by 0 », tous entre 21:32:09 et 21:32:21, L2:20397
|
||
à 29995) : la variable `AI` a un tableau `Person` **de longueur 0**, donc elle est absente (L2:20327). Or le moteur
|
||
ajoute toujours `AI` = `[AIDummy]` (longueur 1) avant les routines (EGB:25006-25037 `SpawnDummy` → `AIDummy` →
|
||
`AddVariable('AI')` `CallFunction_779` ; EGB:25149-25171 `AddVariable('AI')` `CallFunction_739`). Le contenu des 17
|
||
`.lpai` suppose `AI` présent (Grep `\.setUntil\(` sous `Content\Modules` : 23 fichiers, dont les 17 `.lpai` de
|
||
`vin_Base\AI`). Estimé : **pas un état normal du contenu**, mais un défaut d'enchaînement du moteur (variable `AI`
|
||
retirée ou jamais posée pendant la rafale ; cause non identifiée). Concerne G1, G2, G4, G5, G6 et une partie de G3.
|
||
- Question : (A) poser maintenant toutes les gardes (résultat identique sauf Q2 et R1 ; le défaut du cas B est masqué et
|
||
instruit à part, hypothèse H5 ci-dessous) ; ou (B) ne poser que G3 (cas A) et instruire H5 d'abord.
|
||
H5 proposée (N3, lp-cart) : pourquoi `AI` est absente pendant la rafale 21:32:09-21 (dumps STE-D2 de l'EventGraph :
|
||
boucle des routines `CheckingAiIndex`, macro `Async` `ItersPerTck = 7` EGB:24915-24918, effacement des variables de scène).
|
||
|
||
**Q2 (utilisateur)** : accepter que G5 supprime l'écriture `AI_Untils["0"]` et la ligne « (test0104) … by 0 » quand `AI` est
|
||
vide ? Sinon G5 est retirée et `_31` (96) reste.
|
||
|
||
**Q3 (utilisateur)** : étendre plus tard aux lectures F2 restantes (sites d'appel `getActorVar` `CallFunction_141`,
|
||
`setActorVar` `CallFunction_150`, `modifyActorVar` `CallFunction_144` qui passent `Person[0]` en paramètre ; `_30`, 4 lignes,
|
||
L2:12087-22406 ; `_41`) ? Chaque cas demande un second appel avec personne nulle sur le chemin « non valide ».
|
||
|
||
Choix C++ / Blueprint (fiche, point (b)) : **gardes Blueprint `IsValidIndex` + `IsValid`**. Motifs : `Person_C` est une
|
||
classe Blueprint, une fonction C++ `GetPersonAtOrNone` ne peut la rendre typée que par un thunk générique (comme
|
||
`Array_Get`) ou rendre `AActor*` / `UObject*` et ajouter un `Cast` par site (compatibilité de type des tableaux en
|
||
Blueprint : non vérifiée) ; il faut de toute façon un `IsValid` + `Branch` avant chaque appel (le nul reste un nul) ;
|
||
coût d'un build C++, d'un test et d'un ticket de plus. CHG-283 a pu le faire car `FString` est natif. Les gardes
|
||
Blueprint suivent CHG-282 / CHG-291 (JM:1153, 1144) : 4 nœuds par site, sans build.
|
||
|
||
- **R1** : clé `"0"` = joueur ? `getActorVar` sur nul lit aujourd'hui `ActorVars["0"]`. Si le joueur a `Prefix` vide et
|
||
`SaveSlot` 0 (estimé : `setActorVar` admet `isPlayer` en plus de `SaveSlot > 0`, D `GI__setActorVar.txt:243-257` ; des
|
||
PNJ ont `Prefix` vide, EGB:25082-25093), la valeur rendue aujourd'hui est celle du joueur, et G2 rend 0.0. Essai qui
|
||
tranche : `LPDumpSave -File=<sauvegarde de la partie STB-JEU2> -Out=<txt>` (lp-commandlets) et recherche d'une clé `"0"`
|
||
dans `ActorVars` et `AI_Untils`.
|
||
- **R2** : G5 change l'état (`AI_Untils["0"]`) et retire le dénominateur « by 0 » du DIAG ; normaliser par `CHECKING:`.
|
||
- **R3** : correspondance `_31` / `_32` / `_33` estimée (§ 2.8) ; la 2e ligne F2 avant chaque `_33` (L2:20327) n'est pas
|
||
expliquée (incertain). Si un compte F3 de `CallFunction` reste > 0 en VAL : rapport, pas de correctif improvisé.
|
||
- **R4** : STE-GI modifie GI avant H3 : noms de nœuds à recontrôler (P2, P5).
|
||
- **R5** : G3 repose sur un dump antérieur à STB / STC (P3).
|
||
- **R6** : évaluation des nœuds purs : `GetArrayItem` est évalué pour `IsValid` puis pour l'appel ; sans avertissement car
|
||
l'index est valide à ce moment-là (UE `Runtime\Engine\Private\KismetArrayLibrary.cpp:466-468`).
|
||
- **R7** : F2 ne tombe pas à 0 : restent les sites de Q3, `_41`, `_30` et les autres lecteurs de `Person[0]` (96 lignes
|
||
`Person_24 ->` dans `CallFunction`, Grep `^LogLPDump: Warning:.*Person_24_4B712C904750AD4CE1580DBEBD9E7396 ->`).
|
||
- **R8** : masquage (Q1 cas B) : les gardes cachent le symptôme d'un défaut d'enchaînement de l'IA.
|
||
|
||
Nœuds de l'EventGraph cités d'après EGB (antérieur à STC), **à reconfirmer sur `dumps\GI__EventGraph.txt` (STE-D2)** :
|
||
`K2Node_CustomEvent_404` `PlayScene` (EGB:6002-6005) ; `K2Node_CommutativeAssociativeBinaryOperator_74` et
|
||
`K2Node_CallFunction_778` (`PrintString` « (test0104) », `execute` vide, donc le fichier joué n'est jamais écrit au
|
||
journal, EGB:25133-25148) ; `K2Node_CallFunction_768` `SpawnDummy` (EGB:25006-25010) ; `K2Node_VariableSet_258`
|
||
`AIDummy` (EGB:25011-25016) ; `K2Node_CallFunction_779` `AddVariable('AI')` et `K2Node_MakeArray_14` (EGB:25017-25037) ;
|
||
`K2Node_CallFunction_739` `AddVariable('AI')`, `K2Node_MakeArray_3`, `K2Node_VariableGet_525` (EGB:25149-25171) ;
|
||
`K2Node_VariableSet_259` `CheckingPkgIndex` (EGB:24862-24866) ; `K2Node_CallFunction_745` `getAllSlots` (EGB:24911-24913) ;
|
||
`K2Node_MacroInstance_49` `Async` (EGB:24915-24918) ; `K2Node_CallFunction_5384` `GetAllFilesFromFolder(Ext 'lpai')`
|
||
(EGB:24638-24643) ; `K2Node_CustomEvent_72` `ContinueAiAfterLoad` (EGB:24623-24625). Aucun n'est modifié par cette SPEC.
|
||
|
||
## 5. Interdits
|
||
|
||
- Aucune modification de l'EventGraph, de `CallFunctionReturnBool` (É1), des sites d'appel de Q3, du C++.
|
||
- Aucune correction de contenu (`Content\Modules\…\*.lpai`, `.lpscene`), aucun nœud AND / OR / Select pur, aucun
|
||
`FunctionResult` créé, aucun `delete_node`.
|
||
- Pas d'écriture HyperAIStudio (lp-mcp § 1.1) ; outils Epic seulement ; pas d'IMPL sans P1 à P5.
|
||
- Pas de commit par l'agent : ticket au gardien (lp-repo).
|
||
|
||
## 6. Valeurs moteur lues dans le moteur installé (UE = `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\`)
|
||
|
||
- Message F1 / F3 : UE `Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:3213-3216` (« Accessed None trying to read
|
||
(real) property X in not an UClass », X = variable qui tient le nul) ; contexte nul : expression sautée et valeur de
|
||
retour effacée, `:3234-3244`.
|
||
- Cadre d'une fonction script non persistant mis à zéro : `ScriptCore.cpp:987-993`.
|
||
- Lecture hors bornes : message F2 puis élément remis à sa valeur par défaut (nul) :
|
||
UE `Runtime\Engine\Private\KismetArrayLibrary.cpp:466-479`.
|
||
- `K2Node_GetArrayItem` non « par référence » devient un appel `Array_Get`, d'où le nom compilé `CallFunc_Array_Get_Item` :
|
||
UE `Editor\BlueprintGraph\Private\K2Node_GetArrayItem.cpp:335-349`.
|
||
- Suffixe `_N` = n-ième doublon du nom de base : UE `Editor\KismetCompiler\Public\KismetCompilerMisc.h:287-302` ; ordre
|
||
d'enregistrement = liste d'exécution linéaire : `Editor\KismetCompiler\Private\KismetCompiler.cpp:3918-3927`, construite
|
||
par `Editor\KismetCompiler\Private\EdGraphCompiler.cpp:244-249` (`RemoveAtSwap`) : le suffixe ne se déduit pas d'un dump.
|
||
|
||
## 7. Critères de VAL (commande → attendu)
|
||
|
||
1. Dumps après IMPL, `.\Tools\RunCommandlet.ps1 -Lot STE-H3 -Commandlet LPDumpFunction -Args '-BP=GI -Fn=<f> -Pins -Exec'` :
|
||
nœuds `setActorVar` 32 → 34, `getActorVar` 15 → 17, `ReturnStatIfNeeded` 95 → 99, `CallFunction` 1 538 → 1 550
|
||
(1 546 sans G5) ; liens du § 2 relus ; `DiffRemplacementNoeuds.py` sans autre écart.
|
||
2. `hyper_blueprint_debug_inspect` 0 diagnostic par graphe ; `CompileAllBlueprints` éditeur fermé : 0 erreur (lp-validate).
|
||
3. Journal de jeu (build Development, parcours de STB-JEU2 : nouvelle partie, ≥ 20 actions seul, ≥ 3 min en ville,
|
||
`STD-SCN-DIAG.md:198-206`), Grep en mode `count` :
|
||
- F1 ciblée multiligne (§ 1) → **0** (L2 : 267) ;
|
||
- `property CallFunc_Array_Get_Item_3[123] ` → **0** (L2 : 153) ; sans G5 : `_3[23]` → 0, `_31` inchangé ;
|
||
- `property CallFunc_Array_Get_Item_41 ` → information seulement (L2 : 14) ;
|
||
- `property Person in` → seulement des lignes hors des 3 fonctions (L2 : 270 dont 267 ciblées) ;
|
||
- `Person_24_4B712C904750AD4CE1580DBEBD9E7396' of length 0` / `CHECKING:` → inférieur à L2 (351 / 2 318 = 0,151) ;
|
||
- `hours by 0` → 0 si G5 (L2 : 24) ; `hours by [1-9]` → ≥ 1 si le parcours contient des routines de PNJ valides
|
||
(L2 : 7), non-régression de l'IA ;
|
||
- dénominateur : `CHECKING:` (L2 : 2 318) ; `index SPAWN` > 0 avant de mesurer (lp-validate § 5).
|
||
4. R1 / Q2 : `LPDumpSave` avant / après sur la même sauvegarde : clés `"0"` de `ActorVars` et `AI_Untils` relevées.
|
||
|
||
Fichiers à commiter (dépôt jeu, branche `ue5.8`, ticket au gardien) :
|
||
- A `Docs/Migration/etat/ST/STE/STE-H3-SPEC.md`
|
||
|
||
Tours : 34 / 60
|
||
|
||
## Constats
|
||
|
||
- Commandes de comptage rejouées (Grep, mode `count`, sur L2) : `property CallFunc_Array_Get_Item_31 ` → 96 ;
|
||
`property CallFunc_Array_Get_Item_32 ` → 24 ; multiligne F3 sous `CallFunction:` → 153, sous `CallFunctionReturnBool:`
|
||
→ 14 (somme 167 = `STD-SCN-DIAG.md:26`) ; F1 multiligne des 3 fonctions → 267 ; `hours by 0` → 24 lignes ;
|
||
`hours by [^0]` → 7 lignes.
|
||
- Dans `CallFunction`, 96 lignes `Person_24 ->` (Grep cité en R7), dont 35 nœuds `GetArrayItem` dont la sortie va à un
|
||
`self` (Grep multiligne `Array +-> K2Node_\w+\.Person_24_\w+\r?\n[^\n]*Dimension 1[^\n]*\r?\n[^\n]*Output +-> [^\n]*\.self`,
|
||
35 blocs) : seuls les 3 de G4 à G6 sont observés en défaut dans L2.
|
||
- Grep d'un dossier sous `etat\` : les `.txt` y sont ignorés par git ; passer `glob` (ex. `*__*.txt`) ou le fichier en
|
||
`path`, sinon 0 résultat trompeur (essai de cette fiche).
|
||
- Journal et gabarits lus aux chemins DOCMV : `D:\Game\LifePlay-master 5.8\Docs\Journal_Modifications.md`,
|
||
`D:\Game\LifePlay-master 5.8\.claude\skills\lp-doc\gabarits.md`.
|