Files
ZMMO/Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp
2026-05-23 02:16:27 -03:00

224 lines
5.3 KiB
C++

#include "ZMMOThemeSubsystem.h"
#include "ZMMOThemeDataAsset.h"
#include "Engine/DataTable.h"
#include "Engine/Texture2D.h"
#include "Sound/SoundBase.h"
#include "NiagaraSystem.h"
#include "Misc/DateTime.h"
#include "UI/ThemeCalendarRow.h"
#include "UI/UIStyleRow.h"
namespace
{
/** Row Name da linha de estilo de fallback em DT_UI_Styles. */
const FName GUIStyleDefaultRow(TEXT("Default"));
}
void UZMMOThemeSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
DefaultTheme = DefaultThemeAsset.LoadSynchronous();
if (!DefaultTheme)
{
UE_LOG(LogTemp, Warning,
TEXT("[ZMMOThemeSubsystem] DefaultThemeAsset is not set. UI will have no theme."));
}
ResolveActiveTheme();
// Garantia: se ResolveActiveTheme não disparou ResolveActiveUIStyle (porque
// ActiveTheme não mudou — caso comum quando DefaultTheme é nullptr e nada
// resolveu), força a carga do DT_UI_Styles agora para não deixar
// ActiveUIStyle como FUIStyle() default (brushes vazios → nada renderiza
// em runtime). O fallback interno acha a linha "Default" do DT.
ResolveActiveUIStyle(ActiveThemeId);
}
void UZMMOThemeSubsystem::Deinitialize()
{
ActiveTheme = nullptr;
DefaultTheme = nullptr;
Super::Deinitialize();
}
void UZMMOThemeSubsystem::ApplyServerTheme(FName ThemeId)
{
ServerThemeId = ThemeId;
ResolveActiveTheme();
}
void UZMMOThemeSubsystem::ForceTheme(FName ThemeId)
{
ForcedThemeId = ThemeId;
ResolveActiveTheme();
}
void UZMMOThemeSubsystem::ClearForcedTheme()
{
ForcedThemeId = NAME_None;
ResolveActiveTheme();
}
void UZMMOThemeSubsystem::ResolveActiveTheme()
{
FName Resolved = NAME_None;
if (!ForcedThemeId.IsNone())
{
Resolved = ForcedThemeId;
}
#if !UE_BUILD_SHIPPING
else if (!DevThemeOverride.IsNone())
{
Resolved = DevThemeOverride;
}
#endif
else if (!ServerThemeId.IsNone())
{
Resolved = ServerThemeId;
}
else
{
Resolved = ResolveByCalendar();
}
UZMMOThemeDataAsset* NewActive = !Resolved.IsNone() ? LoadThemeById(Resolved) : nullptr;
if (!NewActive)
{
NewActive = DefaultTheme;
Resolved = DefaultTheme ? DefaultTheme->ThemeId : NAME_None;
}
if (NewActive != ActiveTheme || Resolved != ActiveThemeId)
{
ActiveTheme = NewActive;
ActiveThemeId = Resolved;
ResolveActiveUIStyle(ActiveThemeId);
OnThemeChanged.Broadcast(ActiveThemeId);
}
}
void UZMMOThemeSubsystem::ResolveActiveUIStyle(FName ThemeId)
{
// Reset para os defaults do struct antes de aplicar a linha resolvida —
// assim um tema sem linha cai num estado coerente (paleta Aurora Arcana).
ActiveUIStyle = FUIStyle();
UDataTable* Table = UIStyleTable.LoadSynchronous();
if (!Table)
{
return;
}
const FUIStyleRow* Row = nullptr;
if (!ThemeId.IsNone())
{
Row = Table->FindRow<FUIStyleRow>(ThemeId, TEXT("UIStyle"));
}
if (!Row)
{
// Fallback: linha "Default" (mesma política dos assets, §5).
Row = Table->FindRow<FUIStyleRow>(GUIStyleDefaultRow, TEXT("UIStyle"));
}
if (Row)
{
ActiveUIStyle = Row->Style;
}
}
FName UZMMOThemeSubsystem::ResolveByCalendar() const
{
UDataTable* Table = CalendarTable.LoadSynchronous();
if (!Table)
{
return NAME_None;
}
const FDateTime Now = FDateTime::Now();
const int32 NowMonth = Now.GetMonth();
const int32 NowDay = Now.GetDay();
const int32 NowMD = NowMonth * 100 + NowDay;
FName Best = NAME_None;
int32 BestPriority = MIN_int32;
TArray<FName> RowNames = Table->GetRowNames();
for (const FName& RowName : RowNames)
{
const FThemeCalendarRow* Row = Table->FindRow<FThemeCalendarRow>(RowName, TEXT("ThemeCalendar"));
if (!Row || Row->ThemeId.IsNone())
{
continue;
}
const int32 StartMD = Row->StartMonth * 100 + Row->StartDay;
const int32 EndMD = Row->EndMonth * 100 + Row->EndDay;
const bool bInRange = (StartMD <= EndMD)
? (NowMD >= StartMD && NowMD <= EndMD)
: (NowMD >= StartMD || NowMD <= EndMD); // wraps across year (e.g. 12-15 -> 01-05)
if (bInRange && Row->Priority > BestPriority)
{
Best = Row->ThemeId;
BestPriority = Row->Priority;
}
}
return Best;
}
UZMMOThemeDataAsset* UZMMOThemeSubsystem::LoadThemeById(FName ThemeId) const
{
if (const TSoftObjectPtr<UZMMOThemeDataAsset>* Soft = ThemeRegistry.Find(ThemeId))
{
return Soft->LoadSynchronous();
}
return nullptr;
}
const FThemeEntry* UZMMOThemeSubsystem::FindEntryWithFallback(EZMMOThemeKey Key) const
{
if (ActiveTheme)
{
if (const FThemeEntry* Entry = ActiveTheme->FindEntry(Key))
{
return Entry;
}
}
if (DefaultTheme && DefaultTheme != ActiveTheme)
{
if (const FThemeEntry* Entry = DefaultTheme->FindEntry(Key))
{
return Entry;
}
}
return nullptr;
}
UTexture2D* UZMMOThemeSubsystem::GetTexture(EZMMOThemeKey Key) const
{
const FThemeEntry* Entry = FindEntryWithFallback(Key);
return Entry ? Entry->Texture.LoadSynchronous() : nullptr;
}
USoundBase* UZMMOThemeSubsystem::GetSound(EZMMOThemeKey Key) const
{
const FThemeEntry* Entry = FindEntryWithFallback(Key);
return Entry ? Entry->Sound.LoadSynchronous() : nullptr;
}
UNiagaraSystem* UZMMOThemeSubsystem::GetVFX(EZMMOThemeKey Key) const
{
const FThemeEntry* Entry = FindEntryWithFallback(Key);
return Entry ? Entry->VFX.LoadSynchronous() : nullptr;
}
UObject* UZMMOThemeSubsystem::GetGeneric(EZMMOThemeKey Key) const
{
const FThemeEntry* Entry = FindEntryWithFallback(Key);
return Entry ? Entry->Generic.LoadSynchronous() : nullptr;
}