Batch #8318 by eAthena: 0 byte-identical, 2 confirmed by peer review. - fn_14818 EnchantUI::CUIIngredient::Slot15: logic confirmed by peer review - fn_14772 CUITabButton::CUITabButton: logic confirmed by peer review Peer-reviewed-by: Yommy, dori Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes).
967 lines
30 KiB
C++
967 lines
30 KiB
C++
#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 <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <format>
|
|
#include <iterator>
|
|
#include <new>
|
|
|
|
namespace {
|
|
|
|
template <std::size_t Length, std::uint64_t Key>
|
|
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<const char*>(this->m_text);
|
|
}
|
|
|
|
private:
|
|
std::uint8_t m_text[Length];
|
|
bool m_encrypted;
|
|
};
|
|
|
|
}
|
|
|
|
// GLOBAL: gl_24882
|
|
template <>
|
|
CEnchantMgr* Singleton<CEnchantMgr>::s_instance = nullptr;
|
|
|
|
// OFFSET: 0x009F9B40
|
|
// OFFSET: 0x009F9C10
|
|
// FUNCTION: fn_14814
|
|
// FUNCTION: fn_14815
|
|
template <class T, class D> void CUIListBoxEx<T, D>::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<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::Slot15(int, int);
|
|
template void CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::Slot15(int, int);
|
|
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::Paint();
|
|
template void CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::Paint();
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::Slot35(int);
|
|
template void CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::Slot35(int);
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::OnScroll(int);
|
|
template void CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::OnScroll(int);
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::ClearItems();
|
|
template void CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::ClearItems();
|
|
template int CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::AddData(const EnchantUI::CEnchantItemData&, bool);
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::ClearData();
|
|
template void CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::UpdateScrollBar();
|
|
|
|
// OFFSET: 0x006863F0
|
|
// FUNCTION: fn_05565
|
|
template <>
|
|
CEnchantMgr* Singleton<CEnchantMgr>::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: 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<sizeof(offImageSource), 0x050505679903579BULL> 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<sizeof(onImageSource), 0xDD3913D5D7F97BE1ULL> 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<sizeof(fontSource), 0xBF2B4B31397FF5A7ULL> 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<sizeof(fontSource), 0x83EFBFBB37EDBDBDULL> 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<void(CUIRadioButton*)> 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<CUIMainPage*>(this->parent)) {
|
|
mainPage->firstPage->Slot59(value);
|
|
mainPage->secondPage->Slot59(value);
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00A05290
|
|
// FUNCTION: fn_14972
|
|
int CUIEnchantPage::FindEmptySlot() {
|
|
const std::vector<int>& 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<EnchantOptionInfo*> 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<CUIEnchantPage*>(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<CUIEnchantItem, CEnchantItemData>* list = this->enchantList;
|
|
if (static_cast<int>(list->selected) >= 0 && list->selected < list->data.size()) {
|
|
auto it = std::next(list->data.begin(), list->selected);
|
|
CEnchantMgr::GetInstance()->RequestPerfectEnchant(
|
|
this->group, static_cast<int16_t>(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast<int16_t>(this->unknown_00B0), it->itemId
|
|
);
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00A04E60
|
|
// FUNCTION: fn_14966
|
|
void CUINormalRandom::Slot55() {
|
|
CEnchantMgr::GetInstance()->RequestRandomEnchant(
|
|
this->group, static_cast<int16_t>(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast<int16_t>(this->unknown_00B0)
|
|
);
|
|
}
|
|
|
|
// OFFSET: 0x00A04F10
|
|
// FUNCTION: fn_14967
|
|
void CUIReset::Slot55() {
|
|
CEnchantMgr::GetInstance()->RequestResetEnchant(this->group, static_cast<int16_t>(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<int16_t>(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast<int16_t>(this->unknown_00B0)
|
|
);
|
|
return;
|
|
}
|
|
|
|
CUIListBoxEx<CUIEnchantItem, CEnchantItemData>* list = this->enchantList;
|
|
if (static_cast<int>(list->selected) >= 0 && list->selected < list->data.size()) {
|
|
auto it = std::next(list->data.begin(), list->selected);
|
|
CEnchantMgr::GetInstance()->RequestUpgradePerfectEnchant(
|
|
this->group, static_cast<int16_t>(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast<int16_t>(this->unknown_00B0), it->itemId
|
|
);
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00A050E0
|
|
// FUNCTION: fn_14969
|
|
void CUIUpgradeRandom::Slot55() {
|
|
CEnchantMgr::GetInstance()->RequestUpgradeRandomEnchant(
|
|
this->group, static_cast<int16_t>(this->gameItem->GetItemInfo()->unknown_0000[1]), static_cast<int16_t>(this->unknown_00B0)
|
|
);
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00A08110
|
|
// FUNCTION: fn_15017
|
|
bool CUIEnchantUI::CanHitTest() {
|
|
return !CEnchantMgr::GetInstance()->m_unknown_0030;
|
|
}
|
|
|
|
// OFFSET: 0x009F6D40
|
|
// FUNCTION: fn_14766
|
|
template <>
|
|
CUIListBoxEx<EnchantUI::CUIEnchantItem, EnchantUI::CEnchantItemData>::CUIListBoxEx(UIWindowContext& context)
|
|
: CUIControl(context), scrollBar(nullptr), itemWidth(0), rowHeight(0), itemX(0), itemY(0), unknown_00C4(0), selected(static_cast<unsigned int>(-1)),
|
|
unknown_00E0(1), rebind(0) {
|
|
}
|
|
|
|
// OFFSET: 0x009F6F00
|
|
// FUNCTION: fn_14767
|
|
template <>
|
|
CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::CUIListBoxEx(UIWindowContext& context)
|
|
: CUIControl(context), scrollBar(nullptr), itemWidth(0), rowHeight(0), itemX(0), itemY(0), unknown_00C4(0), selected(static_cast<unsigned int>(-1)),
|
|
unknown_00E0(1), rebind(0) {
|
|
}
|
|
|
|
// OFFSET: 0x009F7670
|
|
// FUNCTION: fn_14774
|
|
template <>
|
|
CUIListBoxEx<EnchantUI::CUITargetItem, ITEM_INFO>::~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<unsigned>(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<CUIMainPage*>(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<EnchantOptionInfo*> 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<const int, int>& material) {
|
|
return static_cast<unsigned int>(g_session.GetInventoryItemCount(material.first)) < static_cast<unsigned int>(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<EnchantOptionInfo*> 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<EnchantOptionInfo*> 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<const EnchantPerfectInfo*>(&this->enchantInfo->resetCost));
|
|
if (!this->enchantInfo->resetEnabled) {
|
|
this->unknown_00A0->disabled = 1;
|
|
this->unknown_00A0->Invalidate();
|
|
return false;
|
|
}
|
|
|
|
const std::vector<int>& 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<EnchantOptionInfo*> 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<int> 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<const EnchantPerfectInfo*>(*option));
|
|
return true;
|
|
}
|
|
|
|
// OFFSET: 0x00A075E0
|
|
// FUNCTION: fn_15013
|
|
bool CUIUpgradeRandom::Slot56() {
|
|
if (this->unknown_00B0 == -1) {
|
|
return false;
|
|
}
|
|
|
|
std::optional<EnchantOptionInfo*> 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<int> 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<const EnchantPerfectInfo*>(&base->second));
|
|
return true;
|
|
}
|
|
}
|