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

20 KiB
Raw Blame History

Fiche STE-ORG5-I1 — Niveau N3 — Modèle prévu (modèle de l'agent lp-impl) | utilisé claude-opus-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat (préchargés), sk-change-safety (invoqué)

STE-ORG5-I1-IMPL : bonus d'orgasme ORG5, version minimale (V-min) en C++ dans deux fichiers neufs (2026-10-14)

Périmètre tenu : deux fichiers neufs et ce livrable ; aucun fichier existant modifié, aucun asset, ni éditeur, ni MCP, ni commandlet, ni build. Aucun refus du crochet (commandes Bash sans >, sans python -c, sans heredoc). Le code n'est pas compilé (build commun, fiche BUILD à venir) : tout ce qui suit sur la compilation est « proposé, non compilé ».

1. Fichiers modifiés (chemins, lignes)

Fichier Lignes (LF, sans BOM) Contenu
D:\Game\LifePlay-master 5.8\Source\LifePlay\LPBonusRewardSubsystem.h 119 en-tête d'origine et de licence (l. 1-10), description V-min, accroche H1, réglage (l. 12-33) ; ULPBonusRewardSettings UCLASS(config = Game), bDisabled = false (l. 45-55) ; FLPBonusStat (l. 57) ; FLPBonusPending (l. 64) ; ULPBonusRewardSubsystem : UGameInstanceSubsystem (l. 72-)
D:\Game\LifePlay-master 5.8\Source\LifePlay\LPBonusRewardSubsystem.cpp 667 outils de réflexion dans l'espace de noms nommé LPBonusReward (l. 14-173) ; Initialize (l. 175 : contrôle de formule, ligne de réglage, minuterie FTSTicker 0,1 s) ; Table 20 clés (l. 207) ; RewardChange (l. 232) ; CheckFormula (l. 243) ; Overlaps (l. 269) ; Pick (l. 287) ; Tick / Poll (l. 334, 345) ; FirePending (l. 434) ; OnOrgasm (l. 451) ; AttractionAllowed (l. 488) ; IsInterestedIn (l. 527) ; QueryStat (l. 567) ; ModifyStat (l. 603) ; PersonName (l. 638) ; WarnOnce (l. 659)

Contrôles (sortie brute) :

  • od -An -v -tx1 <f> | tr ' ' '\n' | grep -c '^0d$' → .h CR=0 LF=119, .cpp CR=0 LF=667 ; 3 premiers octets 2f 2f 20 (pas de BOM), comme LPUserSettings.h (CR=0 LF=59, même début).
  • Grep \{ TEXT\("[a-z_]+-?"\), \{ dans le .cpp → 20 (les 20 lignes de la table) ; Grep Users|<nom> dans LPBonusReward* → 0.
  • 5 lignes non ASCII dans le .cpp (accents des formats de journal imposés par la SPEC § 2.4 : contrôle, réglage, désactivé, récompense éligible) ; même pratique que LPDirectControlSubsystem.cpp:449 (« » dans un TEXT, compilé en 5.8).

1.1 Choix du sous-système

UGameInstanceSubsystem + FTSTicker à 0,1 s (précédent LPColorGradeSubsystem.h:51, .cpp:76-82). Raisons : SexOn, SexPersons, CurrentTags, PlayerCharacter sont sur GI ; le sous-système vit toute la session, sans dépendre d'un monde ; aucun tick d'acteur à créer. Coût (estimé) : hors scène, une recherche de propriété (SexOn) par tick ; en scène, une par personne (Sex_OrgasmCounts) plus SexPersons : quelques recherches de FName dans les listes de propriétés de GI et de Person, 10 fois par seconde, de l'ordre de la microseconde ; aucun appel Blueprint hors orgasme.

1.2 Accroche (H1, documentée, sans nœud Blueprint)

  • Aucun nœud Blueprint n'est nécessaire pour V-min : le C++ lit l'état par réflexion. Aucune lecture du journal, aucun crochet de texte.
  • Sondage (Poll, l. 345) : GI.SexOn faux → mémoire des compteurs vidée ; vrai → pour chaque personne valide de GI.SexPersons : premier passage = mémorisation sans récompense ; hausse de Sex_OrgasmCounts → copie de GI.CurrentTags, armement d'un orgasme en attente à t + 0,2 s (remplace l'attente en cours, comme RetriggerableDelay) ; baisse → resynchronisation sans récompense ; personne sortie de la scène → oubliée.
  • FirePending (l. 434) : à échéance, personne valide → OnOrgasm : ligne orgasme de, puis désactivé (réglage) ou tirage Pick → aucune récompense éligible ou QueryStat + RewardChange + ModifyStat(Stat, Change, false, false) sur la première récompense retenue, puis la ligne <nom> <stat> <±valeur> (x=…, tags=…) (marqueur V6).
  • Règle commune ORG4 / ORG5 (SPEC fin de fichier, STE-ORG4-SPEC.md:265-269) : un incrément de Sex_OrgasmCounts arme une seule attente, qui donne au plus une récompense appliquée ; Endsex sans Cum n'incrémente pas le compteur et ne déclenche rien ici.

1.3 Appels et lectures par réflexion, preuves

Élément Nom Type exigé par le code Preuve
GI SexOn FBoolProperty précédent LPDirectControlSubsystem.cpp:460-464
GI SexPersons FArrayProperty d'objets précédent LPShowCoupleSubsystem.cpp:80-96, :303
GI PlayerCharacter FObjectPropertyBase précédents LPDebugSubsystem.cpp:481, LPOutdoorRoomSubsystem.cpp:334
GI CurrentTags FArrayProperty de FStrProperty SPEC E1 (MakeStruct StringArray(Strings = GI.CurrentTags), Person.uexp:21184 de l'auteur) ; nom présent dans O\BPs\GI.uasset (SPEC annexe A point 10) ; type chez nous estimé
Person Sex_OrgasmCounts FIntProperty Person__EventGraph.txt:16118-16125 (macro IncrementInt), :15908-15911 (remise à 0)
Person isPlayer FBoolProperty L6f_dump_Person.txt:558-559 (variable reliée à IfThenElse.Condition)
Person Name_First FTextProperty ou FStrProperty SPEC § 2.4 (texte chez l'auteur, Cursor.uexp:22666) ; type chez nous non lu, les deux acceptés
Person (fonction) QueryStat(Stat, WithoutBuff, RNG) → Value, Bonus Stat chaîne, WithoutBuff et RNG booléens, Value réel en sortie L6f_dump_Person.txt:20-29 ; appel de l'auteur QueryStat(Stat, false, false) (SPEC E6d)
Person (fonction) ModifyStat(Stat, Change, DifficultyAdjusted, NoDisplay) → Value Stat chaîne, Change réel d'entrée, deux booléens L6f_dump_Person.txt:561-570 ; appel de l'auteur ModifyStat(Stat, Change, false, false) (SPEC E7)
GI (fonction) IsInterestedIn(WhoIsInterested, WhoInterests) → Interested deux objets de la classe du paramètre, booléen en sortie L6f_dump_GI.txt:23421-23424, :23442-23443 ; ordre retenu WhoIsInterested = Person, WhoInterests = PlayerCharacter (estimé, SPEC § 2.4)
GI (fonction) Overlaps(Array1, Array2, IfEmptyThenTrue) non appelée : réécrite en C++ (Overlaps, l. 269) L6f_dump_GI.txt:19622-19673 : IfEmptyThenTrue ET (Len(A1)=0 OU Len(A2)=0) → vrai ; sinon un élément de Array1 dans Array2 (Array_Contains) → vrai ; sinon faux

Appels par FindFunction + ProcessEvent, paramètres trouvés par nom (GetAuthoredName ou GetName), mémoire FMemory_Alloca_Aligned(ParmsSize, GetMinAlignment()) dans le cadre de l'appelant, mise à zéro, InitializeValue_InContainer puis DestroyValue_InContainer en sortie de portée (structure FScopedParms, même séquence que LPDirectControlSubsystem.cpp:426-455). Signature ou type inattendu : un avertissement Bonus : … par cause (liste des paramètres et de leurs types dans le message), abandon sans effet.

1.4 API moteur relues dans UE 5.8 (lecture, D:\Game\Launcher\Engine\UE_5.8\Engine\Source\)

  • FTSTicker : Runtime\Core\Public\Containers\Ticker.h:21 (FTickerDelegate bool(float)), :32 (FDelegateHandle), :45 (AddTicker(delegate, InDelay)), :66 (RemoveTicker).
  • UGameInstanceSubsystem / GetGameInstance : Runtime\Engine\Public\Subsystems\GameInstanceSubsystem.h:16, :25.
  • UKismetMathLibrary::FCeil(double) → int32 : Runtime\Engine\Classes\Kismet\KismetMathLibrary.h:796 (UE_INL_API, :180-183), corps KismetMathLibrary.inl:743-746 (FMath::CeilToInt) ; MapRangeClamped : .h:836 (ENGINE_API), corps Private\KismetMathLibrary.cpp:328-331 (FMath::GetMappedRangeValueClamped). Le code appelle ces deux fonctions elles-mêmes : arithmétique identique à celle des nœuds Blueprint.
  • Array_Shuffle : Runtime\Engine\Private\KismetArrayLibrary.cpp:338-352 (Fisher-Yates, RandRange(i, LastIndex)), variante par flux :363-366 ; reproduit avec FRandomStream::RandRange (Runtime\Core\Public\Math\RandomStream.h:209-214, bornes incluses) ; graine GenerateNewSeed (:105-108).
  • Array_Contains → GenericArray_Find (KismetArrayLibrary.cpp:547-559) → Inner->Identical ; TProperty_WithEqualityAndSerializer::Identical compare par operator== (Runtime\CoreUObject\Public\UObject\UnrealType.h:1769) : le C++ (TArray<FString>::Contains) utilise le même opérateur (estimé : l'héritage de FStrProperty depuis ce gabarit n'est pas relu).
  • UObject::FindFunction Runtime\CoreUObject\Public\UObject\Object.h:1258 ; ProcessEvent :1490 ; UStruct::GetMinAlignment Class.h:790 ; FField::GetAuthoredName Field.h:903 ; InitializeValue_InContainer / DestroyValue_InContainer UnrealType.h:1128, :1040 ; FObjectPropertyBase::GetObjectPropertyValue_InContainer / SetObjectPropertyValue_InContainer UnrealType.h:2896, :2901 ; StrProperty.h inclus par UnrealType.h:7815.
  • FMemory_Alloca_Aligned : Runtime\Core\Public\GenericPlatform\GenericPlatformMemory.h:221.
  • TArray::Swap(i, j) Runtime\Core\Public\Containers\Array.h:3536 ; SetNum :2531 ; TOptional::operator-> Misc\Optional.h:384-390, Reset :235.
  • Formats de journal : indicateur + accepté (Runtime\Core\Public\String\FormatStringSan.h:164), conversions f et g (:424, :426).

1.5 Réglage

ULPBonusRewardSettings UCLASS(config = Game), UPROPERTY(config) bool bDisabled = false : section [/Script/LifePlay.LPBonusRewardSettings], clé bDisabled, de la config Game du projet (Config\DefaultGame.ini) ; section absente → valeur du code (faux, fonction active). Lu par GetDefault<ULPBonusRewardSettings>() ; aucun SaveConfig : rien n'est écrit, ni dans Config\ ni dans Saved\. DefaultGame.ini n'est pas modifié (fiche § 3, consigne 2). Une ligne au démarrage donne la valeur lue : Bonus : réglage bDisabled = vrai|faux (…).

1.6 En-tête

.h l. 1-10 : origine « comportement reconstruit d'après LifePlay 2023.04 de Vinfamy » (sans accents, comme les commentaires du module), licence MIT (SPDX-License-Identifier: MIT, texte court des droits et de la condition de mention), renvoi à Licenses/Vinfamy_License.txt (ligne 1 : « … are under MIT », relue). .cpp l. 1-3 : rappel de l'origine et de la licence.

1.7 Build.cs

Inchangé, aucun module nouveau requis : Engine (KismetMathLibrary, GameInstanceSubsystem, GameInstance), Core (Ticker, RandomStream, Optional), CoreUObject (réflexion) sont déjà des dépendances publiques (Source\LifePlay\LifePlay.Build.cs:17). Catégorie de journal LogLifePlay : Source\LifePlay\LifePlay.h:9.

2. Écarts à la SPEC (obligatoire)

# SPEC Fait Pourquoi
E1 § 2.4 (b), § 2.5 : UCLASS(config = GameUserSettings), ini utilisateur ; § 3 : désactivation par bDisabled=True dans l'ini utilisateur UCLASS(config = Game), valeur par défaut du code, rien écrit consigne 2 de Big Boss (fiche § 3) : réglage dans Config\, rien dans Saved\. Conséquence : le moyen de désactiver sans reconstruire dans un jeu empaqueté n'est plus établi (point VAL-11)
E2 § 2.4 : journal niveau Display niveau Log (avertissements en Warning, contrôle de formule fautif en Error) fiche § 4 : UE_LOG(LogLifePlay, Log, …). Les formats de texte sont ceux de la SPEC ; dans le fichier de journal, LogLifePlay: Bonus : … au lieu de LogLifePlay: Display: Bonus : … ; le motif Bonus : de VAL 7.5 / 7.7 / 7.9 trouve les deux
E3 § 2.4 sondage : « si GI.SexOn faux → vider le suivi » la mémoire des compteurs est vidée ; un orgasme déjà détecté garde son tempo de 0,2 s et reçoit sa récompense même si la scène vient de finir comme l'auteur (RetriggerableDelay indépendant de la scène, E3 / E10) ; évite de perdre la récompense de l'orgasme qui termine la scène
E4 E3 : RetriggerableDelay (temps du monde) tempo en temps réel (FPlatformTime::Seconds) sous-système de GameInstance sans monde garanti ; différence seulement en pause ou en temps dilaté pendant les 0,2 s
E5 E6c : la condition attractiontoplayer est évaluée quand la clé passe bAttractionAllowed calculé avant Pick (signature de la SPEC), avec court-circuit : isPlayer → PlayerCharacter → présence dans SexPersons → IsInterestedIn (appel Blueprint) ; l'appel a lieu pour tout PNJ de scène avec le joueur, même si la clé n'est pas tirée signature imposée (bool bAttractionAllowed) ; équivalent si IsInterestedIn est sans effet de bord (non relu en entier : VAL-9)
E6 § 5 : fonction ou propriété absente = avertissement et abandon sur le chemin attractiontoplayer (isPlayer, PlayerCharacter, IsInterestedIn), l'abandon porte sur la clé (refusée), le reste du tirage continue ; ailleurs, abandon de la récompense la condition ne peut pas être prouvée vraie : clé refusée, comme si la condition était fausse ; avertissement unique écrit
E7 § 2.4 (b) signatures ajouts privés : Poll, FirePending, AttractionAllowed, IsInterestedIn, PersonName, WarnOnce, structure FLPBonusPending ; CheckFormula statique publique ; paramètre de Pick nommé InRng découpage du sondage ; InRng évite un C4458 (paramètre qui masque le membre Rng)
E8 § 2.4 formats en plus : Bonus : réglage bDisabled = … au démarrage ; deux lignes Verbose (attente remplacée, personne disparue) invisibles par défaut ; avertissements préfixés Bonus : diagnostic J1 ; un LogStats -Pattern 'Bonus :' compte aussi ces lignes (VAL-5)
E9 § 2.4 contrôle de formule sur 7 valeurs 7 valeurs + 1 cas de signe (50 avec - → −1,1) contrôle du signe
E10 § 2.4 : QueryStat sorties Value, Bonus signature exigée stricte : Stat chaîne, WithoutBuff et RNG booléens, Value réel en sortie ; sinon avertissement et pas de récompense règle « pas de repli silencieux » ; risque si RNG n'est pas booléen chez nous (VAL-8)

3. Instantané (lot, manifeste)

sans objet : la fiche ne demande pas d'instantané et aucun fichier existant n'est touché (deux fichiers neufs). État de départ et d'arrivée relevés (sk-change-safety règles 2 et 10, invoqué après l'écriture des deux fichiers : écart de méthode sans effet, rien n'a été écrasé) :

  • Avant : git -C "D:/Game/LifePlay-master 5.8" status --short → dans Content/Source : M Content/BPs/GI.uasset, PC.uasset, Person.uasset, Room.uasset, Source/LifePlay/LPDebugSubsystem.cpp/.h, LPDirectControlSubsystem.cpp/.h, LPOutdoorRoomSubsystem.cpp/.h, LifePlay.cpp ; ?? LPCameraModeSubsystem.cpp/.h, LPSaveRelaunch.cpp/.h.
  • Après : git -C "D:/Game/LifePlay-master 5.8" status --porcelain -- Content Source Config → mêmes 11 M, 4 ?? d'avant, plus ?? Source/LifePlay/LPBonusRewardSubsystem.cpp et ?? Source/LifePlay/LPBonusRewardSubsystem.h. Différence = le changement prévu.
  • Note pour VAL 7.1 : la SPEC comptait « 10 M, 4 ?? » ; Content/BPs/PC.uasset était déjà M à l'entrée de cette fiche (autre lot), donc 11 M.
  • Journal du changement : 2026-10-14 | ORG5 V-min, 2 fichiers C++ neufs | Source/LifePlay/LPBonusRewardSubsystem.h, .cpp | retour arrière (proposé, non lancé) : déplacer les deux fichiers vers D:\Game\LifePlay_Backup_5.5\STE\ORG5-I1\ | instantané : sans objet (fichiers neufs) | vérification : git status ci-dessus.

4. Ce qui n'a pas été fait

  • Compilation (fiche BUILD du build commun) ; aucune vérification UHT ni MSVC.
  • V-1 (panneau Slate, 3 boutons, choix), V-2 (case de réglage, onglet Lewd), H2 (nœud dans GiveOrgasmReward) : hors V-min.
  • Section [/Script/LifePlay.LPBonusRewardSettings] dans Config\DefaultGame.ini : non ajoutée (fichier existant ; décision ultérieure).
  • Formatage de nombre par FText (SPEC § 6, incertain) : sans objet en V-min (le journal utilise %+.1f) ; à lire pour V-1.
  • Lecture complète de GI.IsInterestedIn (effets de bord) et des types réels des variables de GI et Person (lecture L-M2 de la SPEC) : non faites (pas de MCP).

5. Points à confirmer en VAL (un par ligne)

  • VAL-1 : build du module LifePlay code 0 ; UHT accepte les structures C++ simples FLPBonusStat / FLPBonusPending dans l'en-tête et le membre TOptional<FLPBonusPending>.
  • VAL-2 : aucun avertissement-erreur de masquage (C4456 / C4458) ni C4996 sur les deux fichiers ; UKismetMathLibrary::FCeil (UE_INL_API) se lie en Development.
  • VAL-3 : FMemory_Alloca_Aligned(ParmsSize, Alignment) avec des int32 locaux compile sans avertissement (macro GenericPlatformMemory.h:221).
  • VAL-4 : table : 20 clés et 20 listes de tags identiques à SPEC § 1.2 (relecture ligne à ligne de .cpp l. 207-229) ; RewardChange dans l'ordre de SPEC § 1.3 ; Pick : 10 candidates, 3 au plus, Overlaps(…, true), règle attractiontoplayer.
  • VAL-5 : aucun widget créé (Grep UUserWidget|SNew|AddViewportWidgetContent|CreateWidget dans les deux fichiers = 0) ; les avertissements Bonus : ne comptent pas comme lignes de récompense dans 7.7.
  • VAL-6 : journal de démarrage : 1 ligne Bonus : contrôle formule OK, 0 Error du sous-système, 1 ligne Bonus : réglage bDisabled = faux.
  • VAL-7 : journal J1 : aucun avertissement Bonus : pas de propriete … ni signature inattendue … (types de SexOn, SexPersons, CurrentTags, Sex_OrgasmCounts, isPlayer, Name_First, QueryStat, ModifyStat, IsInterestedIn conformes).
  • VAL-8 : Person.QueryStat.RNG est booléen (L6f_dump_Person.txt:25 : relié à RngActual, type non lu) ; sinon aucune récompense ne sera appliquée.
  • VAL-9 : GI.IsInterestedIn sans effet de bord sur GI ou Person (écart E5) et ordre WhoIsInterested = PNJ correct.
  • VAL-10 : J1 par orgasme : 1 ligne orgasme de, puis 1 ligne <stat> <±valeur> ou aucune récompense éligible ; valeur conforme à SPEC § 1.3 pour le x journalisé ; la ligne « stat ±n » de ModifyStat apparaît en jeu.
  • VAL-11 : désactivation : où écrire bDisabled=True pour qu'un jeu empaqueté la lise sans reconstruire (couche Saved\Config\Windows\Game.ini lue ou non) ; sinon seul DefaultGame.ini avant cuisson vaut (écart E1).
  • VAL-12 : règle commune ORG4 / ORG5 : fin par Endsex sans Cum → 0 ligne ORG5 ; un orgasme → au plus 1 ligne de récompense appliquée par personne (V6).
  • VAL-13 : 7.1 : git status --porcelain -- Content Source Config = état d'entrée (11 M, 4 ??) + 2 ?? de cette fiche.

6. Fichiers à commiter

Dépôt du jeu D:\Game\LifePlay-master 5.8, branche ue5.8 (après BUILD et VAL, sur ticket au gardien) :

  • A Source/LifePlay/LPBonusRewardSubsystem.h
  • A Source/LifePlay/LPBonusRewardSubsystem.cpp
  • A Docs/Migration/etat/ST/STE/STE-ORG5-I1-IMPL.md

Tours : 28 / 45 (déclaré ; python -I Tools/FicheStats.py --lot STE fait foi)

Constats

  • Points à vérifier à la compilation : VAL-1 à VAL-3 ; code commun 5.5 / 5.8 non exigé (développement 5.5 arrêté), mais aucune API propre à 5.8 n'est utilisée (FTSTicker, FDoubleProperty, UE_INL_API existent depuis 5.0, estimé, non relu en 5.5).
  • Un orgasme dans les 0,2 s d'un autre (deux personnes) : seul le dernier reçoit une récompense, comme l'auteur (SPEC E3) ; la règle commune (au plus une fois par personne) est tenue.
  • À verser : lp-ue-compat § 1 (ou ue-cpp-build § 0) — en 5.8, le validateur de format de UE_LOG accepte l'indicateur + et les conversions f / g (Core\Public\String\FormatStringSan.h:164, :424-426) ; UKismetMathLibrary::FCeil est UE_INL_API (en ligne hors Debug, KismetMathLibrary.h:180-183) ; FMemory_Alloca_Aligned est dans GenericPlatform\GenericPlatformMemory.h:221. sk-protocol § 7 — compter les CR d'un fichier sans redirection : od -An -v -tx1 <f> | tr ' ' '\n' | grep -c '^0d$' (le od -c | grep -c '\\r' compte des lignes d'od, pas des octets : faux).