diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 02b3717..63e8e47 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -1131,6 +1131,7 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul | | CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` | | CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` | +| CHG-243 | 2026-10-08 | ERR-008 | `Source/LifePlay/LPMapMarkersGuardSubsystem.h/.cpp` (nouveaux) | garde runtime : si `MapMarkers.WidgetComponents` est vide, clear `Vector2Ds`, reconstitue depuis les `UWidgetComponent` existants ou ajoute un slot de focus invisible — évite le spam `Array_Get` / `Set Input Mode Game And UI` (FocusOnMapMarkers sans Length). Corrélé à l'approche PNJ (`updateAllPersonButtons` → `SpawnMapMarkers`). Live Coding PASS (nouvelle classe) | — → créés | supprimer ces deux fichiers | | CHG-242 | 2026-10-08 | VQ-fix | `Source/LifePlay/LPSettingsSubsystem.cpp`, `LPSettingsSubsystem.h` | contraste UI Settings : `HardenSettingsControls` force un style Slate sombre/lisible sur toutes les cases et SpinBox (valeurs pâles sur fond flou) ; ticker Settings à 0 pour que `ApplyRows` gagne face au BP. Compile `LifePlayEditor` PASS | — → modifiés | retirer `HardenSettingsControls` / remettre ticker 0.5 | | CHG-241 | 2026-10-08 | VQ-fix | `Source/LifePlay/LPSettingsSubsystem.cpp`, `Source/LifePlay/LPVideoQualitySubsystem.cpp` | correctifs post-VQ : (1) `CategoryOf` ignore les infobulles (faux positifs `screen`/`quality` → Sex Description, Map Marker, NewAnimationsOnly hors Video) + classements par nom UMG (`ShowFPS`, etc.) ; (2) `MainMenu.GI` rattaché sur l'écran titre (spinbox DoF / cases) ; (3) `RepairShowFPS` (style + enabled) ; (4) `ApplyRows` à chaque tick ouvert ; (5) `ApplyToScalability` n'écrit plus que les 4 `sg.*` en `SetByConsole` (fin du spam `SetByScalability ignored` sur ScreenPercentage/PoolSize/SSS). Live Coding PASS | — → modifiés | revenir sur les deux `.cpp` | | CHG-240 | 2026-10-07 | VQ5 | `Docs/Migration/24_Eval_Agents_VideoQuality.md` (nouveau), `Docs/Migration/etat/VQ/VQ5/VQ5-VAL.md` (nouveau) | clôture VQ5 : évaluation des agents du chantier VQ (lots VQ0–VQ4 tenus/coupés/rollback, qualité des consignes, modèle ↔ complexité dont Fable bloqué → repli Sonnet en VQ2-T2, sync PASS en code et BLOCKED-UI en jeu). Décision **GO conditionnel** avec 3 recommandations. Annexe VQ5-VAL : chemins Overall / Custom / Recommended PASS (code), smoke `-nullrhi` de VQ4 relu (pas de nouveau run : aucun code changé, chemins non atteignables par console). Constat : `DefaultScalability.ini` s'écarte de `BaseScalability.ini` au niveau Epic (cascades 10 → 4, `RadiusThreshold`, `DetailMode`, `RefractionQuality` 2 → 3) ; VQ1-VAL jamais produit ; VQ1 et VQ2 sans CHG. Documentation seule, aucun code | — → créés | supprimer ces deux fichiers | diff --git a/Docs/Migration/Registre_Erreurs.md b/Docs/Migration/Registre_Erreurs.md index 525bec9..3271358 100644 --- a/Docs/Migration/Registre_Erreurs.md +++ b/Docs/Migration/Registre_Erreurs.md @@ -19,7 +19,7 @@ | ERR-005 | `getBeautyValue` : `Floats_*` index hors bornes | PIE 09-28 ; A/B 2026-10-01 : 0,08/perso en 4.27 contre 3,0 en 5.5 | 1 297 | moyenne | L7i | **corrigé (CHG-042, CX-B12)** — cause : `FString::Find` avec sous-chaîne vide (insensible à la casse, depuis le début) renvoie 0 en UE5 et « non trouvé » en 4.27 ; `Contains(x, "")` devenu vrai : `filterMorphList` appelé sans étiquette ne filtrait plus. 385 nœuds `Contains`/`Split`/`FindSubstring` rebranchés sur `LPStringCompat` ; **validé en jeu le 2026-10-01** : 52 → 2,3/min (4.27 : 1,3) | | ERR-006 | `Accès à None` `NearVec` dans `Person::standIdle` | PIE 09-28 | 273 | moyenne | L7k | **corrigé indirectement par CX-B12** : par personnage 0,05 (4.27) → 0,38 (5.5) → 0,09 (5.5 + CX-B12) | | ERR-007 | `Accès à None` `ContactMenu` dans `UpdateDailyPose` | PIE 09-28 | 257 | moyenne | L7k | **corrigé indirectement par CX-B12** : par personnage 0,12 (4.27) → 0,62 (5.5) → 0,20 (5.5 + CX-B12) | -| ERR-008 | `CallFunc_Array_Get_Item` à None dans `GI` | PIE 09-28 | 229 | moyenne | L5 | ouvert | +| ERR-008 | `CallFunc_Array_Get_Item` à None dans `GI` (`FocusOnMapMarkers` → `MapMarkers.WidgetComponents[0]` vide après `HideAllMarkers` / spawn sans amenité) | PIE 09-28 ; retour utilisateur 2026-10-08 (approche PNJ) | 229 | moyenne | L5 | **atténué (CHG-243)** : `LPMapMarkersGuardSubsystem` reconstitue / garantit `WidgetComponents[0]` + clear `Vector2Ds` ; garde BP Length encore souhaitable. Relecture 2026-10-08 : VQ/Settings hors cause ; approche corrélée via `updateAllPersonButtons`→`SpawnMapMarkers` ; `updateButtonSize` = `ButtonSM` seulement (≠ corps / ERR-043) | | ERR-009 | `SpawnActor failed because no class was specified` | PIE 09-28 | 11 | moyenne | L6 | ouvert | | ERR-010 | `LogLPModdingTrace` : instrumentation de portage à chaque `LoadTxt` | tous journaux | ≈ 1 300 à 5 400 / session | faible (bruit) | L3 | clos (CHG-016) : `LogModdingExtra` en Verbose | | ERR-011 | `GetSocketInfoByName … No SkeletalMesh for CharacterMesh` | tous journaux | 80/perso en 4.27, 76 en 5.5 | bruit (existe en 4.27) | — | accepté | diff --git a/Source/LifePlay/LPMapMarkersGuardSubsystem.cpp b/Source/LifePlay/LPMapMarkersGuardSubsystem.cpp new file mode 100644 index 0000000..6c48b6f --- /dev/null +++ b/Source/LifePlay/LPMapMarkersGuardSubsystem.cpp @@ -0,0 +1,149 @@ +// Voir LPMapMarkersGuardSubsystem.h. + +#include "LPMapMarkersGuardSubsystem.h" + +#include "Components/WidgetComponent.h" +#include "Engine/GameInstance.h" +#include "GameFramework/Actor.h" +#include "LifePlay.h" +#include "UObject/UnrealType.h" + +namespace LPMapMarkersGuard +{ + AActor* FindMapMarkers(UGameInstance* GI) + { + if (!GI) + { + return nullptr; + } + if (const FObjectPropertyBase* Prop = CastField(GI->GetClass()->FindPropertyByName(TEXT("MapMarkers")))) + { + if (AActor* FromGI = Cast(Prop->GetObjectPropertyValue_InContainer(GI))) + { + return FromGI; + } + } + for (TObjectIterator It; It; ++It) + { + AActor* Candidate = *It; + if (!Candidate || Candidate->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) + { + continue; + } + if (Candidate->GetClass()->GetName() != TEXT("MapMarkers_C")) + { + continue; + } + if (Candidate->GetGameInstance() != GI) + { + continue; + } + return Candidate; + } + return nullptr; + } + + void ClearArrayProp(UObject* Object, FName Name) + { + FArrayProperty* Arr = Object ? CastField(Object->GetClass()->FindPropertyByName(Name)) : nullptr; + if (!Arr) + { + return; + } + FScriptArrayHelper Helper(Arr, Arr->ContainerPtrToValuePtr(Object)); + Helper.EmptyValues(); + } +} + +void ULPMapMarkersGuardSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPMapMarkersGuardSubsystem::Tick), 0.25f); +} + +void ULPMapMarkersGuardSubsystem::Deinitialize() +{ + FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); + Super::Deinitialize(); +} + +bool ULPMapMarkersGuardSubsystem::Tick(float DeltaTime) +{ + (void)DeltaTime; + if (AActor* Markers = LPMapMarkersGuard::FindMapMarkers(GetGameInstance())) + { + Guard(Markers); + } + return true; +} + +void ULPMapMarkersGuardSubsystem::Guard(AActor* Markers) +{ + FArrayProperty* Arr = CastField(Markers->GetClass()->FindPropertyByName(TEXT("WidgetComponents"))); + if (!Arr || !CastField(Arr->Inner)) + { + return; + } + FScriptArrayHelper Helper(Arr, Arr->ContainerPtrToValuePtr(Markers)); + + for (int32 Index = Helper.Num() - 1; Index >= 0; --Index) + { + UObject* Entry = *reinterpret_cast(Helper.GetRawPtr(Index)); + if (!IsValid(Entry)) + { + Helper.RemoveValues(Index); + } + } + + if (Helper.Num() > 0) + { + return; + } + + // Desync HideAllMarkers : composants detruits, Vector2Ds encore pleins → AddMarker ne recree rien. + LPMapMarkersGuard::ClearArrayProp(Markers, TEXT("Vector2Ds")); + + TArray Existing; + Markers->GetComponents(Existing); + for (UWidgetComponent* Comp : Existing) + { + if (!IsValid(Comp)) + { + continue; + } + const int32 Slot = Helper.AddValue(); + *reinterpret_cast(Helper.GetRawPtr(Slot)) = Comp; + } + if (Helper.Num() > 0) + { + if (!bLoggedOnce) + { + bLoggedOnce = true; + UE_LOG(LogLifePlay, Display, TEXT("MapMarkers : WidgetComponents reconstitue depuis %d composant(s) (ERR-008)"), Helper.Num()); + } + return; + } + + UWidgetComponent* Focus = NewObject(Markers, TEXT("LPFocusWidgetComponent")); + if (!Focus) + { + return; + } + if (USceneComponent* Root = Markers->GetRootComponent()) + { + Focus->SetupAttachment(Root); + } + Focus->SetHiddenInGame(true); + Focus->SetVisibility(false); + Focus->SetCollisionEnabled(ECollisionEnabled::NoCollision); + Focus->RegisterComponent(); + + const int32 Slot = Helper.AddValue(); + *reinterpret_cast(Helper.GetRawPtr(Slot)) = Focus; + + if (!bLoggedOnce) + { + bLoggedOnce = true; + UE_LOG(LogLifePlay, Display, TEXT("MapMarkers : slot de focus WidgetComponents[0] ajoute (ERR-008, SetInputMode sans crash)")); + } +} diff --git a/Source/LifePlay/LPMapMarkersGuardSubsystem.h b/Source/LifePlay/LPMapMarkersGuardSubsystem.h new file mode 100644 index 0000000..a37bbb4 --- /dev/null +++ b/Source/LifePlay/LPMapMarkersGuardSubsystem.h @@ -0,0 +1,30 @@ +// Garde ERR-008 : GI.FocusOnMapMarkers lit MapMarkers.WidgetComponents[0] sans test de longueur. +// Apres HideAllMarkers (ou un spawn sans amenite), le tableau est vide → spam log + SetInputMode None. +// Ce sous-systeme maintient une entree valide (composant de focus invisible) et vide Vector2Ds +// quand le tableau est vide, pour eviter le desync AddMarker. Aucun Blueprint n'est modifie. + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/Ticker.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "LPMapMarkersGuardSubsystem.generated.h" + +class AActor; + +UCLASS() +class LIFEPLAY_API ULPMapMarkersGuardSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + +private: + bool Tick(float DeltaTime); + void Guard(AActor* Markers); + + FTSTicker::FDelegateHandle TickHandle; + bool bLoggedOnce = false; +};