diff --git a/Source/Contents/CashEmotionMgr.cpp b/Source/Contents/CashEmotionMgr.cpp index c548eb03..18a0578e 100644 --- a/Source/Contents/CashEmotionMgr.cpp +++ b/Source/Contents/CashEmotionMgr.cpp @@ -9,7 +9,11 @@ #include "UI/CashEmotion/UICashEmotionPiece.h" #include "UI/Chat/ChatSuggestions.h" #include "UI/Edit/UIEditCtrl.h" +#include "Contents/MessageTable.h" +#include "UI/Buttons/UIButton.h" #include "UI/Lists/UIScrollBar.h" +#include "UI/UILapineScrollBar.h" +#include "UI/Window/UIFrameWnd.h" #include "UI/Window/WindowBorder.h" #include @@ -17,6 +21,7 @@ #include #include #include +#include #include // STATIC: 0x013896C4 @@ -90,8 +95,75 @@ protected: /* 00B4 */ bool m_disabled; }; +class UICashEmotionScrollWnd : public UIScrollWnd { +}; + +class UICashEmotionListChildWnd_Abstract : public UICashEmotionScrollWnd { +public: + void Slot35(int delta) override; +}; + +class UICashEmotionListChildWnd_Basic : public UICashEmotionListChildWnd_Abstract { +public: + void Slot19() override; + void Paint() override; + +protected: + /* 00B0 */ std::vector m_items; + /* 00BC */ ICashEmotionAction* m_selectAction; + /* 00C0 */ ICashEmotionAction* m_playAction; +}; + +class UICashEmotionListChildWnd_Purchase : public UICashEmotionListChildWnd_Abstract { +public: + void Paint() override; +}; + +class UICashEmotionListChildWnd_Search : public UICashEmotionListChildWnd_Abstract { +public: + void Paint() override; +}; + +class UICashEmotionListChildWnd_UnPurchased : public UICashEmotionListChildWnd_Abstract { +public: + void Paint() override; +}; + +struct CashEmotionListPlacement { + /* 0000 */ int x; + /* 0004 */ int y; + /* 0008 */ int width; + /* 000C */ int height; +}; + +class UICashEmotionListWnd : public UIFrameWnd { +public: + UICashEmotionListWnd(UIWindowContext& windows, UIFrameContext& frame, UIButtonContext& controls); + void Slot11() override; + void Slot19() override; + void Paint() override; + +protected: + /* 00B4 */ CashEmotionListPlacement* m_placement; + /* 00B8 */ UICashEmotionListChildWnd_Abstract* m_basicList; + /* 00BC */ UICashEmotionListChildWnd_Abstract* m_purchaseList; + /* 00C0 */ UICashEmotionListChildWnd_Abstract* m_searchList; + /* 00C4 */ UICashEmotionListChildWnd_Abstract* m_unpurchasedList; + /* 00C8 */ std::map m_tabs; + /* 00D0 */ char m_tab; + /* 00D4 */ UIWindow* unknown_00D4; + /* 00D8 */ UIEditCtrl* m_searchEdit; + /* 00DC */ UIWindow* unknown_00DC; + /* 00E0 */ UIWindow* unknown_00E0; + /* 00E4 */ UIWindow* unknown_00E4; + /* 00E8 */ UICashEmotionListChildWnd_Abstract* m_currentList; + /* 00EC */ UIWindow* unknown_00EC; + UIButtonContext* m_controls; +}; + class UICashEmotionMiniWnd : public UIWindow { public: + ~UICashEmotionMiniWnd() override; void Slot15(int width, int height) override; void Paint() override; int SendMsg(UIWindow* sender, int message, std::intptr_t argument1, std::intptr_t argument2, std::intptr_t argument3, std::intptr_t argument4) override; @@ -114,6 +186,11 @@ protected: UICashEmotionPieceContext* m_controls; }; +// OFFSET: 0x00591D80 +// FUNCTION: fn_02841 +ICashEmotionAction::~ICashEmotionAction() { +} + // OFFSET: 0x00592290 // FUNCTION: fn_02849 CashEmotionCommand::CashEmotionCommand(const std::string& name, int packId, int emotionId) : name(name) { @@ -735,6 +812,98 @@ bool CCashEmotionMgr::IsOwnedPack(int packId) { return std::find(this->m_ownedPacks.begin(), this->m_ownedPacks.end(), packId) != this->m_ownedPacks.end(); } +// OFFSET: 0x007C3AD0 +// FUNCTION: fn_09344 +void UICashEmotionListChildWnd_Abstract::Slot35(int delta) { + if (this->wheelScroll == 1) { + if (delta < 0) { + this->SendMsg(this->scrollBar, 7, 15, 0, 0, 0); + return; + } + this->SendMsg(this->scrollBar, 7, -15, 0, 0, 0); + } +} + +// OFFSET: 0x007C4450 +// FUNCTION: fn_09354 +void UICashEmotionListChildWnd_Basic::Paint() { + this->ClearSurface(0xFFFF00FF); +} + +// OFFSET: 0x007C4460 +// FUNCTION: fn_09355 +void UICashEmotionListChildWnd_Basic::Slot19() { + for (UIWindow* item : this->m_items) { + if (item->geometry.visible) { + item->Slot19(); + } + } +} + +// OFFSET: 0x007C52A0 +// FUNCTION: fn_09369 +void UICashEmotionListChildWnd_Purchase::Paint() { + this->ClearSurface(0xFFFF00FF); +} + +// OFFSET: 0x007C5D90 +// FUNCTION: fn_09384 +void UICashEmotionListChildWnd_Search::Paint() { + this->ClearSurface(0xFFFF00FF); +} + +// OFFSET: 0x007C6AB0 +// FUNCTION: fn_09395 +void UICashEmotionListChildWnd_UnPurchased::Paint() { + this->ClearSurface(0xFFFF00FF); +} + +// OFFSET: 0x007C6CB0 +// FUNCTION: fn_09401 +UICashEmotionListWnd::UICashEmotionListWnd(UIWindowContext& windows, UIFrameContext& frame, UIButtonContext& controls) + : UIFrameWnd(windows, frame, 0), m_placement(nullptr), m_basicList(nullptr), m_purchaseList(nullptr), m_searchList(nullptr), m_unpurchasedList(nullptr), + m_tab(0), unknown_00D4(nullptr), m_searchEdit(nullptr), unknown_00DC(nullptr), unknown_00E0(nullptr), unknown_00E4(nullptr), m_currentList(nullptr), + unknown_00EC(nullptr), m_controls(&controls) { +} + +// OFFSET: 0x007C7C20 +// FUNCTION: fn_09407 +void UICashEmotionListWnd::Paint() { + this->ClearSurface(0xFFFF00FF); + const int width = this->geometry.width - 30; + const int top = this->DrawTitleBarAt(this->m_controls->fonts, this->m_controls->locale, 29, width, false, LookupMessage(542)); + this->DrawPanelBackgroundAt(29, top, width, nullptr); + DrawRectangleBorder(*this, 60, 22, 161, 18, 0xB5B5B5); + this->FillRegion(29, 46, width, 1, 0xCECECE); + this->DrawButtonBarAt(2, 29, width, 1); +} + +// OFFSET: 0x007C7CC0 +// FUNCTION: fn_09410 +void UICashEmotionListWnd::Slot19() { + if (this->m_currentList) { + this->m_currentList->Slot19(); + } +} + +// OFFSET: 0x007C8AF0 +// FUNCTION: fn_09420 +void UICashEmotionListWnd::Slot11() { + if (this->m_placement) { + this->m_placement->x = this->geometry.x; + this->m_placement->y = this->geometry.y; + this->m_placement->width = this->geometry.width; + this->m_placement->height = this->geometry.height; + } +} + +// OFFSET: 0x007C90B0 +// FUNCTION: fn_09427 +UICashEmotionMiniWnd::~UICashEmotionMiniWnd() { + delete this->m_action; + this->m_action = nullptr; +} + // OFFSET: 0x007C9200 // FUNCTION: fn_09428 void UICashEmotionMiniWnd::Select(CashEmotionCommand* command) { diff --git a/Source/UI/Buttons/UIBmpRadioBtn.h b/Source/UI/Buttons/UIBmpRadioBtn.h index 4a8fb752..9991b244 100644 --- a/Source/UI/Buttons/UIBmpRadioBtn.h +++ b/Source/UI/Buttons/UIBmpRadioBtn.h @@ -12,12 +12,17 @@ public: void Slot28(int x, int y) override; protected: - /* 007C */ unsigned char unknown_007C[0x64]; + /* 007C */ unsigned char unknown_007C[0x60]; + /* 00DC */ bool m_hovered; + /* 00DD */ unsigned char unknown_00DD[3]; /* 00E0 */ std::string m_tooltip; UIButtonContext* m_controls; }; class UIBmpRadioBtn2 : public UIBmpRadioBtn { +public: + void Slot47() override; + void Slot48() override; }; class UICaptchaImage : public UIWindow { diff --git a/Source/UI/Buttons/UITextButton.cpp b/Source/UI/Buttons/UITextButton.cpp index 931b1be1..0c11bb09 100644 --- a/Source/UI/Buttons/UITextButton.cpp +++ b/Source/UI/Buttons/UITextButton.cpp @@ -252,6 +252,20 @@ void UIBmpRadioBtn::Slot28(int, int) { } } +// OFFSET: 0x007C7CA0 +// FUNCTION: fn_09408 +void UIBmpRadioBtn2::Slot47() { + this->m_hovered = true; + this->Invalidate(); +} + +// OFFSET: 0x007C7CB0 +// FUNCTION: fn_09409 +void UIBmpRadioBtn2::Slot48() { + this->m_hovered = false; + this->Invalidate(); +} + // OFFSET: 0x00891870 // FUNCTION: fn_11451 void UICaptchaImage::Slot28(int, int) { diff --git a/Source/UI/UIBroadcastBalloon.cpp b/Source/UI/UIBroadcastBalloon.cpp new file mode 100644 index 00000000..b75795c3 --- /dev/null +++ b/Source/UI/UIBroadcastBalloon.cpp @@ -0,0 +1,111 @@ +#include "UI/UIBroadcastBalloon.h" +#include "Render/Primitives/AlphaRectangle.h" +#include "Render/Renderer.h" +#include "Render/TextureQuad.h" +#include "UI/Buttons/UIButton.h" + +#include + +// OFFSET: 0x007C27B0 +// FUNCTION: fn_09330 +UIBroadcastBalloon::UIBroadcastBalloon(UIWindowContext& windows, UIButtonContext& controls) : UIWindow(windows, 0), m_controls(&controls) { +} + +// OFFSET: 0x007C2D70 +// FUNCTION: fn_09333 +void UIBroadcastBalloon::AddMessage(int category, const BroadcastBalloonMessage& message) { + std::list& messages = this->m_messages[category]; + messages.push_back(message); + + unsigned int limit = 3; + if (category == 2) { + limit = 1; + } + if (messages.size() > limit) { + messages.pop_front(); + } + + BroadcastBalloonMessage& last = messages.back(); + int width = 0; + int height = 0; + for (const BroadcastBalloonLine& line : last.lines) { + const SIZE size = this->TextSize(this->m_controls->fonts, this->m_controls->locale, line.text.c_str(), 0, 0, last.fontHeight, 0, 0); + if (width < size.cx) { + width = size.cx; + } + height += size.cy; + } + last.width = width + 12; + last.height = height + (static_cast(last.lines.size()) + 1) * 4; + + const std::size_t count = this->m_messages.size(); + if (count != 0) { + int total = 0; + for (const auto& entry : this->m_messages) { + int sum = 0; + for (const BroadcastBalloonMessage& item : entry.second) { + sum += item.height; + } + total += sum; + if (entry.second.size() > 1) { + total += static_cast(entry.second.size()) * 10 - 10; + } + } + if (count > 1) { + total = static_cast(count) * 17 - 17 + total; + } + this->Resize(this->geometry.width, total); + } +} + +// OFFSET: 0x007C31C0 +// FUNCTION: fn_09334 +void UIBroadcastBalloon::Slot43() { + int y = 0; + for (const auto& entry : this->m_messages) { + for (const BroadcastBalloonMessage& message : entry.second) { + if (message.lines.size() != 0) { + DrawAlphaRectangle( + g_renderer->primitives, g_renderer->queues, this->geometry.x + 1 + (this->geometry.width - message.width) / 2, this->geometry.y + 1 + y, + message.width - 2, message.height - 2, message.backgroundColor, g_renderer->fog.supported != 0, g_depthStart, g_depthEnd + ); + y += message.height + 10; + } + } + y += 7; + } +} + +// OFFSET: 0x007C32C0 +// FUNCTION: fn_09335 +bool UIBroadcastBalloon::Slot42(void*) { + return true; +} + +// OFFSET: 0x007C3430 +// FUNCTION: fn_09337 +void UIBroadcastBalloon::Slot19() { + if (this->m_messages.size() == 0) { + this->Show(0); + return; + } + + this->Show(1); + for (auto& entry : this->m_messages) { + std::list& messages = entry.second; + for (auto it = messages.begin(); it != messages.end();) { + if (it->start + it->duration < std::chrono::system_clock::now()) { + it = messages.erase(it); + this->Invalidate(); + } else { + ++it; + } + } + } +} + +// OFFSET: 0x007C3580 +// FUNCTION: fn_09338 +bool UIBroadcastBalloon::CanHitTest() { + return false; +} diff --git a/Source/UI/UIBroadcastBalloon.h b/Source/UI/UIBroadcastBalloon.h new file mode 100644 index 00000000..1320c1a7 --- /dev/null +++ b/Source/UI/UIBroadcastBalloon.h @@ -0,0 +1,53 @@ +#ifndef RAGNAROK_SOURCE_UI_UIBROADCASTBALLOON_H +#define RAGNAROK_SOURCE_UI_UIBROADCASTBALLOON_H + +#include "UI/Window/UIWindow.h" + +#include +#include +#include +#include +#include +#include + +struct UIButtonContext; + +struct BroadcastBalloonLine { + /* 0000 */ std::string text; + /* 0018 */ std::uint32_t color; + /* 001C */ std::uint32_t outlineColor; +}; + +struct BroadcastBalloonMessage { + /* 0000 */ std::list lines; + /* 0008 */ std::chrono::system_clock::time_point start; + /* 0010 */ std::chrono::seconds duration; + /* 0018 */ int fontHeight; + /* 001C */ std::uint32_t backgroundColor; + /* 0020 */ int width; + /* 0024 */ int height; +}; + +class UIBroadcastBalloon : public UIWindow { +public: + UIBroadcastBalloon(UIWindowContext& windows, UIButtonContext& controls); + bool CanHitTest() override; + void Slot19() override; + bool Slot42(void* output) override; + void Slot43() override; + + void AddMessage(int category, const BroadcastBalloonMessage& message); + +protected: + /* 007C */ std::map> m_messages; + UIButtonContext* m_controls; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(BroadcastBalloonLine) == 0x20); +static_assert(offsetof(BroadcastBalloonMessage, start) == 0x08); +static_assert(offsetof(BroadcastBalloonMessage, height) == 0x24); +static_assert(sizeof(BroadcastBalloonMessage) == 0x28); +#endif + +#endif diff --git a/data/compile.json b/data/compile.json index 99418697..0edc699c 100644 --- a/data/compile.json +++ b/data/compile.json @@ -760,6 +760,7 @@ "Source/UI/Text/UITransBalloonText.cpp": 2, "Source/UI/TextTagExtraction.cpp": 2, "Source/UI/TextTagRegistry.cpp": 2, + "Source/UI/UIBroadcastBalloon.cpp": 2, "Source/UI/UICaptchaRegisterWnd.cpp": 2, "Source/UI/UIChangeMaterialWnd.cpp": 2, "Source/UI/UIGradeEnchantWnd.cpp": 2,