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

269 lines
45 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Fiche STE-ORG4 — Niveau N3 — Modèle prévu (modèle de l'agent lp-cart) | utilisé claude-sonnet-5-5 — Agent lp-cart — Skills lp-mcp (invoqué : § 1, 3, 4), sk-protocol (préchargés avec l'agent : ue-cpp-build, lp-commandlets, lp-paths)
# STE-ORG4-SPEC : fin de scène par `Endsex` — bonus d'orgasme absent et lecture de `PersonCumming` à None (2026-10-14)
Hors gabarit : les sections suivent le gabarit SPEC de `D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md` (§ 1 à 7, dans l'ordre) ; correspondance avec la rubrique 7 de la fiche : objectif = § 1 ; fichiers et ordre, préalables, correctif au geste près, garde, comportement = § 2 (2.0 à 2.5) ; repli = § 3 ; risques et Q# = § 4 ; interdits = § 5 ; critères V5 / V6 = § 7. Lecture seule stricte (Read, Grep, Glob ; Bash pour `git rev-parse`, `md5sum`, `git status`, `git log`, `ls`, `grep -a -c` sur un binaire) ; aucun éditeur, MCP, commandlet, build ni jeu ; un seul fichier écrit : ce livrable.
## Annonce en tête (fiche, rubrique 3)
- **Person n'est pas touché** par le correctif (aucune écriture, aucun `save_assets` de Person) : il appelle l'événement existant `GiveOrgasmReward` de Person (lecture de son nom seulement). Pas d'annonce à Big Boss pour Person.
- **GI est touché** (`Content\BPs\GI.uasset`, graphe `EventGraph` seul) : le fichier contient déjà deux changements non commités d'autres lots (G16 de STE-H3g, B7-a de STG ; § 2.0). **SexAction (`UI/SexAction.uasset`) n'est pas touché** mais sa lecture (P0) est **bloquante** pour la partie O4b (§ 2.0).
- **Statut : exécutable par MCP en deux parties** : **O4a** (garde `PersonCumming`, sans changement de comportement) est complète et indépendante ; **O4b** (bonus à `Endsex`, changement de comportement voulu) est complète **sous la condition P0** (lecture de `SexAction`) et **sous la réponse Q-ORG4-1** de l'utilisateur.
Abréviations (chemins absolus) :
- **E** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\` ; **GI-P2** = E`ST\STE\dumps\GI__EventGraph_P2.txt` (2026-10-13 16:36, GI `c4817b84`, 6 249 nœuds) ; **P-STG** = E`ST\STG\dumps\Person__EventGraph.txt` (2026-10-13 14:08) ; **PC-STE** = E`ST\STE\dumps\PC__EventGraph.txt` (2026-10-10 11:33) ; **PASA** = E`ST\STA\dumps\GI__PlayAppropriateSexAnimation.txt` (préfixe `LogLPDump: Warning:` sur chaque ligne) ; **L6f-G** = E`portage55\L6f\L6f_dump_GI.txt` (5.5, 2026-09-30).
- **HJ** = E`ST\STE\STE-H3e-J\LifePlay.log` (journal H3e, ligne de commande `-ScriptStackOnWarnings -LPHitchLog`) ; **J6** = `D:\Game\LifePlay_Build_5.8_STE-J6_Development\Windows\LifePlay\Saved\Logs\LifePlay.log`.
- **AUD** = E`ST\STE\STE-ORG-AUDIT.md` ; **H3fD** = `STE-H3f-DIAG.md` ; **H3gS** / **H3gI** / **H3gV** = `STE-H3g-SPEC.md` / `-IMPL.md` / `-VAL.md` ; **H3dI** = `STE-H3d-IMPL.md` (tous dans E`ST\STE\`) ; **B7aI** = E`ST\STG\STG-B7a-IMPL.md` ; **UE** = `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\`.
## État relevé à l'entrée de cette SPEC (C9a de la fiche ; Bash, lecture)
| Commande | Sortie |
|---|---|
| `git rev-parse --short HEAD` | `0745917` |
| `md5sum Content/BPs/GI.uasset` | `e64b5b37c9ef0db7735de0d6e3bd7c1c` |
| `md5sum Content/BPs/Person.uasset` | `8668a47c627f7a5254910cf0ac33b12a` |
| `md5sum Content/BPs/Room.uasset` | `18565365b1195bf2416a1048bf9051b6` |
| `md5sum Content/UI/SexAction.uasset` | `a8705f7fbe1fde1daba0464bfc9ff9b5` |
| `md5sum Content/BPs/PC.uasset` | `a19510e613f31905437bab48f38c6154` |
| `git status --short -- Content/BPs/GI.uasset Content/BPs/Person.uasset Content/BPs/Room.uasset Content/UI/SexAction.uasset Content/BPs/PC.uasset` | ` M` pour GI, Person, Room ; SexAction et PC non listés (= HEAD) |
Historique de GI (établi) : `c4817b84` (dump GI-P2, EventGraph 6 249 nœuds) → G16 de STE-H3g → `0E6AEDCC` (EventGraph **6 253** nœuds, H3gV:13, 29 ; G16 ne modifie que 7 nœuds communs, tous dans `UpdateDefaultHair`, H3gV:32) → B7-a de STG, **fonction `Show` seule** (B7aI:9, 104-110) → `e64b5b37` (valeur actuelle). **Nœuds du chemin `Cum` qui pourraient avoir changé de nom : aucun** (établi : H3gV:32 liste les seuls nœuds modifiés par G16, aucun n'est sur le chemin ; B7a n'a pas touché l'EventGraph) ; le dump GI-P2 reste donc valable pour tout ce qui suit, **sauf** les noms des nœuds à créer (attribués par l'éditeur : à relever). Exiger tout de même P1 / P2 à l'IMPL (§ 2.0).
Dates (établi, `ls -l --time-style=long-iso`) : `Person.uasset` modifié le 2026-10-13 à 17:42, **après** le dump P-STG (14:08) : les nœuds de Person cités ci-dessous (chaîne `Cum` / `GiveOrgasmReward`) viennent d'un dump antérieur à la dernière sauvegarde de Person (STG G-B1a, K8-a : `B239E103…` → `8668A47C…`) ; ces écritures visent d'autres fonctions (estimé) ; P7 le re-vérifie en lecture. `PC-STE` (10-10) précède le commit CAM-B1 qui a touché PC (`git log`, a08c134) : les lignes de PC citées sont descriptives, elles ne conditionnent pas le correctif.
## 1. Objectif mesurable
### 1.1 Cause exacte (chemins `Finish`, auto-100, `Endsex` : où ils divergent)
`Person::Cum` est le **seul écrivain** de `PersonCumming` : `VariableSet_211` (`PersonCumming := self`, `self` du `Set` = GI) P-STG:17592-17599, suite de l'événement `Cum` de Person (P-STG:15920-15922). Dans l'EventGraph de GI : **0 écriture, 1 lecteur** (Grep `PersonCumming` sur GI-P2 → `VariableGet_720` et ses deux lectures de membres, GI-P2:28040-28062). Autres graphes de GI : non lus (§ 4, L-PC).
| Chemin | Séquence (fichier:ligne) | `Person::Cum` appelé ? | `PersonCumming` |
|---|---|---|---|
| **Auto 100** | `UpdateOeChange` → `Sex_Orgasm == 100.0` → `Cum()` (P-STG:16030-16043) | oui | écrit (P-STG:17592) |
| **Finish** (« Cum on … ») | PC propose les animations `Finish` si `Sex_Orgasm >= 90` de `SexPersons[0]` (PC-STE:6075-6092, 6123-6142) → PASA : `Array_Contains(Tags,'Finish')` → `Person_C::Cum()` sur `Persons[0]` (PASA:348-361, 85-90) | oui | écrit |
| **Endsex** | PC crée le widget d'action avec `Animation = 'Endsex'` et le libellé `SelectString('Finish showering','Finish having sex')` (PC-STE:2476-2485, 2668-2673) → clic : `SexAction` imprime `Endsex (Animation String)` (HJ:9242) → pile `SexAction:BndEvt…OnButtonClicked → ExecuteUbergraph_SexAction → GI_C:cum → ExecuteUbergraph_GI → AfterCumSpeed` (HJ:9246-9252) | **non** | **jamais écrit** sur ce chemin |
| **Exhausted** | `Exhausted` → `StopUpdateOeForSexPersons` → `SetText` → `GI.Cum(NoPregCheck true, NoEnd false, NoShake true)` (P-STG:15993-16016) | non (aucun `VariableSet_211` dans cette chaîne) | non écrit (même défaut) |
**Cause** (établi pour les nœuds, estimé pour la valeur des broches de l'appel de `SexAction`) : `GI::Cum` (événement `K2Node_CustomEvent_4245`, GI-P2:10803-10809) n'est pas l'événement d'orgasme : c'est le **traitement d'après-orgasme / de fin de scène** que `Person::Cum` appelle lui-même (`GI.Cum(NoEnd = true)`, P-STG:15936-15973) ; le bouton `Endsex` l'appelle **directement**, sans passer par `Person::Cum`. Ni `GiveOrgasmReward` ni l'écriture de `PersonCumming` ne sont donc atteintes. Les 22 nœuds atteints depuis `GI::Cum` (AUD § 2, parcours d'exécution) ne contiennent ni `Person::Cum` ni `GiveOrgasmReward` ; relu aujourd'hui, seul appelant de `Person_C::Cum` / `GiveOrgasmReward` dans GI-P2 : aucun (Grep `appel [A-Za-z_?]+::(Cum|EndSex|AfterCumSpeed|GiveOrgasmReward)\(\)` → GI-P2:10840 `?::Cum()` (ancien modèle), 10905 `AfterCumSpeed`, 14564 `EndSex` seulement). Même pile en 4.27 (H3fD:74) : **ce n'est pas une régression de portage**, c'est la conception d'origine : `Endsex` n'est pas un orgasme.
Valeur de `PersonCumming` à `Endsex` (estimé) : `None` si personne n'a joui depuis le début de la partie (HJ:9243, 9262 : 2 avertissements pour 1 clic) ; **périmée mais valide** (dernier jouisseur) sinon, car aucune écriture de remise à zéro dans l'EventGraph de GI (explique J6:2849 : un `Endsex`, aucune ligne `PersonCumming` — le Grep de J6 sur `Endsex|PersonCumming|Accessed None` ne rend aucune ligne `PersonCumming`).
Chemin `Endsex` dans GI, avec l'hypothèse `ForceEnd = true` (**estimé fort**, § 4 R-ORG4-1) : `CustomEvent_4245` → `IfThenElse_172` (`CondomBroke`, GI-P2:27020-27024 ; `else` → `VariableSet_329`, 27003-27008) → `IfThenElse_98` (`SexOn`, 22660-22667 ; `else` vide) → **`IfThenElse_875`** (`Condition` = `ForceEnd`, `then` → `CallFunction_11423`, `else` → `MacroInstance_18`, 14554-14558) → `CallFunction_11423` = appel de l'événement `AfterCumSpeed` (10905-10908) → `CallFunction_76765` `StopDirtyTalkAndDescription` → `32952` `DirtyTalkEnd` → `32953` `DescriptionEnd` (10810-10821) → `IfThenElse_188` (`BooleanOR(ForceEnd, NoPregCheck, NoShake)`, 27663-27667, 27914-27919 ; vrai → saute `CheckConceived`) → `CallFunction_38905` `PC::StopUpdateSpeed` (10825-10828) → `IfThenElse_876` (`ForceEnd`, `then` → `CallFunction_4939` `EndSex()`, 14559-14567). `AfterCumSpeed` (événement `CustomEvent_702`, 10896-10898) : `VariableSet_9760` `Rate` ← `SelectFloat(A = 0.0, B = 0.25, bPickA = StartsWith(Race(PersonCumming),'Dog') AND IsMale(PersonCumming))` (10899-10904 ; 28034-28062) puis `CallFunction_960` `UpdateOeDisplayOnly(Feedback = false)` (27658-27662) puis la boucle `MacroInstance_806` sur `SexPersons` (7094-7100).
Preuves que `Endsex` suit cette branche : (a) aucun appelant dumpé (GI 5.8 et 5.5, Person 5.8 et 5.5, PC, SexInterface, Scène 5.5, Room) ne passe `ForceEnd = true` : GI-P2:10844 (`false`) ; L6f-G:58598 (`false`) ; Person 5.5 : `false` ×3 (E`portage55\L6f\L6f_dump_Person.txt:37160, 37168, 37202`) ; PC, SexInterface, Scène, Room : 0 occurrence ; (b) la scène se termine **dans la même image** que le clic (HJ:9281-9296, image 883 : remise en place des personnes et des meubles), alors que le chemin à minuterie (`NoEnd` faux) retarde `EndSex` de 10 s (GI-P2:10829-10839) ; (c) `SexAction.uasset` contient la chaîne `Endsex` (2 lignes), `ForceEnd` (1), `NoPregCheck` (1), `PlayAppropriateSexAnimation` (1) (`grep -a -c`) : le widget compare l'animation à `Endsex`, appelle `Cum` et `PlayAppropriateSexAnimation`. **Ce qui n'est pas lu : les valeurs des broches de l'appel `GI.Cum` dans `SexAction`** (aucun dump de `SexAction` n'existe : Glob `E**\*SexAction*` → aucun fichier). D'où P0.
### 1.2 Objectifs mesurables (deux parties)
- **O4a — garde `PersonCumming`** (sans changement de comportement) : les 2 lignes `LogScript: Warning: Accessed None trying to read (real) property PersonCumming in /Game/BPs/GI.GI_C` par clic `Endsex` (HJ:9243, 9262) → **0**. `GI:EventGraph` : N0 → **N0 + 3** nœuds (N0 attendu 6 253, H3gV:29).
- **O4b — bonus à `Endsex`** (changement de comportement voulu, Q-ORG4-1) : à `Endsex`, `SexPersons[0]` reçoit `GiveOrgasmReward` **si et seulement si sa jauge `Sex_Orgasm` ≥ 90** (même éligibilité que l'animation `Finish`, PC-STE:6128-6142) ; personne d'autre, rien en dessous de 90, pas d'effet d'orgasme (pas de `Person::Cum`). `GI:EventGraph` : + **8** nœuds (+ **1** marqueur de trace si Q-ORG4-3 = oui).
- Total attendu : `GI:EventGraph` **N0 + 11** (6 264) sans marqueur, **N0 + 12** (6 265) avec ; 2 coupures ; 22 liens (23) ; 2 valeurs posées (4) ; MD5 de Person et de SexAction inchangés.
## 2. Fichiers touchés, ordre
Un seul fichier : `Content\BPs\GI.uasset`, graphe `EventGraph` (événements `AfterCumSpeed` et `Cum`). Ni Person, ni SexAction, ni PC, ni le C++, ni les scènes, ni les modules. **Ordre** : P0 à P7 → O4a → O4b → `compile_blueprint` → `save_assets` unique → fermeture de l'éditeur. O4a et O4b dans **une seule session MCP, un seul instantané, un seul `save_assets`** (comme H3g § 2). Si P0 contredit l'hypothèse `ForceEnd`, ou si Q-ORG4-1 = (a), **exécuter O4a seul** (§ 3).
### 2.0 Préalables
- **P0 (bloquant pour O4b ; lecture, UE-GO de l'orchestrateur)** — dump du gestionnaire de clic de `SexAction` (lecture L1 de AUD § 5, jamais faite), éditeur fermé (`.\Tools\UeState.ps1 -Kind Editeur` = 0) :
`.\Tools\RunCommandlet.ps1 -Lot STE-ORG4 -Commandlet LPDumpFunction -Args '-BP=SexAction -Fn=EventGraph -Pins -Exec' -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE"`
(le résolveur de `-BP` a trouvé `Action` sous `UI/` pour `Action__EventGraph_P2.txt` ; pour `SexAction` : non essayé). **Attendu** : sur le chemin de la comparaison avec `'Endsex'`, un nœud d'appel `GI_C::Cum` avec `ForceEnd = 'true'`, et aucun appel direct de `GI_C::EndSex`. **Écart** (ForceEnd faux, ou `EndSex` appelé par le widget, ou plusieurs appels) : **arrêt, rapport** ; O4b n'aurait aucun effet (V5 passerait, V6-S1 échouerait) ; ancre de repli à spécifier d'après ce dump.
- **P1 (dump à l'entrée)** : MD5 de GI à l'entrée. Si `e64b5b37…` : le dump GI-P2 + H3gV:32 suffisent pour les liens du chemin `Cum`, mais **refaire** le dump pour fixer N0 et relever les noms : `.\Tools\RunCommandlet.ps1 -Lot STE-ORG4 -Commandlet LPDumpFunction -Args '-BP=GI -Fn=EventGraph -Pins -Exec' -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE"` (éditeur fermé ; 61 s en H3g, H3gV:29) → en-tête `=== GI :: EventGraph (6253 noeuds) ===` attendu. Autre MD5 ou autre N0 : arrêt, rapport.
- **P2** : MD5 de GI relevé juste avant l'IMPL (attendu `e64b5b37…`) = valeur de repli.
- **P3** : gel annoncé à **toutes** les sessions (`lp-mcp` § 1 règle 7) ; `.\Tools\Snapshot.ps1 -Lot STE-ORG4` (`Content\BPs\GI.uasset` ; l'instantané contient donc G16 et B7-a, non commités) ; éditeur headless (`lp-mcp` § 2) ; `AssetTools.load_asset` `/Game/BPs/GI`.
- **P4** (`get_node_infos {nodes:[{refPath:"/Game/BPs/GI.GI:EventGraph.K2Node_<Nom>"}]}`, deux appels de dix nœuds au plus ; liens = GI-P2 ; **relever** les indices de broches ; tout écart : arrêt) :
1. `CustomEvent_4245`, `IfThenElse_172`, `IfThenElse_98`, `IfThenElse_875`, `CallFunction_11423`, `CustomEvent_702`, `VariableSet_9760`, `CallFunction_960`, `VariableGet_720`, `VariableGet_3498` ;
2. `GetArrayItem_1037`, `CallFunction_6681`, `VariableGet_721`, `VariableGet_722`, `IfThenElse_188`, `IfThenElse_189`, `CallFunction_38905`, `CallFunction_4939`.
Liens attendus : `IfThenElse_875.then` → `CallFunction_11423.execute` seul ; `CallFunction_11423.execute` ← `IfThenElse_875.then`, `CallFunction_32955.then`, `IfThenElse_189.then` (GI-P2:10906) ; `CustomEvent_702.then` → `VariableSet_9760.execute` (10898) ; `VariableSet_9760.then` → `CallFunction_960.execute` (10901) ; `VariableGet_720.PersonCumming` → `VariableGet_721.self`, `VariableGet_722.self` (28041) ; `VariableGet_3498.SexPersons` → `GetArrayItem_1037.Array` ; `GetArrayItem_1037.Output` → `CallFunction_6681.self`, `Dimension 1` = `0` (10879-10888).
- **P5** : avant chaque `create_node`, `find_node_types {graph, type_id_filter, context_pins:[]}` ; le `type_id` attendu doit figurer dans la réponse ; après création, `get_node_infos` du nœud (indices). **Pas de type_id improvisé.**
- **P6** : P0 lu et conforme (O4b) ; Q-ORG4-1 répondue (O4b) ; Q-ORG4-3 répondue (marqueur).
- **P7** (lecture, sans écriture) : `find_node_types` avec le filtre `GiveOrgasmReward` doit rendre `Classe|Person|GiveOrgasmReward` (Person MD5 `8668a47c…` à l'entrée ; sinon arrêt) ; relever la broche `self`. Aucun enregistrement de Person.
### 2.1 Conventions (reprises de H3gS § 2.1, H3dI § 1.2, `lp-mcp` § 3-4)
- `type_id` déjà relus en 5.8 : Branche `Utilitaires|ContrôleDeFlux|Branche` (execute 0, Condition 1 = `true` par défaut ; then 0, else 1 ; H3dI:69) ; `Utilitaires|Tableau|IsValidIndex` (`TargetArray` 0, `IndexToTest` 1 = `0` par défaut, `ReturnValue` 0 ; H3dI:68, 187-188) ; `Utilitaires|IsValid` (pur, `Object` 0, `ReturnValue` 0 ; H3gI:45) ; appel d'un événement de Person depuis GI `Classe|Person|<Nom>` (exemple créé : `Classe|Person|MovetoPersonStand`, B7aI:37-38 ; `Classe|Person|RevertStripAll`, STE-H10-GI1-IMPL.md:48) ; lecteur d'une variable de Person depuis GI `Classe|Person|Obtenir<Var>` (exemple : `Classe|Person|ObtenirValid`, `self` entrée 0, STE-H10-GI1-IMPL.md:47) — le tiret bas disparaît du nom (`Vol_BGA` → `ObtenirVolBGA`, `STF-T3-IMPL.md:59`) ; setter d'une variable de GI `Variables|<Catégorie>|Définir<Var>` (catégorie `Pardéfaut`, H3dI:70 ; catégorie `Sex` pour `SexOn`, B7aI:34) ; opérateur promotable `Utilitaires|Opérateurs|Inférieurà(<)` (créé en B7a : B7aI:32 ; typé en reliant `A` d'abord, puis `B` posé par `set_pin_value`, B7aI:44) ; `Math|Flottant|float<float` n'est **pas** créable (B7aI:31).
- **Pas de `Person::Cum` appelé depuis `GI::Cum`** (décision de conception, § 2.3 et R-ORG4-5). Cascades de Branche ; ni AND, ni OR, ni Select purs. `delete_node` interdit. Positions libres.
- Compilation et enregistrement : `compile_blueprint {blueprint:{refPath:"/Game/BPs/GI.GI"}, warnings_as_errors:true}` → `null`, journal de l'éditeur lu (aucune ligne Error / Warning liée à GI) ; **puis** `save_assets {asset_paths:["/Game/BPs/GI"]}` ; `hyper_blueprint_debug_inspect` absent du serveur (mémoire du projet ; H3dI:156) : repli = compilation stricte + journal ; fermeture de l'éditeur, `.\Tools\UeState.ps1` = 0, MD5 relevé.
### 2.2 Garde `PersonCumming` — O4a (graphe `/Game/BPs/GI.GI:EventGraph`, événement `AfterCumSpeed`)
**Avant** (GI-P2:10896-10904, 28034-28062) : `CustomEvent_702.then` → `VariableSet_9760.execute` ; `VariableSet_9760.Rate` ← `CallFunction_1002.ReturnValue` (pur) ← `CallFunction_1005` (`StartsWith`) et `VariableGet_722.IsMale`, tous deux avec `self` ← `VariableGet_720.PersonCumming`. Deux lectures de membres sur `PersonCumming` = deux avertissements par clic si `None`. « Vide » (`None`) est un **état normal** de ce chemin (aucun jouisseur) : l'`IsValid` est donc justifié (`lp-mcp` § 1 règle 4).
**Nœuds créés** (3) :
| ID | Nœud | type_id (P5) | Broches (P4 / `get_node_infos`) |
|---|---|---|---|
| O4a-V | IsValid (pur) | `Utilitaires|IsValid` | `Object` 0, `ReturnValue` 0 |
| O4a-B | Branche | `Utilitaires|ContrôleDeFlux|Branche` | execute 0, Condition 1 ; then 0, else 1 |
| O4a-S | Définir `Rate` | `Variables|<catégorie>|DéfinirRate` — catégorie inconnue : `find_node_types` filtre `Rate` ; le nœud créé doit porter `|SetRate` comme `VariableSet_9760` | execute 0, `Rate` 1 ; then 0 |
**Coupure** (1) : `break_pins` `CustomEvent_702.then` (sortie 0) / `VariableSet_9760.execute` (entrée 0).
**Liens** (6), un appel par message :
1. `VariableGet_720.PersonCumming` → `O4a-V.Object` (0) (les liens vers `VariableGet_721.self` et `VariableGet_722.self` restent) ;
2. `CustomEvent_702.then` (0) → `O4a-B.execute` (0) ;
3. `O4a-V.ReturnValue` (0) → `O4a-B.Condition` (1) ;
4. `O4a-B.then` (0) → `VariableSet_9760.execute` (0) ;
5. `O4a-B.else` (1) → `O4a-S.execute` (0) ;
6. `O4a-S.then` (0) → `CallFunction_960.execute` (0) (celui-ci garde `VariableSet_9760.then`).
**Valeur** (1) : `set_pin_value` `O4a-S.Rate` (entrée 1) = `0.25` (relire la valeur par défaut avant et après).
**Relecture** : `CustomEvent_702.then` → `O4a-B.execute` seul ; `VariableSet_9760.execute` ← `O4a-B.then` seul ; `CallFunction_960.execute` ← `VariableSet_9760.then`, `O4a-S.then` ; `VariableGet_720.PersonCumming` → 3 cibles ; `O4a-B.Condition` relié (sinon `true` par défaut = garde inopérante).
**Comptes O4a** : 3 nœuds ; 1 coupure ; 6 liens ; 1 valeur.
Pourquoi `Rate = 0.25` explicite sur `else` (et non un saut direct vers `CallFunction_960`) : avec `PersonCumming = None`, les deux lectures rendent la valeur par défaut (`Race` vide → `StartsWith('Dog')` faux ; `IsMale` faux), `bPickA` = faux et `SelectFloat` rend `B` = 0.25 (« If bPickA is true, A is returned, otherwise B is », `UE`Runtime\Engine\Classes\Kismet\KismetMathLibrary.h:3850-3852 ; sémantique d'une lecture `Accessed None` : H3fD:86-88, estimé). Le `else` pose donc exactement la valeur d'aujourd'hui, y compris pour le chemin `Exhausted` (qui laisse 10 s avant `EndSex` et pour lequel `Rate` sert encore, estimé).
### 2.3 Bonus à `Endsex` et ses gardes — O4b (même graphe, événement `Cum`, branche `ForceEnd`)
**Principe** (Q-ORG4-1 option b, recommandée) : sur la branche `ForceEnd` vrai de `GI::Cum`, **avant** l'appel de `AfterCumSpeed`, donner `GiveOrgasmReward` à `SexPersons[0]` si `Sex_Orgasm ≥ 90`. Le bonus est lu **avant** `EndSex` (qui vide la scène) : `SexPersons`, `PlayerCharacter` et `SexOn` sont encore valides. Le bonus ne comprend que ce que fait `GiveOrgasmReward` (P-STG:16059-16106, 19071-19101 d'après AUD § 1) : `OrgasmIrBoost`, puis `mood` +20 (joueur) ou `attractiontoplayer` +3 / +6 (PNJ avec le joueur dans la scène), rien sinon.
Pourquoi **bonus seul** et non `Person::Cum` : (1) `Person::Cum` rappelle `GI.Cum` (P-STG:15958-15973) ; appelé depuis `GI::Cum`, il ré-entrerait dans le même événement dont les paramètres (`ForceEnd`, `NoEnd`…) sont, en Blueprint, des variables de la trame de l'ubergraph partagée ; l'appel interne écraserait `ForceEnd` à `false` et la fin de scène (`IfThenElse_188`, `IfThenElse_876`, `CallFunction_4939`) lirait la valeur écrasée (**estimé fort**, non lu dans le moteur : raison suffisante pour s'en abstenir) ; (2) fumée, texte, secousse et son d'orgasme seraient joués à l'image même où la scène est défaite ; (3) l'événement `GiveOrgasmReward` n'appelle aucun nœud de GI (AUD § 1 : 6 nœuds) donc aucune ré-entrée.
**Nœuds créés** (8, + 1 si Q-ORG4-3 = oui) :
| ID | Nœud | type_id (P5) | Broches |
|---|---|---|---|
| O4b-I | IsValidIndex | `Utilitaires|Tableau|IsValidIndex` | `TargetArray` 0, `IndexToTest` 1 = `0` (défaut, ne pas poser), `ReturnValue` 0 |
| O4b-B1 | Branche | `Utilitaires|ContrôleDeFlux|Branche` | execute 0, Condition 1 ; then 0, else 1 |
| O4b-V | IsValid (pur) | `Utilitaires|IsValid` | `Object` 0, `ReturnValue` 0 |
| O4b-B2 | Branche | idem | idem |
| O4b-G | Lecteur de `Sex_Orgasm` (Person) | `Classe|Person|Obtenir…` : filtre `Orgasm` ; attendu `Classe|Person|ObtenirSexOrgasm` (modèle `ObtenirValid`) ; **le type de la variable est `double`** (`EqualEqual_DoubleDouble` P-STG:16030-16033 ; `GreaterEqual_DoubleDouble` PC-STE:6128-6132) | `self` 0 ; sortie `Sex_Orgasm` 0 |
| O4b-C | Comparateur `<` | `Utilitaires|Opérateurs|Inférieurà(<)` (promotable, créé en B7a) | `A` 0, `B` 1 ; `ReturnValue` 0 |
| O4b-B3 | Branche | idem | idem |
| O4b-M (option) | PrintString (marqueur de trace) | filtre `PrintString` | `InString`, `bPrintToScreen`, `bPrintToLog` (défaut vrai) ; execute 0, then 0 |
| O4b-R | Appel de `Person_C::GiveOrgasmReward` | `Classe|Person|GiveOrgasmReward` (P7) | execute 0, `self` 1 ; then 0 |
Repli de type pour O4b-C si la création échoue : `Utilitaires|Opérateurs|Supérieuràouégalà(>=)` (listé, non créé : B7aI:24) avec Branche inversée ; sinon arrêt, rapport.
**Coupure** (1) : `break_pins` `IfThenElse_875.then` (sortie 0) / `CallFunction_11423.execute` (entrée 0).
**Liens** (16 ; 17 avec le marqueur), un appel par message, dans cet ordre (relier `O4b-G` → `O4b-C.A` **avant** de poser `B`) :
1. `IfThenElse_875.then` (0) → `O4b-B1.execute` (0) ;
2. `VariableGet_3498.SexPersons` → `O4b-I.TargetArray` (0) (le lien vers `GetArrayItem_1037.Array` reste) ;
3. `O4b-I.ReturnValue` (0) → `O4b-B1.Condition` (1) ;
4. `O4b-B1.then` (0) → `O4b-B2.execute` (0) ;
5. `GetArrayItem_1037.Output` → `O4b-V.Object` (0) ;
6. `O4b-V.ReturnValue` (0) → `O4b-B2.Condition` (1) ;
7. `O4b-B2.then` (0) → `O4b-B3.execute` (0) ;
8. `GetArrayItem_1037.Output` → `O4b-G.self` (0) ;
9. `O4b-G.Sex_Orgasm` (0) → `O4b-C.A` (0) ;
10. `O4b-C.ReturnValue` (0) → `O4b-B3.Condition` (1) ;
11. `O4b-B3.else` (1) → `O4b-R.execute` (0) — avec le marqueur : `O4b-B3.else` → `O4b-M.execute`, puis `O4b-M.then` → `O4b-R.execute` (lien 11 en deux liens) ;
12. `GetArrayItem_1037.Output` → `O4b-R.self` (1) ;
13. `O4b-R.then` (0) → `CallFunction_11423.execute` (0) ;
14. `O4b-B1.else` (1) → `CallFunction_11423.execute` ;
15. `O4b-B2.else` (1) → `CallFunction_11423.execute` ;
16. `O4b-B3.then` (0) → `CallFunction_11423.execute` (jauge < 90 : pas de bonus).
**Valeurs** : `set_pin_value` `O4b-C.B` = `90.0` (après le lien 9) ; marqueur : `O4b-M.InString` = `ORG4 bonus Endsex`, `O4b-M.bPrintToScreen` = `false`.
**Relecture** : `IfThenElse_875.then` → `O4b-B1.execute` seul ; `CallFunction_11423.execute` ← **6 sources** (`CallFunction_32955.then`, `IfThenElse_189.then`, `O4b-B1.else`, `O4b-B2.else`, `O4b-B3.then`, `O4b-R.then`) ; `GetArrayItem_1037.Output` → 4 cibles (`CallFunction_6681.self`, `O4b-V.Object`, `O4b-G.self`, `O4b-R.self`) ; `VariableGet_3498.SexPersons` → 2 cibles ; les 3 `Condition` des Branches reliés ; `IfThenElse_875.else` → `MacroInstance_18.exec` inchangé.
**Comptes O4b** : 8 nœuds (9) ; 1 coupure ; 16 liens (17) ; 1 valeur (3).
**Gardes** : `IsValidIndex(SexPersons, 0)` puis `IsValid(SexPersons[0])` avant la seule lecture de membre (`Sex_Orgasm`) et avant l'appel de l'événement (format G13 / G16, H3gS § 2.1) ; chaque `else` rejoint `AfterCumSpeed`, c'est-à-dire **exactement le chemin d'aujourd'hui**. Le test de valeur (`Sex_Orgasm < 90`) n'est pas une garde `Accessed None` mais la condition d'éligibilité.
### 2.4 Comptes et résultat attendu de l'IMPL
| | sans marqueur | avec marqueur |
|---|---|---|
| Nœuds créés (O4a 3 + O4b 8 (+1)) | 11 | 12 |
| `GI:EventGraph` (N0 = 6 253) | **6 264** | **6 265** |
| Coupures | 2 | 2 |
| Liens créés | 22 | 23 |
| `set_pin_value` | 2 (`Rate`, `90.0`) | 4 (+ `InString`, `bPrintToScreen`) |
Inchangés (VAL) : toutes les autres fonctions de GI ; `isSameAs` 11, `GetVariable` 53, `CallFunctionReturnBool` 594, `Action:EventGraph` 363 (H3gV:34).
### 2.5 Comportement avant / après (cas changés nommés)
| Cas | Avant | Après |
|---|---|---|
| `Endsex`, `SexPersons[0].Sex_Orgasm ≥ 90` | scène terminée, **aucun bonus** ; 2 avertissements `PersonCumming` si `None` | **bonus** (O4b) : `OrgasmIrBoost` + `mood` +20 si `SexPersons[0]` est le joueur, sinon `attractiontoplayer` +3 (+6 si « Vaginal » et `WantsBabies`) si le joueur est dans la scène, sinon rien ; puis fin identique ; 0 avertissement → **changement voulu** |
| `Endsex`, jauge < 90 | fin ; 2 avertissements si `None` | fin identique ; 0 avertissement ; pas de bonus |
| `Endsex`, `SexPersons` vide ou `[0]` invalide | (cas non rencontré) | `else` → chemin d'aujourd'hui |
| `Finish` (« Cum on … ») puis `Endsex` | 1 bonus (Finish) ; `Endsex` sans bonus | idem : `Person::Cum` remet `Sex_Orgasm` à 0 (P-STG:17282-17287) → jauge < 90 → **pas de double bonus** |
| Orgasme automatique à 100, puis `Endsex` | idem | idem (même remise à 0) |
| Scène suivante, `Endsex` aussitôt | — | jauge remise à 0 au démarrage de la scène par `UpdateSexLikes` (P-STG:15804-15818 ; appelé en boucle sur `SexPersons`, GI-P2:27418-27421, 12385-12391, avec `SexOn := true` voisin, 6896-6899 ; **estimé** : événement propriétaire de la boucle non nommé) → **pas de bonus gratuit en boucle** |
| `Endsex` après un orgasme antérieur (`PersonCumming` périmé mais valide) | `Rate` selon le dernier jouisseur | inchangé (garde vraie) |
| `PersonCumming` détruit, en attente de ramasse-miettes (`IsValid` faux, pointeur non nul) | lit les champs périmés | `Rate = 0.25` (écart théorique, très rare ; seul cas de O4a qui change une valeur) |
| `Exhausted` → `GI.Cum` (minuterie 10 s) avec `PersonCumming` `None` | 2 avertissements, `Rate = 0.25` | 0 avertissement, `Rate = 0.25` |
| Bonus **sans orgasme** | non | **oui, par conception** : terminer à ≥ 90 compte comme une jouissance ; sans effet d'orgasme (pas de remise à 0, pas de particule, pas de texte) → Q-ORG4-1 |
| Persons autres que `[0]` (partenaire, scènes à plusieurs) | pas de bonus | pas de bonus (comme `Finish`, qui ne vise que `Persons[0]`) → option (c) de Q-ORG4-1 |
Le cas « tous ceux à 100 » est vide : l'orgasme à 100 se déclenche dans le même appel que la mise à jour de la jauge (`FClamp` puis `== 100.0` → `Cum()`, P-STG:15896-15901, 16030-16043), donc aucune jauge ne vaut 100 au moment d'un clic (établi par la lecture ; AUD § 2).
## 3. Repli
- Avant écriture : instantané `.\Tools\Snapshot.ps1 -Lot STE-ORG4` (P3), MD5 de départ (P2).
- **Aucun `save_assets` tant que `compile_blueprint` n'est pas à 0 diagnostic.** Échec avant enregistrement : fermer l'éditeur sans enregistrer (le `.uasset` reste l'instantané), contrôler le MD5, rapporter. Aucun correctif improvisé.
- Après enregistrement, lien relu différent, `type_id` introuvable (P5), nœud inattendu, P0 contraire : arrêt, rapport ; puis, sur décision de l'orchestrateur, éditeur fermé : `.\Tools\Restore.ps1 -Lot STE-ORG4 -Only Content\BPs\GI.uasset -WhatIf`, puis sans `-WhatIf` ; MD5 = valeur de P2. Un `Restore` de GI rend l'état de l'instantané : G16 et B7-a (déjà dans le fichier à l'entrée) sont conservés, aucune autre écriture de GI ne doit être intercalée.
- **Repli de conception** : (i) P0 contraire ou Q-ORG4-1 = (a) → **O4a seul** (3 nœuds, EventGraph N0 + 3) ; (ii) O4b-C non créable → inverser avec `>=` (§ 2.3) ou arrêt ; (iii) le marqueur gêne → l'omettre (il est retirable et ignoré en Shipping : `DevelopmentOnly`, § 6).
- Après commit (gardien) : `git revert` du commit du ticket STE-ORG4. **Couplage de commit** : `GI.uasset` est un binaire unique qui porte déjà G16 (commit différé jusqu'au verdict COMMUN-2, STE-TACHES.md:31) et B7-a (STG) ; ces trois changements partent dans le même fichier, donc dans un même commit ou dans l'ordre imposé par le gardien (R-ORG4-2).
## 4. Risques R#, questions Q#
- **R-ORG4-1** : la valeur `ForceEnd = true` de l'appel `GI.Cum` de `SexAction` est **déduite** (§ 1.1 a-c), pas lue. Si elle est fausse, O4b ne s'exécute jamais : la VAL statique (V5) passe quand même, seul l'essai en jeu (V6-S1) le révèle. → P0 bloquant avant O4b.
- **R-ORG4-2** : `GI.uasset` partagé : G16 (STE-H3g) et B7-a (STG) non commités dans le même binaire ; instantané à l'entrée ; commit conjoint ou ordonné ; ne jamais restaurer GI depuis un instantané antérieur à l'un d'eux.
- **R-ORG4-3** : noms et indices de broches des nœuds à créer inconnus (`Classe|Person|ObtenirSexOrgasm`, catégorie de `DéfinirRate`, `GiveOrgasmReward`) : P5 / P7 ; type_id non relu = arrêt.
- **R-ORG4-4** : `GiveOrgasmReward` affiche ses `ModifyStat` (`NoDisplay` faux, P-STG:16070-16087) à l'image même où `EndSex` défait la scène (bascules d'interface, STE-H8-DIAG) : le texte peut s'afficher très brièvement ou être masqué ; l'effet sur la stat existe quand même. V6 le fait constater (observation) ; aucune trace de journal n'existe (§ 7, V6).
- **R-ORG4-5** : ré-entrée de l'événement `Cum` si l'on appelait `Person::Cum` depuis `GI::Cum` (§ 2.3) ; écartée par conception.
- **R-ORG4-6** : articulation avec **STE-ORG5** (reconstruction de la grille v5.26) : O4b appelle le **même** `GiveOrgasmReward` ; si ORG5 y ajoute `PopulateBonusRewards` (AUD § 9.3), une grille s'ouvrirait à l'image de la fin de scène. À traiter dans la SPEC ORG5 (sauter la grille depuis `Endsex`, ou accepter) ; pas de double bonus tant que O4b ne s'ajoute qu'à `Endsex` (jauge ≥ 90) et non à `Finish`.
- **R-ORG4-7** : `PersonCumming` lu ailleurs que dans l'EventGraph de GI (autres fonctions de GI, UI) : non lu (§ 2.0 : P4 peut ajouter `find_nodes {graph, title:"PersonCumming"}` si l'outil le permet) ; O4a ne garde que les deux lectures connues.
- **R-ORG4-8** : le dump de Person (14:08) précède la dernière sauvegarde de Person (17:42) : la chaîne `GiveOrgasmReward` a pu changer ; P7 et la VAL (V6) le couvrent.
- **R-ORG4-9** : autre appelant inconnu de `GI.Cum` avec `ForceEnd = true` (widget non dumpé) : il recevrait aussi le bonus à jauge ≥ 90 (souhaitable : fin de scène).
- **Q-ORG4-1 (utilisateur, avant O4b)** : qui reçoit le bonus quand on termine par `Endsex` ?
| Option | Description | Pour | Contre |
|---|---|---|---|
| (a) personne | O4a seul | conception d'origine (`Endsex` ≠ orgasme) ; aucun bonus gratuit | l'utilisateur reste sans bonus à `Endsex` |
| **(b) `SexPersons[0]` si jauge ≥ 90** (recommandée) | O4a + O4b | même éligibilité que `Finish` (PC-STE:6128-6142) ; bonus seul ; pas de double bonus ni de farm (jauge remise à 0 par `Cum` et par le démarrage de scène) | « bonus sans orgasme » ; la partenaire (index 1) n'est pas servie |
| (c) chaque participant à ≥ 90 | boucle `ForEachLoop` sur `SexPersons` (macro vue en `MacroInstance_806`, GI-P2:7094-7100 ; `type_id` à relever) | plus juste pour deux jouisseurs | +5 nœuds environ, non spécifié ici ; autre SPEC |
| (d) `Person::Cum` complet avant `GI.Cum` | côté `SexAction` (illisible) | effets complets | ré-entrée si fait dans GI ; SexAction non dumpé |
Recommandation : **(b)**. Question jumelle, **Q-ORG4-2** : seuil 90 (celui du message rouge, P-STG:17363-17367, et de `Finish`) ou autre ?
- **Q-ORG4-3 (orchestrateur)** : marqueur de trace `ORG4 bonus Endsex` (1 nœud, ignoré en Shipping) : oui (recommandé : V6 n'a aucune autre preuve de journal, § 7) ou non.
- **Q-ORG4-4 (orchestrateur)** : UE-GO pour P0 (dump de `SexAction`) puis pour l'IMPL, gel annoncé, ticket du gardien (R-ORG4-2).
- **Q-ORG4-5 (orchestrateur)** : O4a seul pour l'alpha si Q-ORG4-1 tarde (3 nœuds, sans changement de comportement, supprime F2).
## 5. Interdits
- Aucune modification de Person (aucun `save_assets` de Person), de SexAction, de PC, de `GetVariable`, `CallFunctionReturnBool`, `isSameAs`, `Action`, du C++, des scènes et modules ; aucune autre branche de `GI::Cum` que `IfThenElse_875.then` ; `AfterCumSpeed` modifié par O4a seulement (3 nœuds).
- **Pas d'appel de `Person::Cum` depuis GI** ; aucun `FunctionResult` créé ; pas de `delete_node` ; pas de AND / OR / Select purs ; pas de `type_id` non relu (P5) ; pas d'écriture HyperAIStudio (`lp-mcp` § 1 règle 1) ; outils Epic seulement.
- Pas d'IMPL de O4b sans P0 conforme et Q-ORG4-1 répondue ; pas d'IMPL sans P1 à P7 ; pas d'IMPL entrelacée avec une autre écriture sur GI.
- 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\`)
Relu aujourd'hui :
- `UKismetSystemLibrary::PrintString` est `DevelopmentOnly` : `UE`Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h:565-566 (le marqueur est ignoré en Shipping, joué en Development).
- `UKismetMathLibrary::SelectFloat(double A, double B, bool bPickA)` : « If bPickA is true, A is returned, otherwise B is » : `UE`Runtime\Engine\Classes\Kismet\KismetMathLibrary.h:3850-3852.
Repris de H3gS § 6 (relus le 2026-10-13, **non relus aujourd'hui**) : `IsValid(const UObject*)` = non nul et non en attente de destruction (`KismetSystemLibrary.h:126-128`) ; `GenericArray_IsValidIndex` sans message (`KismetArrayLibrary.cpp:583-595`) ; `GenericArray_Get` hors plage → message `Attempted to access index …` et valeur par défaut (`KismetArrayLibrary.cpp:459-482`) ; `Accessed None trying to read …` (`ScriptCore.cpp:3213-3216`, valeur effacée `3234-3245`).
**Non lu** (estimé) : le stockage des paramètres d'un événement personnalisé dans la trame de l'ubergraph (raison de ne pas ré-entrer dans `Cum`, § 2.3) — à lire avant de s'en servir comme preuve, hors besoin de cette SPEC.
## 7. Critères de VAL (commande → attendu)
### V5 — VAL statique (éditeur fermé, `.\Tools\UeState.ps1 -Kind Editeur` = 0)
1. **Dump** : `.\Tools\RunCommandlet.ps1 -Lot STE-ORG4V -Commandlet LPDumpFunction -Args '-BP=GI -Fn=EventGraph -Pins -Exec' -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE"` → en-tête `=== GI :: EventGraph (6264 noeuds) ===` (6265 avec marqueur) ; et :
- `CustomEvent_702.then` → `O4a-B.execute` seul ; `O4a-B.then` → `VariableSet_9760.execute` seul ; `O4a-B.else` → `O4a-S.execute` ; `O4a-S.Rate` = 0.25 ; `O4a-S.then` → `CallFunction_960.execute` ; `CallFunction_960.execute` ← `VariableSet_9760.then`, `O4a-S.then` ; `VariableGet_720.PersonCumming` → `VariableGet_721.self`, `VariableGet_722.self`, `O4a-V.Object` ; `O4a-B.Condition` ← `O4a-V.ReturnValue` ;
- `IfThenElse_875.then` → `O4b-B1.execute` seul ; `IfThenElse_875.else` → `MacroInstance_18.exec` inchangé ; `CallFunction_11423.execute` ← 6 sources (§ 2.3) ; `O4b-I.TargetArray` ← `VariableGet_3498.SexPersons` ; `IndexToTest` = 0 ; `O4b-V.Object`, `O4b-G.self`, `O4b-R.self` ← `GetArrayItem_1037.Output` ; `O4b-G.Sex_Orgasm` → `O4b-C.A` ; `O4b-C.B` = 90 ; `O4b-B3.else` → `O4b-R.execute` (ou `O4b-M`) ; `O4b-B3.then`, `O4b-B2.else`, `O4b-B1.else`, `O4b-R.then` → `CallFunction_11423.execute` ; les 4 Branches de O4a / O4b ont leur `Condition` reliée.
Non-régression (un par un) : `-Fn=isSameAs` → 11 ; `-Fn=GetVariable` → 53 ; `-Fn=CallFunctionReturnBool` → 594 ; `-BP=Action -Fn=EventGraph` → 363 (H3gV:34).
2. **Diff indépendant** : `python -I Tools\DiffRemplacementNoeuds.py <dump P1> <dump après> <profil>` → seuls les nœuds et liens du § 2.2-2.4 ; « pins modifiés sur nœuds communs » = `CustomEvent_702`, `VariableSet_9760`, `CallFunction_960`, `VariableGet_720`, `IfThenElse_875`, `CallFunction_11423`, `VariableGet_3498`, `GetArrayItem_1037` (8) et aucun autre.
3. **Compilation** : `compile_blueprint … warnings_as_errors:true` → `null`, journal de l'éditeur sans Error / Warning pour GI ; `CompileAllBlueprints` éditeur fermé : BP `/Game/` du projet **0 erreur** (critère requalifié le 2026-10-13, `CC_Shaders` à part ; ligne de référence de H3gV, citée par STE-TACHES.md:31 : « 25/3/55/420 ») ; `LPTestStringCompat` **3120 / 0 / 60** (`lp-validate`).
4. **Empreintes** : `md5sum Content/BPs/GI.uasset` noté ; `md5sum Content/BPs/Person.uasset` = `8668a47c627f7a5254910cf0ac33b12a` et `Content/UI/SexAction.uasset` = `a8705f7fbe1fde1daba0464bfc9ff9b5` (inchangés) ; Room : valeur relevée à l'entrée = à la sortie ; `git status --short -- Content` : aucun fichier de plus que ` M` GI, Person, Room.
### V6 — essai en jeu (build Development empaqueté par BUILD, **même ligne de commande que HJ** : `-ScriptStackOnWarnings -LPHitchLog`, HJ:291 ; UE-GO / BUILD-GO de l'orchestrateur)
**Ce que le journal ne montre pas** : `ModifyStat` ne laisse aucune ligne. Dans HJ, un `Endsex` a eu lieu (HJ:9242) et `GI::EndSex` applique `arousal -50` et `mood +50` à chaque participant (GI-P2:7803-7811) ; Grep `attractiontoplayer|\bmood\b` sur HJ → aucune ligne ; Grep `\barousal\b` sur HJ → aucune ligne (contrôle positif : `Endsex|ORG4` sur HJ → 1 ligne). Le critère « trace `mood` / `attractiontoplayer` » de la fiche n'est donc **pas tenable par le journal** : preuve = marqueur O4b-M (Q-ORG4-3) **et** observation à l'écran (texte de stat de `ModifyStat`, `NoDisplay` faux) rapportée par l'utilisateur.
**Parcours à jouer** (une scène MF avec le joueur ; noter qui est `SexPersons[0]` : l'homme / la personne au pénis, joueur ou PNJ) :
- **S1** (bonus) : laisser monter la jauge de `SexPersons[0]` entre 90 et 100 (texte rouge « Close to cumming… » visible, seuil P-STG:17363-17367), puis cliquer « Finish having sex » (pas une animation « Cum on … »). Attendu : la scène se termine dans la même image ; texte de stat du bonus à l'écran (`mood` +20 si c'est le joueur ; `attractiontoplayer` +3 / +6 sur le PNJ si le joueur est dans la scène), en plus de ceux de `EndSex` ; marqueur dans le journal.
- **S2** (témoin sous le seuil) : démarrer une scène, cliquer « Finish having sex » aussitôt (jauge < 90). Attendu : fin de scène, aucun texte `mood +20` / `attractiontoplayer`, aucun marqueur, 0 avertissement.
- **S3** (pas de double bonus) : jauge ≥ 90, choisir une animation « Cum on … » (bonus 1 par `Person::Cum`), puis cliquer « Finish having sex » aussitôt. Attendu : pas de second bonus, aucun marqueur pour ce clic, 0 avertissement.
- **S4** (orgasme à 100) : laisser la jauge atteindre 100 (orgasme automatique), puis « Finish having sex ». Attendu : comme S3.
**Greps sur le journal du jeu** (mode `count`, fichier nommé ; base = HJ) :
| Motif | HJ (1 clic `Endsex`) | Attendu |
|---|---|---|
| `Endsex \(Animation String\)` | 1 | = nombre de clics (S1 à S4 : 4 si une scène par cas) — **preuve que le parcours est rejoué** (sans elle, un 0 ne prouve rien) |
| `Accessed None trying to read \(real\) property PersonCumming` | 2 | **0** |
| `ORG4 bonus Endsex` (si marqueur) | 0 | **1** (S1 seul) |
| `GiveOrgasmReward\|OrgasmIrBoost` (piles d'avertissement) | 0 | **0** |
| `fatal\|Unhandled\|Critical\|Assertion` | 0 | **0** |
| `LogScript: Warning: Accessed None` | 13 | ≤ 11 (indicatif : −2 pour F2 ; le reste dépend du parcours) |
Non-régression : `.\Tools\LogStats.ps1 -Compare <journal>,<HJ>` : aucune famille nouvelle ; G13 et G16 intacts (`isCreatureType` → 0).
**Limites** : l'effet réel des stats n'est vu qu'à l'écran ; le cas `PersonCumming` périmé mais valide (un `Endsex` après un orgasme) est couvert par S3 et S4 (0 avertissement avant comme après, `Rate` inchangé) ; le cas « détruit en attente de ramasse-miettes » n'est pas rejoué.
Fichiers à commiter (dépôt jeu, branche `ue5.8`, ticket au gardien) :
- A `Docs/Migration/etat/ST/STE/STE-ORG4-SPEC.md`
Tours : 29 / 45 (compte de l'agent, écriture comprise ; `python -I Tools/FicheStats.py --lot STE` fait foi)
## Constats
- **Lectures qui manquent pour O4b** (commandes données, non lancées) : P0 (`SexAction` EventGraph) ; facultatives : refresh du dump de PC (`-BP=PC -Fn=EventGraph`, celui de 10-10 précède CAM-B1) ; autres fonctions de GI lisant `PersonCumming` (R-ORG4-7).
- Commandes de lecture (Grep / Read sauf mention) et comptes bruts :
- Grep `ForceEnd` sur le dossier `E\ST\STE\dumps` → `No matches found` alors que GI-P2 contient la chaîne (GI-P2:10806) : **les `.txt` de `etat\` sont ignorés par ripgrep même avec le dossier en `path`** ; il faut nommer le fichier (piège déjà noté, H3fD « Constats »). Les Grep ci-dessus nomment tous le fichier.
- Grep `ForceEnd` (fichier nommé) : PC-STE → 0 ; `E`ST\STA\dumps\SexInterface__EventGraph.txt` → 0 ; `L6f_dump_Scene.txt` → 0 ; `E`ST\STE\dumps\Room__EventGraph_V5.txt` → 0 ; L6f-G → 58572, 58574, 58598, 61588, 61590, 72019, 72237 ; `L6f_dump_Person.txt` → 37160, 37168, 37202 (`false`).
- Grep `appel [A-Za-z_?]+::(Cum|EndSex|AfterCumSpeed|GiveOrgasmReward)\(\)` sur GI-P2 → 10840, 10905, 14564 ; sur P-STG (`Cum|EndSex|GiveOrgasmReward`) → 15958, 15966, 16000, 16040, 16107 ; sur PC-STE → 0.
- Grep `variable 'Sex_Orgasm'` sur GI-P2 → 0 ; sur P-STG (`VariableSet_N`) → 15807, 15873, 17282 ; `UpdateSexLikes` : P-STG:15804 (définition) ; GI-P2:27418 (seul appelant) ; `PersonCummedOn` : 4 lectures, 0 écriture dans GI-P2 (18952-18975).
- Grep `Endsex|ORG4` sur HJ → 1 ; `attractiontoplayer|\bmood\b` → 0 ; `\barousal\b` → 0 ; J6 `Endsex|PersonCumming|Accessed None` → 1 ligne `Endsex` (J6:2849), 0 ligne `PersonCumming`.
- Bash `grep -a -c` sur `Content/UI/SexAction.uasset` : `Endsex` 2 ; `ForceEnd` 1 ; `NoPregCheck` 1 ; `PlayAppropriateSexAnimation` 1 (comptes de **lignes** contenant la chaîne ; présence d'un nom, pas valeur d'une broche).
- Le résumé `.txt` d'un `RunCommandlet` rangé sous `etat\` (ex. `STE-H3gV_LPDumpFunction_*.txt`) n'est pas le dump complet (compteurs de lignes d'avertissement dédoublonnées) ; le dump complet est dans `Saved\Logs\portage\` ou dans un fichier `__EventGraph_*.txt` produit à part.
- Hors périmètre, noté : F-ORG2 (AUD) `GI::EndSex` donne `mood +50` / `arousal -50` à chaque participant indépendamment de l'orgasme (GI-P2:7803-7811) ; l'« ancien modèle `ToCum` » (AUD § 6 P5) n'est pas concerné.
- Lecture seule respectée ; un seul fichier écrit (ce livrable, non suivi). Aucun nom d'utilisateur ni chemin de profil cité.
## Décisions de l'utilisateur (2026-10-14, session STE)
- Q-ORG4-1 : **chaque participant**, l'un après l'autre, reçoit un bonus à `Endsex` quand sa jauge dépasse **90 pour un homme** et **100 pour une femme** (réponse libre de l'utilisateur ; la réponse « 90 » à Q-ORG4-2 vaut pour les hommes). Option (c) avec seuil selon le sexe : **SPEC complémentaire O4c à écrire** (boucle sur `SexPersons`, test du sexe, seuils 90 / 100), en remplacement de O4b.
- Q-ORG4-3 (orchestrateur) : marqueur de trace **oui**. Q-ORG4-5 : O4a peut partir seul si O4c tarde.
## Règle commune ORG4 / ORG5 (Big Boss, 2026-10-14)
- Pour **un même orgasme**, une personne reçoit au plus **une fois** le bonus de base (`GiveOrgasmReward`) et au plus **une fois** les récompenses ORG5 (3 au plus) ; le cumul base + ORG5 est voulu, le même bonus deux fois non.
- Orgasme réel (`Person::Cum`, jauge à 100 ou choix « Cum on … ») : `GiveOrgasmReward` une fois (chemin existant) ; ORG5 une fois, déclenché par l'incrément de `Sex_OrgasmCounts` (accroche H1).
- `Endsex` (O4c) : `GiveOrgasmReward` une fois par participant éligible (homme > 90, femme = 100), **sans** `Person::Cum` : `Sex_OrgasmCounts` inchangé, donc **pas de récompense ORG5** à `Endsex` ; un participant qui vient de jouir a sa jauge remise à 0 par `Cum` et n'est pas éligible : pas de second bonus de base.
- Toute IMPL qui change l'un de ces déclencheurs relit cette règle et la VAL la contrôle (V6 : un orgasme = 1 marqueur de base + au plus 1 ligne ORG5 par personne).