123 lines
4.3 KiB
C++
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{};
|
|
}
|
|
};
|
|
} |