From 20cbe4af44f6239c9eb31df3dec1a729d9a75bc3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 28 Feb 2015 17:56:01 -0800 Subject: [PATCH] More work on swapping, almost there just need to write code for stack split/move/combining --- common/inventory.cpp | 99 ++++++++++++++++++++++++++++++++++++++- common/inventory.h | 8 +++- common/item_container.cpp | 14 +++--- common/item_instance.cpp | 22 +++++++-- common/item_instance.h | 4 ++ tests/inventory_test.h | 70 +++++++++++++++++++++++++-- zone/inventory.cpp | 43 +++++++++++++---- zone/mob.cpp | 2 +- 8 files changed, 236 insertions(+), 26 deletions(-) diff --git a/common/inventory.cpp b/common/inventory.cpp index 878e9f450..f2b418ec7 100644 --- a/common/inventory.cpp +++ b/common/inventory.cpp @@ -51,6 +51,10 @@ bool EQEmu::InventorySlot::IsValid() const { return false; } +bool EQEmu::InventorySlot::IsDelete() const { + return type_ == -1 && slot_ == -1 && bag_index_ == -1 && aug_index_ == -1; +} + bool EQEmu::InventorySlot::IsBank() const { if(type_ == InvTypeBank && EQEmu::ValueWithin(slot_, 0, 23)) { return true; @@ -91,6 +95,24 @@ bool EQEmu::InventorySlot::IsGeneral() const { return false; } +bool EQEmu::InventorySlot::IsWeapon() const { + if(type_ == InvTypePersonal && + (EQEmu::ValueWithin(slot_, PersonalSlotPrimary, PersonalSlotSecondary) || slot_ == PersonalSlotRange)) + { + return true; + } + + return false; +} + +bool EQEmu::InventorySlot::IsTrade() const { + if(type_ == InvTypeTrade) { + return true; + } + + return false; +} + const std::string EQEmu::InventorySlot::ToString() const { return StringFormat("(%i, %i, %i, %i)", type_, slot_, bag_index_, aug_index_); } @@ -100,12 +122,14 @@ struct EQEmu::Inventory::impl std::map containers_; int race_; int class_; + int deity_; }; -EQEmu::Inventory::Inventory(int race, int class_) { +EQEmu::Inventory::Inventory(int race, int class_, int deity) { impl_ = new impl; impl_->race_ = race; impl_->class_ = class_; + impl_->deity_ = deity; } EQEmu::Inventory::~Inventory() { @@ -178,7 +202,45 @@ bool EQEmu::Inventory::Put(const InventorySlot &slot, std::shared_ptr %s (%i)\n", src.IsCursor() ? "Cursor" : src.ToString().c_str(), dest.IsCursor() ? "Cursor" : dest.ToString().c_str(), charges); + + if(src == dest) { + return true; + } + + if(dest.IsDelete()) { + //return Delete(src); + return false; + } + + if(!src.IsValid() || !dest.IsValid()) { + return false; + } + + auto i_src = Get(src); + auto i_dest = Get(dest); + + if(dest.IsEquipment() && !CanEquip(i_dest, dest)) { + return false; + } + + if(!i_src) { + return false; + } + + //Check this -> trade no drop + if(dest.IsTrade() && i_src->IsNoDrop()) { + return false; + } + + if(i_src->IsStackable()) { + //charges == 0 -> Move entire stack from src to dest + //charges > 0 -> Move charges number of charges from src to dest (may require creating a new item + } else { + return _swap(src, dest); + } + + return true; } int EQEmu::Inventory::CalcMaterialFromSlot(const InventorySlot &slot) { @@ -288,3 +350,36 @@ bool EQEmu::Inventory::Serialize(MemoryBuffer &buf) { return value; } + +bool EQEmu::Inventory::_swap(const InventorySlot &src, const InventorySlot &dest) { + auto src_i = Get(src); + auto dest_i = Get(dest); + + if(src_i) { + if(!_destroy(src)) { + return false; + } + } + + if(dest_i) { + if(!_destroy(dest)) { + return false; + } + + if(!Put(src, dest_i)) { + return false; + } + } + + if(src_i) { + if(!Put(dest, src_i)) { + return false; + } + } + + return true; +} + +bool EQEmu::Inventory::_destroy(const InventorySlot &slot) { + return Put(slot, std::shared_ptr(nullptr)); +} diff --git a/common/inventory.h b/common/inventory.h index c3606949d..fb55b1765 100644 --- a/common/inventory.h +++ b/common/inventory.h @@ -87,10 +87,13 @@ namespace EQEmu : type_(type), slot_(slot), bag_index_(bag_index), aug_index_(aug_index) { } bool IsValid() const; + bool IsDelete() const; bool IsBank() const; bool IsCursor() const; bool IsEquipment() const; bool IsGeneral() const; + bool IsWeapon() const; + bool IsTrade() const; const std::string ToString() const; @@ -120,7 +123,7 @@ namespace EQEmu class Inventory { public: - Inventory(int race, int class_); + Inventory(int race, int class_, int deity); ~Inventory(); std::shared_ptr Get(const InventorySlot &slot); @@ -133,6 +136,9 @@ namespace EQEmu bool CanEquip(std::shared_ptr inst, const EQEmu::InventorySlot &slot); bool Serialize(MemoryBuffer &buf); private: + bool _swap(const InventorySlot &src, const InventorySlot &dest); + bool _destroy(const InventorySlot &slot); + struct impl; impl *impl_; }; diff --git a/common/item_container.cpp b/common/item_container.cpp index 6f708051a..f602a1f50 100644 --- a/common/item_container.cpp +++ b/common/item_container.cpp @@ -51,13 +51,15 @@ std::shared_ptr EQEmu::ItemContainer::Get(const int slot_id } bool EQEmu::ItemContainer::Put(const int slot_id, std::shared_ptr inst) { - if(!inst) - return false; - - auto iter = impl_->items_.find(slot_id); - if(iter == impl_->items_.end()) { - impl_->items_[slot_id] = inst; + if(!inst) { + impl_->items_.erase(slot_id); return true; + } else { + auto iter = impl_->items_.find(slot_id); + if(iter == impl_->items_.end()) { + impl_->items_[slot_id] = inst; + return true; + } } return false; diff --git a/common/item_instance.cpp b/common/item_instance.cpp index b97edbd54..d4880d199 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -105,6 +105,10 @@ const ItemData *EQEmu::ItemInstance::GetBaseItem() { return impl_->base_item_; } +const ItemData *EQEmu::ItemInstance::GetBaseItem() const { + return impl_->base_item_; +} + std::shared_ptr EQEmu::ItemInstance::Get(const int index) { if(EQEmu::ValueWithin(index, 0, 255)) { return impl_->contents_.Get(index); @@ -139,9 +143,13 @@ bool EQEmu::ItemInstance::Put(const int index, std::shared_ptr ins return false; } - auto *aug_item = inst->GetItem(); - int aug_type = aug_item->AugType; - if(aug_type == -1 || (1 << (item->AugSlotType[index] - 1)) & aug_type) { + if(inst) { + auto *aug_item = inst->GetItem(); + int aug_type = aug_item->AugType; + if(aug_type == -1 || (1 << (item->AugSlotType[index] - 1)) & aug_type) { + return impl_->contents_.Put(index, inst); + } + } else { return impl_->contents_.Put(index, inst); } @@ -323,6 +331,14 @@ bool EQEmu::ItemInstance::IsStackable() const { return impl_->base_item_->Stackable; } +bool EQEmu::ItemInstance::IsNoDrop() { + return GetAttuned() || GetBaseItem()->NoDrop == 0; +} + +bool EQEmu::ItemInstance::IsNoDrop() const { + return GetAttuned() || GetBaseItem()->NoDrop == 0; +} + EQEmu::ItemContainer *EQEmu::ItemInstance::GetContainer() { return &(impl_->contents_); } diff --git a/common/item_instance.h b/common/item_instance.h index 72bcc3c16..29f2394a2 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -35,6 +35,7 @@ namespace EQEmu const ItemData *GetItem(); const ItemData *GetBaseItem(); + const ItemData *GetBaseItem() const; //Container std::shared_ptr Get(const int index); @@ -95,6 +96,9 @@ namespace EQEmu bool IsStackable(); bool IsStackable() const; + bool IsNoDrop(); + bool IsNoDrop() const; + //Internal state //Used for low level operations such as encode/decode ItemContainer *GetContainer(); diff --git a/tests/inventory_test.h b/tests/inventory_test.h index 6cb34c6b3..f373e9cf0 100644 --- a/tests/inventory_test.h +++ b/tests/inventory_test.h @@ -26,7 +26,7 @@ class InventoryTest : public Test::Suite { typedef void(InventoryTest::*TestFunction)(void); public: - InventoryTest() : inv(1, 1) { + InventoryTest() : inv(1, 1, 1) { InitContainer(); InitArmor(); InitAugment(); @@ -34,6 +34,10 @@ public: InitInventory(); TEST_ADD(InventoryTest::InventoryVerifyInitialItemsTest); TEST_ADD(InventoryTest::InventoryCanEquipTest); + TEST_ADD(InventoryTest::InventorySwapGeneral1ToCursor); + TEST_ADD(InventoryTest::InventorySwapCursorToGeneral2); + TEST_ADD(InventoryTest::InventorySplitStackToCursor); + TEST_ADD(InventoryTest::InventoryStackCombine); } ~InventoryTest() { @@ -144,7 +148,7 @@ private: std::shared_ptr m_bag(new EQEmu::ItemInstance(&container)); std::shared_ptr m_armor(new EQEmu::ItemInstance(&armor)); std::shared_ptr m_augment(new EQEmu::ItemInstance(&augment)); - std::shared_ptr m_stackable(new EQEmu::ItemInstance(&stackable, 45)); + std::shared_ptr m_stackable(new EQEmu::ItemInstance(&stackable, 100)); inv.Put(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1), m_bag); inv.Put(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 0), m_armor); inv.Put(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 1), m_augment); @@ -202,10 +206,37 @@ private: armor.Races += 1; } - void InventorySwapItemsTest() - { + void InventorySwapGeneral1ToCursor() { auto swap_result = inv.Swap(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1), + EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor), 0); + + TEST_ASSERT(swap_result == true); + + auto m_bag = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor)); + TEST_ASSERT(m_bag); + TEST_ASSERT(m_bag->GetItem()); + TEST_ASSERT(m_bag->GetItem()->ID == 1000); + + auto m_armor = m_bag->Get(0); + TEST_ASSERT(m_armor); + TEST_ASSERT(m_armor->GetItem()); + TEST_ASSERT(m_armor->GetItem()->ID == 1001); + + auto m_augment = m_bag->Get(1); + TEST_ASSERT(m_augment); + TEST_ASSERT(m_augment->GetItem()); + TEST_ASSERT(m_augment->GetItem()->ID == 1002); + + auto m_stackable = m_bag->Get(7); + TEST_ASSERT(m_stackable); + TEST_ASSERT(m_stackable->GetItem()); + TEST_ASSERT(m_stackable->GetItem()->ID == 1003); + } + + void InventorySwapCursorToGeneral2() { + auto swap_result = inv.Swap(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor), EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral2), 0); + TEST_ASSERT(swap_result == true); auto m_bag = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral2)); @@ -229,6 +260,37 @@ private: TEST_ASSERT(m_stackable->GetItem()->ID == 1003); } + void InventorySplitStackToCursor() { + auto swap_result = inv.Swap(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 7), + EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor), 10); + + TEST_ASSERT(swap_result == true); + + auto m_stackable_cursor = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor)); + auto m_stackable = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 7)); + + TEST_ASSERT(m_stackable_cursor); + TEST_ASSERT(m_stackable); + + TEST_ASSERT(m_stackable_cursor->GetCharges() == 10); + TEST_ASSERT(m_stackable->GetCharges() == 90); + } + + void InventoryStackCombine() { + auto swap_result = inv.Swap(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor), + EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 7), 0); + + TEST_ASSERT(swap_result == true); + + auto m_cursor = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotCursor)); + auto m_stackable = inv.Get(EQEmu::InventorySlot(EQEmu::InvTypePersonal, EQEmu::PersonalSlotGeneral1, 7)); + + TEST_ASSERT(!m_cursor); + TEST_ASSERT(m_stackable); + + TEST_ASSERT(m_stackable->GetCharges() == 100); + } + EQEmu::Inventory inv; ItemData container; ItemData armor; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 55aa08620..143b3d18f 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -3113,17 +3113,42 @@ bool Client::SwapItem(const EQEmu::InventorySlot &src, const EQEmu::InventorySlo } } - auto i_src = m_inventory.Get(src); - auto i_dest = m_inventory.Get(dest); - - if(dest.IsEquipment() && !m_inventory.CanEquip(i_dest, dest)) { - return false; + bool recalc_weapon_speed = false; + if(src.IsWeapon() || dest.IsWeapon()) { + recalc_weapon_speed = true; } - - printf("Equip check passes %s -> %s\n", src.ToString().c_str(), dest.ToString().c_str()); - + + if(src.IsBank() || dest.IsBank()) { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + if(!banker || distance > USE_NPC_RANGE2) + { + std::string hacked = StringFormat("Player tried to make use of a banker(items) but %s is " + "non-existant or too far away (%u units).", + banker ? banker->GetName() : "UNKNOWN NPC", + distance); + database.SetMQDetectionFlag(AccountName(), GetName(), hacked.c_str(), zone->GetShortName()); + Kick(); + return false; + } + } + bool res = m_inventory.Swap(src, dest, number_in_stack); - return true; + if(res) { + printf("Swap success\n"); + } else { + printf("Swap failure!\n"); + } + + if(auto_attack && res && recalc_weapon_speed) { + SetAttackTimer(); + } + + if(res) { + CalcBonuses(); + } + + return res; } diff --git a/zone/mob.cpp b/zone/mob.cpp index d7a63f065..ab4233b4e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -103,7 +103,7 @@ Mob::Mob(const char* in_name, m_TargetV(glm::vec3()), flee_timer(FLEE_CHECK_TIMER), m_Position(position), - m_inventory(in_race, in_class) + m_inventory(in_race, in_class, in_deity) { targeted = 0; tar_ndx=0;