#include "UI/Enchant/EnchantUI.h" #include "Client/Session.h" #include "Contents/EnchantMgr.h" #include "Render/Renderer.h" #include "UI/Buttons/UIRadioButton.h" #include "UI/Lists/UIVScrollBar.h" #include "UI/Text/UITextViewerControl.h" #include "UI/Text/UIStaticTextControl.h" #include "Utility/Cipher/XorBytes.h" #include #include #include #include #include #include #include namespace { template class EncryptedName { public: explicit EncryptedName(const std::uint8_t (&source)[Length]) : m_encrypted(true) { for (std::uint64_t index = 0; index < Length; ++index) { this->m_text[index] = source[index]; } } ~EncryptedName() { for (std::uint64_t index = 0; index < Length; ++index) { this->m_text[index] = 0; } } const char* Text() { if (this->m_encrypted) { utility::XorBytes(this->m_text, Length, Key); this->m_encrypted = false; } return reinterpret_cast(this->m_text); } private: std::uint8_t m_text[Length]; bool m_encrypted; }; } // GLOBAL: gl_24882 template <> CEnchantMgr* Singleton::s_instance = nullptr; // OFFSET: 0x009F9B40 // OFFSET: 0x009F9C10 // FUNCTION: fn_14814 // FUNCTION: fn_14815 template void CUIListBoxEx::Slot15(int width, int height) { this->scrollBar = new CUIVScrollBar(*this->m_context); this->scrollBar->Show(0); this->scrollBar->SetOnChange([this](int position) { this->OnScroll(position); }); this->AddChild(this->scrollBar); } template void CUIListBoxEx::Slot15(int, int); template void CUIListBoxEx::Slot15(int, int); template void CUIListBoxEx::Paint(); template void CUIListBoxEx::Paint(); template void CUIListBoxEx::Slot35(int); template void CUIListBoxEx::Slot35(int); template void CUIListBoxEx::OnScroll(int); template void CUIListBoxEx::OnScroll(int); template void CUIListBoxEx::ClearItems(); template void CUIListBoxEx::ClearItems(); template int CUIListBoxEx::AddData(const EnchantUI::CEnchantItemData&, bool); template void CUIListBoxEx::ClearData(); template void CUIListBoxEx::UpdateScrollBar(); // OFFSET: 0x006863F0 // FUNCTION: fn_05565 template <> CEnchantMgr* Singleton::GetInstance() { if (!s_instance) { s_instance = new (std::nothrow) CEnchantMgr; } return s_instance; } // OFFSET: 0x00A04860 // FUNCTION: fn_14958 CUIEnchantUI::~CUIEnchantUI() { CEnchantMgr::GetInstance()->SendCloseEnchant(); } namespace EnchantUI { // OFFSET: 0x009F70C0 // FUNCTION: fn_14768 CUIEnchantPage::CUIEnchantPage(UIWindowContext& context) : CUIControl(context), unknown_0080{}, gameItem(nullptr), unknown_0088(), unknown_0090(), enchantList(nullptr), unknown_009C{}, enchantInfo(nullptr), group(0), unknown_00B0(-1) { } // OFFSET: 0x009F72F0 // FUNCTION: fn_14770 CUIMainPage::CUIMainPage(UIWindowContext& context) : CUIControl(context), firstPage(nullptr), secondPage(nullptr), tab(nullptr) { } } // OFFSET: 0x009F7380 // FUNCTION: fn_14771 CUITab::CUITab(UIWindowContext& context) : CUIControl(context), current(nullptr) { } // OFFSET: 0x009F7420 // FUNCTION: fn_14772 CUITabButton::CUITabButton(UIWindowContext& context) : CUIImageTextButton(context), page(nullptr), useSkinColors(0), normalColor(0xFFFFFF), selectedColor(0xFFFFFF), borderColor(0xCECECE), normalCell{0, 0}, selectedCell{0, 0}, borderCell{3, 1} { } // OFFSET: 0x009F74E0 namespace EnchantUI { // OFFSET: 0x009F9CE0 // FUNCTION: fn_14816 void CUIEnchantItem::Slot15(int width, int height) { this->itemIcon = new CUIGameItem(*this->m_context); this->itemIcon->SetMode(0); this->itemIcon->SetScale(100); this->itemIcon->Move(2, 3); this->itemIcon->unknown_0084 = 1; this->itemIcon->unknown_0085 = 1; this->AddChild(this->itemIcon); this->name = new CUIStaticText(*this->m_context); this->name->SetSize(101, 12); this->name->Move(28, 9); this->name->SetText("", 0); this->name->SetFitText(1); this->name->SetAlign(0, 6); { const std::uint8_t fontSource[] = {0xC8, 0xEC, 0x87, 0x64, 0xD8, 0x7F}; // GLOBAL: gl_25222 thread_local EncryptedName fontName(fontSource); const std::string font(fontName.Text()); this->name->SetFont(font); } this->name->SetFontHeight(12); this->name->SetBold(1); this->name->SetColor(0xC89682); this->name->SetShadow(0, 0xFFFFFF); this->name->SetOutline(0, 0xFFFFFF); this->AddChild(this->name); } // OFFSET: 0x00A00880 // OFFSET: 0x009F9880 // FUNCTION: fn_14809 void CUIEnchantPage::Slot58(int) { } // OFFSET: 0x009F99B0 // FUNCTION: fn_14812 CUIIngredient* CUIEnchantPage::CreateIngredient() { CUIIngredient* ingredient = new CUIIngredient(*this->m_context); ingredient->SetSize(70, 50); ingredient->Move(4, 200); this->AddChild(ingredient); return ingredient; } // OFFSET: 0x009FB800 // FUNCTION: fn_14818 void CUIIngredient::Slot15(int width, int height) { this->unknown_0080 = new CUIStaticImage(*this->m_context); this->unknown_0080->SetSize(70, 50); this->unknown_0080->Move(0, 0); { const std::uint8_t offImageSource[] = { 0x5B, 0xF8, 0xC3, 0x63, 0xA7, 0xCB, 0xC0, 0xC8, 0x5D, 0xB3, 0xC3, 0x55, 0xDA, 0xBF, 0x59, 0x40, 0xF5, 0x34, 0x6B, 0xF8, 0x09, 0x71, 0x50, 0x4C, 0xC7, 0x35, 0x64, 0xC6, 0x02, 0x6B, 0x66, 0x5A, 0xF4, 0x31, 0x65, 0xB7, 0x05, 0x68, 0x75, 0x05 }; // GLOBAL: gl_25225 thread_local EncryptedName offImageName(offImageSource); const std::string offImage(offImageName.Text()); this->unknown_0080->SetImage(offImage); } this->AddChild(this->unknown_0080); this->unknown_0084 = new CUIStaticImage(*this->m_context); this->unknown_0084->SetSize(70, 50); this->unknown_0084->Move(0, 0); { const std::uint8_t onImageSource[] = { 0x21, 0xD4, 0x39, 0x2D, 0x15, 0xDD, 0xFC, 0x10, 0x27, 0x9F, 0x39, 0x1B, 0x68, 0xA9, 0x65, 0x98, 0x8F, 0x18, 0x91, 0xB6, 0xBB, 0x67, 0x6C, 0x94, 0xBD, 0x19, 0x9E, 0x88, 0xB0, 0x7D, 0x5A, 0x82, 0x8E, 0x15, 0xD7, 0xB5, 0xB8, 0x63, 0x39 }; // GLOBAL: gl_25226 thread_local EncryptedName onImageName(onImageSource); const std::string onImage(onImageName.Text()); this->unknown_0084->SetImage(onImage); } this->AddChild(this->unknown_0084); this->itemIcon = new CUIGameItem(*this->m_context); this->itemIcon->SetMode(0); this->itemIcon->SetScale(100); this->itemIcon->Move(6, 10); this->itemIcon->unknown_0084 = 1; this->itemIcon->unknown_0085 = 1; this->AddChild(this->itemIcon); this->ownedText = new CUIStaticText(*this->m_context); this->ownedText->SetSize(40, 14); this->ownedText->Move(30, 17); this->ownedText->SetText("", 0); this->ownedText->SetFitText(0); this->ownedText->SetAlign(6, 6); { const std::uint8_t fontSource[] = {0xE0, 0x80, 0x13, 0x50, 0x5C, 0x4B}; // GLOBAL: gl_25227 thread_local EncryptedName fontName(fontSource); const std::string font(fontName.Text()); this->ownedText->SetFont(font); } this->ownedText->SetFontHeight(12); this->ownedText->SetBold(1); this->ownedText->SetColor(0xF0F0F0); this->ownedText->SetShadow(0, 0xFFFFFF); this->ownedText->SetOutline(1, 0x965532); this->AddChild(this->ownedText); this->requiredText = new CUIStaticText(*this->m_context); this->requiredText->SetSize(40, 14); this->requiredText->Move(30, 31); this->requiredText->SetText("", 0); this->requiredText->SetFitText(0); this->requiredText->SetAlign(6, 6); { const std::uint8_t fontSource[] = {0xFA, 0xC8, 0x81, 0x5E, 0xD6, 0xBF}; // GLOBAL: gl_25228 thread_local EncryptedName fontName(fontSource); const std::string font(fontName.Text()); this->requiredText->SetFont(font); } this->requiredText->SetFontHeight(12); this->requiredText->SetBold(1); this->requiredText->SetColor(0xF0F0F0); this->requiredText->SetShadow(0, 0xFFFFFF); this->requiredText->SetOutline(1, 0x965532); this->AddChild(this->requiredText); } // OFFSET: 0x009F9B30 // FUNCTION: fn_14813 void CUIEnchantPage::Slot54(CUIEnchantItem* item, const CEnchantItemData& data) { } } // OFFSET: 0x009F9710 // FUNCTION: fn_14808 CUIEnchantUI* CreateEnchantUI() { return new CUIEnchantUI(); } namespace EnchantUI { // OFFSET: 0x009FD340 // FUNCTION: fn_14823 void CUISlot0::Slot15(int width, int height) { CUISlot::Slot15(width, height); this->radioButton->Move(59, 1); this->radioButton->SetOnSelect([this](CUIRadioButton* button) { this->SelectSlot(button); }); this->radioButton->disabled = 0; this->radioButton->Invalidate(); this->radioButton->SetImages( std::string("유저인터페이스\\EnchantUI\\btn_socket_out.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_select.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_over.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_out.bmp") ); this->radioButton->checked = false; this->radioButton->Invalidate(); this->itemIcon->Move(69, 11); this->itemIcon->unknown_0084 = 1; this->itemIcon->unknown_0085 = 1; this->description->Move(0, 44); this->description->SetTextOffset(2, 0); this->description->SetFont(std::string("Gulim")); this->description->SetFontHeight(12); this->description->SetBold(true); this->description->SetTextColor(0x965532); this->description->SetOutline(false, 0xFFFFFF); this->description->SetLineSpacing(1); this->description->SetBackgroundColor(0xFFFF00FF); this->description->SetPadding(0, 0, 0, 0); this->description->SetScrollBarSize(13, 39); this->description->SetScrollStep(13); this->description->SetScrollBarButtonImages(std::string("유저인터페이스\\scroll0up.bmp"), std::string("유저인터페이스\\scroll0down.bmp")); this->description->SetScrollBarBackground(std::string("유저인터페이스\\scroll0mid.bmp")); this->description->SetScrollBarThumbImages( std::string("유저인터페이스\\scroll0bar_up.bmp"), std::string("유저인터페이스\\scroll0bar_mid.bmp"), std::string("유저인터페이스\\scroll0bar_down.bmp") ); } // OFFSET: 0x009FDC30 // FUNCTION: fn_14824 void CUISlot1::Slot15(int width, int height) { CUISlot::Slot15(width, height); this->radioButton->Move(59, 1); this->radioButton->SetOnSelect([this](CUIRadioButton* button) { this->SelectSlot(button); }); this->radioButton->disabled = 0; this->radioButton->Invalidate(); this->radioButton->SetImages( std::string("유저인터페이스\\EnchantUI\\btn_socket_out.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_select.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_over.bmp"), std::string("유저인터페이스\\EnchantUI\\btn_socket_out.bmp") ); this->radioButton->checked = false; this->radioButton->Invalidate(); this->itemIcon->Move(69, 11); this->itemIcon->unknown_0084 = 1; this->itemIcon->unknown_0085 = 1; this->description->Move(0, 44); this->description->SetTextOffset(2, 0); this->description->SetFont(std::string("Gulim")); this->description->SetFontHeight(12); this->description->SetBold(true); this->description->SetTextColor(0x965532); this->description->SetOutline(false, 0xFFFFFF); this->description->SetLineSpacing(1); this->description->SetBackgroundColor(0xFFFF00FF); this->description->SetPadding(0, 0, 0, 0); this->description->SetScrollBarSize(13, 39); this->description->SetScrollStep(13); this->description->SetScrollBarButtonImages(std::string("유저인터페이스\\scroll0up.bmp"), std::string("유저인터페이스\\scroll0down.bmp")); this->description->SetScrollBarBackground(std::string("유저인터페이스\\scroll0mid.bmp")); this->description->SetScrollBarThumbImages( std::string("유저인터페이스\\scroll0bar_up.bmp"), std::string("유저인터페이스\\scroll0bar_mid.bmp"), std::string("유저인터페이스\\scroll0bar_down.bmp") ); } // OFFSET: 0x009FE520 // OFFSET: 0x009FEE10 } namespace EnchantUI { // OFFSET: 0x009FF700 } // OFFSET: 0x00A021D0 // FUNCTION: fn_14838 void CUIRadioButton::SetOnSelect(std::function callback) { this->onSelect = callback; } namespace EnchantUI { // OFFSET: 0x00A02D80 // FUNCTION: fn_14845 void CUIEnchantPage::Slot59(int value) { this->unknown_00B0 = value; } // OFFSET: 0x00A05190 // FUNCTION: fn_14970 void CUIUpgradeEnchantPage::Slot58(int value) { if (CUIMainPage* mainPage = dynamic_cast(this->parent)) { mainPage->firstPage->Slot59(value); mainPage->secondPage->Slot59(value); } } // OFFSET: 0x00A05290 // FUNCTION: fn_14972 int CUIEnchantPage::FindEmptySlot() { const std::vector& slots = this->enchantInfo->slots; auto it = std::find_if(slots.begin(), slots.end(), [item = *this->gameItem->GetItemInfo()](int slot) { if (slot >= 4) { return true; } int card; std::memcpy(&card, item.unknown_001C + slot * 4, sizeof(card)); return card == 0; }); if (it == slots.end()) { return -1; } return *it; } // OFFSET: 0x00A053D0 // FUNCTION: fn_14973 ITEM_INFO* CUITargetItem::GetItemInfo() { return this->itemIcon->GetItemInfo(); } // OFFSET: 0x00A053E0 // FUNCTION: fn_14974 std::optional CUIEnchantPage::FindOption(uint32_t key) { if (this->enchantInfo == nullptr) { return std::nullopt; } auto it = this->enchantInfo->options.find(key); if (it != this->enchantInfo->options.end()) { return &it->second; } return std::nullopt; } // OFFSET: 0x00A05CE0 // FUNCTION: fn_14980 void CUISlot::Paint() { this->ClearSurface(0xFFFF00FF); } } // OFFSET: 0x00A05DF0 // FUNCTION: fn_14983 void CUIEnchantUI::Slot16() { this->Move((g_renderer->viewport.width - this->geometry.width) / 2, (g_renderer->viewport.height - this->geometry.height) / 2); } namespace EnchantUI { // OFFSET: 0x00A06050 // FUNCTION: fn_14988 void CUISlot0::Slot16() { CUISlot::Slot16(); this->radioButton->SetId(0); } // OFFSET: 0x00A06090 // FUNCTION: fn_14989 void CUISlot1::Slot16() { CUISlot::Slot16(); this->radioButton->SetId(1); } // OFFSET: 0x00A060D0 // FUNCTION: fn_14990 void CUISlot2::Slot16() { CUISlot::Slot16(); this->radioButton->SetId(2); } // OFFSET: 0x00A06110 // FUNCTION: fn_14991 void CUISlot3::Slot16() { CUISlot::Slot16(); this->radioButton->SetId(3); } // OFFSET: 0x00A06150 // FUNCTION: fn_14992 void CUISlot::Slot16() { this->itemIcon->hitEnabled = 0; this->description->scrollBar->SetHidden(1); } } // OFFSET: 0x00A06190 // FUNCTION: fn_14994 void CUIEnchantUI::Slot25(int x, int y) { if (this->titleBar->IsPointInside(x, y)) { CUIFrameWnd::Slot25(x, y); } } namespace EnchantUI { // OFFSET: 0x00A061C0 // FUNCTION: fn_14995 void CUIEnchantItem::Slot28(int x, int y) { this->itemIcon->Slot28(x - this->itemIcon->geometry.x, y - this->itemIcon->geometry.y); } // OFFSET: 0x00A061F0 // FUNCTION: fn_14996 void CUITargetItem::Slot28(int x, int y) { this->itemIcon->Slot28(x - this->itemIcon->geometry.x, y - this->itemIcon->geometry.y); } // OFFSET: 0x00A06220 // FUNCTION: fn_14997 void CUIEnchantItem::Slot32(int x, int y) { this->itemIcon->Slot32(x - this->itemIcon->geometry.x, y - this->itemIcon->geometry.y); } // OFFSET: 0x00A06250 // FUNCTION: fn_14998 void CUISlot::Slot32(int x, int y) { if (this->itemIcon->IsPointInside(x, y)) { this->itemIcon->Slot32(x - this->itemIcon->geometry.x, y - this->itemIcon->geometry.y); } } // OFFSET: 0x00A06290 // FUNCTION: fn_14999 void CUITargetItem::Slot32(int x, int y) { this->itemIcon->Slot32(x - this->itemIcon->geometry.x, y - this->itemIcon->geometry.y); } // OFFSET: 0x00A062C0 // FUNCTION: fn_15000 void CUISlot::SelectSlot(UIWindow* button) { if (CUIEnchantPage* page = dynamic_cast(this->parent)) { page->Slot58(button->geometry.id); } } // OFFSET: 0x00A06A50 // FUNCTION: fn_15005 void CUITargetItem::SetSelected(bool selected) { if (selected) { if (this->selected != 1) { this->itemIcon->Move(this->itemIcon->geometry.x + 1, this->itemIcon->geometry.y + 1); this->unknown_00EC->Move(this->unknown_00EC->geometry.x + 1, this->unknown_00EC->geometry.y + 1); this->unknown_00F0->Move(this->unknown_00F0->geometry.x + 1, this->unknown_00F0->geometry.y + 1); } } else if (this->selected == 1) { this->itemIcon->Move(this->itemIcon->geometry.x - 1, this->itemIcon->geometry.y - 1); this->unknown_00EC->Move(this->unknown_00EC->geometry.x - 1, this->unknown_00EC->geometry.y - 1); this->unknown_00F0->Move(this->unknown_00F0->geometry.x - 1, this->unknown_00F0->geometry.y - 1); } CUIListItem::SetSelected(selected); } // OFFSET: 0x00A04D70 // FUNCTION: fn_14965 void CUINormalPerfect::Slot55() { CUIListBoxEx* list = this->enchantList; if (static_cast(list->selected) >= 0 && list->selected < list->data.size()) { auto it = std::next(list->data.begin(), list->selected); CEnchantMgr::GetInstance()->RequestPerfectEnchant( this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast(this->unknown_00B0), it->itemId ); } } // OFFSET: 0x00A04E60 // FUNCTION: fn_14966 void CUINormalRandom::Slot55() { CEnchantMgr::GetInstance()->RequestRandomEnchant( this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast(this->unknown_00B0) ); } // OFFSET: 0x00A04F10 // FUNCTION: fn_14967 void CUIReset::Slot55() { CEnchantMgr::GetInstance()->RequestResetEnchant(this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1])); } // OFFSET: 0x00A04FB0 // FUNCTION: fn_14968 void CUIUpgradePerfect::Slot55() { if (this->unknown_00B8) { CEnchantMgr::GetInstance()->RequestUpgradeEnchant( this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast(this->unknown_00B0) ); return; } CUIListBoxEx* list = this->enchantList; if (static_cast(list->selected) >= 0 && list->selected < list->data.size()) { auto it = std::next(list->data.begin(), list->selected); CEnchantMgr::GetInstance()->RequestUpgradePerfectEnchant( this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast(this->unknown_00B0), it->itemId ); } } // OFFSET: 0x00A050E0 // FUNCTION: fn_14969 void CUIUpgradeRandom::Slot55() { CEnchantMgr::GetInstance()->RequestUpgradeRandomEnchant( this->group, static_cast(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast(this->unknown_00B0) ); } } // OFFSET: 0x00A08110 // FUNCTION: fn_15017 bool CUIEnchantUI::CanHitTest() { return !CEnchantMgr::GetInstance()->m_unknown_0030; } // OFFSET: 0x009F6D40 // FUNCTION: fn_14766 template <> CUIListBoxEx::CUIListBoxEx(UIWindowContext& context) : CUIControl(context), scrollBar(nullptr), itemWidth(0), rowHeight(0), itemX(0), itemY(0), unknown_00C4(0), selected(static_cast(-1)), unknown_00E0(1), rebind(0) { } // OFFSET: 0x009F6F00 // FUNCTION: fn_14767 template <> CUIListBoxEx::CUIListBoxEx(UIWindowContext& context) : CUIControl(context), scrollBar(nullptr), itemWidth(0), rowHeight(0), itemX(0), itemY(0), unknown_00C4(0), selected(static_cast(-1)), unknown_00E0(1), rebind(0) { } // OFFSET: 0x009F7670 // FUNCTION: fn_14774 template <> CUIListBoxEx::~CUIListBoxEx() { } namespace EnchantUI { // OFFSET: 0x00A06BE0 // FUNCTION: fn_15007 void CUIEnchantItem::SetFrozen(bool frozen) { this->itemIcon->transparent = frozen; this->itemIcon->Invalidate(); this->name->SetColor(frozen ? 0xAAAAAA : 0xC89682); } // OFFSET: 0x00A05860 // FUNCTION: fn_14977 void UpdateEnchantItem(CUIEnchantItem* item, const CEnchantItemData* data) { ITEM_INFO info; info.SetItemId(data->itemId); info.identified = 1; item->itemIcon->SetItem(info); const std::string name = info.DisplayName(0); item->name->SetText(name.c_str(), static_cast(name.size())); item->SetFrozen(data->frozen); } // OFFSET: 0x00A05B90 // FUNCTION: fn_14979 void UpdateTargetItem(CUITargetItem* item, ITEM_INFO* info) { item->itemIcon->SetItem(*info); if (info->unknown_0088 > 0) { item->unknown_00EC->SetImage(std::format("유저인터페이스\\grade_enchant\\grade_icon{}.bmp", info->unknown_0088)); } item->unknown_00EC->Show(info->unknown_0088 > 0); const std::string name = info->DisplayName(0); item->unknown_00F0->SetText(name.c_str(), name.size()); } // OFFSET: 0x00A05E20 // FUNCTION: fn_14984 void CUIMainPage::Slot16() { CUITabButton* button = this->tab->FindButton(this->firstPage); if (button != nullptr) { button->disabled = 1; button->Invalidate(); } button = this->tab->FindButton(this->secondPage); if (button != nullptr) { button->disabled = 1; button->Invalidate(); } } } // OFFSET: 0x009F3160 // FUNCTION: fn_14752 void CUIEnchantUI::Slot53() { this->unknown_00B4 = 0; this->m_resolveSkin = true; this->Resize(690, 470); this->Move(0, 0); this->Slot15(this->geometry.width, this->geometry.height); this->Slot16(); } // OFFSET: 0x00A06170 // FUNCTION: fn_14993 void EnchantUI::CUIUpgradeRandom::Slot16() { EnchantUI::CUIEnchantPage::Slot16(); this->enchantList->unknown_00C4 = 1; this->enchantList->Invalidate(); } namespace EnchantUI { // OFFSET: 0x00A05CF0 // FUNCTION: fn_14981 void CUIEnchantPage::Slot16() { for (int i = 0; i < 8; ++i) { CUIIngredient* ingredient = this->CreateIngredient(); ingredient->Move(ingredient->geometry.x + i % 4 * 74, ingredient->geometry.y + i / 4 * 52); this->unknown_0090.push_back(ingredient); } this->ResetSlots(); } // OFFSET: 0x00A05E80 // FUNCTION: fn_14985 void CUINormalPerfect::Slot16() { CUIEnchantPage::Slot16(); for (const auto& slot : this->unknown_0088) { slot.second->radioButton->disabled = 1; slot.second->radioButton->Invalidate(); } } // OFFSET: 0x00A05F10 // FUNCTION: fn_14986 void CUINormalRandom::Slot16() { CUIEnchantPage::Slot16(); for (const auto& slot : this->unknown_0088) { slot.second->radioButton->disabled = 1; slot.second->radioButton->Invalidate(); } this->enchantList->unknown_00C4 = 1; this->enchantList->Invalidate(); } // OFFSET: 0x00A05FB0 // FUNCTION: fn_14987 void CUIReset::Slot16() { CUIEnchantPage::Slot16(); for (const auto& slot : this->unknown_0088) { slot.second->radioButton->disabled = 1; slot.second->radioButton->Invalidate(); } this->enchantList->unknown_00C4 = 1; this->enchantList->Invalidate(); } // OFFSET: 0x00A06350 // FUNCTION: fn_15002 void CUIEnchantPage::Slot57() { this->enchantList->ClearData(); for (CUIIngredient* ingredient : this->unknown_0090) { ingredient->Show(0); } const std::string count = std::to_string(0); this->unknown_009C->SetText(count.c_str(), count.size()); this->unknown_00A0->disabled = 1; this->unknown_00A0->Invalidate(); const bool ready = this->Slot56(); if (CUIMainPage* mainPage = dynamic_cast(this->parent)) { if (CUITabButton* button = mainPage->tab->FindButton(this)) { button->disabled = !ready; button->Invalidate(); } } } // OFFSET: 0x00A06520 // FUNCTION: fn_15003 void CUIReset::Slot57() { for (CUIIngredient* ingredient : this->unknown_0090) { ingredient->Show(0); } const std::string count = std::to_string(0); this->unknown_009C->SetText(count.c_str(), count.size()); this->unknown_00A0->disabled = 1; this->unknown_00A0->Invalidate(); this->enchantList->ClearData(); this->Slot56(); } // OFFSET: 0x00A06800 // FUNCTION: fn_15004 void CUIEnchantPage::ResetSlots() { for (const auto& slot : this->unknown_0088) { slot.second->Show(0); } for (CUIIngredient* ingredient : this->unknown_0090) { ingredient->Show(0); } this->enchantList->ClearData(); const std::string count = std::to_string(0); this->unknown_009C->SetText(count.c_str(), count.size()); this->unknown_00A0->disabled = 1; this->unknown_00A0->Invalidate(); this->Slot59(-1); } // OFFSET: 0x00A06F80 // FUNCTION: fn_15009 bool CUINormalPerfect::Slot56() { const int slot = this->FindEmptySlot(); if (slot == -1) { return false; } std::optional option = this->FindOption(slot); if (!option) { return false; } this->Slot59(slot); if ((*option)->perfectEnchants.size() == 0) { return false; } for (const auto& entry : (*option)->perfectEnchants) { const bool lackZeny = g_session.m_zeny < entry.second.cost; const bool lackMaterial = std::any_of(entry.second.materials.begin(), entry.second.materials.end(), [](const std::pair& material) { return static_cast(g_session.GetInventoryItemCount(material.first)) < static_cast(material.second); }); this->enchantList->AddData({entry.first, lackZeny || lackMaterial}, false); } this->enchantList->UpdateScrollBar(); return true; } } namespace EnchantUI { // OFFSET: 0x00A05470 // FUNCTION: fn_14975 void CUINormalPerfect::Slot54(CUIEnchantItem* item, const CEnchantItemData& data) { for (CUIIngredient* ingredient : this->unknown_0090) { ingredient->Show(0); } const std::string count = std::to_string(0); this->unknown_009C->SetText(count.c_str(), count.size()); std::optional option = this->FindOption(this->unknown_00B0); if (option && (*option)->perfectEnchants.size() != 0) { const int itemId = std::atoi(item->itemIcon->GetItemInfo()->itemId.c_str()); auto it = (*option)->perfectEnchants.find(itemId); if (it != (*option)->perfectEnchants.end()) { this->SetCost(it->second); } } } // OFFSET: 0x00A05640 // FUNCTION: fn_14976 void CUIUpgradePerfect::Slot54(CUIEnchantItem* item, const CEnchantItemData& data) { for (CUIIngredient* ingredient : this->unknown_0090) { ingredient->Show(0); } const std::string count = std::to_string(0); this->unknown_009C->SetText(count.c_str(), count.size()); std::optional option = this->FindOption(this->unknown_00B0); if (option && (*option)->perfectUpgrades.size() != 0) { const int baseItemId = std::atoi(this->unknown_0088[this->unknown_00B0]->itemIcon->GetItemInfo()->itemId.c_str()); auto base = (*option)->perfectUpgrades.find(baseItemId); if (base != (*option)->perfectUpgrades.end()) { const int itemId = std::atoi(item->itemIcon->GetItemInfo()->itemId.c_str()); auto it = base->second.find(itemId); if (it != base->second.end()) { this->SetCost(it->second); } } } } // OFFSET: 0x00A072E0 // FUNCTION: fn_15011 bool CUIReset::Slot56() { this->SetCost(*reinterpret_cast(&this->enchantInfo->resetCost)); if (!this->enchantInfo->resetEnabled) { this->unknown_00A0->disabled = 1; this->unknown_00A0->Invalidate(); return false; } const std::vector& slots = this->enchantInfo->slots; const bool enchanted = std::any_of(slots.begin(), slots.end(), [item = *this->gameItem->GetItemInfo()](int slot) { if (slot >= 4) { return false; } int card; std::memcpy(&card, item.unknown_001C + slot * 4, sizeof(card)); return card != 0; }); this->unknown_00A0->disabled = !enchanted; this->unknown_00A0->Invalidate(); return enchanted; } // OFFSET: 0x00A07BA0 // FUNCTION: fn_15015 void CUIIngredient::SetMaterial(int itemId, int required) { ITEM_INFO info; info.SetItemId(itemId); info.identified = 1; this->itemIcon->SetItem(info); const int owned = g_session.GetInventoryItemCount(itemId); const std::string ownedCount = std::to_string(owned); this->ownedText->SetText(ownedCount.c_str(), ownedCount.size()); const std::string requiredCount = std::format("/{}", required); this->requiredText->SetText(requiredCount.c_str(), requiredCount.size()); const bool enough = owned >= required; this->unknown_0084->Show(enough); const std::uint32_t color = enough ? 0xF0F0F0 : 0x5A3CE6; this->ownedText->SetColor(color); this->ownedText->SetOutline(enough, 0x965532); this->requiredText->SetColor(color); this->requiredText->SetOutline(enough, 0x965532); } // OFFSET: 0x00A07F00 // FUNCTION: fn_15016 void CUIEnchantPage::SetCost(const EnchantPerfectInfo& info) { auto material = info.materials.begin(); for (CUIIngredient* ingredient : this->unknown_0090) { if (material == info.materials.end()) { ingredient->Show(0); } else { ingredient->Show(1); ingredient->SetMaterial(material->first, material->second); ++material; } } const std::string cost = std::to_string(info.cost); this->unknown_009C->SetText(cost.c_str(), cost.size()); const bool lackZeny = g_session.m_zeny < info.cost; const bool lackMaterial = std::any_of(this->unknown_0090.begin(), this->unknown_0090.end(), [](CUIIngredient* ingredient) { return ingredient->geometry.visible && !ingredient->unknown_0084->geometry.visible; }); this->unknown_00A0->disabled = lackZeny || lackMaterial; this->unknown_00A0->Invalidate(); } // OFFSET: 0x00A07100 // FUNCTION: fn_15010 bool CUINormalRandom::Slot56() { const int slot = this->FindEmptySlot(); if (slot == -1) { return false; } std::optional option = this->FindOption(slot); if (!option) { return false; } this->Slot59(slot); if ((*option)->enchants.size() == 0) { return false; } auto grade = (*option)->enchants.find(this->gameItem->GetItemInfo()->unknown_0088); if (grade == (*option)->enchants.end()) { return false; } std::list itemIds; for (const auto& entry : grade->second) { itemIds.push_back(entry.first); } for (int itemId : itemIds) { this->enchantList->AddData({itemId, false}, false); } this->enchantList->UpdateScrollBar(); this->SetCost(*reinterpret_cast(*option)); return true; } // OFFSET: 0x00A075E0 // FUNCTION: fn_15013 bool CUIUpgradeRandom::Slot56() { if (this->unknown_00B0 == -1) { return false; } std::optional option = this->FindOption(this->unknown_00B0); if (!option || (*option)->randomUpgrades.size() == 0) { return false; } const int baseItemId = std::atoi(this->unknown_0088[this->unknown_00B0]->itemIcon->GetItemInfo()->itemId.c_str()); auto base = (*option)->randomUpgrades.find(baseItemId); if (base == (*option)->randomUpgrades.end()) { return false; } std::list itemIds; for (const auto& entry : base->second.rates) { itemIds.push_back(entry.first); } for (int itemId : itemIds) { this->enchantList->AddData({itemId, false}, false); } this->enchantList->UpdateScrollBar(); this->SetCost(*reinterpret_cast(&base->second)); return true; } // OFFSET: 0x00A07440 }