Core/Items: Implemented new bonus type (extra ItemEffect)

This commit is contained in:
Shauren
2020-06-05 00:27:40 +02:00
parent ba5794a12c
commit 63fba47511
8 changed files with 87 additions and 81 deletions

View File

@@ -1015,8 +1015,10 @@ enum ItemBonusType
ITEM_BONUS_RELIC_TYPE = 17,
ITEM_BONUS_OVERRIDE_REQUIRED_LEVEL = 18,
ITEM_BONUS_AZERITE_TIER_UNLOCK_SET = 19,
ITEM_BONUS_SCRAPPING_LOOT_ID = 20,
ITEM_BONUS_OVERRIDE_CAN_DISENCHANT = 21,
ITEM_BONUS_OVERRIDE_CAN_SCRAP = 22
ITEM_BONUS_OVERRIDE_CAN_SCRAP = 22,
ITEM_BONUS_ITEM_EFFECT_ID = 23,
};
enum class ItemContext : uint8

View File

@@ -466,8 +466,8 @@ bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext contex
SetDurability(itemProto->MaxDurability);
for (std::size_t i = 0; i < itemProto->Effects.size(); ++i)
if (i < 5)
SetSpellCharges(i, itemProto->Effects[i]->Charges);
if (itemProto->Effects[i]->LegacySlotIndex < 5)
SetSpellCharges(itemProto->Effects[i]->LegacySlotIndex, itemProto->Effects[i]->Charges);
SetExpiration(itemProto->GetDuration());
SetCreatePlayedTime(0);
@@ -557,9 +557,8 @@ void Item::SaveToDB(CharacterDatabaseTransaction& trans)
stmt->setUInt32(++index, m_itemData->Expiration);
std::ostringstream ssSpells;
if (ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(GetEntry()))
for (uint8 i = 0; i < itemProto->Effects.size(); ++i)
ssSpells << GetSpellCharges(i) << ' ';
for (uint8 i = 0; i < m_itemData->SpellCharges.size() && i < _bonusData.EffectCount; ++i)
ssSpells << GetSpellCharges(i) << ' ';
stmt->setString(++index, ssSpells.str());
stmt->setUInt32(++index, m_itemData->DynamicFlags);
@@ -834,11 +833,6 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
need_save = true;
}
Tokenizer tokens(fields[6].GetString(), ' ', proto->Effects.size());
if (tokens.size() == proto->Effects.size())
for (uint8 i = 0; i < proto->Effects.size(); ++i)
SetSpellCharges(i, atoi(tokens[i]));
SetItemFlags(ItemFieldFlags(itemFlags));
uint32 durability = fields[10].GetUInt16();
@@ -870,6 +864,11 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
bonusListIDs.push_back(atoi(token));
SetBonuses(std::move(bonusListIDs));
// load charges after bonuses, they can add more item effects
Tokenizer tokens(fields[6].GetString(), ' ', proto->Effects.size());
for (uint8 i = 0; i < m_itemData->SpellCharges.size() && i < _bonusData.EffectCount && i < tokens.size(); ++i)
SetSpellCharges(i, atoi(tokens[i]));
SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[19].GetUInt32());
SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[20].GetUInt32());
SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[21].GetUInt32());
@@ -2661,6 +2660,14 @@ void BonusData::Initialize(ItemTemplate const* proto)
AzeriteTierUnlockSetId = azeriteEmpoweredItem->AzeriteTierUnlockSetID;
Suffix = 0;
EffectCount = 0;
for (ItemEffectEntry const* itemEffect : proto->Effects)
Effects[EffectCount++] = itemEffect;
for (std::size_t i = EffectCount; i < Effects.size(); ++i)
Effects[i] = nullptr;
CanDisenchant = (proto->GetFlags() & ITEM_FLAG_NO_DISENCHANT) == 0;
CanScrap = (proto->GetFlags4() & ITEM_FLAG4_SCRAPABLE) != 0;
@@ -2786,5 +2793,9 @@ void BonusData::AddBonus(uint32 type, int32 const (&values)[3])
case ITEM_BONUS_OVERRIDE_CAN_SCRAP:
CanScrap = values[0] != 0;
break;
case ITEM_BONUS_ITEM_EFFECT_ID:
if (ItemEffectEntry const* itemEffect = sItemEffectStore.LookupEntry(values[0]))
Effects[EffectCount++] = itemEffect;
break;
}
}

View File

@@ -24,6 +24,7 @@
#include "ItemDefines.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
#include "IteratorPair.h"
#include "Loot.h"
class SpellInfo;
@@ -46,13 +47,6 @@ struct ItemSetEffect
#define MAX_GEM_SOCKETS MAX_ITEM_PROTO_SOCKETS// (BONUS_ENCHANTMENT_SLOT-SOCK_ENCHANTMENT_SLOT) and item proto size, equal value expected
enum EnchantmentOffset
{
ENCHANTMENT_ID_OFFSET = 0,
ENCHANTMENT_DURATION_OFFSET = 1,
ENCHANTMENT_CHARGES_OFFSET = 2 // now here not only charges, but something new in wotlk
};
#define MAX_ENCHANTMENT_OFFSET 3
enum ItemUpdateState
@@ -63,7 +57,6 @@ enum ItemUpdateState
ITEM_REMOVED = 3
};
#define MAX_ITEM_SPELLS 5
bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto);
@@ -93,6 +86,8 @@ struct BonusData
int32 RequiredLevelOverride;
int32 AzeriteTierUnlockSetId;
uint32 Suffix;
std::array<ItemEffectEntry const*, 13> Effects;
std::size_t EffectCount;
bool CanDisenchant;
bool CanScrap;
bool HasFixedLevel;
@@ -345,6 +340,12 @@ class TC_GAME_API Item : public Object
ItemDisenchantLootEntry const* GetDisenchantLoot(Player const* owner) const;
static ItemDisenchantLootEntry const* GetDisenchantLoot(ItemTemplate const* itemTemplate, uint32 quality, uint32 itemLevel);
void SetFixedLevel(uint8 level);
Trinity::IteratorPair<ItemEffectEntry const* const*> GetEffects() const { return { std::make_pair(&_bonusData.Effects[0], &_bonusData.Effects[0] + _bonusData.EffectCount) }; }
ItemEffectEntry const* GetEffect(std::size_t i) const
{
ASSERT(i < _bonusData.EffectCount, "Attempted to get effect at index " SZFMTD " but item has only " SZFMTD " effects!", i, _bonusData.EffectCount);
return _bonusData.Effects[i];
}
// Item Refund system
void SetNotRefundable(Player* owner, bool changestate = true, CharacterDatabaseTransaction* trans = nullptr, bool addToCollection = true);

View File

@@ -7846,13 +7846,12 @@ void Player::CastAllObtainSpells()
void Player::ApplyItemObtainSpells(Item* item, bool apply)
{
ItemTemplate const* itemTemplate = item->GetTemplate();
for (uint8 i = 0; i < itemTemplate->Effects.size(); ++i)
for (ItemEffectEntry const* effect : item->GetEffects())
{
if (itemTemplate->Effects[i]->TriggerType != ITEM_SPELLTRIGGER_ON_OBTAIN) // On obtain trigger
if (effect->TriggerType != ITEM_SPELLTRIGGER_ON_OBTAIN) // On obtain trigger
continue;
int32 const spellId = itemTemplate->Effects[i]->SpellID;
int32 const spellId = effect->SpellID;
if (spellId <= 0)
continue;
@@ -7949,14 +7948,8 @@ void Player::ApplyItemEquipSpell(Item* item, bool apply, bool formChange /*= fal
if (!item)
return;
ItemTemplate const* proto = item->GetTemplate();
if (!proto)
return;
for (uint8 i = 0; i < proto->Effects.size(); ++i)
for (ItemEffectEntry const* effectData : item->GetEffects())
{
ItemEffectEntry const* effectData = proto->Effects[i];
// wrong triggering type
if (apply && effectData->TriggerType != ITEM_SPELLTRIGGER_ON_EQUIP)
continue;
@@ -8294,10 +8287,8 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT
bool canTrigger = (damageInfo.GetHitMask() & (PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB)) != 0;
if (canTrigger)
{
for (uint8 i = 0; i < proto->Effects.size(); ++i)
for (ItemEffectEntry const* effectData : item->GetEffects())
{
ItemEffectEntry const* effectData = proto->Effects[i];
// wrong triggering type
if (effectData->TriggerType != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
continue;
@@ -8422,19 +8413,18 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT
void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, ObjectGuid castCount, int32* misc)
{
ItemTemplate const* proto = item->GetTemplate();
// special learning case
if (proto->Effects.size() >= 2)
if (item->GetBonus()->EffectCount >= 2)
{
if (proto->Effects[0]->SpellID == 483 || proto->Effects[0]->SpellID == 55884)
if (item->GetEffect(0)->SpellID == 483 || item->GetEffect(0)->SpellID == 55884)
{
uint32 learn_spell_id = proto->Effects[0]->SpellID;
uint32 learning_spell_id = proto->Effects[1]->SpellID;
uint32 learn_spell_id = item->GetEffect(0)->SpellID;
uint32 learning_spell_id = item->GetEffect(1)->SpellID;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learn_spell_id);
if (!spellInfo)
{
TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring", proto->GetId(), learn_spell_id);
TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring", item->GetEntry(), learn_spell_id);
SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, item, nullptr);
return;
}
@@ -8455,10 +8445,8 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, Objec
}
// item spells cast at use
for (uint8 i = 0; i < proto->Effects.size(); ++i)
for (ItemEffectEntry const* effectData : item->GetEffects())
{
ItemEffectEntry const* effectData = proto->Effects[i];
// wrong triggering type
if (effectData->TriggerType != ITEM_SPELLTRIGGER_ON_USE)
continue;
@@ -8466,7 +8454,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, Objec
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effectData->SpellID);
if (!spellInfo)
{
TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring", proto->GetId(), effectData->SpellID);
TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring", item->GetEntry(), effectData->SpellID);
continue;
}
@@ -24288,15 +24276,12 @@ void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint3
void Player::ApplyEquipCooldown(Item* pItem)
{
ItemTemplate const* proto = pItem->GetTemplate();
if (proto->GetFlags() & ITEM_FLAG_NO_EQUIP_COOLDOWN)
if (pItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_EQUIP_COOLDOWN)
return;
std::chrono::steady_clock::time_point now = GameTime::GetGameTimeSteadyPoint();
for (uint8 i = 0; i < proto->Effects.size(); ++i)
for (ItemEffectEntry const* effectData : pItem->GetEffects())
{
ItemEffectEntry const* effectData = proto->Effects[i];
// apply proc cooldown to equip auras if we have any
if (effectData->TriggerType == ITEM_SPELLTRIGGER_ON_EQUIP)
{

View File

@@ -1190,11 +1190,10 @@ void WorldSession::HandleUseCritterItem(WorldPackets::Item::UseCritterItem& useC
if (!item)
return;
if (item->GetTemplate()->Effects.size() < 2)
if (item->GetBonus()->EffectCount < 2)
return;
int32 spellToLearn = item->GetTemplate()->Effects[1]->SpellID;
int32 spellToLearn = item->GetEffect(1)->SpellID;
if (BattlePetSpeciesEntry const* entry = sSpellMgr->GetBattlePetSpecies(uint32(spellToLearn)))
{

View File

@@ -95,9 +95,9 @@ void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet)
if (user->IsInCombat())
{
for (uint32 i = 0; i < proto->Effects.size(); ++i)
for (ItemEffectEntry const* effect : item->GetEffects())
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[i]->SpellID))
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effect->SpellID))
{
if (!spellInfo->CanBeUsedInCombat())
{

View File

@@ -4530,22 +4530,25 @@ void Spell::TakeCastItem()
bool expendable = false;
bool withoutCharges = false;
for (uint8 i = 0; i < proto->Effects.size() && i < 5; ++i)
for (ItemEffectEntry const* itemEffect : m_CastItem->GetEffects())
{
if (itemEffect->LegacySlotIndex >= m_CastItem->m_itemData->SpellCharges.size())
continue;
// item has limited charges
if (proto->Effects[i]->Charges)
if (itemEffect->Charges)
{
if (proto->Effects[i]->Charges < 0)
if (itemEffect->Charges < 0)
expendable = true;
int32 charges = m_CastItem->GetSpellCharges(i);
int32 charges = m_CastItem->GetSpellCharges(itemEffect->LegacySlotIndex);
// item has charges left
if (charges)
{
(charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use
if (proto->GetMaxStackSize() == 1)
m_CastItem->SetSpellCharges(i, charges);
m_CastItem->SetSpellCharges(itemEffect->LegacySlotIndex, charges);
m_CastItem->SetState(ITEM_CHANGED, player);
}
@@ -4695,10 +4698,8 @@ void Spell::TakeReagents()
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
ItemTemplate const* castItemTemplate = m_CastItem ? m_CastItem->GetTemplate() : NULL;
// do not take reagents for these item casts
if (castItemTemplate && castItemTemplate->GetFlags() & ITEM_FLAG_NO_REAGENT_COST)
if (m_CastItem && m_CastItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST)
return;
Player* p_caster = m_caster->ToPlayer();
@@ -4714,13 +4715,16 @@ void Spell::TakeReagents()
uint32 itemcount = m_spellInfo->ReagentCount[x];
// if CastItem is also spell reagent
if (castItemTemplate && castItemTemplate->GetId() == itemid)
if (m_CastItem && m_CastItem->GetEntry() == itemid)
{
for (uint8 s = 0; s < castItemTemplate->Effects.size() && s < 5; ++s)
for (ItemEffectEntry const* itemEffect : m_CastItem->GetEffects())
{
if (itemEffect->LegacySlotIndex >= m_CastItem->m_itemData->SpellCharges.size())
continue;
// CastItem will be used up and does not count as reagent
int32 charges = m_CastItem->GetSpellCharges(s);
if (castItemTemplate->Effects[s]->Charges < 0 && abs(charges) < 2)
int32 charges = m_CastItem->GetSpellCharges(itemEffect->LegacySlotIndex);
if (itemEffect->Charges < 0 && abs(charges) < 2)
{
++itemcount;
break;
@@ -6286,9 +6290,9 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
if (!proto)
return SPELL_FAILED_ITEM_NOT_READY;
for (uint8 i = 0; i < proto->Effects.size() && i < 5; ++i)
if (proto->Effects[i]->Charges)
if (m_CastItem->GetSpellCharges(i) == 0)
for (ItemEffectEntry const* itemEffect : m_CastItem->GetEffects())
if (itemEffect->LegacySlotIndex < m_CastItem->m_itemData->SpellCharges.size() && itemEffect->Charges)
if (m_CastItem->GetSpellCharges(itemEffect->LegacySlotIndex) == 0)
return SPELL_FAILED_NO_CHARGES_REMAIN;
// consumable cast item checks
@@ -6388,11 +6392,15 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
ItemTemplate const* proto = m_CastItem->GetTemplate();
if (!proto)
return SPELL_FAILED_ITEM_NOT_READY;
for (uint8 s = 0; s < proto->Effects.size() && s < 5; ++s)
for (ItemEffectEntry const* itemEffect : m_CastItem->GetEffects())
{
if (itemEffect->LegacySlotIndex >= m_CastItem->m_itemData->SpellCharges.size())
continue;
// CastItem will be used up and does not count as reagent
int32 charges = m_CastItem->GetSpellCharges(s);
if (proto->Effects[s]->Charges < 0 && abs(charges) < 2)
int32 charges = m_CastItem->GetSpellCharges(itemEffect->LegacySlotIndex);
if (itemEffect->Charges < 0 && abs(charges) < 2)
{
++itemcount;
break;
@@ -6521,10 +6529,9 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
return SPELL_FAILED_LOWLEVEL;
bool isItemUsable = false;
ItemTemplate const* proto = targetItem->GetTemplate();
for (uint8 e = 0; e < proto->Effects.size(); ++e)
for (ItemEffectEntry const* itemEffect : targetItem->GetEffects())
{
if (proto->Effects[e]->SpellID && proto->Effects[e]->TriggerType == ITEM_SPELLTRIGGER_ON_USE)
if (itemEffect->SpellID && itemEffect->TriggerType == ITEM_SPELLTRIGGER_ON_USE)
{
isItemUsable = true;
break;
@@ -6718,11 +6725,11 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
return SPELL_FAILED_ITEM_AT_MAX_CHARGES;
if (Item* item = player->GetItemByEntry(itemId))
{
for (uint8 x = 0; x < proto->Effects.size() && x < 5; ++x)
if (proto->Effects[x]->Charges != 0 && item->GetSpellCharges(x) == proto->Effects[x]->Charges)
for (ItemEffectEntry const* itemEffect : item->GetEffects())
if (itemEffect->LegacySlotIndex <= item->m_itemData->SpellCharges.size()
&& itemEffect->Charges != 0
&& item->GetSpellCharges(itemEffect->LegacySlotIndex) == itemEffect->Charges)
return SPELL_FAILED_ITEM_AT_MAX_CHARGES;
}
break;
}
case SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM:

View File

@@ -5392,9 +5392,10 @@ void Spell::EffectRechargeItem(SpellEffIndex /*effIndex*/)
if (Item* item = player->GetItemByEntry(effectInfo->ItemType))
{
ItemTemplate const* proto = item->GetTemplate();
for (size_t x = 0; x < proto->Effects.size() && x < 5; ++x)
item->SetSpellCharges(x, proto->Effects[x]->Charges);
for (ItemEffectEntry const* itemEffect : item->GetEffects())
if (itemEffect->LegacySlotIndex <= item->m_itemData->SpellCharges.size())
item->SetSpellCharges(itemEffect->LegacySlotIndex, itemEffect->Charges);
item->SetState(ITEM_CHANGED, player);
}
}