initial commit; cv 0.0.3
This commit is contained in:
196
Api/BoxPalList.hpp
Normal file
196
Api/BoxPalList.hpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "UmgTypes.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
#include "TextBlock.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ==================================================================
|
||||
// THE PALBOX GRID CHAIN
|
||||
//
|
||||
// PalBoxWidget (the screen)
|
||||
// -> WBP_BoxPalList_C THIS FILE
|
||||
// -> WBP_BoxPalListBase_C THIS FILE
|
||||
// -> WBP_BoxPalScrollList
|
||||
// == WBP_PalCharacterScrollList_C
|
||||
// -> the slot tiles
|
||||
|
||||
// ==================================================================
|
||||
class BoxPalListWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalList.WBP_BoxPalList_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("BoxPalList"); }
|
||||
|
||||
BoxPalListWidget() = default;
|
||||
|
||||
|
||||
explicit BoxPalListWidget(UObject* Obj) : UserWidget(Obj, ClassOf<BoxPalListWidget>(), DebugTag()) {}
|
||||
|
||||
auto BaseObject() const -> UObject* { return BoundObject(STR("WBP_BoxPalListBase")); }
|
||||
|
||||
auto CanDragDrop() const -> std::optional<bool>
|
||||
{
|
||||
return PropertyBoolValue(STR("CanDragDrop"));
|
||||
}
|
||||
auto DisplaysBanMarkInExpedition() const -> std::optional<bool>
|
||||
{
|
||||
return PropertyBoolValue(STR("bDisplayBanMarkInExpedition"));
|
||||
}
|
||||
|
||||
// ---- hooks ---------------------------------------------------
|
||||
|
||||
static auto UpdateSlotFilteringPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalList.WBP_BoxPalList_C:UpdateSlotFiltering");
|
||||
}
|
||||
static auto UpdateAllSlotFilteringPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalList.WBP_BoxPalList_C:UpdateAllSlotFiltering");
|
||||
}
|
||||
static auto SetupPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalList.WBP_BoxPalList_C:Setup");
|
||||
}
|
||||
static auto DestructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalList.WBP_BoxPalList_C:Destruct");
|
||||
}
|
||||
|
||||
struct SlotButtonFrame { UObject* SlotButton; };
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class BoxPalListBaseWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("BoxPalListBase"); }
|
||||
|
||||
BoxPalListBaseWidget() = default;
|
||||
explicit BoxPalListBaseWidget(UObject* Obj)
|
||||
: UserWidget(Obj, ClassOf<BoxPalListBaseWidget>(), DebugTag()) {}
|
||||
|
||||
auto ScrollList() const -> UObject* { return BoundObject(STR("WBP_BoxPalScrollList")); }
|
||||
|
||||
auto LastSelectedPage() const -> std::optional<int32> { return PropertyValue<int32>(STR("LastSelectedPageNum")); }
|
||||
auto MaxPageNum() const -> std::optional<int32> { return PropertyValue<int32>(STR("MaxPageNum")); }
|
||||
auto SkipPageNum() const -> std::optional<int32> { return PropertyValue<int32>(STR("SkipPageNum")); }
|
||||
|
||||
auto IsValidPageAction() const -> std::optional<bool> { return PropertyBoolValue(STR("bIsValidPageAction")); }
|
||||
auto IsSkippingPage() const -> std::optional<bool> { return PropertyBoolValue(STR("bSkipPage")); }
|
||||
|
||||
// ---- chrome, promoted -----------------------------------------
|
||||
auto CanvasSliderValue() const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(STR("Canvas_SliderValue"));
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
|
||||
auto TextBoxName() const -> TextBlock { return WrapText(STR("Text_BoxName")); }
|
||||
auto TextSliderValue() const -> TextBlock { return WrapText(STR("Text_SliderValue")); }
|
||||
|
||||
auto Slider() const -> UObject* { return BoundObject(STR("Slider")); }
|
||||
auto SearchButton() const -> UObject* { return BoundObject(STR("WBP_CommonButton_Activation_Search")); }
|
||||
auto SortButton() const -> UObject* { return BoundObject(STR("WBP_CommonButton_Sort")); }
|
||||
auto NumArrowLeft() const -> UObject* { return BoundObject(STR("WBP_Common_NumArrow_L")); }
|
||||
auto NumArrowRight() const -> UObject* { return BoundObject(STR("WBP_Common_NumArrow_R")); }
|
||||
|
||||
static auto OnCreatedNewSlotPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:OnCreatedNewSlot__DelegateSignature");
|
||||
}
|
||||
static auto OnUpdateSlotPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:OnUpdateSlot__DelegateSignature");
|
||||
}
|
||||
static auto OnSlotSyncedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:OnSlotSynced__DelegateSignature");
|
||||
}
|
||||
static auto OnUpdatedPagePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:OnUpdatedPage__DelegateSignature");
|
||||
}
|
||||
static auto SetCharacterSlotsPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:SetCharacterSlots");
|
||||
}
|
||||
static auto SetCurrentPagePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:SetCurrentPage");
|
||||
}
|
||||
static auto DestructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/Common/"
|
||||
"WBP_BoxPalListBase.WBP_BoxPalListBase_C:Destruct");
|
||||
}
|
||||
|
||||
struct SlotWidgetFrame { UObject* Widget; };
|
||||
|
||||
struct SlotClickFrame
|
||||
{
|
||||
UObject* Widget; // 0x00
|
||||
uint8 PressType; // 0x08
|
||||
};
|
||||
|
||||
struct PageFrame { int32 NowPage; }; // OnUpdatedPage, OnSelectPalBoxPage
|
||||
|
||||
struct SetCharacterSlotsFrame
|
||||
{
|
||||
UObject** Data;
|
||||
int32 Num;
|
||||
int32 Max;
|
||||
};
|
||||
static_assert(sizeof(SetCharacterSlotsFrame) == 16,
|
||||
"SetCharacterSlots frame is 16 bytes on the dumped build");
|
||||
|
||||
auto GetCurrentPage() -> std::optional<int32>
|
||||
{
|
||||
struct Frame { int32 CurrentPageNum; } p{};
|
||||
if (!Call(STR("GetCurrentPage"), p)) return std::nullopt;
|
||||
return p.CurrentPageNum;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
auto WrapText(const TCHAR* Name) const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
protected:
|
||||
BoxPalListBaseWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
0
Api/Canvas.hpp
Normal file
0
Api/Canvas.hpp
Normal file
32
Api/CanvasPanel.hpp
Normal file
32
Api/CanvasPanel.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
#include "CanvasPanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// UCanvasPanel adds exactly one function of its own. Everything else
|
||||
// it offers comes from UPanelWidget and UWidget, which is now inherited
|
||||
// rather than re-declared.
|
||||
class CanvasPanel : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.CanvasPanel"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("Canvas"); }
|
||||
|
||||
CanvasPanel() = default;
|
||||
explicit CanvasPanel(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<CanvasPanel>(), DebugTag()) {}
|
||||
|
||||
// Returns an invalid CanvasPanelSlot if the call failed or the
|
||||
// engine handed back null. Check it before using it -- the wrapper
|
||||
// will refuse to dispatch and log, but silently doing nothing is
|
||||
// not what you want out of a layout call.
|
||||
auto AddChildToCanvas(UObject* Content) -> CanvasPanelSlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChildToCanvas"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? CanvasPanelSlot{ *Result } : CanvasPanelSlot{};
|
||||
}
|
||||
};
|
||||
}
|
||||
91
Api/CanvasPanelSlot.hpp
Normal file
91
Api/CanvasPanelSlot.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class CanvasPanelSlot : public PanelSlot
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.CanvasPanelSlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("CanvasSlot"); }
|
||||
|
||||
CanvasPanelSlot() = default;
|
||||
explicit CanvasPanelSlot(UObject* Obj)
|
||||
: PanelSlot(Obj, ClassOf<CanvasPanelSlot>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetZOrder(int32 InZOrder) -> bool
|
||||
{
|
||||
struct P { int32 InZOrder; } p{ InZOrder };
|
||||
return Call(STR("SetZOrder"), p);
|
||||
}
|
||||
auto SetSize(FVector2D_ InSize) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InSize; } p{ InSize };
|
||||
return Call(STR("SetSize"), p);
|
||||
}
|
||||
auto SetPosition(FVector2D_ InPosition) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InPosition; } p{ InPosition };
|
||||
return Call(STR("SetPosition"), p);
|
||||
}
|
||||
auto SetOffsets(FMargin_ InOffset) -> bool
|
||||
{
|
||||
struct P { FMargin_ InOffset; } p{ InOffset };
|
||||
return Call(STR("SetOffsets"), p);
|
||||
}
|
||||
auto SetMinimum(FVector2D_ InMinimumAnchors) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InMinimumAnchors; } p{ InMinimumAnchors };
|
||||
return Call(STR("SetMinimum"), p);
|
||||
}
|
||||
auto SetMaximum(FVector2D_ InMaximumAnchors) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InMaximumAnchors; } p{ InMaximumAnchors };
|
||||
return Call(STR("SetMaximum"), p);
|
||||
}
|
||||
auto SetAnchors(FAnchors_ InAnchors) -> bool
|
||||
{
|
||||
struct P { FAnchors_ InAnchors; } p{ InAnchors };
|
||||
return Call(STR("SetAnchors"), p);
|
||||
}
|
||||
auto SetAlignment(FVector2D_ InAlignment) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InAlignment; } p{ InAlignment };
|
||||
return Call(STR("SetAlignment"), p);
|
||||
}
|
||||
auto SetAutoSize(bool InbAutoSize) -> bool
|
||||
{
|
||||
struct P { bool InbAutoSize; } p{ InbAutoSize };
|
||||
return Call(STR("SetAutoSize"), p);
|
||||
}
|
||||
// Engine signature is (const FAnchorData&); Blueprint const-ref params
|
||||
// are still laid out by value in the ProcessEvent frame.
|
||||
// FAnchorData_ field order is the guess flagged in UmgTypes.hpp --
|
||||
// prefer the granular setters until you have dumped the struct.
|
||||
auto SetLayout(const FAnchorData_& InLayoutData) -> bool
|
||||
{
|
||||
struct P { FAnchorData_ InLayoutData; } p{ InLayoutData };
|
||||
return Call(STR("SetLayout"), p);
|
||||
}
|
||||
|
||||
// ---- getters --------------------------------------------------------
|
||||
//
|
||||
// ZOrder, bAutoSize and LayoutData are plain UPROPERTYs, so these
|
||||
// three read the memory directly instead of building a ProcessEvent
|
||||
// frame. The rest have no backing property of their own (they are
|
||||
// views into LayoutData) and go through the UFUNCTION.
|
||||
|
||||
auto GetZOrder() const -> std::optional<int32> { return PropertyValue<int32>(STR("ZOrder")); }
|
||||
auto GetAutoSize() const -> std::optional<bool> { return PropertyValue<bool>(STR("bAutoSize")); }
|
||||
auto GetLayout() const -> std::optional<FAnchorData_> { return PropertyValue<FAnchorData_>(STR("LayoutData")); }
|
||||
|
||||
auto GetSize() -> std::optional<FVector2D_> { return CallForReturn<FVector2D_>(STR("GetSize")); }
|
||||
auto GetPosition() -> std::optional<FVector2D_> { return CallForReturn<FVector2D_>(STR("GetPosition")); }
|
||||
auto GetAlignment() -> std::optional<FVector2D_> { return CallForReturn<FVector2D_>(STR("GetAlignment")); }
|
||||
auto GetOffsets() -> std::optional<FMargin_> { return CallForReturn<FMargin_>(STR("GetOffsets")); }
|
||||
auto GetAnchors() -> std::optional<FAnchors_> { return CallForReturn<FAnchors_>(STR("GetAnchors")); }
|
||||
};
|
||||
}
|
||||
29
Api/HorizontalBox.hpp
Normal file
29
Api/HorizontalBox.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
#include "HorizontalBoxSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// UHorizontalBox : public UPanelWidget directly -- same shape as
|
||||
// Overlay. Every generic panel operation is already on PanelWidget;
|
||||
// AddChildToHorizontalBox exists only to hand back the concrete slot
|
||||
// type instead of the generic UPanelSlot* AddChild() returns.
|
||||
class HorizontalBox : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.HorizontalBox"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("HorizontalBox"); }
|
||||
|
||||
HorizontalBox() = default;
|
||||
explicit HorizontalBox(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<HorizontalBox>(), DebugTag()) {}
|
||||
|
||||
auto AddChildToHorizontalBox(UObject* Content) -> HorizontalBoxSlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChildToHorizontalBox"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? HorizontalBoxSlot{ *Result } : HorizontalBoxSlot{};
|
||||
}
|
||||
};
|
||||
}
|
||||
52
Api/HorizontalBoxSlot.hpp
Normal file
52
Api/HorizontalBoxSlot.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// Same four-member surface as VerticalBoxSlot (Size + Padding + H/V
|
||||
// alignment). Different engine class, different ClassPath -- kept
|
||||
// separate rather than shared, matching how CanvasPanelSlot,
|
||||
// SizeBoxSlot, and OverlaySlot each stand alone in this tree despite
|
||||
// overlapping surfaces.
|
||||
class HorizontalBoxSlot : public PanelSlot
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.HorizontalBoxSlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("HorizontalBoxSlot"); }
|
||||
|
||||
HorizontalBoxSlot() = default;
|
||||
explicit HorizontalBoxSlot(UObject* Obj)
|
||||
: PanelSlot(Obj, ClassOf<HorizontalBoxSlot>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetSize(FSlateChildSize_ InSize) -> bool
|
||||
{
|
||||
struct P { FSlateChildSize_ InSize; } p{ InSize };
|
||||
return Call(STR("SetSize"), p);
|
||||
}
|
||||
auto SetPadding(FMargin_ InPadding) -> bool
|
||||
{
|
||||
struct P { FMargin_ InPadding; } p{ InPadding };
|
||||
return Call(STR("SetPadding"), p);
|
||||
}
|
||||
auto SetHorizontalAlignment(EHorizontalAlignment_ InHorizontalAlignment) -> bool
|
||||
{
|
||||
struct P { EHorizontalAlignment_ InHorizontalAlignment; } p{ InHorizontalAlignment };
|
||||
return Call(STR("SetHorizontalAlignment"), p);
|
||||
}
|
||||
auto SetVerticalAlignment(EVerticalAlignment_ InVerticalAlignment) -> bool
|
||||
{
|
||||
struct P { EVerticalAlignment_ InVerticalAlignment; } p{ InVerticalAlignment };
|
||||
return Call(STR("SetVerticalAlignment"), p);
|
||||
}
|
||||
|
||||
// ---- property reads
|
||||
|
||||
auto GetSize() const -> std::optional<FSlateChildSize_> { return PropertyValue<FSlateChildSize_>(STR("Size")); }
|
||||
auto GetPadding() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
|
||||
auto GetHorizontalAlignment() const -> std::optional<EHorizontalAlignment_> { return PropertyValue<EHorizontalAlignment_>(STR("HorizontalAlignment")); }
|
||||
auto GetVerticalAlignment() const -> std::optional<EVerticalAlignment_> { return PropertyValue<EVerticalAlignment_>(STR("VerticalAlignment")); }
|
||||
};
|
||||
}
|
||||
246
Api/MainMenuPalWidget.hpp
Normal file
246
Api/MainMenuPalWidget.hpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "UmgTypes.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
#include "Overlay.hpp"
|
||||
#include "VerticalBox.hpp"
|
||||
#include "TextBlock.hpp"
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class MainMenuPalWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("MainMenuPal"); }
|
||||
|
||||
MainMenuPalWidget() = default;
|
||||
explicit MainMenuPalWidget(UObject* Obj)
|
||||
: UserWidget(Obj, ClassOf<MainMenuPalWidget>(), DebugTag()) {
|
||||
}
|
||||
|
||||
auto CanvasPalName() const -> CanvasPanel { return WrapCanvas(STR("Canvas_PalName")); }
|
||||
auto CanvasPalStatus() const -> CanvasPanel { return WrapCanvas(STR("Canvas_PalStatus")); }
|
||||
auto CanvasSkillInfo() const -> CanvasPanel { return WrapCanvas(STR("Canvas_SkillInfo")); }
|
||||
auto CanvasPartnerSkill() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanel_PartnerSkill")); }
|
||||
auto CanvasPotencial() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanel_Potencial")); } // engine spelling
|
||||
auto CanvasDebuff() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelDebuff")); }
|
||||
auto CanvasAwakening() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Awakening")); }
|
||||
auto CanvasDropGuide() const -> CanvasPanel { return WrapCanvas(STR("Canvas_DropGuide")); }
|
||||
|
||||
auto CanvasLocked() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Locked")); }
|
||||
auto CanvasUnlocked() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Unlocked")); }
|
||||
auto CanvasLock1() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelLock_1")); }
|
||||
auto CanvasLockText() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelLockText")); }
|
||||
auto CanvasLockText1() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelLockText_1")); }
|
||||
|
||||
auto BoxPalList() const -> VerticalBox { return WrapVBox(STR("VerticalBox_Pal_List")); }
|
||||
auto BoxSkill() const -> VerticalBox { return WrapVBox(STR("VerticalBox_Skill")); }
|
||||
|
||||
auto OverlayCircle() const -> Overlay { return WrapOverlay(STR("Overlay_Circle")); }
|
||||
auto OverlayIconCircle() const -> Overlay { return WrapOverlay(STR("Overlay_Icon_Circle")); }
|
||||
auto OverlayIconSquare() const -> Overlay { return WrapOverlay(STR("Overlay_Icon_sq")); }
|
||||
auto OverlaySquare() const -> Overlay { return WrapOverlay(STR("Overlay_sq")); }
|
||||
|
||||
auto GridSuitability() const -> UObject* { return BoundObject(STR("UniformGrid_Suitability")); }
|
||||
|
||||
auto FindCanvas(const TCHAR* Name) -> CanvasPanel { return Child<CanvasPanel>(Name); }
|
||||
|
||||
auto CachedHandle() const -> UObject* { return BoundObject(STR("CachedIndividualHandle")); }
|
||||
|
||||
auto CachedSpecies() const -> std::optional<FName>
|
||||
{
|
||||
return Pal::PalIndividual::CharacterIdFromHandle(CachedHandle());
|
||||
}
|
||||
auto CachedIdentity() const -> Pal::PalIdentity
|
||||
{
|
||||
return Pal::PalIndividual::Identify(CachedHandle());
|
||||
}
|
||||
|
||||
// ---- screen state --------------------------------------------
|
||||
|
||||
auto IsShowingStatusWithList() const -> std::optional<bool> { return PropertyBoolValue(STR("isShowStatusWithList")); }
|
||||
auto IsLocked() const -> std::optional<bool> { return PropertyBoolValue(STR("Is Lock")); }
|
||||
auto IsEditingNickName() const -> std::optional<bool> { return PropertyBoolValue(STR("IsEditingNickName")); }
|
||||
|
||||
// ==============================================================
|
||||
// WIDGET ARRAYS
|
||||
// ==============================================================
|
||||
|
||||
auto PalPanels() const -> std::vector<UObject*> { return ReadObjectArray(STR("PalPanels")); }
|
||||
auto ActiveSkillPanels() const -> std::vector<UObject*> { return ReadObjectArray(STR("ActiveSkillPanelArray")); }
|
||||
auto PassiveSkillPanels() const -> std::vector<UObject*> { return ReadObjectArray(STR("PassiveSkillPanelArray")); }
|
||||
auto ConditionWidgets() const -> std::vector<UObject*> { return ReadObjectArray(STR("ConditionWidgetArray")); }
|
||||
auto RarityWidgets() const -> std::vector<UObject*> { return ReadObjectArray(STR("RarityWidgetArray")); }
|
||||
|
||||
// The engine's spelling of "SideInfo" is "SindeInfo". Lol.
|
||||
auto SideInfoConditionWidgets() const -> std::vector<UObject*> { return ReadObjectArray(STR("SindeInfoConditionWidgetArray")); }
|
||||
|
||||
// ==============================================================
|
||||
// TEXT
|
||||
// ==============================================================
|
||||
auto TextNowHP() const -> TextBlock { return WrapText(STR("Text_NowHP")); }
|
||||
auto TextMaxHP() const -> TextBlock { return WrapText(STR("Text_MaxHP")); }
|
||||
auto TextNowHunger() const -> TextBlock { return WrapText(STR("Text_NowHunger")); }
|
||||
auto TextMaxHunger() const -> TextBlock { return WrapText(STR("Text_MaxHunger")); }
|
||||
auto TextNowSanity() const -> TextBlock { return WrapText(STR("Text_NowSanity")); }
|
||||
auto TextLevel() const -> TextBlock { return WrapText(STR("Text_LevelValue")); }
|
||||
auto TextNextExp() const -> TextBlock { return WrapText(STR("Text_NextExp")); }
|
||||
auto TextDefense() const -> TextBlock { return WrapText(STR("Text_DefenseValue")); }
|
||||
auto TextRangeAttack() const -> TextBlock { return WrapText(STR("Text_RangeAttackValue")); }
|
||||
auto TextWorkSpeed() const -> TextBlock { return WrapText(STR("Text_WorkSpeedValue")); }
|
||||
auto TextFriendship() const -> TextBlock { return WrapText(STR("Text_FriendshipRank_Num")); }
|
||||
auto TextNickName() const -> TextBlock { return WrapText(STR("BP_PalTextBlock_NickName")); }
|
||||
auto TextLvLabel() const -> TextBlock { return WrapText(STR("BP_PalTextBlock_LvText")); }
|
||||
auto TextSoulRank() const -> TextBlock { return WrapText(STR("BP_PalTextBlock_SoulRank")); }
|
||||
|
||||
auto RichTextPartnerSkillName() const -> UObject* { return BoundObject(STR("RichText_PartnerSkillName")); }
|
||||
auto RichTextPartnerSkillDesc() const -> UObject* { return BoundObject(STR("RichText_PartnerSkillDesc")); }
|
||||
|
||||
// ---- gauges, images, nested widgets: raw ---------------------
|
||||
|
||||
auto GaugeHP() const -> UObject* { return BoundObject(STR("Gauge_HP")); }
|
||||
auto GaugeHunger() const -> UObject* { return BoundObject(STR("Gauge_Hunger")); }
|
||||
auto GaugeExp() const -> UObject* { return BoundObject(STR("Gauge_Exp")); }
|
||||
auto GaugeFriendship() const -> UObject* { return BoundObject(STR("Gauge_Friendship")); }
|
||||
|
||||
auto ImageRare() const -> UObject* { return BoundObject(STR("Image_Rare")); }
|
||||
auto ImageStrong() const -> UObject* { return BoundObject(STR("Image_Strong")); }
|
||||
auto ImageMutant() const -> UObject* { return BoundObject(STR("Image_Mutant")); }
|
||||
auto ImageLocked() const -> UObject* { return BoundObject(STR("Image_Icon_Locked")); }
|
||||
auto ImageGlobalImport() const -> UObject* { return BoundObject(STR("Image_Icon_GlobalInport")); } // engine spelling
|
||||
|
||||
auto FoodAmountWidget() const -> UObject* { return BoundObject(STR("WBP_MainMenu_Pal_FoodAmount")); }
|
||||
auto GenderIcon() const -> UObject* { return BoundObject(STR("WBP_PalGenderIcon")); }
|
||||
auto SkillInfoWidget() const -> UObject* { return BoundObject(STR("WBP_MainMenu_PalSkillInfo")); }
|
||||
auto PotencialWidget() const -> UObject* { return BoundObject(STR("WBP_Menu_Pal_Potencial")); } // engine spelling
|
||||
auto NoDataWidget() const -> UObject* { return BoundObject(STR("WBP_NoData")); }
|
||||
auto BuffTimers() const -> UObject* { return BoundObject(STR("WBP_StatusBuffTimerContainer")); }
|
||||
auto KeyGuideIcon() const -> UObject* { return BoundObject(STR("WBP_PalKeyGuideIcon")); }
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
static auto BindFromHandlePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:BindFromHandle");
|
||||
}
|
||||
static auto SetPalHandlesPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:Set Pal Handles");
|
||||
}
|
||||
static auto SetupStatusPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:Setup Status");
|
||||
}
|
||||
static auto ListToStatusPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:ListToStatus");
|
||||
}
|
||||
static auto OnClickedPalButtonPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:OnClickedPalButtonEvent");
|
||||
}
|
||||
static auto OnHoveredPalButtonPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:OnHoveredPalButtonEvent");
|
||||
}
|
||||
// The honest bookends for "this screen exists" and "your cached
|
||||
// pointers are now garbage." If you cache anything off this
|
||||
// class, hook Destruct.
|
||||
static auto ConstructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:Construct");
|
||||
}
|
||||
static auto DestructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/MainMenu/Pal/"
|
||||
"WBP_MainMenu_Pal_00.WBP_MainMenu_Pal_00_C:Destruct");
|
||||
}
|
||||
|
||||
// ---- frames --------------------------------------------------
|
||||
struct BindFromHandleFrame { UObject* targetHandle; };
|
||||
struct HandleFrame { UObject* Handle; };
|
||||
struct SetPalHandlesFrame
|
||||
{
|
||||
UObject** Data;
|
||||
int32 Num;
|
||||
int32 Max;
|
||||
};
|
||||
|
||||
private:
|
||||
auto WrapCanvas(const TCHAR* Name) const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
auto WrapVBox(const TCHAR* Name) const -> VerticalBox
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? VerticalBox{ O } : VerticalBox{};
|
||||
}
|
||||
auto WrapOverlay(const TCHAR* Name) const -> Overlay
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? Overlay{ O } : Overlay{};
|
||||
}
|
||||
auto WrapText(const TCHAR* Name) const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
static constexpr int32 kMaxPlausibleEntries = 1024;
|
||||
|
||||
auto ReadObjectArray(const TCHAR* PropertyName) const -> std::vector<UObject*>
|
||||
{
|
||||
std::vector<UObject*> Out;
|
||||
|
||||
auto* Arr = TryPropertyPtr<TArrayRead_<UObject*>>(PropertyName);
|
||||
if (!Arr) return Out;
|
||||
|
||||
if (!Arr->IsPlausible(kMaxPlausibleEntries))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] '{}' does not look like a live TArray (Num={}, Max={})\n"),
|
||||
m_tag, PropertyName, Arr->Num, Arr->Max);
|
||||
return Out;
|
||||
}
|
||||
|
||||
Out.reserve(static_cast<size_t>(Arr->Num));
|
||||
for (int32 i = 0; i < Arr->Num; ++i)
|
||||
{
|
||||
if (UObject* Entry = Arr->Data[i]) Out.push_back(Entry);
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
protected:
|
||||
MainMenuPalWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {
|
||||
}
|
||||
};
|
||||
}
|
||||
72
Api/Mod/PalDetail_PalIconNightEye.hpp
Normal file
72
Api/Mod/PalDetail_PalIconNightEye.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UmgTypes.hpp>
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Api/Widget.hpp>
|
||||
#include <Api/UserWidget.hpp>
|
||||
#include <Api/WidgetTree.hpp>
|
||||
#include <Api/TextBlock.hpp>
|
||||
#include <Api/CanvasPanel.hpp>
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterHandle.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterParameter.hpp>
|
||||
|
||||
using namespace PalUIExtension::Api::Pal;
|
||||
|
||||
namespace PalUIExtension::Api::Mod
|
||||
{
|
||||
|
||||
class PalDetail_PalIconNightEye : public UserWidget
|
||||
{
|
||||
public:
|
||||
|
||||
constexpr static auto ClassPath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalDetail_PalIconNightEye.PalDetail_PalIconNightEye_C");
|
||||
}
|
||||
|
||||
constexpr static auto WidgetTreePath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalDetail_PalIconNightEye.PalDetail_PalIconNightEye_C:WidgetTree");
|
||||
}
|
||||
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalDetail_PalIconNightEye"); }
|
||||
|
||||
PalDetail_PalIconNightEye() = default;
|
||||
|
||||
explicit PalDetail_PalIconNightEye(UObject* Obj) : UserWidget(Obj, ClassOf<PalDetail_PalIconNightEye>(), DebugTag()) {
|
||||
|
||||
}
|
||||
|
||||
auto GetIconVisibility() -> std::optional<bool>
|
||||
{
|
||||
return CallForReturn<bool>(STR("GetIconVisibility"));
|
||||
}
|
||||
// ---- calls ---------------------------------------------------
|
||||
|
||||
struct SetIconVisibilityFrame { bool visibility; bool localVar1; };
|
||||
|
||||
auto SetIconVisibility(bool newState) -> bool {
|
||||
SetIconVisibilityFrame p{
|
||||
newState,
|
||||
newState
|
||||
};
|
||||
if (!Call(STR("SetIconVisibility"), p)){
|
||||
Output::send<LogLevel::Error>(
|
||||
STR("[SetIconVisibility] Call to method failed.\n"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
PalDetail_PalIconNightEye(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {
|
||||
}
|
||||
};
|
||||
}
|
||||
101
Api/Mod/PalDetail_PalNumber.hpp
Normal file
101
Api/Mod/PalDetail_PalNumber.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UmgTypes.hpp>
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Api/Widget.hpp>
|
||||
#include <Api/UserWidget.hpp>
|
||||
#include <Api/WidgetTree.hpp>
|
||||
#include <Api/TextBlock.hpp>
|
||||
#include <Api/CanvasPanel.hpp>
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterHandle.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterParameter.hpp>
|
||||
|
||||
using namespace PalUIExtension::Api::Pal;
|
||||
|
||||
namespace PalUIExtension::Api::Mod
|
||||
{
|
||||
|
||||
class PalDetail_PalNumberWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
|
||||
static auto ClassPath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalDetail_PalNumber.PalDetail_PalNumber_C");
|
||||
}
|
||||
|
||||
static auto WidgetTreePath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalDetail_PalNumber.PalDetail_PalNumber_C:WidgetTree");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalDetail_PalNumber"); }
|
||||
|
||||
PalDetail_PalNumberWidget() = default;
|
||||
|
||||
explicit PalDetail_PalNumberWidget(UObject* Obj) : UserWidget(Obj, ClassOf<PalDetail_PalNumberWidget>(), DebugTag()) {
|
||||
|
||||
}
|
||||
|
||||
auto GetBindedHandle() -> UObject*
|
||||
{
|
||||
struct P { UObject* returnValue; } p{ nullptr };
|
||||
if (!CallForReturn(STR("GetBindedHandle"), p, &P::returnValue)) return nullptr;
|
||||
return p.returnValue;
|
||||
}
|
||||
|
||||
auto GetBindedName() -> std::optional<FName>
|
||||
{
|
||||
struct P { FName returnValue; } p{ FName{} };
|
||||
if (!CallForReturn(STR("GetBindedName"), p, &P::returnValue)) return std::nullopt;
|
||||
return p.returnValue;
|
||||
}
|
||||
|
||||
auto CurrentHandle() -> PalIndividualCharacterHandle
|
||||
{
|
||||
UObject* H = GetBindedHandle();
|
||||
return H ? PalIndividualCharacterHandle{ H } : PalIndividualCharacterHandle{};
|
||||
}
|
||||
|
||||
|
||||
auto HasBinding() -> bool
|
||||
{
|
||||
return CallForReturn<bool>(STR("GetHasBinding")).value_or(false);
|
||||
}
|
||||
// ---- calls ---------------------------------------------------
|
||||
|
||||
struct SetPalNumberTextFrame { FString text; };
|
||||
|
||||
auto SetPalNumberText(const FString& text) -> bool {
|
||||
SetPalNumberTextFrame p{text};
|
||||
if (!Call(STR("SetPalNumberText"), p)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SetPalBinding(FPalInstanceID _target) -> std::optional<FName>{
|
||||
struct P {
|
||||
FPalInstanceID TargetInstanceId;
|
||||
FName ResolvedName;
|
||||
UObject* GetIndivCharacterParamReturnVal;
|
||||
bool IsValid;
|
||||
FName GetCharacterIdReturnVal;
|
||||
} p{ _target, FName{} };
|
||||
if (!CallForReturn(STR("SetPalBinding"), p, &P::ResolvedName)) return std::nullopt;
|
||||
return p.ResolvedName;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
PalDetail_PalNumberWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {
|
||||
}
|
||||
};
|
||||
}
|
||||
101
Api/Mod/PalStatus_PalNumber.hpp
Normal file
101
Api/Mod/PalStatus_PalNumber.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UmgTypes.hpp>
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Api/Widget.hpp>
|
||||
#include <Api/UserWidget.hpp>
|
||||
#include <Api/WidgetTree.hpp>
|
||||
#include <Api/TextBlock.hpp>
|
||||
#include <Api/CanvasPanel.hpp>
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterHandle.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterParameter.hpp>
|
||||
|
||||
using namespace PalUIExtension::Api::Pal;
|
||||
|
||||
namespace PalUIExtension::Api::Mod
|
||||
{
|
||||
|
||||
class PalStatus_PalNumberWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
|
||||
static auto ClassPath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalStatus_PalNumber.PalStatus_PalNumber_C");
|
||||
}
|
||||
|
||||
static auto WidgetTreePath() -> const TCHAR* {
|
||||
return STR("/Game/Mods/PalUIExtension/PalStatus_PalNumber.PalStatus_PalNumber_C:WidgetTree");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalStatus_PalNumber"); }
|
||||
|
||||
PalStatus_PalNumberWidget() = default;
|
||||
|
||||
explicit PalStatus_PalNumberWidget(UObject* Obj) : UserWidget(Obj, ClassOf<PalStatus_PalNumberWidget>(), DebugTag()) {
|
||||
|
||||
}
|
||||
|
||||
auto GetBindedHandle() -> UObject*
|
||||
{
|
||||
struct P { UObject* returnValue; } p{ nullptr };
|
||||
if (!CallForReturn(STR("GetBindedHandle"), p, &P::returnValue)) return nullptr;
|
||||
return p.returnValue;
|
||||
}
|
||||
|
||||
auto GetBindedName() -> std::optional<FName>
|
||||
{
|
||||
struct P { FName returnValue; } p{ FName{} };
|
||||
if (!CallForReturn(STR("GetBindedName"), p, &P::returnValue)) return std::nullopt;
|
||||
return p.returnValue;
|
||||
}
|
||||
|
||||
auto CurrentHandle() -> PalIndividualCharacterHandle
|
||||
{
|
||||
UObject* H = GetBindedHandle();
|
||||
return H ? PalIndividualCharacterHandle{ H } : PalIndividualCharacterHandle{};
|
||||
}
|
||||
|
||||
|
||||
auto HasBinding() -> bool
|
||||
{
|
||||
return CallForReturn<bool>(STR("GetHasBinding")).value_or(false);
|
||||
}
|
||||
// ---- calls ---------------------------------------------------
|
||||
|
||||
struct SetPalNumberTextFrame { FString text; };
|
||||
|
||||
auto SetPalNumberText(const FString& text) -> bool {
|
||||
SetPalNumberTextFrame p{text};
|
||||
if (!Call(STR("SetPalNumberText"), p)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SetPalBinding(FPalInstanceID _target) -> std::optional<FName>{
|
||||
struct P {
|
||||
FPalInstanceID TargetInstanceId;
|
||||
FName ResolvedName;
|
||||
UObject* GetIndivCharacterParamReturnVal;
|
||||
bool IsValid;
|
||||
FName GetCharacterIdReturnVal;
|
||||
} p{ _target, FName{} };
|
||||
if (!CallForReturn(STR("SetPalBinding"), p, &P::ResolvedName)) return std::nullopt;
|
||||
return p.ResolvedName;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
PalStatus_PalNumberWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {
|
||||
}
|
||||
};
|
||||
}
|
||||
34
Api/Overlay.hpp
Normal file
34
Api/Overlay.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
#include "OverlaySlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// UOverlay : public UPanelWidget directly (confirmed by the header --
|
||||
// no ContentWidget rung to skip here, unlike SizeBox). Every generic
|
||||
// panel operation -- AddChild, GetChildAt, GetAllChildren,
|
||||
// GetChildObjects -- is already on PanelWidget and works unchanged;
|
||||
// AddChildToOverlay below is the one thing Overlay adds beyond that,
|
||||
// and it exists only because the engine returns the concrete
|
||||
// UOverlaySlot* type instead of the generic UPanelSlot* AddChild()
|
||||
// returns, so you get typed access to Padding/Alignment without an
|
||||
// extra cast.
|
||||
class Overlay : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.Overlay"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("Overlay"); }
|
||||
|
||||
Overlay() = default;
|
||||
explicit Overlay(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<Overlay>(), DebugTag()) {}
|
||||
|
||||
auto AddChildToOverlay(UObject* Content) -> OverlaySlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChildToOverlay"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? OverlaySlot{ *Result } : OverlaySlot{};
|
||||
}
|
||||
};
|
||||
}
|
||||
45
Api/OverlaySlot.hpp
Normal file
45
Api/OverlaySlot.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// Same three-member surface as SizeBoxSlot (Padding + H/V alignment).
|
||||
// Different engine class, different ClassPath -- not merged into one
|
||||
// wrapper, matching how CanvasPanelSlot stands alone rather than
|
||||
// sharing with PanelSlot's other concrete subclasses.
|
||||
class OverlaySlot : public PanelSlot
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.OverlaySlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("OverlaySlot"); }
|
||||
|
||||
OverlaySlot() = default;
|
||||
explicit OverlaySlot(UObject* Obj)
|
||||
: PanelSlot(Obj, ClassOf<OverlaySlot>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetPadding(FMargin_ InPadding) -> bool
|
||||
{
|
||||
struct P { FMargin_ InPadding; } p{ InPadding };
|
||||
return Call(STR("SetPadding"), p);
|
||||
}
|
||||
auto SetHorizontalAlignment(EHorizontalAlignment_ InHorizontalAlignment) -> bool
|
||||
{
|
||||
struct P { EHorizontalAlignment_ InHorizontalAlignment; } p{ InHorizontalAlignment };
|
||||
return Call(STR("SetHorizontalAlignment"), p);
|
||||
}
|
||||
auto SetVerticalAlignment(EVerticalAlignment_ InVerticalAlignment) -> bool
|
||||
{
|
||||
struct P { EVerticalAlignment_ InVerticalAlignment; } p{ InVerticalAlignment };
|
||||
return Call(STR("SetVerticalAlignment"), p);
|
||||
}
|
||||
|
||||
// ---- property reads
|
||||
|
||||
auto GetPadding() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
|
||||
auto GetHorizontalAlignment()const -> std::optional<EHorizontalAlignment_> { return PropertyValue<EHorizontalAlignment_>(STR("HorizontalAlignment")); }
|
||||
auto GetVerticalAlignment() const -> std::optional<EVerticalAlignment_> { return PropertyValue<EVerticalAlignment_>(STR("VerticalAlignment")); }
|
||||
};
|
||||
}
|
||||
136
Api/Pal/PalCharacter.hpp
Normal file
136
Api/Pal/PalCharacter.hpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include "PalIndividual.hpp"
|
||||
#include "PalTypes.hpp"
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
|
||||
class PalCharacter : public UnrealWrapper
|
||||
{
|
||||
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalCharacter"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalCharacter"); }
|
||||
|
||||
PalCharacter() = default;
|
||||
explicit PalCharacter(UObject* Obj) : UnrealWrapper(Obj, ClassOf<PalCharacter>(), DebugTag()) {}
|
||||
|
||||
// ---- components ------------------------------------------------
|
||||
|
||||
auto GetCharacterParameterComponent() -> UObject* { return ObjectGetter(STR("GetCharacterParameterComponent")); }
|
||||
auto GetActionComponent() -> UObject* { return ObjectGetter(STR("GetActionComponent")); }
|
||||
auto GetPalCharacterMovementComponent() -> UObject* { return ObjectGetter(STR("GetPalCharacterMovementComponent")); }
|
||||
auto GetMainMesh() -> UObject* { return ObjectGetter(STR("GetMainMesh")); }
|
||||
auto GetCurrentReservedMontage() -> UObject* { return ObjectGetter(STR("GetCurrentReservedMontage")); }
|
||||
|
||||
auto GetOverrideFaceMesh() -> UObject* { return ObjectGetter(STR("GetOverrideFaceMesh")); }
|
||||
auto GetHandAttachMesh() -> UObject* { return ObjectGetter(STR("GetHandAttachMesh")); }
|
||||
|
||||
// ---- identity --------------------------------------------------
|
||||
|
||||
auto GetIndividualParameterObject() -> UObject*
|
||||
{
|
||||
UObject* Comp = GetCharacterParameterComponent();
|
||||
if (!Comp) return nullptr;
|
||||
LooseObject C{ Comp, STR("PalCharacterParameterComponent") };
|
||||
return C.CallForReturn<UObject*>(STR("GetIndividualParameter")).value_or(nullptr);
|
||||
}
|
||||
|
||||
auto Identify() -> PalIdentity
|
||||
{
|
||||
return PalIndividual::IdentifyFromParameter(GetIndividualParameterObject());
|
||||
}
|
||||
|
||||
auto CharacterId() -> std::optional<FName>
|
||||
{
|
||||
return PalIndividual::CharacterId(GetIndividualParameterObject());
|
||||
}
|
||||
|
||||
auto BlueprintClassName() const -> std::optional<StringType>
|
||||
{
|
||||
if (!m_obj) return std::nullopt;
|
||||
UClass* Cls = m_obj->GetClassPrivate();
|
||||
if (!Cls) return std::nullopt;
|
||||
return Cls->GetName();
|
||||
}
|
||||
|
||||
// ---- state queries ---------------------------------------------
|
||||
|
||||
auto IsInitialized() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInitialized")); }
|
||||
auto IsPart() -> std::optional<bool> { return CallForReturn<bool>(STR("IsPart")); }
|
||||
auto IsCooping() -> std::optional<bool> { return CallForReturn<bool>(STR("IsCooping")); }
|
||||
auto IsPreCooping() -> std::optional<bool> { return CallForReturn<bool>(STR("IsPreCooping")); }
|
||||
auto IsLocalHiddenForCutscene() -> std::optional<bool> { return CallForReturn<bool>(STR("IsLocalHiddenForCutscene")); }
|
||||
auto IsUseCustomAutoAimTarget() -> std::optional<bool> { return CallForReturn<bool>(STR("IsUseCustomAutoAimTarget")); }
|
||||
auto IsAllActiveSkillCooldownFinished() -> std::optional<bool> { return CallForReturn<bool>(STR("IsAllActiveSkillCooldownFinished")); }
|
||||
auto GetBattleMode() -> std::optional<bool> { return CallForReturn<bool>(STR("GetBattleMode")); }
|
||||
auto GetTalkMode() -> std::optional<bool> { return CallForReturn<bool>(STR("GetTalkMode")); }
|
||||
auto GetActiveActorFlag() -> std::optional<bool> { return CallForReturn<bool>(STR("GetActiveActorFlag")); }
|
||||
|
||||
auto IsActiveSkillCooldownFinished(EPalWazaID_ WazaID) -> std::optional<bool>
|
||||
{
|
||||
struct P { EPalWazaID_ WazaID; bool ReturnValue; } p{ WazaID, false };
|
||||
return CallForReturn(STR("IsActiveSkillCooldownFinished"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// ---- local-only mutators ---------------------------------------
|
||||
|
||||
auto ForceResetJumpState() -> bool { return CallVoid(STR("ForceResetJumpState")); }
|
||||
auto RequestJump() -> bool { return CallVoid(STR("RequestJump")); }
|
||||
auto ResetTickInterval() -> bool { return CallVoid(STR("ResetTickInterval")); }
|
||||
auto LocalInitialized() -> bool { return CallVoid(STR("LocalInitialized")); }
|
||||
auto RefreshDungeonLightingChannels() -> bool { return CallVoid(STR("RefreshDungeonLightingChannels")); }
|
||||
auto RequestExecuteTickNextFrameForAction() -> bool { return CallVoid(STR("RequestExecuteTickNextFrameForAction")); }
|
||||
auto BroadcastOnCompleteInitializeParameter() -> bool { return CallVoid(STR("BroadcastOnCompleteInitializeParameter")); }
|
||||
|
||||
auto UpdateGroundRayCast(bool bImmediateApply) -> bool
|
||||
{
|
||||
struct P { bool bImmediateApply; } p{ bImmediateApply };
|
||||
return Call(STR("UpdateGroundRayCast"), p);
|
||||
}
|
||||
auto SetVisibleCharacterMesh(bool Active) -> bool
|
||||
{
|
||||
struct P { bool Active; } p{ Active };
|
||||
return Call(STR("SetVisibleCharacterMesh"), p);
|
||||
}
|
||||
auto SetVisibleHandAttachMesh(bool Active) -> bool
|
||||
{
|
||||
struct P { bool Active; } p{ Active };
|
||||
return Call(STR("SetVisibleHandAttachMesh"), p);
|
||||
}
|
||||
auto SetLocalHiddenForCutscene(bool bHide) -> bool
|
||||
{
|
||||
struct P { bool bHide; } p{ bHide };
|
||||
return Call(STR("SetLocalHiddenForCutscene"), p);
|
||||
}
|
||||
auto SetActiveCollisionMovement(bool Active) -> bool
|
||||
{
|
||||
struct P { bool Active; } p{ Active };
|
||||
return Call(STR("SetActiveCollisionMovement"), p);
|
||||
}
|
||||
auto ChangeBattleModeFlag(bool IsBattle) -> bool
|
||||
{
|
||||
struct P { bool IsBattle; } p{ IsBattle };
|
||||
return Call(STR("ChangeBattleModeFlag"), p);
|
||||
}
|
||||
auto SetDisableChangeIntervalByImportance(FName FlagName, bool bIsDisable) -> bool
|
||||
{
|
||||
struct P { FName flagName; bool isDisable; } p{ FlagName, bIsDisable };
|
||||
return Call(STR("SetDisableChangeIntervalByImportance"), p);
|
||||
}
|
||||
|
||||
private:
|
||||
auto ObjectGetter(const TCHAR* FunctionName) -> UObject*
|
||||
{
|
||||
struct P { UObject* ReturnValue; } p{ nullptr };
|
||||
auto Result = CallForReturn(FunctionName, p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
369
Api/Pal/PalDatabaseCharacterParameter.hpp
Normal file
369
Api/Pal/PalDatabaseCharacterParameter.hpp
Normal file
@@ -0,0 +1,369 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Static/LogGate.hpp>
|
||||
#include "PalIndividual.hpp"
|
||||
#include "PalTypes.hpp"
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
|
||||
class PalDatabaseCharacterParameter : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalDatabaseCharacterParameter"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalDatabase"); }
|
||||
|
||||
PalDatabaseCharacterParameter() = default;
|
||||
explicit PalDatabaseCharacterParameter(UObject* Obj) : UnrealWrapper(Obj, ClassOf<PalDatabaseCharacterParameter>(), DebugTag()) {}
|
||||
|
||||
|
||||
static auto StripVariantPrefix(FName CharacterId) -> std::optional<FName>
|
||||
{
|
||||
const StringType Id = CharacterId.ToString();
|
||||
|
||||
static const StringType Prefixes[] = {
|
||||
STR("BOSS_"), STR("PREDATOR_"), STR("RAID_"), STR("GYM_"),
|
||||
};
|
||||
|
||||
for (const StringType& Prefix : Prefixes)
|
||||
{
|
||||
if (Id.size() <= Prefix.size()) continue;
|
||||
if (!StartsWithNoCase(Id, Prefix)) continue;
|
||||
|
||||
return FName(StringViewType(Id).substr(Prefix.size()), FNAME_Add);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto ResolveBaseRowName(FName CharacterId) -> FName
|
||||
{
|
||||
return StripVariantPrefix(CharacterId).value_or(CharacterId);
|
||||
}
|
||||
|
||||
// ---- row lookups by species FName -----------------------------
|
||||
|
||||
auto GetZukanIndex(FName RowName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName RowName; int32 ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetZukanIndex"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetZukanIndexResolved(FName CharacterId) -> std::optional<int32>
|
||||
{
|
||||
if (auto Direct = GetZukanIndex(CharacterId); Direct.has_value() && *Direct > 0)
|
||||
{
|
||||
return Direct;
|
||||
}
|
||||
|
||||
auto Base = StripVariantPrefix(CharacterId);
|
||||
if (!Base.has_value()) return std::nullopt;
|
||||
|
||||
auto Fallback = GetZukanIndex(*Base);
|
||||
if (!Fallback.has_value() || *Fallback <= 0) return std::nullopt;
|
||||
|
||||
if constexpr (Static::kVerboseLogging)
|
||||
{
|
||||
Output::send<LogLevel::Verbose>(
|
||||
STR("[PalDatabase] ZukanIndex: '{}' had none, resolved via base '{}' -> {}\n"),
|
||||
CharacterId.ToString(), Base->ToString(), *Fallback);
|
||||
}
|
||||
|
||||
return Fallback;
|
||||
}
|
||||
|
||||
|
||||
auto GetLocalizedBaseCharacterName(FName CharacterId) -> std::optional<FText>
|
||||
{
|
||||
return GetLocalizedCharacterName(ResolveBaseRowName(CharacterId));
|
||||
}
|
||||
|
||||
auto GetTribe(FName RowName) -> std::optional<EPalTribeID_>
|
||||
{
|
||||
struct P { FName RowName; EPalTribeID_ ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetTribe"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetSize(FName RowName) -> std::optional<EPalSizeType_>
|
||||
{
|
||||
struct P { FName RowName; EPalSizeType_ ReturnValue; } p{ RowName, EPalSizeType_::None };
|
||||
return CallForReturn(STR("GetSize"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetGenusCategory(FName RowName) -> std::optional<EPalGenusCategoryType_>
|
||||
{
|
||||
struct P { FName RowName; EPalGenusCategoryType_ ReturnValue; } p{ RowName, EPalGenusCategoryType_::None };
|
||||
return CallForReturn(STR("GetGenusCategory"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetOrganizationType(FName RowName) -> std::optional<EPalOrganizationType_>
|
||||
{
|
||||
struct P { FName RowName; EPalOrganizationType_ ReturnValue; } p{ RowName, EPalOrganizationType_::None };
|
||||
return CallForReturn(STR("GetOrganizationType"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetWeaponType(FName RowName) -> std::optional<EPalWeaponType_>
|
||||
{
|
||||
struct P { FName RowName; EPalWeaponType_ ReturnValue; } p{ RowName, EPalWeaponType_::None };
|
||||
return CallForReturn(STR("GetWeaponType"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetBattleBGM(FName RowName) -> std::optional<EPalBattleBGMType_>
|
||||
{
|
||||
struct P { FName RowName; EPalBattleBGMType_ ReturnValue; } p{ RowName, EPalBattleBGMType_::None };
|
||||
return CallForReturn(STR("GetBattleBGM"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetPalAwakeningItemElement(FName ItemId) -> std::optional<EPalElementType_>
|
||||
{
|
||||
struct P { FName ItemId; EPalElementType_ ReturnValue; } p{ ItemId, EPalElementType_::None };
|
||||
return CallForReturn(STR("GetPalAwakeningItemElement"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
struct ElementTypePair { EPalElementType_ Primary{}; EPalElementType_ Secondary{}; };
|
||||
// void return, two out-params -- not CallForReturn's shape.
|
||||
auto GetElementType(FName RowName) -> std::optional<ElementTypePair>
|
||||
{
|
||||
struct P { FName RowName; EPalElementType_ Element1; EPalElementType_ Element2; }
|
||||
p{ RowName, EPalElementType_::None, EPalElementType_::None };
|
||||
if (!Call(STR("GetElementType"), p)) return std::nullopt;
|
||||
return ElementTypePair{ p.Element1, p.Element2 };
|
||||
}
|
||||
|
||||
// void return, one out-param.
|
||||
auto GetBestWorkSuitability(FName RowName) -> std::optional<EPalWorkSuitability_>
|
||||
{
|
||||
struct P { FName RowName; EPalWorkSuitability_ BestWorkSuitability; }
|
||||
p{ RowName, EPalWorkSuitability_::None };
|
||||
if (!Call(STR("GetBestWorkSuitability"), p)) return std::nullopt;
|
||||
return p.BestWorkSuitability;
|
||||
}
|
||||
|
||||
auto GetWeaponEquip(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetWeaponEquip"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetUseBossHPGauge(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetUseBossHPGauge"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetNocturnal(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetNocturnal"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsTowerBoss(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsTowerBoss"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsRaidBoss(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsRaidBoss"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsPredatorBoss(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsPredatorBoss"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsPal(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsPal"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsLegend(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsLegend"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetIsBoss(FName RowName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName RowName; bool ReturnValue; } p{ RowName, false };
|
||||
return CallForReturn(STR("GetIsBoss"), p, &P::ReturnValue);
|
||||
}
|
||||
auto IsArenaUnusableItem(FName ItemId) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName ItemId; bool ReturnValue; } p{ ItemId, false };
|
||||
return CallForReturn(STR("IsArenaUnusableItem"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetViewingDistance_cm(FName RowName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName RowName; int32 ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetViewingDistance_cm"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetViewingAngle_Degree(FName RowName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName RowName; int32 ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetViewingAngle_Degree"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetRarity(FName RowName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName RowName; int32 ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetRarity"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetFoodAmount(FName RowName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName RowName; int32 ReturnValue; } p{ RowName, 0 };
|
||||
return CallForReturn(STR("GetFoodAmount"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetPriceRate(FName RowName) -> std::optional<float>
|
||||
{
|
||||
struct P { FName RowName; float ReturnValue; } p{ RowName, 0.f };
|
||||
return CallForReturn(STR("GetPriceRate"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetHearingRate(FName RowName) -> std::optional<float>
|
||||
{
|
||||
struct P { FName RowName; float ReturnValue; } p{ RowName, 0.f };
|
||||
return CallForReturn(STR("GetHearingRate"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetExpRatio(FName RowName) -> std::optional<float>
|
||||
{
|
||||
struct P { FName RowName; float ReturnValue; } p{ RowName, 0.f };
|
||||
return CallForReturn(STR("GetExpRatio"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetCaptureRateCorrect(FName RowName) -> std::optional<float>
|
||||
{
|
||||
struct P { FName RowName; float ReturnValue; } p{ RowName, 0.f };
|
||||
return CallForReturn(STR("GetCaptureRateCorrect"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetBPClassName(FName RowName) -> std::optional<FName>
|
||||
{
|
||||
struct P { FName RowName; FName ReturnValue; } p{ RowName, {} };
|
||||
return CallForReturn(STR("GetBPClassName"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetFirstDefeatRewardItemID(FName CharacterID) -> std::optional<FName>
|
||||
{
|
||||
struct P { FName CharacterID; FName ReturnValue; } p{ CharacterID, {} };
|
||||
return CallForReturn(STR("GetFirstDefeatRewardItemID"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// void return, FName out-param -- not a FString, no ownership question, just an index pair like every other FName here.
|
||||
auto GetPrefixNameMsgID(FName CharacterID) -> std::optional<FName>
|
||||
{
|
||||
struct P { FName CharacterID; FName OutMsgID; } p{ CharacterID, {} };
|
||||
if (!Call(STR("GetPrefixNameMsgID"), p)) return std::nullopt;
|
||||
return p.OutMsgID;
|
||||
}
|
||||
|
||||
auto GetLocalizedCharacterName(FName CharacterID) -> std::optional<FText>
|
||||
{
|
||||
struct P { FName CharacterID; FText OutText; } p{ CharacterID, {} };
|
||||
if (!Call(STR("GetLocalizedCharacterName"), p)) return std::nullopt;
|
||||
return p.OutText;
|
||||
}
|
||||
auto GetLocalizedUniqueNPCName(FName UniqueNPCID) -> std::optional<FText>
|
||||
{
|
||||
struct P { FName UniqueNPCID; FText OutText; } p{ UniqueNPCID, {} };
|
||||
if (!Call(STR("GetLocalizedUniqueNPCName"), p)) return std::nullopt;
|
||||
return p.OutText;
|
||||
}
|
||||
|
||||
// ---- friendship ------------------------------------------------
|
||||
|
||||
auto GetMinFriendshipRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetMinFriendshipRank")); }
|
||||
auto GetMaxFriendshipRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetMaxFriendshipRank")); }
|
||||
auto GetFriendshipRank(int32 FriendshipPoint) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 FriendshipPoint; int32 ReturnValue; } p{ FriendshipPoint, 0 };
|
||||
return CallForReturn(STR("GetFriendshipRank"), p, &P::ReturnValue);
|
||||
}
|
||||
auto CalcFriendshipProgress(int32 FriendshipPoint) -> std::optional<float>
|
||||
{
|
||||
struct P { int32 FriendshipPoint; float ReturnValue; } p{ FriendshipPoint, 0.f };
|
||||
return CallForReturn(STR("CalcFriendshipProgress"), p, &P::ReturnValue);
|
||||
}
|
||||
// bool return + int32& out-param.
|
||||
auto GetFriendshipRequiredPointByRank(int32 FriendshipRank) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 FriendshipRank; int32 OutRequiredPoint; bool ReturnValue; }
|
||||
p{ FriendshipRank, 0, false };
|
||||
if (!Call(STR("GetFriendshipRequiredPointByRank"), p) || !p.ReturnValue) return std::nullopt;
|
||||
return p.OutRequiredPoint;
|
||||
}
|
||||
|
||||
// ---- derived from a live UPalIndividualCharacterParameter* ---------
|
||||
|
||||
auto GetHP(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetHP"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetMeleeAttack(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetMeleeAttack"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetShotAttack(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetShotAttack"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDefense(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetDefense"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetCraftSpeed(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetCraftSpeed"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetSupport(UObject* IndividualParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualParameter; int32 ReturnValue; } p{ IndividualParameter, 0 };
|
||||
return CallForReturn(STR("GetSupport"), p, &P::ReturnValue);
|
||||
}
|
||||
auto HigherLevelOtomoFromTrainer(UObject* IndividualCharacterParameter) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* IndividualCharacterParameter; int32 ReturnValue; } p{ IndividualCharacterParameter, 0 };
|
||||
return CallForReturn(STR("HigherLevelOtomoFromTrainer"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto UpdateApplyDatabaseToIndividualParameter(UObject* IndividualParameter) -> bool
|
||||
{
|
||||
struct P { UObject* IndividualParameter; } p{ IndividualParameter };
|
||||
return Call(STR("UpdateApplyDatabaseToIndividualParameter"), p);
|
||||
}
|
||||
|
||||
// Raw UObject* return -- UPalPettingPresset isn't modeled anywhere
|
||||
auto GetPettingPreset() -> UObject*
|
||||
{
|
||||
struct P { UObject* ReturnValue; } p{ nullptr };
|
||||
auto Result = CallForReturn(STR("GetPettingPreset"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
// WorldContextObject is a plain UObject* at the native signature
|
||||
// level -- the meta=(WorldContext=...) annotation only affects
|
||||
// the Blueprint editor node, not the ProcessEvent frame. Pass
|
||||
// any live UObject with a valid world (a player controller, an
|
||||
// actor, etc.).
|
||||
auto CalcCorrectedLevel(int32 TrainerLevel, int32 TargetCharacterLevel, UObject* WorldContextObject) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 TrainerLevel; int32 TargetCharacterLevel; UObject* WorldContextObject; int32 ReturnValue; }
|
||||
p{ TrainerLevel, TargetCharacterLevel, WorldContextObject, 0 };
|
||||
return CallForReturn(STR("CalcCorrectedLevel"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static auto StartsWithNoCase(const StringType& Str, const StringType& Prefix) -> bool
|
||||
{
|
||||
if (Str.size() < Prefix.size()) return false;
|
||||
for (size_t i = 0; i < Prefix.size(); ++i)
|
||||
{
|
||||
CharType A = Str[i];
|
||||
CharType B = Prefix[i];
|
||||
if (A >= STR('a') && A <= STR('z')) A = static_cast<CharType>(A - STR('a') + STR('A'));
|
||||
if (B >= STR('a') && B <= STR('z')) B = static_cast<CharType>(B - STR('a') + STR('A'));
|
||||
if (A != B) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
301
Api/Pal/PalGameMode.hpp
Normal file
301
Api/Pal/PalGameMode.hpp
Normal file
@@ -0,0 +1,301 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/FString.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
using namespace RC;
|
||||
using namespace RC::Unreal;
|
||||
|
||||
// ==================================================================
|
||||
// EPalGameModeType -- confirmed from the dump, UENUM(BlueprintType)
|
||||
// over uint8, two values. Mirrored here for the same reason
|
||||
// ESlateVisibility_ is: a params struct needs a concrete type of the
|
||||
// right width, and pulling in the game's generated header is not an
|
||||
// option.
|
||||
// ==================================================================
|
||||
enum class EPalGameModeType_ : uint8
|
||||
{
|
||||
Title = 0,
|
||||
InGame = 1,
|
||||
};
|
||||
|
||||
// ==================================================================
|
||||
// APalGameModeBase.
|
||||
//
|
||||
// Two things exist on this class and one of them matters:
|
||||
// GameModeType, an FEnumProperty over uint8. Read it to tell the
|
||||
// title screen apart from a live world without inspecting the map
|
||||
// name.
|
||||
//
|
||||
// ---- THE THING TO INTERNALISE ABOUT GAME MODES -------------------
|
||||
//
|
||||
// A GameMode exists ONLY ON THE AUTHORITY. On a listen host and in
|
||||
// single-player that is the local process, so this class is
|
||||
// reachable. On a client connected to a dedicated server, no
|
||||
// APalGameMode is ever instantiated locally and nothing in this file
|
||||
// will ever resolve to anything.
|
||||
//
|
||||
// That is not a limitation of this wrapper, it is what a game mode
|
||||
// IS, and it decides what you may and may not build on top of it. A
|
||||
// client-side UI mod that depends on a game-mode hook for
|
||||
// correctness is broken for every dedicated-server player. Use these
|
||||
// as a supplement to an engine-level signal, never as the only one.
|
||||
// ==================================================================
|
||||
class PalGameModeBase : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalGameModeBase"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalGameModeBase"); }
|
||||
|
||||
PalGameModeBase() = default;
|
||||
explicit PalGameModeBase(UObject* Obj)
|
||||
: UnrealWrapper(Obj, ClassOf<PalGameModeBase>(), DebugTag()) {}
|
||||
|
||||
// FEnumProperty over uint8 -- GetSize() reports 1, and
|
||||
// EPalGameModeType_ is 1 byte, so the size check in PropertyPtr is
|
||||
// meaningful here rather than vacuous.
|
||||
auto GameModeType() const -> std::optional<EPalGameModeType_>
|
||||
{
|
||||
return PropertyValue<EPalGameModeType_>(STR("GameModeType"));
|
||||
}
|
||||
|
||||
auto IsInGame() const -> bool
|
||||
{
|
||||
const auto T = GameModeType();
|
||||
return T.has_value() && *T == EPalGameModeType_::InGame;
|
||||
}
|
||||
|
||||
auto IsTitle() const -> bool
|
||||
{
|
||||
const auto T = GameModeType();
|
||||
return T.has_value() && *T == EPalGameModeType_::Title;
|
||||
}
|
||||
|
||||
protected:
|
||||
PalGameModeBase(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UnrealWrapper(Obj, Expected, Tag) {}
|
||||
};
|
||||
|
||||
// ==================================================================
|
||||
// APalGameMode -- the in-world game mode.
|
||||
//
|
||||
// ---- WHAT IS WORTH HOOKING HERE, AND WHAT IS NOT -----------------
|
||||
//
|
||||
// RestartGame is the one this tree cares about: it is the game
|
||||
// deciding the current world is finished, fired BEFORE the travel
|
||||
// that destroys it. That ordering is the whole value -- a cache of
|
||||
// widget pointers wants to be dropped while those pointers are still
|
||||
// merely useless, not after they are dangling.
|
||||
//
|
||||
// READ THE CAVEAT BLOCK ON RestartGamePath() BEFORE RELYING ON IT.
|
||||
// It has two failure modes that are invisible in testing if you only
|
||||
// test single-player.
|
||||
//
|
||||
// The session/auth callbacks (OnUpdateSession, OnCompleteAuth,
|
||||
// OnCompleteCreateSession, OnEOSLoginDedicatedServerComplete) are
|
||||
// dedicated-server plumbing. They are wrapped as PATHS ONLY, with
|
||||
// frames, because they are occasionally useful for diagnosing "why
|
||||
// did my server mod not initialise" -- but nothing in a client-side
|
||||
// UI mod should be reading them, and none of them fire in
|
||||
// single-player at all.
|
||||
//
|
||||
// ---- WHY THERE IS NO Current() -----------------------------------
|
||||
//
|
||||
// The obvious convenience -- a static that hands back the live game
|
||||
// mode -- is deliberately absent. Every way to get one without a
|
||||
// UWorld in hand goes through UObjectGlobals::FindObject or
|
||||
// FindObjects, and for a Blueprint-reachable native class that is a
|
||||
// linear walk of the entire GUObjectArray (see the note in
|
||||
// PalBoxNightWorkIndicator on what that cost actually is). A helper
|
||||
// that looks like a property read and is really a million-object scan
|
||||
// is the kind of convenience that ends up on a per-frame path.
|
||||
//
|
||||
// Get the instance from a hook that already has it instead: every
|
||||
// callback below receives it as Ctx.Context, and
|
||||
// Hook::RegisterInitGameStatePostCallback hands over an
|
||||
// AGameModeBase* directly at world start.
|
||||
// ==================================================================
|
||||
class PalGameMode : public PalGameModeBase
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalGameMode"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalGameMode"); }
|
||||
|
||||
PalGameMode() = default;
|
||||
explicit PalGameMode(UObject* Obj)
|
||||
: PalGameModeBase(Obj, ClassOf<PalGameMode>(), DebugTag()) {}
|
||||
|
||||
// ---- properties ------------------------------------------------
|
||||
|
||||
// APlayerStart*. No wrapper for it in this tree, so the raw
|
||||
// pointer is the honest return type.
|
||||
auto CachePlayerStart() const -> UObject*
|
||||
{
|
||||
return TryObjectProperty(STR("CachePlayerStart"));
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
// ==============================================================
|
||||
// RestartGame. READ THIS BEFORE BUILDING ON IT.
|
||||
//
|
||||
// Two failure modes, both silent, both invisible if you only
|
||||
// test single-player:
|
||||
//
|
||||
// 1. AUTHORITY ONLY. No APalGameMode is instantiated on a client
|
||||
// connected to a dedicated server, so this hook never installs
|
||||
// and never fires there. For a client-side UI mod that is the
|
||||
// majority multiplayer case.
|
||||
//
|
||||
// 2. REFLECTION ONLY. This is a native UFUNCTION(BlueprintCallable).
|
||||
// UE4SS hooks UFunction execution, which catches invocations
|
||||
// that go through ProcessEvent -- a Blueprint node, a console
|
||||
// exec, a reflected call. It does NOT catch native C++ code
|
||||
// calling APalGameMode::RestartGame() directly, because that
|
||||
// path never touches the reflection system. Whether Palworld
|
||||
// restarts via Blueprint or via C++ is not answerable from a
|
||||
// header dump; it has to be observed.
|
||||
//
|
||||
// The practical consequence: treat a RestartGame callback as an
|
||||
// OPPORTUNISTIC EARLY WARNING, not as the teardown signal. The
|
||||
// load-bearing one is Hook::RegisterLoadMapPreCallback, which is
|
||||
// engine-level, fires on every map transition regardless of what
|
||||
// triggered it, and works identically on clients and hosts.
|
||||
//
|
||||
// Wiring both is correct and costs nothing: teardown must be
|
||||
// idempotent anyway, since a restart that goes through both paths
|
||||
// will call it twice.
|
||||
// ==============================================================
|
||||
static auto RestartGamePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:RestartGame");
|
||||
}
|
||||
|
||||
static auto InitDedicatedServerPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:InitDedicatedServer");
|
||||
}
|
||||
|
||||
static auto OnServerLobbyUpdatePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:OnServerLobbyUpdate");
|
||||
}
|
||||
|
||||
static auto OnUpdateSessionPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:OnUpdateSession");
|
||||
}
|
||||
|
||||
static auto OnCompleteAuthPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:OnCompleteAuth");
|
||||
}
|
||||
|
||||
static auto OnCompleteCreateSessionPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:OnCompleteCreateSession");
|
||||
}
|
||||
|
||||
static auto OnEOSLoginDedicatedServerCompletePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:OnEOSLoginDedicatedServerComplete");
|
||||
}
|
||||
|
||||
static auto CreateSessionPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:CreateSession");
|
||||
}
|
||||
|
||||
static auto FindPlayerStartWithTagPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameMode:FindPlayerStartWithTag");
|
||||
}
|
||||
|
||||
// ---- frames ---------------------------------------------------
|
||||
//
|
||||
// Leading parameters only, matching the convention in
|
||||
// PalCommonCharacterSlot.hpp: hook frames are NOT size-checked, so
|
||||
// reading past the declared parameters reads engine locals. On a
|
||||
// PRE hook those are uninitialised.
|
||||
//
|
||||
// The FString members are the part to be careful with. A
|
||||
// `const FString&` UFUNCTION parameter still occupies a full
|
||||
// FString in the frame (TArray<TCHAR>: 8-byte data pointer +
|
||||
// int32 Num + int32 Max = 16 bytes), so the layout below is what
|
||||
// the engine writes. It is NOT owned by you -- read it, do not
|
||||
// move from it, do not free it.
|
||||
//
|
||||
// RestartGame takes nothing, which is why there is no frame for
|
||||
// it and why its callback should not call Ctx.GetParams at all.
|
||||
|
||||
struct HttpResponseFrame
|
||||
{
|
||||
FString ResponseBody; // 0x00
|
||||
bool bResponseOK; // 0x10
|
||||
int32 ResponseCode; // 0x14 (after 3 bytes of padding)
|
||||
};
|
||||
|
||||
struct EOSLoginCompleteFrame
|
||||
{
|
||||
UObject* UserInfo; // 0x00 const UPocketpairUserInfo*
|
||||
bool bSuccess; // 0x08
|
||||
FString ErrorStr; // 0x10 (after 7 bytes of padding)
|
||||
};
|
||||
|
||||
struct CreateSessionFrame { FString Address; };
|
||||
struct FindPlayerStartWithTagFrame { FName Tag; UObject* ReturnValue; };
|
||||
|
||||
protected:
|
||||
PalGameMode(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: PalGameModeBase(Obj, Expected, Tag) {}
|
||||
};
|
||||
|
||||
// ==================================================================
|
||||
// APalGameModeLogin -- the title-screen game mode.
|
||||
//
|
||||
// Included because it is the OTHER half of the world-lifecycle
|
||||
// picture and it is easy to forget it exists: going from a save back
|
||||
// to the title is a map transition into THIS game mode, not a
|
||||
// RestartGame on the previous one. If teardown only fires on
|
||||
// APalGameMode, quitting to title leaves every cached pointer alive
|
||||
// and dangling.
|
||||
//
|
||||
// GoToTitle is BlueprintImplementableEvent -- no native body. It
|
||||
// dispatches through the object's class chain like anything else, so
|
||||
// it is hookable, but if no Blueprint subclass overrides it there is
|
||||
// nothing to hook and the registry will defer forever.
|
||||
// ==================================================================
|
||||
class PalGameModeLogin : public PalGameModeBase
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalGameModeLogin"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalGameModeLogin"); }
|
||||
|
||||
PalGameModeLogin() = default;
|
||||
explicit PalGameModeLogin(UObject* Obj)
|
||||
: PalGameModeBase(Obj, ClassOf<PalGameModeLogin>(), DebugTag()) {}
|
||||
|
||||
static auto OnCompletePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameModeLogin:OnComplete");
|
||||
}
|
||||
static auto GoToTitlePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Script/Pal.PalGameModeLogin:GoToTitle");
|
||||
}
|
||||
|
||||
struct OnCompleteFrame { bool bCanPlayMulti; };
|
||||
|
||||
protected:
|
||||
PalGameModeLogin(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: PalGameModeBase(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
127
Api/Pal/PalIndividual.hpp
Normal file
127
Api/Pal/PalIndividual.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api\UmgTypes.hpp>
|
||||
#include <Api\UnrealWrapper.hpp>
|
||||
|
||||
#include "PalTypes.hpp"
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
|
||||
enum class EPalGenderType_ : uint8 { None = 0, Male = 1, Female = 2 };
|
||||
|
||||
enum class EPalElementType_ : uint8
|
||||
{
|
||||
None = 0, Normal = 1, Fire = 2, Water = 3, Leaf = 4,
|
||||
Electricity = 5, Ice = 6, Earth = 7, Dark = 8, Dragon = 9, MAX = 10,
|
||||
};
|
||||
|
||||
enum class EPalWorkSuitability_ : uint8
|
||||
{
|
||||
None = 0, EmitFlame = 1, Watering = 2, Seeding = 3,
|
||||
GenerateElectricity = 4, Handcraft = 5, Collection = 6, Deforest = 7,
|
||||
Mining = 8, OilExtraction = 9, ProductMedicine = 10, Cool = 11,
|
||||
Transport = 12, MonsterFarm = 13, Anyone = 14, MAX = 15,
|
||||
};
|
||||
|
||||
class LooseObject : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
LooseObject() = default;
|
||||
explicit LooseObject(UObject* Obj, const TCHAR* Tag = STR("Loose"))
|
||||
: UnrealWrapper(Obj, UncheckedTag{}, Tag) {
|
||||
}
|
||||
|
||||
using UnrealWrapper::Call;
|
||||
using UnrealWrapper::CallVoid;
|
||||
using UnrealWrapper::CallForReturn;
|
||||
using UnrealWrapper::PropertyPtr;
|
||||
using UnrealWrapper::PropertyValue;
|
||||
};
|
||||
|
||||
// this struct is now redundant. watch it never get removed.
|
||||
|
||||
struct FPalInstanceID {
|
||||
FGuid_ PlayerUID;
|
||||
FGuid_ InstanceID;
|
||||
FString DebugName;
|
||||
};
|
||||
|
||||
struct PalIdentity
|
||||
{
|
||||
UObject* Handle{};
|
||||
UObject* IndividualParameter{};
|
||||
|
||||
std::optional<FName> CharacterId; // the species. "SheepBall", etc.
|
||||
std::optional<int32> Level;
|
||||
std::optional<int32> Rank;
|
||||
std::optional<EPalGenderType_> Gender;
|
||||
std::optional<bool> bRare;
|
||||
|
||||
explicit operator bool() const { return CharacterId.has_value(); }
|
||||
};
|
||||
|
||||
class PalIndividual
|
||||
{
|
||||
public:
|
||||
static auto ParameterFromHandle(UObject* Handle) -> UObject*
|
||||
{
|
||||
if (!Handle) return nullptr;
|
||||
LooseObject H{ Handle, STR("PalIndividualCharacter") };
|
||||
return H.CallForReturn<UObject*>(STR("TryGetIndividualParameter")).value_or(nullptr);
|
||||
}
|
||||
|
||||
static auto CharacterId(UObject* Parameter) -> std::optional<FName>
|
||||
{
|
||||
if (!Parameter) return std::nullopt;
|
||||
LooseObject P{ Parameter, STR("PalIndividualCharacterParameter") };
|
||||
return P.CallForReturn<FName>(STR("GetCharacterID"));
|
||||
}
|
||||
|
||||
static auto CharacterIdFromHandle(UObject* Handle) -> std::optional<FName>
|
||||
{
|
||||
return CharacterId(ParameterFromHandle(Handle));
|
||||
}
|
||||
|
||||
static auto IdentifyFromParameter(UObject* Parameter) -> PalIdentity
|
||||
{
|
||||
PalIdentity Out{};
|
||||
Out.IndividualParameter = Parameter;
|
||||
if (!Parameter) return Out;
|
||||
|
||||
LooseObject P{ Parameter, STR("PalIndividualCharacterParameter") };
|
||||
Out.CharacterId = P.CallForReturn<FName>(STR("GetCharacterID"));
|
||||
Out.Level = P.CallForReturn<int32>(STR("GetLevel"));
|
||||
Out.Rank = P.CallForReturn<int32>(STR("GetRank"));
|
||||
Out.Gender = P.CallForReturn<EPalGenderType_>(STR("GetGenderType"));
|
||||
Out.bRare = P.CallForReturn<bool>(STR("IsRarePal"));
|
||||
return Out;
|
||||
}
|
||||
|
||||
static auto Identify(UObject* Handle) -> PalIdentity
|
||||
{
|
||||
PalIdentity Out = IdentifyFromParameter(ParameterFromHandle(Handle));
|
||||
Out.Handle = Handle;
|
||||
return Out;
|
||||
}
|
||||
|
||||
static auto GetInstanceIds(UObject* Handle) -> std::optional<FPalInstanceID>
|
||||
{
|
||||
if (!Handle) return std::nullopt;
|
||||
// FPalInstanceID
|
||||
LooseObject H{ Handle, STR("PalIndividualCharacterHandle") };
|
||||
auto _Out = H.CallForReturn<FPalInstanceID>(STR("GetIndividualID"));
|
||||
|
||||
if (!_Out.has_value()) return std::nullopt;
|
||||
return FPalInstanceID{ _Out.value().PlayerUID, _Out.value().InstanceID, _Out.value().DebugName };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
56
Api/Pal/PalIndividualCharacterHandle.hpp
Normal file
56
Api/Pal/PalIndividualCharacterHandle.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include "PalIndividual.hpp"
|
||||
#include "PalCharacter.hpp"
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
class PalIndividualCharacterHandle : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalIndividualCharacterHandle"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalIndividualCharacterHandle"); }
|
||||
|
||||
PalIndividualCharacterHandle() = default;
|
||||
explicit PalIndividualCharacterHandle(UObject* Obj) : UnrealWrapper(Obj, ClassOf<PalIndividualCharacterHandle>(), DebugTag()) {}
|
||||
|
||||
auto TryGetIndividualParameterObject() -> UObject*
|
||||
{
|
||||
struct P { UObject* ReturnValue; } p{ nullptr };
|
||||
auto Result = CallForReturn(STR("TryGetIndividualParameter"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
auto TryGetIndividualActorObject() -> UObject*
|
||||
{
|
||||
struct P { UObject* ReturnValue; } p{ nullptr };
|
||||
auto Result = CallForReturn(STR("TryGetIndividualActor"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
auto TryGetIndividualActor() -> PalCharacter
|
||||
{
|
||||
UObject* A = TryGetIndividualActorObject();
|
||||
return A ? PalCharacter{ A } : PalCharacter{};
|
||||
}
|
||||
|
||||
auto TryGetPhantomActor(int32 PhantomId) -> UObject*
|
||||
{
|
||||
struct P { int32 PhantomId; UObject* ReturnValue; } p{ PhantomId, nullptr };
|
||||
auto Result = CallForReturn(STR("TryGetPhantomActor"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
auto GetPhantomIDByActor(UObject* Character) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* Character; int32 ReturnValue; } p{ Character, 0 };
|
||||
return CallForReturn(STR("GetPhantomIDByActor"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetInstanceIds() -> std::optional<FPalInstanceID>
|
||||
{
|
||||
return PalIndividual::GetInstanceIds(Get());
|
||||
}
|
||||
};
|
||||
}
|
||||
480
Api/Pal/PalIndividualCharacterParameter.hpp
Normal file
480
Api/Pal/PalIndividualCharacterParameter.hpp
Normal file
@@ -0,0 +1,480 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include "PalIndividual.hpp"
|
||||
#include "PalTypes.hpp"
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
|
||||
class PalIndividualCharacterParameter : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalIndividualCharacterParameter"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalIndividualCharacterParameter"); }
|
||||
|
||||
PalIndividualCharacterParameter() = default;
|
||||
explicit PalIndividualCharacterParameter(UObject* Obj) : UnrealWrapper(Obj, ClassOf<PalIndividualCharacterParameter>(), DebugTag()) {}
|
||||
|
||||
// ---- identity ---------------------------------------------------
|
||||
|
||||
auto GetCharacterID() -> std::optional<FName> { return CallForReturn<FName>(STR("GetCharacterID")); }
|
||||
auto GetUniqueNPCID() -> std::optional<FName> { return CallForReturn<FName>(STR("GetUniqueNPCID")); }
|
||||
auto GetSkinName() -> std::optional<FName> { return CallForReturn<FName>(STR("GetSkinName")); }
|
||||
|
||||
auto GetGenderType() -> std::optional<EPalGenderType_>
|
||||
{
|
||||
return CallForReturn<EPalGenderType_>(STR("GetGenderType"));
|
||||
}
|
||||
auto GetTribeID() -> std::optional<EPalTribeID_>
|
||||
{
|
||||
return CallForReturn<EPalTribeID_>(STR("GetTribeID"));
|
||||
}
|
||||
auto IsRarePal() -> std::optional<bool> { return CallForReturn<bool>(STR("IsRarePal")); }
|
||||
auto IsFavoritePal() -> std::optional<bool> { return CallForReturn<bool>(STR("IsFavoritePal")); }
|
||||
auto GetFavoriteIndex() -> std::optional<int32> { return CallForReturn<int32>(STR("GetFavoriteIndex")); }
|
||||
auto IsSkinApplied() -> std::optional<bool> { return CallForReturn<bool>(STR("IsSkinApplied")); }
|
||||
auto GetSkinAppliedCharacterId() -> std::optional<FGuid_>
|
||||
{
|
||||
return CallForReturn<FGuid_>(STR("GetSkinAppliedCharacterId"));
|
||||
}
|
||||
|
||||
auto GetPalId() -> std::optional<FPalInstanceID>
|
||||
{
|
||||
struct P { alignas(8) uint8 Frame[48]; } p{}; // FPalInstanceID
|
||||
if (!Call(STR("GetPalId"), p)) return std::nullopt;
|
||||
FPalInstanceID Out{};
|
||||
std::memcpy(&Out, p.Frame, sizeof(Out));
|
||||
return Out;
|
||||
}
|
||||
|
||||
// Nickname getters
|
||||
|
||||
auto GetNickname() -> std::optional<FString>
|
||||
{
|
||||
struct P { FString OutName; } p{};
|
||||
if (!Call(STR("GetNickname"), p)) return std::nullopt;
|
||||
return p.OutName;
|
||||
}
|
||||
auto GetNickNameWithOnlineID() -> std::optional<FString>
|
||||
{
|
||||
struct P { FString OutName; } p{};
|
||||
if (!Call(STR("GetNickNameWithOnlineID"), p)) return std::nullopt;
|
||||
return p.OutName;
|
||||
}
|
||||
auto GetNickNameByCheckBlockedUser() -> std::optional<FString>
|
||||
{
|
||||
struct P { FString OutName; } p{};
|
||||
if (!Call(STR("GetNickNameByCheckBlockedUser"), p)) return std::nullopt;
|
||||
return p.OutName;
|
||||
}
|
||||
|
||||
// ---- level / experience ------------------------------------------
|
||||
|
||||
auto GetLevel() -> std::optional<int32> { return CallForReturn<int32>(STR("GetLevel")); }
|
||||
auto IsLevelMax() -> std::optional<bool> { return CallForReturn<bool>(STR("IsLevelMax")); }
|
||||
auto GetExp() -> std::optional<int64> { return CallForReturn<int64>(STR("GetExp")); }
|
||||
auto GetRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetRank")); }
|
||||
auto GetRankUpExp() -> std::optional<int32> { return CallForReturn<int32>(STR("GetRankUpExp")); }
|
||||
auto GetPalSoulRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetPalSoulRank")); }
|
||||
auto IsOverrideLevel() -> std::optional<bool> { return CallForReturn<bool>(STR("IsOverrideLevel")); }
|
||||
auto GetOverrideLevel() -> std::optional<int32> { return CallForReturn<int32>(STR("GetOverrideLevel")); }
|
||||
|
||||
auto SetOverrideLevel(int32 OverrideLevel) -> bool
|
||||
{
|
||||
struct P { int32 OverrideLevel; } p{ OverrideLevel };
|
||||
return Call(STR("SetOverrideLevel"), p);
|
||||
}
|
||||
|
||||
// ---- HP / shield / death -----------------------------------------
|
||||
|
||||
auto GetHP() -> std::optional<FFixedPoint64_> { return CallForReturn<FFixedPoint64_>(STR("GetHP")); }
|
||||
auto GetMaxHP_withBuff() -> std::optional<FFixedPoint64_> { return CallForReturn<FFixedPoint64_>(STR("GetMaxHP_withBuff")); }
|
||||
auto GetMaxHP() -> std::optional<int32> { return CallForReturn<int32>(STR("GetMaxHP")); }
|
||||
auto GetHPRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetHPRank")); }
|
||||
|
||||
auto GetShieldHP() -> std::optional<FFixedPoint64_> { return CallForReturn<FFixedPoint64_>(STR("GetShieldHP")); }
|
||||
auto GetShieldMaxHP() -> std::optional<FFixedPoint64_> { return CallForReturn<FFixedPoint64_>(STR("GetShieldMaxHP")); }
|
||||
|
||||
auto GetCurrentPhaseHPLimit() -> std::optional<FFixedPoint64_>
|
||||
{
|
||||
struct P { FFixedPoint64_ OutPhaseHPLimit; bool ReturnValue; } p{ {}, false };
|
||||
auto Result = CallForReturn(STR("GetCurrentPhaseHPLimit"), p, &P::ReturnValue);
|
||||
if (!Result || !*Result) return std::nullopt;
|
||||
return p.OutPhaseHPLimit;
|
||||
}
|
||||
auto IsPhaseHPLimitValid() -> std::optional<bool> { return CallForReturn<bool>(STR("IsPhaseHPLimitValid")); }
|
||||
|
||||
auto AddHP(FFixedPoint64_ PlusHP) -> bool
|
||||
{
|
||||
struct P { FFixedPoint64_ PlusHP; } p{ PlusHP };
|
||||
return Call(STR("AddHP"), p);
|
||||
}
|
||||
auto FullRecoveryHP() -> bool { return CallVoid(STR("FullRecoveryHP")); }
|
||||
auto IsHPFullRecovered() -> std::optional<bool> { return CallForReturn<bool>(STR("IsHPFullRecovered")); }
|
||||
|
||||
auto SetShieldHP(FFixedPoint64_ NextShieldHP) -> bool
|
||||
{
|
||||
struct P { FFixedPoint64_ NextSheildHP; } p{ NextShieldHP }; // engine spelling
|
||||
return Call(STR("SetShieldHP"), p);
|
||||
}
|
||||
auto SetShieldMaxHP(FFixedPoint64_ NextShieldMaxHP) -> bool
|
||||
{
|
||||
struct P { FFixedPoint64_ NextSheildMaxHP; } p{ NextShieldMaxHP }; // engine spelling
|
||||
return Call(STR("SetShieldMaxHP"), p);
|
||||
}
|
||||
auto DecreaseShieldHPByDamage(int32 Damage) -> bool
|
||||
{
|
||||
struct P { int32 Damage; } p{ Damage };
|
||||
return Call(STR("DecreaseShieldHPByDamage"), p);
|
||||
}
|
||||
|
||||
auto IsDead() -> std::optional<bool> { return CallForReturn<bool>(STR("IsDead")); }
|
||||
auto GetPhysicalHealth() -> std::optional<EPalStatusPhysicalHealthType_>
|
||||
{
|
||||
return CallForReturn<EPalStatusPhysicalHealthType_>(STR("GetPhysicalHealth"));
|
||||
}
|
||||
auto SetPhysicalHealth(EPalStatusPhysicalHealthType_ PhysicalHealth) -> bool
|
||||
{
|
||||
struct P { EPalStatusPhysicalHealthType_ PhysicalHealth; } p{ PhysicalHealth };
|
||||
return Call(STR("SetPhysicalHealth"), p);
|
||||
}
|
||||
auto IsRespawnReady() -> std::optional<bool> { return CallForReturn<bool>(STR("IsRespawnReady")); }
|
||||
auto GetRespawnTime() -> std::optional<float> { return CallForReturn<float>(STR("GetRespawnTime")); }
|
||||
|
||||
// ---- combat stats -------------------------------------------------
|
||||
|
||||
auto GetMeleeAttack() -> std::optional<int32> { return CallForReturn<int32>(STR("GetMeleeAttack")); }
|
||||
auto GetMeleeAttack_withBuff() -> std::optional<int32> { return CallForReturn<int32>(STR("GetMeleeAttack_withBuff")); }
|
||||
auto GetShotAttack() -> std::optional<int32> { return CallForReturn<int32>(STR("GetShotAttack")); }
|
||||
auto GetShotAttack_withBuff() -> std::optional<int32> { return CallForReturn<int32>(STR("GetShotAttack_withBuff")); }
|
||||
auto GetDefense() -> std::optional<int32> { return CallForReturn<int32>(STR("GetDefense")); }
|
||||
auto GetDefense_withBuff() -> std::optional<int32> { return CallForReturn<int32>(STR("GetDefense_withBuff")); }
|
||||
auto GetAttackRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetAttackRank")); }
|
||||
auto GetDefenceRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetDefenceRank")); }
|
||||
|
||||
auto HasMasteredWaza(EPalWazaID_ WazaID) -> std::optional<bool>
|
||||
{
|
||||
struct P { EPalWazaID_ WazaID; bool ReturnValue; } p{ WazaID, false };
|
||||
return CallForReturn(STR("HasMasteredWaza"), p, &P::ReturnValue);
|
||||
}
|
||||
auto AddEquipWaza(EPalWazaID_ WazaID) -> bool
|
||||
{
|
||||
struct P { EPalWazaID_ WazaID; } p{ WazaID };
|
||||
return Call(STR("AddEquipWaza"), p);
|
||||
}
|
||||
auto RemoveEquipWaza(EPalWazaID_ WazaID) -> bool
|
||||
{
|
||||
struct P { EPalWazaID_ WazaID; } p{ WazaID };
|
||||
return Call(STR("RemoveEquipWaza"), p);
|
||||
}
|
||||
auto ClearEquipWaza() -> bool { return CallVoid(STR("ClearEquipWaza")); }
|
||||
|
||||
// ---- sanity / hunger / stomach ------------------------------------
|
||||
|
||||
auto GetSanityValue() -> std::optional<float> { return CallForReturn<float>(STR("GetSanityValue")); }
|
||||
auto GetMaxSanityValue() -> std::optional<float> { return CallForReturn<float>(STR("GetMaxSanityValue")); }
|
||||
auto GetSanityRate() -> std::optional<float> { return CallForReturn<float>(STR("GetSanityRate")); }
|
||||
auto GetAffectSanityValue() -> std::optional<float> { return CallForReturn<float>(STR("GetAffectSanityValue")); }
|
||||
|
||||
auto GetHungerType() -> std::optional<EPalStatusHungerType_>
|
||||
{
|
||||
return CallForReturn<EPalStatusHungerType_>(STR("GetHungerType"));
|
||||
}
|
||||
auto GetFullStomach() -> std::optional<float> { return CallForReturn<float>(STR("GetFullStomach")); }
|
||||
auto GetMaxFullStomach() -> std::optional<float> { return CallForReturn<float>(STR("GetMaxFullStomach")); }
|
||||
auto GetFullStomachRate() -> std::optional<float> { return CallForReturn<float>(STR("GetFullStomachRate")); }
|
||||
auto GetFullStomachDecreasingRate() -> std::optional<float> { return CallForReturn<float>(STR("GetFullStomachDecreasingRate")); }
|
||||
auto IsFullStomachDecreaseStoppedByFood() -> std::optional<bool>
|
||||
{
|
||||
return CallForReturn<bool>(STR("IsFullStomachDecreaseStoppedByFood"));
|
||||
}
|
||||
|
||||
auto SetFullStomach(float NextValue) -> bool
|
||||
{
|
||||
struct P { float NextValue; } p{ NextValue };
|
||||
return Call(STR("SetFullStomach"), p);
|
||||
}
|
||||
auto SetDecreaseFullStomachRates(FName Name, float Rate) -> bool
|
||||
{
|
||||
struct P { FName Name; float Rate; } p{ Name, Rate };
|
||||
return Call(STR("SetDecreaseFullStomachRates"), p);
|
||||
}
|
||||
auto RemoveDecreaseFullStomachRates(FName Name) -> bool
|
||||
{
|
||||
struct P { FName Name; } p{ Name };
|
||||
return Call(STR("RemoveDecreaseFullStomachRates"), p);
|
||||
}
|
||||
|
||||
auto GetEffectFoodName() -> std::optional<FName> { return CallForReturn<FName>(STR("GetEffectFoodName")); }
|
||||
auto GetEffectFoodName_FullStomachKeep() -> std::optional<FName> { return CallForReturn<FName>(STR("GetEffectFoodName_FullStomachKeep")); }
|
||||
auto GetEffectFoodTimeRate() -> std::optional<float> { return CallForReturn<float>(STR("GetEffectFoodTimeRate")); }
|
||||
auto GetEffectFoodTimeRate_FullStomachKeep() -> std::optional<float> { return CallForReturn<float>(STR("GetEffectFoodTimeRate_FullStomachKeep")); }
|
||||
auto GetFoodStatusRate(EPalFoodStatusEffectType_ EffectType) -> std::optional<float>
|
||||
{
|
||||
struct P { EPalFoodStatusEffectType_ EffectType; float ReturnValue; } p{ EffectType, 0.f };
|
||||
return CallForReturn(STR("GetFoodStatusRate"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// ---- friendship -----------------------------------------------------
|
||||
|
||||
auto GetFriendshipRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetFriendshipRank")); }
|
||||
auto GetFriendshipPoint() -> std::optional<int32> { return CallForReturn<int32>(STR("GetFriendshipPoint")); }
|
||||
auto AddFriendShip(int32 Value, bool bApplyPassiveSkill) -> bool
|
||||
{
|
||||
struct P { int32 Value; bool bApplyPassiveSkill; } p{ Value, bApplyPassiveSkill };
|
||||
return Call(STR("AddFriendShip"), p);
|
||||
}
|
||||
|
||||
// ---- status points --------------------------------------------------
|
||||
|
||||
auto GetUnusedStatusPoint() -> std::optional<int32> { return CallForReturn<int32>(STR("GetUnusedStatusPoint")); }
|
||||
auto DecrementUnusedStatusPoint() -> bool { return CallVoid(STR("DecrementUnusedStatusPoint")); }
|
||||
auto IsStatusPointAllMax() -> std::optional<bool> { return CallForReturn<bool>(STR("IsStatusPointAllMax")); }
|
||||
|
||||
auto GetStatusPoint(FName StatusName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName StatusName; int32 ReturnValue; } p{ StatusName, 0 };
|
||||
return CallForReturn(STR("GetStatusPoint"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetTotalStatusPoint(FName StatusName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName StatusName; int32 ReturnValue; } p{ StatusName, 0 };
|
||||
return CallForReturn(STR("GetTotalStatusPoint"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetExStatusPoint(FName StatusName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName StatusName; int32 ReturnValue; } p{ StatusName, 0 };
|
||||
return CallForReturn(STR("GetExStatusPoint"), p, &P::ReturnValue);
|
||||
}
|
||||
auto SetStatusPoint(FName StatusName, int32 Point) -> bool
|
||||
{
|
||||
struct P { FName StatusName; int32 Point; } p{ StatusName, Point };
|
||||
return Call(STR("SetStatusPoint"), p);
|
||||
}
|
||||
auto SetExStatusPoint(FName StatusName, int32 Point) -> bool
|
||||
{
|
||||
struct P { FName StatusName; int32 Point; } p{ StatusName, Point };
|
||||
return Call(STR("SetExStatusPoint"), p);
|
||||
}
|
||||
// Two outputs (bool return + int32& out), same non-CallForReturn
|
||||
// shape as CheckValidWorkSuitability in PalDetailWidget.hpp.
|
||||
auto IsStatusPointAddable(FName StatusName) -> std::optional<int32>
|
||||
{
|
||||
struct P { FName StatusName; int32 AddablePoint; bool ReturnValue; } p{ StatusName, 0, false };
|
||||
if (!Call(STR("IsStatusPointAddable"), p) || !p.ReturnValue) return std::nullopt;
|
||||
return p.AddablePoint;
|
||||
}
|
||||
|
||||
// ---- work suitability -------------------------------------------------
|
||||
|
||||
auto GetCurrentWorkSuitability() -> std::optional<EPalWorkSuitability_>
|
||||
{
|
||||
return CallForReturn<EPalWorkSuitability_>(STR("GetCurrentWorkSuitability"));
|
||||
}
|
||||
auto GetWorkSpeedRank() -> std::optional<int32> { return CallForReturn<int32>(STR("GetWorkSpeedRank")); }
|
||||
auto GetCraftSpeed() -> std::optional<int32> { return CallForReturn<int32>(STR("GetCraftSpeed")); }
|
||||
auto GetCraftSpeed_withBuff() -> std::optional<int32> { return CallForReturn<int32>(STR("GetCraftSpeed_withBuff")); }
|
||||
auto GetCraftSpeedSickRate() -> std::optional<float> { return CallForReturn<float>(STR("GetCraftSpeedSickRate")); }
|
||||
auto GetBaseCampCraftSpeedBuffRate() -> std::optional<float> { return CallForReturn<float>(STR("GetBaseCampCraftSpeedBuffRate")); }
|
||||
auto GetWorkerSick() -> std::optional<EPalBaseCampWorkerSickType_>
|
||||
{
|
||||
return CallForReturn<EPalBaseCampWorkerSickType_>(STR("GetWorkerSick"));
|
||||
}
|
||||
auto GetBaseCampWorkerEventType() -> std::optional<EPalBaseCampWorkerEventType_>
|
||||
{
|
||||
return CallForReturn<EPalBaseCampWorkerEventType_>(STR("GetBaseCampWorkerEventType"));
|
||||
}
|
||||
|
||||
auto HasWorkSuitability(EPalWorkSuitability_ Suitability) -> std::optional<bool>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ InWorkSuitability; bool ReturnValue; } p{ Suitability, false };
|
||||
return CallForReturn(STR("HasWorkSuitability"), p, &P::ReturnValue);
|
||||
}
|
||||
auto HasWorkSuitabilityRank(EPalWorkSuitability_ Suitability, int32 Rank) -> std::optional<bool>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ InWorkSuitability; int32 SuitabilityRank; bool ReturnValue; }
|
||||
p{ Suitability, Rank, false };
|
||||
return CallForReturn(STR("HasWorkSuitabilityRank"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetWorkSuitabilityRank(EPalWorkSuitability_ Suitability) -> std::optional<int32>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ InWorkSuitability; int32 ReturnValue; } p{ Suitability, 0 };
|
||||
return CallForReturn(STR("GetWorkSuitabilityRank"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetWorkSuitabilityRankWithCharacterRank(EPalWorkSuitability_ Suitability) -> std::optional<int32>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ WorkSuitability; int32 ReturnValue; } p{ Suitability, 0 };
|
||||
return CallForReturn(STR("GetWorkSuitabilityRankWithCharacterRank"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetRankBasedWorkSuitabilityBonus(EPalWorkSuitability_ Suitability) -> std::optional<int32>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ WorkSuitability; int32 ReturnValue; } p{ Suitability, 0 };
|
||||
return CallForReturn(STR("GetRankBasedWorkSuitabilityBonus"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetCraftSpeedByWorkSuitability(EPalWorkSuitability_ Suitability) -> std::optional<int32>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ WorkSuitability; int32 ReturnValue; } p{ Suitability, 0 };
|
||||
return CallForReturn(STR("GetCraftSpeedByWorkSuitability"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetCraftSpeedBuffRate(EPalWorkSuitability_ Suitability) -> std::optional<float>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ WorkSuitability; float ReturnValue; } p{ Suitability, 0.f };
|
||||
return CallForReturn(STR("GetCraftSpeedBuffRate"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetCraftSpeed_withBuff_WorkSuitability(EPalWorkSuitability_ Suitability) -> std::optional<int32>
|
||||
{
|
||||
struct P { EPalWorkSuitability_ Suitability; int32 ReturnValue; } p{ Suitability, 0 };
|
||||
return CallForReturn(STR("GetCraftSpeed_withBuff_WorkSuitability"), p, &P::ReturnValue);
|
||||
}
|
||||
auto SetWorkSuitabilityAddRank(EPalWorkSuitability_ Suitability, int32 AddRank) -> bool
|
||||
{
|
||||
struct P { EPalWorkSuitability_ WorkSuitability; int32 addRank; } p{ Suitability, AddRank };
|
||||
return Call(STR("SetWorkSuitabilityAddRank"), p);
|
||||
}
|
||||
|
||||
// ---- passive skills ---------------------------------------------------
|
||||
//
|
||||
// GetPassiveSkillList (TArray<FName>) is NOT wrapped -- see class
|
||||
// comment. Add/Remove take scalar FName params, no such problem.
|
||||
auto AddPassiveSkill(FName AddSkill, FName OverrideSkill) -> bool
|
||||
{
|
||||
struct P { FName AddSkill; FName OverrideSkill; } p{ AddSkill, OverrideSkill };
|
||||
return Call(STR("AddPassiveSkill"), p);
|
||||
}
|
||||
auto RemovePassiveSkill(FName SkillId) -> bool
|
||||
{
|
||||
struct P { FName SkillId; } p{ SkillId };
|
||||
return Call(STR("RemovePassiveSkill"), p);
|
||||
}
|
||||
auto GetPassiveRateBySkillEffect(EPalPassiveSkillEffectType_ EffectType) -> std::optional<float>
|
||||
{
|
||||
struct P { EPalPassiveSkillEffectType_ EffectType; float ReturnValue; } p{ EffectType, 0.f };
|
||||
return CallForReturn(STR("GetPassiveRateBySkillEffect"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetPassiveRateByEquipment(EPalPassiveSkillEffectType_ EffectType) -> std::optional<float>
|
||||
{
|
||||
struct P { EPalPassiveSkillEffectType_ EffectType; float ReturnValue; } p{ EffectType, 0.f };
|
||||
return CallForReturn(STR("GetPassiveRateByEquipment"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// ---- classification / misc flags ---------------------------------------
|
||||
|
||||
auto GetSizeType() -> std::optional<EPalSizeType_> { return CallForReturn<EPalSizeType_>(STR("GetSizeType")); }
|
||||
auto GetGroupType() -> std::optional<EPalGroupType_> { return CallForReturn<EPalGroupType_>(STR("GetGroupType")); }
|
||||
auto GetGroupId() -> std::optional<FGuid_> { return CallForReturn<FGuid_>(STR("GetGroupId")); }
|
||||
auto GetBaseCampId() -> std::optional<FGuid_> { return CallForReturn<FGuid_>(STR("GetBaseCampId")); }
|
||||
auto GetArenaRank() -> std::optional<EPalArenaRank_> { return CallForReturn<EPalArenaRank_>(STR("GetArenaRank")); }
|
||||
auto GetArenaRankPoint() -> std::optional<int32> { return CallForReturn<int32>(STR("GetArenaRankPoint")); }
|
||||
auto IsInArena() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInArena")); }
|
||||
auto IsInRaidArea() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInRaidArea")); }
|
||||
auto IsUncapturable() -> std::optional<bool> { return CallForReturn<bool>(STR("IsUncapturable")); }
|
||||
auto IsForceCapturable() -> std::optional<bool> { return CallForReturn<bool>(STR("IsForceCapturable")); }
|
||||
auto IsAwakening() -> std::optional<bool> { return CallForReturn<bool>(STR("IsAwakening")); }
|
||||
auto IsPartBroken() -> std::optional<bool> { return CallForReturn<bool>(STR("IsPartBroken")); }
|
||||
auto IsParts() -> std::optional<bool> { return CallForReturn<bool>(STR("IsParts")); }
|
||||
auto GetParentParameterObject() -> UObject*
|
||||
{
|
||||
struct P { UObject* ReturnValue; } p{ nullptr };
|
||||
auto Result = CallForReturn(STR("GetParentParameter"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
auto IsAssignedToExpedition() -> std::optional<bool> { return CallForReturn<bool>(STR("IsAssignedToExpedition")); }
|
||||
auto IsAssignedToExpeditionIn(FGuid_ MapObjectId) -> std::optional<bool>
|
||||
{
|
||||
struct P { FGuid_ MapObjectConcreteInstanceId; bool ReturnValue; } p{ MapObjectId, false };
|
||||
return CallForReturn(STR("IsAssignedToExpeditionIn"), p, &P::ReturnValue);
|
||||
}
|
||||
auto IsExcludedFromTeamMission() -> std::optional<bool> { return CallForReturn<bool>(STR("IsExcludedFromTeamMission")); }
|
||||
auto CanTargetFromAI() -> std::optional<bool> { return CallForReturn<bool>(STR("CanTargetFromAI")); }
|
||||
auto HasGenusCategory(EPalGenusCategoryType_ Category) -> std::optional<bool>
|
||||
{
|
||||
struct P { EPalGenusCategoryType_ Category; bool ReturnValue; } p{ Category, false };
|
||||
return CallForReturn(STR("HasGenusCategory"), p, &P::ReturnValue);
|
||||
}
|
||||
auto CanAddTalentByItem(FName ItemName) -> std::optional<bool>
|
||||
{
|
||||
struct P { FName ItemName; bool ReturnValue; } p{ ItemName, false };
|
||||
return CallForReturn(STR("CanAddTalentByItem"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetNPCWeaponType() -> std::optional<EPalWeaponType_> { return CallForReturn<EPalWeaponType_>(STR("GetNPCWeaponType")); }
|
||||
auto GetEquipItemContainerId() -> std::optional<FPalContainerId_>
|
||||
{
|
||||
return CallForReturn<FPalContainerId_>(STR("GetEquipItemContainerId"));
|
||||
}
|
||||
|
||||
auto SetUncapturable(bool bInUncapturable) -> bool
|
||||
{
|
||||
struct P { bool bInUncapturable; } p{ bInUncapturable };
|
||||
return Call(STR("SetUncapturable"), p);
|
||||
}
|
||||
auto SetForceCapturable(bool bInForceCapturable) -> bool
|
||||
{
|
||||
struct P { bool bInForceCapturable; } p{ bInForceCapturable };
|
||||
return Call(STR("SetForceCapturable"), p);
|
||||
}
|
||||
auto SetInArena(bool InArena) -> bool
|
||||
{
|
||||
struct P { bool InArena; } p{ InArena };
|
||||
return Call(STR("SetInArena"), p);
|
||||
}
|
||||
auto SetInRaidArea(bool InRaidArea) -> bool
|
||||
{
|
||||
struct P { bool InRaidArea; } p{ InRaidArea };
|
||||
return Call(STR("SetInRaidArea"), p);
|
||||
}
|
||||
auto SetCanTargetFromAI(bool bInCanTargetFromAI) -> bool
|
||||
{
|
||||
struct P { bool bInCanTargetFromAI; } p{ bInCanTargetFromAI };
|
||||
return Call(STR("SetCanTargetFromAI"), p);
|
||||
}
|
||||
auto SetSkinName(FName InSkinName) -> bool
|
||||
{
|
||||
struct P { FName InSkinName; } p{ InSkinName };
|
||||
return Call(STR("SetSkinName"), p);
|
||||
}
|
||||
auto SetSkinAppliedCharacterId(FGuid_ InCharacterId) -> bool
|
||||
{
|
||||
struct P { FGuid_ InCharacterId; } p{ InCharacterId };
|
||||
return Call(STR("SetSkinAppliedCharacterId"), p);
|
||||
}
|
||||
auto SetSecurityPoliceTargetPlayerId(FGuid_ PlayerId) -> bool
|
||||
{
|
||||
struct P { FGuid_ PlayerId; } p{ PlayerId };
|
||||
return Call(STR("SetSecurityPoliceTargetPlayerId"), p);
|
||||
}
|
||||
auto SetInvaderData(EPalInvaderType_ InvaderType, FGuid_ InBaseCampId) -> bool
|
||||
{
|
||||
struct P { EPalInvaderType_ InvaderType; FGuid_ InBaseCampId; } p{ InvaderType, InBaseCampId };
|
||||
return Call(STR("SetInvaderData"), p);
|
||||
}
|
||||
auto SetDisableNaturalUpdate(FName Key, bool Disable) -> bool
|
||||
{
|
||||
struct P { FName Key; bool Disable; } p{ Key, Disable };
|
||||
return Call(STR("SetDisableNaturalUpdate"), p);
|
||||
}
|
||||
auto SetDisableNaturalHealing(FName Key, bool Disable) -> bool
|
||||
{
|
||||
struct P { FName Key; bool Disable; } p{ Key, Disable };
|
||||
return Call(STR("SetDisableNaturalHealing"), p);
|
||||
}
|
||||
auto NaturalUpdateSaveParameter(EPalCharacterNaturalUpdateType_ Type) -> bool
|
||||
{
|
||||
struct P { EPalCharacterNaturalUpdateType_ Type; } p{ Type };
|
||||
return Call(STR("NaturalUpdateSaveParameter"), p);
|
||||
}
|
||||
auto SetForcePartBreak() -> bool { return CallVoid(STR("SetForcePartBreak")); }
|
||||
|
||||
// ---- medical bed / misc actions -----------------------------------
|
||||
|
||||
auto StartRecuperatingInMedicalBed() -> bool { return CallVoid(STR("StartRecuperatingInMedicalBed")); }
|
||||
auto RecuperateInMedicalBed() -> bool { return CallVoid(STR("RecuperateInMedicalBed")); }
|
||||
auto EndRecuperatingInMedicalBed() -> bool { return CallVoid(STR("EndRecuperatingInMedicalBed")); }
|
||||
auto StartRemainderOfLifeTimer() -> bool { return CallVoid(STR("StartRemainderOfLifeTimer")); }
|
||||
};
|
||||
}
|
||||
110
Api/Pal/PalTypes.hpp
Normal file
110
Api/Pal/PalTypes.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
|
||||
struct FGuid_ { uint32_t A{}, B{}, C{}, D{}; };
|
||||
|
||||
struct FFixedPoint64_ { int64_t Value{}; };
|
||||
|
||||
struct FPalContainerId_ { FGuid_ ID{}; };
|
||||
|
||||
using EPalTribeID_ = uint16_t;
|
||||
using EPalWazaID_ = uint16_t;
|
||||
using EPalPassiveSkillEffectType_ = uint8_t;
|
||||
|
||||
enum class EPalStatusPhysicalHealthType_ : uint8_t
|
||||
{
|
||||
Healthful = 0, MinorInjury = 1, Severe = 2,
|
||||
Dying = 3, DeadBody = 4, CloudCemetery = 5,
|
||||
};
|
||||
|
||||
enum class EPalGroupType_ : uint8_t
|
||||
{
|
||||
Undefined = 0, Neutral = 1, Organization = 2, IndependentGuild = 3,
|
||||
Guild = 4, ResidentEnemy = 5, RaidBoss = 6,
|
||||
};
|
||||
|
||||
enum class EPalArenaRank_ : uint8_t
|
||||
{
|
||||
Bronze = 0, Silver = 1, Gold = 2, Platinum = 3,
|
||||
Diamond = 4, Master = 5, Legend = 6,
|
||||
};
|
||||
|
||||
enum class EPalGenusCategoryType_ : uint8_t
|
||||
{
|
||||
None = 0, Humanoid = 1, FourLegged = 2, Dragon = 3,
|
||||
Fish = 4, Bird = 5, Monster = 6, Other = 7,
|
||||
};
|
||||
|
||||
enum class EPalExpCalcType_ : uint8_t { None = 0, Build = 1, Craft = 2 };
|
||||
|
||||
enum class EPalSizeType_ : uint8_t
|
||||
{
|
||||
None = 0, XS = 1, S = 2, M = 3, L = 4, XL = 5,
|
||||
};
|
||||
|
||||
enum class EPalStatusHungerType_ : uint8_t
|
||||
{
|
||||
Default = 0, Hunger = 1, Starvation = 2,
|
||||
};
|
||||
|
||||
enum class EPalWeaponType_ : uint8_t
|
||||
{
|
||||
None = 0, ThrowObject = 1, Handgun = 2, AssaultRifle = 3, Shotgun = 4,
|
||||
SniperRifle = 5, RocketLauncher = 6, MeleeWeapon = 7, Bow = 8,
|
||||
BowGun = 9, FlameThrower = 10, GatlingGun = 11, Liftup = 12,
|
||||
LaserRifle = 13, MissileLauncher = 14, GrenadeLauncher = 15,
|
||||
Katana = 16, MetalDetector = 17, GiantClub = 18, FishingRod = 19,
|
||||
LaserMiningTool = 20, MAX = 21,
|
||||
};
|
||||
|
||||
enum class EPalBaseCampWorkerEventType_ : uint8_t
|
||||
{
|
||||
None = 0, Escape = 1, OverworkDeath = 2, Sick = 3, DodgeWork = 4,
|
||||
DodgeWork_Short = 5, DodgeWork_Sleep = 6, EatTooMuch = 7,
|
||||
Trantrum = 8, FightWithFriend = 9, TurnFoodBox = 10, DestroyBuilding = 11,
|
||||
};
|
||||
|
||||
enum class EPalBaseCampWorkerSickType_ : uint8_t
|
||||
{
|
||||
None = 0, Cold = 1, Sprain = 2, Bulimia = 3, GastricUlcer = 4,
|
||||
Fracture = 5, Weakness = 6, DepressionSprain = 7, DisturbingElement = 8,
|
||||
};
|
||||
|
||||
enum class EPalInvaderType_ : uint8_t { None = 0, InvaderEnemy = 1, VisitorNPC = 2 };
|
||||
|
||||
enum class EPalFoodStatusEffectType_ : uint8_t
|
||||
{
|
||||
None = 0, Attack = 1, Defense = 2, HungerResist = 3, SANResist = 4,
|
||||
WorkSpeed = 5, Regene_Hp = 6, MaxSP = 7, Exp_Increase = 8,
|
||||
LeanBackInvalid = 9, KnockbackInvalid = 10, ExplosionResist = 11,
|
||||
LeanBackAndKnockbackInvalid = 12, FullStomachKeep = 13,
|
||||
};
|
||||
|
||||
enum class EPalCharacterNaturalUpdateType_ : uint8_t
|
||||
{
|
||||
Player = 0, OtomoPal = 1, BaseCampPal = 2,
|
||||
};
|
||||
|
||||
enum class EPalOrganizationType_ : uint8_t
|
||||
{
|
||||
None = 0, Guild = 1, Police = 2, City = 3, Brigade = 4,
|
||||
TeamBlackHunter = 5, TeamFireCult = 6, TeamWelfareGroup = 7,
|
||||
Ally = 8, MAX = 9,
|
||||
};
|
||||
|
||||
enum class EPalBattleBGMType_ : uint8_t
|
||||
{
|
||||
None = 0, Cute = 1, Cool = 2, Strong = 3, Human_Common = 4,
|
||||
Human_Villain = 5, FieldBoss_Tier_1 = 6, FieldBoss_Tier_2 = 7,
|
||||
FieldBoss_Tier_3 = 8, FieldBoss_Tier_4 = 9, FieldBoss_Tier_5 = 10,
|
||||
Predator = 11, EyeofCthulhu = 12, WildlifeSanctuaryDrone = 13,
|
||||
Machine = 14, Legend = 15, KingWhale = 16, RaidBoss = 17,
|
||||
TowerBoss = 18, RaidBoss_KingBahamut = 19, RaidBoss_DarkMechaDragon = 20,
|
||||
RaidBoss_MoonLord = 21, RaidBoss_LegendDeer = 22, RaidBoss_LegendDeer2 = 23,
|
||||
WorldTreeDragon = 24,
|
||||
};
|
||||
}
|
||||
155
Api/Pal/PalUIStatusModel.hpp
Normal file
155
Api/Pal/PalUIStatusModel.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include "PalIndividual.hpp" // EPalElementType_
|
||||
|
||||
namespace PalUIExtension::Api::Pal
|
||||
{
|
||||
class PalUIStatusModel : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/Pal.PalUIStatusModel"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalUIStatusModel"); }
|
||||
|
||||
PalUIStatusModel() = default;
|
||||
explicit PalUIStatusModel(UObject* Obj) : UnrealWrapper(Obj, ClassOf<PalUIStatusModel>(), DebugTag()) {}
|
||||
|
||||
|
||||
// Count of pals currently loaded into the model -- "pal number".
|
||||
auto GetDisplayPalNum() -> std::optional<uint8>
|
||||
{
|
||||
return CallForReturn<uint8>(STR("GetDisplayPalNum"));
|
||||
}
|
||||
|
||||
auto GetDisplayPalHandle(uint8 Index) -> UObject*
|
||||
{
|
||||
struct P { uint8 Index; UObject* ReturnValue; } p{ Index, nullptr };
|
||||
auto Result = CallForReturn(STR("GetDisplayPalHandle"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
auto GetNowSelectedIndex() -> std::optional<uint8>
|
||||
{
|
||||
return CallForReturn<uint8>(STR("GetNowSelectedIndex"));
|
||||
}
|
||||
|
||||
auto ChangeIndex(uint8 Index) -> bool
|
||||
{
|
||||
struct P { uint8 Index; } p{ Index };
|
||||
return Call(STR("ChangeIndex"), p);
|
||||
}
|
||||
|
||||
auto OnClickedPalIcon(int32 Index) -> bool
|
||||
{
|
||||
struct P { int32 Index; } p{ Index };
|
||||
return Call(STR("OnClickedPalIcon"), p);
|
||||
}
|
||||
|
||||
// ---- extended: mechanical Index -> stat getters ----------------
|
||||
|
||||
auto GetDisplayLevel(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayLevel"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplaySpeed(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplaySpeed"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayMeleeAttack(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayMeleeAttack"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayShotAttack(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayShotAttack"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayDefence(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayDefence"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayEatingHabits(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayEatingHabits"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayNowExp(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayNowExp"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayNextExp(int32 Index) -> std::optional<int32>
|
||||
{
|
||||
struct P { int32 Index; int32 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayNextExp"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayHunger(int32 Index) -> std::optional<float>
|
||||
{
|
||||
struct P { int32 Index; float ReturnValue; } p{ Index, 0.f };
|
||||
return CallForReturn(STR("GetDisplayHunger"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayMaxHunger(int32 Index) -> std::optional<float>
|
||||
{
|
||||
struct P { int32 Index; float ReturnValue; } p{ Index, 0.f };
|
||||
return CallForReturn(STR("GetDisplayMaxHunger"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetDisplayElementType1(int32 Index) -> std::optional<EPalElementType_>
|
||||
{
|
||||
struct P { int32 Index; EPalElementType_ ReturnValue; } p{ Index, EPalElementType_::None };
|
||||
return CallForReturn(STR("GetDisplayElementType1"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayElementType2(int32 Index) -> std::optional<EPalElementType_>
|
||||
{
|
||||
struct P { int32 Index; EPalElementType_ ReturnValue; } p{ Index, EPalElementType_::None };
|
||||
return CallForReturn(STR("GetDisplayElementType2"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// Raw byte -- see class comment. Do not assume ordering/meaning.
|
||||
auto GetDisplayPhysicalHealthTypeRaw(int32 Index) -> std::optional<uint8>
|
||||
{
|
||||
struct P { int32 Index; uint8 ReturnValue; } p{ Index, 0 };
|
||||
return CallForReturn(STR("GetDisplayPhysicalHealthType"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// ---- extended: FText / FString getters -------------------------
|
||||
|
||||
auto GetDisplayDefaultName(int32 Index) -> std::optional<FText>
|
||||
{
|
||||
struct P { int32 Index; FText ReturnValue; } p{ Index, {} };
|
||||
return CallForReturn(STR("GetDisplayDefaultName"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayCoopActionName(int32 Index) -> std::optional<FText>
|
||||
{
|
||||
struct P { int32 Index; FText ReturnValue; } p{ Index, {} };
|
||||
return CallForReturn(STR("GetDisplayCoopActionName"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetDisplayActiveSkillName(int32 Index, int32 SkillIndex) -> std::optional<FText>
|
||||
{
|
||||
struct P { int32 Index; int32 SkillIndex; FText ReturnValue; } p{ Index, SkillIndex, {} };
|
||||
return CallForReturn(STR("GetDisplayActiveSkillName"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto GetDisplayNickName(int32 Index) -> std::optional<FString>
|
||||
{
|
||||
struct P { int32 Index; FString OutName; } p{ Index, {} };
|
||||
if (!Call(STR("GetDisplayNickName"), p)) return std::nullopt;
|
||||
return p.OutName;
|
||||
}
|
||||
|
||||
auto ChangeNickname(int32 Index, const TCHAR* NewNickName) -> bool
|
||||
{
|
||||
struct P { int32 Index; FString NewNickName; } p{ Index, FString(NewNickName) };
|
||||
return Call(STR("ChangeNickname"), p);
|
||||
}
|
||||
};
|
||||
}
|
||||
123
Api/Pal/PalUtility.hpp
Normal file
123
Api/Pal/PalUtility.hpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#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{};
|
||||
}
|
||||
};
|
||||
}
|
||||
284
Api/PalBoxPalListWidget.hpp
Normal file
284
Api/PalBoxPalListWidget.hpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include <Api/UmgTypes.hpp>
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Api/Widget.hpp>
|
||||
#include <Api/UserWidget.hpp>
|
||||
#include <Api/WidgetTree.hpp>
|
||||
#include <Api/TextBlock.hpp>
|
||||
#include <Api/CanvasPanel.hpp>
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
#include <Api/Pal/PalTypes.hpp>
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class PalBoxPalListWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C");
|
||||
}
|
||||
static auto WidgetTreePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:WidgetTree");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalListEntry"); }
|
||||
|
||||
PalBoxPalListWidget() = default;
|
||||
|
||||
explicit PalBoxPalListWidget(UObject* Obj) : UserWidget(Obj, ClassOf<PalBoxPalListWidget>(), DebugTag()) {}
|
||||
|
||||
static auto BindFromSlotPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:BindFromSlot");
|
||||
}
|
||||
static auto SetupPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:Setup");
|
||||
}
|
||||
static auto UnbindPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:Unbind");
|
||||
}
|
||||
static auto SetEmptyPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:SetEmpty");
|
||||
}
|
||||
static auto OnUpdateHandleSlotPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:OnUpdateHandleSlot");
|
||||
}
|
||||
static auto OnUpdateSkinPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:OnUpdateSkin");
|
||||
}
|
||||
static auto UpdateHPPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:UpdateHP");
|
||||
}
|
||||
static auto UpdateLevelPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalList.WBP_IngameMenu_PalBox_PalList_C:Update Level");
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// FRAME LAYOUTS
|
||||
// ==============================================================
|
||||
|
||||
// ParmsSize 0x08. Full frame ends 0x3C by my arithmetic.
|
||||
struct BindFromSlotFrame
|
||||
{
|
||||
UObject* TargetSlot; // 0x00 UPalIndividualSlot-ish
|
||||
uint8 CompilerLocals[0x34]; // 0x08
|
||||
};
|
||||
|
||||
// ParmsSize 0x08. Full frame ends 0x1C.
|
||||
struct SetupFrame
|
||||
{
|
||||
UObject* TargetSlot; // 0x00
|
||||
uint8 CompilerLocals[0x14]; // 0x08
|
||||
};
|
||||
|
||||
struct UnbindFrame
|
||||
{
|
||||
uint8 CompilerLocals[0x30];
|
||||
};
|
||||
|
||||
struct OnUpdateHandleSlotFrame
|
||||
{
|
||||
UObject* Slot; // 0x00
|
||||
UObject* LastHandle; // 0x08
|
||||
};
|
||||
|
||||
// ParmsSize 0x08 (FName). Full frame ends 0x62.
|
||||
struct OnUpdateSkinFrame
|
||||
{
|
||||
FName NewSkinName; // 0x00
|
||||
uint8 CompilerLocals[0x5A]; // 0x08
|
||||
};
|
||||
|
||||
// ParmsSize 0x10. Full frame ends 0x158
|
||||
struct UpdateHPFrame
|
||||
{
|
||||
Pal::FFixedPoint64_ nowHP; // 0x00
|
||||
Pal::FFixedPoint64_ nowMaxHP; // 0x08
|
||||
uint8 CompilerLocals[0x148]; // 0x10
|
||||
};
|
||||
|
||||
// ParmsSize 0x10. Note DOUBLE, not FFixedPoint64
|
||||
struct UpdateHungerFrame
|
||||
{
|
||||
double nowHunger; // 0x00
|
||||
double nowMaxHunger; // 0x08
|
||||
uint8 CompilerLocals[0x64]; // 0x10
|
||||
};
|
||||
|
||||
// ParmsSize 0x04. Full frame ends 0x38.
|
||||
struct UpdateLevelFrame
|
||||
{
|
||||
int32 NewLevel; // 0x00
|
||||
uint8 CompilerLocals[0x38]; // 0x04
|
||||
};
|
||||
|
||||
// ParmsSize 0x04. Full frame ends 0x39.
|
||||
struct UpdateFavoriteFrame
|
||||
{
|
||||
int32 NewIndex; // 0x00
|
||||
uint8 CompilerLocals[0x35]; // 0x04
|
||||
};
|
||||
|
||||
// ParmsSize 0x10 (FString). Full frame ends 0x28.
|
||||
struct UpdateNicknameFrame
|
||||
{
|
||||
FString NewNickName; // 0x00
|
||||
uint8 CompilerLocals[0x18]; // 0x10
|
||||
};
|
||||
|
||||
struct SetEnableDragDropFrame { bool bIsEnable; };
|
||||
|
||||
// EnumProperty at 0x08 with one byte before the frame ends.
|
||||
struct DraggedButtonEventFrame
|
||||
{
|
||||
UObject* Widget; // 0x00
|
||||
uint8 PressType; // 0x08
|
||||
};
|
||||
|
||||
// ==============================================================
|
||||
// CALLS
|
||||
// ==============================================================
|
||||
|
||||
auto BindFromSlot(UObject* Slot) -> bool
|
||||
{
|
||||
BindFromSlotFrame p{ Slot, {} };
|
||||
return Call(STR("BindFromSlot"), p);
|
||||
}
|
||||
auto Setup(UObject* Slot) -> bool
|
||||
{
|
||||
SetupFrame p{ Slot, {} };
|
||||
return Call(STR("Setup"), p);
|
||||
}
|
||||
auto Unbind() -> bool
|
||||
{
|
||||
UnbindFrame p{};
|
||||
return Call(STR("Unbind"), p);
|
||||
}
|
||||
auto SetEmpty() -> bool { return CallVoid(STR("SetEmpty")); }
|
||||
|
||||
auto SetEnableDragDrop(bool bIsEnable) -> bool
|
||||
{
|
||||
SetEnableDragDropFrame p{ bIsEnable };
|
||||
return Call(STR("SetEnableDragDrop"), p);
|
||||
}
|
||||
auto SetLevel(int32 NewLevel) -> bool
|
||||
{
|
||||
UpdateLevelFrame p{ NewLevel, {} };
|
||||
return Call(STR("Update Level"), p); // the space is real
|
||||
}
|
||||
auto SetFavorite(int32 NewIndex) -> bool
|
||||
{
|
||||
UpdateFavoriteFrame p{ NewIndex, {} };
|
||||
return Call(STR("UpdateFavorite"), p);
|
||||
}
|
||||
auto UpdateHP(Pal::FFixedPoint64_ NowHP, Pal::FFixedPoint64_ NowMaxHP) -> bool
|
||||
{
|
||||
UpdateHPFrame p{ NowHP, NowMaxHP, {} };
|
||||
return Call(STR("UpdateHP"), p);
|
||||
}
|
||||
auto UpdateHunger(double NowHunger, double NowMaxHunger) -> bool
|
||||
{
|
||||
UpdateHungerFrame p{ NowHunger, NowMaxHunger, {} };
|
||||
return Call(STR("UpdateHunger"), p);
|
||||
}
|
||||
auto UpdateSkin(FName NewSkinName) -> bool
|
||||
{
|
||||
OnUpdateSkinFrame p{ NewSkinName, {} };
|
||||
return Call(STR("OnUpdateSkin"), p);
|
||||
}
|
||||
|
||||
|
||||
static auto SlotHandle(UObject* Slot) -> UObject*
|
||||
{
|
||||
if (!Slot) return nullptr;
|
||||
Pal::LooseObject S{ Slot, STR("PalIndividualSlot") };
|
||||
return S.CallForReturn<UObject*>(STR("GetHandle")).value_or(nullptr);
|
||||
}
|
||||
|
||||
static auto SlotIsEmpty(UObject* Slot) -> std::optional<bool>
|
||||
{
|
||||
if (!Slot) return std::nullopt;
|
||||
Pal::LooseObject S{ Slot, STR("PalIndividualSlot") };
|
||||
return S.CallForReturn<bool>(STR("IsEmpty"));
|
||||
}
|
||||
|
||||
static auto SpeciesFromSlot(UObject* Slot) -> std::optional<FName>
|
||||
{
|
||||
return Pal::PalIndividual::CharacterIdFromHandle(SlotHandle(Slot));
|
||||
}
|
||||
|
||||
auto BoundSlot() const -> UObject* { return BoundObject(STR("TargetSlot")); }
|
||||
|
||||
auto BoundHandle() const -> UObject* { return SlotHandle(BoundSlot()); }
|
||||
|
||||
// ==============================================================
|
||||
// BOUND WIDGETS (promoted variables -- property read, no walk)
|
||||
// ==============================================================
|
||||
|
||||
auto TextHP() const -> TextBlock { return WrapText(STR("Text_HP")); }
|
||||
auto TextMaxHP() const -> TextBlock { return WrapText(STR("Text_MaxHP")); }
|
||||
auto TextLevel() const -> TextBlock { return WrapText(STR("Text_LevelValue")); }
|
||||
auto TextNickName() const -> TextBlock { return WrapText(STR("Text_NickName")); }
|
||||
auto TextLvLabel() const -> TextBlock { return WrapText(STR("BP_PalTextBlock_LvText")); }
|
||||
|
||||
auto CanvasRoot() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanel_119")); }
|
||||
auto CanvasSelectFrame() const -> CanvasPanel { return WrapCanvas(STR("PalSelect_BaseTri")); }
|
||||
|
||||
auto GaugeHP() const -> UObject* { return BoundObject(STR("Gauge_HP")); }
|
||||
auto GaugeHunger() const -> UObject* { return BoundObject(STR("Gauge_Hunger")); }
|
||||
auto ImageHPBar() const -> UObject* { return BoundObject(STR("Image_HP_Bar")); }
|
||||
auto ImageHungerBar() const -> UObject* { return BoundObject(STR("Image_Huger_Bar")); } // engine spelling
|
||||
auto ImageCheck() const -> UObject* { return BoundObject(STR("Image_Check")); }
|
||||
auto ImageLocked() const -> UObject* { return BoundObject(STR("Image_Icon_Locked")); }
|
||||
auto ImageListBar() const -> UObject* { return BoundObject(STR("Image_Pal_List_Bar")); }
|
||||
auto ImageEmpty() const -> UObject* { return BoundObject(STR("Empty")); }
|
||||
auto PalIcon() const -> UObject* { return BoundObject(STR("PalIcon")); }
|
||||
auto RetainerBox() const -> UObject* { return BoundObject(STR("RetainerBox_0")); }
|
||||
auto InvisibleButton() const -> UObject* { return BoundObject(STR("WBP_PalInvisibleButton")); }
|
||||
auto DynamicMaterial() const -> UObject* { return BoundObject(STR("dynamicMaterial")); }
|
||||
|
||||
v auto AnimFocus() const -> UObject* { return BoundObject(STR("Anm_Focus")); }
|
||||
auto AnimNormal() const -> UObject* { return BoundObject(STR("Anm_Normal")); }
|
||||
auto AnimNormalToFocus() const -> UObject* { return BoundObject(STR("Anm_NormalToFocus")); }
|
||||
auto AnimPalAlive() const -> UObject* { return BoundObject(STR("Anm_Pal_Alive")); }
|
||||
auto AnimPalDead() const -> UObject* { return BoundObject(STR("Anm_Pal_Dead")); }
|
||||
|
||||
// ---- plain properties ----------------------------------------
|
||||
|
||||
auto IsDragDropEnabled() const -> std::optional<bool>
|
||||
{
|
||||
return PropertyBoolValue(STR("bEnableDragDrop"));
|
||||
}
|
||||
|
||||
private:
|
||||
auto WrapCanvas(const TCHAR* Name) const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
auto WrapText(const TCHAR* Name) const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
protected:
|
||||
PalBoxPalListWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
224
Api/PalBoxWidget.hpp
Normal file
224
Api/PalBoxWidget.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "UmgTypes.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
#include "SizeBox.hpp"
|
||||
#include "TextBlock.hpp"
|
||||
|
||||
#include "PalDetailWidget.hpp"
|
||||
#include "PalBoxPalListWidget.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
|
||||
class PalBoxWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalBox"); }
|
||||
|
||||
PalBoxWidget() = default;
|
||||
|
||||
explicit PalBoxWidget(UObject* Obj) : UserWidget(Obj, ClassOf<PalBoxWidget>(), DebugTag()) {}
|
||||
|
||||
auto PalDetail() const -> PalDetailWidget
|
||||
{
|
||||
UObject* O = BoundObject(STR("WBP_IngameMenu_PalBox_PalDetail"));
|
||||
return O ? PalDetailWidget{ O } : PalDetailWidget{};
|
||||
}
|
||||
|
||||
static constexpr int32 kPartySlotCount = 5;
|
||||
|
||||
auto PartySlot(int32 Index) const -> PalBoxPalListWidget
|
||||
{
|
||||
if (Index < 0 || Index >= kPartySlotCount) return PalBoxPalListWidget{};
|
||||
|
||||
const TCHAR* Names[kPartySlotCount] = {
|
||||
STR("WBP_IngameMenu_PalBox_PalList_0"),
|
||||
STR("WBP_IngameMenu_PalBox_PalList_1"),
|
||||
STR("WBP_IngameMenu_PalBox_PalList_2"),
|
||||
STR("WBP_IngameMenu_PalBox_PalList_3"),
|
||||
STR("WBP_IngameMenu_PalBox_PalList_4"),
|
||||
};
|
||||
|
||||
UObject* O = BoundObject(Names[Index]);
|
||||
return O ? PalBoxPalListWidget{ O } : PalBoxPalListWidget{};
|
||||
}
|
||||
|
||||
// Convenience for "do something to every party tile."
|
||||
auto PartySlots() const -> std::vector<PalBoxPalListWidget>
|
||||
{
|
||||
std::vector<PalBoxPalListWidget> Out;
|
||||
Out.reserve(kPartySlotCount);
|
||||
for (int32 i = 0; i < kPartySlotCount; ++i)
|
||||
{
|
||||
PalBoxPalListWidget W = PartySlot(i);
|
||||
if (W.IsValid()) Out.push_back(W);
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
auto BoxPalList() const -> UObject* { return BoundObject(STR("WBP_BoxPalList")); }
|
||||
auto BaseCampPalList() const -> UObject* { return BoundObject(STR("WBP_BaseCampPalList")); }
|
||||
|
||||
// ==============================================================
|
||||
// INJECTION ANCHORS
|
||||
// ==============================================================
|
||||
auto CanvasPalBox() const -> CanvasPanel { return WrapCanvas(STR("Canvas_PalBox")); }
|
||||
auto CanvasPalCamp() const -> CanvasPanel { return WrapCanvas(STR("Canvas_PalCamp")); }
|
||||
|
||||
auto FindCanvas(const TCHAR* Name) -> CanvasPanel { return Child<CanvasPanel>(Name); }
|
||||
|
||||
auto SizeBoxPresetButton() const -> SizeBox
|
||||
{
|
||||
UObject* O = BoundObject(STR("SizeBox_PresetButton"));
|
||||
return O ? SizeBox{ O } : SizeBox{};
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// BOUND WIDGETS
|
||||
// ==============================================================
|
||||
|
||||
auto TextBaseCampName() const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(STR("Text_BaseCampName"));
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
auto CloseButton() const -> UObject* { return BoundObject(STR("CloseButton")); }
|
||||
auto PresetButton() const -> UObject* { return BoundObject(STR("WBP_CommonButton_Preset")); }
|
||||
auto ReturnButton() const -> UObject* { return BoundObject(STR("WBP_CommonButton_Return")); }
|
||||
auto MenuButton() const -> UObject* { return BoundObject(STR("WBP_Menu_btn")); }
|
||||
auto ImageFrame() const -> UObject* { return BoundObject(STR("Image_Frame")); }
|
||||
|
||||
// ==============================================================
|
||||
// STATE
|
||||
// ==============================================================
|
||||
|
||||
auto IsDragDropEnabled() const -> std::optional<bool>
|
||||
{
|
||||
return PropertyBoolValue(STR("bIsEnableDragDropControl"));
|
||||
}
|
||||
|
||||
auto PartyPalDetails() const -> std::vector<UObject*>
|
||||
{
|
||||
std::vector<UObject*> Out;
|
||||
|
||||
auto* Arr = TryPropertyPtr<TArrayRead_<UObject*>>(STR("PartyPalDetails"));
|
||||
if (!Arr) return Out;
|
||||
|
||||
if (!Arr->IsPlausible(kMaxPlausibleEntries))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] PartyPalDetails does not look like a live TArray (Num={}, Max={})\n"),
|
||||
m_tag, Arr->Num, Arr->Max);
|
||||
return Out;
|
||||
}
|
||||
|
||||
Out.reserve(static_cast<size_t>(Arr->Num));
|
||||
for (int32 i = 0; i < Arr->Num; ++i)
|
||||
{
|
||||
if (UObject* Entry = Arr->Data[i]) Out.push_back(Entry);
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
static auto DisplayPalDetailPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:Display Pal Detail");
|
||||
}
|
||||
static auto SetupPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:Setup");
|
||||
}
|
||||
static auto SetupPartyPalPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:Setup Party Pal");
|
||||
}
|
||||
static auto SetBaseCampPalListPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:SetBaseCampPalList");
|
||||
}
|
||||
static auto SetEnableDragDropPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:SetEnableDragDrop");
|
||||
}
|
||||
static auto OnClickedPartySlotInternalPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:OnClickedPartySlotInternal");
|
||||
}
|
||||
static auto OnHoveredPartySlotInternalPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:OnHoveredPartySlot_Internal");
|
||||
}
|
||||
static auto OnInitializedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:OnInitialized");
|
||||
}
|
||||
static auto PreConstructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/"
|
||||
"WBP_IngameMenu_PalBox.WBP_IngameMenu_PalBox_C:PreConstruct");
|
||||
}
|
||||
|
||||
// ---- frames --------------------------------------------------
|
||||
|
||||
struct DisplayPalDetailFrame
|
||||
{
|
||||
UObject* Slot; // 0x00
|
||||
uint8 slotType; // 0x08
|
||||
};
|
||||
|
||||
struct SetupFrame { UObject* Parameter; }; // 0x00, ParmsSize 0x08
|
||||
struct SetEnableDragDropFrame { bool bIsEnableDragDrop; };
|
||||
|
||||
struct SlotFrame { UObject* Slot; };
|
||||
struct SetBaseCampPalListFrame
|
||||
{
|
||||
UObject** Data;
|
||||
int32 Num;
|
||||
int32 Max;
|
||||
};
|
||||
static_assert(sizeof(SetBaseCampPalListFrame) == 16,
|
||||
"SetBaseCampPalList frame is 16 bytes on the dumped build");
|
||||
|
||||
|
||||
private:
|
||||
static constexpr int32 kMaxPlausibleEntries = 1024;
|
||||
|
||||
auto WrapCanvas(const TCHAR* Name) const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
|
||||
protected:
|
||||
PalBoxWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
155
Api/PalCharacterScrollList.hpp
Normal file
155
Api/PalCharacterScrollList.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <Unreal/UClass.hpp>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WrapBox.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ==================================================================
|
||||
// WBP_PalCharacterScrollList_C -- the end of the chain. This is the
|
||||
// widget that creates, owns and destroys the slot tiles.
|
||||
//
|
||||
// PalBoxWidget
|
||||
// -> WBP_BoxPalList -> WBP_BoxPalListBase -> WBP_BoxPalScrollList
|
||||
// -> WBP_BaseCampPalList ------------------------> (same class)
|
||||
// == THIS
|
||||
// -> MyBlock (UWrapBox)
|
||||
// -> the tiles
|
||||
// ==================================================================
|
||||
class PalCharacterScrollList : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalCharacterScrollList"); }
|
||||
|
||||
PalCharacterScrollList() = default;
|
||||
explicit PalCharacterScrollList(UObject* Obj) : UserWidget(Obj, ClassOf<PalCharacterScrollList>(), DebugTag()) {}
|
||||
|
||||
// ==============================================================
|
||||
// THE TILES
|
||||
// ==============================================================
|
||||
|
||||
auto TileContainer() const -> WrapBox
|
||||
{
|
||||
UObject* O = BoundObject(STR("MyBlock"));
|
||||
return O ? WrapBox{ O } : WrapBox{};
|
||||
}
|
||||
|
||||
auto Tiles() -> std::vector<UObject*>
|
||||
{
|
||||
WrapBox Box = TileContainer();
|
||||
if (!Box.IsValid()) return {};
|
||||
return Box.GetChildObjects();
|
||||
}
|
||||
|
||||
auto TileClass() const -> UClass*
|
||||
{
|
||||
return PropertyValue<UClass*>(STR("slotWidgetClass")).value_or(nullptr);
|
||||
}
|
||||
|
||||
auto TileClassName() const -> RC::StringType
|
||||
{
|
||||
UClass* C = TileClass();
|
||||
return C ? C->GetFullName() : RC::StringType{ STR("<null>") };
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
static auto CreateSlotWidgetPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:CreateSlotWidget");
|
||||
}
|
||||
static auto AddSlotButtonToScrollListPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:AddSlotButtonToScrollList");
|
||||
}
|
||||
static auto BindButtonEventsPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:BindButtonEvents");
|
||||
}
|
||||
|
||||
static auto OnUpdateSlotBindedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:OnUpdateSlot_Binded");
|
||||
}
|
||||
static auto OnSlotSyncedInternalPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:OnSlotSynced_Internal");
|
||||
}
|
||||
static auto OnEmptySlotInternalPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:OnEmptySlot_Internal");
|
||||
}
|
||||
static auto AddCharacterSlotPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:AddCharacterSlot");
|
||||
}
|
||||
|
||||
static auto ClearAllChildrenPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:ClearAllChildren");
|
||||
}
|
||||
static auto ClearInnerChildrenPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/CommonWidget/CommonScrollList/"
|
||||
"WBP_PalCharacterScrollList.WBP_PalCharacterScrollList_C:ClearInnnerChildren");
|
||||
}
|
||||
|
||||
// ---- frames --------------------------------------------------
|
||||
|
||||
struct CreateSlotWidgetFrame
|
||||
{
|
||||
UObject* TargetSlot;
|
||||
UObject* createdSlotButton;
|
||||
};
|
||||
|
||||
// Covers AddSlotButtonToScrollList (createdSlotButton),
|
||||
// BindButtonEvents (createdSlotButton), OnUpdateSlot_Binded
|
||||
// (SlotButton), OnSlotSynced_Internal (SelfButton),
|
||||
// OnEmptySlot_Internal (SlotButton), OnHovered_Internal /
|
||||
// OnUnhovered_Internal / OnFocused_Internal /
|
||||
// OnUnfocused_Internal (Widget), and "On Right Clicked Internal"
|
||||
// (ButtonBase). Eleven functions, one shape -- a single tile
|
||||
// pointer at 0x00. Naming it once is the honest version.
|
||||
struct TileFrame { UObject* Tile; };
|
||||
struct AddCharacterSlotFrame { UObject* TargetSlot; };
|
||||
|
||||
struct TileClickFrame
|
||||
{
|
||||
UObject* Widget; // 0x00
|
||||
uint8 PressType; // 0x08
|
||||
};
|
||||
|
||||
struct AddCharacterSlotsFrame
|
||||
{
|
||||
UObject** Data;
|
||||
int32 Num;
|
||||
int32 Max;
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
PalCharacterScrollList(UObject* Obj, UClass* Expected, const TCHAR* Tag) : UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
179
Api/PalCommonCharacterSlot.hpp
Normal file
179
Api/PalCommonCharacterSlot.hpp
Normal file
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "UmgTypes.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
#include "Overlay.hpp"
|
||||
#include "TextBlock.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class PalCommonCharacterSlot : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalCharacterSlot"); }
|
||||
|
||||
PalCommonCharacterSlot() = default;
|
||||
explicit PalCommonCharacterSlot(UObject* Obj)
|
||||
: UserWidget(Obj, ClassOf<PalCommonCharacterSlot>(), DebugTag()) {}
|
||||
|
||||
// ==============================================================
|
||||
// ANCHORS
|
||||
// ==============================================================
|
||||
|
||||
auto IconOverlay() -> Overlay
|
||||
{
|
||||
return Child<Overlay>(STR("Overlay_60"));
|
||||
}
|
||||
|
||||
auto OverlayAwakening() const -> Overlay { return WrapOverlay(STR("Overlay_Awakening")); }
|
||||
auto OverlayBan() const -> Overlay { return WrapOverlay(STR("Overlay_Ban")); }
|
||||
auto OverlayDoping() const -> Overlay { return WrapOverlay(STR("Overlay_Doping")); }
|
||||
auto OverlayRevive() const -> Overlay { return WrapOverlay(STR("Overlay_Revive")); }
|
||||
|
||||
auto IconWidget() const -> UObject* { return BoundObject(STR("WBP_PalCommonCharacterIcon")); }
|
||||
|
||||
|
||||
auto ImageWorkIcon() const -> UObject* { return BoundObject(STR("Image_IconPalWork")); }
|
||||
auto ImageExpedition() const -> UObject* { return BoundObject(STR("Image_Icon_Expedition")); }
|
||||
auto ImageLocked() const -> UObject* { return BoundObject(STR("Image_Icon_Locked")); }
|
||||
auto ImageGlobalImport() const -> UObject* { return BoundObject(STR("Image_Icon_GlobalInport")); } // engine spelling
|
||||
auto ImageSleep() const -> UObject* { return BoundObject(STR("Image_PalSleep")); }
|
||||
auto ImageMeal() const -> UObject* { return BoundObject(STR("Image_Meal")); }
|
||||
auto ImageMutant() const -> UObject* { return BoundObject(STR("Image_Mutant")); }
|
||||
auto ImageRare() const -> UObject* { return BoundObject(STR("Image_Rare")); }
|
||||
auto ImageStrong() const -> UObject* { return BoundObject(STR("Image_Strong")); }
|
||||
auto ImageLowHealth() const -> UObject* { return BoundObject(STR("Image_PalLowHealth")); }
|
||||
auto ImageDying() const -> UObject* { return BoundObject(STR("Image_PalDying")); }
|
||||
auto ImageBattle() const -> UObject* { return BoundObject(STR("Image_PalBattleImage")); }
|
||||
|
||||
auto TextHPPercent() const -> TextBlock { return WrapText(STR("Text_HPPercent")); }
|
||||
auto TextReviveTimer() const -> TextBlock { return WrapText(STR("Text_ReviveTimer")); }
|
||||
|
||||
// ---- state ----------------------------------------------------
|
||||
|
||||
auto IsBattleModeDisplayed() const -> std::optional<bool> { return PropertyBoolValue(STR("isBattleModeDisplay")); }
|
||||
auto IsLowHealthBlinking() const -> std::optional<bool> { return PropertyBoolValue(STR("isLowHealthBlink")); }
|
||||
auto IsHealthPercentDisplayed()const -> std::optional<bool> { return PropertyBoolValue(STR("isDisplayHealthPercent")); }
|
||||
auto IsRarityDisplayed() const -> std::optional<bool> { return PropertyBoolValue(STR("isDisplayRarity")); }
|
||||
auto LastWorkDetail() const -> std::optional<bool> { return PropertyBoolValue(STR("lastWorkDetail")); }
|
||||
auto IsDopingEffectDisplayed() const -> std::optional<bool> { return PropertyBoolValue(STR("bIsDisplayDopingEffect")); }
|
||||
auto IsFavoriteMarkDisplayed() const -> std::optional<bool> { return PropertyBoolValue(STR("bIsDisplayFavoriteMark")); }
|
||||
auto IsAwakeningMarkDisplayed()const -> std::optional<bool> { return PropertyBoolValue(STR("bIsDisplayAwakeningMark")); }
|
||||
|
||||
auto CachedLastHPPercent() const -> std::optional<double> { return PropertyValue<double>(STR("chachedLastHPPercent")); } // engine spelling
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
static auto OnUpdateSlotBindedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C:On Update Slot Binded");
|
||||
}
|
||||
|
||||
static auto OnInitializedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C:OnInitialized");
|
||||
}
|
||||
|
||||
static auto OnSetEmptyBindedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C:On Set Empty Binded");
|
||||
}
|
||||
|
||||
static auto OnNotifyWorkDetailBindedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C:OnNotifyWorkDetail_Binded");
|
||||
}
|
||||
static auto DisplayBanMarkPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlot.WBP_PalCommonCharacterSlot_C:DisplayBanMark");
|
||||
}
|
||||
|
||||
// ---- frames ---------------------------------------------------
|
||||
|
||||
struct UpdateSlotBindedFrame { UObject* TargetSlot; };
|
||||
|
||||
struct BoolFrame { bool bValue; };
|
||||
|
||||
struct RegisterIconWidgetFrame { UObject* IconWidget; };
|
||||
struct UpdateFavoriteFrame { int32 NewIndex; };
|
||||
|
||||
struct UpdateHPFrame
|
||||
{
|
||||
int64 nowHP;
|
||||
int64 nowMaxHP;
|
||||
};
|
||||
|
||||
// On Set Rarity Binded: two adjacent bools.
|
||||
struct SetRarityFrame
|
||||
{
|
||||
bool IsBoss; // 0x00
|
||||
bool IsRare; // 0x01
|
||||
};
|
||||
|
||||
private:
|
||||
auto WrapOverlay(const TCHAR* Name) const -> Overlay
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? Overlay{ O } : Overlay{};
|
||||
}
|
||||
auto WrapText(const TCHAR* Name) const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
protected:
|
||||
PalCommonCharacterSlot(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
|
||||
|
||||
class PalCommonCharacterIcon : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterIcon.WBP_PalCommonCharacterIcon_C");
|
||||
}
|
||||
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalCharacterIcon"); }
|
||||
|
||||
PalCommonCharacterIcon() = default;
|
||||
explicit PalCommonCharacterIcon(UObject* Obj) : UserWidget(Obj, ClassOf<PalCommonCharacterIcon>(), DebugTag()) {}
|
||||
|
||||
auto IconImage() const -> UObject* { return BoundObject(STR("iconImage")); }
|
||||
auto Throbber() const -> UObject* { return BoundObject(STR("CircularThrobber_96")); }
|
||||
auto DynamicMaterial() const -> UObject* { return BoundObject(STR("dynamicMaterial")); }
|
||||
auto IsEmpty() const -> std::optional<bool> { return PropertyBoolValue(STR("IsEmpty")); }
|
||||
auto SphereMaskRadius() const -> std::optional<double> { return PropertyValue<double>(STR("SphereMaskRadius")); }
|
||||
|
||||
|
||||
static auto OnLoadedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterIcon.WBP_PalCommonCharacterIcon_C:OnLoaded");
|
||||
}
|
||||
static auto OnEmptyPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/WBP_PalCommonCharacterIcon.WBP_PalCommonCharacterIcon_C:OnEmpty");
|
||||
}
|
||||
|
||||
protected:
|
||||
PalCommonCharacterIcon(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
139
Api/PalCommonCharacterSlotButton.hpp
Normal file
139
Api/PalCommonCharacterSlotButton.hpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "UmgTypes.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
#include "Overlay.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ==================================================================
|
||||
// WBP_PalCommonCharacterSlotButton_C
|
||||
// ==================================================================
|
||||
class PalCommonCharacterSlotButton : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalSlotButton"); }
|
||||
|
||||
PalCommonCharacterSlotButton() = default;
|
||||
explicit PalCommonCharacterSlotButton(UObject* Obj)
|
||||
: UserWidget(Obj, ClassOf<PalCommonCharacterSlotButton>(), DebugTag()) {}
|
||||
|
||||
// ==============================================================
|
||||
// NESTED WIDGETS
|
||||
// ==============================================================
|
||||
|
||||
auto IconWidget() const -> UObject* { return BoundObject(STR("WBP_PalCommonCharacterSlot")); }
|
||||
auto InvisibleButton() const -> UObject* { return BoundObject(STR("WBP_PalInvisibleButton")); }
|
||||
|
||||
// ==============================================================
|
||||
// ANCHORS
|
||||
// ==============================================================
|
||||
|
||||
auto RootCanvas() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanel_36")); }
|
||||
auto OverlaySelect() const -> Overlay { return WrapOverlay(STR("Overlay_Select")); }
|
||||
|
||||
// TODO: Review below and remove or update.
|
||||
auto IconAnchor() -> Overlay
|
||||
{
|
||||
return Child<Overlay>(STR("Overlay_60"),
|
||||
WidgetTree::EScope::IncludeNestedUserWidgets);
|
||||
}
|
||||
|
||||
auto Check0() const -> UObject* { return BoundObject(STR("Check_0")); }
|
||||
auto Check1() const -> UObject* { return BoundObject(STR("Check_1")); }
|
||||
auto Check2() const -> UObject* { return BoundObject(STR("Check_2")); }
|
||||
auto FocusedFrame() const -> UObject* { return BoundObject(STR("FocusedFrame")); }
|
||||
auto PushEff() const -> UObject* { return BoundObject(STR("PushEff")); }
|
||||
|
||||
// ---- state ----------------------------------------------------
|
||||
auto IsDisplayingDopingEffect() const -> std::optional<bool> { return PropertyBoolValue(STR("bIsDisplayDopingEffect")); }
|
||||
auto HasCheckAnimPlayed() const -> std::optional<bool> { return PropertyBoolValue(STR("bCheckAnimPlayed")); }
|
||||
|
||||
// ==============================================================
|
||||
// HOOK TARGETS
|
||||
// ==============================================================
|
||||
|
||||
static auto DestructPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C:Destruct");
|
||||
}
|
||||
|
||||
static auto RegisterCharacterSlotWidgetPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C:RegisterCharacterSlotWidget");
|
||||
}
|
||||
static auto OnInitializedPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C:OnInitialized");
|
||||
}
|
||||
|
||||
static auto SetEnablePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C:SetEnable");
|
||||
}
|
||||
static auto SetSearchResultActivePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/Thumbnails/Character/"
|
||||
"WBP_PalCommonCharacterSlotButton.WBP_PalCommonCharacterSlotButton_C:SetSearchResultActive");
|
||||
}
|
||||
|
||||
// ---- frames ---------------------------------------------------
|
||||
|
||||
struct WidgetFrame { UObject* Widget; }; // PlayFocusAnim / PlayUnfocusAnim
|
||||
struct RegisterCharacterSlotWidgetFrame { UObject* characterSlotWidget; };
|
||||
struct RegisterButtonFrame { UObject* Button; };
|
||||
struct BoolFrame { bool bValue; }; // SetEnable, SetSearchResultActive, the AnmEvent_* family
|
||||
|
||||
// ==============================================================
|
||||
// CALLS
|
||||
// ==============================================================
|
||||
|
||||
auto SetEnable(bool bIsEnable) -> bool
|
||||
{
|
||||
BoolFrame p{ bIsEnable };
|
||||
return Call(STR("SetEnable"), p);
|
||||
}
|
||||
auto SetEnableGreyoutOnly(bool bIsEnable) -> bool
|
||||
{
|
||||
BoolFrame p{ bIsEnable };
|
||||
return Call(STR("SetEnable_GreyoutOnly"), p);
|
||||
}
|
||||
auto SetSearchResultActive(bool bActive) -> bool
|
||||
{
|
||||
// The parameter is spelled bAvtive in the engine. Dont fix. Lol.
|
||||
BoolFrame p{ bActive };
|
||||
return Call(STR("SetSearchResultActive"), p);
|
||||
}
|
||||
|
||||
private:
|
||||
auto WrapCanvas(const TCHAR* Name) const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
auto WrapOverlay(const TCHAR* Name) const -> Overlay
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? Overlay{ O } : Overlay{};
|
||||
}
|
||||
|
||||
protected:
|
||||
PalCommonCharacterSlotButton(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
270
Api/PalDetailWidget.hpp
Normal file
270
Api/PalDetailWidget.hpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UmgTypes.hpp"
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "UserWidget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
#include "TextBlock.hpp"
|
||||
#include "CanvasPanel.hpp"
|
||||
|
||||
#include <Api/Pal/PalIndividual.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterHandle.hpp>
|
||||
#include <Api/Pal/PalIndividualCharacterParameter.hpp>
|
||||
|
||||
using namespace PalUIExtension::Api::Pal;
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
|
||||
|
||||
class PalDetailWidget : public UserWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* {
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalDetail.WBP_IngameMenu_PalBox_PalDetail_C");
|
||||
}
|
||||
|
||||
static auto WidgetTreePath() -> const TCHAR* {
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalDetail.WBP_IngameMenu_PalBox_PalDetail_C:WidgetTree");
|
||||
}
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PalDetail"); }
|
||||
|
||||
PalDetailWidget() = default;
|
||||
explicit PalDetailWidget(UObject* Obj) : UserWidget(Obj, ClassOf<PalDetailWidget>(), DebugTag()) {
|
||||
}
|
||||
|
||||
struct GetBindedHandleFrame
|
||||
{
|
||||
UObject* targetHandle; // 0x00 out
|
||||
bool bValidSoftRef; // 0x08
|
||||
uint8 Pad09[7]; // 0x09
|
||||
UObject* SoftRefAsObject; // 0x10
|
||||
UObject* CastAsHandle; // 0x18
|
||||
bool bCastSucceeded; // 0x20
|
||||
uint8 Pad21[7]; // 0x21
|
||||
};
|
||||
static_assert(sizeof(GetBindedHandleFrame) == 40,
|
||||
"GetBindedHandle frame is 40 bytes on the dumped build");
|
||||
|
||||
struct GetBindedIndividualParameterFrame
|
||||
{
|
||||
UObject* IndividualParameter; // 0x00 out
|
||||
UObject* Handle; // 0x08
|
||||
UObject* TryGetResult; // 0x10
|
||||
bool bValid0; // 0x18
|
||||
bool bValid1; // 0x19
|
||||
uint8 Pad1A[6]; // 0x1A
|
||||
};
|
||||
static_assert(sizeof(GetBindedIndividualParameterFrame) == 32,
|
||||
"GetBindedIndividualParameter frame is 32 bytes on the dumped build");
|
||||
|
||||
auto GetBindedHandle() -> UObject*
|
||||
{
|
||||
GetBindedHandleFrame p{};
|
||||
if (!Call(STR("GetBindedHandle"), p)) return nullptr;
|
||||
return p.targetHandle;
|
||||
}
|
||||
auto GetBindedIndividualParameter() -> UObject*
|
||||
{
|
||||
GetBindedIndividualParameterFrame p{};
|
||||
if (!Call(STR("GetBindedIndividualParameter"), p)) return nullptr;
|
||||
return p.IndividualParameter;
|
||||
}
|
||||
|
||||
auto CurrentHandle() -> PalIndividualCharacterHandle
|
||||
{
|
||||
UObject* H = GetBindedHandle();
|
||||
return H ? PalIndividualCharacterHandle{ H } : PalIndividualCharacterHandle{};
|
||||
}
|
||||
auto CurrentParameter() -> PalIndividualCharacterParameter
|
||||
{
|
||||
UObject* P = GetBindedIndividualParameter();
|
||||
return P ? PalIndividualCharacterParameter{ P } : PalIndividualCharacterParameter{};
|
||||
}
|
||||
|
||||
auto ResolveIdentity() -> PalIdentity
|
||||
{
|
||||
if (UObject* Param = GetBindedIndividualParameter())
|
||||
{
|
||||
return PalIndividual::IdentifyFromParameter(Param);
|
||||
}
|
||||
return PalIndividual::Identify(GetBindedHandle());
|
||||
}
|
||||
|
||||
auto CurrentCharacterId() -> std::optional<FName>
|
||||
{
|
||||
if (UObject* Param = GetBindedIndividualParameter())
|
||||
{
|
||||
return PalIndividual::CharacterId(Param);
|
||||
}
|
||||
return PalIndividual::CharacterIdFromHandle(GetBindedHandle());
|
||||
}
|
||||
|
||||
|
||||
auto CanvasBase() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Base")); }
|
||||
auto CanvasBaseToolTip() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Base_ToolTip")); }
|
||||
auto CanvasInfo() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Info")); }
|
||||
auto CanvasFood() const -> CanvasPanel { return WrapCanvas(STR("Canvas_Food")); }
|
||||
auto CanvasPartnerSkill() const -> CanvasPanel { return WrapCanvas(STR("Canvas_PartnerSkill")); }
|
||||
auto CanvasPotencial() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanel_Potencial")); }
|
||||
auto CanvasDebuff() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelDebuff")); }
|
||||
auto CanvasStatePalWork() const -> CanvasPanel { return WrapCanvas(STR("CanvasPanelStatePalWork")); }
|
||||
|
||||
auto TextPalName() const -> TextBlock { return WrapText(STR("Text_PalName")); }
|
||||
auto TextLevel() const -> TextBlock { return WrapText(STR("Text_LevelValue")); }
|
||||
auto TextNowHP() const -> TextBlock { return WrapText(STR("Text_NowHP")); }
|
||||
auto TextMaxHP() const -> TextBlock { return WrapText(STR("Text_MaxHP")); }
|
||||
auto TextNowHunger() const -> TextBlock { return WrapText(STR("Text_NowHunger")); }
|
||||
auto TextMaxHunger() const -> TextBlock { return WrapText(STR("Text_MaxHunger")); }
|
||||
auto TextNowSanity() const -> TextBlock { return WrapText(STR("Text_NowSanity")); }
|
||||
auto TextMaxSanity() const -> TextBlock { return WrapText(STR("Text_MaxSanity")); }
|
||||
auto TextAttack() const -> TextBlock { return WrapText(STR("Text_AttackValue")); }
|
||||
auto TextDefense() const -> TextBlock { return WrapText(STR("Text_DefenseValue")); }
|
||||
auto TextWorkValue() const -> TextBlock { return WrapText(STR("Text_WorkValue")); }
|
||||
auto TextWorkTypeName() const -> TextBlock { return WrapText(STR("Text_WorkTypeName")); }
|
||||
auto TextNextExp() const -> TextBlock { return WrapText(STR("Text_NextExp")); }
|
||||
auto TextFriendshipRank() const -> TextBlock { return WrapText(STR("Text_FriendshipRank_Num")); }
|
||||
|
||||
auto GaugeHP() const -> UObject* { return BoundObject(STR("Gauge_HP")); }
|
||||
auto GaugeHunger() const -> UObject* { return BoundObject(STR("Gauge_Hunger")); }
|
||||
auto GaugeExp() const -> UObject* { return BoundObject(STR("Gauge_Exp")); }
|
||||
auto GaugeFriendship() const -> UObject* { return BoundObject(STR("Gauge_Friendship")); }
|
||||
auto ImageRare() const -> UObject* { return BoundObject(STR("Image_Rare")); }
|
||||
auto ImageStrong() const -> UObject* { return BoundObject(STR("Image_Strong")); }
|
||||
auto ImageMutant() const -> UObject* { return BoundObject(STR("Image_Mutant")); }
|
||||
auto CharacterIcon() const -> UObject* { return BoundObject(STR("WBP_PalCommonCharacterIcon")); }
|
||||
auto ElementIcon0() const -> UObject* { return BoundObject(STR("WBP_PalElementIcon")); }
|
||||
auto ElementIcon1() const -> UObject* { return BoundObject(STR("WBP_PalElementIcon_1")); }
|
||||
|
||||
// ---- plain properties ----------------------------------------
|
||||
auto SavedMaxHunger() const -> std::optional<float> { return PropertyValue<float>(STR("savedMaxHunger")); }
|
||||
auto SavedMaxSanity() const -> std::optional<float> { return PropertyValue<float>(STR("savedMaxSanity")); }
|
||||
auto HidePassive() const -> std::optional<bool> { return PropertyValue<bool>(STR("HidePassive")); }
|
||||
|
||||
|
||||
// ---- calls ---------------------------------------------------
|
||||
auto SetEmpty() -> bool { return CallVoid(STR("SetEmpty")); }
|
||||
auto SetupPartnerSkillInfo() -> bool { return CallVoid(STR("SetupPartnerSkillInfo")); }
|
||||
auto ToggleSkillInfo() -> bool { return CallVoid(STR("ToggleSKillInfo")); } // engine spelling
|
||||
auto OverrideHideTalent() -> bool { return CallVoid(STR("OverrideHideTalent")); }
|
||||
|
||||
auto SetVisiblePartnerSkillInfo(bool bVisible) -> bool
|
||||
{
|
||||
struct P { bool bVisible; } p{ bVisible };
|
||||
return Call(STR("SetVisiblePartnerSkillInfo"), p);
|
||||
}
|
||||
auto SetVisibleToggleSkillInfoKeyGuide(bool bVisible) -> bool
|
||||
{
|
||||
struct P { bool bVisible; } p{ bVisible };
|
||||
return Call(STR("SetVisibleToggleSkillInfoKeyGuide"), p);
|
||||
}
|
||||
auto SetAwakening(bool bAwaked) -> bool
|
||||
{
|
||||
struct P { bool bAwaked; } p{ bAwaked };
|
||||
return Call(STR("SetAwakening"), p);
|
||||
}
|
||||
auto SetImportedFlag(bool bImportedFlag) -> bool
|
||||
{
|
||||
struct P { bool bImportedFlag; } p{ bImportedFlag };
|
||||
return Call(STR("SetImportedFlag"), p);
|
||||
}
|
||||
auto SetDetailMode(bool IsTip) -> bool
|
||||
{
|
||||
struct P { bool IsTip; } p{ IsTip };
|
||||
return Call(STR("Set Detail Mode"), p); // the spaces are real
|
||||
}
|
||||
auto SetRarity(bool IsBoss, bool IsRare) -> bool
|
||||
{
|
||||
struct P { bool IsBoss; bool IsRare; } p{ IsBoss, IsRare };
|
||||
return Call(STR("SetRarity"), p);
|
||||
}
|
||||
auto SetRank(int32 Rank) -> bool
|
||||
{
|
||||
struct P { int32 Rank; } p{ Rank };
|
||||
return Call(STR("SetRank"), p);
|
||||
}
|
||||
auto SetSoulRank(int32 Rank) -> bool
|
||||
{
|
||||
struct P { int32 Rank; } p{ Rank };
|
||||
return Call(STR("SetSoulRank"), p);
|
||||
}
|
||||
auto SetFoodAmount(int32 FoodAmount) -> bool
|
||||
{
|
||||
struct P { int32 FoodAmount; } p{ FoodAmount };
|
||||
return Call(STR("SetFoodAmount"), p);
|
||||
}
|
||||
auto SetFriendShip(int32 FriendshipRank, int32 FriendshipPoint) -> bool
|
||||
{
|
||||
struct P { int32 FriendshipRank; int32 FriendshipPoint; } p{ FriendshipRank, FriendshipPoint };
|
||||
return Call(STR("SetFriendShip"), p);
|
||||
}
|
||||
auto SetGender(EPalGenderType_ GenderType) -> bool
|
||||
{
|
||||
struct P { EPalGenderType_ GenderType; } p{ GenderType };
|
||||
return Call(STR("SetGender"), p);
|
||||
}
|
||||
auto SetElementType(EPalElementType_ Type1, EPalElementType_ Type2) -> bool
|
||||
{
|
||||
struct P { EPalElementType_ type1; EPalElementType_ type2; } p{ Type1, Type2 };
|
||||
return Call(STR("SetElementType"), p);
|
||||
}
|
||||
|
||||
struct WorkSuitabilityCheck { bool bValid{}; EPalWorkSuitability_ Out{}; };
|
||||
auto CheckValidWorkSuitability(EPalWorkSuitability_ In) -> std::optional<WorkSuitabilityCheck>
|
||||
{
|
||||
struct P
|
||||
{
|
||||
EPalWorkSuitability_ InWorkSuitability;
|
||||
bool IsValid;
|
||||
EPalWorkSuitability_ OutWorkSuitability;
|
||||
} p{ In, false, EPalWorkSuitability_::None };
|
||||
|
||||
if (!Call(STR("Check Valid Work Suitability"), p)) return std::nullopt;
|
||||
return WorkSuitabilityCheck{ p.IsValid, p.OutWorkSuitability };
|
||||
}
|
||||
|
||||
// ---- hook targets --------------------------------------------
|
||||
static auto BindFromHandlePath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalDetail.WBP_IngameMenu_PalBox_PalDetail_C:BindFromHandle");
|
||||
}
|
||||
|
||||
static auto SetEmptyPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalDetail.WBP_IngameMenu_PalBox_PalDetail_C:SetEmpty");
|
||||
}
|
||||
static auto UpdateStatusParameterPath() -> const TCHAR*
|
||||
{
|
||||
return STR("/Game/Pal/Blueprint/UI/UserInterface/IngameMenu/PalBox/WBP_IngameMenu_PalBox_PalDetail.WBP_IngameMenu_PalBox_PalDetail_C:Update Status Parameter");
|
||||
}
|
||||
|
||||
struct BindFromHandleFrame { UObject* targetHandle; }; // UPalIndividualCharacterHandle*
|
||||
struct UpdateStatusParameterFrame { UObject* individualParam; }; // UPalIndividualCharacterParameter*
|
||||
|
||||
|
||||
|
||||
private:
|
||||
auto WrapCanvas(const TCHAR* Name) const -> CanvasPanel
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? CanvasPanel{ O } : CanvasPanel{};
|
||||
}
|
||||
auto WrapText(const TCHAR* Name) const -> TextBlock
|
||||
{
|
||||
UObject* O = BoundObject(Name);
|
||||
return O ? TextBlock{ O } : TextBlock{};
|
||||
}
|
||||
|
||||
protected:
|
||||
PalDetailWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UserWidget(Obj, Expected, Tag) {
|
||||
}
|
||||
};
|
||||
}
|
||||
50
Api/PanelSlot.hpp
Normal file
50
Api/PanelSlot.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "UmgTypes.hpp"
|
||||
#include "UnrealWrapper.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// UPanelSlot.
|
||||
//
|
||||
// Worth saying plainly: this class exposes ZERO UFUNCTIONs. Its entire
|
||||
// surface is two Instanced UPROPERTYs, Parent and Content. So this
|
||||
// wrapper cannot use ProcessEvent at all -- it exists only to give
|
||||
// CanvasPanelSlot a place to inherit property access from, and to give
|
||||
// you something typed to hold when AddChild() hands back a UPanelSlot*
|
||||
// whose concrete type you do not know yet.
|
||||
//
|
||||
// If you were expecting a base class full of shared calls to inherit,
|
||||
// that expectation does not survive contact with the engine here. The
|
||||
// real shared behaviour lives in UnrealWrapper, not in UPanelSlot.
|
||||
// ------------------------------------------------------------------
|
||||
class PanelSlot : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.PanelSlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PanelSlot"); }
|
||||
|
||||
PanelSlot() = default;
|
||||
explicit PanelSlot(UObject* Obj) : PanelSlot(Obj, ClassOf<PanelSlot>(), DebugTag()) {}
|
||||
|
||||
// Raw UObject* rather than wrappers: Parent is a UPanelWidget and
|
||||
// Content is a UWidget, and wrapping either here would mean
|
||||
// including headers that (transitively) include this one.
|
||||
auto GetParentObject() const -> UObject*
|
||||
{
|
||||
auto Ptr = PropertyPtr<UObject*>(STR("Parent"));
|
||||
return Ptr ? *Ptr : nullptr;
|
||||
}
|
||||
|
||||
auto GetContentObject() const -> UObject*
|
||||
{
|
||||
auto Ptr = PropertyPtr<UObject*>(STR("Content"));
|
||||
return Ptr ? *Ptr : nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
PanelSlot(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UnrealWrapper(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
173
Api/PanelWidget.hpp
Normal file
173
Api/PanelWidget.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Widget.hpp"
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class PanelWidget : public Widget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.PanelWidget"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("PanelWidget"); }
|
||||
|
||||
PanelWidget() = default;
|
||||
explicit PanelWidget(UObject* Obj)
|
||||
: Widget(Obj, ClassOf<PanelWidget>(), DebugTag()) {}
|
||||
|
||||
auto GetChildrenCount() -> std::optional<int32>
|
||||
{
|
||||
return CallForReturn<int32>(STR("GetChildrenCount"));
|
||||
}
|
||||
auto HasAnyChildren() -> std::optional<bool>
|
||||
{
|
||||
return CallForReturn<bool>(STR("HasAnyChildren"));
|
||||
}
|
||||
|
||||
auto GetChildAt(int32 Index) -> Widget
|
||||
{
|
||||
struct P { int32 Index; UObject* ReturnValue; } p{ Index, nullptr };
|
||||
auto Result = CallForReturn(STR("GetChildAt"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? Widget{ *Result } : Widget{};
|
||||
}
|
||||
|
||||
auto GetChildIndex(UObject* Content) -> std::optional<int32>
|
||||
{
|
||||
struct P { UObject* Content; int32 ReturnValue; } p{ Content, -1 };
|
||||
return CallForReturn(STR("GetChildIndex"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto HasChild(UObject* Content) -> std::optional<bool>
|
||||
{
|
||||
struct P { UObject* Content; bool ReturnValue; } p{ Content, false };
|
||||
return CallForReturn(STR("HasChild"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto AddChild(UObject* Content) -> PanelSlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChild"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? PanelSlot{ *Result } : PanelSlot{};
|
||||
}
|
||||
|
||||
auto RemoveChild(UObject* Content) -> std::optional<bool>
|
||||
{
|
||||
struct P { UObject* Content; bool ReturnValue; } p{ Content, false };
|
||||
return CallForReturn(STR("RemoveChild"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto RemoveChildAt(int32 Index) -> std::optional<bool>
|
||||
{
|
||||
struct P { int32 Index; bool ReturnValue; } p{ Index, false };
|
||||
return CallForReturn(STR("RemoveChildAt"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
auto ClearChildren() -> bool { return CallVoid(STR("ClearChildren")); }
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Deliberately NOT a wrapper around the engine's GetAllChildren.
|
||||
//
|
||||
// That UFUNCTION returns TArray<UWidget*> by value. ProcessEvent
|
||||
// constructs that array into the frame with the engine's allocator
|
||||
// and then hands you ownership of the allocation. Getting that
|
||||
// wrong is either a leak on every call or a free across a module
|
||||
// boundary, and neither shows up as a crash near the cause.
|
||||
//
|
||||
// The count + index loop is two extra ProcessEvent calls per child
|
||||
// and zero ownership questions. If you are calling this per-frame
|
||||
// over a large panel, cache the result; if you are calling it once
|
||||
// in a Construct hook, the cost is irrelevant.
|
||||
// ------------------------------------------------------------------
|
||||
auto GetAllChildren() -> std::vector<Widget>
|
||||
{
|
||||
std::vector<Widget> Children;
|
||||
|
||||
auto Count = GetChildrenCount();
|
||||
if (!Count || *Count <= 0) return Children;
|
||||
|
||||
Children.reserve(static_cast<size_t>(*Count));
|
||||
for (int32 i = 0; i < *Count; ++i)
|
||||
{
|
||||
Widget Child = GetChildAt(i);
|
||||
if (Child) Children.push_back(Child);
|
||||
}
|
||||
return Children;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Children WITHOUT ProcessEvent.
|
||||
//
|
||||
// GetAllChildren() above costs 1 + N ProcessEvent calls. That is
|
||||
// fine once in a Construct hook and not fine as the inner loop of a
|
||||
// whole-tree walk, where it becomes 1 + N per PANEL.
|
||||
//
|
||||
// UPanelWidget::Slots is a TArray<UPanelSlot*> UPROPERTY (confirmed
|
||||
// in the dump: UHTHeaderDump/UMG/Public/PanelWidget.h line 14), and
|
||||
// UPanelSlot::Content is a UPROPERTY too. So the full child list is
|
||||
// reachable as pure memory reads. Slot order is child order -- the
|
||||
// engine's own GetChildAt(i) is Slots[i]->Content.
|
||||
//
|
||||
// Nothing here is copied out of engine memory except the pointers
|
||||
// themselves, which is what makes reading the TArray safe; see the
|
||||
// TArrayRead_ comment in UmgTypes.hpp for where that line is.
|
||||
//
|
||||
// Returns false if Slots could not be read or looked implausible,
|
||||
// WITHOUT touching Out -- so a caller can fall back to
|
||||
// GetAllChildren() and know it isn't double-appending.
|
||||
// ------------------------------------------------------------------
|
||||
static constexpr int32 kMaxPlausibleChildren = 4096;
|
||||
|
||||
auto TryGetChildObjects(std::vector<UObject*>& Out) const -> bool
|
||||
{
|
||||
auto* Slots = TryPropertyPtr<TArrayRead_<UObject*>>(STR("Slots"));
|
||||
if (!Slots) return false;
|
||||
|
||||
if (!Slots->IsPlausible(kMaxPlausibleChildren))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] 'Slots' does not look like a live TArray ")
|
||||
STR("(Num={}, Max={}, Data={}) -- falling back to ProcessEvent\n"),
|
||||
m_tag, Slots->Num, Slots->Max,
|
||||
Slots->Data ? STR("set") : STR("null"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Out.reserve(Out.size() + static_cast<size_t>(Slots->Num));
|
||||
for (int32 i = 0; i < Slots->Num; ++i)
|
||||
{
|
||||
UObject* Slot = Slots->Data[i];
|
||||
if (!Slot) continue;
|
||||
|
||||
// Read Content off the slot directly rather than through the
|
||||
// PanelSlot wrapper: the wrapper's IsA check would log on
|
||||
// anything unexpected, and this runs once per node of a
|
||||
// whole-tree walk.
|
||||
if (UObject* Content = RawObject{ Slot, STR("PanelSlot") }.TryObjectProperty(STR("Content")))
|
||||
{
|
||||
Out.push_back(Content);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Property read first, ProcessEvent second. Use this anywhere you
|
||||
// just want the children and don't care which route produced them.
|
||||
auto GetChildObjects() -> std::vector<UObject*>
|
||||
{
|
||||
std::vector<UObject*> Out;
|
||||
if (TryGetChildObjects(Out)) return Out;
|
||||
|
||||
for (Widget& Child : GetAllChildren())
|
||||
{
|
||||
if (UObject* O = Child.Get()) Out.push_back(O);
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
protected:
|
||||
PanelWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: Widget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
131
Api/SizeBox.hpp
Normal file
131
Api/SizeBox.hpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// USizeBox : public UContentWidget : public UPanelWidget.
|
||||
//
|
||||
// UContentWidget is not in this tree's header dump, so there is no
|
||||
// Api::ContentWidget rung to inherit from. This derives from
|
||||
// PanelWidget directly, skipping it. That is safe, not a shortcut:
|
||||
// Invoke() dispatches through Target->GetFunctionByNameInChain() on
|
||||
// the LIVE object's actual engine class chain (USizeBox ->
|
||||
// UContentWidget -> UPanelWidget -> UWidget -> ...), never through
|
||||
// this wrapper's C++ inheritance. Our wrapper hierarchy only
|
||||
// determines which typed accessors are in scope at the call site; it
|
||||
// has no effect on what ProcessEvent can reach. UContentWidget itself
|
||||
// adds no BlueprintCallable surface beyond what UPanelWidget already
|
||||
// gives you (its one member, GetContentSlot(), is a plain C++ helper
|
||||
// on the engine side, not a UFUNCTION), so nothing is actually lost
|
||||
// by skipping the rung -- there would be nothing to put in it.
|
||||
//
|
||||
// Every AddChild / GetChildAt / GetAllChildren / GetChildObjects call
|
||||
// from PanelWidget works unchanged. SizeBox only adds the
|
||||
// override/clear surface below.
|
||||
// ------------------------------------------------------------------
|
||||
class SizeBox : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.SizeBox"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("SizeBox"); }
|
||||
|
||||
SizeBox() = default;
|
||||
explicit SizeBox(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<SizeBox>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetWidthOverride(float InWidthOverride) -> bool
|
||||
{
|
||||
struct P { float InWidthOverride; } p{ InWidthOverride };
|
||||
return Call(STR("SetWidthOverride"), p);
|
||||
}
|
||||
auto SetHeightOverride(float InHeightOverride) -> bool
|
||||
{
|
||||
struct P { float InHeightOverride; } p{ InHeightOverride };
|
||||
return Call(STR("SetHeightOverride"), p);
|
||||
}
|
||||
auto SetMinDesiredWidth(float InMinDesiredWidth) -> bool
|
||||
{
|
||||
struct P { float InMinDesiredWidth; } p{ InMinDesiredWidth };
|
||||
return Call(STR("SetMinDesiredWidth"), p);
|
||||
}
|
||||
auto SetMinDesiredHeight(float InMinDesiredHeight) -> bool
|
||||
{
|
||||
struct P { float InMinDesiredHeight; } p{ InMinDesiredHeight };
|
||||
return Call(STR("SetMinDesiredHeight"), p);
|
||||
}
|
||||
auto SetMaxDesiredWidth(float InMaxDesiredWidth) -> bool
|
||||
{
|
||||
struct P { float InMaxDesiredWidth; } p{ InMaxDesiredWidth };
|
||||
return Call(STR("SetMaxDesiredWidth"), p);
|
||||
}
|
||||
auto SetMaxDesiredHeight(float InMaxDesiredHeight) -> bool
|
||||
{
|
||||
struct P { float InMaxDesiredHeight; } p{ InMaxDesiredHeight };
|
||||
return Call(STR("SetMaxDesiredHeight"), p);
|
||||
}
|
||||
auto SetMinAspectRatio(float InMinAspectRatio) -> bool
|
||||
{
|
||||
struct P { float InMinAspectRatio; } p{ InMinAspectRatio };
|
||||
return Call(STR("SetMinAspectRatio"), p);
|
||||
}
|
||||
auto SetMaxAspectRatio(float InMaxAspectRatio) -> bool
|
||||
{
|
||||
struct P { float InMaxAspectRatio; } p{ InMaxAspectRatio };
|
||||
return Call(STR("SetMaxAspectRatio"), p);
|
||||
}
|
||||
|
||||
// ---- clear (each Set* above also flips its bOverride_ flag on;
|
||||
// these are the only way to flip it back off -- there is no
|
||||
// "SetWidthOverride with override=false" overload on the engine
|
||||
// side, Clear* is a distinct UFUNCTION)
|
||||
|
||||
auto ClearWidthOverride() -> bool { return CallVoid(STR("ClearWidthOverride")); }
|
||||
auto ClearHeightOverride() -> bool { return CallVoid(STR("ClearHeightOverride")); }
|
||||
auto ClearMinDesiredWidth() -> bool { return CallVoid(STR("ClearMinDesiredWidth")); }
|
||||
auto ClearMinDesiredHeight() -> bool { return CallVoid(STR("ClearMinDesiredHeight")); }
|
||||
auto ClearMaxDesiredWidth() -> bool { return CallVoid(STR("ClearMaxDesiredWidth")); }
|
||||
auto ClearMaxDesiredHeight() -> bool { return CallVoid(STR("ClearMaxDesiredHeight")); }
|
||||
auto ClearMinAspectRatio() -> bool { return CallVoid(STR("ClearMinAspectRatio")); }
|
||||
auto ClearMaxAspectRatio() -> bool { return CallVoid(STR("ClearMaxAspectRatio")); }
|
||||
|
||||
// ---- property reads: the float values ------------------------
|
||||
|
||||
auto GetWidthOverride() const -> std::optional<float> { return PropertyValue<float>(STR("WidthOverride")); }
|
||||
auto GetHeightOverride() const -> std::optional<float> { return PropertyValue<float>(STR("HeightOverride")); }
|
||||
auto GetMinDesiredWidth() const -> std::optional<float> { return PropertyValue<float>(STR("MinDesiredWidth")); }
|
||||
auto GetMinDesiredHeight() const -> std::optional<float> { return PropertyValue<float>(STR("MinDesiredHeight")); }
|
||||
auto GetMaxDesiredWidth() const -> std::optional<float> { return PropertyValue<float>(STR("MaxDesiredWidth")); }
|
||||
auto GetMaxDesiredHeight() const -> std::optional<float> { return PropertyValue<float>(STR("MaxDesiredHeight")); }
|
||||
auto GetMinAspectRatio() const -> std::optional<float> { return PropertyValue<float>(STR("MinAspectRatio")); }
|
||||
auto GetMaxAspectRatio() const -> std::optional<float> { return PropertyValue<float>(STR("MaxAspectRatio")); }
|
||||
|
||||
// ---- property reads: the override flags -----------------------
|
||||
//
|
||||
// These are `uint8 bOverride_X : 1;` bitfields, EIGHT of them
|
||||
// declared back to back on the header. That is exactly the shape
|
||||
// where PropertyValue<uint8>/PropertyPtr<uint8> goes quietly
|
||||
// wrong: the compiler is free to pack all eight into one shared
|
||||
// byte, and a plain offset+size read would hand back that whole
|
||||
// byte -- every flag OR'd together -- for a call asking about
|
||||
// just one of them, with no warning, because the property exists
|
||||
// and 1 byte does equal sizeof(uint8).
|
||||
//
|
||||
// PropertyBoolValue (UnrealWrapper.hpp) exists specifically for
|
||||
// this: it reads FBoolProperty's own ByteOffset/FieldMask and
|
||||
// tests the correct single bit rather than the storage byte. Use
|
||||
// it for every bOverride_* flag; do not reach for PropertyValue
|
||||
// here even though it would compile.
|
||||
auto IsWidthOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_WidthOverride")); }
|
||||
auto IsHeightOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_HeightOverride")); }
|
||||
auto IsMinDesiredWidthOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MinDesiredWidth")); }
|
||||
auto IsMinDesiredHeightOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MinDesiredHeight")); }
|
||||
auto IsMaxDesiredWidthOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MaxDesiredWidth")); }
|
||||
auto IsMaxDesiredHeightOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MaxDesiredHeight")); }
|
||||
auto IsMinAspectRatioOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MinAspectRatio")); }
|
||||
auto IsMaxAspectRatioOverridden() const -> std::optional<bool> { return PropertyBoolValue(STR("bOverride_MaxAspectRatio")); }
|
||||
};
|
||||
}
|
||||
41
Api/SizeBoxSlot.hpp
Normal file
41
Api/SizeBoxSlot.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class SizeBoxSlot : public PanelSlot
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.SizeBoxSlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("SizeBoxSlot"); }
|
||||
|
||||
SizeBoxSlot() = default;
|
||||
explicit SizeBoxSlot(UObject* Obj)
|
||||
: PanelSlot(Obj, ClassOf<SizeBoxSlot>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetPadding(FMargin_ InPadding) -> bool
|
||||
{
|
||||
struct P { FMargin_ InPadding; } p{ InPadding };
|
||||
return Call(STR("SetPadding"), p);
|
||||
}
|
||||
auto SetHorizontalAlignment(EHorizontalAlignment_ InHorizontalAlignment) -> bool
|
||||
{
|
||||
struct P { EHorizontalAlignment_ InHorizontalAlignment; } p{ InHorizontalAlignment };
|
||||
return Call(STR("SetHorizontalAlignment"), p);
|
||||
}
|
||||
auto SetVerticalAlignment(EVerticalAlignment_ InVerticalAlignment) -> bool
|
||||
{
|
||||
struct P { EVerticalAlignment_ InVerticalAlignment; } p{ InVerticalAlignment };
|
||||
return Call(STR("SetVerticalAlignment"), p);
|
||||
}
|
||||
|
||||
// ---- property reads
|
||||
|
||||
auto GetPadding() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
|
||||
auto GetHorizontalAlignment()const -> std::optional<EHorizontalAlignment_> { return PropertyValue<EHorizontalAlignment_>(STR("HorizontalAlignment")); }
|
||||
auto GetVerticalAlignment() const -> std::optional<EVerticalAlignment_> { return PropertyValue<EVerticalAlignment_>(STR("VerticalAlignment")); }
|
||||
};
|
||||
}
|
||||
289
Api/TextBlock.hpp
Normal file
289
Api/TextBlock.hpp
Normal file
@@ -0,0 +1,289 @@
|
||||
#pragma once
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
|
||||
#include "TextLayoutWidget.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class TextBlock : public TextLayoutWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.TextBlock"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("TextBlock"); }
|
||||
|
||||
TextBlock() = default;
|
||||
explicit TextBlock(UObject* Obj)
|
||||
: TextLayoutWidget(Obj, ClassOf<TextBlock>(), DebugTag()) {}
|
||||
|
||||
// ---- text -----------------------------------------------------------
|
||||
//
|
||||
// Note:
|
||||
// SetText takes FText BY VALUE on the engine side (unlike UWidget::SetToolTipText, which takes const FText&).
|
||||
|
||||
auto SetText(const FText& InText) -> bool
|
||||
{
|
||||
struct P { FText InText; } p{ InText };
|
||||
return Call(STR("SetText"), p);
|
||||
}
|
||||
auto GetText() -> std::optional<FText>
|
||||
{
|
||||
return CallForReturn<FText>(STR("GetText"));
|
||||
}
|
||||
|
||||
// ---- colour ---------------------------------------------------------
|
||||
|
||||
auto SetColorAndOpacity(FSlateColor_ InColorAndOpacity) -> bool
|
||||
{
|
||||
struct P { FSlateColor_ InColorAndOpacity; } p{ InColorAndOpacity };
|
||||
return Call(STR("SetColorAndOpacity"), p);
|
||||
}
|
||||
// Convenience: the common case is a literal colour, styling mode 0.
|
||||
auto SetColor(FLinearColor_ Color) -> bool
|
||||
{
|
||||
return SetColorAndOpacity(FSlateColor_{ Color, static_cast<uint8>(ESlateColorStylingMode_::UseColor_Specified) });
|
||||
}
|
||||
auto SetOpacity(float InOpacity) -> bool
|
||||
{
|
||||
struct P { float InOpacity; } p{ InOpacity };
|
||||
return Call(STR("SetOpacity"), p);
|
||||
}
|
||||
|
||||
auto SetShadowOffset(FVector2D_ InShadowOffset) -> bool
|
||||
{
|
||||
struct P { FVector2D_ InShadowOffset; } p{ InShadowOffset };
|
||||
return Call(STR("SetShadowOffset"), p);
|
||||
}
|
||||
auto SetShadowColorAndOpacity(FLinearColor_ InColor) -> bool
|
||||
{
|
||||
struct P { FLinearColor_ InColor; } p{ InColor };
|
||||
return Call(STR("SetShadowColorAndOpacity"), p);
|
||||
}
|
||||
|
||||
// ---- layout / policy -------------------------------------------------
|
||||
|
||||
auto SetMinDesiredWidth(float InMinDesiredWidth) -> bool
|
||||
{
|
||||
struct P { float InMinDesiredWidth; } p{ InMinDesiredWidth };
|
||||
return Call(STR("SetMinDesiredWidth"), p);
|
||||
}
|
||||
auto SetAutoWrapText(bool InAutoTextWrap) -> bool
|
||||
{
|
||||
struct P { bool InAutoTextWrap; } p{ InAutoTextWrap };
|
||||
return Call(STR("SetAutoWrapText"), p);
|
||||
}
|
||||
auto SetTextTransformPolicy(ETextTransformPolicy_ InTransformPolicy) -> bool
|
||||
{
|
||||
struct P { ETextTransformPolicy_ InTransformPolicy; } p{ InTransformPolicy };
|
||||
return Call(STR("SetTextTransformPolicy"), p);
|
||||
}
|
||||
auto SetTextOverflowPolicy(ETextOverflowPolicy_ InOverflowPolicy) -> bool
|
||||
{
|
||||
struct P { ETextOverflowPolicy_ InOverflowPolicy; } p{ InOverflowPolicy };
|
||||
return Call(STR("SetTextOverflowPolicy"), p);
|
||||
}
|
||||
|
||||
// ---- materials -------------------------------------------------------
|
||||
// Plain object pointers. Nothing subtle here.
|
||||
|
||||
auto SetFontMaterial(UObject* InMaterial) -> bool
|
||||
{
|
||||
struct P { UObject* InMaterial; } p{ InMaterial };
|
||||
return Call(STR("SetFontMaterial"), p);
|
||||
}
|
||||
auto SetFontOutlineMaterial(UObject* InMaterial) -> bool
|
||||
{
|
||||
struct P { UObject* InMaterial; } p{ InMaterial };
|
||||
return Call(STR("SetFontOutlineMaterial"), p);
|
||||
}
|
||||
auto GetDynamicFontMaterial() -> std::optional<UObject*> { return CallForReturn<UObject*>(STR("GetDynamicFontMaterial")); }
|
||||
auto GetDynamicOutlineMaterial() -> std::optional<UObject*> { return CallForReturn<UObject*>(STR("GetDynamicOutlineMaterial")); }
|
||||
|
||||
// ---- property reads ---------------------------------------------------
|
||||
|
||||
auto GetMinDesiredWidth() const -> std::optional<float> { return PropertyValue<float>(STR("MinDesiredWidth")); }
|
||||
auto GetShadowOffset() const -> std::optional<FVector2D_> { return PropertyValue<FVector2D_>(STR("ShadowOffset")); }
|
||||
auto GetShadowColorAndOpacity()const -> std::optional<FLinearColor_> { return PropertyValue<FLinearColor_>(STR("ShadowColorAndOpacity")); }
|
||||
auto GetColorAndOpacity() const -> std::optional<FSlateColor_> { return PropertyValue<FSlateColor_>(STR("ColorAndOpacity")); }
|
||||
auto GetTextTransformPolicy() const -> std::optional<ETextTransformPolicy_>{ return PropertyValue<ETextTransformPolicy_>(STR("TextTransformPolicy")); }
|
||||
auto GetTextOverflowPolicy() const -> std::optional<ETextOverflowPolicy_> { return PropertyValue<ETextOverflowPolicy_>(STR("TextOverflowPolicy")); }
|
||||
auto GetSimpleTextMode() const -> std::optional<bool> { return PropertyValue<bool>(STR("bSimpleTextMode")); }
|
||||
auto GetWrapWithInvalidationPanel() const -> std::optional<bool> { return PropertyValue<bool>(STR("bWrapWithInvalidationPanel")); }
|
||||
|
||||
// ======================================================================
|
||||
// FONT
|
||||
//
|
||||
// SetFont(FSlateFontInfo) and SetStrikeBrush(FSlateBrush) are NOT
|
||||
// exposed as value-taking calls, and this is not caution for its
|
||||
// own sake:
|
||||
//
|
||||
// FSlateFontInfo holds TSharedPtr<const FCompositeFont>.
|
||||
// FSlateBrush holds FSlateResourceHandle, which holds a shared ref.
|
||||
//
|
||||
// Any POD mirror you build will copy those pointers without touching
|
||||
// their refcounts. Then either the engine's copy-assign increments a
|
||||
// count you never decrement (leak), or a destructor runs on a count
|
||||
// you never incremented (a free of a live font, surfacing as a crash
|
||||
// in the renderer somewhere unrelated). There is no struct layout
|
||||
// that fixes this -- the problem is that these types are not data.
|
||||
//
|
||||
// What works instead: mutate the live struct in place through
|
||||
// reflection. No copy is ever made, so no refcount is ever touched.
|
||||
// ======================================================================
|
||||
|
||||
// Reads the current font size. Whether the engine stores it as float
|
||||
// or int32 varies by engine version and both are 4 bytes, so the
|
||||
// size check cannot tell them apart. This logs both readings once --
|
||||
// exactly one of them will be a sane font size (something like 12 to
|
||||
// 48); that tells you which typed setter to call. Run it once, then
|
||||
// delete the call.
|
||||
auto LogFontSize() const -> void
|
||||
{
|
||||
void* SizePtr = FontMemberPtr(STR("Size"), 4);
|
||||
if (!SizePtr) return;
|
||||
|
||||
float AsFloat = *reinterpret_cast<float*>(SizePtr);
|
||||
int32 AsInt = *reinterpret_cast<int32*>(SizePtr);
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[TextBlock] Font.Size reads as float={} / int32={} -- ")
|
||||
STR("whichever looks like a font size is the real type\n"),
|
||||
AsFloat, AsInt);
|
||||
}
|
||||
|
||||
auto SetFontSizeFloat(float NewSize) -> bool
|
||||
{
|
||||
if (void* P = FontMemberPtr(STR("Size"), 4)) { *reinterpret_cast<float*>(P) = NewSize; return PushFontToSlate(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SetFontSizeInt(int32 NewSize) -> bool
|
||||
{
|
||||
if (void* P = FontMemberPtr(STR("Size"), 4)) { *reinterpret_cast<int32*>(P) = NewSize; return PushFontToSlate(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SetFontLetterSpacing(int32 Value) -> bool
|
||||
{
|
||||
if (void* P = FontMemberPtr(STR("LetterSpacing"), 4)) { *reinterpret_cast<int32*>(P) = Value; return PushFontToSlate(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SetFontSkewAmount(float Value) -> bool
|
||||
{
|
||||
if (void* P = FontMemberPtr(STR("SkewAmount"), 4)) { *reinterpret_cast<float*>(P) = Value; return PushFontToSlate(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets the named typeface (e.g. "Bold", "Regular", "Italic") on the
|
||||
// live font. FName is a trivially-copyable index pair, so this one
|
||||
// is a safe direct write.
|
||||
auto SetFontTypeface(FName TypefaceName) -> bool
|
||||
{
|
||||
if (void* P = FontMemberPtr(STR("TypefaceFontName"), sizeof(FName)))
|
||||
{
|
||||
*reinterpret_cast<FName*>(P) = TypefaceName;
|
||||
return PushFontToSlate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Raw access, for anything not covered above. Returns null and logs
|
||||
// if the member is missing or is not the size you expected.
|
||||
auto FontMemberPtr(const TCHAR* MemberName, size_t ExpectedSize) const -> void*
|
||||
{
|
||||
if (!m_obj) return nullptr;
|
||||
|
||||
FProperty* FontProp = m_obj->GetPropertyByNameInChain(STR("Font"));
|
||||
if (!FontProp)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(STR("[TextBlock] no 'Font' property\n"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FStructProperty::GetStruct() gives the UScriptStruct whose
|
||||
// property chain describes FSlateFontInfo's members.
|
||||
auto* AsStruct = static_cast<FStructProperty*>(FontProp);
|
||||
UScriptStruct* FontStruct = AsStruct->GetStruct();
|
||||
if (!FontStruct)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(STR("[TextBlock] 'Font' is not a struct property\n"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The `TFieldRange iter = FontStruct->ForEachProperty();` that
|
||||
// used to sit here was a leftover from before this was a
|
||||
// range-for: it built a second, unused range object every call.
|
||||
FProperty* Member = nullptr;
|
||||
for (FProperty* Property : FontStruct->ForEachProperty())
|
||||
{
|
||||
if (Property && Property->GetName() == MemberName) { Member = Property; break; }
|
||||
}
|
||||
|
||||
if (!Member)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[TextBlock] FSlateFontInfo has no member '{}' on this build\n"), MemberName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(Member->GetSize()) != ExpectedSize)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[TextBlock] FSlateFontInfo::{} is {} bytes, expected {}\n"),
|
||||
MemberName, Member->GetSize(), ExpectedSize);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8* FontBase = reinterpret_cast<uint8*>(m_obj) + FontProp->GetOffset_Internal();
|
||||
return FontBase + Member->GetOffset_Internal();
|
||||
}
|
||||
|
||||
private:
|
||||
// ------------------------------------------------------------------
|
||||
// The in-place write above changes UTextBlock::Font but does not
|
||||
// reach the SSlateTextBlock -- that only happens inside
|
||||
// SynchronizeProperties, which the setters call and a raw write
|
||||
// does not. There is no UFUNCTION that forces a resync.
|
||||
//
|
||||
// So this calls SetFont with the params block pointing AT THE LIVE
|
||||
// Font ITSELF. The engine does Font = InFontInfo, a real C++
|
||||
// self-assignment (safe for TSharedPtr and FName), and then runs
|
||||
// the resync. Nothing is copied on our side, so nothing can get the
|
||||
// refcount wrong -- with one open question:
|
||||
//
|
||||
// Does UE4SS's ProcessEvent destruct the params buffer after the
|
||||
// call? If it does, this destructs the widget's live Font.
|
||||
//
|
||||
// Evidence it does not: your existing FTextFromString passes an
|
||||
// FString and reads back an FText through this same path, repeatedly,
|
||||
// without crashing. That is suggestive, not proof -- a refcount
|
||||
// underflow frees memory that stays readable until something else
|
||||
// claims it, so the crash lands far from the cause.
|
||||
//
|
||||
// This is the single highest-value thing to nail down in this
|
||||
// codebase: the answer unlocks FText, FSlateFontInfo, FSlateBrush
|
||||
// and TArray returns all at once. Test it in isolation: pass an
|
||||
// FString whose refcount you can watch, call through ProcessEvent,
|
||||
// and read the count after.
|
||||
// ------------------------------------------------------------------
|
||||
auto PushFontToSlate() -> bool
|
||||
{
|
||||
if (!m_obj) return false;
|
||||
|
||||
FProperty* FontProp = m_obj->GetPropertyByNameInChain(STR("Font"));
|
||||
if (!FontProp) return false;
|
||||
|
||||
void* LiveFont = reinterpret_cast<uint8*>(m_obj) + FontProp->GetOffset_Internal();
|
||||
|
||||
UFunction* Fn = m_obj->GetFunctionByNameInChain(STR("SetFont"));
|
||||
if (!Fn)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(STR("[TextBlock] SetFont not found\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_obj->ProcessEvent(Fn, LiveFont);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
68
Api/TextLayoutWidget.hpp
Normal file
68
Api/TextLayoutWidget.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// UTextLayoutWidget. One UFUNCTION (SetJustification) and a handful of
|
||||
// protected UPROPERTYs.
|
||||
//
|
||||
// "protected" is a C++ access specifier. It is invisible to reflection:
|
||||
// the properties are in the class's FProperty chain either way, and
|
||||
// PropertyPtr reaches them fine. Do not read protected as unreachable.
|
||||
//
|
||||
// AutoWrapText is uint8 : 1 and is therefore NOT exposed as a property
|
||||
// read -- see the bitfield note in UnrealWrapper.hpp. UTextBlock has a
|
||||
// SetAutoWrapText UFUNCTION; use that.
|
||||
// ------------------------------------------------------------------
|
||||
class TextLayoutWidget : public Widget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.TextLayoutWidget"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("TextLayoutWidget"); }
|
||||
|
||||
TextLayoutWidget() = default;
|
||||
explicit TextLayoutWidget(UObject* Obj)
|
||||
: Widget(Obj, ClassOf<TextLayoutWidget>(), DebugTag()) {}
|
||||
|
||||
auto SetJustification(ETextJustify_ InJustification) -> bool
|
||||
{
|
||||
struct P { ETextJustify_ InJustification; } p{ InJustification };
|
||||
return Call(STR("SetJustification"), p);
|
||||
}
|
||||
|
||||
// ---- property reads -------------------------------------------------
|
||||
auto GetJustification() const -> std::optional<ETextJustify_> { return PropertyValue<ETextJustify_>(STR("Justification")); }
|
||||
auto GetWrappingPolicy() const -> std::optional<ETextWrappingPolicy_>{ return PropertyValue<ETextWrappingPolicy_>(STR("WrappingPolicy")); }
|
||||
auto GetWrapTextAt() const -> std::optional<float> { return PropertyValue<float>(STR("WrapTextAt")); }
|
||||
auto GetLineHeightPercentage()const -> std::optional<float> { return PropertyValue<float>(STR("LineHeightPercentage")); }
|
||||
auto GetMargin() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Margin")); }
|
||||
|
||||
// ---- property writes ------------------------------------------------
|
||||
//
|
||||
// These have no UFUNCTION setter, so a direct write is the only
|
||||
// option -- and it will NOT reach the Slate widget until something
|
||||
// else triggers SynchronizeProperties. Set them before the widget
|
||||
// is added to a panel, or accept that they take effect late.
|
||||
auto SetWrapTextAt(float Value) -> bool
|
||||
{
|
||||
if (float* P = PropertyPtr<float>(STR("WrapTextAt"))) { *P = Value; return true; }
|
||||
return false;
|
||||
}
|
||||
auto SetLineHeightPercentage(float Value) -> bool
|
||||
{
|
||||
if (float* P = PropertyPtr<float>(STR("LineHeightPercentage"))) { *P = Value; return true; }
|
||||
return false;
|
||||
}
|
||||
auto SetMargin(FMargin_ Value) -> bool
|
||||
{
|
||||
if (FMargin_* P = PropertyPtr<FMargin_>(STR("Margin"))) { *P = Value; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
TextLayoutWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: Widget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
156
Api/UmgTypes.hpp
Normal file
156
Api/UmgTypes.hpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <Unreal/UObject.hpp>
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
using namespace RC;
|
||||
using namespace RC::Unreal;
|
||||
|
||||
template <typename T>
|
||||
struct TArrayRead_
|
||||
{
|
||||
T* Data{};
|
||||
int32 Num{};
|
||||
int32 Max{};
|
||||
|
||||
auto IsPlausible(int32 SanityLimit) const -> bool
|
||||
{
|
||||
if (Num < 0 || Max < 0 || Num > Max) return false;
|
||||
if (Num > SanityLimit) return false;
|
||||
return Num == 0 || Data != nullptr;
|
||||
}
|
||||
};
|
||||
struct FVector2D_ { double X{}; double Y{}; };
|
||||
struct FVector_ { double X{}; double Y{}; double Z{}; };
|
||||
struct FMargin_ { float Left{}; float Top{}; float Right{}; float Bottom{}; };
|
||||
struct FAnchors_ { FVector2D_ Minimum{}; FVector2D_ Maximum{}; };
|
||||
|
||||
// GUESS (field order).
|
||||
struct FAnchorData_ { FMargin_ Offsets{}; FAnchors_ Anchors{}; FVector2D_ Alignment{}; };
|
||||
|
||||
// GUESS (field order). Engine source order is Translation, Scale, Shear, Angle.
|
||||
struct FWidgetTransform_
|
||||
{
|
||||
FVector2D_ Translation{};
|
||||
FVector2D_ Scale{ 1.0, 1.0 };
|
||||
FVector2D_ Shear{};
|
||||
float Angle{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum class ESlateVisibility_ : uint8
|
||||
{
|
||||
Visible = 0,
|
||||
Collapsed = 1,
|
||||
Hidden = 2,
|
||||
HitTestInvisible = 3,
|
||||
SelfHitTestInvisible = 4,
|
||||
};
|
||||
|
||||
enum class EWidgetClipping_ : uint8
|
||||
{
|
||||
Inherit = 0,
|
||||
ClipToBounds = 1,
|
||||
ClipToBoundsWithoutIntersecting = 2,
|
||||
ClipToBoundsAlways = 3,
|
||||
OnDemand = 4,
|
||||
};
|
||||
|
||||
enum class EUINavigation_ : uint8
|
||||
{
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Up = 2,
|
||||
Down = 3,
|
||||
Next = 4,
|
||||
Previous = 5,
|
||||
Num = 6,
|
||||
Invalid = 7,
|
||||
};
|
||||
|
||||
enum class EUINavigationRule_ : uint8
|
||||
{
|
||||
Escape = 0,
|
||||
Explicit = 1,
|
||||
Wrap = 2,
|
||||
Stop = 3,
|
||||
Custom = 4,
|
||||
CustomBoundary = 5,
|
||||
Invalid = 6,
|
||||
};
|
||||
|
||||
using EMouseCursorByte_ = uint8;
|
||||
|
||||
enum class EHorizontalAlignment_ : uint8
|
||||
{
|
||||
Fill = 0,
|
||||
Left = 1,
|
||||
Center = 2,
|
||||
Right = 3,
|
||||
};
|
||||
|
||||
enum class EVerticalAlignment_ : uint8
|
||||
{
|
||||
Fill = 0,
|
||||
Top = 1,
|
||||
Center = 2,
|
||||
Bottom = 3,
|
||||
};
|
||||
|
||||
struct FLinearColor_ { float R{}; float G{}; float B{}; float A{ 1.0f }; };
|
||||
|
||||
struct FSlateColor_
|
||||
{
|
||||
FLinearColor_ SpecifiedColor{};
|
||||
uint8 ColorUseRule{ 0 }; // UseColor_Specified
|
||||
};
|
||||
|
||||
enum class ESlateColorStylingMode_ : uint8
|
||||
{
|
||||
UseColor_Specified = 0,
|
||||
UseColor_Specified_Link = 1,
|
||||
UseColor_Foreground = 2,
|
||||
UseColor_Foreground_Subdued = 3,
|
||||
};
|
||||
|
||||
enum class ETextTransformPolicy_ : uint8
|
||||
{
|
||||
None = 0,
|
||||
ToLower = 1,
|
||||
ToUpper = 2,
|
||||
};
|
||||
|
||||
enum class ETextOverflowPolicy_ : uint8
|
||||
{
|
||||
Clip = 0,
|
||||
Ellipsis = 1,
|
||||
};
|
||||
|
||||
enum class ETextWrappingPolicy_ : uint8
|
||||
{
|
||||
DefaultWrapping = 0,
|
||||
AllowPerCharacterWrapping = 1,
|
||||
};
|
||||
|
||||
// TEnumAsByte<ETextJustify::Type>
|
||||
enum class ETextJustify_ : uint8
|
||||
{
|
||||
Left = 0,
|
||||
Center = 1,
|
||||
Right = 2,
|
||||
};
|
||||
|
||||
enum class ESlateSizeRule_ : uint8
|
||||
{
|
||||
Automatic = 0,
|
||||
Fill = 1,
|
||||
};
|
||||
|
||||
struct FSlateChildSize_
|
||||
{
|
||||
float Value{};
|
||||
ESlateSizeRule_ SizeRule{ ESlateSizeRule_::Automatic };
|
||||
};
|
||||
}
|
||||
476
Api/UnrealWrapper.hpp
Normal file
476
Api/UnrealWrapper.hpp
Normal file
@@ -0,0 +1,476 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include <DynamicOutput/DynamicOutput.hpp>
|
||||
|
||||
#include <Unreal/UObjectGlobals.hpp>
|
||||
#include <Unreal/UObject.hpp>
|
||||
#include <Unreal/UClass.hpp>
|
||||
#include <Unreal/CoreUObject/UObject/Class.hpp>
|
||||
#include <Unreal/CoreUObject/UObject/UnrealType.hpp>
|
||||
|
||||
#include <Reflection/UnrealApiCall.hpp>
|
||||
#include <Reflection/UnrealObjectRef.hpp>
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
using namespace RC;
|
||||
using namespace RC::Unreal;
|
||||
|
||||
namespace Detail
|
||||
{
|
||||
struct Tag_Umg {};
|
||||
|
||||
// ONE dispatcher per return type, shared by every wrapper in this
|
||||
// tree -- not one per wrapper class.
|
||||
//
|
||||
// The reason this is correct: UnrealApiCall::Invoke resolves the
|
||||
// function with Target->GetFunctionByNameInChain(), which walks the
|
||||
// *live object's* class chain. It never touches the ClassPath the
|
||||
// UnrealApiCall was constructed with. So the class path is only
|
||||
// used by ResolveClass/FindInstance/ConstructInstance, none of
|
||||
// which we call here. Giving each wrapper class its own set of
|
||||
// per-return-type statics would buy nothing but N redundant
|
||||
// StaticFindObject calls.
|
||||
//
|
||||
// The corollary matters for the inheritance below: a base-class
|
||||
// wrapper calling a function on a derived object works, because
|
||||
// dispatch is by the object's chain, not by the wrapper's type.
|
||||
//
|
||||
// The path below is a placeholder that is never resolved. If you
|
||||
// ever call ResolveClass on one of these, it will resolve UObject
|
||||
// and the resulting IsA check will be meaningless.
|
||||
template <typename R>
|
||||
inline auto Api() -> Reflection::UnrealApiCall<Tag_Umg, R>&
|
||||
{
|
||||
static Reflection::UnrealApiCall<Tag_Umg, R> Instance{
|
||||
STR("/Script/CoreUObject.Object"), STR("UMG") };
|
||||
return Instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolves and caches the UClass for a wrapper type T.
|
||||
// T must expose static ClassPath() and static DebugTag().
|
||||
template <typename T>
|
||||
inline auto ClassOf() -> UClass*
|
||||
{
|
||||
static Reflection::UnrealObjectRef Ref{ T::ClassPath(), T::DebugTag() };
|
||||
return static_cast<UClass*>(Ref.Get());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Base for every UObject wrapper here.
|
||||
//
|
||||
// Lifetime: holds a raw UObject* and does nothing to keep it alive.
|
||||
// These wrappers are meant to live inside one hook callback. Storing
|
||||
// one across frames means storing a pointer the GC can invalidate,
|
||||
// and a freed UObject slot will eventually be reused by a different
|
||||
// object -- so a stale pointer does not read as null, it reads as
|
||||
// some other widget.
|
||||
// ------------------------------------------------------------------
|
||||
class UnrealWrapper
|
||||
{
|
||||
public:
|
||||
auto IsValid() const -> bool { return m_obj != nullptr; }
|
||||
explicit operator bool() const { return IsValid(); }
|
||||
auto Get() const -> UObject* { return m_obj; }
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Diagnostics.
|
||||
//
|
||||
// These were copy-pasted identically into PalDetailWidget and
|
||||
// MainMenuPalWidget, differing only in the log prefix -- which
|
||||
// m_tag already carries. Hoisted here so every wrapper has them and
|
||||
// there is one copy to fix when the reflection API shifts.
|
||||
//
|
||||
// LogFunctionSignature is the tool for settling a frame layout
|
||||
// without guessing: it prints the engine's own offsets and sizes
|
||||
// for a UFunction's parameter block, which is exactly what the
|
||||
// hand-written ParamsStructs in this codebase have to match. Run it
|
||||
// before trusting a new struct, not after it misbehaves.
|
||||
// ------------------------------------------------------------------
|
||||
auto LogFunctionSignature(const TCHAR* FunctionName) const -> void
|
||||
{
|
||||
if (!m_obj) return;
|
||||
|
||||
UFunction* Fn = m_obj->GetFunctionByNameInChain(FunctionName);
|
||||
if (!Fn)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] '{}' is not in the chain of {}\n"),
|
||||
m_tag, FunctionName, m_obj->GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] {} -- frame is {} bytes:\n"),
|
||||
m_tag, FunctionName, Fn->GetStructureSize());
|
||||
|
||||
for (FProperty* Prop : Fn->ForEachProperty())
|
||||
{
|
||||
if (!Prop) continue;
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR(" [0x{:X}] {} ({} bytes)\n"),
|
||||
Prop->GetOffset_Internal(), Prop->GetName(), Prop->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Every UPROPERTY on the object's class chain, with offset and
|
||||
// size. The companion to the above for the property side: this is
|
||||
// how you find out what a widget is actually called internally, or
|
||||
// whether the field you want is a promoted variable at all.
|
||||
auto LogProperties() const -> void
|
||||
{
|
||||
if (!m_obj) return;
|
||||
|
||||
UClass* Cls = m_obj->GetClassPrivate();
|
||||
if (!Cls)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] no class on {}\n"), m_tag, m_obj->GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] properties of {}:\n"), m_tag, m_obj->GetFullName());
|
||||
|
||||
for (FProperty* Prop : Cls->ForEachPropertyInChain())
|
||||
{
|
||||
if (!Prop) continue;
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR(" [0x{:X}] {} ({} bytes)\n"),
|
||||
Prop->GetOffset_Internal(), Prop->GetName(), Prop->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
UnrealWrapper() = default;
|
||||
|
||||
UnrealWrapper(UObject* Obj, UClass* Expected, const TCHAR* DebugTag)
|
||||
: m_tag(DebugTag ? DebugTag : STR("Wrapper"))
|
||||
{
|
||||
if (!Obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] constructed from null object\n"), m_tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Expected)
|
||||
{
|
||||
// Class path did not resolve. Accept the object rather than
|
||||
// reject it -- Invoke dispatches by the object's own chain,
|
||||
// so calls will still work; you just lost the type check.
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] class unresolved -- accepting {} unchecked\n"),
|
||||
m_tag, Obj->GetFullName());
|
||||
m_obj = Obj;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Obj->IsA(Expected))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] {} is not of the expected class\n"),
|
||||
m_tag, Obj->GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
m_obj = Obj;
|
||||
}
|
||||
|
||||
struct NoParams {};
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Deliberate opt-out of the class check.
|
||||
//
|
||||
// The (Obj, Expected, Tag) constructor below treats a null Expected
|
||||
// as "the ClassPath failed to resolve" and logs a Warning about it,
|
||||
// which is right for that case and wrong for a caller that never
|
||||
// had a ClassPath to begin with (LooseObject). The difference
|
||||
// matters more than it looks: that Warning calls
|
||||
// Obj->GetFullName(), which walks the whole outer chain calling
|
||||
// GetName() -- an FName::ToString, i.e. a ProcessEvent -- at every
|
||||
// level, then formats the line and writes it to every open output
|
||||
// device, because Output::send has no level filter (see
|
||||
// Static/LogGate.hpp).
|
||||
//
|
||||
// Paid once at startup for a genuinely unresolved class, that is
|
||||
// fine. Paid on every LooseObject construction on a per-bind path,
|
||||
// it is a dozen-plus ProcessEvent calls and a disk write per pal
|
||||
// selection. This tag ctor says "unchecked on purpose" and stays
|
||||
// silent.
|
||||
//
|
||||
// It does NOT weaken anything: LooseObject was already accepting
|
||||
// the object unchecked. The only thing removed is a diagnostic
|
||||
// that was describing a condition that wasn't happening.
|
||||
// ------------------------------------------------------------------
|
||||
struct UncheckedTag { explicit UncheckedTag() = default; };
|
||||
|
||||
UnrealWrapper(UObject* Obj, UncheckedTag, const TCHAR* DebugTag)
|
||||
: m_obj(Obj), m_tag(DebugTag ? DebugTag : STR("Wrapper"))
|
||||
{
|
||||
}
|
||||
|
||||
// Void-returning UFUNCTIONs only. Detail::Api<void> will log if the
|
||||
// engine function actually has a ReturnValue property, which is the
|
||||
// signal that you meant to use CallForReturn.
|
||||
template <typename ParamsStruct>
|
||||
auto Call(const TCHAR* FunctionName, ParamsStruct& Params) const -> bool
|
||||
{
|
||||
if (!m_obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] '{}' on an invalid wrapper\n"), m_tag, FunctionName);
|
||||
return false;
|
||||
}
|
||||
return Detail::Api<void>().Invoke(m_obj, FunctionName, Params);
|
||||
}
|
||||
|
||||
auto CallVoid(const TCHAR* FunctionName) const -> bool
|
||||
{
|
||||
NoParams p{};
|
||||
return Call(FunctionName, p);
|
||||
}
|
||||
|
||||
// Zero-parameter getter.
|
||||
template <typename R>
|
||||
auto CallForReturn(const TCHAR* FunctionName) const -> std::optional<R>
|
||||
{
|
||||
struct Params { R ReturnValue; };
|
||||
Params p{};
|
||||
return CallForReturn<Params, R>(FunctionName, p, &Params::ReturnValue);
|
||||
}
|
||||
|
||||
// Getter with parameters. ReturnValue must be the LAST member of
|
||||
// ParamsStruct, matching the engine's frame layout.
|
||||
template <typename ParamsStruct, typename R>
|
||||
auto CallForReturn(const TCHAR* FunctionName, ParamsStruct& Params,
|
||||
R ParamsStruct::* ReturnMember) const -> std::optional<R>
|
||||
{
|
||||
if (!m_obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] '{}' on an invalid wrapper\n"), m_tag, FunctionName);
|
||||
return std::nullopt;
|
||||
}
|
||||
return Detail::Api<R>().InvokeForReturn(m_obj, FunctionName, Params, ReturnMember);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Direct UPROPERTY access.
|
||||
//
|
||||
// Cheaper than ProcessEvent and the only option for properties with
|
||||
// no UFUNCTION accessor (UPanelSlot::Parent and ::Content, for
|
||||
// instance -- that class exposes zero UFUNCTIONs).
|
||||
//
|
||||
// Two things this does NOT handle:
|
||||
// 1. Bitfield bools (uint8 bFoo : 1). FProperty::GetSize() returns
|
||||
// 1 for those too, so the size check passes and you read the
|
||||
// whole byte -- every flag packed into it, not just yours.
|
||||
// Use the UFUNCTION getter for those. That is why UWidget's
|
||||
// bIsEnabled/bIsVariable are not exposed here.
|
||||
// 2. Writes. Poking a property directly skips the setter's
|
||||
// SynchronizeProperties call, so the Slate widget will not
|
||||
// update. Read by property, write by UFUNCTION.
|
||||
//
|
||||
// UE4SS API name check: GetPropertyByNameInChain and
|
||||
// GetOffset_Internal are what I expect this version to expose;
|
||||
// verify against your UObject.hpp / UnrealType.hpp before assuming
|
||||
// this compiles as written.
|
||||
// ------------------------------------------------------------------
|
||||
template <typename T>
|
||||
auto PropertyPtr(const TCHAR* PropertyName) const -> T*
|
||||
{
|
||||
if (!m_obj) return nullptr;
|
||||
|
||||
FProperty* Prop = m_obj->GetPropertyByNameInChain(PropertyName);
|
||||
if (!Prop)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] property '{}' not found on {}\n"),
|
||||
m_tag, PropertyName, m_obj->GetFullName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(Prop->GetSize()) != sizeof(T))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] property '{}' is {} bytes engine-side, read as {} bytes\n"),
|
||||
m_tag, PropertyName, Prop->GetSize(), sizeof(T));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<T*>(
|
||||
reinterpret_cast<uint8*>(m_obj) + Prop->GetOffset_Internal());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// PropertyPtr for the case where ABSENCE IS AN EXPECTED ANSWER, not
|
||||
// a mistake.
|
||||
//
|
||||
// PropertyPtr logs a Warning when the property is missing, and that
|
||||
// log line calls Obj->GetFullName() -- an outer-chain walk with an
|
||||
// FName::ToString at every level (see the UncheckedTag note above).
|
||||
// That is the right behaviour when you are naming a property you
|
||||
// believe exists and want to hear about it if you are wrong.
|
||||
//
|
||||
// It is the wrong behaviour when you are PROBING: "does this widget
|
||||
// happen to be a UUserWidget, i.e. does it have a WidgetTree?" is a
|
||||
// question asked once per node during a tree walk, and the answer is
|
||||
// "no" for almost every node. Logging that is not a diagnostic, it's
|
||||
// a per-node GetFullName() plus a disk write.
|
||||
//
|
||||
// A size mismatch still logs. That one is never an expected answer:
|
||||
// the property exists but is not the shape you think it is, which is
|
||||
// the silent-corruption case, not the absent case.
|
||||
// ------------------------------------------------------------------
|
||||
template <typename T>
|
||||
auto TryPropertyPtr(const TCHAR* PropertyName) const -> T*
|
||||
{
|
||||
if (!m_obj) return nullptr;
|
||||
|
||||
FProperty* Prop = m_obj->GetPropertyByNameInChain(PropertyName);
|
||||
if (!Prop) return nullptr;
|
||||
|
||||
if (static_cast<size_t>(Prop->GetSize()) != sizeof(T))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] property '{}' is {} bytes engine-side, read as {} bytes\n"),
|
||||
m_tag, PropertyName, Prop->GetSize(), sizeof(T));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<T*>(
|
||||
reinterpret_cast<uint8*>(m_obj) + Prop->GetOffset_Internal());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto PropertyValue(const TCHAR* PropertyName) const -> std::optional<T>
|
||||
{
|
||||
if (T* Ptr = PropertyPtr<T>(PropertyName)) return *Ptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Convenience for the most common property shape in this tree: an
|
||||
// ObjectProperty read back as a bare pointer. Silent on absence.
|
||||
auto TryObjectProperty(const TCHAR* PropertyName) const -> UObject*
|
||||
{
|
||||
auto Ptr = TryPropertyPtr<UObject*>(PropertyName);
|
||||
return Ptr ? *Ptr : nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// For `uint8 bFoo : 1;` UPROPERTY bitfields specifically. Do NOT
|
||||
// reach for PropertyValue<uint8>/PropertyPtr<uint8> on one of
|
||||
// these -- it is the wrong tool and it does not fail loudly.
|
||||
//
|
||||
// UHT reflects a bitfield as an FBoolProperty whose GetSize()
|
||||
// reports the storage unit (1 byte), not "1 bit" -- there is no
|
||||
// size check that can tell a lone byte-backed bool apart from one
|
||||
// of several single-bit flags packed into a shared byte. When a
|
||||
// header declares several `uint8 bX : 1;` fields back to back
|
||||
// (exactly the bOverride_* run on USizeBox), the compiler is free
|
||||
// to pack all of them into one physical byte. PropertyPtr<uint8>
|
||||
// would then hand back that WHOLE byte -- every packed flag
|
||||
// OR'd together -- for a call that named only one of them, and
|
||||
// both the found-a-property and the size-matches checks would
|
||||
// pass. That is a silent wrong answer, not a logged failure.
|
||||
//
|
||||
// FBoolProperty carries the two pieces of information that make
|
||||
// this correct: GetByteOffset(), the offset of the specific byte
|
||||
// within the struct (added ON TOP of the base FProperty offset
|
||||
// ContainerPtrToValuePtr already applies -- see
|
||||
// FBoolProperty::GetPropertyValue in UnrealType.hpp, which is
|
||||
// exactly the two-offset addition this function replicates), and
|
||||
// GetFieldMask(), the single bit within that byte belonging to
|
||||
// this specific flag. Test the bit, don't read the byte.
|
||||
auto PropertyBoolValue(const TCHAR* PropertyName) const -> std::optional<bool>
|
||||
{
|
||||
if (!m_obj) return std::nullopt;
|
||||
|
||||
FProperty* Prop = m_obj->GetPropertyByNameInChain(PropertyName);
|
||||
if (!Prop)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] property '{}' not found on {}\n"),
|
||||
m_tag, PropertyName, m_obj->GetFullName());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop);
|
||||
if (!BoolProp)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] property '{}' is not a bool/bitfield property on {} -- ")
|
||||
STR("use PropertyValue<T> for a plain field instead\n"),
|
||||
m_tag, PropertyName, m_obj->GetFullName());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const uint8* Base = reinterpret_cast<const uint8*>(m_obj)
|
||||
+ Prop->GetOffset_Internal() + BoolProp->GetByteOffset();
|
||||
return (*Base & BoolProp->GetFieldMask()) != 0;
|
||||
}
|
||||
|
||||
UObject* m_obj{};
|
||||
const TCHAR* m_tag{ STR("Wrapper") };
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// RawObject -- the wrapper for objects this tree has no class for.
|
||||
//
|
||||
// Every helper that wants to read a property off an object it cannot
|
||||
// name a ClassPath for was previously spelling this inline as a local
|
||||
// struct deriving UnrealWrapper with Expected=nullptr (see the old
|
||||
// WidgetTree::FromUserWidget). That spelling takes the "class path did
|
||||
// not resolve" branch of the checking constructor, which emits a
|
||||
// Warning and calls GetFullName() EVERY TIME -- on a per-bind path,
|
||||
// that is the exact cost the UncheckedTag comment above exists to
|
||||
// avoid.
|
||||
//
|
||||
// This uses UncheckedTag, so it is silent by construction. It is the
|
||||
// same idea as Pal/PalIndividual.hpp's LooseObject, hoisted to where
|
||||
// the UMG side can reach it without including the Pal headers.
|
||||
// ------------------------------------------------------------------
|
||||
class RawObject : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
RawObject() = default;
|
||||
explicit RawObject(UObject* Obj, const TCHAR* Tag = STR("Raw"))
|
||||
: UnrealWrapper(Obj, UncheckedTag{}, Tag) {}
|
||||
|
||||
using UnrealWrapper::Call;
|
||||
using UnrealWrapper::CallVoid;
|
||||
using UnrealWrapper::CallForReturn;
|
||||
using UnrealWrapper::PropertyPtr;
|
||||
using UnrealWrapper::PropertyValue;
|
||||
using UnrealWrapper::TryPropertyPtr;
|
||||
using UnrealWrapper::TryObjectProperty;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Cheap "is this object one of ours" probe.
|
||||
//
|
||||
// Wrapper constructors log when the IsA check fails, which is right for
|
||||
// "I expected a CanvasPanel and got something else" and wrong for "walk
|
||||
// this tree and tell me which nodes are panels." Use this to ask first,
|
||||
// then construct.
|
||||
//
|
||||
// A false here can also mean the ClassPath never resolved. That is
|
||||
// deliberately NOT treated as "yes" -- unlike the wrapper constructors,
|
||||
// which accept unchecked on an unresolved class because refusing would
|
||||
// break a call that would otherwise have worked. Here the answer feeds
|
||||
// a branch, so guessing "yes" would send a non-panel down the panel
|
||||
// path. ClassOf<T>() logs once on the failed resolve either way.
|
||||
// ------------------------------------------------------------------
|
||||
template <typename T>
|
||||
inline auto ObjectIsA(UObject* Obj) -> bool
|
||||
{
|
||||
if (!Obj) return false;
|
||||
UClass* Cls = ClassOf<T>();
|
||||
return Cls && Obj->IsA(Cls);
|
||||
}
|
||||
}
|
||||
175
Api/UserWidget.hpp
Normal file
175
Api/UserWidget.hpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "Widget.hpp"
|
||||
#include "WidgetTree.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// UUserWidget. Base for every WBP_..._C generated class.
|
||||
//
|
||||
// NAMESPACE: this file previously declared itself in
|
||||
// PalStatusOverlay::Api while every other header in the tree is in
|
||||
// PalUIExtension::Api. Nothing included it, so nothing caught it -- the
|
||||
// first #include from dllmain.cpp would have failed to find any of
|
||||
// these types. Fixed; if you have local code referring to
|
||||
// PalStatusOverlay::Api::UserWidget, that is why it no longer resolves.
|
||||
// Static/WidgetRegistry.hpp had the same split and is fixed to match.
|
||||
//
|
||||
// GetWidgetFromName is NOT available on this build. It does not appear
|
||||
// in the UserWidget.h you pulled from UMG.zip, which is UHT's own
|
||||
// generated listing of every UFUNCTION this class has. Stock UE ships
|
||||
// it BlueprintPure; this build either stripped it or the dump is
|
||||
// incomplete.
|
||||
//
|
||||
// That is no longer an obstacle rather than a caveat: FindWidget below
|
||||
// walks the widget tree instead of calling it. See WidgetTree.hpp for
|
||||
// why walking beats both the missing UFUNCTION and the global-object
|
||||
// scan that UnrealApiCall::FindInstance does.
|
||||
// ------------------------------------------------------------------
|
||||
class UserWidget : public Widget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.UserWidget"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("UserWidget"); }
|
||||
|
||||
UserWidget() = default;
|
||||
explicit UserWidget(UObject* Obj)
|
||||
: Widget(Obj, ClassOf<UserWidget>(), DebugTag()) {}
|
||||
|
||||
// ---- the widget tree ------------------------------------------
|
||||
//
|
||||
// WidgetTree is a plain UPROPERTY (Transient/DuplicateTransient,
|
||||
// not a bitfield) -- direct property read, no ProcessEvent frame.
|
||||
auto GetWidgetTree() const -> WidgetTree
|
||||
{
|
||||
return WidgetTree::FromUserWidget(m_obj);
|
||||
}
|
||||
|
||||
// Find a widget by its designer name anywhere in THIS widget's
|
||||
// tree. Replaces the GetWidgetFromName call this class used to make
|
||||
// opportunistically.
|
||||
//
|
||||
// Scope defaults to ThisTreeOnly -- names are unique per tree, not
|
||||
// globally, and a nested WBP has its own "Text_PalName" just like
|
||||
// its parent does. See WidgetTree::EScope.
|
||||
auto FindWidget(const TCHAR* Name,
|
||||
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> Widget
|
||||
{
|
||||
return GetWidgetTree().Find(Name, Scope);
|
||||
}
|
||||
|
||||
auto FindWidgetObject(const TCHAR* Name,
|
||||
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> UObject*
|
||||
{
|
||||
return GetWidgetTree().FindObjectByName(Name, Scope);
|
||||
}
|
||||
|
||||
// Typed variant: UserWidget{W}.FindWidgetAs<CanvasPanel>(STR("Canvas_Base"))
|
||||
template <typename T>
|
||||
auto FindWidgetAs(const TCHAR* Name,
|
||||
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T
|
||||
{
|
||||
return GetWidgetTree().FindAs<T>(Name, Scope);
|
||||
}
|
||||
|
||||
// ---- bound widget properties ------------------------------------
|
||||
//
|
||||
// The OTHER way to reach a child, and the cheaper one when it
|
||||
// applies. UMG promotes any widget marked "Is Variable" in the
|
||||
// designer to a UPROPERTY on the generated class, so the pointer
|
||||
// sits at a fixed offset -- no walk at all. PalDetailWidget's
|
||||
// CanvasBase()/TextPalName()/etc. are exactly this.
|
||||
//
|
||||
// The catch, and the reason FindWidget still earns its place: this
|
||||
// only works for widgets that were marked as variables. Anything
|
||||
// left unpromoted (most auto-named CanvasPanel_N containers) has no
|
||||
// property and can only be found by walking.
|
||||
auto BoundObject(const TCHAR* Name) const -> UObject*
|
||||
{
|
||||
return TryObjectProperty(Name);
|
||||
}
|
||||
|
||||
// Property first, tree walk second. Use this when you do not know
|
||||
// whether a given child was promoted to a variable, and do not want
|
||||
// to care.
|
||||
auto ChildObject(const TCHAR* Name,
|
||||
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> UObject*
|
||||
{
|
||||
if (UObject* Bound = BoundObject(Name)) return Bound;
|
||||
return FindWidgetObject(Name, Scope);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto Child(const TCHAR* Name,
|
||||
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T
|
||||
{
|
||||
UObject* O = ChildObject(Name, Scope);
|
||||
if (!O)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] no child named '{}' (neither a bound variable nor in the tree)\n"),
|
||||
m_tag, Name);
|
||||
return T{};
|
||||
}
|
||||
if (!ObjectIsA<T>(O))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[{}] child '{}' resolved to {}, which is not a {}\n"),
|
||||
m_tag, Name, O->GetFullName(), T::DebugTag());
|
||||
return T{};
|
||||
}
|
||||
return T{ O };
|
||||
}
|
||||
|
||||
// ---- viewport lifecycle -------------------------------------------
|
||||
auto AddToViewport(int32 ZOrder) -> bool
|
||||
{
|
||||
struct P { int32 ZOrder; } p{ ZOrder };
|
||||
return Call(STR("AddToViewport"), p);
|
||||
}
|
||||
auto RemoveFromViewport() -> bool { return CallVoid(STR("RemoveFromViewport")); }
|
||||
|
||||
auto GetIsVisible() -> std::optional<bool> { return CallForReturn<bool>(STR("GetIsVisible")); }
|
||||
|
||||
// BlueprintImplementableEvent. Dispatch is by the object's own
|
||||
// class chain regardless of native-vs-event, so this call works
|
||||
// the same either way -- but if no Blueprint graph overrides it,
|
||||
// the engine's default thunk hands back bool's zero value (false),
|
||||
// not an error. A false here does not prove non-interactable.
|
||||
auto IsInteractable() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInteractable")); }
|
||||
|
||||
// ---- cosmetic setters ----------------------------------------------
|
||||
auto SetPadding(FMargin_ InPadding) -> bool
|
||||
{
|
||||
struct P { FMargin_ InPadding; } p{ InPadding };
|
||||
return Call(STR("SetPadding"), p);
|
||||
}
|
||||
auto SetColorAndOpacity(FLinearColor_ InColorAndOpacity) -> bool
|
||||
{
|
||||
struct P { FLinearColor_ InColorAndOpacity; } p{ InColorAndOpacity };
|
||||
return Call(STR("SetColorAndOpacity"), p);
|
||||
}
|
||||
auto SetForegroundColor(FSlateColor_ InForegroundColor) -> bool
|
||||
{
|
||||
struct P { FSlateColor_ InForegroundColor; } p{ InForegroundColor };
|
||||
return Call(STR("SetForegroundColor"), p);
|
||||
}
|
||||
|
||||
// ---- plain properties ------------------------------------------
|
||||
auto GetPriority() const -> std::optional<int32> { return PropertyValue<int32>(STR("Priority")); }
|
||||
auto GetColorAndOpacity()const -> std::optional<FLinearColor_>{ return PropertyValue<FLinearColor_>(STR("ColorAndOpacity")); }
|
||||
auto GetPaddingValue() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
|
||||
// bIsFocusable / bStopAction / bHasScriptImplementedTick /
|
||||
// bHasScriptImplementedPaint are packed uint8:1 bitfields -- not
|
||||
// exposed, same reasoning as UWidget::bIsEnabled in Widget.hpp.
|
||||
// No UFUNCTION getter exists for any of them either; there is no
|
||||
// clean substitute, only a byte read and a manual mask.
|
||||
|
||||
protected:
|
||||
UserWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: Widget(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
27
Api/VerticalBox.hpp
Normal file
27
Api/VerticalBox.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
#include "VerticalBoxSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// UVerticalBox : public UPanelWidget directly -- same shape as
|
||||
// Overlay / HorizontalBox.
|
||||
class VerticalBox : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.VerticalBox"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("VerticalBox"); }
|
||||
|
||||
VerticalBox() = default;
|
||||
explicit VerticalBox(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<VerticalBox>(), DebugTag()) {}
|
||||
|
||||
auto AddChildToVerticalBox(UObject* Content) -> VerticalBoxSlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChildToVerticalBox"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? VerticalBoxSlot{ *Result } : VerticalBoxSlot{};
|
||||
}
|
||||
};
|
||||
}
|
||||
49
Api/VerticalBoxSlot.hpp
Normal file
49
Api/VerticalBoxSlot.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// Same four-member surface as HorizontalBoxSlot. Kept separate for
|
||||
// the same reason -- see HorizontalBoxSlot.hpp.
|
||||
class VerticalBoxSlot : public PanelSlot
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.VerticalBoxSlot"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("VerticalBoxSlot"); }
|
||||
|
||||
VerticalBoxSlot() = default;
|
||||
explicit VerticalBoxSlot(UObject* Obj)
|
||||
: PanelSlot(Obj, ClassOf<VerticalBoxSlot>(), DebugTag()) {}
|
||||
|
||||
// ---- setters (write through the UFUNCTION so SynchronizeProperties runs)
|
||||
|
||||
auto SetSize(FSlateChildSize_ InSize) -> bool
|
||||
{
|
||||
struct P { FSlateChildSize_ InSize; } p{ InSize };
|
||||
return Call(STR("SetSize"), p);
|
||||
}
|
||||
auto SetPadding(FMargin_ InPadding) -> bool
|
||||
{
|
||||
struct P { FMargin_ InPadding; } p{ InPadding };
|
||||
return Call(STR("SetPadding"), p);
|
||||
}
|
||||
auto SetHorizontalAlignment(EHorizontalAlignment_ InHorizontalAlignment) -> bool
|
||||
{
|
||||
struct P { EHorizontalAlignment_ InHorizontalAlignment; } p{ InHorizontalAlignment };
|
||||
return Call(STR("SetHorizontalAlignment"), p);
|
||||
}
|
||||
auto SetVerticalAlignment(EVerticalAlignment_ InVerticalAlignment) -> bool
|
||||
{
|
||||
struct P { EVerticalAlignment_ InVerticalAlignment; } p{ InVerticalAlignment };
|
||||
return Call(STR("SetVerticalAlignment"), p);
|
||||
}
|
||||
|
||||
// ---- property reads
|
||||
|
||||
auto GetSize() const -> std::optional<FSlateChildSize_> { return PropertyValue<FSlateChildSize_>(STR("Size")); }
|
||||
auto GetPadding() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
|
||||
auto GetHorizontalAlignment() const -> std::optional<EHorizontalAlignment_> { return PropertyValue<EHorizontalAlignment_>(STR("HorizontalAlignment")); }
|
||||
auto GetVerticalAlignment() const -> std::optional<EVerticalAlignment_> { return PropertyValue<EVerticalAlignment_>(STR("VerticalAlignment")); }
|
||||
};
|
||||
}
|
||||
227
Api/Widget.hpp
Normal file
227
Api/Widget.hpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
|
||||
#include <Unreal/FText.hpp>
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UmgTypes.hpp"
|
||||
#include "UnrealWrapper.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
|
||||
class Widget : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.Widget"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("Widget"); }
|
||||
|
||||
Widget() = default;
|
||||
explicit Widget(UObject* Obj) : Widget(Obj, ClassOf<Widget>(), DebugTag()) {}
|
||||
|
||||
// ---- visibility / enable ------------------------------------------
|
||||
|
||||
auto SetVisibility(ESlateVisibility_ InVisibility) -> bool
|
||||
{
|
||||
struct P { ESlateVisibility_ InVisibility; } p{ InVisibility };
|
||||
return Call(STR("SetVisibility"), p);
|
||||
}
|
||||
auto GetVisibility() -> std::optional<ESlateVisibility_>
|
||||
{
|
||||
return CallForReturn<ESlateVisibility_>(STR("GetVisibility"));
|
||||
}
|
||||
|
||||
auto SetIsEnabled(bool bInIsEnabled) -> bool
|
||||
{
|
||||
struct P { bool bInIsEnabled; } p{ bInIsEnabled };
|
||||
return Call(STR("SetIsEnabled"), p);
|
||||
}
|
||||
auto GetIsEnabled() -> std::optional<bool>
|
||||
{
|
||||
return CallForReturn<bool>(STR("GetIsEnabled"));
|
||||
}
|
||||
|
||||
auto IsVisible() -> std::optional<bool> { return CallForReturn<bool>(STR("IsVisible")); }
|
||||
auto IsRendered() -> std::optional<bool> { return CallForReturn<bool>(STR("IsRendered")); }
|
||||
auto IsInViewport() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInViewport")); }
|
||||
auto IsHovered() -> std::optional<bool> { return CallForReturn<bool>(STR("IsHovered")); }
|
||||
|
||||
// ---- render transform ---------------------------------------------
|
||||
|
||||
auto SetRenderTransform(FWidgetTransform_ InTransform) -> bool
|
||||
{
|
||||
struct P { FWidgetTransform_ InTransform; } p{ InTransform };
|
||||
return Call(STR("SetRenderTransform"), p);
|
||||
}
|
||||
auto SetRenderTranslation(FVector2D_ Translation) -> bool
|
||||
{
|
||||
struct P { FVector2D_ Translation; } p{ Translation };
|
||||
return Call(STR("SetRenderTranslation"), p);
|
||||
}
|
||||
auto SetRenderScale(FVector2D_ Scale) -> bool
|
||||
{
|
||||
struct P { FVector2D_ Scale; } p{ Scale };
|
||||
return Call(STR("SetRenderScale"), p);
|
||||
}
|
||||
auto SetRenderShear(FVector2D_ Shear) -> bool
|
||||
{
|
||||
struct P { FVector2D_ Shear; } p{ Shear };
|
||||
return Call(STR("SetRenderShear"), p);
|
||||
}
|
||||
auto SetRenderTransformPivot(FVector2D_ Pivot) -> bool
|
||||
{
|
||||
struct P { FVector2D_ Pivot; } p{ Pivot };
|
||||
return Call(STR("SetRenderTransformPivot"), p);
|
||||
}
|
||||
auto SetRenderTransformAngle(float Angle) -> bool
|
||||
{
|
||||
struct P { float Angle; } p{ Angle };
|
||||
return Call(STR("SetRenderTransformAngle"), p);
|
||||
}
|
||||
auto GetRenderTransformAngle() -> std::optional<float>
|
||||
{
|
||||
return CallForReturn<float>(STR("GetRenderTransformAngle"));
|
||||
}
|
||||
|
||||
auto SetRenderOpacity(float InOpacity) -> bool
|
||||
{
|
||||
struct P { float InOpacity; } p{ InOpacity };
|
||||
return Call(STR("SetRenderOpacity"), p);
|
||||
}
|
||||
auto GetRenderOpacity() -> std::optional<float>
|
||||
{
|
||||
return CallForReturn<float>(STR("GetRenderOpacity"));
|
||||
}
|
||||
|
||||
// ---- clipping / cursor ---------------------------------------------
|
||||
|
||||
auto SetClipping(EWidgetClipping_ InClipping) -> bool
|
||||
{
|
||||
struct P { EWidgetClipping_ InClipping; } p{ InClipping };
|
||||
return Call(STR("SetClipping"), p);
|
||||
}
|
||||
auto GetClipping() -> std::optional<EWidgetClipping_>
|
||||
{
|
||||
return CallForReturn<EWidgetClipping_>(STR("GetClipping"));
|
||||
}
|
||||
|
||||
auto SetCursor(EMouseCursorByte_ InCursor) -> bool
|
||||
{
|
||||
struct P { EMouseCursorByte_ InCursor; } p{ InCursor };
|
||||
return Call(STR("SetCursor"), p);
|
||||
}
|
||||
auto ResetCursor() -> bool { return CallVoid(STR("ResetCursor")); }
|
||||
|
||||
// ---- tooltips -------------------------------------------------------
|
||||
//
|
||||
// FText here is the same ownership question flagged in
|
||||
// FTextFromString: you are handing a refcounted object into a frame
|
||||
// the engine will destruct. Confirm who owns the allocation before
|
||||
// shipping this on a hot path.
|
||||
auto SetToolTipText(const FText& InToolTipText) -> bool
|
||||
{
|
||||
struct P { FText InToolTipText; } p{ InToolTipText };
|
||||
return Call(STR("SetToolTipText"), p);
|
||||
}
|
||||
auto SetToolTip(UObject* InWidget) -> bool
|
||||
{
|
||||
struct P { UObject* Widget; } p{ InWidget };
|
||||
return Call(STR("SetToolTip"), p);
|
||||
}
|
||||
|
||||
// ---- focus ----------------------------------------------------------
|
||||
|
||||
auto SetKeyboardFocus() -> bool { return CallVoid(STR("SetKeyboardFocus")); }
|
||||
auto SetFocus() -> bool { return CallVoid(STR("SetFocus")); }
|
||||
auto SetUserFocus(UObject* PlayerController) -> bool
|
||||
{
|
||||
struct P { UObject* PlayerController; } p{ PlayerController };
|
||||
return Call(STR("SetUserFocus"), p);
|
||||
}
|
||||
|
||||
auto HasKeyboardFocus() -> std::optional<bool> { return CallForReturn<bool>(STR("HasKeyboardFocus")); }
|
||||
auto HasMouseCapture() -> std::optional<bool> { return CallForReturn<bool>(STR("HasMouseCapture")); }
|
||||
auto HasAnyUserFocus() -> std::optional<bool> { return CallForReturn<bool>(STR("HasAnyUserFocus")); }
|
||||
auto HasFocusedDescendants()-> std::optional<bool> { return CallForReturn<bool>(STR("HasFocusedDescendants")); }
|
||||
|
||||
auto HasUserFocus(UObject* PlayerController) -> std::optional<bool>
|
||||
{
|
||||
struct P { UObject* PlayerController; bool ReturnValue; } p{ PlayerController, false };
|
||||
return CallForReturn(STR("HasUserFocus"), p, &P::ReturnValue);
|
||||
}
|
||||
auto HasUserFocusedDescendants(UObject* PlayerController) -> std::optional<bool>
|
||||
{
|
||||
struct P { UObject* PlayerController; bool ReturnValue; } p{ PlayerController, false };
|
||||
return CallForReturn(STR("HasUserFocusedDescendants"), p, &P::ReturnValue);
|
||||
}
|
||||
auto HasMouseCaptureByUser(int32 UserIndex, int32 PointerIndex) -> std::optional<bool>
|
||||
{
|
||||
struct P { int32 UserIndex; int32 PointerIndex; bool ReturnValue; }
|
||||
p{ UserIndex, PointerIndex, false };
|
||||
return CallForReturn(STR("HasMouseCaptureByUser"), p, &P::ReturnValue);
|
||||
}
|
||||
|
||||
// ---- navigation -----------------------------------------------------
|
||||
|
||||
auto SetNavigationRuleBase(EUINavigation_ Direction, EUINavigationRule_ Rule) -> bool
|
||||
{
|
||||
struct P { EUINavigation_ Direction; EUINavigationRule_ Rule; } p{ Direction, Rule };
|
||||
return Call(STR("SetNavigationRuleBase"), p);
|
||||
}
|
||||
auto SetNavigationRuleExplicit(EUINavigation_ Direction, UObject* InWidget) -> bool
|
||||
{
|
||||
struct P { EUINavigation_ Direction; UObject* InWidget; } p{ Direction, InWidget };
|
||||
return Call(STR("SetNavigationRuleExplicit"), p);
|
||||
}
|
||||
auto SetNavigationRule(EUINavigation_ Direction, EUINavigationRule_ Rule, FName WidgetToFocus) -> bool
|
||||
{
|
||||
struct P { EUINavigation_ Direction; EUINavigationRule_ Rule; FName WidgetToFocus; }
|
||||
p{ Direction, Rule, WidgetToFocus };
|
||||
return Call(STR("SetNavigationRule"), p);
|
||||
}
|
||||
auto SetAllNavigationRules(EUINavigationRule_ Rule, FName WidgetToFocus) -> bool
|
||||
{
|
||||
struct P { EUINavigationRule_ Rule; FName WidgetToFocus; } p{ Rule, WidgetToFocus };
|
||||
return Call(STR("SetAllNavigationRules"), p);
|
||||
}
|
||||
|
||||
// ---- layout / lifetime ----------------------------------------------
|
||||
|
||||
auto GetDesiredSize() -> std::optional<FVector2D_>
|
||||
{
|
||||
return CallForReturn<FVector2D_>(STR("GetDesiredSize"));
|
||||
}
|
||||
auto ForceLayoutPrepass() -> bool { return CallVoid(STR("ForceLayoutPrepass")); }
|
||||
auto InvalidateLayoutAndVolatility() -> bool { return CallVoid(STR("InvalidateLayoutAndVolatility")); }
|
||||
auto ForceVolatile(bool bForce) -> bool
|
||||
{
|
||||
struct P { bool bForce; } p{ bForce };
|
||||
return Call(STR("ForceVolatile"), p);
|
||||
}
|
||||
auto RemoveFromParent() -> bool { return CallVoid(STR("RemoveFromParent")); }
|
||||
|
||||
// ---- relationships ---------------------------------------------------
|
||||
//
|
||||
// Returns the raw UPanelWidget*. Not a PanelWidget wrapper, because
|
||||
// PanelWidget derives from this class and cannot be named here.
|
||||
// Wrap it at the call site: PanelWidget{ *w.GetParentObject() }.
|
||||
auto GetParentObject() -> std::optional<UObject*>
|
||||
{
|
||||
return CallForReturn<UObject*>(STR("GetParent"));
|
||||
}
|
||||
|
||||
// UWidget::Slot is a UPROPERTY with no getter.
|
||||
auto GetSlotObject() const -> UObject*
|
||||
{
|
||||
auto Ptr = PropertyPtr<UObject*>(STR("Slot"));
|
||||
return Ptr ? *Ptr : nullptr;
|
||||
}
|
||||
|
||||
auto GetOwningPlayer() -> std::optional<UObject*> { return CallForReturn<UObject*>(STR("GetOwningPlayer")); }
|
||||
auto GetOwningLocalPlayer() -> std::optional<UObject*> { return CallForReturn<UObject*>(STR("GetOwningLocalPlayer")); }
|
||||
auto GetGameInstance() -> std::optional<UObject*> { return CallForReturn<UObject*>(STR("GetGameInstance")); }
|
||||
|
||||
protected:
|
||||
Widget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
|
||||
: UnrealWrapper(Obj, Expected, Tag) {}
|
||||
};
|
||||
}
|
||||
45
Api/WidgetBlueprintGeneratedClass.hpp
Normal file
45
Api/WidgetBlueprintGeneratedClass.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <Api/UnrealWrapper.hpp>
|
||||
#include <Api/WidgetTree.hpp>
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
// ==================================================================
|
||||
// UWidgetBlueprintGeneratedClass -- the CLASS object behind every WBP_...
|
||||
// ==================================================================
|
||||
class WidgetBlueprintGeneratedClass : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.WidgetBlueprintGeneratedClass"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("WBPGeneratedClass"); }
|
||||
|
||||
WidgetBlueprintGeneratedClass() = default;
|
||||
|
||||
explicit WidgetBlueprintGeneratedClass(UObject* Obj) : UnrealWrapper(Obj, ClassOf<WidgetBlueprintGeneratedClass>(), DebugTag()) {}
|
||||
|
||||
static auto Of(UObject* Instance) -> WidgetBlueprintGeneratedClass
|
||||
{
|
||||
if (!Instance) return WidgetBlueprintGeneratedClass{};
|
||||
|
||||
UClass* Cls = Instance->GetClassPrivate();
|
||||
if (!Cls) return WidgetBlueprintGeneratedClass{};
|
||||
|
||||
UClass* Expected = ClassOf<WidgetBlueprintGeneratedClass>();
|
||||
if (!Expected || !Cls->IsA(Expected)) return WidgetBlueprintGeneratedClass{};
|
||||
|
||||
return WidgetBlueprintGeneratedClass{ static_cast<UObject*>(Cls) };
|
||||
}
|
||||
|
||||
auto TemplateTree() const -> WidgetTree
|
||||
{
|
||||
UObject* Tree = TryObjectProperty(STR("WidgetTree"));
|
||||
return Tree ? WidgetTree{ Tree } : WidgetTree{};
|
||||
}
|
||||
|
||||
auto TemplateTreeObject() const -> UObject*
|
||||
{
|
||||
return TryObjectProperty(STR("WidgetTree"));
|
||||
}
|
||||
};
|
||||
}
|
||||
125
Api/WidgetLayoutLibrary.hpp
Normal file
125
Api/WidgetLayoutLibrary.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Reflection/UnrealObjectRef.hpp>
|
||||
|
||||
#include "UmgTypes.hpp"
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "PanelSlot.hpp"
|
||||
#include "CanvasPanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
|
||||
class WidgetLayoutLibrary : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.WidgetLayoutLibrary"); }
|
||||
static auto CDOPath() -> const TCHAR* { return STR("/Script/UMG.Default__WidgetLayoutLibrary"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("WidgetLayoutLibrary"); }
|
||||
|
||||
static auto Instance() -> WidgetLayoutLibrary&
|
||||
{
|
||||
static WidgetLayoutLibrary Singleton{};
|
||||
return Singleton;
|
||||
}
|
||||
|
||||
// ---- slot casts ---------------------------------------------------
|
||||
|
||||
auto SlotAsCanvasSlot(UObject* Widget) -> CanvasPanelSlot
|
||||
{
|
||||
return GetSingleton<CanvasPanelSlot>(STR("SlotAsCanvasSlot"), Widget);
|
||||
}
|
||||
auto SlotAsWrapBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsWrapBoxSlot"), Widget); }
|
||||
auto SlotAsWidgetSwitcherSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsWidgetSwitcherSlot"), Widget); }
|
||||
auto SlotAsVerticalBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsVerticalBoxSlot"), Widget); }
|
||||
auto SlotAsUniformGridSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsUniformGridSlot"), Widget); }
|
||||
auto SlotAsSizeBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsSizeBoxSlot"), Widget); }
|
||||
auto SlotAsScrollBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsScrollBoxSlot"), Widget); }
|
||||
auto SlotAsScaleBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsScaleBoxSlot"), Widget); }
|
||||
auto SlotAsSafeBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsSafeBoxSlot"), Widget); }
|
||||
auto SlotAsOverlaySlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsOverlaySlot"), Widget); }
|
||||
auto SlotAsHorizontalBoxSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsHorizontalBoxSlot"), Widget); }
|
||||
auto SlotAsGridSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsGridSlot"), Widget); }
|
||||
auto SlotAsBorderSlot(UObject* Widget) -> PanelSlot { return GetSingleton<PanelSlot>(STR("SlotAsBorderSlot"), Widget); }
|
||||
|
||||
// ---- viewport / mouse ----------------------------------------------
|
||||
|
||||
auto RemoveAllWidgets(UObject* WorldContextObject) -> bool
|
||||
{
|
||||
struct P { UObject* WorldContextObject; } p{ WorldContextObject };
|
||||
return Call(STR("RemoveAllWidgets"), p);
|
||||
}
|
||||
|
||||
auto GetViewportSize(UObject* WorldContextObject) -> std::optional<FVector2D_>
|
||||
{
|
||||
struct P { UObject* WorldContextObject; FVector2D_ ReturnValue; } p{ WorldContextObject, {} };
|
||||
return CallForReturn(STR("GetViewportSize"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetViewportScale(UObject* WorldContextObject) -> std::optional<float>
|
||||
{
|
||||
struct P { UObject* WorldContextObject; float ReturnValue; } p{ WorldContextObject, 0.f };
|
||||
return CallForReturn(STR("GetViewportScale"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetMousePositionOnViewport(UObject* WorldContextObject) -> std::optional<FVector2D_>
|
||||
{
|
||||
struct P { UObject* WorldContextObject; FVector2D_ ReturnValue; } p{ WorldContextObject, {} };
|
||||
return CallForReturn(STR("GetMousePositionOnViewport"), p, &P::ReturnValue);
|
||||
}
|
||||
auto GetMousePositionOnPlatform() -> std::optional<FVector2D_>
|
||||
{
|
||||
return CallForReturn<FVector2D_>(STR("GetMousePositionOnPlatform"));
|
||||
}
|
||||
|
||||
struct MousePositionDPI { float X{}; float Y{}; };
|
||||
auto GetMousePositionScaledByDPI(UObject* Player) -> std::optional<MousePositionDPI>
|
||||
{
|
||||
struct P { UObject* Player; float LocationX; float LocationY; bool ReturnValue; }
|
||||
p{ Player, 0.f, 0.f, false };
|
||||
if (!Call(STR("GetMousePositionScaledByDPI"), p) || !p.ReturnValue) return std::nullopt;
|
||||
return MousePositionDPI{ p.LocationX, p.LocationY };
|
||||
}
|
||||
|
||||
auto ProjectWorldLocationToWidgetPosition(UObject* PlayerController, FVector_ WorldLocation, bool bPlayerViewportRelative) -> std::optional<FVector2D_>
|
||||
{
|
||||
struct P
|
||||
{
|
||||
UObject* PlayerController;
|
||||
FVector_ WorldLocation;
|
||||
FVector2D_ ScreenPosition;
|
||||
bool bPlayerViewportRelative;
|
||||
bool ReturnValue;
|
||||
} p{ PlayerController, WorldLocation, {}, bPlayerViewportRelative, false };
|
||||
|
||||
if (!Call(STR("ProjectWorldLocationToWidgetPosition"), p) || !p.ReturnValue) return std::nullopt;
|
||||
return p.ScreenPosition;
|
||||
}
|
||||
|
||||
private:
|
||||
WidgetLayoutLibrary() : UnrealWrapper(ResolveCDO(), ClassOf<WidgetLayoutLibrary>(), DebugTag()) {}
|
||||
|
||||
WidgetLayoutLibrary(const WidgetLayoutLibrary&) = delete;
|
||||
auto operator=(const WidgetLayoutLibrary&) -> WidgetLayoutLibrary& = delete;
|
||||
|
||||
static auto ResolveCDO() -> UObject*
|
||||
{
|
||||
static Reflection::UnrealObjectRef Ref{ CDOPath(), DebugTag() };
|
||||
return Ref.Get();
|
||||
}
|
||||
|
||||
auto CallStaticObjectGetter(const TCHAR* FunctionName, UObject* InObject) -> UObject*
|
||||
{
|
||||
struct P { UObject* InObject; UObject* ReturnValue; } p{ InObject, nullptr };
|
||||
auto Result = CallForReturn(FunctionName, p, &P::ReturnValue);
|
||||
return (Result && *Result) ? *Result : nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto GetSingleton(const TCHAR* FunctionName, UObject* InObject) -> T
|
||||
{
|
||||
UObject* Obj = CallStaticObjectGetter(FunctionName, InObject);
|
||||
return Obj ? T{ Obj } : T{};
|
||||
}
|
||||
};
|
||||
}
|
||||
349
Api/WidgetTree.hpp
Normal file
349
Api/WidgetTree.hpp
Normal file
@@ -0,0 +1,349 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Unreal/NameTypes.hpp>
|
||||
|
||||
#include "UnrealWrapper.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "PanelWidget.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
|
||||
class WidgetTree : public UnrealWrapper
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.WidgetTree"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("WidgetTree"); }
|
||||
|
||||
WidgetTree() = default;
|
||||
explicit WidgetTree(UObject* Obj) : UnrealWrapper(Obj, ClassOf<WidgetTree>(), DebugTag()) {}
|
||||
|
||||
static auto FromUserWidget(UObject* UserWidget) -> WidgetTree
|
||||
{
|
||||
if (!UserWidget) return WidgetTree{};
|
||||
|
||||
UObject* Tree = RawObject{ UserWidget, STR("UserWidget") }.TryObjectProperty(STR("WidgetTree"));
|
||||
return Tree ? WidgetTree{ Tree } : WidgetTree{};
|
||||
}
|
||||
|
||||
// The tree a widget LIVES IN.
|
||||
static auto ContainingTree(UObject* AnyWidget) -> WidgetTree
|
||||
{
|
||||
if (!AnyWidget) return WidgetTree{};
|
||||
|
||||
UClass* TreeClass = ClassOf<WidgetTree>();
|
||||
if (!TreeClass) return WidgetTree{}; // ClassOf already logged
|
||||
|
||||
if (UObject* Tree = AnyWidget->GetTypedOuter(TreeClass))
|
||||
{
|
||||
return WidgetTree{ Tree };
|
||||
}
|
||||
|
||||
UObject* Outer = AnyWidget->GetOuterPrivate();
|
||||
for (int32 Hops = 0; Outer && Hops < kMaxOuterHops; ++Hops)
|
||||
{
|
||||
if (Outer->IsA(TreeClass)) return WidgetTree{ Outer };
|
||||
Outer = Outer->GetOuterPrivate();
|
||||
}
|
||||
return WidgetTree{};
|
||||
}
|
||||
|
||||
static auto FromWidget(UObject* AnyWidget) -> WidgetTree
|
||||
{
|
||||
if (WidgetTree Owned = FromUserWidget(AnyWidget)) return Owned;
|
||||
return ContainingTree(AnyWidget);
|
||||
}
|
||||
|
||||
// ---- root ---------------------------------------------------------
|
||||
|
||||
auto GetRootWidgetObject() const -> UObject*
|
||||
{
|
||||
auto Ptr = PropertyPtr<UObject*>(STR("RootWidget"));
|
||||
return Ptr ? *Ptr : nullptr;
|
||||
}
|
||||
|
||||
auto Root() const -> Widget
|
||||
{
|
||||
UObject* R = GetRootWidgetObject();
|
||||
return R ? Widget{ R } : Widget{};
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// TRAVERSAL
|
||||
// ==================================================================
|
||||
|
||||
enum class EScope : uint8
|
||||
{
|
||||
ThisTreeOnly,
|
||||
IncludeNestedUserWidgets,
|
||||
};
|
||||
|
||||
// What a visitor wants to happen next.
|
||||
enum class EVisit : uint8
|
||||
{
|
||||
Continue, // descend into this node's children
|
||||
SkipChildren, // this node is fine, its subtree is not interesting
|
||||
Stop, // end the walk entirely
|
||||
};
|
||||
|
||||
static constexpr int32 kDefaultNodeBudget = 8192;
|
||||
static constexpr int32 kMaxDepth = 64;
|
||||
static constexpr int32 kMaxOuterHops = 32;
|
||||
|
||||
template <typename Fn>
|
||||
auto ForEach(Fn&& Visit, EScope Scope = EScope::ThisTreeOnly, int32 NodeBudget = kDefaultNodeBudget) -> bool
|
||||
{
|
||||
WalkState State{ Scope, NodeBudget, false, false };
|
||||
WalkNode(GetRootWidgetObject(), 0, State, Visit);
|
||||
|
||||
if (State.bExhausted)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] walk aborted -- exceeded {} nodes or depth {}. ")
|
||||
STR("Results are incomplete.\n"), NodeBudget, kMaxDepth);
|
||||
}
|
||||
return !State.bExhausted;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// SEARCH
|
||||
// ==================================================================
|
||||
|
||||
auto FindObjectByName(FName Name, EScope Scope = EScope::ThisTreeOnly) -> UObject*
|
||||
{
|
||||
if (Name.IsNone()) return nullptr;
|
||||
|
||||
UObject* Found = nullptr;
|
||||
ForEach([&](UObject* Node, int32) -> EVisit {
|
||||
if (Node->GetNamePrivate() == Name)
|
||||
{
|
||||
Found = Node;
|
||||
return EVisit::Stop;
|
||||
}
|
||||
return EVisit::Continue;
|
||||
}, Scope);
|
||||
|
||||
return Found;
|
||||
}
|
||||
|
||||
auto FindObjectByName(const TCHAR* Name, EScope Scope = EScope::ThisTreeOnly) -> UObject*
|
||||
{
|
||||
if (!Name) return nullptr;
|
||||
return FindObjectByName(FName(Name, FNAME_Find), Scope);
|
||||
}
|
||||
|
||||
// Same search, wrapped. Invalid Widget on a miss.
|
||||
auto Find(const TCHAR* Name, EScope Scope = EScope::ThisTreeOnly) -> Widget
|
||||
{
|
||||
UObject* O = FindObjectByName(Name, Scope);
|
||||
return O ? Widget{ O } : Widget{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto FindAs(const TCHAR* Name, EScope Scope = EScope::ThisTreeOnly) -> T
|
||||
{
|
||||
UObject* O = FindObjectByName(Name, Scope);
|
||||
if (!O)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] no widget named '{}' in this tree\n"), Name);
|
||||
return T{};
|
||||
}
|
||||
|
||||
if (!ObjectIsA<T>(O))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] '{}' resolved to {}, which is not a {}\n"),
|
||||
Name, O->GetFullName(), T::DebugTag());
|
||||
return T{};
|
||||
}
|
||||
return T{ O };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto FindFirstOfType(EScope Scope = EScope::ThisTreeOnly) -> T
|
||||
{
|
||||
UClass* Cls = ClassOf<T>();
|
||||
if (!Cls) return T{};
|
||||
|
||||
UObject* Found = nullptr;
|
||||
ForEach([&](UObject* Node, int32) -> EVisit
|
||||
{
|
||||
if (Node->IsA(Cls)) { Found = Node; return EVisit::Stop; }
|
||||
return EVisit::Continue;
|
||||
}, Scope);
|
||||
|
||||
return Found ? T{ Found } : T{};
|
||||
}
|
||||
|
||||
// Every widget of a given wrapper type, in traversal order.
|
||||
template <typename T>
|
||||
auto FindAllOfType(EScope Scope = EScope::ThisTreeOnly) -> std::vector<T>
|
||||
{
|
||||
std::vector<T> Out;
|
||||
|
||||
UClass* Cls = ClassOf<T>();
|
||||
if (!Cls) return Out;
|
||||
|
||||
ForEach([&](UObject* Node, int32) -> EVisit
|
||||
{
|
||||
if (Node->IsA(Cls)) Out.emplace_back(Node);
|
||||
return EVisit::Continue;
|
||||
}, Scope);
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
// Flat list of every widget object the walk reaches.
|
||||
auto AllWidgetObjects(EScope Scope = EScope::ThisTreeOnly) -> std::vector<UObject*>
|
||||
{
|
||||
std::vector<UObject*> Out;
|
||||
ForEach([&](UObject* Node, int32) -> EVisit
|
||||
{
|
||||
Out.push_back(Node);
|
||||
return EVisit::Continue;
|
||||
}, Scope);
|
||||
return Out;
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// DIAGNOSTICS
|
||||
// ==================================================================
|
||||
|
||||
auto Dump(EScope Scope = EScope::IncludeNestedUserWidgets) -> void
|
||||
{
|
||||
if (!m_obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(STR("[WidgetTree] Dump on an invalid tree\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] dump of {}\n"), m_obj->GetFullName());
|
||||
|
||||
int32 Count = 0;
|
||||
ForEach([&](UObject* Node, int32 Depth) -> EVisit
|
||||
{
|
||||
++Count;
|
||||
|
||||
std::basic_string<TCHAR> Indent;
|
||||
Indent.reserve(static_cast<size_t>(Depth) * 2);
|
||||
for (int32 i = 0; i < Depth; ++i) Indent += STR(" ");
|
||||
|
||||
UClass* Cls = Node->GetClassPrivate();
|
||||
std::basic_string<TCHAR> ClassName =
|
||||
Cls ? Cls->GetName() : std::basic_string<TCHAR>(STR("<no class>"));
|
||||
|
||||
if (ObjectIsA<PanelWidget>(Node))
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR(" {}{} [{}] ({} children)\n"),
|
||||
Indent.c_str(), Node->GetName().c_str(), ClassName.c_str(),
|
||||
static_cast<int32>(PanelWidget{ Node }.GetChildObjects().size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR(" {}{} [{}]\n"),
|
||||
Indent.c_str(), Node->GetName().c_str(), ClassName.c_str());
|
||||
}
|
||||
return EVisit::Continue;
|
||||
}, Scope);
|
||||
|
||||
Output::send<LogLevel::Warning>(STR("[WidgetTree] {} widgets\n"), Count);
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
// CONSTRUCTION
|
||||
// ==================================================================
|
||||
|
||||
auto ConstructWidget(UClass* WidgetClass, const TCHAR* InstanceName) -> UObject*
|
||||
{
|
||||
if (!m_obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] ConstructWidget on an invalid tree\n"));
|
||||
return nullptr;
|
||||
}
|
||||
if (!WidgetClass)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] ConstructWidget: null class\n"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FStaticConstructObjectParameters Params{ WidgetClass };
|
||||
Params.Outer = m_obj;
|
||||
if (InstanceName) Params.Name = FName(InstanceName);
|
||||
|
||||
UObject* Obj = UObjectGlobals::StaticConstructObject(Params);
|
||||
if (!Obj)
|
||||
{
|
||||
Output::send<LogLevel::Warning>(
|
||||
STR("[WidgetTree] ConstructWidget: StaticConstructObject returned null for '{}'\n"),
|
||||
InstanceName ? InstanceName : STR("<unnamed>"));
|
||||
}
|
||||
return Obj;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto ConstructWidget(const TCHAR* InstanceName) -> T
|
||||
{
|
||||
UObject* O = ConstructWidget(ClassOf<T>(), InstanceName);
|
||||
return O ? T{ O } : T{};
|
||||
}
|
||||
|
||||
private:
|
||||
struct WalkState
|
||||
{
|
||||
EScope Scope;
|
||||
int32 Budget;
|
||||
bool bStopped;
|
||||
bool bExhausted;
|
||||
};
|
||||
|
||||
static auto OwnedTreeObject(UObject* Node) -> UObject*
|
||||
{
|
||||
return RawObject{ Node, STR("Widget") }.TryObjectProperty(STR("WidgetTree"));
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
static auto WalkNode(UObject* Node, int32 Depth, WalkState& State, Fn& Visit) -> void
|
||||
{
|
||||
if (!Node || State.bStopped) return;
|
||||
|
||||
if (State.Budget-- <= 0 || Depth >= kMaxDepth)
|
||||
{
|
||||
State.bExhausted = true;
|
||||
State.bStopped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const EVisit Result = Visit(Node, Depth);
|
||||
if (Result == EVisit::Stop) { State.bStopped = true; return; }
|
||||
if (Result == EVisit::SkipChildren) return;
|
||||
|
||||
if (UObject* NestedTree = OwnedTreeObject(Node))
|
||||
{
|
||||
if (State.Scope == EScope::IncludeNestedUserWidgets)
|
||||
{
|
||||
UObject* NestedRoot =
|
||||
RawObject{ NestedTree, STR("WidgetTree") }.TryObjectProperty(STR("RootWidget"));
|
||||
WalkNode(NestedRoot, Depth + 1, State, Visit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ObjectIsA<PanelWidget>(Node)) return;
|
||||
|
||||
for (UObject* Child : PanelWidget{ Node }.GetChildObjects())
|
||||
{
|
||||
if (State.bStopped) return;
|
||||
WalkNode(Child, Depth + 1, State, Visit);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
25
Api/WrapBox.hpp
Normal file
25
Api/WrapBox.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "PanelWidget.hpp"
|
||||
#include "PanelSlot.hpp"
|
||||
|
||||
namespace PalUIExtension::Api
|
||||
{
|
||||
class WrapBox : public PanelWidget
|
||||
{
|
||||
public:
|
||||
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.WrapBox"); }
|
||||
static auto DebugTag() -> const TCHAR* { return STR("WrapBox"); }
|
||||
|
||||
WrapBox() = default;
|
||||
explicit WrapBox(UObject* Obj)
|
||||
: PanelWidget(Obj, ClassOf<WrapBox>(), DebugTag()) {}
|
||||
|
||||
auto AddChildToWrapBox(UObject* Content) -> PanelSlot
|
||||
{
|
||||
struct P { UObject* Content; UObject* ReturnValue; } p{ Content, nullptr };
|
||||
auto Result = CallForReturn(STR("AddChildToWrapBox"), p, &P::ReturnValue);
|
||||
return (Result && *Result) ? PanelSlot{ *Result } : PanelSlot{};
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user