LifePlay-Revival/Source/LifePlay/LPModDailies.cpp
edujiro5972 ad75b08d06 AP5 (restes) : animate() / LPModDailies, commandlet LPPatchDailies, Person, DeliverAnims, mod lpr_anim_quaternius
Phase AP5 (CHG-209).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:49:17 +02:00

174 lines
5.4 KiB
C++

// 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<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;
}
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;
}