mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
[Feature] Implement Move Multiple Items (#4259)
* Implement Move Multiple Items * Send LinkDead on invalid packet * structure this more like MoveItem * implement all modes * remove un-needed debug message * handle mode 3 swaps in bank\shared bank correctly. * Revert "handle mode 3 swaps in bank\shared bank correctly." This reverts commit ce01fbfde70d52e88381772a6c7a77b4b650c7c5. * Revert "remove un-needed debug message" This reverts commit f4b662459e11a60c3a46a97e5320757c4b2b9a84. * handle mode 3 swaps without extra unintended code * correct variable type * remove magic numbers * forgot a semicolon in emu_constants.h * fix bad rebase artifact * Remove unused struct * apply changes discussed in PR * last rebase conflict * last rebase conflict fix more inventory type enum refs * fix windows build error * fix other windows build error. * fix duplication bug
This commit is contained in:
parent
d465a3deba
commit
fc3c691588
@ -1620,6 +1620,32 @@ struct MoveItem_Struct
|
||||
/*0012*/
|
||||
};
|
||||
|
||||
// New for RoF2 - Size: 12
|
||||
struct InventorySlot_Struct
|
||||
{
|
||||
/*000*/ int16 Type; // Worn and Normal inventory = 0, Bank = 1, Shared Bank = 2, Delete Item = -1
|
||||
/*002*/ int16 Unknown02;
|
||||
/*004*/ int16 Slot;
|
||||
/*006*/ int16 SubIndex;
|
||||
/*008*/ int16 AugIndex; // Guessing - Seen 0xffff
|
||||
/*010*/ int16 Unknown01; // Normally 0 - Seen 13262 when deleting an item, but didn't match item ID
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct MultiMoveItemSub_Struct
|
||||
{
|
||||
/*0000*/ InventorySlot_Struct from_slot;
|
||||
/*0012*/ InventorySlot_Struct to_slot;
|
||||
/*0024*/ uint32 number_in_stack;
|
||||
/*0028*/ uint8 unknown[8];
|
||||
};
|
||||
|
||||
struct MultiMoveItem_Struct
|
||||
{
|
||||
/*0000*/ uint32 count;
|
||||
/*0004*/ MultiMoveItemSub_Struct moves[0];
|
||||
};
|
||||
|
||||
// both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will
|
||||
// be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this
|
||||
// info at the moment..but, it is forwarded on to the server for handling/future use)
|
||||
|
||||
@ -10909,7 +10909,121 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
|
||||
{
|
||||
Kick("Unimplemented move multiple items"); // TODO: lets not desync though
|
||||
// This packet is only sent from the client if we ctrl click items in inventory
|
||||
if (m_ClientVersionBit & EQ::versions::maskRoF2AndLater) {
|
||||
if (!CharacterID()) {
|
||||
LinkDead();
|
||||
return;
|
||||
}
|
||||
|
||||
if (app->size < sizeof(MultiMoveItem_Struct)) {
|
||||
LinkDead();
|
||||
return; // Not enough data to be a valid packet
|
||||
}
|
||||
|
||||
const MultiMoveItem_Struct* multi_move = reinterpret_cast<const MultiMoveItem_Struct*>(app->pBuffer);
|
||||
if (app->size != sizeof(MultiMoveItem_Struct) + sizeof(MultiMoveItemSub_Struct) * multi_move->count) {
|
||||
LinkDead();
|
||||
return; // Packet size does not match expected size
|
||||
}
|
||||
|
||||
const int16 from_parent = multi_move->moves[0].from_slot.Slot;
|
||||
const int16 to_parent = multi_move->moves[0].to_slot.Slot;
|
||||
|
||||
// CTRL + left click drops an item into a bag without opening it.
|
||||
// This can be a bag, in which case it tries to fill the target bag with the contents of the bag on the cursor
|
||||
// CTRL + right click swaps the contents of two bags if a bag is on your cursor and you ctrl-right click on another bag.
|
||||
|
||||
// We need to check if this is a swap or just an addition (left click or right click)
|
||||
// Check if any component of this transaction is coming from anywhere other than the cursor
|
||||
bool left_click = true;
|
||||
for (int i = 0; i < multi_move->count; i++) {
|
||||
if (multi_move->moves[i].from_slot.Slot != EQ::invslot::slotCursor) {
|
||||
left_click = false;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a left click which is purely additive. This should always be cursor object or cursor bag contents into general\bank\whatever bag
|
||||
if (left_click) {
|
||||
for (int i = 0; i < multi_move->count; i++) {
|
||||
MoveItem_Struct* mi = new MoveItem_Struct();
|
||||
mi->from_slot = multi_move->moves[i].from_slot.SubIndex == -1 ? multi_move->moves[i].from_slot.Slot : m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot, multi_move->moves[i].from_slot.SubIndex);
|
||||
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot, multi_move->moves[i].to_slot.SubIndex);
|
||||
|
||||
if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||
} else if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||
}
|
||||
|
||||
// This sends '1' as the stack count for unstackable items, which our titanium-era SwapItem blows up
|
||||
if (m_inv.GetItem(mi->from_slot)->IsStackable()) {
|
||||
mi->number_in_stack = multi_move->moves[i].number_in_stack;
|
||||
} else {
|
||||
mi->number_in_stack = 0;
|
||||
}
|
||||
|
||||
if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) {
|
||||
bool error = false;
|
||||
SwapItemResync(mi);
|
||||
InterrogateInventory(this, false, true, false, error, false);
|
||||
if (error) {
|
||||
InterrogateInventory(this, true, false, true, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is the swap.
|
||||
// Client behavior is just to move stacks without combining them
|
||||
// Items get rearranged to fill the 'top' of the bag first
|
||||
} else {
|
||||
struct MoveInfo {
|
||||
EQ::ItemInstance* item;
|
||||
uint16 to_slot;
|
||||
};
|
||||
|
||||
std::vector<MoveInfo> items;
|
||||
items.reserve(multi_move->count);
|
||||
|
||||
for (int i = 0; i < multi_move->count; i++) {
|
||||
// These are always bags, so we don't need to worry about raw items in slotCursor
|
||||
uint16 from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot, multi_move->moves[i].from_slot.SubIndex);
|
||||
if (multi_move->moves[i].from_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||
from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].from_slot.SubIndex);
|
||||
} else if (multi_move->moves[i].from_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||
from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].from_slot.SubIndex);
|
||||
}
|
||||
|
||||
uint16 to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot, multi_move->moves[i].to_slot.SubIndex);
|
||||
if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||
to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||
} else if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||
to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||
}
|
||||
|
||||
// I wasn't able to produce any error states on purpose.
|
||||
MoveInfo move{
|
||||
.item = m_inv.PopItem(from_slot), // Don't delete the instance here
|
||||
.to_slot = to_slot
|
||||
};
|
||||
|
||||
if (move.item) {
|
||||
items.push_back(move);
|
||||
database.SaveInventory(CharacterID(), NULL, from_slot); // We have to manually save inventory here.
|
||||
} else {
|
||||
LinkDead();
|
||||
return; // Prevent inventory desync here. Forcing a resync would be better, but we don't have a MoveItem struct to work with.
|
||||
}
|
||||
}
|
||||
|
||||
for (const MoveInfo& move : items) {
|
||||
PutItemInInventory(move.to_slot, *move.item); // This saves inventory too
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
LinkDead(); // This packet should not be sent by an older client
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user