LifePlay-Revival/Docs/Migration/etat/ST/STE/STE-ORG5-I2-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

45 KiB
Raw Blame History

Fiche STE-ORG5-I2 — Niveau N3 — Modèle prévu (modèle de l'agent lp-cart) | utilisé claude-sonnet-5-5 — Agent lp-cart — Skills sk-protocol (préchargés avec l'agent : ue-cpp-build, lp-commandlets, lp-paths)

STE-ORG5-I2-SPEC : affichage à l'écran des récompenses ORG5 appliquées, au même emplacement que chez l'auteur (2026-10-14)

Hors gabarit : les sections suivent l'ordre du gabarit SPEC (1 objectif, 2 fichiers touchés et ordre, 3 repli, 4 risques et questions, 5 interdits, 6 valeurs moteur, 7 critères de VAL) ; les rubriques demandées par la fiche (rubrique 7) sont des sous-sections : affichage chez l'auteur = 1.1 à 1.3, état chez nous = 1.4, options et recommandation = 2.1, conception = 2.2, désactivation = 4.2, estimation et build visé = 4.1, V5 / V6 = 7, Q# = 4.4. Annexes A (commandes et lectures, comptes bruts) et B (établi / estimé / incertain) après la section 7. Lecture seule : ni éditeur, ni MCP, ni commandlet, ni build, ni jeu ; l'exécutable de l'auteur n'a pas été lancé ; aucun fichier du projet modifié hormis ce livrable. Aucun refus du crochet (outil de lecture : sept scripts Python en fichier dans le scratchpad de session, lancés par python -I, aucune écriture hors scratchpad ; aucun > ni < dans une commande Bash). Constat majeur : l'emplacement de l'auteur n'est pas un point fixe de l'écran : c'est un conteneur dans le menu d'actions (Cursor → EquipMenu → BonusRewardBox), qui existe chez nous sans sa dernière case ; l'option recommandée ajoute cette case depuis le C++, sans modifier d'asset. Cela corrige deux points de STE-ORG5-SPEC.md (E8 et E9, voir 1.1 A6 et A7) et rend la position « à droite au centre » de Q-ORG5-7 caduque. Cette troisième version du livrable corrige la première sur quatre points de forme (nombre de tours, une attribution de fonction en annexe B, le décompte des arbres secondaires en annexe A, et un contrôle d'annexe A.12 qui recopiait par erreur le motif de recherche d'un nom d'utilisateur) ; le fond est inchangé.

Sigles

Sigle Chemin Contenu
A D:\XxX\LifePlay_2023_04_Stable_64bit\LifePlay\Content build cuit de l'auteur, version 2023.04 Stable (Docs\Change_Logs.txt:1), UE 4.20, sans .pak ; les nombres après A\…\X.uexp sont des offsets d'octets absolus du .uexp ; les assets n'ont pas de numéro de ligne : les widgets sont cités par nom et numéro d'export
O D:\Game\LifePlay-master 5.8\Content nos assets (copie 5.8)
DMP D:\Game\LifePlay-master 5.8\Docs\Migration\etat\portage55\L6e\L6e-CX-B09_LPDumpWidgetTree_20260930-083639.txt dump LPDumpWidgetTree de nos widgets, daté 2026-09-30 (antérieur au resave U5, 809102a du 2026-10-08)
SPEC5 D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\STE-ORG5-SPEC.md SPEC amont (E1 à E10, décisions de l'utilisateur)
I1 D:\Game\LifePlay-master 5.8\Source\LifePlay\LPBonusRewardSubsystem.h et .cpp code V-min (ORG5-I1, construit dans COMMUN-6)
ENG D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime moteur 5.8 installé
S scratchpad de session scripts de lecture : wtree.py (arbre et propriétés d'un asset cuit), wt2.py (arbre par numéro d'export), bnd2.py (liaisons Bindings), bind.py, names.py (noms par asset), dumpq.py (dump compact), ctx.py ; réutilise kdis.py, kdbg.py, kall.py de SPEC5

1. Objectif mesurable

Après un orgasme dont la récompense ORG5 a été appliquée par I1, une ligne « Bonus : prénom stat ±valeur » apparaît dans le menu d'actions de la scène, dans le panneau EquipMenu, sous la grille d'objets, là où l'auteur affiche son en-tête vert et ses boutons de récompense. Critères mesurables (repris en 7) : (i) la ligne est un enfant de EquipMenu ajouté en dernière position, donc visible seulement menu ouvert et GI.SexOn vrai ; (ii) le texte suit le journal d'I1 (%+.1f) ; (iii) une ligne par récompense appliquée, remplacée à l'orgasme suivant ; (iv) bDisabled vrai : ni application ni ligne ; (v) 0 asset modifié, 0 module ajouté ; (vi) aucun widget créé dans la pile de l'événement Cum.

1.1 Affichage chez l'auteur (build 2023.04 Stable, lu par décodage de A\UI\Cursor et A\UI\BonusReward)

Lecture : tout est tiré de l'en-tête .uasset et des propriétés étiquetées du .uexp (scripts S, annexe A points 1 à 6), pas du jeu en marche.

# Constat Source Niveau
A1 Arbre : HorizontalBox_2 (racine) → VerticalBox_1 → BelowCursor (CanvasPanel) → EquipMenu (VerticalBox) → BonusRewardBox (VerticalBox, variable) → deux enfants : TextBlock_0 (en-tête vert) et ScrollBonuses (ScrollBox). Les deux ont 5 ancêtres wt2.py … path 718 TextBlock_0,ScrollBonuses : « TextBlock_0#653 : 5 ancêtres », « ScrollBonuses#579 : 5 ancêtres » (A.3) établi
A2 BelowCursor : Visibility = Collapsed par défaut, slot Padding Top = 10 ; trois enfants : VerticalBox_0 (recherche + liste d'actions, offsets Right 300, Bottom 200), SexExclusives (rangée de boutons et raccourcis, offsets Top 210, Right 300, Bottom 150), EquipMenu wt2.py … tree 718 0 (A.3, noeuds=62) établi
A3 EquipMenu : RenderOpacity = 0.7, dans un CanvasPanelSlot aux offsets Left 310, Right 300, Bottom 500 (ancres par défaut : x = 310, largeur 300, hauteur 500 ; sens des offsets : estimé) ; quatre enfants dans l'ordre : HorizontalBox_3 (bouton ShuffleActor, texte ActionText = nom de la personne, police 18), ClothesBoxB (WrapBox de SexAction_C : la grille d'objets), ScrollBox_3 (Fill, contient BondageBox), BonusRewardBox (Fill) wtree.py … show EquipMenu : Slots = [VerticalBoxSlot_0 … _3] ; tree 718 ; show ActionText (A.3) établi
A4 BonusRewardBox : slot Fill. TextBlock_0 : slot Padding L10 T10 R10 B2, HAlign Left, VAlign Center ; texte « Pick bonus reward for last person who came » ; couleur (0,1,0,1) (vert) ; police Bold Italic 15, contour 2 noir alpha 0,8. ScrollBonuses : slot Padding L10 T5 R10, Fill, orientation et barre par défaut wt2.py … path 718 (propriétés et slots) établi
A5 Ligne de récompense : widget BonusReward = Button RewardButton (fond Normal teinte (0,0,0,0) donc transparent, survol noir 0,5, pressé noir 0,7, son ui_positive) contenant RewardText (TextBlock police 16, contour 2 noir 0,8, aucune couleur écrite donc blanc par défaut, slot Padding L10 R10, HAlign Left) ; texte lié à Get_RewardText_Text_0 (« {stat} {sign}{change} », SPEC5 E7) wt2.py BonusReward… tree 34 1 ; wtree.py … show RewardText 1500 ; wtree.py … show RewardButton 1200 ; bnd2.py … BonusReward_C : Bindings n=1 (A.4) établi (blanc : estimé)
A6 L'en-tête n'est pas lié : Bindings de Cursor_C = 8 entrées (DistanceText, SexExclusives, StripAllButton, MoveHomeButton : Visibility ; ActionText : Text ; ScrollBox_1, EquipMenu, ScrollBox_3 : Visibility), aucune pour TextBlock_0 : la fonction Get_TextBlock_0_Text_0 (SPEC5 E8 : trois textes selon DisableBonus et le contenu de ScrollBonuses) existe mais n'est branchée à aucune propriété ; le texte affiché est le texte statique, ce que montrent les trois captures (avant et après l'orgasme : même phrase) bnd2.py … Cursor_C : Bindings (ArrayProperty) = structarray DelegateRuntimeBinding n=8 (A.4) établi ; corrige SPEC5 § 1.1 E8
A7 Visibilité : EquipMenu.Visibility est lié à Get_SexExclusives_Visibility_0 (entrée 7 des Bindings), fonction qui rend « visible » si GI.SexOn, « replié » sinon ; BonusRewardBox (enfant d'EquipMenu) n'est donc visible qu'en scène. ScrollBox_3 est lié à Get_BondageBox_Visibility_0 (non décodée) Cursor.uexp:19759-19818 (JUMPIFNOT GI.SexOn, valeurs byte0 puis byte1) ; ordre des énumérateurs SlateWrapperTypes.h:24-32 (Visible = 0, Collapsed = 1) établi ; résout SPEC5 § 1.1 E9
A8 Ouverture du menu : BelowCursor est affiché par PC : en scène (GI.SexOn), si le menu est déjà visible → ExitSexActions, sinon MoveCursorToMouse, BelowCursor.SetVisibility(Visible), mode d'entrée jeu + interface, focus du filtre. Masqué (Collapsed) par PC et GI A\BPs\PC.uexp:23886 (test SexOn), :23960 (ExitSexActions), :24025 (MoveCursorToMouse), :24049 (SetVisibility(byte0)) ; masquage PC.uexp:22307, :29250, GI.uexp:804580 (byte1) établi (déclencheur = clic : estimé, voir 1.3 D7)
A9 Position à l'écran : le widget Cursor suit la souris tant que BelowCursor n'est pas visible (SetPositionInViewport(GetMousePositionOnViewport) quand IsVisible est faux) ; menu ouvert, sa position reste celle du clic. L'emplacement n'est donc pas fixe : il est celui du menu, d'où les trois positions des captures A\UI\Cursor.uexp:12566-12636 (GetMousePositionOnViewport :12566, SetPositionInViewport :12597, IsVisible :12610, JUMPIFNOT :12636) établi

1.2 Mesures sur les trois captures (lecture visuelle, images de 2000 px de large, ±5 px)

Capture Bord gauche de la grille Bord gauche de l'en-tête vert Écart Ordonnée de l'en-tête Bas de la grille
1 (avant l'orgasme, dialogue affiché) ≈ 865 ≈ 875 ≈ 10 ≈ 625 ≈ 570
2 (avant l'orgasme) ≈ 532 ≈ 545 ≈ 13 ≈ 698 ≈ 625
3 (après l'orgasme, deux boutons) ≈ 551 ≈ 563 ≈ 12 ≈ 690 ≈ 625
  • L'en-tête est aligné à gauche sur la grille d'objets (+ 10 unités de Padding du slot, soit ≈ 12 px : échelle d'interface ≈ 1,2, estimée par 373 px entre le champ de recherche et la grille de la capture 3 pour 310 unités). Il est sous la grille : de ≈ 55 à ≈ 73 px entre le bas des tuiles et le milieu du texte ; le surplus par rapport au Padding (≈ 30 à 45 px) peut venir de ScrollBox_3 (Fill, lié à Get_BondageBox_Visibility_0, non décodée) : non établi.
  • Capture 3 : boutons « prone_to_orgasm -1.1 » et « fitness +1.1 » à ≈ 578 px (bord gauche), ordonnées ≈ 733 et ≈ 776 (pas ≈ 43 px), texte blanc à contour noir, sous le même panneau translucide (0,7). Format du nombre : signe + ou - collé au nombre, un chiffre après la virgule ici (« 1.1 »), séparé de la stat par une espace.
  • Capture 1 : l'en-tête vert chevauche la ligne de dialogue (sous-titre) : le panneau n'est pas écrêté et n'est pas masqué par les sous-titres (estimé : ordre d'affichage).

1.3 Durée d'affichage et fin de scène chez l'auteur

# Constat Source Niveau
D1 Apparition : après RetriggerableDelay(0,2 s), ScrollBonuses.ClearChildren() puis une ligne par récompense retenue (3 au plus) SPEC5 E3 à E6 (Cursor.uexp:7141-8896) établi
D2 Disparition au clic sur une ligne : ModifyStat puis GetParent().ClearChildren() (retire les trois boutons) A\UI\BonusReward.uexp:1115-1201 (SPEC5 E7) établi
D3 Disparition au remplacement : le prochain PopulateBonusRewards commence par ClearChildren Cursor.uexp:7198 (SPEC5 E4) établi
D4 Aucune minuterie, aucun nettoyage de fin de scène : ScrollBonuses, lastCum, countBR, statTags ne sont cités que par PopulateBonusRewards et Get_TextBlock_0_Text_0 (45 fonctions décodées, 15 correspondances) ; le seul RetriggerableDelay de la voie du bonus est celui de 0,2 s SPEC5 annexe A point 8 ; relu : kall.py sur BelowCursor|BonusRewardBox|EquipMenu → fonctions=45 decodees=45 correspondances=3, toutes des IsVisible (A.5) établi
D5 Hors scène (SexOn faux), EquipMenu est replié (A7) donc rien n'est visible, mais les lignes non cliquées ne sont pas retirées : elles reparaissent à l'ouverture du menu d'une scène suivante, avant tout nouvel orgasme D4 + A7 estimé (jamais vu en jeu)
D6 L'en-tête reste affiché menu ouvert en scène, avec ou sans récompense (texte statique, A6) A6 établi
D7 Le menu ne s'ouvre que par un clic en scène : le journal de l'auteur dit « click anywhere » (Docs\Change_Logs.txt:39) et la capture 1 affiche « Click any mouse button for action menu » texte de la capture ; Change_Logs.txt:39 estimé pour le déclencheur (le bytecode de PC montre l'ouverture, pas l'événement d'entrée)

1.4 État chez nous (5.8)

# Constat Source Niveau
N1 Cursor de chez nous a le même arbre jusqu'à EquipMenu : EquipMenu VerticalBox (DMP:10566, RenderOpacity 0.7, offsets Left 310, Right 300, Bottom 350 : 500 chez l'auteur) avec trois enfants : HorizontalBox_3 (DMP:10575 ; ShuffleActor :10581, ActionText :10590), ClothesBoxB WrapBox (DMP:10602, SexAction_C :10610), ScrollBox_3 Fill (DMP:10615, BondageBox :10622) ; BelowCursor CanvasPanel Collapsed, Padding Top 10 (DMP:10204) dumpq.py DMP 10122 : widgets: 59 (lignes 10122 a 10628) (A.7) établi pour le dump (antérieur au resave U5) ; estimé inchangé
N2 Absents chez nous : BonusRewardBox, ScrollBonuses, TextBlock_0, BonusReward : aucun des 59 widgets du dump ; noms absents de O\UI\Cursor.uasset (Grep des noms : seuls BelowCursor, ShuffleActor, ClothesBoxB, EquipMenu, ActionText, BondageBox, DistanceText, ScrollBox_3, SexAction sortent) A.7 et A.8 ; SPEC5 annexe A point 10 (ScrollBonuses 0, TextBlock_0 0) établi
N3 Cursor.uasset : dernier commit 809102a (2026-10-08, resave U5) ; git status --porcelain -- Content/UI/Cursor.uasset : sortie vide (propre : aucun autre lot ne le modifie) A.9 établi
N4 GI.uasset et PC.uasset de chez nous contiennent le nom BelowCursor (un par fichier) : même logique d'ouverture estimée (non décodée) A.8 estimé
N5 I1 lit déjà GI par réflexion : FindProp .cpp:22, ReadObject :131-142, ReadObjects :144, SexOn :347-361 ; FirePending :434-449 (hors pile de Cum, tempo de 0,2 s) ; OnOrgasm :451-486 : bDisabled :456-460, ModifyStat :480, journal :484 ; membres privés .h:100-118 lu établi
N6 Build.cs : "Slate", "SlateCore", "UMG" déjà en dépendances privées Source\LifePlay\LifePlay.Build.cs:38 établi
N7 Essai COMMUN-6 : « J11 : OK, avec réserve : ORG5 : 3 récompenses appliquées, au journal seulement ; rien n'est visible à l'écran (affichage à décider) » : I1 est dans le build COMMUN-6 Docs\Migration\etat\BOSS\COMMUN-6-BILAN.md:22 ; COMMUN-6-TESTS.md:44 établi
N8 Précédents C++ de ce mode d'ajout : lignes injectées dans un conteneur existant LPColorGradeSubsystem.cpp:188-302 (NewObject + AddChild, police copiée d'un widget voisin :289-297) ; recherche d'un UVerticalBox par nom avec filtre du propriétaire LPSettingsSubsystem.cpp:564-575, AddChild → UVerticalBoxSlot :734-737 ; GetWidgetFromName LPModInfoSubsystem.cpp:75,105,113 lu établi

2. Fichiers touchés, ordre

2.1 Options comparées et recommandation

Critère (a) Ajouter la case manquante dans EquipMenu depuis le C++ (recommandée) (b) Superposition C++ autonome (Slate ou UMG) (c) Modifier Cursor par MCP
Principe GI.Cursor → GetWidgetFromName("EquipMenu") → NewObject d'un UVerticalBox « LPBonusRewardBox » + UTextBlock ajoutés par AddChild comme dernier enfant, soit l'indice 3 de l'auteur widget ajouté à la fenêtre (AddViewportWidgetContent) et placé aux coordonnées écran d'EquipMenu (GetCachedGeometry) créer BonusRewardBox + ScrollBonuses dans l'arbre de Cursor ; le C++ y ajoute les lignes
Fichiers touchés LPBonusRewardSubsystem.h et .cpp (I1, modifiés, ≈ + 150 lignes) ; aucun asset, aucun Build.cs les mêmes + un widget Slate (≈ + 250 lignes) Content/UI/Cursor.uasset (propre aujourd'hui, N3) + les deux fichiers C++
Emplacement « au même endroit » par construction : même parent, même indice que l'auteur ; suit le menu (A9) et sa visibilité (A7, A8) sans code à reconstituer : suivre la position figée au clic, BelowCursor visible, SexOn, RenderOpacity 0,7, ordre d'affichage ; non identique identique à l'auteur
Compatibilité avec la règle « aucun asset » d'ORG5-I1 oui oui non (viole la règle, rouvre l'exigence « Cursor » de SPEC5 § 2.1 option A)
Risque faible : panneau absent ou d'un autre type → avertissement unique, aucun affichage (R1, R2) moyen : mauvais emplacement, z-order, souris captée (SPEC5 R2) élevé : widgets par MCP non démontrés (SPEC5 4.3 L-M1), jeton UE, resave de Cursor
Tours (estimés) IMPL ≈ 30 ; BUILD ≈ 12 ; VAL V5 ≈ 20 IMPL ≈ 45 ; BUILD ≈ 12 ; VAL ≈ 25 IMPL MCP ≈ 60 à 80 (widgets inconnus) ; build ≈ 12 ; VAL ≈ 25
Sessions MCP / jeton UE 0 / build seul 0 / build seul ≥ 2 / éditeur + build
Réutilisable pour la grille V-1 (boutons cliquables) oui : le même conteneur accueillera des UButton plus tard, position et visibilité déjà bonnes non (panneau Slate de SPEC5 2.4 à refaire) oui
Recommandation : (a). Raisons : (1) c'est le seul chemin qui reproduit l'emplacement de l'auteur sans le reconstituer à la main (A7 à A9) ; (2) aucun asset touché, comme tout ORG5-I1 ; (3) précédent direct dans le dépôt (N8) ; (4) l'échec est inoffensif : sans EquipMenu, on retrouve le comportement actuel (journal seulement). Réserve : l'apparence n'est pas garantie pixel pour pixel (police copiée d'un voisin, 1.2).

2.2 Conception retenue (option a)

Fichiers, ordre : (1) Source/LifePlay/LPBonusRewardSubsystem.h (M) ; (2) Source/LifePlay/LPBonusRewardSubsystem.cpp (M). Aucun autre fichier ; Content/UI/Cursor.uasset interdit.

// .h : declarations avancees  class UPanelWidget;  class UVerticalBox;
private:
    void ShowAppliedReward(const FString& Name, const FString& Stat, double Change);  // appele par OnOrgasm apres ModifyStat reussi
    void ClearRewardDisplay();                                                         // appele par Poll quand SexOn est faux
    UPanelWidget* FindEquipMenu(UObject* GI);                                          // GI.Cursor puis GetWidgetFromName("EquipMenu")
    TWeakObjectPtr<UVerticalBox> RewardBox;                                            // "LPBonusRewardBox", enfant d'EquipMenu

Algorithme de ShowAppliedReward (tout dans la tâche FirePending, jamais dans la pile de Cum) :

  1. GI = GetGameInstance() ; ReadObject(GI, "Cursor") (.cpp:131) → Cast<UUserWidget> ; absent ou d'un autre type : WarnOnce, retour (aucun effet).
  2. Equip = Cast<UPanelWidget>(Cursor->GetWidgetFromName("EquipMenu")) ; absent : WarnOnce, retour.
  3. Si RewardBox n'est pas valide ou RewardBox->GetParent() != Equip : NewObject<UVerticalBox>(Equip, "LPBonusRewardBox"), Equip->AddChild(Box) ; le slot est un UVerticalBoxSlot → SetSize(FSlateChildSize(ESlateSizeRule::Fill)) (comme l'auteur, A4). Si le slot d'Equip est un UCanvasPanelSlot dont GetSize().Y est inférieur à 500 : SetSize(FVector2D(GetSize().X, 500)) (hauteur de l'auteur, A3 ; chez nous 350, N1). Une ligne de journal, une seule fois : Bonus : affichage dans EquipMenu (classe %s, %d enfants).
  4. Box->ClearChildren() (remplacement, comme D3) ; NewObject<UTextBlock>(Box) ; SetText(FText::FromString(Printf("Bonus : %s %s %+.1f", Name, Stat, Change))) (même format que le journal .cpp:484) ; police copiée du ActionText d'EquipMenu si trouvé (précédent LPColorGradeSubsystem.cpp:289-297), puis Size = 16 et OutlineSettings = FFontOutlineSettings(2, FLinearColor(0, 0, 0, 0.8f)) (A5) ; SetColorAndOpacity blanc (1,1,1,1) ; Box->AddChildToVerticalBox(Line) puis SetPadding(FMargin(10, 5, 10, 0)), SetHorizontalAlignment(HAlign_Left) (marges de ScrollBonuses, A4).
  5. Aucune destruction explicite ; aucun gestionnaire de clic (les lignes ne sont pas des boutons) ; le ramasse-miettes reprend les lignes retirées. Points d'appel à modifier dans I1 : OnOrgasm après le journal .cpp:484 (appel de ShowAppliedReward) ; Poll dans le bloc SexOn faux .cpp:355-361 (appel de ClearRewardDisplay : ClearChildren seulement) ; bDisabled : aucun changement (le retour .cpp:456-460 précède tout). Forme (A4, A5, 1.2) : lignes de texte blanc, police 16, contour noir 2 (alpha 0,8), alignées à gauche, marge gauche 10, sous la grille d'objets, dans le panneau d'EquipMenu (opacité 0,7 héritée), visibles menu ouvert en scène. Pas d'en-tête vert (Q-I2-1), pas de bouton. Durée : ligne remplacée à la récompense suivante (comme D3) et retirée à la fin de la scène (SexOn faux) : écart voulu par rapport à l'auteur (D5), car nos lignes ne sont pas cliquables et ne servent plus après la scène (Q-I2-2). Aucune minuterie.

2.3 Ordre

Code (I2) → build (build commun, éditeur fermé) → VAL V5 (hors jeu) → essai V6 en jeu (créneau d'essai du build). Avant tout : décider Q-I2-1 à Q-I2-4.

3. Repli

  • Repli de l'affichage : retirer les deux appels (OnOrgasm, Poll) ou git restore des deux fichiers par le gardien (I1 d'origine gardé) ; aucun asset ni sauvegarde à restaurer. Retour au comportement de COMMUN-6 (récompenses au journal seulement, N7).
  • Repli à l'exécution : EquipMenu ou GI.Cursor introuvable, ou type inattendu : un avertissement Bonus : par cause, aucun effet sur l'application des récompenses.
  • Désactivation sans reconstruire : voir 4.2 (bDisabled, point VAL-11 d'I1 toujours ouvert).
  • L'option (b) n'est pas prévue comme repli (elle n'aurait pas l'emplacement exact).

4. Risques R#, questions Q#

4.1 Estimation et build visé

Périmètre Fiches Tours (estimés) Sessions MCP Session de jeu Appui de l'estimation
IMPL C++ (option a) ORG5-I2a (lp-impl, N3) ≈ 30 (budget 45) 0 0 I1 : 28 à 29 tours pour 786 lignes ; I2 ajoute ≈ 150 lignes avec un risque d'API UMG (précédents N8)
BUILD ORG5-I2b (lp-builder, N1) ≈ 12 0 0 budget des BUILD d'ORG5 (SPEC5 2.6) ; build partagé, éditeur fermé
VAL hors jeu (V5) ORG5-I2v (lp-validator, N2) ≈ 20 0 0 VAL d'I1 : 11 / 40 ; ici lecture de code UMG et comptes de build
Essai en jeu (V6) utilisateur – 0 1 (partagée avec les essais ORG4 du même build) un menu ouvert après un orgasme suffit
Total 3 fiches + 1 essai ≈ 62 0 1 durée d'agent ≈ 1 h 30
Build visé : COMMUN-7 (le prochain build C++ ; BOSS\BOSS-ETAT.md:54 : « build COMMUN-7 (B2r, B2s, C6-b + M1e DirectControl, ORG4) », fiche STG-COMMUN7-BUILD, derrière STE ORG4 Ia puis Ic), à condition que I2a et V5 soient prêtes avant la demande de jeton du build ; sinon COMMUN-8. I1 est déjà dans COMMUN-6 (N7) : I2 modifie des fichiers non encore commités (git status : ?? des deux fichiers ; le gardien doit les commiter avec I2 ou avant). Le code d'affichage est sans effet quand bDisabled, GI.Cursor ou EquipMenu manquent : entrer dans COMMUN-7 avant un essai V6 en jeu ne rend rien pire (SPEC5 3.1, R9, alpha le 2026-10-18).

4.2 Désactivation

  • bDisabled coupe tout, application et affichage : OnOrgasm revient à .cpp:456-460 avant Pick, ModifyStat et l'appel d'affichage, qui n'a lieu qu'après un ModifyStat réussi (.cpp:480). Rien n'est créé, rien n'est affiché, aucune ligne ne reste d'un orgasme précédent : ClearRewardDisplay retire déjà les lignes en fin de scène.
  • Un réglage d'affichage seul n'est pas proposé (Q-I2-4) : il demanderait un second UPROPERTY(config) et une règle de plus pour la VAL.
  • Limite connue, inchangée : le moyen de poser bDisabled=True dans un jeu empaqueté reste le point VAL-11 d'I1.

4.3 Risques

# Risque Parade
R1 GI.Cursor introuvable ou d'un autre type ; EquipMenu absent ou d'un autre type que UPanelWidget (le dump DMP est antérieur au resave U5) ReadObject, Cast, avertissement unique par cause, aucun effet ; journal de la classe et du nombre d'enfants au premier affichage : la VAL V6 le lit
R2 Mise en page : EquipMenu fait 350 chez nous contre 500 chez l'auteur, ScrollBox_3 Fill partage la place restante : la ligne déborde sous le panneau si la liste de bondage est visible SetSize à 500 si inférieur (2.2 point 3, Q-I2-3) ; sinon la ligne déborde sans écrêtage (Clipping par défaut), sans autre conséquence
R3 Plantages autour des orgasmes (Change_Logs.txt:34) : un widget créé dans la pile de Cum création dans FirePending (ticker, 0,2 s après la détection, .cpp:434-449), jamais dans Cum ; aucun gestionnaire de clic ; bDisabled coupe tout
R4 Cycle de vie : Cursor recréé (chargement d'une partie) ; pointeurs périmés TWeakObjectPtr ; le parent de RewardBox est revérifié à chaque affichage ; la boîte est recréée au besoin
R5 La ligne n'est vue que menu ouvert (A7, A8, D7) : un joueur qui n'ouvre pas le menu ne la voit pas identique à l'auteur ; ModifyStat(…, NoDisplay = false) affiche déjà la variation de stat (SPEC5 3.1) ; décision Q-I2-5
R6 Police copiée d'ActionText (taille 18 chez l'auteur) : apparence légèrement différente taille forcée à 16, contour imposé (A5) ; comparaison sur capture en V6
R7 Fichiers d'I1 non commités et construits dans COMMUN-6 : conflit d'état avec le gardien ticket groupé I1 + I2, ou commit d'I1 avant I2 (4.1)
R8 Faux sentiment de preuve : tout vient du bytecode et du dump, rien n'a été vu en jeu (ni chez l'auteur, ni chez nous) V6 obligatoire avant d'écrire « conforme à l'auteur »
R9 FText::FromString et formatage %+.1f : « +2.0 » là où l'auteur écrit un nombre sans zéro final (format FText non lu, SPEC5 § 6) accepté (lisibilité) ; Q-I2-6

4.4 Questions (utilisateur / orchestrateur)

# Question Recommandation
Q-I2-1 Forme : lignes seules (« Bonus : prénom stat ±valeur », blanc 16, comme les boutons de l'auteur), ou ajouter l'en-tête vert de l'auteur (texte à adapter : « Pick … » n'a plus de sens sans choix) ? lignes seules, sans en-tête ; en-tête seulement avec la grille V-1
Q-I2-2 Durée : remplacer à la récompense suivante et retirer en fin de scène (défaut proposé), garder comme l'auteur (périmé d'une scène à l'autre, D5), ou minuterie (ex. 20 s) ? remplacer + retirer en fin de scène
Q-I2-3 Hauteur d'EquipMenu : passer de 350 à 500 à l'exécution comme l'auteur, ou garder 350 ? 500 (seulement si inférieur)
Q-I2-4 bDisabled coupe aussi l'affichage (défaut) ou un second réglage pour l'affichage seul ? un seul réglage
Q-I2-5 Faut-il en plus un signal hors menu (le menu fermé, la ligne est invisible, R5) ? non : « même emplacement que l'auteur » est la décision de l'utilisateur ; J2 dira si c'est insuffisant
Q-I2-6 Nombre sans zéro final (« +2 ») ou toujours une décimale (« +2.0 ») ? une décimale (= journal)
Q-I2-7 Le build de l'auteur décodé est 2023.04 Stable ; les captures de l'utilisateur sont censées venir du même build : l'en-tête statique des captures est bien celui de ce build (A6). Confirmer la version des captures (le registre parle de « v5.26 », nom de la fonctionnalité, COMMUN-6-TESTS.md:21) confirmer, sans effet sur le code
Q-I2-8 La grille V-1 (choix par boutons) : la réaliser dans ce même conteneur (boutons UButton + gestionnaire UFUNCTION, retrait différé) plutôt que par le panneau Slate de SPEC5 2.4 ? oui, à instruire quand V-1 sera décidée (hors fiche)

5. Interdits

  • Aucun asset modifié : ni Cursor, ni BonusReward, ni GI, PC, Person, Room, TopRight ; aucun champ ajouté à GameSettings.sav.
  • Aucune création de widget dans la pile de l'événement Cum ; création seulement depuis FirePending ; aucune destruction d'un widget dans son propre gestionnaire (il n'y en a pas) ; pas de AddToViewport ni de AddViewportWidgetContent (option b écartée).
  • Pas de grille de choix, pas de bouton cliquable (V-1 hors fiche) ; pas d'application de plusieurs récompenses (V-min : la première) ; le bonus ancien (GiveOrgasmReward) reste intact.
  • Pas de repli silencieux : panneau ou propriété introuvable = avertissement unique et abandon ; pas de ProcessEvent nouveau pour l'affichage.
  • Pas de commit par l'agent (le gardien) ; build seulement sous jeton UE, éditeur fermé ; aucun contenu de jeu (noms, textes) dans un skill ou un message de commit public (lp-paths).

6. Valeurs moteur lues dans le moteur installé (ENG)

Valeur Source Établi
UUserWidget::GetWidgetFromName(const FName&) const rend WidgetTree ? WidgetTree->FindWidget(Name) : nullptr ; WidgetTree est public ENG\UMG\Public\Blueprint\UserWidget.h:1426, :1524 ; ENG\UMG\Private\UserWidget.cpp:1259-1262 oui
UPanelWidget::AddChild(UWidget*), ClearChildren(), GetChildrenCount(), GetChildAt(int32), RemoveChild ENG\UMG\Public\Components\PanelWidget.h:59, :122, :28, :36, :114 oui
UVerticalBox::AddChildToVerticalBox(UWidget*) rend UVerticalBoxSlot* ; UVerticalBoxSlot::SetPadding(FMargin), SetSize(FSlateChildSize), SetHorizontalAlignment, SetVerticalAlignment …\Components\VerticalBox.h:30 ; …\Components\VerticalBoxSlot.h:53, :58, :63, :68 oui
UTextBlock::SetText(FText), SetColorAndOpacity(FSlateColor), SetFont(FSlateFontInfo), SetFontSize(float) …\Components\TextBlock.h:103, :114, :156, :222 oui
FFontOutlineSettings : OutlineSize (int32), OutlineColor (FLinearColor), constructeur (int32, FLinearColor) ENG\SlateCore\Public\Fonts\SlateFontInfo.h:36, :42, :67, :78 oui
UCanvasPanelSlot::SetSize(FVector2D), GetSize() …\Components\CanvasPanelSlot.h:112, :116 oui
ESlateVisibility : Visible, Collapsed, Hidden, HitTestInvisible, SelfHitTestInvisible dans cet ordre (Visible = 0, Collapsed = 1 : lecture des byte0 / byte1 du bytecode) …\Components\SlateWrapperTypes.h:24, :26, :28, :32 oui (valeurs numériques : ordre de déclaration d'un UENUM sans valeur explicite)
Modules UMG, Slate, SlateCore déjà dépendances du module LifePlay Source\LifePlay\LifePlay.Build.cs:38 oui

7. Critères de VAL (commande → attendu ; « attendu » écrit avant tout lancement)

Convention : V5 = VAL hors jeu (diff, build, lecture), V6 = VAL en jeu (essai du build). Reprise du lot STE (STE-TACHES.md:31 : « VAL V5 PASS … V6 à COMMUN-6 », estimé). Le critère « règle commune ORG4 / ORG5 » de SPEC5 (« V6 : un orgasme = 1 marqueur de base + au plus 1 ligne ORG5 par personne ») est repris en V6-6.

# Fiche Commande ou lecture Attendu
V5-1 V5 git -C "D:/Game/LifePlay-master 5.8" status --porcelain -- Content Source Config aucun asset en plus : Content/UI/Cursor.uasset et BonusReward* absents de la sortie ; seuls Source/LifePlay/LPBonusRewardSubsystem.h et .cpp portent la modification (?? s'ils ne sont pas commités, M sinon)
V5-2 V5 .\Tools\Build.ps1 (éditeur fermé) puis .\Tools\Build.ps1 -FromLog sur le journal code de sortie 0 ; module LifePlay recompilé ; 0 erreur ; 0 avertissement C4996 nouveau sur les deux fichiers
V5-3 V5 Grep dans le .cpp : AddToViewport|AddViewportWidgetContent|CreateWidget|UButton 0 occurrence ; GetWidgetFromName et EquipMenu et LPBonusRewardBox présents
V5-4 V5 lecture : NewObject<UTextBlock> et NewObject<UVerticalBox> seulement dans ShowAppliedReward, appelée uniquement depuis OnOrgasm (donc depuis FirePending), jamais depuis Poll ni Initialize ; Poll n'appelle que ClearRewardDisplay
V5-5 V5 lecture de OnOrgasm l'appel d'affichage vient après ModifyStat réussi et après le retour bDisabled ; le format est Bonus : %s %s %+.1f (même que le journal .cpp:484)
V5-6 V5 lecture de ShowAppliedReward contre 2.2 GI.Cursor → GetWidgetFromName("EquipMenu") → Cast<UPanelWidget> ; parent de RewardBox revérifié ; ClearChildren avant l'ajout ; police 16, contour (2, noir 0,8), marge (10, 5, 10, 0) ; SetSize à 500 seulement si inférieur ; aucun repli silencieux (WarnOnce)
V6-1 V6 scène avec le joueur ; orgasme ; clic pour ouvrir le menu ; capture une ligne « Bonus : prénom stat ±valeur » dans le panneau d'EquipMenu, à droite de la liste d'actions, sous la grille d'objets, bord gauche aligné sur la grille (≈ + 12 px), texte blanc à contour noir, translucide ; à comparer à la capture 3 de l'auteur (1.2)
V6-2 V6 journal avec LogStats.ps1 -Latest <journal> -Pattern 'Bonus :' une ligne affichage dans EquipMenu (classe …, … enfants) une seule fois par session ; par orgasme : orgasme de, puis soit Bonus : prénom stat ±valeur (x=…) soit aucune récompense éligible ; la ligne affichée porte les mêmes prénom, stat et valeur que la ligne de journal
V6-3 V6 second orgasme dans la même scène, menu ouvert la ligne est remplacée, jamais empilée (au plus 1 ligne en V-min)
V6-4 V6 fin de scène, nouvelle scène, menu ouvert avant tout orgasme aucune ligne (écart voulu avec l'auteur, D5)
V6-5 V6 bDisabled=True (réglage d'I1), orgasme, menu ouvert une ligne de journal désactivé ; aucune ligne affichée ; le bonus ancien (mood) est toujours là
V6-6 V6 règle commune ORG4 / ORG5 un orgasme : 1 marqueur de base + au plus 1 ligne ORG5 (et au plus 1 ligne affichée) par personne ; Endsex sans Cum : 0 ligne ORG5
V6-7 V6 menu ouvert pendant un orgasme, menu fermé puis rouvert ; -ScriptStackOnWarnings 0 plantage ; 0 Accessed None ni avertissement Bonus : de type propriété / widget absent nouveau par rapport au journal de référence COMMUN-6

Annexe A. Commandes et lectures, comptes bruts

Aucun > ni < dans les commandes ; A, O, S, DMP, ENG comme dans les sigles. Les scripts sont en lecture seule (Python, python -I).

  1. Classes des exports de Cursor : python -I S/wtree.py A/UI/Cursor.uasset A/UI/Cursor.uexp classes → entre autres 26 CanvasPanel, 11 VerticalBox, 10 HorizontalBox, 6 ScrollBox, 6 WrapBox, 18 TextBlock, 15 WidgetTree, 1 WidgetBlueprintGeneratedClass.
  2. Arbres de widgets : python -I S/wt2.py A/UI/Cursor.uasset A/UI/Cursor.uexp wts → WidgetTree export 718 outer=2 root=E:HorizontalBox_2#381 (l'arbre de la classe), 719 outer=330 (modèle), puis 13 arbres secondaires (12 de racine RandomButton, 1 de racine VerticalBox_0) ; … wtree.py … show WidgetTree 200.
  3. Chemin et arbre : python -I S/wt2.py A/UI/Cursor.uasset A/UI/Cursor.uexp path 718 TextBlock_0,ScrollBonuses 600 → == TextBlock_0#653 : 5 ancetres et == ScrollBonuses#579 : 5 ancetres (chaîne HorizontalBox_2, VerticalBox_1, BelowCursor, EquipMenu, BonusRewardBox) ; … tree 718 0 130 → noeuds=62 ; … wtree.py … show EquipMenu 400, show BelowCursor 400, show ClothesBoxB 400, show ActionText 400, show ScrollBonuses 400, show TextBlock_0 400.
  4. Liaisons : python -I S/bnd2.py A/UI/Cursor.uasset A/UI/Cursor.uexp Cursor_C → Bindings … n=8 (ObjectName : DistanceText, SexExclusives, StripAllButton, MoveHomeButton, ActionText, ScrollBox_1, EquipMenu, ScrollBox_3) ; python -I S/bnd2.py A/UI/BonusReward.uasset A/UI/BonusReward.uexp BonusReward_C → n=1 (RewardText.Text → Get_RewardText_Text_0) ; python -I S/wt2.py A/UI/BonusReward.uasset A/UI/BonusReward.uexp tree 34 1 400 → noeuds=2 ; python -I S/bind.py A/UI/Cursor.uasset A/UI/Cursor.uexp "^Get_|…" (occurrences des noms de fonctions : seule la table des fonctions de la classe cite Get_TextBlock_0_Text_0).
  5. Usages : python -I S/kall.py A/UI/Cursor.uasset A/UI/Cursor.uexp "BelowCursor|BonusRewardBox|EquipMenu" 150 → fonctions=45 decodees=45 correspondances=3 (trois IsVisible). kdbg.py sur Get_SexExclusives_Visibility_0 → decoded=6, ShowCursorAndText → ExecuteUbergraph_Cursor(8419), HideCursorAndText → (8496) (affichent / masquent Image_0, BuildingCursorText, Closed, pas BelowCursor) ; kdbg.py … ExecuteUbergraph_Cursor 12300 13000 230 (positionnement de la souris) ; kdbg.py A/BPs/PC.uasset A/BPs/PC.uexp ExecuteUbergraph_PC 23880 24120 230 (ouverture du menu).
  6. Qui cite BelowCursor chez l'auteur : python -I S/names.py A/BPs "BelowCursor,EquipMenu,BonusRewardBox,ScrollBonuses,Cursor" → GI.uasset : BelowCursor,Cursor, PC.uasset : BelowCursor,Cursor, StreetMap_Static.uasset : BelowCursor,Cursor, Birdview, Person, Room : Cursor ; … names.py A/UI … → Cursor.uasset : BelowCursor,BonusRewardBox,EquipMenu,ScrollBonuses, PersonButton.uasset : BelowCursor, TopRight.uasset : BelowCursor ; kall.py motif BelowCursor : GI correspondances=1 (SetVisibility(byte1)), PC 10 (byte1 ×2, byte0 ×1, IsVisible ×7), TopRight 1, PersonButton 3, StreetMap_Static 2 (SetVisibility(byte0)).
  7. Chez nous : python -X utf8 -I S/dumpq.py DMP 10122 → widgets: 59 (lignes 10122 a 10628) (EquipMenu :10566, ClothesBoxB :10602, ScrollBox_3 :10615, BondageBox :10622) ; git -C "D:/Game/LifePlay-master 5.8" log -1 --format="%h %ad %s" --date=short -- Content/UI/Cursor.uasset → 809102a 2026-10-08 U5 : contenu sous UE 5.8, resave de 590 paquets hors Heavy ….
  8. Noms chez nous : Grep (sortie des noms trouvés, -o) dans O\UI\Cursor.uasset des motifs ScrollBox_3|BondageBox|HorizontalBox_3|ActionText|DistanceText|ClothesBoxB|BonusReward|SexAction : ActionText, BondageBox, ClothesBoxB, DistanceText, ScrollBox_3, SexAction ; aucun BonusReward ; avec EquipMenu|BelowCursor|ShuffleActor|BonusRewardBox|ScrollBonuses : BelowCursor, EquipMenu, ShuffleActor, ClothesBoxB, pas de BonusRewardBox ni ScrollBonuses. Grep BelowCursor en comptage : Content\BPs\PC.uasset:1, Content\BPs\GI.uasset:1.
  9. Dépôt : git -C "D:/Game/LifePlay-master 5.8" status --porcelain -- Content/UI/Cursor.uasset Content/UI/BonusReward.uasset Source/LifePlay/LPBonusRewardSubsystem.h Source/LifePlay/LPBonusRewardSubsystem.cpp → ?? Source/LifePlay/LPBonusRewardSubsystem.cpp, ?? Source/LifePlay/LPBonusRewardSubsystem.h ; rc=0.
  10. Code et registres lus : LPBonusRewardSubsystem.h (119 lignes) ; .cpp:330-530 ; LPColorGradeSubsystem.cpp:170-315 ; STE-TACHES.md:30-31 ; BOSS-ETAT.md:54 ; COMMUN-6-BILAN.md:22 ; Change_Logs.txt:1-50 ; moteur : Grep des lignes citées en section 6.
  11. Les trois captures de l'utilisateur ont été lues (image) ; mesures de 1.2 à l'œil, ±5 px.
  12. Contrôle du livrable : Grep d'un nom d'utilisateur et d'un chemin de profil (le motif n'est pas recopié ici : il contiendrait le nom) → 0 occurrence sur la première version ; Grep \r (comptage) → No matches found (0) ; od -An -v -tx1 STE-ORG5-I2-SPEC.md | tr ' ' '\n' | grep -c '^0d$' → 0 ; git status --porcelain -- Docs/Migration/etat/ST/STE/STE-ORG5-I2-SPEC.md Content Source Config : Content/UI/Cursor.uasset absent, livrable ??. Le contrôle est rejoué sur cette troisième version dans le compte rendu (la seconde version portait le motif du contrôle, donc un nom : détecté par le même Grep, corrigé ici).

Annexe B. Établi / estimé / incertain

  • Établi (décodage du .uasset / .uexp de l'auteur et dump de chez nous) : arbre Cursor jusqu'à BonusRewardBox (A1 à A4) ; style de la ligne (A5) ; en-tête non lié (A6) ; liaison de visibilité d'EquipMenu à SexOn (A7) ; ouverture du menu par PC et suivi de la souris tant que le menu est fermé (A8, A9) ; durée : pas de minuterie, remplacement et clic seuls (D1 à D4, D6) ; chez nous : l'arbre d'EquipMenu sans la dernière case, Cursor propre dans git (N1 à N3) ; COMMUN-6 : récompenses au journal seulement (N7) ; API moteur citées (6).
  • Estimé : sens des offsets du CanvasPanelSlot (x, y, largeur, hauteur) ; échelle d'interface ≈ 1,2 ; couleur blanche par défaut de RewardText ; déclencheur de l'ouverture (clic) ; lignes périmées d'une scène à l'autre (D5) ; Cursor de chez nous inchangé depuis le dump (resave sans perte) ; GI / PC de chez nous ouvrent le menu comme ceux de l'auteur ; tous les tours.
  • Incertain (essai qui trancherait) : comportement en jeu (rien n'a été lancé : un orgasme, un clic, une capture, V6-1) ; type réel de GI.Cursor et de EquipMenu chez nous au chargement (journal du premier affichage, V6-2) ; rôle exact de Get_BondageBox_Visibility_0 et écart vertical de 30 à 45 px (1.2) ; format d'affichage du nombre par l'auteur (FText, 1.2 : « 1.1 » vu, « 2 » ou « 2.0 » non vu) ; effet de la hauteur 500 sur d'autres éléments (aucun lien trouvé : l'ubergraph de Cursor compare à 200 et 300 pour placer le menu près du bord de l'écran, Cursor.uexp:12849-12980, sans lire la hauteur d'EquipMenu ; la fonction qui porte ce code n'est pas identifiée) ; version exacte du build des captures (Q-I2-7).

Tours : 44 / 45 (comptés à la main : 38 jusqu'au premier Write, un contrôle, le second Write, un contrôle, ce troisième Write, un contrôle et la remise du compte rendu ; python -I Tools/FicheStats.py --lot STE fait foi)

Constats

  • F-I2-1 : le panneau de récompenses de l'auteur est dans le menu d'actions (Cursor → BelowCursor → EquipMenu → BonusRewardBox), affiché au clic, replié hors scène ; sa position suit le clic. La « position à droite au centre » de STE-ORG5-SPEC.md Q-ORG5-7 est remplacée par cet emplacement établi.
  • F-I2-2 : l'en-tête vert est un texte statique non lié (8 liaisons dont aucune pour TextBlock_0) : STE-ORG5-SPEC.md § 1.1 E8 (trois textes selon DisableBonus) est inexact pour l'affichage ; la fonction existe mais ne sert pas. STE-ORG-AUDIT.md § 9 est concerné de même.
  • F-I2-3 : EquipMenu est lié à GI.SexOn : l'incertitude de SPEC5 E9 est levée (le panneau n'est visible qu'en scène).
  • F-I2-4 : chez nous EquipMenu mesure 350 de haut contre 500 chez l'auteur (qui l'a agrandi en ajoutant BonusRewardBox) ; l'option recommandée rétablit 500 à l'exécution sans toucher l'asset (Q-I2-3).
  • F-I2-5 : la grille V-1 de SPEC5 (panneau Slate par AddViewportWidgetContent, 2.4) gagnerait à passer par le même conteneur (Q-I2-8) : position et visibilité seraient celles de l'auteur ; le risque d'un panneau qui capte la souris (SPEC5 R2) disparaîtrait.
  • F-I2-6 : le dump DMP est antérieur au resave U5 : relire l'arbre au premier affichage (journal de V6-2) plutôt que de supposer.
  • À verser : lp-commandlets (section « Lire un build cuit », à créer par SPEC5) — arbre d'un asset UMG cuit UE 4.20 : les propriétés sont étiquetées (nom 8 octets, type 8, taille 4, index 4, puis extras de type : structure = nom + 16 octets de GUID ; octet = nom d'énumération ; tableau = type interne ; puis 1 octet de GUID de propriété), les tableaux de structures portent une étiquette interne après le nombre d'éléments ; les liaisons de propriétés d'un widget sont le tableau Bindings (DelegateRuntimeBinding : ObjectName, PropertyName, FunctionName) de l'export de la classe : une fonction Get_X_Y_0 présente dans la classe n'est pas forcément liée ; hiérarchie = Slots du parent → Content de chaque slot ; l'arbre de la classe est le WidgetTree dont l'Outer est la classe. sk-protocol § 7 (ou sk-windows-shell) — python -I ignore PYTHONIOENCODING : pour afficher des caractères hors cp1252 utiliser python -X utf8 -I ; -I n'ajoute pas le dossier du script à sys.path : un script du scratchpad réutilise un autre par exec(open(chemin).read()) ; l'outil Grep sait lire un .uasset donné en chemin (-o, comptes) mais saute les binaires en recherche récursive : utiliser un script (names.py). sk-protocol § 7 aussi — un contrôle « aucun nom d'utilisateur dans le livrable » ne doit pas recopier le motif dans le livrable (le motif contient le nom : le même Grep le détecte). lp-ue-compat ou ue-cpp-build § 0 — UUserWidget::GetWidgetFromName = WidgetTree->FindWidget (UserWidget.cpp:1259-1262) : trouve un widget non variable par son nom, y compris un widget créé dans l'éditeur et jamais déclaré en variable.
  • Fichiers modifiés par cette fiche : le présent livrable seulement (trois Write, les deux derniers corrigeant des points de forme du premier) ; scripts dans le scratchpad de session (hors dépôt).
  • Fichiers à commiter : A D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\STE-ORG5-I2-SPEC.md (dépôt du jeu, branche ue5.8 ; .md suivi sous etat/).

Décisions de l'utilisateur (2026-10-14, session STE)

  • Q-I2-1 : lignes seules (« Bonus : prénom stat ±valeur », blanc 16), sans en-tête vert ni bouton.
  • Q-I2-2 : minuterie de 20 s : chaque ligne disparaît seule 20 s après son affichage (une nouvelle récompense ajoute ou remplace la ligne selon la SPEC ; retrait aussi en fin de scène, ce qui ne change rien à la règle des 20 s) ; à intégrer à l'IMPL I2a (écart à la proposition : minuterie au lieu de « remplacer + retirer »).
  • Q-I2-3 : hauteur d'EquipMenu portée à 500 à l'exécution, seulement si inférieure, depuis le C++.
  • Q-I2-4 : bDisabled coupe tout (application et affichage), un seul réglage.