LifePlay-Revival/Docs/Migration/etat/ST/STE/STE-H3-SPEC.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

26 KiB
Raw Blame History

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.