2026-10-07 13:49:17 +00:00
|
|
|
// 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"
|
2026-10-19 20:57:42 +00:00
|
|
|
#include "LPModGuard.h"
|
2026-10-07 13:49:17 +00:00
|
|
|
|
|
|
|
|
namespace LPModDailiesImpl
|
|
|
|
|
{
|
|
|
|
|
TSet<FString> GModKeys;
|
|
|
|
|
|
|
|
|
|
const FName RoomPropertyName(TEXT("Room"));
|
|
|
|
|
const FName DailiesPropertyName(TEXT("Dailies"));
|
|
|
|
|
|
|
|
|
|
/** <cle>_F / <cle>_M -> <cle> (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<FObjectPropertyBase>(GameInstance->GetClass()->FindPropertyByName(RoomPropertyName)) : nullptr;
|
|
|
|
|
return Prop ? Cast<AActor>(Prop->GetObjectPropertyValue_InContainer(GameInstance)) : nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TSet<FString>& ULPModDailiesSubsystem::ModKeys()
|
|
|
|
|
{
|
|
|
|
|
return LPModDailiesImpl::GModKeys;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ULPModDailiesSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
|
|
|
{
|
|
|
|
|
Super::Initialize(Collection);
|
|
|
|
|
LPModDailiesImpl::GModKeys.Reset();
|
|
|
|
|
ModClips.Reset();
|
|
|
|
|
|
|
|
|
|
IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
|
|
|
|
for (const TSharedRef<IPlugin>& Plugin : IPluginManager::Get().GetEnabledPluginsWithContent())
|
|
|
|
|
{
|
|
|
|
|
if (Plugin->GetType() != EPluginType::Mod)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-10-19 20:57:42 +00:00
|
|
|
// Garde des mods (lot U7) : la raison est deja au journal (ligne "Modules : <mod> refuse").
|
|
|
|
|
if (FLPModGuard::IsRefused(Plugin->GetName()))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogLifePlay, Log, TEXT("LPModDailies : %s ignore (mod refuse)"), *Plugin->GetName());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-10-07 13:49:17 +00:00
|
|
|
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<FAssetData> 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<UAnimSequence>(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<FArrayProperty>(Room->GetClass()->FindPropertyByName(LPModDailiesImpl::DailiesPropertyName));
|
|
|
|
|
const FObjectPropertyBase* Inner = Prop ? CastField<FObjectPropertyBase>(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<UAnimSequence>& 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<void>(Room));
|
|
|
|
|
TSet<const UObject*> Present;
|
|
|
|
|
TSet<FString> 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;
|
|
|
|
|
}
|