227 lines
45 KiB
Markdown
227 lines
45 KiB
Markdown
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.
|