#include "Network/Packets/NoticeEnums.h" #include "UI/Window/WindowId.h" #include "UI/Window/UIEnums.h" #include "Client/Mode/ModeMessage.h" #include "Contents/Actors/ActorEnums.h" #include "Client/Mode/CGameMode.h" #include "Contents/Combat/CombatTypes.h" #include "Contents/Items/InventoryType.h" #include "Client/Mode/ModeMgr.h" #include "Client/Replay.h" #include "Client/Session.h" #include "Contents/Actors/Pc.h" #include "Contents/ClientState.h" #include "Contents/Effects/EffectActor.h" #include "Contents/Effects/RagEffect.h" #include "Contents/GuildInfo.h" #include "Contents/Skills/ClientSkills.h" #include "Contents/Items/ItemInfo.h" #include "Contents/MessageTable.h" #include "Contents/Messenger/ClientMessenger.h" #include "Contents/Messenger/MessengerMember.h" #include "Contents/PetEvolutionMgr.h" #include "Contents/World/World.h" #include "UI/Items/UIEquipWnd.h" #include "UI/Window/UIWindow.h" #include "UI/Window/UIWindowMgr.h" #include "Utility/Text/FormatString.h" #include "Resource/MSGSTRINGID.h" #include #include #include #include #include #include #include #include #include #include #include extern template std::list::iterator std::list::erase(std::list::const_iterator) noexcept; namespace { struct EquipSlotDecoder { EquipSlots Decode(int mask) const; }; #pragma pack(push, 1) struct ItemRandomOption { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t value; /* 0004 */ unsigned char param; }; struct ItemAddPacket { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t index; /* 0004 */ int amount; /* 0008 */ int itemId; /* 000C */ unsigned char type; /* 000D */ unsigned char identified; /* 000E */ unsigned char damaged; /* 000F */ unsigned char refine; /* 0010 */ std::uint32_t cards[4]; }; struct ItemAddOptionsPacket { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t index; /* 0004 */ int amount; /* 0008 */ int itemId; /* 000C */ unsigned char type; /* 000D */ unsigned char identified; /* 000E */ unsigned char damaged; /* 000F */ unsigned char refine; /* 0010 */ std::uint32_t cards[4]; /* 0020 */ ItemRandomOption options[5]; }; struct ItemAddGradePacket { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t index; /* 0004 */ int amount; /* 0008 */ int itemId; /* 000C */ unsigned char type; /* 000D */ unsigned char identified; /* 000E */ unsigned char damaged; /* 000F */ std::uint32_t cards[4]; /* 001F */ ItemRandomOption options[5]; /* 0038 */ unsigned char refine; /* 0039 */ unsigned char grade; }; struct ItemSlotPacket { /* 0000 */ std::int16_t id; /* 0002 */ int slot; /* 0006 */ unsigned char type; /* 0007 */ int itemId; /* 000B */ unsigned char identified; /* 000C */ unsigned char damaged; /* 000D */ unsigned char refine; /* 000E */ std::uint32_t cards[4]; }; struct ItemSlotOptionsPacket { /* 0000 */ std::int16_t id; /* 0002 */ int slot; /* 0006 */ unsigned char type; /* 0007 */ int itemId; /* 000B */ unsigned char identified; /* 000C */ unsigned char damaged; /* 000D */ unsigned char refine; /* 000E */ std::uint32_t cards[4]; /* 001E */ ItemRandomOption options[5]; }; struct FriendNoticePacket { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t result; /* 0004 */ int accountId; /* 0008 */ int characterId; /* 000C */ char name[24]; }; struct ItemListRecord { /* 0000 */ std::int16_t index; /* 0002 */ int itemId; /* 0006 */ unsigned char type; /* 0007 */ std::int16_t amount; /* 0009 */ int location; /* 000D */ std::uint32_t cards[4]; /* 001D */ int expireTime; /* 0021 */ unsigned char flags; }; #pragma pack(pop) bool IsSameItem(const ITEM_INFO& value, const ITEM_INFO& item) { if (value.itemId != item.itemId || std::memcmp(value.cards, item.cards, sizeof(value.cards)) != 0 || value.identified != item.identified || value.refiningLevel != item.refiningLevel || value.grade != item.grade) { return false; } const int count = item.optionCount; if (value.optionCount != count || value.optionCount >= 5) { return false; } if (value.optionCount != 0) { const auto* left = reinterpret_cast(value.options); const auto* right = reinterpret_cast(item.options); for (int index = 0; index < count; ++index) { if (left[index].id != right[index].id || left[index].value != right[index].value || left[index].param != right[index].param) { return false; } } } return true; } void CountSameNameChar(char left, char right, short& matched) { if (left == 'I' || left == 'l') { if (right == 'I' || right == 'l') { ++matched; } } else if (left == right) { ++matched; } } } #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(ItemRandomOption) == 5); static_assert(sizeof(ItemAddPacket) == 0x20); static_assert(sizeof(ItemAddOptionsPacket) == 0x39); static_assert(sizeof(ItemAddGradePacket) == 0x3A); static_assert(sizeof(ItemListRecord) == 0x22); #endif int GetEquipLocationIndex(const int& mask); namespace EquipmentProperties { class CEquipment { public: virtual ~CEquipment(); virtual void Load(bool force); virtual void Clear(); int& GetId(); }; class CEquipmentAttachable : public CEquipment {}; class CEquipmentWearable : public CEquipment { public: bool IsActive(); bool UpdateActive(); std::vector& GetAttachables(); }; class CEquipmentPropertiesMgr { public: virtual ~CEquipmentPropertiesMgr(); unsigned char unknown_0004[0x9]; bool m_dirty; unsigned char unknown_000E[0x12]; UIWindow* unknown_0020; /* 0024 */ std::unordered_map properties1; /* 0044 */ unsigned char unknown_0044[0x1B8]; CEquipmentPropertiesMgr(); CEquipmentWearable* FindProperty1(const int& key); void AddExpansion(const int& kind, const int& itemId, bool notify); void EquipItem(const int& itemId, bool notify); __declspec(noinline) void EquipWearable(const ITEM_INFO* item, bool notify); void UnequipItem(const int& itemId, bool notify); void UnequipWearable(const int& location); }; } #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(EquipmentProperties::CEquipmentPropertiesMgr) == 0x1FC); #endif // OFFSET: 0x00AA6440 // FUNCTION: fn_17229 void __fastcall RefreshItemWindows(UIWindowMgr* mgr) { if (mgr->itemWnd) { reinterpret_cast(mgr->itemWnd)->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } if (mgr->window_01E8) { mgr->window_01E8->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } if (UIWindow* window = mgr->LookupWindow(WID_QUIKSLOTWND)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } if (mgr->window_4F260) { mgr->window_4F260->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } } // INLINED static int CountItemOptions(const ItemRandomOption* options) { int count = 0; for (int index = 0; index < 5; ++index) { if (options[index].id != 0) { ++count; } } return count; } // OFFSET: 0x00D1F5A0 // FUNCTION: fn_21640 void CGameMode::ReceiveItem1738Add(const unsigned char* data) { const ItemAddPacket packet = *reinterpret_cast(data); ItemRandomOption options[5] = {}; ITEM_INFO item; item.identity[1] = packet.index; item.quantity = packet.amount; item.SetItemId(packet.itemId); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.identity[0] = packet.type; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = 0; g_session.AddItem1738(item, 1); } struct ItemAddSlotPacket { /* 0000 */ std::int16_t id; /* 0002 */ int itemId; /* 0006 */ unsigned char type; /* 0007 */ int slot; /* 000B */ unsigned char identified; /* 000C */ unsigned char damaged; /* 000D */ unsigned char refine; /* 000E */ std::uint32_t cards[4]; }; struct ItemAddSlotOptionsPacket { /* 0000 */ std::int16_t id; /* 0002 */ int itemId; /* 0006 */ unsigned char type; /* 0007 */ int slot; /* 000B */ unsigned char identified; /* 000C */ unsigned char damaged; /* 000D */ unsigned char refine; /* 000E */ std::uint32_t cards[4]; /* 001E */ ItemRandomOption options[5]; }; // OFFSET: 0x00D1F770 // FUNCTION: fn_21641 void ReceiveItemSlot(const unsigned char* data) { const ItemAddSlotPacket packet = *reinterpret_cast(data); ItemRandomOption options[5] = {}; if (packet.slot < 0) { g_modeMgr.gameMode->SendMsg(MM_CANCEL_EXCHANGE_ITEM, 0, 0, 0, 0); } if (packet.itemId == 0) { g_session.SetUnknown5214(packet.slot); return; } ITEM_INFO item; item.identity[0] = packet.type; item.SetItemId(packet.itemId); item.identity[1] = 0xFFFFFFFF; item.quantity = packet.slot; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } g_session.AddItemSlot(item); } // OFFSET: 0x00D1F970 // FUNCTION: fn_21642 void ReceiveItemSlotOptions(const unsigned char* data) { const ItemAddSlotOptionsPacket packet = *reinterpret_cast(data); if (packet.slot < 0) { g_modeMgr.gameMode->SendMsg(MM_CANCEL_EXCHANGE_ITEM, 0, 0, 0, 0); } if (packet.itemId == 0) { g_session.SetUnknown5214(packet.slot); return; } ITEM_INFO item; item.identity[0] = packet.type; item.SetItemId(packet.itemId); item.identity[1] = 0xFFFFFFFF; item.quantity = packet.slot; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.optionCount = CountItemOptions(packet.options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = packet.options[index]; } g_session.AddItemSlot(item); } // OFFSET: 0x00D1FBA0 // FUNCTION: fn_21643 void ReceiveFriendAddResult(const unsigned char* data) { const FriendNoticePacket* packet = reinterpret_cast(data); char name[25]; std::memcpy(name, packet->name, 24); name[24] = '\0'; g_windowMgr.SendNotice(UIM_OPENCHATWND, 0, 0, 0, 0, 0); char buffer[128]; int message; switch (packet->result) { case FRIEND_ADD_SUCCEED: { std::sprintf(buffer, LookupMessage(MSI_FRIEND_ADD_SUCCEED), name); g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(buffer), 0xFFFF00, 0, 0, 0); MessengerMember member; member.name = name; member.accountId = packet->accountId; member.characterId = packet->characterId; member.offline = 0; g_session.AddFriend(member); g_session.RefreshWindow02C8(); return; } case FRIEND_ADD_FALSE: message = 822; break; case FRIEND_ADD_ME_OVERSIZE: g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(LookupMessage(MSI_FRIEND_ADD_ME_OVERSIZE)), 0xFF, 0, 0, 0); return; case FRIEND_ADD_OTHER_OVERSIZE: message = 820; break; case 4: message = 2737; break; case 6: message = 3448; break; case 7: message = 3449; break; case 8: message = 3450; break; default: return; } std::sprintf(buffer, LookupMessage(message), name); g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(buffer), 0xFF, 0, 0, 0); } // OFFSET: 0x00D1FDA0 // FUNCTION: fn_21644 void CGameMode::ReceiveItem1740Add(const unsigned char* data) { const ItemAddPacket packet = *reinterpret_cast(data); ItemRandomOption options[5] = {}; ITEM_INFO item; item.identity[1] = packet.index; item.quantity = packet.amount; item.SetItemId(packet.itemId); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.identity[0] = packet.type; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = 0; g_session.AddItem1740(item); } // OFFSET: 0x00D1FF70 // FUNCTION: fn_21645 void CGameMode::ReceiveItem1740AddOptions(const unsigned char* data) { const auto* packet = reinterpret_cast(data); ItemRandomOption options[5]; std::memcpy(options, packet->options, sizeof(options)); ITEM_INFO item; item.identity[1] = packet->index; item.quantity = packet->amount; item.SetItemId(packet->itemId); item.identified = packet->identified; item.damaged = packet->damaged; item.refiningLevel = packet->refine; item.identity[0] = packet->type; std::memcpy(item.cards, packet->cards, sizeof(packet->cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = 0; g_session.AddItem1740(item); } // OFFSET: 0x00D20180 // FUNCTION: fn_21646 void CGameMode::ReceiveItem1740AddGrade(const unsigned char* data) { const ItemAddGradePacket packet = *reinterpret_cast(data); ItemRandomOption options[5]; std::memcpy(options, packet.options, sizeof(options)); ITEM_INFO item; item.identity[1] = packet.index; item.quantity = packet.amount; item.SetItemId(packet.itemId); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.identity[0] = packet.type; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = packet.grade; g_session.AddItem1740(item); } // OFFSET: 0x00D204B0 // FUNCTION: fn_21647 void CGameMode::ReceiveItem1738AddOptions(const unsigned char* data) { const auto* packet = reinterpret_cast(data); ItemRandomOption options[5]; std::memcpy(options, packet->options, sizeof(options)); ITEM_INFO item; item.identity[1] = packet->index; item.quantity = packet->amount; item.SetItemId(packet->itemId); item.identified = packet->identified; item.damaged = packet->damaged; item.refiningLevel = packet->refine; item.identity[0] = packet->type; std::memcpy(item.cards, packet->cards, sizeof(packet->cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = 0; g_session.AddItem1738(item, 1); } // OFFSET: 0x00D206C0 // FUNCTION: fn_21648 void CGameMode::ReceiveItem1738AddGrade(const unsigned char* data) { const ItemAddGradePacket packet = *reinterpret_cast(data); ItemRandomOption options[5]; std::memcpy(options, packet.options, sizeof(options)); ITEM_INFO item; item.identity[1] = packet.index; item.quantity = packet.amount; item.SetItemId(packet.itemId); item.identified = packet.identified; item.damaged = packet.damaged; item.refiningLevel = packet.refine; item.identity[0] = packet.type; std::memcpy(item.cards, packet.cards, sizeof(packet.cards)); item.optionCount = CountItemOptions(options); for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } item.grade = packet.grade; g_session.AddItem1738(item, 1); } namespace { #pragma pack(push, 1) struct EquipItemListHeader { /* 0000 */ std::int16_t id; /* 0002 */ std::int16_t length; /* 0004 */ unsigned char listType; }; struct EquipItemListRecord { /* 0000 */ std::int16_t index; /* 0002 */ int itemId; /* 0006 */ unsigned char type; /* 0007 */ std::uint32_t location; /* 000B */ std::uint32_t wearState; /* 000F */ std::uint32_t cards[4]; /* 001F */ std::uint32_t expireTime; /* 0023 */ std::uint16_t bindOnEquip; /* 0025 */ std::uint16_t sprite; /* 0027 */ unsigned char optionCount; /* 0028 */ ItemRandomOption options[5]; /* 0041 */ unsigned char refine; /* 0042 */ unsigned char grade; /* 0043 */ unsigned char flags; }; #pragma pack(pop) static_assert(sizeof(EquipItemListHeader) == 5); static_assert(sizeof(EquipItemListRecord) == 0x44); // INLINED __forceinline void FillEquipItemListItem(ITEM_INFO& item, const EquipItemListRecord& record, const ItemRandomOption* options) { item.identity[0] = record.type; item.identity[1] = record.index; item.identity[2] = record.location; item.identity[3] = record.wearState; item.quantity = 1; std::memcpy(item.cards, record.cards, sizeof(record.cards)); item.SetItemId(record.itemId); item.identified = record.flags & 1; item.damaged = (record.flags >> 1) & 1; item.refiningLevel = record.refine; item.expireTime = record.expireTime; item.bindOnEquip = record.bindOnEquip; item.sprite = record.sprite; item.flags[0] = (record.flags >> 2) & 1; item.optionCount = CountItemOptions(options); item.grade = record.grade; for (int index = 0; index < item.optionCount; ++index) { reinterpret_cast(item.options)[index] = options[index]; } } } // OFFSET: 0x00D43940 // FUNCTION: fn_21832 void CGameMode::ReceiveEquipItemList(const unsigned char* packet) { const EquipItemListHeader* header = reinterpret_cast(packet); const EquipItemListRecord* records = reinterpret_cast(packet + sizeof(EquipItemListHeader)); const std::size_t count = (header->length - sizeof(EquipItemListHeader)) / sizeof(EquipItemListRecord); switch (header->listType) { case INVTYPE_STORAGE: case INVTYPE_GUILD_STORAGE: for (std::int64_t i = 0; i < count; ++i) { const EquipItemListRecord record = records[i]; if (record.location == 0x8000) { continue; } ItemRandomOption options[5]; std::memcpy(options, record.options, sizeof(options)); ITEM_INFO item; FillEquipItemListItem(item, record, options); g_session.AddItem1738(item, 0); } break; case INVTYPE_INVENTORY: for (std::int64_t i = 0; i < count; ++i) { const EquipItemListRecord record = records[i]; ItemRandomOption options[5]; std::memcpy(options, record.options, sizeof(options)); ITEM_INFO item; FillEquipItemListItem(item, record, options); if (g_session.IsPetEgg(std::atoi(item.itemId.c_str())) && item.damaged) { g_session.petEggIndex = item.identity[1]; } if (item.identity[3] == 0) { g_session.AddItem1710(item, 0); g_session.UpdateInventoryItem(item, 0); } else { char equipped = 1; if (g_session.IsCostumeLocation(item.identity[3])) { g_session.EquipItem2B50(item, &equipped); } else { g_session.EquipUnknown17F0(item, &equipped); } } if (g_session.IsPetEgg(std::atoi(item.itemId.c_str())) && item.damaged) { const int petEgg = std::atoi(LookupClientItemByIdentity(g_session.m_inventoryItems, g_session.petEggIndex).itemId.c_str()); if (!g_petEvolutionMgr->IsAutoFeeding(petEgg) && g_petAutoFeed == true) { g_session.SendPetAutoFeedOption(0); } if (g_equipmentPropertiesMgr == nullptr) { g_equipmentPropertiesMgr = new (std::nothrow) EquipmentProperties::CEquipmentPropertiesMgr; } const int kind = 0; const int itemId = std::atoi(item.itemId.c_str()); g_equipmentPropertiesMgr->AddExpansion(kind, itemId, false); } } break; case INVTYPE_CART: for (std::int64_t i = 0; i < count; ++i) { const EquipItemListRecord record = records[i]; ItemRandomOption options[5]; std::memcpy(options, record.options, sizeof(options)); ITEM_INFO item; FillEquipItemListItem(item, record, options); g_session.AddItem1740(item); } break; } } // OFFSET: 0x00D443F0 // FUNCTION: fn_21833 void CGameMode::ReceiveItemListPart(unsigned char listType, int count, const unsigned char* data) { const ItemListRecord* record = reinterpret_cast(data); for (int i = 0; i < count; ++i, ++record) { ITEM_INFO item; item.identity[0] = record->type; item.identity[1] = record->index; item.identity[3] = record->location; item.identity[2] = 0; item.quantity = record->amount; std::memcpy(item.cards, record->cards, sizeof(record->cards)); item.SetItemId(record->itemId); item.damaged = 0; item.identified = record->flags & 1; item.expireTime = record->expireTime; item.refiningLevel = 0; item.flags[0] = (record->flags >> 1) & 1; switch (listType) { case INVTYPE_INVENTORY: g_session.UpdateInventoryItem(item, 0); break; case INVTYPE_CART: g_session.AddItem1740(item); break; case INVTYPE_STORAGE: case INVTYPE_GUILD_STORAGE: g_session.AddItem1738(item, 0); break; } } } // OFFSET: 0x00D44580 // FUNCTION: fn_21834 void CGameMode::RefreshItemListWindow(unsigned char listType, bool partial) { if (partial) { return; } switch (listType) { case INVTYPE_STORAGE: case INVTYPE_GUILD_STORAGE: if (UIWindow* window = g_windowMgr.window_0288) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); reinterpret_cast(window)[0x190] = 1; } break; case INVTYPE_INVENTORY: RefreshItemWindows(&g_windowMgr); break; case INVTYPE_CART: g_session.RefreshItem1740Window(); break; } } // OFFSET: 0x00DD43A0 // FUNCTION: fn_23232 __declspec(noinline) EquipSlots EquipSlotDecoder::Decode(int mask) const { std::vector slots; for (int index = 0; index < 10; ++index) { if (mask & 1) { slots.push_back(index); } mask >>= 1; } if (slots.empty()) { return {-1, -1, -1}; } if (slots.size() == 1) { return {-1, -1, slots[0]}; } if (slots.size() == 2) { return {-1, slots[1], slots[0]}; } return {slots[2], slots[1], slots[0]}; } // OFFSET: 0x00DD7030 // FUNCTION: fn_23282 __declspec(noinline) bool CSession::AddItem1710(const ITEM_INFO& item, unsigned char refresh) { for (ITEM_INFO& value : this->m_inventoryItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; value.flags[1] = item.flags[1]; value.flags[0] = item.flags[0]; if (refresh) { RefreshItemWindows(&g_windowMgr); } return false; } } this->m_inventoryItems.push_back(item); if (refresh) { RefreshItemWindows(&g_windowMgr); } return true; } // OFFSET: 0x00DD7210 // FUNCTION: fn_23284 bool CSession::AddItem1768(const ITEM_INFO& item, unsigned char alternate, unsigned char refresh) { for (ITEM_INFO& value : this->merchantPurchaseSavedItems) { if (value.identity[1] == item.identity[1]) { if (!alternate) { value.quantity += item.quantity; if (refresh == 1) { if (UIWindow* window = reinterpret_cast(g_windowMgr.merchantShopMakeWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } } } return false; } } this->merchantPurchaseSavedItems.push_back(item); if (refresh == 1) { UIWindow* window = reinterpret_cast(alternate ? g_windowMgr.buyingShopMakeWnd : g_windowMgr.merchantShopMakeWnd); if (window) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } } return true; } // OFFSET: 0x00DD72C0 // FUNCTION: fn_23285 bool CSession::AddItem1740(const ITEM_INFO& item) { for (ITEM_INFO& value : this->merchantShopItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; if (UIWindow* window = reinterpret_cast(g_windowMgr.merchantItemWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return false; } } this->merchantShopItems.push_back(item); if (UIWindow* window = reinterpret_cast(g_windowMgr.merchantItemWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return true; } // OFFSET: 0x00DD7350 // FUNCTION: fn_23286 bool CSession::AddItem1750(const ITEM_INFO& item, int type, unsigned char refresh) { for (ITEM_INFO& value : this->merchantMyShopItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; if (refresh == 1) { std::uint32_t window; if (type == 1) { window = g_windowMgr.buyingShopMirrorItemWnd; } else { window = type == 2 ? g_windowMgr.purchaseMirrorItemWnd : g_windowMgr.merchantMirrorItemWnd; } if (window) { reinterpret_cast(window)->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } } return false; } } this->merchantMyShopItems.push_back(item); if (refresh == 1) { std::uint32_t window; if (type == 1) { window = g_windowMgr.buyingShopMirrorItemWnd; } else { window = type == 2 ? g_windowMgr.purchaseMirrorItemWnd : g_windowMgr.merchantMirrorItemWnd; } if (window) { reinterpret_cast(window)->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } } return true; } // OFFSET: 0x00DD7420 // FUNCTION: fn_23287 void CSession::AddItem1758(const ITEM_INFO& item) { for (ITEM_INFO& value : this->merchantPurchaseItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; return; } } this->merchantPurchaseItems.push_back(item); } // OFFSET: 0x00DD7470 // FUNCTION: fn_23288 void CSession::AddItem1760(const ITEM_INFO& item) { for (ITEM_INFO& value : this->merchantHopeToSellItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; return; } } this->merchantHopeToSellItems.push_back(item); } // OFFSET: 0x00DD74C0 // FUNCTION: fn_23289 void CSession::AddItem1748(const ITEM_INFO& item) { for (ITEM_INFO& value : this->merchantMirrorItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; return; } } this->merchantMirrorItems.push_back(item); } // OFFSET: 0x00DD7510 // FUNCTION: fn_23290 bool CSession::Equip(const ITEM_INFO& item) { const EquipSlots slots = g_session.GetEquipSlots(item.identity[3]); const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; for (int index = 0; index < 3; ++index) { const int slot = order[index]; if (slot != -1) { ITEM_INFO& equipped = this->alternateEquippedItems[slot]; equipped = item; equipped.quantity = 1; equipped.expireTime = item.expireTime; equipped.bindOnEquip = item.bindOnEquip; equipped.sprite = item.sprite; } } return true; } // OFFSET: 0x00DD75C0 // FUNCTION: fn_23291 void CSession::AddItem1718(const RawItemBasic& item, int quantity, std::uint16_t unknown_006C) { ITEM_INFO value; value.AssignBasic(item); value.quantity = quantity; value.priceFactor = unknown_006C; value.refiningLevel = 0; value.damaged = 0; value.SetItemId(std::atoi(value.itemId.c_str())); this->shopItems.push_back(value); } // OFFSET: 0x00DD76C0 // FUNCTION: fn_23292 void CSession::AppendItem1718(const ITEM_INFO& item) { this->shopItems.push_back(item); } // OFFSET: 0x00DD76D0 // FUNCTION: fn_23293 void CSession::MergeItem1718ByName(const ITEM_INFO& item) { for (ITEM_INFO& value : this->shopItems) { if (value.itemId == item.itemId) { value.quantity = value.quantity == -1 ? -1 : value.quantity + item.quantity; return; } } this->shopItems.push_back(item); } // OFFSET: 0x00DD7770 // FUNCTION: fn_23294 void CSession::MergeItem1720ByName(const ITEM_INFO& item) { for (ITEM_INFO& value : this->purchaseItems) { if (value.itemId == item.itemId) { value.quantity += item.quantity; return; } } this->purchaseItems.push_back(item); } // OFFSET: 0x00DD77F0 // FUNCTION: fn_23295 void CSession::MergeItem1720(const ITEM_INFO& item) { for (ITEM_INFO& value : this->purchaseItems) { const bool same = item.flags[3] ? value.identity[1] == item.identity[1] : value.itemId == item.itemId; if (same) { value.quantity += item.quantity; return; } } this->purchaseItems.push_back(item); } // OFFSET: 0x00DD7890 // FUNCTION: fn_23296 void CSession::AddItem1730(const ITEM_INFO& item) { const ITEM_INFO owned = LookupClientItemByIdentity(this->m_inventoryItems, item.identity[1]); if (owned.identity[1] > 0) { for (ITEM_INFO& value : this->storageItems) { if (value.identity[1] == item.identity[1]) { const int quantity = value.quantity + item.quantity; if (owned.quantity >= quantity) { value.quantity = quantity; } return; } } if (owned.quantity >= item.quantity) { this->storageItems.push_back(item); } } } // OFFSET: 0x00DD7970 // FUNCTION: fn_23297 void CSession::AddItem1718(const RawItemBasic& item, int mode) { if (mode != 0) { const ITEM_INFO owned = LookupClientItemByIdentity(this->m_inventoryItems, item.id); if (owned.identity[1] != 0) { ITEM_INFO value(owned); value.AssignBasic(item); value.SetItemId(std::atoi(value.itemId.c_str())); this->shopItems.push_back(value); } } else { ITEM_INFO value; value.AssignBasic(item); value.refiningLevel = 0; value.damaged = 0; value.SetItemId(std::atoi(value.itemId.c_str())); value.identity[2] = item.location; value.sprite = item.sprite; this->shopItems.push_back(value); } } // OFFSET: 0x00DD7B30 // FUNCTION: fn_23298 void CSession::MergeItem1718(const ITEM_INFO& item) { for (ITEM_INFO& value : this->shopItems) { if (value.identity[1] == item.identity[1]) { if (value.quantity != -1) { value.quantity += item.quantity; } return; } } this->shopItems.push_back(item); } // OFFSET: 0x00DD7B80 // FUNCTION: fn_23299 bool CSession::AddItem1738(const ITEM_INFO& item, unsigned char refresh) { if (g_replayAnimationScaling && !Replay::Instance().header.options.Get(ReplayRecordOption::Item)) { return true; } for (ITEM_INFO& value : this->cartItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; if (refresh && g_windowMgr.window_0288) { g_windowMgr.window_0288->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return false; } } this->cartItems.push_back(item); if (refresh && g_windowMgr.window_0288) { g_windowMgr.window_0288->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return true; } // OFFSET: 0x00DD6D70 // FUNCTION: fn_23280 bool CSession::EquipUnknown17F0(const ITEM_INFO& item, const char* name) { const EquipSlots slots = g_session.GetEquipSlots(item.identity[3]); const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; for (int index = 0; index < 3; ++index) { const int slot = order[index]; if (slot != -1 && slot < 10) { ITEM_INFO& equipped = this->equippedItems[slot]; equipped = item; equipped.quantity = 1; } } RequestBasicInfoRefresh(); if (g_equipmentPropertiesMgr == nullptr) { g_equipmentPropertiesMgr = new (std::nothrow) EquipmentProperties::CEquipmentPropertiesMgr; } g_equipmentPropertiesMgr->EquipWearable(&item, *name == '\0'); g_windowMgr.InvalidateFrames(); return true; } // OFFSET: 0x00DFC9B0 // FUNCTION: fn_23599 bool CSession::OnItemEquipped(ITEM_INFO& item) { g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(FormatOwnedString("%s %s", item.DisplayName(0).c_str(), LookupMessage(MSI_ITEM_IS_EQUIPED)).c_str()), 0xC8FF00, 7, 0, 0); if (item.bindOnEquip == 1) { item.bindOnEquip = 2; } if (g_session.IsCostumeLocation(item.identity[3])) { char name[1] = ""; g_session.EquipItem2B50(item, name); } else { const EquipSlots slots = g_session.GetEquipSlots(item.identity[3]); const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; for (int index = 0; index < 3; ++index) { const int slot = order[index]; if (slot != -1 && slot < 10) { ITEM_INFO& equipped = this->equippedItems[slot]; equipped = item; equipped.quantity = 1; } } RequestBasicInfoRefresh(); if (g_equipmentPropertiesMgr == nullptr) { g_equipmentPropertiesMgr = new (std::nothrow) EquipmentProperties::CEquipmentPropertiesMgr; } g_equipmentPropertiesMgr->EquipWearable(&item, true); g_windowMgr.InvalidateFrames(); } if (item.identity[3] != 0x8000) { this->RemoveItem1710(item.identity[1], 1); } if (UIWindow* window = reinterpret_cast(g_windowMgr.itemParamChangeDisplayWnd)) { window->SendMsg(nullptr, UM_REFRESH, 0, 0, 0, 0); } return true; } // OFFSET: 0x00DFC620 // FUNCTION: fn_23598 bool CSession::UnequipItem(int location, std::uint32_t identity) { if (location == 0x8000) { const ITEM_INFO item = LookupClientItemByIdentity(g_session.m_inventoryItems, identity); if (item.identity[1] != 0) { g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(FormatOwnedString("%s%s", item.DisplayName(0).c_str(), LookupMessage(MSI_ITEM_IS_REMOVED)).c_str()), 0xFF, 7, 0, 0); } this->equippedArrowIndex = 0; g_windowMgr.InvalidateFrames(); CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); if (mode != nullptr) { auto it = g_activeSkillEffects.begin(); for (; it != g_activeSkillEffects.end(); ++it) { if (it->id == 0x2B7) { break; } } if (it != g_activeSkillEffects.end()) { g_activeSkillEffects.erase(it); } if (mode->scene && mode->scene->player && mode->scene->player->skillEffect) { mode->scene->player->skillEffect->SendMsg(nullptr, AM_SETCOUNT, 0, 1, 0, 0, 0, 0); } } } else { const EquipSlots slots = g_session.GetEquipSlots(location); if (slots.values[2] < 10) { if (slots.values[2] != -1) { ITEM_INFO& item = this->equippedItems[slots.values[2]]; g_windowMgr.SendNotice(UIM_PUSHINTOCHATHISTORY, reinterpret_cast(FormatOwnedString("%s%s", item.DisplayName(0).c_str(), LookupMessage(MSI_ITEM_IS_REMOVED)).c_str()), 0xFF, 7, 0, 0); item.identity[3] = 0; bool merged = false; for (ITEM_INFO& value : this->m_inventoryItems) { if (value.identity[1] == item.identity[1]) { value.quantity += item.quantity; value.flags[1] = item.flags[1]; value.flags[0] = item.flags[0]; merged = true; break; } } if (!merged) { this->m_inventoryItems.push_back(item); } RefreshItemWindows(&g_windowMgr); item.Reset(); } if (slots.values[1] != -1 && slots.values[1] < 10) { this->equippedItems[slots.values[1]].Reset(); } if (slots.values[0] != -1 && slots.values[0] < 10) { this->equippedItems[slots.values[0]].Reset(); } } RequestBasicInfoRefresh(); } if (g_equipmentPropertiesMgr == nullptr) { g_equipmentPropertiesMgr = new (std::nothrow) EquipmentProperties::CEquipmentPropertiesMgr; } g_equipmentPropertiesMgr->UnequipWearable(location); g_windowMgr.InvalidateFrames(); if (UIWindow* window = g_windowMgr.itemParamChangeDisplayWnd) { window->SendMsg(nullptr, UM_REFRESH, 0, 0, 0, 0); } return true; } // OFFSET: 0x00DD7E10 // FUNCTION: fn_23300 EquipSlots CSession::GetEquipSlots(int location) { EquipSlots result; if (location == EQP_AMMO) { result.values[0] = -1; result.values[1] = -1; result.values[2] = 15; return result; } int mask = location; switch (location) { case EQP_COSTUME_HEAD_TOP: mask = 0x100; break; case EQP_COSTUME_HEAD_MID: mask = 0x200; break; case EQP_COSTUME_HEAD_TOP | EQP_COSTUME_HEAD_MID: mask = 0x300; break; case EQP_COSTUME_HEAD_LOW: mask = 1; break; case EQP_COSTUME_HEAD_TOP | EQP_COSTUME_HEAD_LOW: mask = 0x101; break; case EQP_COSTUME_HEAD_MID | EQP_COSTUME_HEAD_LOW: mask = 0x201; break; case EQP_COSTUME_HEAD_TOP | EQP_COSTUME_HEAD_MID | EQP_COSTUME_HEAD_LOW: mask = 0x301; break; case EQP_COSTUME_GARMENT: mask = 4; break; case 0x4000: case EQP_SHADOW_WEAPON: mask = 2; break; case EQP_SHADOW_ARMOR: mask = 0x10; break; case EQP_SHADOW_SHIELD: mask = 0x20; break; case EQP_SHADOW_SHOES: mask = 0x40; break; case EQP_SHADOW_ACC_R: mask = 8; break; case EQP_SHADOW_ACC_L: mask = 0x80; break; } const EquipSlots decoded = EquipSlotDecoder().Decode(mask); result.values[0] = decoded.values[0]; result.values[1] = decoded.values[1]; result.values[2] = decoded.values[2]; return result; } // OFFSET: 0x00DD8890 // FUNCTION: fn_23313 void CSession::ClearInventory() { this->m_inventoryItems.erase(this->m_inventoryItems.begin(), this->m_inventoryItems.end()); RefreshItemWindows(&g_windowMgr); } // OFFSET: 0x00DD88C0 // FUNCTION: fn_23314 void CSession::ClearItems1760And1748() { this->merchantHopeToSellItems.erase(this->merchantHopeToSellItems.begin(), this->merchantHopeToSellItems.end()); this->merchantMirrorItems.erase(this->merchantMirrorItems.begin(), this->merchantMirrorItems.end()); } // OFFSET: 0x00DD8900 // FUNCTION: fn_23315 void CSession::ClearItems1768() { this->merchantPurchaseSavedItems.erase(this->merchantPurchaseSavedItems.begin(), this->merchantPurchaseSavedItems.end()); } // OFFSET: 0x00DD8920 // FUNCTION: fn_23316 void CSession::ClearItems1740() { this->merchantShopItems.erase(this->merchantShopItems.begin(), this->merchantShopItems.end()); } // OFFSET: 0x00DD8940 // FUNCTION: fn_23317 __declspec(noinline) void CSession::ClearItems1750() { this->merchantMyShopItems.erase(this->merchantMyShopItems.begin(), this->merchantMyShopItems.end()); } // OFFSET: 0x00DD8960 // FUNCTION: fn_23318 void CSession::ClearItems1758() { this->merchantPurchaseItems.erase(this->merchantPurchaseItems.begin(), this->merchantPurchaseItems.end()); } // OFFSET: 0x00DD8980 // FUNCTION: fn_23319 void CSession::ClearItems1760() { this->merchantHopeToSellItems.erase(this->merchantHopeToSellItems.begin(), this->merchantHopeToSellItems.end()); } // OFFSET: 0x00DD89A0 // FUNCTION: fn_23320 void CSession::ClearItems1748() { this->merchantMirrorItems.erase(this->merchantMirrorItems.begin(), this->merchantMirrorItems.end()); } // OFFSET: 0x00DD89C0 // FUNCTION: fn_23321 void CSession::ClearRecords0734() { this->miniPartyRecords.clear(); } // OFFSET: 0x00DD8A20 // FUNCTION: fn_23322 void CSession::ClearItems1784(int index) { if (index >= 0 && static_cast(index) < 11) { this->inventoryCategories[index].erase(this->inventoryCategories[index].begin(), this->inventoryCategories[index].end()); } } // OFFSET: 0x00DD8A50 // FUNCTION: fn_23323 void CSession::ClearItemSlotQuantities() { this->otherExchangeMoney = 0; for (int index = 0; index < 10; ++index) { this->m_itemSlots[index].quantity = 0; } } // OFFSET: 0x00DD8AC0 // FUNCTION: fn_23324 void CSession::ResetEquipped() { for (int index = 0; index < 10; ++index) { this->alternateEquippedItems[index].Reset(); } } // OFFSET: 0x00DD8AF0 // FUNCTION: fn_23325 void CSession::ClearPartyMembers() { CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); if (mode) { for (auto member = this->m_partyMembers.begin(); member != this->m_partyMembers.end(); ++member) { CRenderObject* actor = mode->scene->GetGameActorByAidIncludeMe(member->accountId); if (actor && static_cast(actor)->HasId(160) == true) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } } this->m_partyMembers.clear(); this->partyLeader = 0; this->partyName = ""; } // OFFSET: 0x00DD8BD0 // FUNCTION: fn_23326 void CSession::ResetUnknown166C() { this->strengthBonus = 0; this->dexterityBonus = 0; this->luckBonus = 0; this->agilityBonus = 0; this->vitalityBonus = 0; this->intelligenceBonus = 0; } // OFFSET: 0x00DD8C10 // FUNCTION: fn_23327 void CSession::ClearItems1720() { this->purchaseItems.erase(this->purchaseItems.begin(), this->purchaseItems.end()); } // OFFSET: 0x00DD8C70 // FUNCTION: fn_23328 void CSession::ResetHotkeyDescriptions() { for (int index = 0; index < 9; ++index) { this->m_hotkeyDescriptions[index] = "nothing"; } } // OFFSET: 0x00DD8CA0 // FUNCTION: fn_23329 void CSession::ClearItems1738() { this->cartItems.erase(this->cartItems.begin(), this->cartItems.end()); } // OFFSET: 0x00DD8CC0 // FUNCTION: fn_23330 void CSession::CopyItems1740To1750() { this->merchantMyShopItems.erase(this->merchantMyShopItems.begin(), this->merchantMyShopItems.end()); for (auto item = this->merchantShopItems.begin(); item != this->merchantShopItems.end(); ++item) { this->merchantMyShopItems.push_back(*item); } } // OFFSET: 0x00DD8DB0 // FUNCTION: fn_23331 void CSession::CopyItems1760To1770() { this->filteredInventoryItems.erase(this->filteredInventoryItems.begin(), this->filteredInventoryItems.end()); for (auto item = this->merchantHopeToSellItems.begin(); item != this->merchantHopeToSellItems.end(); ++item) { this->filteredInventoryItems.push_back(*item); } } // OFFSET: 0x00DD8EA0 // FUNCTION: fn_23332 std::pair CSession::FindItem1750(const ITEM_INFO& item, unsigned char) { auto found = this->merchantMyShopItems.begin(); for (; found != this->merchantMyShopItems.end(); ++found) { if (IsSameItem(*found, item)) { break; } } return found != this->merchantMyShopItems.end() ? std::make_pair(true, *found) : std::make_pair(false, ITEM_INFO()); } // OFFSET: 0x00DD91C0 // FUNCTION: fn_23333 __declspec(noinline) int CSession::IsDifferentName(const char* name, const char* other) { if (std::strlen(other) == 0) { return 1; } char first[24]; std::sprintf(first, "%s", name); const short firstLength = static_cast(std::strlen(first)); char second[24]; std::sprintf(second, "%s", other); const short secondLength = static_cast(std::strlen(second)); const short difference = firstLength >= secondLength ? firstLength - secondLength : secondLength - firstLength; const short length = firstLength >= secondLength ? secondLength : firstLength; if (firstLength >= secondLength + 2 && firstLength <= secondLength + 4) { short matched = 0; for (short index = 0; index < length; ++index) { CountSameNameChar(first[index + 2], second[index], matched); } if (matched == length) { return 0; } } if (firstLength >= secondLength + 1 && firstLength <= secondLength + 4) { short matched = 0; for (short index = 0; index < length; ++index) { CountSameNameChar(first[index + 1], second[index], matched); } if (matched == length) { return 0; } } if (difference > 2) { return 1; } short matched = 0; for (int index = 0; index < length; ++index) { CountSameNameChar(first[index], second[index], matched); } if (matched >= length - 2) { return 0; } matched = 0; char skip = 1; short position = 0; for (short index = 0; index < length; ++index) { CountSameNameChar(first[index], second[position], matched); if (matched != index + 1 && skip == 1) { skip = 2; --position; } ++position; } if (matched != length - 1) { return 1; } return 0; } // OFFSET: 0x00DDA470 // FUNCTION: fn_23338 void CSession::RemoveItem1758ByItemId(ITEM_INFO& item) { for (auto it = this->merchantPurchaseItems.begin(); it != this->merchantPurchaseItems.end(); ++it) { if (std::atoi(it->itemId.c_str()) == std::atoi(item.itemId.c_str())) { item.itemId = it->itemId; it->quantity -= item.quantity; if (it->quantity <= 0) { this->merchantPurchaseItems.erase(it); } return; } } } // OFFSET: 0x00DDA510 // FUNCTION: fn_23339 void CSession::RemoveItem1710(std::uint32_t id, int quantity) { for (auto it = this->m_inventoryItems.begin(); it != this->m_inventoryItems.end(); ++it) { if (it->identity[1] == id) { if (it->quantity <= quantity) { this->m_inventoryItems.erase(it); } else { it->quantity -= quantity; } for (auto it2 = this->m_inventoryItems.begin(); it2 != this->m_inventoryItems.end(); ++it2) { if (it2->identity[1] == id) { it2->flags[2] = 0; break; } } break; } } RefreshItemWindows(&g_windowMgr); } // OFFSET: 0x00DFF0E0 // FUNCTION: fn_23609 void CSession::RefreshItem1768Window(unsigned char alternate, int argument) { if (UIWindow* window = reinterpret_cast(alternate ? g_windowMgr.buyingShopMakeWnd : g_windowMgr.merchantShopMakeWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, argument, 0, 0, 0); } } // OFFSET: 0x00DDA5A0 // FUNCTION: fn_23340 bool CSession::RemoveItem1768(const ITEM_INFO& item, unsigned char alternate) { int index = 0; for (auto it = this->merchantPurchaseSavedItems.begin(); it != this->merchantPurchaseSavedItems.end(); ++it, ++index) { if (it->identity[1] == item.identity[1]) { if (alternate) { this->merchantPurchaseSavedItems.erase(it); this->RefreshItem1768Window(1, 0); return true; } it->quantity -= item.quantity; if (it->quantity <= 0) { this->merchantPurchaseSavedItems.erase(it); this->RefreshItem1768Window(0, index + 1); return true; } this->RefreshItem1768Window(0, 0); return true; } } this->RefreshItem1768Window(alternate, 0); return false; } // OFFSET: 0x00DDA690 // FUNCTION: fn_23341 bool CSession::RemoveItem1740(const ITEM_INFO& item) { for (auto it = this->merchantShopItems.begin(); it != this->merchantShopItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->merchantShopItems.erase(it); } for (auto it2 = this->merchantShopItems.begin(); it2 != this->merchantShopItems.end(); ++it2) { if (it2->identity[1] == item.identity[1]) { it2->flags[2] = 0; break; } } if (UIWindow* window = reinterpret_cast(g_windowMgr.merchantItemWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return true; } } if (UIWindow* window = reinterpret_cast(g_windowMgr.merchantItemWnd)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return false; } // OFFSET: 0x00DDA8B0 // FUNCTION: fn_23343 void CSession::RemoveItem1758(ITEM_INFO& item) { for (auto it = this->merchantPurchaseItems.begin(); it != this->merchantPurchaseItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { item.itemId = it->itemId; it->quantity -= item.quantity; if (it->quantity <= 0) { this->merchantPurchaseItems.erase(it); } return; } } } // OFFSET: 0x00DDA920 // FUNCTION: fn_23344 void CSession::RemoveItem1760(const ITEM_INFO& item) { for (auto it = this->merchantHopeToSellItems.begin(); it != this->merchantHopeToSellItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->merchantHopeToSellItems.erase(it); } return; } } } // OFFSET: 0x00DDA9C0 // FUNCTION: fn_23345 void CSession::RemoveItem1718ByItemId(const ITEM_INFO& item) { for (auto it = this->shopItems.begin(); it != this->shopItems.end(); ++it) { if (std::atoi(it->itemId.c_str()) == std::atoi(item.itemId.c_str())) { if (it->quantity != -1) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->shopItems.erase(it); } } return; } } } // OFFSET: 0x00DDAA50 // FUNCTION: fn_23346 void CSession::RemoveItem1718(const ITEM_INFO& item) { for (auto it = this->shopItems.begin(); it != this->shopItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { if (it->quantity != -1) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->shopItems.erase(it); } } return; } } } // OFFSET: 0x00DDAAB0 // FUNCTION: fn_23347 void CSession::RemoveItem1720ByName(const ITEM_INFO& item) { for (auto it = this->purchaseItems.begin(); it != this->purchaseItems.end(); ++it) { if (it->itemId == item.itemId) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->purchaseItems.erase(it); } return; } } } // OFFSET: 0x00DDAC40 // FUNCTION: fn_23349 void CSession::RemoveItem1730(const ITEM_INFO& item) { for (auto it = this->storageItems.begin(); it != this->storageItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->storageItems.erase(it); } return; } } } // OFFSET: 0x00DDAC90 // FUNCTION: fn_23350 void CSession::ReduceItem1718(const ITEM_INFO& item) { for (auto it = this->shopItems.begin(); it != this->shopItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->shopItems.erase(it); } return; } } } // OFFSET: 0x00DDACE0 // FUNCTION: fn_23351 bool CSession::RemoveItem1738(const ITEM_INFO& item) { for (auto it = this->cartItems.begin(); it != this->cartItems.end(); ++it) { if (it->identity[1] == item.identity[1]) { it->quantity -= item.quantity; if (it->quantity <= 0) { this->cartItems.erase(it); } for (auto it2 = this->cartItems.begin(); it2 != this->cartItems.end(); ++it2) { if (it2->identity[1] == item.identity[1]) { it2->flags[2] = 0; break; } } if (UIWindow* window = g_windowMgr.window_0288) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return true; } } if (UIWindow* window = g_windowMgr.window_0288) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return false; } // OFFSET: 0x00E2EC10 // FUNCTION: fn_24149 bool CSession::EquipItem6B68(const ITEM_INFO& item) { const EquipSlots slots = g_session.GetEquipSlots(item.identity[3]); const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; for (int index = 0; index < 3; ++index) { const int slot = order[index]; if (slot != -1) { ITEM_INFO& equipped = this->swapEquippedItems[slot]; equipped = item; equipped.quantity = 1; equipped.expireTime = item.expireTime; equipped.bindOnEquip = item.bindOnEquip; equipped.sprite = item.sprite; } } if (g_windowMgr.equipWnd) { static_cast(g_windowMgr.equipWnd)->NotifyEquipmentChanged(); } RefreshItemWindows(&g_windowMgr); return true; } // OFFSET: 0x00E2FC50 // FUNCTION: fn_24164 ITEM_INFO CSession::FindItemByIdentity(std::uint32_t identity, int location) { if (identity == 0) { return ITEM_INFO(0); } ITEM_INFO item = LookupClientItemByIdentity(this->m_inventoryItems, identity); if (item.identity[1] > 0) { return item; } const EquipSlots slots = g_session.GetEquipSlots(location); const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; for (int index = 0; index < 3; ++index) { const int slot = order[index]; if (slot != -1) { if (this->IsCostumeLocation(location)) { item = this->GetItem2B50(slot); } else { item = this->GetItem17F0(slot); } if (item.identity[1] == identity) { break; } } } if (item.identity[1] != 0) { return item; } return ITEM_INFO(0); } // OFFSET: 0x00E49F40 // FUNCTION: fn_24247 void CSession::SetItems1750(int count, const short* ids) { this->ClearItems1750(); for (int i = 0; i < count; ++i) { const ITEM_INFO item = LookupClientItemByIdentity(g_session.m_inventoryItems, ids[i]); if (item.identity[1] > 0) { this->merchantMyShopItems.push_back(item); } } } // OFFSET: 0x00DF9560 // FUNCTION: fn_23520 int CSession::IsGuildMemberNameDifferent(const char* name) { std::list members = g_guildInfo.m_members; for (auto it = members.begin(); it != members.end(); ++it) { if (this->IsDifferentName(name, it->name.c_str()) == 0) { return 1; } } return 0; } // OFFSET: 0x00DF9690 // FUNCTION: fn_23521 int CSession::IsFriendNameDifferent(const char* name) { for (auto it = this->m_friends.begin(); it != this->m_friends.end(); ++it) { if (this->IsDifferentName(name, it->name.c_str()) == 0) { return 1; } } return 0; } // OFFSET: 0x00DFCBB0 // FUNCTION: fn_23600 bool CSession::AddExchangeItem(ITEM_INFO item) { int count = 0; for (int i = 0; i < 10; ++i) { if (this->m_exchangeItems[i].quantity != 0) { ++count; } } if (count == 10) { return false; } this->RemoveItem1710(item.identity[1], item.quantity); ITEM_INFO added = item; int index = 0; for (; index < 10; ++index) { if (this->m_exchangeItems[index].quantity != 0 && this->m_exchangeItems[index].identity[1] == added.identity[1]) { this->m_exchangeItems[index].quantity += added.quantity; break; } } if (index == 10) { for (int i = 0; i < 10; ++i) { if (this->m_exchangeItems[i].quantity == 0) { this->m_exchangeItems[i] = added; break; } } } if (UIWindow* window = g_windowMgr.LookupWindow(WID_EXCHANGEUI)) { window->SendMsg(nullptr, UM_REFRESHITEM, 0, 0, 0, 0); } return true; } // OFFSET: 0x00D1F550 // FUNCTION: fn_21639 void CGameMode::ReceiveShowRankingSchedule(const unsigned char* packet) { if (packet) { g_session.SetShowRankingSchedule(true, packet); } }