Early stages of swapping requirements in, should check for basic validity and equipable status

This commit is contained in:
KimLS 2015-02-26 22:09:29 -08:00
parent 568938d003
commit 18b4d068ea
10 changed files with 232 additions and 114 deletions

View File

@ -38,8 +38,8 @@ T ClampUpper(const T& value, const T& upper) {
return std::min(value, upper);
}
template <typename T>
bool ValueWithin(const T& value, const T& lower, const T& upper) {
template <typename T, typename U, typename V>
bool ValueWithin(const T& value, const U& lower, const V& upper) {
return value >= lower && value <= upper;
}

View File

@ -19,8 +19,82 @@
#include "inventory.h"
#include "data_verification.h"
#include "item_container_personal_serialization.h"
#include "string_util.h"
#include <map>
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<int, ItemContainer> containers_;
@ -35,15 +109,15 @@ EQEmu::Inventory::~Inventory() {
}
std::shared_ptr<EQEmu::ItemInstance> 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::ItemInstance> EQEmu::Inventory::Get(const InventorySlot &
}
bool EQEmu::Inventory::Put(const InventorySlot &slot, std::shared_ptr<ItemInstance> inst) {
if(impl_->containers_.count(slot.type_) == 0) {
if(slot.type_ == 0) {
impl_->containers_.insert(std::pair<int, ItemContainer>(slot.type_, ItemContainer(new ItemContainerPersonalSerialization())));
if(impl_->containers_.count(slot.Type()) == 0) {
if(slot.Type() == 0) {
impl_->containers_.insert(std::pair<int, ItemContainer>(slot.Type(), ItemContainer(new ItemContainerPersonalSerialization())));
} else {
impl_->containers_.insert(std::pair<int, ItemContainer>(slot.type_, ItemContainer()));
impl_->containers_.insert(std::pair<int, ItemContainer>(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:

View File

@ -20,24 +20,10 @@
#define COMMON_INVENTORY_H
#include "item_container.h"
#include <string>
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:

View File

@ -124,14 +124,14 @@ bool EQEmu::ItemInstance::Put(const int index, std::shared_ptr<ItemInstance> 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;
}

View File

@ -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)

View File

@ -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<EQEmu::ItemInstance> inst, const EQEmu::InventorySlot &slot);
//
// class Client::TextLink
//

View File

@ -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)

View File

@ -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;

View File

@ -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<EQEmu::ItemInstance> 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;
}

View File

@ -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();
}
}