diff --git a/Source/Client/Application/Launcher.cpp b/Source/Client/Application/Launcher.cpp index 3bfe2c6c..66a08c82 100644 --- a/Source/Client/Application/Launcher.cpp +++ b/Source/Client/Application/Launcher.cpp @@ -4,9 +4,12 @@ #include "Contents/Configuration/ServiceConfiguration.h" #include #include +#include #include +#include #include #include +#include // STATIC: 0x0173ad64 // GLOBAL: gl_24472 @@ -652,6 +655,56 @@ bool RestartFromCommandLine(char* commandLine) { return true; } +// OFFSET: 0x00E58A60 +// FUNCTION: fn_24365 +void ApplyAiPatch() { + const std::uint8_t patchSource[] = {0xAE, 0x6A, 0x8A, 0x9F, 0x78, 0x25, 0xF2, 0x53, 0xEF}; + thread_local EncryptedName patchName(patchSource); + if (_access(patchName.Text(), 0) != 0) { + return; + } + + const std::uint8_t aiSource[] = {0x5A, 0x44, 0x39}; + thread_local EncryptedName aiName(aiSource); + if (_access(aiName.Text(), 0) != 0) { + return; + } + + const std::uint8_t backupSource[] = {0x80, 0x9A, 0x84, 0x15, 0xFA, 0x16, 0xA8, 0x3C, 0xB1, 0xD3}; + thread_local EncryptedName backupName(backupSource); + if (_access(backupName.Text(), 0) == 0) { + const std::uint8_t deleteSource[] = {0xDA, 0xE4, 0x2A, 0xAD, 0xB0, 0x5E, 0x86, 0x18, 0xEB, 0xAD}; + char path[16] = {}; + thread_local EncryptedName deleteName(deleteSource); + std::strcpy(path, deleteName.Text()); + + SHFILEOPSTRUCTA operation; + operation.hwnd = nullptr; + operation.wFunc = FO_DELETE; + operation.pFrom = path; + operation.pTo = nullptr; + operation.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION; + operation.fAnyOperationsAborted = FALSE; + operation.hNameMappings = nullptr; + operation.lpszProgressTitle = nullptr; + SHFileOperationA(&operation); + } + + const std::uint8_t backupTargetSource[] = {0xBC, 0xB4, 0x22, 0x91, 0x80, 0x76, 0x92, 0x6E, 0x8D, 0xFD}; + thread_local EncryptedName backupTarget(backupTargetSource); + const char* backup = backupTarget.Text(); + const std::uint8_t currentSource[] = {0x9E, 0x90, 0x5D}; + thread_local EncryptedName current(currentSource); + std::rename(current.Text(), backup); + + const std::uint8_t installTargetSource[] = {0x52, 0x8E, 0x03}; + thread_local EncryptedName installTarget(installTargetSource); + const char* install = installTarget.Text(); + const std::uint8_t patchedSource[] = {0x92, 0xA4, 0xC8, 0x65, 0x2C, 0x2F, 0xC6, 0xC9, 0xD3}; + thread_local EncryptedName patched(patchedSource); + std::rename(patched.Text(), install); +} + // INLINED void ExitForProtectionCheck(const char* commandLine) { if (std::strstr(commandLine, "/check_themida")) { diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index 5391c979..8a7bf969 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -144,6 +144,7 @@ public: void ReceiveStatDump(const unsigned char* packet); void ReceiveActorText(const unsigned char* packet); void ReceiveRentalItemTime(const unsigned char* packet); + void ReceiveEquipWindowMicroscope(const unsigned char* packet); void ReceiveMemorialDungeonInfo(const unsigned char* packet); void ReceiveMemorialDungeonNotify(const unsigned char* packet); void ReceiveMemorialDungeonSubscriptionInfo(const unsigned char* packet); diff --git a/Source/Client/Mode/EquipWindowLook.h b/Source/Client/Mode/EquipWindowLook.h new file mode 100644 index 00000000..6d556955 --- /dev/null +++ b/Source/Client/Mode/EquipWindowLook.h @@ -0,0 +1,27 @@ +#ifndef RAGNAROK_SOURCE_CLIENT_MODE_EQUIPWINDOWLOOK_H +#define RAGNAROK_SOURCE_CLIENT_MODE_EQUIPWINDOWLOOK_H + +#include + +struct EquipWindowLook { + /* 0000 */ int job; + /* 0004 */ int sex; + /* 0008 */ int head; + /* 000C */ int accessory; + /* 0010 */ int accessory2; + /* 0014 */ int accessory3; + /* 0018 */ int bodyPalette; + /* 001C */ int headPalette; + /* 0020 */ int robe; + /* 0024 */ int unknown_0024; + /* 0028 */ char name[24]; +}; + +extern EquipWindowLook g_equipWindowLook; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(EquipWindowLook) == 0x40); +static_assert(offsetof(EquipWindowLook, name) == 0x28); +#endif + +#endif diff --git a/Source/Client/Mode/GameModeEquipWindow.cpp b/Source/Client/Mode/GameModeEquipWindow.cpp new file mode 100644 index 00000000..bbad17b8 --- /dev/null +++ b/Source/Client/Mode/GameModeEquipWindow.cpp @@ -0,0 +1,69 @@ +#include "Client/Mode/CGameMode.h" +#include "Client/Mode/EquipWindowLook.h" +#include "Client/Session.h" +#include "Contents/Items/ItemInfo.h" +#include "UI/Window/UIWindowMgr.h" + +#include +#include + +struct ClientItemLists { + bool Equip(const ITEM_INFO& item); + void ResetEquipped(); +}; + +// GLOBAL: gl_25085 +EquipWindowLook g_equipWindowLook; + +// OFFSET: 0x00D325F0 +// FUNCTION: fn_21743 +void CGameMode::ReceiveEquipWindowMicroscope(const unsigned char* packet) { + reinterpret_cast(&g_session)->ResetEquipped(); + g_session.ResetItems3500(); + + const int length = *reinterpret_cast(packet + 2); + const int count = static_cast(length - 45) / 37u; + const unsigned char* entry = packet + 45; + for (int index = 0; index < count; ++index, entry += 37) { + ITEM_INFO item; + item.unknown_0000[0] = entry[6]; + item.unknown_0000[2] = *reinterpret_cast(entry + 7); + item.unknown_0000[1] = *reinterpret_cast(entry); + item.unknown_0000[3] = *reinterpret_cast(entry + 9); + item.quantity = 1; + std::memcpy(item.unknown_001C, entry + 12, sizeof(item.unknown_001C)); + item.SetItemId(*reinterpret_cast(entry + 2)); + + const unsigned char flags = entry[36]; + item.identified = flags & 1; + item.unknown_005D = (flags >> 1) & 1; + item.unknown_0060 = entry[11]; + item.unknown_0068 = *reinterpret_cast(entry + 28); + item.unknown_0064 = *reinterpret_cast(entry + 32); + item.unknown_0070 = *reinterpret_cast(entry + 34); + item.unknown_0074[0] = (flags >> 2) & 1; + + if (item.unknown_0000[3] != 0) { + if (g_session.IsCostumeLocation(item.unknown_0000[3])) { + g_session.EquipItem3500(item); + } else { + reinterpret_cast(&g_session)->Equip(item); + } + } + } + + g_equipWindowLook.job = *reinterpret_cast(packet + 0x1C); + g_equipWindowLook.sex = packet[0x2C]; + g_equipWindowLook.head = *reinterpret_cast(packet + 0x1E); + g_equipWindowLook.accessory = *reinterpret_cast(packet + 0x20); + g_equipWindowLook.accessory2 = *reinterpret_cast(packet + 0x22); + g_equipWindowLook.accessory3 = *reinterpret_cast(packet + 0x24); + g_equipWindowLook.bodyPalette = *reinterpret_cast(packet + 0x2A); + g_equipWindowLook.headPalette = *reinterpret_cast(packet + 0x28); + std::memcpy(g_equipWindowLook.name, packet + 4, sizeof(g_equipWindowLook.name)); + g_equipWindowLook.robe = *reinterpret_cast(packet + 0x26); + + if (!g_windowMgr.CloseWindow(0x8B)) { + g_windowMgr.EnsureWindow(0x8B); + } +} diff --git a/Source/Client/Session.h b/Source/Client/Session.h index 9222081f..f5a556d4 100644 --- a/Source/Client/Session.h +++ b/Source/Client/Session.h @@ -652,6 +652,7 @@ public: int MercSkillLevel(int skillId) const; int HomunSkillLevel(int skillId) const; CRenderObject* FindGameActor(std::uint32_t aid) const; + int GetPetTargetAid() const; bool sub_E1DE30(std::uint32_t aid); int sub_DDD2C0(const ITEM_INFO& item); const char* GetJobName(unsigned job, int sex); diff --git a/Source/Contents/Jobs/JobInheritance.cpp b/Source/Contents/Jobs/JobInheritance.cpp index 2dda856c..0d4c1d78 100644 --- a/Source/Contents/Jobs/JobInheritance.cpp +++ b/Source/Contents/Jobs/JobInheritance.cpp @@ -1212,6 +1212,33 @@ int CSession::GetPairValue() const { return third == -1 ? second : third; } +// OFFSET: 0x00E0A220 +// FUNCTION: fn_23793 +int CSession::GetPetTargetAid() const { + int target = 0; + std::uint32_t aid = *reinterpret_cast(&this->unknown_5570[8]); + CGameMode* mode; + CRenderObject* actor; + if (aid != 0 && (mode = static_cast(g_modeMgr.GetGameMode())) != nullptr && + (actor = mode->scene->GetGameActorByAID(aid)) != nullptr) { + target = static_cast(actor)->hitTargetAid; + } + + aid = *reinterpret_cast(&this->unknown_5620[8]); + if (aid != 0 && (mode = static_cast(g_modeMgr.GetGameMode())) != nullptr && + (actor = mode->scene->GetGameActorByAID(aid)) != nullptr) { + target = static_cast(actor)->hitTargetAid; + } + + if (target > 0) { + mode = static_cast(g_modeMgr.GetGameMode()); + if (mode == nullptr || mode->scene->GetGameActorByAID(target) == nullptr) { + return 0; + } + } + return target; +} + // OFFSET: 0x00E1D950 // FUNCTION: fn_23854 int CSession::WorldUnknown0050() const { diff --git a/Source/UI/Item/UIItemStoreWnd.cpp b/Source/UI/Item/UIItemStoreWnd.cpp new file mode 100644 index 00000000..f42d0fbe --- /dev/null +++ b/Source/UI/Item/UIItemStoreWnd.cpp @@ -0,0 +1,10 @@ +#include "UI/Item/UIItemStoreWnd.h" +#include "UI/Text/UIStaticText.h" + +#include + +// OFFSET: 0x00D532D0 +// FUNCTION: fn_21919 +void UIItemStoreWnd::SetTitle(std::string title) { + this->title->SetTextFitted(title.c_str()); +} diff --git a/Source/UI/Item/UIItemStoreWnd.h b/Source/UI/Item/UIItemStoreWnd.h index 705ebed4..0a31154f 100644 --- a/Source/UI/Item/UIItemStoreWnd.h +++ b/Source/UI/Item/UIItemStoreWnd.h @@ -5,6 +5,7 @@ #include "UI/Item/UIItemBaseWnd.h" #include +#include class UICheckBox; class UIStaticText; @@ -17,6 +18,7 @@ public: void Slot25(int x, int y) override; __declspec(noinline) ITEM_INFO GetItemAt(int x, int y); void SetTabChecked(int index, bool checked); + void SetTitle(std::string title); /* 00F0 */ int unknown_00F0; /* 00F4 */ unsigned char unknown_00F4[0x04]; diff --git a/data/compile.json b/data/compile.json index 0598c9f4..a74034ea 100644 --- a/data/compile.json +++ b/data/compile.json @@ -259,6 +259,7 @@ "Source/Client/Input/Mouse.cpp": 0, "Source/Client/Input/VietnameseInput.cpp": 0, "Source/Client/Mode/GameMode.cpp": 0, + "Source/Client/Mode/GameModeEquipWindow.cpp": 0, "Source/Client/Mode/GameModeHandlers.cpp": 0, "Source/Client/Mode/GameModeMiniMap.cpp": 0, "Source/Client/Mode/GameModeNames.cpp": 0, @@ -865,6 +866,7 @@ "Source/UI/Guild/UIGuildWnd.cpp": 2, "Source/UI/Guild/UIGuild_Storage_Log.cpp": 2, "Source/UI/Item/UIEquipmentPropertiesWnd.cpp": 2, + "Source/UI/Item/UIItemStoreWnd.cpp": 2, "Source/UI/Item/UIMerchantItemLogWnd.cpp": 2, "Source/UI/Item/UIParaItemShopWnd.cpp": 2, "Source/UI/Items/MergeItemWnd.cpp": 2,