20 KiB
Fiche STE-ORG5-V1 — Niveau N2 — Modèle prévu (modèle de l'agent lp-validator) | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills lp-validate, sk-recount, sk-protocol (debug-verification préchargé)
STE-ORG5-V1-VAL : VAL statique, sans compilation, du bonus d'orgasme ORG5 V-min (2026-10-14)
Périmètre : lecture seule, sans compilation, éditeur, MCP, commandlet ni jeu. Seul fichier écrit : ce livrable. Aucun refus du crochet (commandes Bash sans >, sans python -c, sans heredoc). Tout verdict sur la compilation reste « probable ».
1. Fiches amont relues
- Fiche
fiches\STE-ORG5-V1.md(lue en entier) ; livrable IMPLSTE-ORG5-I1-IMPL.md(lu en entier : § 1.4, § 2 écarts E1-E10, § 5 points VAL-1 à VAL-13) ; SPECSTE-ORG5-SPEC.md(lue en entier, dont « Décisions de l'utilisateur » l. 291-294 et « Règle commune ORG4 / ORG5 » l. 296-300). La fichefiches\STE-ORG5-I1.mdn'a pas été relue : ses consignes sont reprises par la fiche V1 (§ 3 et § 4), qui fait foi ici. - Code lu en entier :
Source\LifePlay\LPBonusRewardSubsystem.h(119 lignes) et.cpp(667 lignes). - Dumps relus :
L6f_dump_Person.txt:20-29(QueryStat),:555-570(isPlayer,ModifyStat) ;L6f_dump_GI.txt:19622-19673(Overlaps),:21245-21255etGI__EventGraph_P2.txt:14299-14306(CurrentTags),:23421-23503(IsInterestedIn),:23548-23554(appelQueryStat(Stat, WithoutBuff, RNG)par un autre sous-graphe) ;Person__EventGraph.txt:15904-15913(remise à 0),:16112-16125(IncrementIntdeSex_OrgasmCounts). - Moteur 5.8 relu :
KismetMathLibrary.h:180-184, 796, 836, 4581-4583;GenericPlatformMemory.h:221;Ticker.h:21, 45, 66;Class.h:790;Field.h:903;UObjectBaseUtility.h:633-657;KismetArrayLibrary.cpp:338-353;RandomStream.h:105-108, 209-214;TextProperty.h(présent).
2. Points à confirmer des IMPL
| Id | Point | Verdict | Preuve / raison |
|---|---|---|---|
| VAL-1 | build LifePlay code 0 ; UHT accepte FLPBonusStat / FLPBonusPending / TOptional |
reporté (BUILD COMMUN-6) ; lecture : probable | Structures C++ simples hors USTRUCT, membres non UPROPERTY dans un UCLASS : usage permis ; generated.h en dernier include (.h:42) ; Misc/Optional.h inclus (.h:39) |
| VAL-2 | pas de C4456 / C4458 / C4996 ; FCeil (UE_INL_API) se lie |
lecture : PASS-STATIC probable ; édition de liens reporté (BUILD) | Aucun nom local ne masque un membre (membres TickHandle, Rng, LastCounts, Pending, Warned ; paramètre InRng ; boucles sans imbrication du même nom) ; FCeil : KismetMathLibrary.h:796, UE_INL_API = vide si KISMET_MATH_INLINE_ENABLED (corps .inl inclus :4581-4583), sinon ENGINE_API (:181-183) : lié dans les deux cas ; module Engine en dépendance publique (LifePlay.Build.cs:17) |
| VAL-3 | FMemory_Alloca_Aligned(ParmsSize, Alignment) sans avertissement |
lecture : probable ; reporté (BUILD) | Macro GenericPlatformMemory.h:221 : (Size==0) ? 0 : (Alignment <= 16 ? FMemory_Alloca(Size) : (void*)…) ; arguments : deux int32 simples (pas d'effet d'évaluation double, pas de problème de parenthèses) ; appelée dans le cadre de l'appelant (.cpp:558, 593, 628), jamais dans FScopedParms ; le précédent (LPDirectControlSubsystem.cpp:426) utilise FMemory_Alloca sans alignement : la variante alignée est nouvelle dans le module |
| VAL-4 | table 20 clés / 20 listes ; RewardChange ; Pick |
PASS-STATIC | Voir § 3, lignes C2, C3, C4 |
| VAL-5 | aucun widget créé | PASS (Grep) | Grep UUserWidget|SNew|AddViewportWidgetContent|CreateWidget|GEngine|GetWorld dans les deux fichiers : aucune correspondance (seul SaveConfig apparaît, dans le commentaire .h:28). Les avertissements Bonus : … ne comptent pas comme lignes de récompense : le motif de récompense de 7.7 est <stat> <±valeur> ; réserve : LogStats -Pattern 'Bonus :' compte aussi avertissements et lignes réglage (E8), à filtrer à la lecture |
| VAL-6 | journal de démarrage : 1 contrôle formule OK, 0 Error, 1 réglage … faux |
reporté (J1, journal de jeu) ; lecture : PASS-STATIC | Initialize (.cpp:175-194) émet exactement une ligne OK ou Error, puis une ligne réglage ; valeurs recalculées à la main (§ 3, C5) : la formule est juste, donc OK attendu. Réserve : un ULPBonusRewardSubsystem par UGameInstance ; en éditeur, un par session PIE : plusieurs lignes possibles, normales |
| VAL-7 | pas d'avertissement pas de propriete … / signature inattendue … |
reporté (J1) ; incertain | Types non lus chez nous : CurrentTags (tableau de chaînes : estimé, voir D2), Name_First, RNG (VAL-8). Le code avertit une fois par cause et abandonne sans plantage (§ 3, C6) |
| VAL-8 | Person.QueryStat.RNG booléen |
incertain, reporté à la compilation / en jeu | L6f_dump_Person.txt:25 : RNG -> VariableSet_1.RngActual, type non porté par le dump. Indice : L6f_dump_GI.txt:23552 appelle QueryStat avec RNG = 'false' (littéral booléen d'un autre sous-graphe), donc probablement booléen. Si faux : QueryStat avertit une fois (.cpp:584-589) et aucune récompense n'est appliquée (jamais de plantage) |
| VAL-9 | IsInterestedIn sans effet de bord ; ordre WhoIsInterested = PNJ |
PASS-STATIC | L6f_dump_GI.txt:23421-23503 lu en entier : lectures de IsMale, Genital (de WhoInterests), Interested_Men/Women/Trans (de WhoIsInterested), NPCsRespectPlayerSex, PlayerCharacter ; variable InterestedToReturn positionnée à des constantes, retour par FunctionResult ; aucune écriture sur GI ni sur Person (seul l'ensemble InterestedToReturn, variable locale à la fonction, estimé). Ordre : les préférences (Interested_*) sont lues sur WhoIsInterested, le sexe/genre sur WhoInterests : « le PNJ s'intéresse au joueur » = (Person, Player), ordre du code (.cpp:520). Branche K2Node_CallFunction_34711 (appel récursif inversé) : sortie non reliée à l'exécution principale (nœud orphelin de la lecture ; non tracé jusqu'au bout) |
| VAL-10 | journal J1 par orgasme, valeur conforme | reporté (J1) | Critère 7.7 ; la valeur suit la formule vérifiée en C5 |
| VAL-11 | désactivation en jeu empaqueté (config = Game) |
reporté (J1) ; incertain | Voir § 6 case 3 : lecture du moteur non aboutie (aucune occurrence de {SAVED}/Config dans Core\Private\Misc) ; seul DefaultGame.ini avant cuisson est établi comme voie sûre |
| VAL-12 | règle commune ORG4 / ORG5 | PASS-STATIC | Voir § 3, C2 (dernière ligne) ; vérification en jeu reportée à J1 (V6) |
| VAL-13 | git status = entrée + 2 ?? |
PASS | Voir § 3, C1 |
3. Critères rejoués (commande ou lecture → résultat)
| # | Contrôle | Preuve | Verdict |
|---|---|---|---|
| C1 | Périmètre | git -C 'D:/Game/LifePlay-master 5.8' status --porcelain -- Content Source Config → 11 M (Content/BPs/GI, PC, Person, Room.uasset ; Source/LifePlay/LPDebugSubsystem.cpp/.h, LPDirectControlSubsystem.cpp/.h, LPOutdoorRoomSubsystem.cpp/.h, LifePlay.cpp) et 6 ?? (LPBonusRewardSubsystem.cpp/.h, LPCameraModeSubsystem.cpp/.h, LPSaveRelaunch.cpp/.h) = état d'entrée de l'IMPL § 3 (11 M, 4 ??) + exactement les deux ?? de la fiche. Aucune ligne Config ; LifePlay.Build.cs absent de la sortie (inchangé) ; HEAD 0745917. Build.cs:17 : Core, CoreUObject, Engine en dépendance publique ; Slate/UMG non utilisés : suffisant |
PASS (l'attribution des 11 M à d'autres lots repose sur l'IMPL ; aucun n'est un fichier du lot) |
| C2 | Conformité SPEC : table, tirage, H1, règle commune | Table (.cpp:207-228, relue ligne à ligne contre SPEC § 1.2) : 20 clés dans l'ordre, tags identiques (ex. likes_oral 6 tags, likes_handjob 3, fitness / prone_to_orgasm- {Penetration, Foreplay, Cum}, masochist / likes_rough / masochist- {Aggressive, AggressiveFM}, clés 1, 3, 4 vides). Les offsets Cursor.uexp:4643-6408 ne sont pas rejoués (pas d'accès au désassembleur) : la table est comparée à la SPEC, non à l'asset de l'auteur. Tirage (Pick, .cpp:287-332) : mélange Fisher-Yates identique à KismetArrayLibrary.cpp:344-351 (RandRange(i, LastIndex) bornes incluses, RandomStream.h:209-214), SetNum(min(20,10)), filtre Overlaps(PositionTags, Entry.Tags, true) dans l'ordre de l'auteur (SPEC E6b : Overlaps(Tags.Strings, tags, true)), attractiontoplayer refusée si !bAttractionAllowed, sortie à 3 (NotEqual(countBR, 3)), - final retiré avec drapeau de signe ; condition attractiontoplayer (.cpp:488-525) : NON isPlayer, IsInterestedIn(Person, PlayerCharacter), SexPersons contient le joueur (SPEC E6c). Application : première récompense (Stats[0]), QueryStat(Stat,false,false).Value puis ModifyStat(Stat,Change,false,false) (.cpp:473-483, 596-597, 632-633), conforme à la décision Q-ORG5-1. Overlaps (.cpp:269-285) = L6f_dump_GI.txt:19622-19673 : IfEmptyThenTrue ET (len A = 0 OU len B = 0) → vrai ; sinon un élément de A dans B ; sinon faux. H1 : Poll compare Sex_OrgasmCounts à LastCounts : premier passage = mémorisation, hausse = 1 attente armée (une par hausse, remplacée si une autre arrive dans les 0,2 s), baisse = resynchronisation sans récompense, SexOn faux = mémoire vidée ; Person__EventGraph.txt:16118-16125 (incrément dans Cum) et :15908-15911 (remise à 0 avec l'énergie) cohérents. Règle commune : Endsex sans Cum n'incrémente pas le compteur donc 0 ligne ORG5 ; un incrément = au plus une attente = au plus une récompense appliquée. Toute personne : boucle sur tout SexPersons (PNJ compris), attractiontoplayer réservée aux PNJ |
PASS-STATIC |
| C3 | Formule RewardChange (.cpp:232-241) |
Ordre : Abs → MapRangeClamped(0,100,0,1.9) → 2 - m → × 10 → FCeil → entier en réel → / 10 → × signe, identique à SPEC § 1.3 (Cursor.uexp:8339-8521) ; fonctions UKismetMathLibrary elles-mêmes (.h:796, 836), en double |
PASS-STATIC |
| C4 | 21 points de la formule, calculés à la main (sans exécution : aucun script permis par la fiche) | v = ceil((2 - 1,9·x/100)·10)/10 : x = 0 → 20 → 2,0 ; 1 → 19,81 → 2,0 ; 5 → 19,05 → 2,0 ; 10 → 18,1 → 1,9 ; 20 → 16,2 → 1,7 ; 25 → 15,25 → 1,6 ; 30 → 14,3 → 1,5 ; 40 → 12,4 → 1,3 ; 45 → 11,45 → 1,2 ; 50 → 10,5 → 1,1 ; 55 → 9,55 → 1,0 ; 60 → 8,6 → 0,9 ; 70 → 6,7 → 0,7 ; 75 → 5,75 → 0,6 ; 80 → 4,8 → 0,5 ; 90 → 2,9 → 0,3 ; 95 → 1,95 → 0,2 ; 99 → 1,19 → 0,2 ; 100 → 1,9 exact, 2 − 1,9 = 0,10000000000000009 (soustraction exacte en IEEE double), × 10 = 1,0000000000000009 > 1 → ceil 2 → 0,2 ; 120 → fixé à 1,9 → 0,2 ; −30 → |
x |
| C5 | CheckFormula (.cpp:243-267) |
8 cas : 0, 10, 25, 50, 75, 90, 100 (+50 négatif) → 2,0 ; 1,9 ; 1,6 ; 1,1 ; 0,6 ; 0,3 ; 0,2 ; −1,1, tolérance 1e-9, valeurs égales à ma table (C4) | PASS-STATIC |
| C6 | Réflexion : noms, types, retours (contre dumps) | QueryStat(Stat, WithoutBuff, RNG) → Value, Bonus (Person:20-29) : code exige Stat chaîne, WithoutBuff / RNG booléens, Value réel en sortie (.cpp:579-583), noms exacts ; ModifyStat(Stat, Change, DifficultyAdjusted, NoDisplay) → Value (Person:561-570) : code exige Stat, Change réel d'entrée, deux booléens (.cpp:614-618), noms exacts ; IsInterestedIn(WhoIsInterested, WhoInterests) → Interested (GI:23421-23443) : noms exacts, Interested booléen en sortie (.cpp:538-547). Paramètres cherchés par nom, pas par position : l'ordre et la taille de la structure viennent de Function->ParmsSize / GetMinAlignment (Class.h:790). Propriétés : SexOn (booléen), SexPersons (tableau d'objets), PlayerCharacter (objet), CurrentTags (tableau de chaînes), Sex_OrgasmCounts (entier, macro IncrementInt), isPlayer (booléen), Name_First (texte ou chaîne). Introuvable ou type inattendu : WarnOnce (un avertissement par cause), abandon ou clé refusée, aucun plantage. Le type du retour Bonus de QueryStat n'est pas contrôlé : sans importance (non lu) |
PASS-STATIC ; types réels non lus : voir D2 |
| C7 | Sûreté | IsValid(Person) avant lecture (.cpp:375) et avant OnOrgasm (.cpp:443) ; TWeakObjectPtr pour l'attente et la mémoire des compteurs ; GetGameInstance() testé (.cpp:337, :462) ; ObjectProperty valeur nulle : IsValid(Player) (.cpp:510) ; Persons peut contenir des nuls : filtrés ; pas de monde requis ; pas de check, ensure, GEngine ni appel de débogage (Grep) donc rien de spécifique au Shipping ; Deinitialize retire la minuterie (.cpp:198). Paramètres de ProcessEvent : mémoire mise à zéro, initialisée puis détruite (FScopedParms, même séquence que LPDirectControlSubsystem.cpp:426-455). Coût : 10 Hz ; hors scène : 1 recherche de propriété par tick ; en scène : SexPersons + 1 recherche par personne + une copie de tableau d'objets ; IsInterestedIn (Blueprint) seulement à l'orgasme d'un PNJ avec le joueur présent ; négligeable (estimé) |
PASS-STATIC (D1 : chaîne Printf construite à chaque tick même quand l'avertissement est déjà émis) |
| C8 | Compilation probable (lecture) | Includes suffisants : Containers/Ticker.h, Math/RandomStream.h, Misc/Optional.h, Subsystems/GameInstanceSubsystem.h (.h) ; Engine/GameInstance.h, HAL/PlatformTime.h, Kismet/KismetMathLibrary.h, LifePlay.h, UObject/TextProperty.h (présent), UObject/UnrealType.h (.cpp). UCLASS + GENERATED_BODY présents sur les deux classes, LIFEPLAY_API sur les deux. LogLifePlay : LifePlay.h:9 DECLARE_LOG_CATEGORY_EXTERN(LogLifePlay, Log, All). FTSTicker::AddTicker(FTickerDelegate, float) Ticker.h:45, RemoveTicker statique :66 (appel par l'instance : comme LPColorGradeSubsystem.cpp:76, 81). IsA(TObjectPtr<UClass>) : le gabarit UObjectBaseUtility.h:633-639 convertit en const UClass*. FField::GetAuthoredName Field.h:903. Table : liste d'initialisation de FLPBonusStat (agrégat) : valide. Avertissements : aucun masquage C4456 / C4458 relevé ; conversions double / float explicites (static_cast<float>) ; aucun float vers double implicite dans UE_LOG (arguments double pour %f / %g, int32 pour %d) ; conditionnelle TEXT(", sortie") : TEXT("") de types const TCHAR[n] différents : décroissance en pointeur, admise. Fonctions de l'espace de noms nommé LPBonusReward : noms uniques dans le module (Grep BonusReward|FLPBonus|namespace LPBonus dans Source : seuls les deux fichiers) donc pas de collision en build unitaire |
PASS-STATIC (compilation : reportée à BUILD) |
| C9 | Consignes | Grep SaveConfig|TryUpdateDefaultConfigFile|GConfig|FFileHelper|Saved : une seule occurrence, dans le commentaire .h:28 ; UCLASS(config = Game) (.h:45) ; UPROPERTY(config) lecture seule par GetDefault ; aucune lecture de journal, aucune accroche cachée, aucun nœud Blueprint, aucune écriture de fichier ; Config\ inchangé (C1). En-tête .h:1-10 : origine « reconstruit d'après LifePlay 2023.04 de Vinfamy », licence MIT (SPDX-License-Identifier: MIT), renvoi à Licenses/Vinfamy_License.txt ; .cpp:1-4 : rappel d'origine et de licence. Le texte du fichier Licenses n'a pas été relu par moi (confiance à la SPEC § 4.2 R8 qui cite Licenses\Vinfamy_License.txt:1) |
PASS-STATIC |
| C10 | CR / LF | od -An -v -tx1 <f> | tr ' ' '\n' | grep -c '^0d$' : LPBonusRewardSubsystem.h CR=0 LF=119 first3= 2f 2f 20 ; LPBonusRewardSubsystem.cpp CR=0 LF=667 first3= 2f 2f 20 (pas de BOM). Comparaison : LPUserSettings.h i/lf w/lf (git ls-files --eol), mêmes trois premiers octets. Grep Users|C:\\ dans les deux fichiers : 0 |
PASS |
| C11 | Fichiers non ASCII | Le .cpp contient des accents dans cinq formats de journal (contrôle, réglage, désactivé, récompense), sans BOM : même pratique que LPDirectControlSubsystem.cpp:449 (« », // en tête sans BOM) déjà compilé en 5.8 (estimé, build antérieur du module) |
PASS-STATIC |
4. Diff lu en entier
- Deux fichiers neufs, non suivis par git (pas de diff contre
STE-ORG5-avant: aucun fichier existant touché). Lus en entier :.h119 lignes,.cpp667 lignes (comptesod: LF = 119 et 667). Aucun fichier existant modifié par le lot (C1). Aucun script de modification : les fichiers portent l'horodatage de l'écriture de l'IMPL (2026-10-14, 01:25 et 01:26). - Ajouts hors SPEC relevés (tous annoncés dans l'IMPL E1-E10, aucun caché) :
CheckFormulapublique statique,FLPBonusPending,Poll,FirePending,AttractionAllowed,IsInterestedIn,PersonName,WarnOnce, ligne de journalréglage, huit cas de formule au lieu de sept. - Écarts IMPL à la SPEC relus : E1 (config Game, conforme à la consigne de Big Boss), E2 (niveau
Log), E3 (attente survivant à la fin de scène : conforme à l'auteur), E4 (temps réel), E5-E6 (conditionattractiontoplayerévaluée avant le tirage : équivalent carIsInterestedInsans effet de bord, VAL-9), E7-E10 : acceptés, aucun ne contredit une décision de l'utilisateur.
5. Verdict
PASS-STATIC : aucun défaut bloquant. Deux défauts mineurs (D1, D2) et trois incertitudes reportées (VAL-7/8 types réels, VAL-11 désactivation empaquetée), à lever à la compilation (BUILD COMMUN-6) et en jeu (J1). ORG5 peut entrer dans le build COMMUN-6.
Défauts :
| # | Gravité | Constat | Correctif proposé (non appliqué) |
|---|---|---|---|
| D1 | mineur | Les appels WarnOnce(Key, FString::Printf(...)) construisent le message à chaque appel, même quand la clé est déjà dans Warned : si SexOn, SexPersons ou Sex_OrgasmCounts est introuvable, une chaîne est formatée à 10 Hz (.cpp:350, 366, 382, 402). Aucun effet fonctionnel, coût négligeable |
tester Warned.Contains(Key) avant de construire le message (macro ou surcharge WarnOnce(Key, TFunctionRef)), ou le laisser en l'état pour l'alpha |
| D2 | mineur (risque fonctionnel faible) | Le type de GI.CurrentTags n'est pas lu : le code exige TArray<FString> (ReadStrings, .cpp:162-172). Si le tableau est de FName ou de FText, ReadStrings échoue et aucun orgasme n'est récompensé (avertissement unique GI.CurrentTags). Indices pour des chaînes : GI__EventGraph_P2.txt:14305 compare à un littéral 'Vaginal', L6f_dump_GI.txt:21245 branche l'élément sur un SwitchString, la SPEC cite StringArray(Strings = GI.CurrentTags) |
À lever à J1 par l'absence d'avertissement GI.CurrentTags ; si présent : accepter FNameProperty / FTextProperty dans ReadStrings |
6. Points à confirmer par l'utilisateur (cases)
- 1. Build COMMUN-6 : code 0, module
LifePlayrecompilé, 0 erreur, 0 nouvel avertissement sur les deux fichiers (VAL-1, VAL-2, VAL-3). - 2. J1 : journal sans
Bonus : pas de propriete …nisignature inattendue …(VAL-7, VAL-8, D2) ; par orgasme une ligneorgasme depuis une ligne récompense ouaucune récompense éligible(VAL-10) ;EndsexsansCum: aucune ligne ORG5 (VAL-12). - 3. Désactivation d'un jeu empaqueté : décider où écrire
bDisabled=True(DefaultGame.iniavant cuisson établi ; coucheSaved\Config\…\Game.ininon établie) (VAL-11). Non vérifié ici : la recherche du chemin de couche dansCore\Private\Miscn'a donné aucune occurrence de{SAVED}/Config. - 4. Acceptation de D1 en l'état pour l'alpha.
Non rejoué par cette VAL : compilation et UHT, LPTestStringCompat, CompileAllBlueprints, comportement en jeu, offsets Cursor.uexp de la table, texte du fichier Licenses.
Tours : 11 / 40
Constats
Fichiers à commiter:A Docs/Migration/etat/ST/STE/STE-ORG5-V1-VAL.md(dépôt du jeu, brancheue5.8). Aucun autre fichier écrit ; aucun fichier source modifié.- À verser :
lp-validate§ 1 — pour un fichier neuf non suivi,git status --porcelain -- Content Source Configavant / après suffit comme contrôle de périmètre ; compter les CR parod -An -v -tx1 <f> | tr ' ' '\n' | grep -c '^0d$'.ue-cpp-build—FMemory_Alloca_Aligned(5.8,GenericPlatformMemory.h:221) renvoie0siSize == 0: le tampon peut être nul pour une fonction sans paramètre.