diff --git a/common/data_verification.h b/common/data_verification.h index 9da85a579..e0cb055c2 100644 --- a/common/data_verification.h +++ b/common/data_verification.h @@ -38,8 +38,8 @@ T ClampUpper(const T& value, const T& upper) { return std::min(value, upper); } -template -bool ValueWithin(const T& value, const T& lower, const T& upper) { +template +bool ValueWithin(const T& value, const U& lower, const V& upper) { return value >= lower && value <= upper; } diff --git a/common/inventory.cpp b/common/inventory.cpp index 9cd897bed..ecdf35934 100644 --- a/common/inventory.cpp +++ b/common/inventory.cpp @@ -19,8 +19,82 @@ #include "inventory.h" #include "data_verification.h" #include "item_container_personal_serialization.h" +#include "string_util.h" #include +bool EQEmu::InventorySlot::IsValid() const { + if(type_ == InvTypePersonal && EQEmu::ValueWithin(slot_, PersonalSlotCharm, PersonalSlotCursor)) { + return true; + } + + if(type_ == InvTypeBank && EQEmu::ValueWithin(slot_, 0, 23)) { + return true; + } + + if(type_ == InvTypeSharedBank && EQEmu::ValueWithin(slot_, 0, 1)) { + return true; + } + + if(type_ == InvTypeTribute && EQEmu::ValueWithin(slot_, 0, 4)) { + return true; + } + + if(type_ == InvTypeTrade && EQEmu::ValueWithin(slot_, 0, 7)) { + return true; + } + + if(type_ == InvTypeWorld && EQEmu::ValueWithin(slot_, 0, 255)) { + return true; + } + + + return false; +} + +bool EQEmu::InventorySlot::IsBank() const { + if(type_ == InvTypeBank && EQEmu::ValueWithin(slot_, 0, 23)) { + return true; + } + + if(type_ == InvTypeSharedBank && EQEmu::ValueWithin(slot_, 0, 1)) { + return true; + } + + return false; +} + +bool EQEmu::InventorySlot::IsCursor() const { + if(type_ == InvTypePersonal && slot_ == PersonalSlotCursor) { + return true; + } + + if(type_ == InvTypeCursorBuffer) { + return true; + } + + return false; +} + +bool EQEmu::InventorySlot::IsEquipment() const { + if(type_ == InvTypePersonal && EQEmu::ValueWithin(slot_, PersonalSlotCharm, PersonalSlotAmmo)) { + return true; + } + + return false; +} + +bool EQEmu::InventorySlot::IsGeneral() const { + if(type_ == InvTypePersonal && EQEmu::ValueWithin(slot_, PersonalSlotGeneral1, PersonalSlotGeneral10)) { + return true; + } + + return false; +} + +const std::string EQEmu::InventorySlot::ToString() const { + return StringFormat("(%i, %i, %i, %i)", type_, slot_, bag_index_, aug_index_); +} + struct EQEmu::Inventory::impl { std::map containers_; @@ -35,15 +109,15 @@ EQEmu::Inventory::~Inventory() { } std::shared_ptr EQEmu::Inventory::Get(const InventorySlot &slot) { - auto iter = impl_->containers_.find(slot.type_); + auto iter = impl_->containers_.find(slot.Type()); if(iter != impl_->containers_.end()) { - auto item = iter->second.Get(slot.slot_); + auto item = iter->second.Get(slot.Slot()); if(item) { - if(slot.bag_index_ > -1) { - auto sub_item = item->Get(slot.bag_index_); + if(slot.BagIndex() > -1) { + auto sub_item = item->Get(slot.BagIndex()); if(sub_item) { - if(slot.aug_index_ > -1) { - return sub_item->Get(slot.aug_index_); + if(slot.AugIndex() > -1) { + return sub_item->Get(slot.AugIndex()); } else { return sub_item; } @@ -58,42 +132,42 @@ std::shared_ptr EQEmu::Inventory::Get(const InventorySlot & } bool EQEmu::Inventory::Put(const InventorySlot &slot, std::shared_ptr inst) { - if(impl_->containers_.count(slot.type_) == 0) { - if(slot.type_ == 0) { - impl_->containers_.insert(std::pair(slot.type_, ItemContainer(new ItemContainerPersonalSerialization()))); + if(impl_->containers_.count(slot.Type()) == 0) { + if(slot.Type() == 0) { + impl_->containers_.insert(std::pair(slot.Type(), ItemContainer(new ItemContainerPersonalSerialization()))); } else { - impl_->containers_.insert(std::pair(slot.type_, ItemContainer())); + impl_->containers_.insert(std::pair(slot.Type(), ItemContainer())); } } //Verify item can be put into the slot requested - auto &container = impl_->containers_[slot.type_]; - if(slot.bag_index_ > -1) { - auto item = container.Get(slot.slot_); + auto &container = impl_->containers_[slot.Type()]; + if(slot.BagIndex() > -1) { + auto item = container.Get(slot.Slot()); if(!item) return false; - if(slot.aug_index_ > -1) { - auto bag_item = item->Get(slot.bag_index_); + if(slot.AugIndex() > -1) { + auto bag_item = item->Get(slot.BagIndex()); if(!bag_item) { return false; } - return bag_item->Put(slot.aug_index_, inst); + return bag_item->Put(slot.AugIndex(), inst); } else { - return item->Put(slot.bag_index_, inst); + return item->Put(slot.BagIndex(), inst); } } else { - if(slot.aug_index_ > -1) { - auto item = container.Get(slot.slot_); + if(slot.AugIndex() > -1) { + auto item = container.Get(slot.Slot()); if(!item) return false; - return item->Put(slot.aug_index_, inst); + return item->Put(slot.AugIndex(), inst); } - return container.Put(slot.slot_, inst); + return container.Put(slot.Slot(), inst); } return false; @@ -104,10 +178,10 @@ bool EQEmu::Inventory::Swap(const InventorySlot &src, const InventorySlot &dest, } int EQEmu::Inventory::CalcMaterialFromSlot(const InventorySlot &slot) { - if(slot.type_ != 0) + if(slot.Type() != 0) return _MaterialInvalid; - switch(slot.slot_) { + switch(slot.Slot()) { case PersonalSlotHead: return MaterialHead; case PersonalSlotChest: diff --git a/common/inventory.h b/common/inventory.h index 15196e622..b50bc7b0a 100644 --- a/common/inventory.h +++ b/common/inventory.h @@ -20,24 +20,10 @@ #define COMMON_INVENTORY_H #include "item_container.h" +#include namespace EQEmu { - struct InventorySlot - { - InventorySlot(int type, int slot) - : type_(type), slot_(slot), bag_index_(-1), aug_index_(-1) { } - InventorySlot(int type, int slot, int bag_index) - : type_(type), slot_(slot), bag_index_(bag_index), aug_index_(-1) { } - InventorySlot(int type, int slot, int bag_index, int aug_index) - : type_(type), slot_(slot), bag_index_(bag_index), aug_index_(aug_index) { } - - int type_; - int slot_; - int bag_index_; - int aug_index_; - }; - enum InventoryType : int { InvTypePersonal = 0, @@ -89,6 +75,48 @@ namespace EQEmu PersonalSlotCursor }; + class InventorySlot + { + public: + InventorySlot() : type_(-1), slot_(-1), bag_index_(-1), aug_index_(-1) { } + InventorySlot(int type, int slot) + : type_(type), slot_(slot), bag_index_(-1), aug_index_(-1) { } + InventorySlot(int type, int slot, int bag_index) + : type_(type), slot_(slot), bag_index_(bag_index), aug_index_(-1) { } + InventorySlot(int type, int slot, int bag_index, int aug_index) + : type_(type), slot_(slot), bag_index_(bag_index), aug_index_(aug_index) { } + + bool IsValid() const; + bool IsBank() const; + bool IsCursor() const; + bool IsEquipment() const; + bool IsGeneral() const; + + const std::string ToString() const; + + inline int Type() { return type_; } + inline int Type() const { return type_; } + inline int Slot() { return slot_; } + inline int Slot() const { return slot_; } + inline int BagIndex() { return bag_index_; } + inline int BagIndex() const { return bag_index_; } + inline int AugIndex() { return aug_index_; } + inline int AugIndex() const { return aug_index_; } + + private: + int type_; + int slot_; + int bag_index_; + int aug_index_; + }; + + inline bool operator==(const InventorySlot &lhs, const InventorySlot &rhs) { + return lhs.Type() == rhs.Type() && + lhs.Slot() == rhs.Slot() && + lhs.BagIndex() == rhs.BagIndex() && + lhs.AugIndex() == rhs.AugIndex(); } + inline bool operator!=(const InventorySlot &lhs, const InventorySlot &rhs) { return !(lhs == rhs); } + class Inventory { public: diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 4b442dfc2..b97edbd54 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -124,14 +124,14 @@ bool EQEmu::ItemInstance::Put(const int index, std::shared_ptr ins auto *item = impl_->base_item_; if(item->ItemClass == ItemClassContainer) { // Bag - if(!EQEmu::ValueWithin(index, 0, (int)item->BagSlots)) { + if(!EQEmu::ValueWithin(index, 0, item->BagSlots)) { return false; } return impl_->contents_.Put(index, inst); } else if(item->ItemClass == ItemClassCommon) { // Augment - if(!EQEmu::ValueWithin(index, 0, (int)EmuConstants::ITEM_COMMON_SIZE)) { + if(!EQEmu::ValueWithin(index, 0, EmuConstants::ITEM_COMMON_SIZE)) { return false; } diff --git a/zone/client.cpp b/zone/client.cpp index c3e2b1729..db29aa107 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2167,7 +2167,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ Log.Out(Logs::General, Logs::None, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); } -void Client::EVENT_ITEM_ScriptStopReturn(){ +void Client::ItemScriptStopReturn(){ /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items This will stopgap players from items being returned if global_npc.pl has a catch all return_items */ @@ -2175,11 +2175,11 @@ void Client::EVENT_ITEM_ScriptStopReturn(){ char buffer[50]; gettimeofday(&read_time, 0); sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); + SetEntityVariable("Stop_Return", buffer); } void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ - this->EVENT_ITEM_ScriptStopReturn(); + ItemScriptStopReturn(); int32 new_value = m_pp.platinum + platinum; if(new_value >= 0 && new_value > m_pp.platinum) diff --git a/zone/client.h b/zone/client.h index 3d4e1c8d0..f4aa36412 100644 --- a/zone/client.h +++ b/zone/client.h @@ -800,7 +800,7 @@ public: int32 acmod(); // Item methods - void EVENT_ITEM_ScriptStopReturn(); + void ItemScriptStopReturn(); uint32 NukeItem(uint32 itemnum, uint8 where_to_check = (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); void SetTint(int16 slot_id, uint32 color); @@ -823,6 +823,10 @@ public: void IncStats(uint8 type,int16 increase_val); void DropItem(int16 slot_id); + //New Inventory + bool SwapItem(const EQEmu::InventorySlot &src, const EQEmu::InventorySlot &dest, int number_in_stack); + bool CanEquipItem(std::shared_ptr inst, const EQEmu::InventorySlot &slot); + // // class Client::TextLink // diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 10f729aa2..4304fc3c4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9593,70 +9593,11 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) } MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - auto res = m_inventory.Swap(EQEmu::InventorySlot(mi->from_type, mi->from_slot, mi->from_bag_slot, mi->from_aug_slot), - EQEmu::InventorySlot(mi->to_type, mi->to_slot, mi->to_bag_slot, mi->to_aug_slot), - mi->number_in_stack); - - //printf("%i %i %i %i --> %i %i %i %i (%u)\n", - // mi->from_type, mi->from_slot, mi->from_bag_slot, mi->from_aug_slot, - // mi->to_type, mi->to_slot, mi->to_bag_slot, mi->to_aug_slot, - // mi->number_in_stack); - - //if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - //{ - // if (mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - // { - // char *detect = nullptr; - // const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); - // const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); - // MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", - // mi->from_slot, - // itm_from ? itm_from->GetID() : 0, - // mi->to_slot, - // itm_to ? itm_to->GetID() : 0, - // casting_spell_id); - // database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); - // safe_delete_array(detect); - // Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots - // return; - // } - //} - // - //// Illegal bagslot usage checks. Currently, user only receives a message if this check is triggered. - //bool mi_hack = false; - // - //if (mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { - // if (mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - // else { - // int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - // if (!m_inv[from_parent]) { mi_hack = true; } - // else if (!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - // else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - // } - //} - // - //if (mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { - // if (mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - // else { - // int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - // if (!m_inv[to_parent]) { mi_hack = true; } - // else if (!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - // else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - // } - //} - // - //if (mi_hack) { Message(15, "Caution: Illegal use of inaccessible bag slots!"); } - // - //if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { - // SwapItemResync(mi); - // - // bool error = false; - // InterrogateInventory(this, false, true, false, error, false); - // if (error) - // InterrogateInventory(this, true, false, true, error); - //} - // - //return; + EQEmu::InventorySlot src(mi->from_type, mi->from_slot, mi->from_bag_slot, mi->from_aug_slot); + EQEmu::InventorySlot dest(mi->to_type, mi->to_slot, mi->to_bag_slot, mi->to_aug_slot); + if(!SwapItem(src, dest, mi->number_in_stack)) { + //Send Resync Here + } } void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) diff --git a/zone/exp.cpp b/zone/exp.cpp index 7e878206a..2aa60ce92 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -61,7 +61,7 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level) void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { - this->EVENT_ITEM_ScriptStopReturn(); + ItemScriptStopReturn(); uint32 add_exp = in_add_exp; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 59901fd85..207124e9e 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -18,8 +18,8 @@ #include "../common/global_define.h" #include "../common/eqemu_logsys.h" - #include "../common/string_util.h" +#include "../common/data_verification.h" #include "quest_parser_collection.h" #include "worldserver.h" #include "zonedb.h" @@ -191,7 +191,7 @@ bool Client::CheckLoreConflict(const ItemData* item) } bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, uint32 aug6, bool attuned, uint16 to_slot, uint32 ornament_icon, uint32 ornament_idfile, uint32 ornament_hero_model) { - this->EVENT_ITEM_ScriptStopReturn(); + ItemScriptStopReturn(); // TODO: update calling methods and script apis to handle a failure return @@ -3092,3 +3092,74 @@ std::string InventoryOld::GetCustomItemData(int16 slot_id, std::string identifie } return ""; } + +//New Inventory +bool Client::SwapItem(const EQEmu::InventorySlot &src, const EQEmu::InventorySlot &dest, int number_in_stack) { + + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) + { + if(src != dest && !src.IsCursor() && src.IsValid() && dest.IsValid()) { + auto i_src = m_inventory.Get(src); + auto i_dest = m_inventory.Get(dest); + std::string detect = StringFormat("Player issued a move item from %s (item id %u) to %s (item id %u) while casting %u.", + src.ToString().c_str(), + i_src ? i_src->GetItem()->ID : 0, + dest.ToString().c_str(), + i_dest ? i_dest->GetItem()->ID : 0, + casting_spell_id); + database.SetMQDetectionFlag(AccountName(), GetName(), detect.c_str(), zone->GetShortName()); + Kick(); + return false; + } + } + + auto i_src = m_inventory.Get(src); + auto i_dest = m_inventory.Get(dest); + + if(dest.IsEquipment() && !CanEquipItem(i_dest, dest)) { + return false; + } + + printf("Equip check passes %s -> %s\n", src.ToString().c_str(), dest.ToString().c_str()); + + bool res = m_inventory.Swap(src, dest, number_in_stack); + + return true; +} + +bool Client::CanEquipItem(std::shared_ptr inst, const EQEmu::InventorySlot &slot) { + if(!inst) { + return false; + } + + if(slot.Type() != 0) { + return false; + } + + if(!EQEmu::ValueWithin(slot.Slot(), EQEmu::PersonalSlotCharm, EQEmu::PersonalSlotAmmo)) { + return false; + } + + auto item = inst->GetItem(); + //check slot + + int use_slot = -1; + if(slot.Slot() == EQEmu::PersonalSlotPowerSource) { + use_slot = EQEmu::PersonalSlotAmmo; + } + else if(slot.Slot() == EQEmu::PersonalSlotAmmo) { + use_slot = EQEmu::PersonalSlotPowerSource; + } else { + use_slot = slot.Slot(); + } + + if(!(item->Slots & (1 << use_slot))) { + return false; + } + + if(!item->IsEquipable(GetBaseRace(), GetBaseClass())) { + return false; + } + + return true; +} diff --git a/zone/trading.cpp b/zone/trading.cpp index 0bcd3da41..f1e7bdc1f 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -923,7 +923,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if(!tradingWith->IsMoving()) tradingWith->FaceTarget(this); - this->EVENT_ITEM_ScriptStopReturn(); + ItemScriptStopReturn(); } }