20 KiB
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 octets2f 2f 20(pas de BOM), commeLPUserSettings.h(CR=0 LF=59, même début).- Grep
\{ TEXT\("[a-z_]+-?"\), \{dans le.cpp→20(les 20 lignes de la table) ; GrepUsers|<nom>dansLPBonusReward*→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 queLPDirectControlSubsystem.cpp:449(« » dans unTEXT, 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.SexOnfaux → mémoire des compteurs vidée ; vrai → pour chaque personne valide deGI.SexPersons: premier passage = mémorisation sans récompense ; hausse deSex_OrgasmCounts→ copie deGI.CurrentTags, armement d'un orgasme en attente à t + 0,2 s (remplace l'attente en cours, commeRetriggerableDelay) ; baisse → resynchronisation sans récompense ; personne sortie de la scène → oubliée. FirePending(l. 434) : à échéance, personne valide →OnOrgasm: ligneorgasme de, puisdésactivé(réglage) ou tiragePick→aucune récompense éligibleouQueryStat+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 deSex_OrgasmCountsarme une seule attente, qui donne au plus une récompense appliquée ;EndsexsansCumn'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(FTickerDelegatebool(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), corpsKismetMathLibrary.inl:743-746(FMath::CeilToInt) ;MapRangeClamped:.h:836(ENGINE_API), corpsPrivate\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 avecFRandomStream::RandRange(Runtime\Core\Public\Math\RandomStream.h:209-214, bornes incluses) ; graineGenerateNewSeed(:105-108).Array_Contains→GenericArray_Find(KismetArrayLibrary.cpp:547-559) →Inner->Identical;TProperty_WithEqualityAndSerializer::Identicalcompare paroperator==(Runtime\CoreUObject\Public\UObject\UnrealType.h:1769) : le C++ (TArray<FString>::Contains) utilise le même opérateur (estimé : l'héritage deFStrPropertydepuis ce gabarit n'est pas relu).UObject::FindFunctionRuntime\CoreUObject\Public\UObject\Object.h:1258;ProcessEvent:1490;UStruct::GetMinAlignmentClass.h:790;FField::GetAuthoredNameField.h:903;InitializeValue_InContainer/DestroyValue_InContainerUnrealType.h:1128,:1040;FObjectPropertyBase::GetObjectPropertyValue_InContainer/SetObjectPropertyValue_InContainerUnrealType.h:2896,:2901;StrProperty.hinclus parUnrealType.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), conversionsfetg(: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→ dansContent/Source:MContent/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 11M, 4??d'avant, plus?? Source/LifePlay/LPBonusRewardSubsystem.cppet?? 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 11M. - 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]dansConfig\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
LifePlaycode 0 ; UHT accepte les structures C++ simplesFLPBonusStat/FLPBonusPendingdans l'en-tête et le membreTOptional<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 desint32locaux compile sans avertissement (macroGenericPlatformMemory.h:221). - VAL-4 : table : 20 clés et 20 listes de tags identiques à SPEC § 1.2 (relecture ligne à ligne de
.cppl. 207-229) ;RewardChangedans l'ordre de SPEC § 1.3 ;Pick: 10 candidates, 3 au plus,Overlaps(…, true), règleattractiontoplayer. - VAL-5 : aucun widget créé (Grep
UUserWidget|SNew|AddViewportWidgetContent|CreateWidgetdans les deux fichiers = 0) ; les avertissementsBonus :ne comptent pas comme lignes de récompense dans 7.7. - VAL-6 : journal de démarrage : 1 ligne
Bonus : contrôle formule OK, 0Errordu sous-système, 1 ligneBonus : réglage bDisabled = faux. - VAL-7 : journal J1 : aucun avertissement
Bonus : pas de propriete …nisignature inattendue …(types deSexOn,SexPersons,CurrentTags,Sex_OrgasmCounts,isPlayer,Name_First,QueryStat,ModifyStat,IsInterestedInconformes). - VAL-8 :
Person.QueryStat.RNGest booléen (L6f_dump_Person.txt:25: relié àRngActual, type non lu) ; sinon aucune récompense ne sera appliquée. - VAL-9 :
GI.IsInterestedInsans effet de bord sur GI ou Person (écart E5) et ordreWhoIsInterested = PNJcorrect. - VAL-10 : J1 par orgasme : 1 ligne
orgasme de, puis 1 ligne<stat> <±valeur>ouaucune récompense éligible; valeur conforme à SPEC § 1.3 pour lexjournalisé ; la ligne « stat ±n » deModifyStatapparaît en jeu. - VAL-11 : désactivation : où écrire
bDisabled=Truepour qu'un jeu empaqueté la lise sans reconstruire (coucheSaved\Config\Windows\Game.inilue ou non) ; sinon seulDefaultGame.iniavant cuisson vaut (écart E1). - VAL-12 : règle commune ORG4 / ORG5 : fin par
EndsexsansCum→ 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 (11M, 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.hA Source/LifePlay/LPBonusRewardSubsystem.cppA 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_APIexistent 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 (ouue-cpp-build§ 0) — en 5.8, le validateur de format deUE_LOGaccepte l'indicateur+et les conversionsf/g(Core\Public\String\FormatStringSan.h:164,:424-426) ;UKismetMathLibrary::FCeilestUE_INL_API(en ligne hors Debug,KismetMathLibrary.h:180-183) ;FMemory_Alloca_Alignedest dansGenericPlatform\GenericPlatformMemory.h:221.sk-protocol§ 7 — compter les CR d'un fichier sans redirection :od -An -v -tx1 <f> | tr ' ' '\n' | grep -c '^0d$'(leod -c | grep -c '\\r'compte des lignes d'od, pas des octets : faux).