diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index 61b57414afd..5bc9ddda37f 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -969,6 +969,23 @@ void CCharEntity::PostTick() pushPacket(new CCharAppearancePacket(this)); } + // notify client containers are dirty and then no longer dirty + if (!dirtyInventoryContainers.empty()) + { + + // Notify client containers were dirty + // Note: Retail sends this in the same chunk as the inventory equip packets, but it doesnt seem to matter as long as it arrives + for (const auto& [container, dirty]: dirtyInventoryContainers) + { + pushPacket(new CInventoryFinishPacket(container)); + } + + dirtyInventoryContainers.clear(); + + // Notify client containers are now ok + pushPacket(new CInventoryFinishPacket()); + } + if (ReloadParty()) { charutils::ReloadParty(this); diff --git a/src/map/entities/charentity.h b/src/map/entities/charentity.h index 001a92e732a..fbaddfe5587 100644 --- a/src/map/entities/charentity.h +++ b/src/map/entities/charentity.h @@ -23,6 +23,7 @@ #define _CHARENTITY_H #include "event_info.h" +#include "item_container.h" #include "monstrosity.h" #include "packets/char.h" #include "packets/entity_update.h" @@ -511,6 +512,9 @@ class CCharEntity : public CBattleEntity bool getBlockingAid() const; void setBlockingAid(bool isBlockingAid); + // Send updates about dirty containers in post tick + std::map dirtyInventoryContainers; + bool m_EquipSwap; // true if equipment was recently changed bool m_EffectsChanged; time_point m_LastSynthTime; diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index c3cd1fddc3c..f411af24f05 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -1205,8 +1205,6 @@ namespace charutils PChar->pushPacket(new CInventoryItemPacket(PItem, LocationID, slotID)); } } - - PChar->pushPacket(new CInventoryFinishPacket(LocationID)); }; // Send important items first @@ -1859,6 +1857,9 @@ namespace charutils PChar->UpdateHealth(); PChar->m_EquipSwap = true; PChar->updatemask |= UPDATE_LOOK; + + // Mark container dirty + PChar->dirtyInventoryContainers[static_cast(PItem->getLocationID())] = true; } } } @@ -2657,9 +2658,10 @@ namespace charutils return; } - CItemEquipment* PItem = dynamic_cast(PChar->getStorage(containerID)->GetItem(slotID)); + CItemEquipment* PItem = dynamic_cast(PChar->getStorage(containerID)->GetItem(slotID)); + CItem* POldItem = PChar->getEquip(static_cast(equipSlotID)); - if (PItem && PItem == PChar->getEquip((SLOTTYPE)equipSlotID)) + if (PItem && PItem == PChar->getEquip(static_cast(equipSlotID))) { return; } @@ -2725,7 +2727,6 @@ namespace charutils // Do not forget to update the timer when equipping the subject PChar->pushPacket(new CInventoryItemPacket(PItem, containerID, slotID)); - PChar->pushPacket(new CInventoryFinishPacket()); } PItem->setSubType(ITEM_LOCKED); @@ -2776,6 +2777,17 @@ namespace charutils PChar->UpdateHealth(); PChar->m_EquipSwap = true; PChar->updatemask |= UPDATE_LOOK; + + // PItem can be null if item id is 0 (unequip) + if (PItem) + { + PChar->dirtyInventoryContainers[static_cast(PItem->getLocationID())] = true; + } + + if (POldItem) + { + PChar->dirtyInventoryContainers[static_cast(POldItem->getLocationID())] = true; + } } /************************************************************************