Files
PalUIExtension/Api/Pal/PalUtility.hpp
2026-09-07 19:33:58 -04:00

123 lines
4.3 KiB
C++

#pragma once
#include <cstdint>
#include <optional>
#include <unordered_map>
#include <Reflection/UnrealObjectRef.hpp>
#include <Api/UnrealWrapper.hpp>
#include "PalDatabaseCharacterParameter.hpp"
namespace PalUIExtension::Api::Pal
{
class PalUtility : public UnrealWrapper
{
public:
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalUtility"); }
static auto CDOPath() -> const TCHAR* { return STR("/Script/Pal.Default__PalUtility"); }
static auto DebugTag() -> const TCHAR* { return STR("PalUtility"); }
static auto Instance() -> PalUtility&
{
static PalUtility Singleton{};
return Singleton;
}
// ---- static getters -----------------------------------------
//
// Add more here as you need them -- see the class comment for
// the one-line shape.
auto GetDatabaseCharacterParameter(UObject* WorldContextObject) -> PalDatabaseCharacterParameter
{
return GetSingleton<PalDatabaseCharacterParameter>(
STR("GetDatabaseCharacterParameter"), WorldContextObject);
}
auto ZukanIndexFor(FName CharacterId, UObject* WorldContextObject) -> std::optional<int32>
{
const uint64_t Key = NameKey(CharacterId);
if (auto It = s_zukan_cache.find(Key); It != s_zukan_cache.end())
{
if (It->second == kNoZukanEntry) return std::nullopt;
return It->second;
}
PalDatabaseCharacterParameter DB = GetDatabaseCharacterParameter(WorldContextObject);
if (!DB)
{
return std::nullopt;
}
auto Index = DB.GetZukanIndexResolved(CharacterId);
s_zukan_cache.emplace(Key, Index.value_or(kNoZukanEntry));
return Index;
}
static auto ClearZukanCache() -> void { s_zukan_cache.clear(); }
auto NocturnalFor(FName CharacterId, UObject* WorldContextObject) -> std::optional<bool>
{
const uint64_t Key = NameKey(CharacterId);
if (auto It = s_nocturnal_cache.find(Key); It != s_nocturnal_cache.end())
{
return It->second != 0;
}
PalDatabaseCharacterParameter DB = GetDatabaseCharacterParameter(WorldContextObject);
if (!DB) return std::nullopt;
const FName RowName = PalDatabaseCharacterParameter::ResolveBaseRowName(CharacterId);
auto Nocturnal = DB.GetNocturnal(RowName);
if (!Nocturnal.has_value()) return std::nullopt;
s_nocturnal_cache.emplace(Key, *Nocturnal ? uint8_t{ 1 } : uint8_t{ 0 });
return Nocturnal;
}
static auto ClearNocturnalCache() -> void { s_nocturnal_cache.clear(); }
private:
PalUtility() : UnrealWrapper(ResolveCDO(), ClassOf<PalUtility>(), DebugTag()) {}
static constexpr int32 kNoZukanEntry = -1;
static inline std::unordered_map<uint64_t, int32> s_zukan_cache{};
static inline std::unordered_map<uint64_t, uint8_t> s_nocturnal_cache{};
static auto NameKey(FName N) -> uint64_t
{
return (static_cast<uint64_t>(N.GetComparisonIndex().ToUnstableInt()) << 32)
| static_cast<uint64_t>(static_cast<uint32_t>(N.GetNumber()));
}
PalUtility(const PalUtility&) = delete;
auto operator=(const PalUtility&) -> PalUtility & = delete;
static auto ResolveCDO() -> UObject*
{
static Reflection::UnrealObjectRef Ref{ CDOPath(), DebugTag() };
return Ref.Get();
}
auto CallStaticObjectGetter(const TCHAR* FunctionName, UObject* WorldContextObject) -> UObject*
{
struct P { UObject* WorldContextObject; UObject* ReturnValue; } p{ WorldContextObject, nullptr };
auto Result = CallForReturn(FunctionName, p, &P::ReturnValue);
return (Result && *Result) ? *Result : nullptr;
}
template <typename T>
auto GetSingleton(const TCHAR* FunctionName, UObject* WorldContextObject) -> T
{
UObject* Obj = CallStaticObjectGetter(FunctionName, WorldContextObject);
return Obj ? T{ Obj } : T{};
}
};
}