[9501] Implement item limit category inventory mode.

* Now expected item limit categories (for example for item 5513 and related)
  correctly limited by its amount in inventory.
* Provide and use additional arg in SendEquipError for alt. way get affected
  item prototype. This let send to function item id and prevent crash client at
  limit category equip errors that required item prototype data.
This commit is contained in:
VladimirMangos 2010-03-02 14:04:47 +03:00
parent 52ace6bcb7
commit c71f79584d
16 changed files with 137 additions and 56 deletions

View file

@ -203,6 +203,17 @@ uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const
return count;
}
uint32 Bag::GetItemCountWithLimitCategory(uint32 limitCategory) const
{
uint32 count = 0;
for(uint32 i = 0; i < GetBagSize(); ++i)
if (m_bagslot[i])
if (m_bagslot[i]->GetProto()->ItemLimitCategory == limitCategory )
count += m_bagslot[i]->GetCount();
return count;
}
uint8 Bag::GetSlotByItemGUID(uint64 guid) const
{
for(uint32 i = 0; i < GetBagSize(); ++i)

View file

@ -43,6 +43,7 @@ class Bag : public Item
Item* GetItemByPos( uint8 slot ) const;
uint32 GetItemCount( uint32 item, Item* eItem = NULL ) const;
uint32 GetItemCountWithLimitCategory(uint32 limitCategory) const;
uint8 GetSlotByItemGUID(uint64 guid) const;
bool IsEmpty() const;

View file

@ -323,6 +323,12 @@ enum ItemEnchantmentType
ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET = 8
};
enum ItemLimitCategoryMode
{
ITEM_LIMIT_CATEGORY_MODE_HAVE = 0, // limit applied to amount items in inventory/bank
ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1, // limit applied to amount equipped items (including used gems)
};
enum TotemCategoryType
{
TOTEM_CATEGORY_TYPE_KNIFE = 1,

View file

@ -1020,7 +1020,7 @@ struct ItemLimitCategoryEntry
//char* name[16] // 1-16 m_name_lang
// 17 name flags
uint32 maxCount; // 18, max allowed equipped as item or in gem slot
//uint32 unk; // 19, 1 for gems only...
uint32 mode; // 19, 0 = have, 1 = equip (enum ItemLimitCategoryMode)
};
struct ItemRandomPropertiesEntry

View file

@ -66,7 +66,7 @@ const char ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx";
//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx";
//const char ItemCondExtCostsEntryfmt[]="xiii";
const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix";
const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxix";
const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
const char ItemRandomPropertiesfmt[]="nxiiiiissssssssssssssssx";
const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiiiiiiiii";
const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii";

View file

@ -796,7 +796,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
else
{
item->is_blocked = false;
player->SendEquipError( msg, NULL, NULL );
player->SendEquipError( msg, NULL, NULL, roll->itemid );
}
}
}
@ -848,7 +848,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
else
{
item->is_blocked = false;
player->SendEquipError( msg, NULL, NULL );
player->SendEquipError( msg, NULL, NULL, roll->itemid );
}
}
else if(rollvote == DISENCHANT)

View file

@ -1230,6 +1230,8 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
{
if(ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory))
{
// NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
{
if (Gems[j])

View file

@ -2262,7 +2262,7 @@ bool ChatHandler::HandleAddItemSetCommand(const char* args)
}
else
{
pl->SendEquipError( msg, NULL, NULL );
pl->SendEquipError( msg, NULL, NULL, pProto->ItemId );
PSendSysMessage(LANG_ITEM_CANNOT_CREATE, pProto->ItemId, 1);
}
}

View file

@ -154,7 +154,7 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
}
else
player->SendEquipError( msg, NULL, NULL );
player->SendEquipError( msg, NULL, NULL, item->itemid );
}
void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
@ -496,8 +496,10 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data )
uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count );
if ( msg != EQUIP_ERR_OK )
{
target->SendEquipError( msg, NULL, NULL );
_player->SendEquipError( msg, NULL, NULL ); // send duplicate of error massage to master looter
target->SendEquipError( msg, NULL, NULL, item.itemid );
// send duplicate of error massage to master looter
_player->SendEquipError( msg, NULL, NULL, item.itemid );
return;
}

View file

@ -7242,10 +7242,10 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
uint32 learning_spell_id = proto->Spells[1].SpellId;
SpellEntry const *spellInfo = sSpellStore.LookupEntry(learn_spell_id);
if(!spellInfo)
if (!spellInfo)
{
sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, learn_spell_id);
SendEquipError(EQUIP_ERR_NONE,item,NULL);
SendEquipError(EQUIP_ERR_NONE, item);
return;
}
@ -8593,6 +8593,35 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons
return count;
}
uint32 Player::GetItemCountWithLimitCategory( uint32 limitCategory ) const
{
uint32 count = 0;
for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetProto()->ItemLimitCategory == limitCategory)
count += pItem->GetCount();
for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetProto()->ItemLimitCategory == limitCategory)
count += pItem->GetCount();
for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
count += pBag->GetItemCountWithLimitCategory(limitCategory);
for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetProto()->ItemLimitCategory == limitCategory)
count += pItem->GetCount();
for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
count += pBag->GetItemCountWithLimitCategory(limitCategory);
return count;
}
Item* Player::GetItemByGuid( uint64 guid ) const
{
for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
@ -8981,16 +9010,40 @@ uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem,
}
// no maximum
if(pProto->MaxCount <= 0)
return EQUIP_ERR_OK;
uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
if (curcount + count > uint32(pProto->MaxCount))
if(pProto->MaxCount > 0)
{
if(no_space_count)
*no_space_count = count +curcount - pProto->MaxCount;
return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
if (curcount + count > uint32(pProto->MaxCount))
{
if(no_space_count)
*no_space_count = count +curcount - pProto->MaxCount;
return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
}
}
// check unique-equipped limit
if (pProto->ItemLimitCategory)
{
ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory);
if (!limitEntry)
{
if(no_space_count)
*no_space_count = count;
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
}
if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE)
{
uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory);
if (curcount + count > uint32(limitEntry->maxCount))
{
if(no_space_count)
*no_space_count = count + curcount - limitEntry->maxCount;
return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS;
}
}
}
return EQUIP_ERR_OK;
@ -10429,12 +10482,12 @@ void Player::SetAmmo( uint32 item )
return;
// check ammo
if(item)
if (item)
{
uint8 msg = CanUseAmmo( item );
if( msg != EQUIP_ERR_OK )
if (msg != EQUIP_ERR_OK)
{
SendEquipError( msg, NULL, NULL );
SendEquipError(msg, NULL, NULL, item);
return;
}
}
@ -10450,7 +10503,7 @@ void Player::RemoveAmmo()
m_ammoDPS = 0.0f;
if(CanModifyStats())
if (CanModifyStats())
UpdateDamagePhysical(RANGED_ATTACK);
}
@ -11674,7 +11727,7 @@ void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
}
}
void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2 )
void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2, uint32 itemid /*= 0*/ )
{
sLog.outDebug( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg);
WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, 1+8+8+1);
@ -11690,25 +11743,28 @@ void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2 )
{
case EQUIP_ERR_CANT_EQUIP_LEVEL_I:
case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW:
{
ItemPrototype const* proto = pItem ? pItem->GetProto() : NULL;
data << uint32(proto ? proto->RequiredLevel : 0);
} break;
{
ItemPrototype const* proto = pItem ? pItem->GetProto() : sObjectMgr.GetItemPrototype(itemid);
data << uint32(proto ? proto->RequiredLevel : 0);
break;
}
case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one...
{
data << uint64(0);
data << uint32(0);
data << uint64(0);
} break;
{
data << uint64(0);
data << uint32(0);
data << uint64(0);
break;
}
case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS:
case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS:
case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS:
{
ItemPrototype const* proto = pItem ? pItem->GetProto() : NULL;
data << uint32(proto ? proto->ItemLimitCategory : 0);
} break;
default:
break;
{
ItemPrototype const* proto = pItem ? pItem->GetProto() : sObjectMgr.GetItemPrototype(itemid);
data << uint32(proto ? proto->ItemLimitCategory : 0);
break;
}
default:
break;
}
}
GetSession()->SendPacket(&data);
@ -12888,7 +12944,7 @@ bool Player::CanAddQuest( Quest const *pQuest, bool msg )
return true;
else if( msg2 != EQUIP_ERR_OK )
{
SendEquipError( msg2, NULL, NULL );
SendEquipError(msg2, NULL, NULL, srcitem);
return false;
}
}
@ -13000,7 +13056,7 @@ bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i] )
{
if(msg)
SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, pQuest->ReqItemId[i]);
return false;
}
}
@ -13027,7 +13083,7 @@ bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] );
if( res != EQUIP_ERR_OK )
{
SendEquipError( res, NULL, NULL );
SendEquipError( res, NULL, NULL, pQuest->RewChoiceItemId[reward] );
return false;
}
}
@ -13725,7 +13781,7 @@ bool Player::GiveQuestSourceItem( Quest const *pQuest )
else if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
return true;
else
SendEquipError( msg, NULL, NULL );
SendEquipError( msg, NULL, NULL, srcitem );
return false;
}
@ -13750,7 +13806,7 @@ bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
if(res != EQUIP_ERR_OK)
{
if(msg)
SendEquipError( res, NULL, NULL );
SendEquipError( res, NULL, NULL, srcitem );
return false;
}
@ -18056,7 +18112,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count );
if (msg != EQUIP_ERR_OK)
{
SendEquipError( msg, NULL, NULL );
SendEquipError( msg, NULL, NULL, item );
return false;
}
@ -18101,7 +18157,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
uint8 msg = CanEquipNewItem( slot, dest, item, false );
if (msg != EQUIP_ERR_OK)
{
SendEquipError( msg, NULL, NULL );
SendEquipError( msg, NULL, NULL, item );
return false;
}
@ -20472,7 +20528,7 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons
msg = CanStoreNewItem( NULL_BAG, NULL_SLOT,dest,lootItem->itemid,lootItem->count);
if(msg != EQUIP_ERR_OK)
{
SendEquipError( msg, NULL, NULL );
SendEquipError( msg, NULL, NULL, lootItem->itemid );
continue;
}
@ -20689,14 +20745,16 @@ uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_s
{
ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory);
if(!limitEntry)
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
// NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
if(limit_count > limitEntry->maxCount)
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; // attempt add too many limit category items (gems)
return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
// there is an equip limit on this item
if(HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,limitEntry->maxCount-limit_count+1,except_slot))
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
}
return EQUIP_ERR_OK;

View file

@ -1148,6 +1148,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void SetSheath( SheathState sheathed ); // overwrite Unit version
uint8 FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const;
uint32 GetItemCount( uint32 item, bool inBankAlso = false, Item* skipItem = NULL ) const;
uint32 GetItemCountWithLimitCategory(uint32 limitCategory) const;
Item* GetItemByGuid( uint64 guid ) const;
Item* GetItemByPos( uint16 pos ) const;
Item* GetItemByPos( uint8 bag, uint8 slot ) const;
@ -1243,7 +1244,7 @@ class MANGOS_DLL_SPEC Player : public Unit
Item* GetItemFromBuyBackSlot( uint32 slot );
void RemoveItemFromBuyBackSlot( uint32 slot, bool del );
uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; }
void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 );
void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 = NULL, uint32 itemid = 0 );
void SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param );
void SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param );
void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; }

View file

@ -5616,7 +5616,7 @@ SpellCastResult Spell::CheckItems()
uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 );
if (msg != EQUIP_ERR_OK )
{
p_caster->SendEquipError( msg, NULL, NULL );
p_caster->SendEquipError( msg, NULL, NULL, m_spellInfo->EffectItemType[i] );
return SPELL_FAILED_DONT_REPORT;
}
}

View file

@ -3485,7 +3485,7 @@ void Aura::HandleChannelDeathItem(bool apply, bool Real)
if( msg != EQUIP_ERR_OK )
{
count-=noSpaceForCount;
((Player*)caster)->SendEquipError( msg, NULL, NULL );
((Player*)caster)->SendEquipError( msg, NULL, NULL, spellInfo->EffectItemType[m_effIndex] );
if (count==0)
return;
}

View file

@ -3192,7 +3192,7 @@ void Spell::DoCreateItem(SpellEffectIndex eff_idx, uint32 itemtype)
else
{
// if not created by another reason from full inventory or unique items amount limitation
player->SendEquipError( msg, NULL, NULL );
player->SendEquipError( msg, NULL, NULL, newitemid );
return;
}
}

View file

@ -98,7 +98,7 @@ bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args)
return false;
uint8 msg = atoi(args);
m_session->GetPlayer()->SendEquipError(msg, 0, 0);
m_session->GetPlayer()->SendEquipError(msg, NULL, NULL);
return true;
}

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "9500"
#define REVISION_NR "9501"
#endif // __REVISION_NR_H__