#pragma once #include #include #include #include #include #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( STR("GetDatabaseCharacterParameter"), WorldContextObject); } auto ZukanIndexFor(FName CharacterId, UObject* WorldContextObject) -> std::optional { 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 { 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(), DebugTag()) {} static constexpr int32 kNoZukanEntry = -1; static inline std::unordered_map s_zukan_cache{}; static inline std::unordered_map s_nocturnal_cache{}; static auto NameKey(FName N) -> uint64_t { return (static_cast(N.GetComparisonIndex().ToUnstableInt()) << 32) | static_cast(static_cast(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 auto GetSingleton(const TCHAR* FunctionName, UObject* WorldContextObject) -> T { UObject* Obj = CallStaticObjectGetter(FunctionName, WorldContextObject); return Obj ? T{ Obj } : T{}; } }; }