From 5889ceb370ad37381b1b04b336b7f4947f58de15 Mon Sep 17 00:00:00 2001 From: sruon Date: Sun, 8 Feb 2026 06:01:20 -0700 Subject: [PATCH] Retail accurate inventory container tracking --- src/map/CMakeLists.txt | 2 + src/map/ai/states/item_state.cpp | 4 +- src/map/attackround.cpp | 2 +- src/map/char_recast_container.cpp | 2 +- src/map/entities/charentity.cpp | 87 ++++++++++++++----- src/map/entities/charentity.h | 11 ++- src/map/inventory_sync_state.cpp | 72 +++++++++++++++ src/map/inventory_sync_state.h | 65 ++++++++++++++ src/map/linkshell.cpp | 4 +- src/map/lua/lua_baseentity.cpp | 14 +-- src/map/map_networking.cpp | 3 + src/map/packets/c2s/0x01a_action.cpp | 2 +- src/map/packets/c2s/0x029_item_move.cpp | 4 +- src/map/packets/c2s/0x03a_item_stack.cpp | 2 +- src/map/packets/c2s/0x03b_subcontainer.cpp | 2 +- src/map/packets/c2s/0x083_shop_buy.cpp | 2 +- src/map/packets/c2s/0x085_shop_sell_set.cpp | 2 +- src/map/packets/c2s/0x0aa_guild_buy.cpp | 2 +- src/map/packets/c2s/0x0ac_guild_sell.cpp | 2 +- .../c2s/0x0c4_group_comlink_active.cpp | 4 +- src/map/packets/c2s/0x0fa_myroom_layout.cpp | 2 +- src/map/packets/c2s/0x0fb_myroom_bankin.cpp | 2 +- .../packets/c2s/0x0fc_myroom_plant_add.cpp | 2 +- .../packets/c2s/0x0fe_myroom_plant_crop.cpp | 2 +- .../packets/c2s/0x0ff_myroom_plant_stop.cpp | 2 +- src/map/packets/c2s/0x106_bazaar_buy.cpp | 2 +- src/map/packets/c2s/0x10a_bazaar_itemset.cpp | 2 +- src/map/packets/s2c/0x01d_item_same.cpp | 13 +-- src/map/packets/s2c/0x01d_item_same.h | 5 +- src/map/utils/auctionutils.cpp | 4 +- src/map/utils/battleutils.cpp | 8 +- src/map/utils/charutils.cpp | 74 +++++++--------- src/map/utils/charutils.h | 5 +- src/map/utils/dboxutils.cpp | 4 +- src/map/utils/fishingutils.cpp | 8 +- src/map/utils/synthutils.cpp | 2 +- 36 files changed, 299 insertions(+), 126 deletions(-) create mode 100644 src/map/inventory_sync_state.cpp create mode 100644 src/map/inventory_sync_state.h diff --git a/src/map/CMakeLists.txt b/src/map/CMakeLists.txt index 23ef3d9a6fd..cd5581f17e1 100644 --- a/src/map/CMakeLists.txt +++ b/src/map/CMakeLists.txt @@ -74,6 +74,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/instance.h ${CMAKE_CURRENT_SOURCE_DIR}/ipc_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ipc_client.h + ${CMAKE_CURRENT_SOURCE_DIR}/inventory_sync_state.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/inventory_sync_state.h ${CMAKE_CURRENT_SOURCE_DIR}/item_container.cpp ${CMAKE_CURRENT_SOURCE_DIR}/item_container.h ${CMAKE_CURRENT_SOURCE_DIR}/items.h diff --git a/src/map/ai/states/item_state.cpp b/src/map/ai/states/item_state.cpp index d8b79a1e510..0a2a15ce2e7 100644 --- a/src/map/ai/states/item_state.cpp +++ b/src/map/ai/states/item_state.cpp @@ -145,7 +145,7 @@ CItemState::CItemState(CCharEntity* PEntity, const uint16 targid, const uint8 lo m_PItem->setSubType(ITEM_LOCKED); m_PEntity->pushPacket(m_PItem, ItemLockFlg::NoSelect); - m_PEntity->pushPacket(); + m_PEntity->pushPacket(m_PEntity); } void CItemState::UpdateTarget(CBaseEntity* target) @@ -253,7 +253,7 @@ void CItemState::Cleanup(timer::time_point tick) } m_PEntity->pushPacket(m_PItem, static_cast(m_location), m_slot); - m_PEntity->pushPacket(); + m_PEntity->pushPacket(m_PEntity); } auto CItemState::CanChangeState() -> bool diff --git a/src/map/attackround.cpp b/src/map/attackround.cpp index aad359cb903..94e9b36c389 100644 --- a/src/map/attackround.cpp +++ b/src/map/attackround.cpp @@ -447,7 +447,7 @@ void CAttackRound::ProcFollowUpAttacks() } charutils::UpdateItem(PChar, loc, slot, -1); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } } diff --git a/src/map/char_recast_container.cpp b/src/map/char_recast_container.cpp index eaa132a95dc..20e74a74539 100644 --- a/src/map/char_recast_container.cpp +++ b/src/map/char_recast_container.cpp @@ -179,7 +179,7 @@ void CCharRecastContainer::Check() CItem* PItem = m_PChar->getStorage(containerID)->GetItem(slotID); m_PChar->pushPacket(PItem, static_cast(containerID), slotID); - m_PChar->pushPacket(); + m_PChar->pushPacket(m_PChar); } if (type == RECAST_ITEM || type == RECAST_MAGIC || type == RECAST_LOOT) { diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index b33b80d5907..34d85507f34 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -25,17 +25,20 @@ #include +#include "enums/item_lockflg.h" #include "packets/basic.h" #include "packets/char_status.h" #include "packets/char_sync.h" #include "packets/char_update.h" #include "packets/entity_update.h" #include "packets/s2c/0x01d_item_same.h" +#include "packets/s2c/0x01f_item_list.h" #include "packets/s2c/0x02a_talknumwork.h" #include "packets/s2c/0x032_event.h" #include "packets/s2c/0x033_eventstr.h" #include "packets/s2c/0x034_eventnum.h" #include "packets/s2c/0x036_talknum.h" +#include "packets/s2c/0x050_equip_list.h" #include "packets/s2c/0x051_grap_list.h" #include "packets/s2c/0x052_eventucoff.h" #include "packets/s2c/0x053_systemmes.h" @@ -69,6 +72,7 @@ #include "enums/recast.h" #include "ipc_client.h" #include "item_container.h" +#include "items/item_equipment.h" #include "items/item_furnishing.h" #include "items/item_usable.h" #include "items/item_weapon.h" @@ -79,6 +83,7 @@ #include "mobskill.h" #include "modifier.h" #include "notoriety_container.h" +#include "packets/s2c/0x020_item_attr.h" #include "packets/s2c/0x028_battle2.h" #include "packets/s2c/0x029_battle_message.h" #include "packets/s2c/0x063_miscdata_status_icons.h" @@ -200,7 +205,6 @@ CCharEntity::CCharEntity() m_EquipFlag = 0; m_EquipBlock = 0; m_StatsDebilitation = 0; - m_EquipSwap = false; MeritMode = false; PMeritPoints = nullptr; @@ -1079,29 +1083,6 @@ void CCharEntity::PostTick() CBattleEntity::PostTick(); - if (m_EquipSwap) - { - updatemask |= UPDATE_HP; - m_EquipSwap = false; - pushPacket(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(container); - } - - dirtyInventoryContainers.clear(); - - // Notify client containers are now ok - pushPacket(); - } - if (ReloadParty()) { charutils::ReloadParty(this); @@ -1158,6 +1139,64 @@ void CCharEntity::PostTick() } } +// Flush all pending equipment changes at end of network cycle after all SmallPackets have been processed +void CCharEntity::flushEquipChanges() +{ + if (!inventorySyncState_.hasPendingEquipChanges()) + { + return; + } + + // EQUIP_LIST + GRAP_LIST pairs for each change + for (const auto& change : inventorySyncState_.pendingEquipChanges()) + { + if (change.equipping) + { + pushPacket(change.containerSlotId, change.equipSlot, change.container); + } + else + { + pushPacket(0, change.equipSlot, LOC_INVENTORY); + } + + pushPacket(this); + } + + // For each dirty container: ITEM_LIST or ITEM_ATTR for items in that container, then ITEM_SAME pair + // Only send ITEM_SAME if the container has already been sent to the client during zone-in + for (const auto& container : inventorySyncState_.dirtyContainers()) + { + for (const auto& change : inventorySyncState_.pendingEquipChanges()) + { + if (static_cast(change.item->getLocationID()) == container) + { + if (change.item->isSubType(ITEM_CHARGED)) + { + pushPacket(change.item, container, change.item->getSlotID()); + } + else + { + pushPacket(change.item, change.equipping ? ItemLockFlg::NoDrop : ItemLockFlg::Normal); + } + } + } + + // Only send ITEM_SAME if container has been loaded - prevents sort reset during zone-in + if (inventorySyncState_.isSynced(container)) + { + pushPacket(container, this); + pushPacket(this); + } + } + + inventorySyncState_.clearEquipChanges(); +} + +auto CCharEntity::inventorySyncState() -> InventorySyncState& +{ + return inventorySyncState_; +} + void CCharEntity::addTrait(CTrait* PTrait) { CBattleEntity::addTrait(PTrait); diff --git a/src/map/entities/charentity.h b/src/map/entities/charentity.h index a74525e00b8..c4685878ea3 100644 --- a/src/map/entities/charentity.h +++ b/src/map/entities/charentity.h @@ -24,6 +24,7 @@ #include "aman.h" #include "event_info.h" +#include "inventory_sync_state.h" #include "item_container.h" #include "map_session.h" #include "monstrosity.h" @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -550,14 +552,13 @@ 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; timer::time_point m_LastSynthTime{}; timer::time_point m_LastRangedAttackTime{}; + void flushEquipChanges(); + auto inventorySyncState() -> InventorySyncState&; + CHAR_SUBSTATE m_Substate; int16 addTP(int16 tp) override; @@ -701,6 +702,8 @@ class CCharEntity : public CBattleEntity bool m_isBlockingAid; bool m_reloadParty; + InventorySyncState inventorySyncState_; + mutable std::unordered_map> charVarCache; std::unordered_set charVarChanges; std::unordered_set charTriggerAreaIDs; // Holds any TriggerArea IDs that the player is currently within the bounds of diff --git a/src/map/inventory_sync_state.cpp b/src/map/inventory_sync_state.cpp new file mode 100644 index 00000000000..eda066f9be0 --- /dev/null +++ b/src/map/inventory_sync_state.cpp @@ -0,0 +1,72 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "inventory_sync_state.h" +#include "items/item.h" + +// Marks a given container as having been entirely streamed to the client +void InventorySyncState::markSynced(const CONTAINER_ID id) +{ + if (id < MAX_CONTAINER_ID) + { + syncedContainers_.set(id); + } +} + +auto InventorySyncState::isSynced(const CONTAINER_ID id) const -> bool +{ + return id < MAX_CONTAINER_ID && syncedContainers_.test(id); +} + +// Computes a flag for use in ITEM_FLAG representing containers that have been entirely streamed. +auto InventorySyncState::getSyncedFlags() const -> uint32_t +{ + uint32_t flags = 0; + std::memcpy(&flags, syncedContainers_.data.data(), std::min(sizeof(flags), syncedContainers_.data.size())); + return flags; +} + +void InventorySyncState::queueEquipChange(CONTAINER_ID container, uint8 containerSlotId, SLOTTYPE equipSlot, CItem* item, Equipping equipping) +{ + pendingEquipChanges_.emplace_back(container, containerSlotId, equipSlot, item, equipping); + dirtyContainers_.insert(equipping ? container : static_cast(item->getLocationID())); +} + +void InventorySyncState::clearEquipChanges() +{ + pendingEquipChanges_.clear(); + dirtyContainers_.clear(); +} + +auto InventorySyncState::hasPendingEquipChanges() const -> bool +{ + return !pendingEquipChanges_.empty(); +} + +auto InventorySyncState::pendingEquipChanges() const -> const std::vector& +{ + return pendingEquipChanges_; +} + +auto InventorySyncState::dirtyContainers() const -> const std::set& +{ + return dirtyContainers_; +} diff --git a/src/map/inventory_sync_state.h b/src/map/inventory_sync_state.h new file mode 100644 index 00000000000..2ce447443d9 --- /dev/null +++ b/src/map/inventory_sync_state.h @@ -0,0 +1,65 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "common/cbasetypes.h" +#include "common/xi.h" +#include "item_container.h" + +#include +#include + +enum SLOTTYPE : uint8; +class CItem; + +using Equipping = xi::Flag; + +struct equip_change_t +{ + CONTAINER_ID container; // Container where the item is located + uint8 containerSlotId; // Item slot in container (0 for unequip) + SLOTTYPE equipSlot; // Equipment slot + CItem* item; // The item being equipped/unequipped + Equipping equipping; // Yes = equip (NoDrop), No = unequip (Normal) +}; + +// Tracks which inventory containers have been synced with the client +class InventorySyncState +{ +public: + // Which containers have been sent to the client + void markSynced(CONTAINER_ID id); + auto isSynced(CONTAINER_ID id) const -> bool; + auto getSyncedFlags() const -> uint32_t; + + // Equipment change queue + void queueEquipChange(CONTAINER_ID container, uint8 containerSlotId, SLOTTYPE equipSlot, CItem* item, Equipping equipping); + void clearEquipChanges(); + auto hasPendingEquipChanges() const -> bool; + auto pendingEquipChanges() const -> const std::vector&; + auto dirtyContainers() const -> const std::set&; + +private: + xi::bitset syncedContainers_{}; + std::vector pendingEquipChanges_; + std::set dirtyContainers_; +}; diff --git a/src/map/linkshell.cpp b/src/map/linkshell.cpp index 532c3d0ca96..d8d09750783 100644 --- a/src/map/linkshell.cpp +++ b/src/map/linkshell.cpp @@ -247,7 +247,7 @@ void CLinkshell::ChangeMemberRank(const std::string& MemberName, const uint8 req charutils::SaveCharStats(PMember); charutils::SaveCharEquip(PMember); - PMember->pushPacket(); + PMember->pushPacket(PMember); PMember->pushPacket(PMember); return; } @@ -323,7 +323,7 @@ void CLinkshell::RemoveMemberByName(const std::string& MemberName, uint8 request charutils::SaveCharStats(PMember); charutils::SaveCharEquip(PMember); - PMember->pushPacket(); + PMember->pushPacket(PMember); PMember->pushPacket(PMember); if (breakLinkshell) { diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 67b86d91cdd..acf73e40eb0 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -4277,7 +4277,7 @@ bool CLuaBaseEntity::delItem(uint16 itemID, int32 quantity, const sol::object& c if (SlotID != ERROR_SLOTID) { charutils::UpdateItem(PChar, location, SlotID, -quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); return true; } @@ -4309,7 +4309,7 @@ bool CLuaBaseEntity::delItemAt(const uint16 itemID, const int32 quantity, uint8 if (const auto* PItem = PChar->getStorage(containerId)->GetItem(slotId); PItem && PItem->getID() == itemID) { charutils::UpdateItem(PChar, containerId, slotId, -quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); return true; } @@ -4367,7 +4367,7 @@ bool CLuaBaseEntity::delContainerItems(const sol::object& containerID) } } - PChar->pushPacket(); + PChar->pushPacket(PChar); return true; } @@ -4823,7 +4823,7 @@ bool CLuaBaseEntity::addLinkpearl(const std::string& lsname, bool equip) charutils::SaveCharEquip(PChar); PChar->pushPacket(PChar, PItemLinkPearl->GetLSID()); PChar->pushPacket(PItemLinkPearl, LOC_INVENTORY, PItemLinkPearl->getSlotID()); - PChar->pushPacket(); + PChar->pushPacket(PChar); charutils::LoadInventory(PChar); } return true; @@ -4851,7 +4851,7 @@ auto CLuaBaseEntity::addSoulPlate(const std::string& name, uint32 interestData, // Deduct Blank Plate battleutils::RemoveAmmo(PChar); - PChar->pushPacket(); + PChar->pushPacket(PChar); // Used Soul Plate CItem* PItem = itemutils::GetItem(ITEMID::SOUL_PLATE); @@ -4984,7 +4984,7 @@ void CLuaBaseEntity::confirmTrade() const } } PChar->TradeContainer->Clean(); - PChar->pushPacket(); + PChar->pushPacket(PChar); } /************************************************************************ @@ -5024,7 +5024,7 @@ void CLuaBaseEntity::tradeComplete() const } } PChar->TradeContainer->Clean(); - PChar->pushPacket(); + PChar->pushPacket(PChar); } auto CLuaBaseEntity::getTrade() -> CTradeContainer* diff --git a/src/map/map_networking.cpp b/src/map/map_networking.cpp index 2843de7dd61..069cd8b4135 100644 --- a/src/map/map_networking.cpp +++ b/src/map/map_networking.cpp @@ -547,6 +547,9 @@ int32 MapNetworking::parse(uint8* buff, size_t* buffsize, MapSession* map_sessio PChar->retriggerLatents = false; // reset as we have retriggered the latents somewhere } + // Flush any batched equip changes after processing all incoming packets + PChar->flushEquipChanges(); + map_session_data->client_packet_id = SmallPD_Code; // Google Translate: diff --git a/src/map/packets/c2s/0x01a_action.cpp b/src/map/packets/c2s/0x01a_action.cpp index 330a3fca14c..597e988cc63 100644 --- a/src/map/packets/c2s/0x01a_action.cpp +++ b/src/map/packets/c2s/0x01a_action.cpp @@ -431,7 +431,7 @@ void GP_CLI_COMMAND_ACTION::process(MapSession* PSession, CCharEntity* PChar) co if (luautils::OnChocoboDig(PChar)) { charutils::UpdateItem(PChar, LOC_INVENTORY, slotID, -1); - PChar->pushPacket(); + PChar->pushPacket(PChar); PChar->loc.zone->PushPacket(PChar, CHAR_INRANGE_SELF, std::make_unique(PChar)); } } diff --git a/src/map/packets/c2s/0x029_item_move.cpp b/src/map/packets/c2s/0x029_item_move.cpp index 46109ba1915..7ce0e9023ee 100644 --- a/src/map/packets/c2s/0x029_item_move.cpp +++ b/src/map/packets/c2s/0x029_item_move.cpp @@ -258,12 +258,12 @@ void GP_CLI_COMMAND_ITEM_MOVE::process(MapSession* PSession, CCharEntity* PChar) } } - PChar->pushPacket(); + PChar->pushPacket(PChar); ShowError("GP_CLI_COMMAND_ITEM_MOVE: Location %u Slot %u is full", Category2, ItemIndex2); return; } } - PChar->pushPacket(); + PChar->pushPacket(PChar); } diff --git a/src/map/packets/c2s/0x03a_item_stack.cpp b/src/map/packets/c2s/0x03a_item_stack.cpp index 61fe0e57158..cec44f31a4a 100644 --- a/src/map/packets/c2s/0x03a_item_stack.cpp +++ b/src/map/packets/c2s/0x03a_item_stack.cpp @@ -96,5 +96,5 @@ void GP_CLI_COMMAND_ITEM_STACK::process(MapSession* PSession, CCharEntity* PChar } } - PChar->pushPacket(); + PChar->pushPacket(PChar); } diff --git a/src/map/packets/c2s/0x03b_subcontainer.cpp b/src/map/packets/c2s/0x03b_subcontainer.cpp index c865f7d3476..36b2d3adfa7 100644 --- a/src/map/packets/c2s/0x03b_subcontainer.cpp +++ b/src/map/packets/c2s/0x03b_subcontainer.cpp @@ -175,7 +175,7 @@ void GP_CLI_COMMAND_SUBCONTAINER::process(MapSession* PSession, CCharEntity* PCh { PChar->pushPacket(PMannequin, static_cast(Category1), ItemIndex1); PChar->pushPacket(static_cast(Category1), ItemIndex1, headId, bodyId, handsId, legId, feetId, mainId, subId, rangeId); - PChar->pushPacket(); + PChar->pushPacket(PChar); } else { diff --git a/src/map/packets/c2s/0x083_shop_buy.cpp b/src/map/packets/c2s/0x083_shop_buy.cpp index 74ba54840c8..0e91c510594 100644 --- a/src/map/packets/c2s/0x083_shop_buy.cpp +++ b/src/map/packets/c2s/0x083_shop_buy.cpp @@ -76,7 +76,7 @@ void GP_CLI_COMMAND_SHOP_BUY::process(MapSession* PSession, CCharEntity* PChar) charutils::UpdateItem(PChar, LOC_INVENTORY, 0, -static_cast(price * quantity)); ShowInfo("User '%s' purchased %u of item of ID %u [from VENDOR] ", PChar->getName(), quantity, itemId); PChar->pushPacket(this->ShopItemIndex, quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } } diff --git a/src/map/packets/c2s/0x085_shop_sell_set.cpp b/src/map/packets/c2s/0x085_shop_sell_set.cpp index b78ef388df6..3f6b180c341 100644 --- a/src/map/packets/c2s/0x085_shop_sell_set.cpp +++ b/src/map/packets/c2s/0x085_shop_sell_set.cpp @@ -123,6 +123,6 @@ void GP_CLI_COMMAND_SHOP_SELL_SET::process(MapSession* PSession, CCharEntity* PC auditSale(PChar, itemId, quantity, basePrice); ShowInfo("GP_CLI_COMMAND_SHOP_SELL_SET: Player '%s' sold %u of itemID %u (Total: %u gil) [to VENDOR] ", PChar->getName(), quantity, itemId, cost); PChar->pushPacket(nullptr, itemId, quantity, MsgStd::Sell); - PChar->pushPacket(); + PChar->pushPacket(PChar); PChar->Container->setItem(PChar->Container->getExSize(), 0, -1, 0); } diff --git a/src/map/packets/c2s/0x0aa_guild_buy.cpp b/src/map/packets/c2s/0x0aa_guild_buy.cpp index 8a4457bf69b..6772cb870f5 100644 --- a/src/map/packets/c2s/0x0aa_guild_buy.cpp +++ b/src/map/packets/c2s/0x0aa_guild_buy.cpp @@ -80,7 +80,7 @@ void GP_CLI_COMMAND_GUILD_BUY::process(MapSession* PSession, CCharEntity* PChar) ShowInfo("GP_CLI_COMMAND_GUILD_BUY: Player '%s' purchased %u of itemID %u [from GUILD] ", PChar->getName(), quantity, ItemNo); PChar->PGuildShop->GetItem(shopSlotId)->setQuantity(PChar->PGuildShop->GetItem(shopSlotId)->getQuantity() - quantity); PChar->pushPacket(PChar, PChar->PGuildShop->GetItem(PChar->PGuildShop->SearchItem(ItemNo))->getQuantity(), ItemNo, quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } } diff --git a/src/map/packets/c2s/0x0ac_guild_sell.cpp b/src/map/packets/c2s/0x0ac_guild_sell.cpp index 83907b3033f..af7d32cb3f9 100644 --- a/src/map/packets/c2s/0x0ac_guild_sell.cpp +++ b/src/map/packets/c2s/0x0ac_guild_sell.cpp @@ -100,7 +100,7 @@ void GP_CLI_COMMAND_GUILD_SELL::process(MapSession* PSession, CCharEntity* PChar PChar->PGuildShop->GetItem(shopSlotId)->setQuantity(PChar->PGuildShop->GetItem(shopSlotId)->getQuantity() + quantity); PChar->pushPacket( PChar, PChar->PGuildShop->GetItem(PChar->PGuildShop->SearchItem(ItemNo))->getQuantity(), ItemNo, quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } // TODO: error messages! diff --git a/src/map/packets/c2s/0x0c4_group_comlink_active.cpp b/src/map/packets/c2s/0x0c4_group_comlink_active.cpp index b4f608c6cf3..1ea03c94371 100644 --- a/src/map/packets/c2s/0x0c4_group_comlink_active.cpp +++ b/src/map/packets/c2s/0x0c4_group_comlink_active.cpp @@ -112,7 +112,7 @@ const auto equipLinkshell = [](CCharEntity* PChar, CItemLinkshell* PItemLinkshel PItemLinkshell->getSlotID()); PChar->pushPacket(PItemLinkshell, static_cast(PItemLinkshell->getLocationID()), PItemLinkshell->getSlotID()); - PChar->pushPacket(); + PChar->pushPacket(PChar); PChar->pushPacket(MsgStd::LinkshellNoLongerExists); return; @@ -221,6 +221,6 @@ void GP_CLI_COMMAND_GROUP_COMLINK_ACTIVE::process(MapSession* PSession, CCharEnt break; } - PChar->pushPacket(); + PChar->pushPacket(PChar); PChar->pushPacket(PChar); } diff --git a/src/map/packets/c2s/0x0fa_myroom_layout.cpp b/src/map/packets/c2s/0x0fa_myroom_layout.cpp index 540ed11b494..66176fdbc7d 100644 --- a/src/map/packets/c2s/0x0fa_myroom_layout.cpp +++ b/src/map/packets/c2s/0x0fa_myroom_layout.cpp @@ -196,6 +196,6 @@ void GP_CLI_COMMAND_MYROOM_LAYOUT::process(MapSession* PSession, CCharEntity* PC } PChar->pushPacket(PItem, static_cast(MyroomCategory), MyroomItemIndex); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } diff --git a/src/map/packets/c2s/0x0fb_myroom_bankin.cpp b/src/map/packets/c2s/0x0fb_myroom_bankin.cpp index 202be2ee2ee..2b77a711548 100644 --- a/src/map/packets/c2s/0x0fb_myroom_bankin.cpp +++ b/src/map/packets/c2s/0x0fb_myroom_bankin.cpp @@ -119,7 +119,7 @@ void GP_CLI_COMMAND_MYROOM_BANKIN::process(MapSession* PSession, CCharEntity* PC PChar->loc.zone->SpawnConditionalNPCs(PChar); } PChar->pushPacket(PItem, static_cast(MyroomCategory), PItem->getSlotID()); - PChar->pushPacket(); + PChar->pushPacket(PChar); } else { diff --git a/src/map/packets/c2s/0x0fc_myroom_plant_add.cpp b/src/map/packets/c2s/0x0fc_myroom_plant_add.cpp index 2f0e5b0ead0..6f0dfb1aa5d 100644 --- a/src/map/packets/c2s/0x0fc_myroom_plant_add.cpp +++ b/src/map/packets/c2s/0x0fc_myroom_plant_add.cpp @@ -120,6 +120,6 @@ void GP_CLI_COMMAND_MYROOM_PLANT_ADD::process(MapSession* PSession, CCharEntity* PChar->pushPacket(PPotItem, static_cast(MyroomPlantCategory), MyroomPlantItemIndex); charutils::UpdateItem(PChar, MyroomAddCategory, MyroomAddItemIndex, -1); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } diff --git a/src/map/packets/c2s/0x0fe_myroom_plant_crop.cpp b/src/map/packets/c2s/0x0fe_myroom_plant_crop.cpp index 73025fbfc0f..034f415b93f 100644 --- a/src/map/packets/c2s/0x0fe_myroom_plant_crop.cpp +++ b/src/map/packets/c2s/0x0fe_myroom_plant_crop.cpp @@ -103,6 +103,6 @@ void GP_CLI_COMMAND_MYROOM_PLANT_CROP::process(MapSession* PSession, CCharEntity PItem->getSlotID()); PChar->pushPacket(PItem, static_cast(MyroomPlantCategory), MyroomPlantItemIndex); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } diff --git a/src/map/packets/c2s/0x0ff_myroom_plant_stop.cpp b/src/map/packets/c2s/0x0ff_myroom_plant_stop.cpp index f1b9b13dcf4..d7cff7b4423 100644 --- a/src/map/packets/c2s/0x0ff_myroom_plant_stop.cpp +++ b/src/map/packets/c2s/0x0ff_myroom_plant_stop.cpp @@ -62,6 +62,6 @@ void GP_CLI_COMMAND_MYROOM_PLANT_STOP::process(MapSession* PSession, CCharEntity PItem->getSlotID()); PChar->pushPacket(PItem, static_cast(MyroomPlantCategory), MyroomPlantItemIndex); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } diff --git a/src/map/packets/c2s/0x106_bazaar_buy.cpp b/src/map/packets/c2s/0x106_bazaar_buy.cpp index 45e9ab1bd99..b6d1cf83923 100644 --- a/src/map/packets/c2s/0x106_bazaar_buy.cpp +++ b/src/map/packets/c2s/0x106_bazaar_buy.cpp @@ -164,7 +164,7 @@ void GP_CLI_COMMAND_BAZAAR_BUY::process(MapSession* PSession, CCharEntity* PChar charutils::UpdateItem(PTarget, LOC_INVENTORY, this->BazaarItemIndex, -static_cast(this->BuyNum)); PTarget->pushPacket(PBazaar->GetItem(this->BazaarItemIndex), LOC_INVENTORY, this->BazaarItemIndex); - PTarget->pushPacket(); + PTarget->pushPacket(PTarget); DebugBazaarsFmt("Bazaar Interaction [Purchase Successful] - Buyer: {}, Seller: {}, Item: {}, Qty: {}, Cost: {}", PChar->name, PTarget->name, PItem->getName(), this->BuyNum, PriceWithTax); diff --git a/src/map/packets/c2s/0x10a_bazaar_itemset.cpp b/src/map/packets/c2s/0x10a_bazaar_itemset.cpp index 9669397fed5..c8c58579d20 100644 --- a/src/map/packets/c2s/0x10a_bazaar_itemset.cpp +++ b/src/map/packets/c2s/0x10a_bazaar_itemset.cpp @@ -60,7 +60,7 @@ void GP_CLI_COMMAND_BAZAAR_ITEMSET::process(MapSession* PSession, CCharEntity* P PItem->setSubType((Price == 0 ? ITEM_UNLOCKED : ITEM_LOCKED)); PChar->pushPacket(PItem, LOC_INVENTORY, ItemIndex); - PChar->pushPacket(); + PChar->pushPacket(PChar); DebugBazaarsFmt("Bazaar Interaction [Price Set] - Character: {}, Item: {}, Price: {}", PChar->name, PItem->getName(), Price); } diff --git a/src/map/packets/s2c/0x01d_item_same.cpp b/src/map/packets/s2c/0x01d_item_same.cpp index c76327d76f2..3312d895270 100644 --- a/src/map/packets/s2c/0x01d_item_same.cpp +++ b/src/map/packets/s2c/0x01d_item_same.cpp @@ -21,22 +21,23 @@ #include "0x01d_item_same.h" +#include "entities/charentity.h" #include "item_container.h" -GP_SERV_COMMAND_ITEM_SAME::GP_SERV_COMMAND_ITEM_SAME() +GP_SERV_COMMAND_ITEM_SAME::GP_SERV_COMMAND_ITEM_SAME(CCharEntity* PChar) { auto& packet = this->data(); packet.State = GP_SERV_COMMAND_ITEM_SAME_STATE::AllLoaded; - packet.padding00[0] = CONTAINER_ID::MAX_CONTAINER_ID; // This data appears to be used as the container index at times, but the client does not use any part of it at all. - packet.Flags = 0x0003FFFF; + packet.padding00[0] = CONTAINER_ID::MAX_CONTAINER_ID; + packet.Flags = PChar->inventorySyncState().getSyncedFlags(); } -GP_SERV_COMMAND_ITEM_SAME::GP_SERV_COMMAND_ITEM_SAME(const CONTAINER_ID id) +GP_SERV_COMMAND_ITEM_SAME::GP_SERV_COMMAND_ITEM_SAME(const CONTAINER_ID id, CCharEntity* PChar) { auto& packet = this->data(); packet.State = GP_SERV_COMMAND_ITEM_SAME_STATE::StillLoading; - packet.padding00[0] = id; // This data appears to be used as the container index at times, but the client does not use any part of it at all. - // TODO: Missing Flags + packet.padding00[0] = id; + packet.Flags = PChar->inventorySyncState().getSyncedFlags(); } diff --git a/src/map/packets/s2c/0x01d_item_same.h b/src/map/packets/s2c/0x01d_item_same.h index d90ce80c36f..4ff907f2f3f 100644 --- a/src/map/packets/s2c/0x01d_item_same.h +++ b/src/map/packets/s2c/0x01d_item_same.h @@ -24,6 +24,7 @@ #include "base.h" enum CONTAINER_ID : uint8; +class CCharEntity; enum class GP_SERV_COMMAND_ITEM_SAME_STATE : uint8_t { @@ -43,6 +44,6 @@ class GP_SERV_COMMAND_ITEM_SAME final : public GP_SERV_PACKET(param.BidPrice)); PChar->pushPacket(GP_CLI_COMMAND_AUC_COMMAND::Bid, 0x01, param.ItemNo, param.BidPrice, param.ItemStacks, PItem->getStackSize()); - PChar->pushPacket(); + PChar->pushPacket(PChar); return true; } @@ -325,7 +325,7 @@ void auctionutils::CancelSale(CCharEntity* PChar, int8_t AucWorkIndex) if (success) { PChar->pushPacket(GP_CLI_COMMAND_AUC_COMMAND::LotCancel, 0, PChar, static_cast(AucWorkIndex), false); - PChar->pushPacket(); + PChar->pushPacket(PChar); return; } } diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 5a1f06af803..0758978ed91 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -3963,7 +3963,7 @@ bool HasNinjaTool(CBattleEntity* PEntity, CSpell* PSpell, bool ConsumeTool) { // Futae Takes 2 of Your Tools charutils::UpdateItem(PChar, LOC_INVENTORY, SlotID, -2); - PChar->pushPacket(); + PChar->pushPacket(PChar); } else { @@ -3979,7 +3979,7 @@ bool HasNinjaTool(CBattleEntity* PEntity, CSpell* PSpell, bool ConsumeTool) if (ConsumeTool && xirand::GetRandomNumber(100) > chance) { charutils::UpdateItem(PChar, LOC_INVENTORY, SlotID, -1); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } } @@ -6236,13 +6236,13 @@ bool RemoveAmmo(CCharEntity* PChar, int quantity) charutils::UnequipItem(PChar, SLOT_AMMO); PChar->RequestPersist(CHAR_PERSIST::EQUIP); charutils::UpdateItem(PChar, loc, slot, -quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); return true; } else { charutils::UpdateItem(PChar, PChar->equipLoc[SLOT_AMMO], PChar->equip[SLOT_AMMO], -quantity); - PChar->pushPacket(); + PChar->pushPacket(PChar); return false; } } diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 454a24563b3..1818fce54aa 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -1472,13 +1472,17 @@ void SendInventory(CCharEntity* PChar) PChar->pushPacket(PItem, LocationID, slotID); } } + + // Mark this container as synced and send ITEM_SAME with updated flags + PChar->inventorySyncState().markSynced(LocationID); + PChar->pushPacket(LocationID, PChar); }; // Send important items first // Note: it's possible that non-essential inventory items are sent in response to another packet - // TODO: What order are these sent in? - for (auto&& containerID : { LOC_INVENTORY, LOC_TEMPITEMS, LOC_WARDROBE, LOC_WARDROBE2, LOC_WARDROBE3, LOC_WARDROBE4, LOC_WARDROBE5, LOC_WARDROBE6, LOC_WARDROBE7, LOC_WARDROBE8, LOC_MOGSAFE, LOC_STORAGE, LOC_MOGLOCKER, LOC_MOGSATCHEL, LOC_MOGSACK, LOC_MOGCASE, LOC_MOGSAFE2 }) + // Container order based on retail capture + for (auto&& containerID : { LOC_INVENTORY, LOC_MOGSAFE, LOC_MOGSAFE2, LOC_STORAGE, LOC_RECYCLEBIN, LOC_WARDROBE, LOC_WARDROBE2, LOC_WARDROBE3, LOC_WARDROBE4, LOC_WARDROBE5, LOC_WARDROBE6, LOC_WARDROBE7, LOC_WARDROBE8, LOC_TEMPITEMS, LOC_MOGLOCKER, LOC_MOGSATCHEL, LOC_MOGSACK, LOC_MOGCASE }) { pushContainer(containerID); } @@ -1513,7 +1517,7 @@ void SendInventory(CCharEntity* PChar) PChar->pushPacket(PChar, 2); } - PChar->pushPacket(); // "Finish" type + PChar->pushPacket(PChar); } // Sends all 64 Unity ranking packets to the client (0x063 type 0x07) @@ -1731,7 +1735,7 @@ uint8 AddItem(CCharEntity* PChar, uint8 LocationID, CItem* PItem, bool silence) } PChar->pushPacket(PItem, static_cast(LocationID), SlotID); - PChar->pushPacket(); + PChar->pushPacket(PChar); } else { @@ -1976,7 +1980,7 @@ void DropItem(CCharEntity* PChar, uint8 container, uint8 slotID, int32 quantity, { ShowInfo("Player %s DROPPING itemID: %s (%u) quantity: %u", PChar->getName(), itemutils::GetItemPointer(ItemID)->getName(), ItemID, quantity); PChar->pushPacket(nullptr, ItemID, quantity, MsgStd::ThrowAway); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } @@ -2059,7 +2063,7 @@ void DoTrade(CCharEntity* PChar, CCharEntity* PTarget) * * ************************************************************************/ -void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, bool update) +void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, Recalculate recalculate) { if (PChar == nullptr) { @@ -2161,9 +2165,6 @@ void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, bool update) PChar->PLatentEffectContainer->DelLatentEffects(((CItemEquipment*)PItem)->getReqLvl(), equipSlotID); PChar->delPetModifiers(&((CItemEquipment*)PItem)->petModList); - PChar->pushPacket(PItem, ItemLockFlg::Normal); // ??? - PChar->pushPacket(0, static_cast(equipSlotID), LOC_INVENTORY); - switch (equipSlotID) { case SLOT_HEAD: @@ -2258,15 +2259,14 @@ void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, bool update) luautils::OnItemUnequip(PChar, PItem); - if (update) + PChar->inventorySyncState().queueEquipChange(LOC_INVENTORY, 0, static_cast(equipSlotID), PItem, Equipping::No); + + if (recalculate) { charutils::BuildingCharSkillsTable(PChar); PChar->UpdateHealth(); - PChar->m_EquipSwap = true; + PChar->updatemask |= UPDATE_HP; PChar->updatemask |= UPDATE_LOOK; - - // Mark container dirty - PChar->dirtyInventoryContainers[static_cast(PItem->getLocationID())] = true; } } } @@ -2326,7 +2326,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta } } - UnequipItem(PChar, equipSlotID, false); + UnequipItem(PChar, equipSlotID, Recalculate::No); // When equipping PItem - Remove all equip in slots which are also restricted by PItem // e.g. Equipping a Black Cloak should remove head equipment @@ -2338,7 +2338,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta { if (removeSlotID & (1 << i)) { - UnequipItem(PChar, i, false); + UnequipItem(PChar, i, Recalculate::No); if (i >= SLOT_HEAD && i <= SLOT_FEET) { switch (i) @@ -2370,7 +2370,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta CItemEquipment* armor = PChar->getEquip((SLOTTYPE)i); if (armor && armor->isType(ITEM_EQUIPMENT) && armor->getRemoveSlotId() & PItem->getEquipSlotId()) { - UnequipItem(PChar, i, false); + UnequipItem(PChar, i, Recalculate::No); } } @@ -2398,12 +2398,12 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta CItemWeapon* PWeapon = static_cast(sub); if (PWeapon->getSkillType() != SKILL_NONE || static_cast(PItem)->getSkillType() == SKILL_HAND_TO_HAND) { - UnequipItem(PChar, SLOT_SUB, false); + UnequipItem(PChar, SLOT_SUB, Recalculate::No); } } else { - UnequipItem(PChar, SLOT_SUB, false); + UnequipItem(PChar, SLOT_SUB, Recalculate::No); } } if (static_cast(PItem)->getSkillType() == SKILL_HAND_TO_HAND) @@ -2452,7 +2452,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta { if (!PItem->isType(ITEM_WEAPON)) { - UnequipItem(PChar, SLOT_MAIN, false); + UnequipItem(PChar, SLOT_MAIN, Recalculate::No); } break; } @@ -2481,7 +2481,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta { if (!PItem->isType(ITEM_WEAPON)) { - UnequipItem(PChar, SLOT_MAIN, false); + UnequipItem(PChar, SLOT_MAIN, Recalculate::No); } else if (static_cast(PItem)->getSkillType() != SKILL_NONE) { @@ -2506,7 +2506,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta if (static_cast(PItem)->getSkillType() != weapon->getSkillType() || (weapon->getSkillType() != SKILL_ARCHERY && static_cast(PItem)->getSubSkillType() != weapon->getSubSkillType())) { - UnequipItem(PChar, SLOT_AMMO, false); + UnequipItem(PChar, SLOT_AMMO, Recalculate::No); } } PChar->m_Weapons[SLOT_RANGED] = PItem; @@ -2526,7 +2526,7 @@ bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 conta if (static_cast(PItem)->getSkillType() != weapon->getSkillType() || (weapon->getSkillType() != SKILL_ARCHERY && static_cast(PItem)->getSubSkillType() != weapon->getSubSkillType())) { - UnequipItem(PChar, SLOT_RANGED, false); + UnequipItem(PChar, SLOT_RANGED, Recalculate::No); } } if (PChar->equip[SLOT_RANGED] == 0) @@ -2970,7 +2970,7 @@ void AddItemToRecycleBin(CCharEntity* PChar, uint32 container, uint8 slotID, uin } PChar->pushPacket(nullptr, PItem->getID(), quantity, MsgStd::ThrowAway); } - PChar->pushPacket(); + PChar->pushPacket(PChar); } void EmptyRecycleBin(CCharEntity* PChar) @@ -3113,8 +3113,7 @@ void EquipItem(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 contai return; } - CItemEquipment* PItem = dynamic_cast(PChar->getStorage(containerID)->GetItem(slotID)); - CItem* POldItem = PChar->getEquip(static_cast(equipSlotID)); + CItemEquipment* PItem = dynamic_cast(PChar->getStorage(containerID)->GetItem(slotID)); if (PItem && PItem == PChar->getEquip(static_cast(equipSlotID))) { @@ -3159,8 +3158,6 @@ void EquipItem(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 contai { RemoveSub(PChar); } - - PChar->pushPacket(slotID, static_cast(equipSlotID), static_cast(containerID)); } else { @@ -3201,12 +3198,10 @@ void EquipItem(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 contai // Only call the lua onEquip if its a valid equip - e.g. has passed EquipArmor and other checks above luautils::OnItemEquip(PChar, PItem); - - PChar->pushPacket(slotID, static_cast(equipSlotID), static_cast(containerID)); - PChar->pushPacket(PItem, ItemLockFlg::NoDrop); } } } + if (equipSlotID == SLOT_MAIN || equipSlotID == SLOT_RANGED || equipSlotID == SLOT_SUB) { if (!PItem || !PItem->isType(ITEM_EQUIPMENT) || @@ -3224,25 +3219,18 @@ void EquipItem(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 contai } BuildingCharWeaponSkills(PChar); - PChar->pushPacket(PChar); } charutils::BuildingCharSkillsTable(PChar); - PChar->UpdateHealth(); - PChar->m_EquipSwap = true; - PChar->updatemask |= UPDATE_LOOK; - // PItem can be null if item id is 0 (unequip) - if (PItem) + if (PItem != nullptr && PItem->isType(ITEM_EQUIPMENT)) { - PChar->dirtyInventoryContainers[static_cast(PItem->getLocationID())] = true; + PChar->inventorySyncState().queueEquipChange(static_cast(containerID), slotID, static_cast(equipSlotID), PItem, Equipping::Yes); } - if (POldItem) - { - PChar->dirtyInventoryContainers[static_cast(POldItem->getLocationID())] = true; - } + PChar->updatemask |= UPDATE_HP; + PChar->updatemask |= UPDATE_LOOK; } /************************************************************************ @@ -3292,8 +3280,6 @@ void CheckValidEquipment(CCharEntity* PChar) CheckUnarmedWeapon(PChar); } - PChar->pushPacket(PChar); - BuildingCharWeaponSkills(PChar); PChar->RequestPersist(CHAR_PERSIST::EQUIP); } diff --git a/src/map/utils/charutils.h b/src/map/utils/charutils.h index 7f14f916f36..7ced6d2aa08 100644 --- a/src/map/utils/charutils.h +++ b/src/map/utils/charutils.h @@ -27,6 +27,8 @@ #include "items/item_equipment.h" #include "zone.h" +using Recalculate = xi::Flag; + struct Charge_t; enum class MissionLog : uint8_t; enum class QuestLog : uint8_t; @@ -130,8 +132,7 @@ void CheckEquipLogic(CCharEntity* PChar, SCRIPTTYPE ScriptType, uint32 param); void SaveJobChangeGear(CCharEntity* PChar); void LoadJobChangeGear(CCharEntity* PChar); void EquipItem(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 containerID); -void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, - bool update = true); // call with update == false to prevent calls to UpdateHealth() - used for correct handling of stats on armor swaps +void UnequipItem(CCharEntity* PChar, uint8 equipSlotID, xi::Flag recalculate = Recalculate::Yes); bool hasSlotEquipped(CCharEntity* PChar, uint8 equipSlotID); void RemoveSub(CCharEntity* PChar); bool EquipArmor(CCharEntity* PChar, uint8 slotID, uint8 equipSlotID, uint8 containerID); diff --git a/src/map/utils/dboxutils.cpp b/src/map/utils/dboxutils.cpp index 8c22d99742a..974609a694f 100644 --- a/src/map/utils/dboxutils.cpp +++ b/src/map/utils/dboxutils.cpp @@ -181,7 +181,7 @@ void dboxutils::AddItemsToBeSent(CCharEntity* PChar, GP_CLI_COMMAND_PBX_BOXNO Bo { PChar->UContainer->SetItem(PostWorkNo, PUBoxItem); PChar->pushPacket(GP_CLI_COMMAND_PBX_COMMAND::Set, BoxNo, PUBoxItem, PostWorkNo, PChar->UContainer->GetItemsCount(), 1); - PChar->pushPacket(); + PChar->pushPacket(PChar); } else { @@ -651,7 +651,7 @@ void dboxutils::TakeItemFromCell(CCharEntity* PChar, GP_CLI_COMMAND_PBX_BOXNO Bo PChar->getName(), PChar->id, PItem->getName(), PItem->getID(), PostWorkNo); PChar->pushPacket(GP_CLI_COMMAND_PBX_COMMAND::Get, BoxNo, PItem, PostWorkNo, PChar->UContainer->GetItemsCount(), 1); - PChar->pushPacket(); + PChar->pushPacket(PChar); PChar->UContainer->SetItem(PostWorkNo, nullptr); destroy(PItem); } diff --git a/src/map/utils/fishingutils.cpp b/src/map/utils/fishingutils.cpp index c96b99f2233..06bb137cd31 100644 --- a/src/map/utils/fishingutils.cpp +++ b/src/map/utils/fishingutils.cpp @@ -1356,13 +1356,13 @@ bool BaitLoss(CCharEntity* PChar, RemoveFly removeFly, SendUpdate sendUpdate) { if (PBait->getQuantity() == 1) { - charutils::UnequipItem(PChar, SLOT_AMMO, false); + charutils::UnequipItem(PChar, SLOT_AMMO); } charutils::UpdateItem(PChar, PBait->getLocationID(), PBait->getSlotID(), -1); if (sendUpdate) { - PChar->pushPacket(); + PChar->pushPacket(PChar); } } } @@ -1390,11 +1390,11 @@ void RodBreak(CCharEntity* PChar) if (PRod->breakable && PRod->brokenRodId > 0) { BaitLoss(PChar, RemoveFly::Yes, SendUpdate::No); - charutils::UnequipItem(PChar, SLOT_RANGED, false); + charutils::UnequipItem(PChar, SLOT_RANGED); uint8 location = PRanged->getLocationID(); charutils::UpdateItem(PChar, location, PRanged->getSlotID(), -1); charutils::AddItem(PChar, location, PRod->brokenRodId, 1); - PChar->pushPacket(); + PChar->pushPacket(PChar); } } diff --git a/src/map/utils/synthutils.cpp b/src/map/utils/synthutils.cpp index 71b7fa0520c..37d1ed0f7bb 100644 --- a/src/map/utils/synthutils.cpp +++ b/src/map/utils/synthutils.cpp @@ -861,7 +861,7 @@ void handleSynthSuccess(CCharEntity* PChar) PChar->pushPacket(PItem, LOC_INVENTORY, invSlotID); } - PChar->pushPacket(); + PChar->pushPacket(PChar); // Use appropiate message (Regular or desynthesis) const auto message = PChar->CraftContainer->getCraftType() == CRAFT_DESYNTHESIS ? SynthesisResult::SuccessDesynth : SynthesisResult::Success;