// Portage UE 5.5 - voir LPRoomTransitionSubsystem.h #include "LPRoomTransitionSubsystem.h" #include "Components/SceneComponent.h" #include "Engine/GameInstance.h" #include "GameFramework/Actor.h" #include "HAL/PlatformTime.h" #include "LifePlay.h" #include "UObject/UnrealType.h" namespace { constexpr double SafetySeconds = 10.0; template T* GetObjectProperty(const UObject* Container, const TCHAR* Name) { const FObjectPropertyBase* Prop = Container ? CastField(Container->GetClass()->FindPropertyByName(Name)) : nullptr; return Prop ? Cast(Prop->GetObjectPropertyValue_InContainer(Container)) : nullptr; } /** Contenu d'une variable tableau d'acteurs de GI (DummyPersonsInUse, PersonsUsedThisScene). */ TArray GetActorArray(const UObject* Container, const TCHAR* Name) { TArray Result; const FArrayProperty* Prop = Container ? CastField(Container->GetClass()->FindPropertyByName(Name)) : nullptr; const FObjectPropertyBase* Inner = Prop ? CastField(Prop->Inner) : nullptr; if (!Inner) { return Result; } FScriptArrayHelper Helper(Prop, Prop->ContainerPtrToValuePtr(Container)); for (int32 i = 0; i < Helper.Num(); ++i) { if (AActor* Actor = Cast(Inner->GetObjectPropertyValue(Helper.GetRawPtr(i)))) { Result.Add(Actor); } } return Result; } /** Valeur texte d'une variable de GI, quel que soit son type (chaine, nom, texte). */ FString GetPropertyText(const UObject* Container, const TCHAR* Name) { FString Text; if (const FProperty* Prop = Container ? Container->GetClass()->FindPropertyByName(Name) : nullptr) { Prop->ExportTextItem_Direct(Text, Prop->ContainerPtrToValuePtr(Container), nullptr, nullptr, PPF_None); } return Text; } } void ULPRoomTransitionSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPRoomTransitionSubsystem::Tick)); } void ULPRoomTransitionSubsystem::Deinitialize() { FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); Super::Deinitialize(); } bool ULPRoomTransitionSubsystem::Tick(float DeltaTime) { const UGameInstance* GI = GetGameInstance(); const AActor* Room = GetObjectProperty(GI, TEXT("Room")); const USceneComponent* Root = IsValid(Room) ? Room->GetRootComponent() : nullptr; if (!Root) { return true; } const TArray InUse = GetActorArray(GI, TEXT("DummyPersonsInUse")); const double Now = FPlatformTime::Seconds(); // 1. Suivi des personnages caches : rendus a la reserve, reaffiches par le jeu, ou delai de securite depasse. for (auto It = Hidden.CreateIterator(); It; ++It) { AActor* Actor = It.Key().Get(); if (!IsValid(Actor) || !InUse.Contains(Actor) || !Actor->IsHidden()) { It.RemoveCurrent(); } else if (Now - It.Value() > SafetySeconds) { Actor->SetActorHiddenInGame(false); UE_LOG(LogLifePlay, Log, TEXT("Changement de lieu : %s toujours actif apres %.0f s, reaffiche"), *Actor->GetName(), SafetySeconds); It.RemoveCurrent(); } } // 2. Changement de piece : cacher les personnages actifs au tick precedent, sauf compagnon et scene en cours. const FString Signature = GetPropertyText(GI, TEXT("CurrentAmenity")) + TEXT("|") + Root->GetRelativeScale3D().ToString(); if (!LastRoomSignature.IsEmpty() && Signature != LastRoomSignature) { const AActor* Companion = GetObjectProperty(GI, TEXT("Companion")); const TArray ScenePersons = GetActorArray(GI, TEXT("PersonsUsedThisScene")); int32 Count = 0; for (const TWeakObjectPtr& Weak : LastInUse) { AActor* Actor = Weak.Get(); if (!IsValid(Actor) || Actor == Companion || ScenePersons.Contains(Actor) || Actor->IsHidden() || !InUse.Contains(Actor)) { continue; } Actor->SetActorHiddenInGame(true); Hidden.Add(Actor, Now); ++Count; } if (Count > 0) { UE_LOG(LogLifePlay, Log, TEXT("Changement de lieu (%s -> %s) : %d personnage(s) de la piece precedente caches"), *LastRoomSignature, *Signature, Count); } } LastRoomSignature = Signature; LastInUse.Reset(InUse.Num()); for (AActor* Actor : InUse) { LastInUse.Add(Actor); } return true; }