diff --git a/changelog.txt b/changelog.txt index 47aa7016f..8dd4b77ec 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 02/06/2015 == Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions +Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore checks. The client excludes this range and it causes issues when performing item searches - dupe lore checks were added to account for this. == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. diff --git a/common/item.cpp b/common/item.cpp index 40fc98474..0e2f0f574 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -506,6 +506,7 @@ int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItem(m_cursor, item_id, quantity); @@ -552,6 +553,7 @@ int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByUse(m_cursor, use, quantity); @@ -597,6 +599,7 @@ int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByLoreGroup(m_cursor, loregroup); @@ -1119,6 +1122,8 @@ ItemInst* Inventory::_GetItem(const std::map& bucket, int16 sl // Assumes item has already been allocated int16 Inventory::_PutItem(int16 slot_id, ItemInst* inst) { + // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... + // // If putting a nullptr into slot, we need to remove slot without memory delete if (inst == nullptr) { //Why do we not delete the poped item here???? @@ -1263,6 +1268,9 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1327,6 +1335,9 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) return Inventory::CalcSlotId(MainCursor, bag_iter->first); } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1406,6 +1417,9 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 94138fbe5..69f1de882 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -177,16 +177,16 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { } -bool Client::CheckLoreConflict(const Item_Struct* item) { - if (!item) - return false; - if (!(item->LoreFlag)) - return false; +bool Client::CheckLoreConflict(const Item_Struct* item) +{ + if (!item) { return false; } + if (!item->LoreFlag) { return false; } + if (item->LoreGroup == 0) { return false; } - if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result + if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX); - //If the item has a lore group, we check for other items with the same group and return the result + // If the item has a lore group, we check for other items with the same group and return the result return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX); } @@ -680,20 +680,37 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { return INVALID_ID; } -void Client::SendCursorBuffer() { +void Client::SendCursorBuffer() +{ // Temporary work-around for the RoF+ Client Buffer // Instead of dealing with client moving items in cursor buffer, // we can just send the next item in the cursor buffer to the cursor. - if (GetClientVersion() >= ClientVersion::RoF) - { - if (!GetInv().CursorEmpty()) - { - const ItemInst* inst = GetInv().GetCursorItem(); - if (inst) - { - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } + if (GetClientVersion() < ClientVersion::RoF) { return; } + if (GetInv().CursorEmpty()) { return; } + + auto test_inst = GetInv().GetCursorItem(); + if (test_inst == nullptr) { return; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor); + SendCursorBuffer(); + } + else { + SendItemPacket(MainCursor, test_inst, ItemPacketSummonItem); } } @@ -1320,10 +1337,33 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } - // This could be expounded upon at some point to let the server know that - // the client has moved a buffered cursor item onto the active cursor -U if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + if (GetClientVersion() >= ClientVersion::RoF) { return true; } // Can't do RoF+ + + if (move_in->to_slot == MainCursor) { + auto test_inst = m_inv.GetItem(MainCursor); + if (test_inst == nullptr) { return true; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return true; } + if (!test_item->LoreFlag) { return true; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor, 0, true); + } + } return true; }