diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index ca04408b..34357778 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -115,6 +115,11 @@ public: void ReceiveActorSequenceDeadline(const unsigned char* packet); void ReceiveActorSignal(const unsigned char* packet); void IgnorePacket(const unsigned char* packet); + void ReceiveItem1738EquipList(const unsigned char* packet); + void ReceiveItem1738EquipListOptions(const unsigned char* packet); + void ReceiveItem1738NormalList(const unsigned char* packet); + void ReceiveHomunculusProperty64(const unsigned char* packet); + void ReceiveHomunculusProperty(const unsigned char* packet); void ReceiveHuntingList(const unsigned char* packet); void ReceiveEquipAck(const unsigned char* packet); void ReceiveToggleNotice(const unsigned char* packet); diff --git a/Source/Client/Mode/GameModePackets.cpp b/Source/Client/Mode/GameModePackets.cpp index 29d53b3a..0825f10b 100644 --- a/Source/Client/Mode/GameModePackets.cpp +++ b/Source/Client/Mode/GameModePackets.cpp @@ -13,6 +13,10 @@ #include "Contents/PlayerNameDictionary.h" #include "Contents/World/World.h" #include "Network/TextEncoding.h" +#include "UI/Item/UIItemStoreWnd.h" +#include "UI/Text/UIStaticText.h" +#include "UI/UIBarGraph.h" +#include "UI/Window/UIHomunInfoWnd.h" #include "UI/Window/UIWindowMgr.h" #include "Utility/Text/FormatString.h" @@ -30,6 +34,10 @@ struct SessionMessengerContext { void UpdateRandomStats(); }; +struct ClientItemLists { + bool AddItem1738(const ITEM_INFO& item, unsigned char refresh); +}; + namespace { #pragma pack(push, 1) struct SimulationPacketHeader { @@ -71,8 +79,89 @@ namespace { /* 000F */ unsigned char unknown_000F[4]; /* 0013 */ char name[1]; }; + + struct ItemStoreListHeader { + /* 0000 */ std::uint16_t packetType; + /* 0002 */ std::uint16_t length; + /* 0004 */ char title[24]; + }; + + struct StoreItemOption { + /* 0000 */ std::int16_t id; + /* 0002 */ std::int16_t value; + /* 0004 */ unsigned char param; + }; + + struct StoreEquipRecord { + /* 0000 */ std::int16_t index; + /* 0002 */ int itemId; + /* 0006 */ unsigned char type; + /* 0007 */ std::uint32_t location; + /* 000B */ std::uint32_t wearState; + /* 000F */ unsigned char refine; + /* 0010 */ std::uint32_t cards[4]; + /* 0020 */ std::uint32_t expireTime; + /* 0024 */ std::uint16_t bindOnEquip; + /* 0026 */ std::uint16_t sprite; + /* 0028 */ unsigned char flags; + }; + + struct StoreEquipOptionRecord { + /* 0000 */ std::int16_t index; + /* 0002 */ int itemId; + /* 0006 */ unsigned char type; + /* 0007 */ std::uint32_t location; + /* 000B */ std::uint32_t wearState; + /* 000F */ unsigned char refine; + /* 0010 */ std::uint32_t cards[4]; + /* 0020 */ std::uint32_t expireTime; + /* 0024 */ std::uint16_t bindOnEquip; + /* 0026 */ std::uint16_t sprite; + /* 0028 */ unsigned char optionCount; + /* 0029 */ StoreItemOption options[5]; + /* 0042 */ unsigned char flags; + }; + + struct StoreNormalRecord { + /* 0000 */ std::int16_t index; + /* 0002 */ int itemId; + /* 0006 */ unsigned char type; + /* 0007 */ std::int16_t amount; + /* 0009 */ std::uint32_t location; + /* 000D */ std::uint32_t cards[4]; + /* 001D */ std::uint32_t expireTime; + /* 0021 */ unsigned char flags; + }; + + struct HomunculusProperty64Packet { + /* 0000 */ std::uint16_t packetType; + /* 0002 */ std::uint16_t type; + /* 0004 */ std::int64_t value; + }; + + struct HomunculusPropertyPacket { + /* 0000 */ std::uint16_t packetType; + /* 0002 */ std::uint16_t type; + /* 0004 */ int value; + }; #pragma pack(pop) + static_assert(sizeof(ItemStoreListHeader) == 0x1C); + static_assert(sizeof(StoreEquipRecord) == 0x29); + static_assert(sizeof(StoreEquipOptionRecord) == 0x43); + static_assert(sizeof(StoreNormalRecord) == 0x22); + + // INLINED + int CountStoreItemOptions(const StoreItemOption* options) { + int count = 0; + for (int index = 0; index < 5; ++index) { + if (options[index].id != 0) { + ++count; + } + } + return count; + } + static_assert(sizeof(SimulationPacketHeader) == 0x24); static_assert(sizeof(EnchantSimulationRecord) == 0x1C); static_assert(sizeof(PackageSimulationRecord) == 0x32); @@ -208,6 +297,168 @@ void CGameMode::ReceiveActorSignal(const unsigned char* packet) { void CGameMode::IgnorePacket(const unsigned char*) { } +// OFFSET: 0x00D2B710 +// FUNCTION: fn_21710 +void CGameMode::ReceiveItem1738EquipList(const unsigned char* data) { + const ItemStoreListHeader* packet = reinterpret_cast(data); + UIItemStoreWnd* window = static_cast(g_windowMgr.EnsureWindow(33)); + if (packet->title[0] != '\0') { + std::string title(packet->title); + window->title->SetTextFitted(title.c_str()); + } + + const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreEquipRecord); + const StoreEquipRecord* record = reinterpret_cast(data + sizeof(ItemStoreListHeader)); + for (int i = 0; i < count; ++i, ++record) { + StoreItemOption options[5] = {}; + ITEM_INFO item; + item.unknown_0000[0] = record->type; + item.unknown_0000[1] = record->index; + item.unknown_0000[2] = record->location; + item.unknown_0000[3] = record->wearState; + item.quantity = 1; + std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards)); + item.SetItemId(record->itemId); + item.identified = record->flags & 1; + item.unknown_005D = (record->flags >> 1) & 1; + item.unknown_0060 = record->refine; + item.unknown_0068 = record->expireTime; + item.unknown_0064 = record->bindOnEquip; + *reinterpret_cast(item.unknown_0074) = record->sprite; + *reinterpret_cast(item.unknown_0078) = (record->flags >> 2) & 1; + item.unknown_0098 = CountStoreItemOptions(options); + for (int index = 0; index < item.unknown_0098; ++index) { + reinterpret_cast(item.unknown_009C)[index] = options[index]; + } + reinterpret_cast(&g_session)->AddItem1738(item, 1); + } + + g_session.RefreshItem1738Window(); +} + +// OFFSET: 0x00D2BA50 +// FUNCTION: fn_21711 +void CGameMode::ReceiveItem1738EquipListOptions(const unsigned char* data) { + const ItemStoreListHeader* packet = reinterpret_cast(data); + UIItemStoreWnd* window = static_cast(g_windowMgr.EnsureWindow(33)); + if (packet->title[0] != '\0') { + std::string title(packet->title); + window->title->SetTextFitted(title.c_str()); + } + + const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreEquipOptionRecord); + const StoreEquipOptionRecord* record = reinterpret_cast(data + sizeof(ItemStoreListHeader)); + for (int i = 0; i < count; ++i, ++record) { + StoreItemOption options[5]; + std::memcpy(options, record->options, sizeof(options)); + ITEM_INFO item; + item.unknown_0000[0] = record->type; + item.unknown_0000[1] = record->index; + item.unknown_0000[2] = record->location; + item.unknown_0000[3] = record->wearState; + item.quantity = 1; + std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards)); + item.SetItemId(record->itemId); + item.identified = record->flags & 1; + item.unknown_005D = (record->flags >> 1) & 1; + item.unknown_0060 = record->refine; + item.unknown_0068 = record->expireTime; + item.unknown_0064 = record->bindOnEquip; + *reinterpret_cast(item.unknown_0074) = record->sprite; + *reinterpret_cast(item.unknown_0078) = (record->flags >> 2) & 1; + item.unknown_0098 = CountStoreItemOptions(options); + for (int index = 0; index < item.unknown_0098; ++index) { + reinterpret_cast(item.unknown_009C)[index] = options[index]; + } + reinterpret_cast(&g_session)->AddItem1738(item, 1); + } + + g_session.RefreshItem1738Window(); +} + +// OFFSET: 0x00D2BDF0 +// FUNCTION: fn_21712 +void CGameMode::ReceiveItem1738NormalList(const unsigned char* data) { + const ItemStoreListHeader* packet = reinterpret_cast(data); + UIItemStoreWnd* window = static_cast(g_windowMgr.EnsureWindow(33)); + if (packet->title[0] != '\0') { + std::string title(packet->title); + window->title->SetTextFitted(title.c_str()); + } + + const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreNormalRecord); + const StoreNormalRecord* record = reinterpret_cast(data + sizeof(ItemStoreListHeader)); + for (int i = 0; i < count; ++i, ++record) { + ITEM_INFO item; + item.unknown_0000[0] = record->type; + item.unknown_0000[1] = record->index; + item.unknown_0000[3] = record->location; + item.unknown_0000[2] = 0; + item.quantity = record->amount; + std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards)); + item.SetItemId(record->itemId); + item.identified = record->flags & 1; + item.unknown_005D = 0; + item.unknown_0060 = 0; + item.unknown_0068 = record->expireTime; + item.unknown_0074[0] = (record->flags >> 1) & 1; + reinterpret_cast(&g_session)->AddItem1738(item, 0); + } + + g_session.RefreshItem1738Window(); +} + +// OFFSET: 0x00D2C190 +// FUNCTION: fn_21713 +void CGameMode::ReceiveHomunculusProperty64(const unsigned char* data) { + if (!data) { + return; + } + + const HomunculusProperty64Packet* packet = reinterpret_cast(data); + switch (packet->type) { + case 1: + g_session.m_homunExp = packet->value; + g_session.RefreshHomunculusInfo(); + if (UIHomunInfoWnd* window = static_cast(g_windowMgr.window_03C0)) { + window->expBar->SetValues(g_session.m_homunExp, g_session.m_homunMaxExp); + } + break; + case 5: + g_session.m_homunHp = static_cast(packet->value); + g_session.RefreshHomunculusInfo(); + break; + case 7: + g_session.m_homunSp = static_cast(packet->value); + g_session.RefreshHomunculusInfo(); + break; + } + + g_session.RefreshHomunculusInfo(); +} + +// OFFSET: 0x00D2C250 +// FUNCTION: fn_21714 +void CGameMode::ReceiveHomunculusProperty(const unsigned char* data) { + const HomunculusPropertyPacket* packet = reinterpret_cast(data); + switch (packet->type) { + case 5: + g_session.m_homunHp = packet->value; + g_session.RefreshHomunculusInfo(); + break; + case 7: + g_session.m_homunSp = packet->value; + g_session.RefreshHomunculusInfo(); + break; + case 1: + g_session.m_homunExp = packet->value; + g_session.RefreshHomunculusInfo(); + break; + } + + g_session.RefreshHomunculusInfo(); +} + // OFFSET: 0x00D2C2B0 // FUNCTION: fn_21715 void CGameMode::ReceiveHuntingList(const unsigned char* packet) { diff --git a/Source/Client/Session.cpp b/Source/Client/Session.cpp index 87fd10a4..86a2908f 100644 --- a/Source/Client/Session.cpp +++ b/Source/Client/Session.cpp @@ -1681,6 +1681,15 @@ void CSession::RefreshHpSpWindows() { static_cast(g_modeMgr.GetGameMode())->NotifyItemBounds(); } +// OFFSET: 0x00DF9460 +// FUNCTION: fn_23515 +void CSession::RefreshHomunculusInfo() { + if (g_modeMgr.GetGameMode() && g_windowMgr.window_03C0) { + g_windowMgr.window_03C0->Invalidate(); + g_windowMgr.window_03C0->InvalidateChildren(); + } +} + // OFFSET: 0x00DFC0B0 // FUNCTION: fn_23593 void CSession::FlagInventoryItem(std::uint32_t id) { diff --git a/Source/Client/Session.h b/Source/Client/Session.h index 874992dd..3a69196c 100644 --- a/Source/Client/Session.h +++ b/Source/Client/Session.h @@ -271,7 +271,14 @@ public: /* 556C */ int unknown_556C; /* 5570 */ unsigned char unknown_5570[0x4C]; /* 55BC */ int m_homunJob; - /* 55C0 */ unsigned char unknown_55C0[0x58]; + /* 55C0 */ unsigned char unknown_55C0[0x04]; + /* 55C4 */ int m_homunHp; + /* 55C8 */ unsigned char unknown_55C8[0x04]; + /* 55CC */ int m_homunSp; + /* 55D0 */ unsigned char unknown_55D0[0x10]; + /* 55E0 */ std::int64_t m_homunExp; + /* 55E8 */ std::int64_t m_homunMaxExp; + /* 55F0 */ unsigned char unknown_55F0[0x28]; /* 5618 */ int m_traceAi; /* 561C */ int m_homunActionState; /* 5620 */ unsigned char unknown_5620[0xC]; @@ -706,6 +713,7 @@ public: void UpdateUnknown1708(int enabled); void RefreshItem1738Window(); void RefreshHpSpWindows(); + void RefreshHomunculusInfo(); void EraseRecord0734(int id); void IgnoreText(std::string text); void SaveMiniPartyInfo(); diff --git a/Source/UI/Item/UIItemStoreWnd.h b/Source/UI/Item/UIItemStoreWnd.h index 456ce6bb..ac4c8b11 100644 --- a/Source/UI/Item/UIItemStoreWnd.h +++ b/Source/UI/Item/UIItemStoreWnd.h @@ -7,6 +7,7 @@ #include class UICheckBox; +class UIStaticText; class UIItemStoreWnd : public UIItemBaseWnd { public: @@ -21,7 +22,9 @@ public: /* 00F8 */ unsigned char unknown_00F8[0x08]; /* 0100 */ UICheckBox* tabs[8]; /* 0120 */ int unknown_0120[8]; - /* 0140 */ unsigned char unknown_0140[0x24]; + /* 0140 */ unsigned char unknown_0140[0x1C]; + /* 015C */ UIStaticText* title; + /* 0160 */ unsigned char unknown_0160[0x04]; /* 0164 */ WindowPlacement* saved; }; diff --git a/Source/UI/Window/UIHomunInfoWnd.h b/Source/UI/Window/UIHomunInfoWnd.h index f0d354e0..933c1388 100644 --- a/Source/UI/Window/UIHomunInfoWnd.h +++ b/Source/UI/Window/UIHomunInfoWnd.h @@ -4,11 +4,15 @@ #include "UI/Window/UIFrameWnd.h" #include "Contents/UserData/NormalItemSlots.h" +class UIINT64BarGraph; + class UIHomunInfoWnd : public UIFrameWnd { public: void Slot11() override; - /* 00B4 */ unsigned char unknown_00B4[0x18]; + /* 00B4 */ unsigned char unknown_00B4[0x10]; + /* 00C4 */ UIINT64BarGraph* expBar; + /* 00C8 */ unsigned char unknown_00C8[0x04]; /* 00CC */ HomunInfoWndInfo* saved; };