mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-14 19:51: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*/
|
/*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
|
// 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
|
// 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)
|
// 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)
|
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)
|
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user