diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 4f245ee58..4f64c7a58 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -241,48 +241,70 @@ EQEmu::ItemInstance* EQEmu::InventoryProfile::GetCursorItem() } // Swap items in inventory -bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, uint16 race_id, uint8 class_id, uint16 deity_id, uint8 level) +bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id, uint8 class_id, uint16 deity_id, uint8 level) { + fail_state = swapInvalid; + // Temp holding areas for a and b ItemInstance* inst_a = GetItem(slot_a); ItemInstance* inst_b = GetItem(slot_b); if (inst_a) { - if (!inst_a->IsSlotAllowed(slot_b)) + if (!inst_a->IsSlotAllowed(slot_b)) { + fail_state = swapNotAllowed; return false; - + } if ((slot_b >= legacy::EQUIPMENT_BEGIN && slot_b <= legacy::EQUIPMENT_END) || slot_b == inventory::slotPowerSource) { auto item_a = inst_a->GetItem(); - if (!item_a) + if (!item_a) { + fail_state = swapNullData; return false; - if (race_id && class_id && !item_a->IsEquipable(race_id, class_id)) + } + if (race_id && class_id && !item_a->IsEquipable(race_id, class_id)) { + fail_state = swapRaceClass; return false; - if (deity_id && item_a->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_a->Deity)) + } + if (deity_id && item_a->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_a->Deity)) { + fail_state = swapDeity; return false; - if (level && item_a->ReqLevel && level < item_a->ReqLevel) + } + if (level && item_a->ReqLevel && level < item_a->ReqLevel) { + fail_state = swapLevel; return false; + } } } if (inst_b) { - if (!inst_b->IsSlotAllowed(slot_a)) + if (!inst_b->IsSlotAllowed(slot_a)) { + fail_state = swapNotAllowed; return false; - + } if ((slot_a >= legacy::EQUIPMENT_BEGIN && slot_a <= legacy::EQUIPMENT_END) || slot_a == inventory::slotPowerSource) { auto item_b = inst_b->GetItem(); - if (!item_b) + if (!item_b) { + fail_state = swapNullData; return false; - if (race_id && class_id && !item_b->IsEquipable(race_id, class_id)) + } + if (race_id && class_id && !item_b->IsEquipable(race_id, class_id)) { + fail_state = swapRaceClass; return false; - if (deity_id && item_b->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_b->Deity)) + } + if (deity_id && item_b->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_b->Deity)) { + fail_state = swapDeity; return false; - if (level && item_b->ReqLevel && level < item_b->ReqLevel) + } + if (level && item_b->ReqLevel && level < item_b->ReqLevel) { + fail_state = swapLevel; return false; + } } } - _PutItem(slot_a, inst_b); // Copy b->a - _PutItem(slot_b, inst_a); // Copy a->b + _PutItem(slot_a, inst_b); // Assign b->a + _PutItem(slot_b, inst_a); // Assign a->b + + fail_state = swapPass; return true; } diff --git a/common/inventory_profile.h b/common/inventory_profile.h index d6d166337..702c92e83 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -127,7 +127,8 @@ namespace EQEmu ItemInstance* GetCursorItem(); // Swap items in inventory - bool SwapItem(int16 slot_a, int16 slot_b, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); + enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel }; + bool SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); // Remove item from inventory bool DeleteItem(int16 slot_id, uint8 quantity = 0); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index ceaeb282b..42520058c 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1793,7 +1793,8 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Nothing in destination slot: split stack into two if ((int16)move_in->number_in_stack >= src_inst->GetCharges()) { // Move entire stack - if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; } + EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid; + if (!m_inv.SwapItem(src_slot_id, dst_slot_id, fail_state)) { return false; } Log(Logs::Detail, Logs::Inventory, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack); } else { @@ -1823,7 +1824,21 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } SetMaterial(dst_slot_id,src_inst->GetItem()->ID); } - if(!m_inv.SwapItem(src_slot_id, dst_slot_id, GetRace(), GetClass(), GetDeity(), GetLevel())) { return false; } + + EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid; + if (!m_inv.SwapItem(src_slot_id, dst_slot_id, fail_state, GetRace(), GetClass(), GetDeity(), GetLevel())) { + const char* fail_message = "The selected slot was invalid."; + if (fail_state == EQEmu::InventoryProfile::swapRaceClass || fail_state == EQEmu::InventoryProfile::swapDeity) + fail_message = "Your class, deity and/or race may not equip that item."; + else if (fail_state == EQEmu::InventoryProfile::swapLevel) + fail_message = "You are not sufficient level to use this item."; + + if (fail_message) + Message(CC_Red, "%s", fail_message); + + return false; + } + Log(Logs::Detail, Logs::Inventory, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id); if (src_slot_id <= EQEmu::legacy::EQUIPMENT_END || src_slot_id == EQEmu::inventory::slotPowerSource) { diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index 0866eebc5..d29bb435b 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -40,7 +40,8 @@ int Lua_Inventory::PushCursor(Lua_ItemInst item) { bool Lua_Inventory::SwapItem(int slot_a, int slot_b) { Lua_Safe_Call_Bool(); - return self->SwapItem(slot_a, slot_b); + EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid; + return self->SwapItem(slot_a, slot_b, fail_state); } bool Lua_Inventory::DeleteItem(int slot_id) { diff --git a/zone/lua_inventory.h b/zone/lua_inventory.h index baaa5d10f..1e391b4da 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -18,6 +18,11 @@ namespace luabind { luabind::scope lua_register_inventory(); +// This class should be deprecated due to the nature of inventory actions. +// Direct manipulation of the inventory system bypasses the client management +// of database calls and can lead to lost items, duplicated items and/or +// desync'd inventories, if not handled correctly. + class Lua_Inventory : public Lua_Ptr { typedef EQEmu::InventoryProfile NativeType;