// Lot AP5 - voir LPModDailies.h #include "LPModDailies.h" #include "Animation/AnimSequence.h" #include "AssetRegistry/AssetRegistryModule.h" #include "Engine/GameInstance.h" #include "GameFramework/Actor.h" #include "Interfaces/IPluginManager.h" #include "LifePlay.h" namespace LPModDailiesImpl { TSet GModKeys; const FName RoomPropertyName(TEXT("Room")); const FName DailiesPropertyName(TEXT("Dailies")); /** _F / _M -> (coupure au dernier '_', comme Person::PlayDailyAnim apres AP5). */ bool KeyOf(const FString& AssetName, FString& Key) { FString Suffix; if (!AssetName.Split(TEXT("_"), &Key, &Suffix, ESearchCase::IgnoreCase, ESearchDir::FromEnd) || Key.IsEmpty()) { return false; } return Suffix.Equals(TEXT("F"), ESearchCase::IgnoreCase) || Suffix.Equals(TEXT("M"), ESearchCase::IgnoreCase); } AActor* RoomOf(UGameInstance* GameInstance) { const FObjectPropertyBase* Prop = GameInstance ? CastField(GameInstance->GetClass()->FindPropertyByName(RoomPropertyName)) : nullptr; return Prop ? Cast(Prop->GetObjectPropertyValue_InContainer(GameInstance)) : nullptr; } } const TSet& ULPModDailiesSubsystem::ModKeys() { return LPModDailiesImpl::GModKeys; } void ULPModDailiesSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); LPModDailiesImpl::GModKeys.Reset(); ModClips.Reset(); IAssetRegistry& Registry = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")).Get(); for (const TSharedRef& Plugin : IPluginManager::Get().GetEnabledPluginsWithContent()) { if (Plugin->GetType() != EPluginType::Mod) { continue; } const FString Root = FString::Printf(TEXT("/%s/Anims"), *Plugin->GetName()); #if WITH_EDITOR // Editeur (-game compris) : le registre explore le contenu en arriere-plan et ne connait pas encore les // clips du mod a ce stade. Jeu empaque : le registre du mod (AssetRegistry.bin) est deja charge. Registry.ScanPathsSynchronous({ Root }, false); #endif TArray Assets; Registry.GetAssetsByPath(FName(*Root), Assets, true); int32 Count = 0; for (const FAssetData& Asset : Assets) { FString Key; if (Asset.AssetClassPath != UAnimSequence::StaticClass()->GetClassPathName() || !LPModDailiesImpl::KeyOf(Asset.AssetName.ToString(), Key)) { continue; } ModClips.Add(TSoftObjectPtr(Asset.GetSoftObjectPath())); LPModDailiesImpl::GModKeys.Add(Key.ToLower()); ++Count; } if (Count > 0) { UE_LOG(LogLifePlay, Log, TEXT("LPModDailies : %s, %d clip(s) sous %s"), *Plugin->GetName(), Count, *Root); } } if (ModClips.Num() == 0) { return; } UE_LOG(LogLifePlay, Log, TEXT("LPModDailies : %d cle(s) de mod : %s"), LPModDailiesImpl::GModKeys.Num(), *FString::Join(LPModDailiesImpl::GModKeys.Array(), TEXT(", "))); TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPModDailiesSubsystem::Tick)); } void ULPModDailiesSubsystem::Deinitialize() { FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); LPModDailiesImpl::GModKeys.Reset(); Super::Deinitialize(); } bool ULPModDailiesSubsystem::Tick(float DeltaTime) { AActor* Room = LPModDailiesImpl::RoomOf(GetGameInstance()); if (!IsValid(Room) || DoneRoom.Get() == Room) { return true; } DoneRoom = Room; const FArrayProperty* Prop = CastField(Room->GetClass()->FindPropertyByName(LPModDailiesImpl::DailiesPropertyName)); const FObjectPropertyBase* Inner = Prop ? CastField(Prop->Inner) : nullptr; if (!Inner) { UE_LOG(LogLifePlay, Error, TEXT("LPModDailies : %s sans tableau Dailies, clips des mods non ajoutes."), *Room->GetName()); return true; } if (Loaded.Num() == 0) { for (const TSoftObjectPtr& Clip : ModClips) { if (UAnimSequence* Anim = Clip.LoadSynchronous()) { Loaded.Add(Anim); } else { UE_LOG(LogLifePlay, Error, TEXT("LPModDailies : %s introuvable."), *Clip.ToString()); } } } FScriptArrayHelper Array(Prop, Prop->ContainerPtrToValuePtr(Room)); TSet Present; TSet BaseKeys; for (int32 Index = 0; Index < Array.Num(); ++Index) { if (const UObject* Existing = Inner->GetObjectPropertyValue(Array.GetRawPtr(Index))) { Present.Add(Existing); FString Key; if (LPModDailiesImpl::KeyOf(Existing->GetName(), Key)) { BaseKeys.Add(Key.ToLower()); } } } int32 Added = 0; for (UAnimSequence* Anim : Loaded) { if (Present.Contains(Anim)) { continue; } FString Key; if (LPModDailiesImpl::KeyOf(Anim->GetName(), Key) && BaseKeys.Contains(Key.ToLower())) { UE_LOG(LogLifePlay, Warning, TEXT("LPModDailies : %s ignore, la cle '%s' existe deja dans le jeu."), *Anim->GetPathName(), *Key); continue; } const int32 Index = Array.AddValue(); Inner->SetObjectPropertyValue(Array.GetRawPtr(Index), Anim); ++Added; } UE_LOG(LogLifePlay, Log, TEXT("LPModDailies : %d clip(s) ajoute(s) a %s.Dailies (%d au total)."), Added, *Room->GetName(), Array.Num()); return true; } bool ULPModDailiesLibrary::IsModDaily(const FString& Key) { // Appele seulement pour une cle absente de l'aiguillage de PlayDaily : rare, journalise pour l'essai en jeu. const bool bMod = LPModDailiesImpl::GModKeys.Contains(Key.ToLower()); UE_LOG(LogLifePlay, Log, TEXT("LPModDailies : PlayDaily(%s) -> %s"), *Key, bMod ? TEXT("cle de mod") : TEXT("cle inconnue")); return bMod; }