#pragma once #include #include #include #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(), 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 { std::vector 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(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 { return PropertyBoolValue(STR("bIsEnableDragDropControl")); } auto PartyPalDetails() const -> std::vector { std::vector Out; auto* Arr = TryPropertyPtr>(STR("PartyPalDetails")); if (!Arr) return Out; if (!Arr->IsPlausible(kMaxPlausibleEntries)) { Output::send( STR("[{}] PartyPalDetails does not look like a live TArray (Num={}, Max={})\n"), m_tag, Arr->Num, Arr->Max); return Out; } Out.reserve(static_cast(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) {} }; }