Core/Spells: make Spell::CheckItems partially respect gameobject casters as well

This commit is contained in:
Ovahlord committed 2022-12-20 09:46:57 +01:00
1 parent 708e862ee4
commit 56b0ca2370
1 file changed
+89 -62
+89 -62
View File
@@ -7042,19 +7042,18 @@ SpellCastResult Spell::CheckPower() const
SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const
{
Player* player = m_caster->ToPlayer();
if (!player)
return SPELL_CAST_OK;
// We expect a cast item but we don't have one. Fail cast right away
if (!m_CastItem && !m_castItemGUID.IsEmpty())
return SPELL_FAILED_ITEM_NOT_READY;
if (!m_CastItem)
Player* player = m_caster->ToPlayer();
if (m_CastItem)
{
if (m_castItemGUID)
// Casting spells from an item always requires a player using it.
if (!player)
return SPELL_FAILED_ITEM_NOT_READY;
}
else
{
uint32 itemid = m_CastItem->GetEntry();
if (!player->HasItemCount(itemid))
if (!player->HasItemCount(m_CastItem->GetEntry()))
return SPELL_FAILED_ITEM_NOT_READY;
ItemTemplate const* proto = m_CastItem->GetTemplate();
@@ -7066,14 +7065,14 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
if (m_CastItem->GetSpellCharges(i) == 0)
return SPELL_FAILED_NO_CHARGES_REMAIN;
// consumable cast item checks
// Consumable cast item checks
if (proto->GetClass() == ITEM_CLASS_CONSUMABLE && m_targets.GetUnitTarget())
{
// such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example
// Such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example
SpellCastResult failReason = SPELL_CAST_OK;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// skip check, pet not required like checks, and for TARGET_UNIT_PET m_targets.GetUnitTarget() is not the real target but the caster
// Skip check, pet not required like checks, and for TARGET_UNIT_PET m_targets.GetUnitTarget() is not the real target but the caster
if (m_spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_PET)
continue;
@@ -7118,68 +7117,77 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
}
}
// check target item
// Check target item
if (m_targets.GetItemTargetGUID())
{
Item* item = m_targets.GetItemTarget();
if (!item)
if (!item || item->GetGUID() != m_targets.GetItemTargetGUID())
return SPELL_FAILED_ITEM_GONE;
if (!item->IsFitToSpellRequirements(m_spellInfo))
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
// if not item target then required item must be equipped
else
else if (player)
{
// Check item requirements
if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT))
if (!player->HasItemFitToSpellRequirements(m_spellInfo))
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
// do not take reagents for these item casts
if (!(m_CastItem && m_CastItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST))
// Check reagent requirements
bool needReagentCheck = [&]()
{
bool checkReagents = !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST) && !player->CanNoReagentCast(m_spellInfo);
// Not own traded item (in trader trade slot) requires reagents even if triggered spell
if (!checkReagents)
if (Item* targetItem = m_targets.GetItemTarget())
if (targetItem->GetOwnerGUID() != player->GetGUID())
checkReagents = true;
if (m_CastItem && m_CastItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST)
return false;
// check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case.
if (checkReagents)
if (!(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST) || (player && !player->CanNoReagentCast(m_spellInfo)))
return false;
return true;
}();
// Enchanting other players items always requires a reagent check so we do not skip its checks
if (!needReagentCheck)
if (Item* targetItem = m_targets.GetItemTarget())
if (player && targetItem->GetOwnerGUID() != player->GetGUID())
needReagentCheck = true;
if (needReagentCheck)
{
for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
{
for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++)
// No reagent required, check the next entry
if (m_spellInfo->Reagent[i] <= 0)
continue;
uint32 requiredItemId = m_spellInfo->Reagent[i];
uint32 requiredItemQuantity = m_spellInfo->ReagentCount[i];
// if CastItem is also spell reagent
if (m_CastItem && m_CastItem->GetEntry() == requiredItemId)
{
if (m_spellInfo->Reagent[i] <= 0)
continue;
ItemTemplate const* proto = m_CastItem->GetTemplate();
if (!proto)
return SPELL_FAILED_ITEM_NOT_READY;
uint32 itemid = m_spellInfo->Reagent[i];
uint32 itemcount = m_spellInfo->ReagentCount[i];
// if CastItem is also spell reagent
if (m_CastItem && m_CastItem->GetEntry() == itemid)
for (uint8 s = 0; s < proto->Effects.size(); ++s)
{
ItemTemplate const* proto = m_CastItem->GetTemplate();
if (!proto)
return SPELL_FAILED_ITEM_NOT_READY;
for (uint8 s = 0; s < proto->Effects.size(); ++s)
// 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)
{
// 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)
{
++itemcount;
break;
}
++requiredItemQuantity;
break;
}
}
if (!player->HasItemCount(itemid, itemcount))
{
if (param1)
*param1 = itemid;
return SPELL_FAILED_REAGENTS;
}
}
if (!player || !player->HasItemCount(requiredItemId, requiredItemQuantity))
{
if (param1)
*param1 = requiredItemId;
return SPELL_FAILED_REAGENTS;
}
}
}
@@ -7193,15 +7201,19 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
case SPELL_EFFECT_CREATE_ITEM_2:
{
// m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error
Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player;
if (target->GetTypeId() == TYPEID_PLAYER && !IsTriggered())
Player* playerTarget = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget()->ToPlayer() : player;
if (!playerTarget)
return SPELL_FAILED_BAD_TARGETS;
if (!IsTriggered())
{
// SPELL_EFFECT_CREATE_ITEM_2 differs from SPELL_EFFECT_CREATE_ITEM in that it picks the random item to create from a pool of potential items,
// so we need to make sure there is at least one free space in the player's inventory
if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_CREATE_ITEM_2)
if (target->ToPlayer()->GetFreeInventorySpace() == 0)
if (playerTarget->GetFreeInventorySpace() == 0)
{
player->SendEquipError(EQUIP_ERR_INV_FULL, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
if (player)
player->SendEquipError(EQUIP_ERR_INV_FULL, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
return SPELL_FAILED_DONT_REPORT;
}
@@ -7213,13 +7225,14 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
uint32 createCount = std::clamp<uint32>(m_spellInfo->Effects[i].CalcValue(), 1u, itemTemplate->GetMaxStackSize());
ItemPosCountVec dest;
InventoryResult msg = target->ToPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->Effects[i].ItemType, createCount);
InventoryResult msg = playerTarget->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->Effects[i].ItemType, createCount);
if (msg != EQUIP_ERR_OK)
{
/// @todo Needs review
if (!itemTemplate->GetItemLimitCategory())
{
player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
if (player)
player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
return SPELL_FAILED_DONT_REPORT;
}
else
@@ -7227,14 +7240,15 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
// Conjure Food/Water/Refreshment spells
if (m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE || (!(m_spellInfo->SpellFamilyFlags[0] & 0x40000000)))
return SPELL_FAILED_TOO_MANY_OF_ITEM;
else if (!(target->ToPlayer()->HasItemCount(m_spellInfo->Effects[i].ItemType)))
else if (!(playerTarget->HasItemCount(m_spellInfo->Effects[i].ItemType)))
{
player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
if (player)
player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].ItemType);
return SPELL_FAILED_DONT_REPORT;
}
else
player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), CastSpellExtraArgs()
.SetTriggeringSpell(this)); // move this to anywhere
if (player)
player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), CastSpellExtraArgs().SetTriggeringSpell(this)); // move this to anywhere
return SPELL_FAILED_DONT_REPORT;
}
}
@@ -7242,6 +7256,19 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
}
break;
}
default:
break;
}
}
// From this point forward, we will only check player based requirements so we end this procedure here for creature and gameobject casts.
if (!player)
return SPELL_CAST_OK;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
switch (m_spellInfo->Effects[i].Effect)
{
case SPELL_EFFECT_ENCHANT_ITEM:
if (m_spellInfo->Effects[i].ItemType && m_targets.GetItemTarget()
&& (m_targets.GetItemTarget()->IsVellum()))