From 18685748f679c80348955a992793f007544f5e2b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 15 Feb 2025 14:55:52 -0600 Subject: [PATCH] [Bots] Crash fixes related to GetNumberNeedingHealedInGroup (#4684) - Add pre-death checks and early returns - Cleanup pointers for GetUltimateSpellType checks - Rename IsBotSpellTypeOtherBeneficial to BotSpellTypeUsesTargetSettings - Rename GetUltimateSpellTypeDelayCheck to GetUltimateSpellTypeRecastCheck -Add entity validation to GetNumberNeedingHealedInGroup --- common/spdat.h | 2 +- common/spdat_bot.cpp | 2 +- zone/bot.cpp | 134 +++++++++++++++++++---------------- zone/bot.h | 2 +- zone/bot_commands/depart.cpp | 2 +- zone/botspellsai.cpp | 126 +++++++++++++++++--------------- 6 files changed, 146 insertions(+), 122 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 1d3bd521e..9dd0b871c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -900,7 +900,7 @@ const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellTyp // Bot related functions bool IsBotSpellTypeDetrimental (uint16 spell_type); bool IsBotSpellTypeBeneficial (uint16 spell_type); -bool IsBotSpellTypeOtherBeneficial(uint16 spell_type); +bool BotSpellTypeUsesTargetSettings(uint16 spell_type); bool IsBotSpellTypeInnate (uint16 spell_type); bool IsAEBotSpellType(uint16 spell_type); bool IsGroupBotSpellType(uint16 spell_type); diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp index e6e6a68c2..04eb5cff8 100644 --- a/common/spdat_bot.cpp +++ b/common/spdat_bot.cpp @@ -90,7 +90,7 @@ bool IsBotSpellTypeBeneficial(uint16 spell_type) { return false; } -bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) { +bool BotSpellTypeUsesTargetSettings(uint16 spell_type) { switch (spell_type) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: diff --git a/zone/bot.cpp b/zone/bot.cpp index 0940b04e1..c90c6ad46 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3265,7 +3265,11 @@ Mob* Bot::GetBotTarget(Client* bot_owner) } bool Bot::TargetValidation(Mob* other) { - if (!other || GetAppearance() == eaDead) { + if (GetAppearance() == eaDead || GetHP() < 0) { + return false; + } + + if (!other || other->GetAppearance() == eaDead || other->GetHP() < 0) { return false; } @@ -7684,15 +7688,21 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { } uint8 Bot::GetNumberNeedingHealedInGroup(Mob* tar, uint16 spell_type, uint16 spell_id, float range) { - if (!tar) { + if (!TargetValidation(tar)) { return 0; } uint8 count = 0; - auto target_list = tar->IsClient() ? GatherSpellTargets(false, tar) : tar->CastToBot()->GetSpellTargetList(); + std::vector target_list = tar->IsClient() ? GatherSpellTargets(false, tar) : tar->CastToBot()->GetSpellTargetList(); - for (Mob* m : target_list) { - if (m && tar->CalculateDistance(m) < range && CastChecks(spell_id, m, spell_type, true, IsGroupBotSpellType(spell_type))) { + for (auto* m : target_list) { + if ( + m && + entity_list.IsMobInZone(m) && + TargetValidation(m) && + tar->CalculateDistance(m) < range && + CastChecks(spell_id, m, spell_type, true, IsGroupBotSpellType(spell_type)) + ) { ++count; } } @@ -9403,7 +9413,7 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { - if (!tar) { + if (!TargetValidation(tar)) { LogBotSpellChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); return false; } @@ -9425,24 +9435,18 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return true; } - if ( - GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) || - GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type) - ) { + if (!EQ::ValueWithin(GetManaRatio(), GetSpellTypeMinManaLimit(spell_type), GetSpellTypeMaxManaLimit(spell_type))) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if ( - GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) || - GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type) - ) { + if (!EQ::ValueWithin(GetHPRatio(), GetSpellTypeMinHPLimit(spell_type), GetSpellTypeMaxHPLimit(spell_type))) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if (!GetUltimateSpellTypeDelayCheck(spell_type, tar)) { - LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + if (!GetUltimateSpellTypeRecastCheck(spell_type, tar)) { + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeRecastCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9451,10 +9455,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { case BotSpellTypes::AEMez: return true; default: - if ( - GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellTypeMinThreshold(spell_type, tar) || - GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellTypeMaxThreshold(spell_type, tar) - ) { + if (!EQ::ValueWithin(GetHPRatioForSpellType(spell_type, tar), GetUltimateSpellTypeMinThreshold(spell_type, tar), GetUltimateSpellTypeMaxThreshold(spell_type, tar))) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeMinThreshold or GetUltimateSpellTypeMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9465,7 +9466,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks, bool ae_check) { if (prechecks) { - if (!tar) { + if (!tar || tar->GetAppearance() == eaDead || tar->GetHP() < 0) { LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9616,7 +9617,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck if ( BotSpellTypeRequiresTarget(spell_type) && - !tar + !TargetValidation(tar) ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; @@ -10520,7 +10521,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { return; } - if (!precast && IsBotSpellTypeOtherBeneficial(spell_type)) { + if (!precast && BotSpellTypeUsesTargetSettings(spell_type)) { return; } @@ -10545,7 +10546,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { owner->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } } - else if (IsBotSpellTypeOtherBeneficial(spell_type)) { + else if (BotSpellTypeUsesTargetSettings(spell_type)) { if (tar->IsClient()) { tar->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } @@ -10559,6 +10560,12 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { } BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) { + if (!TargetValidation(tar)) { + BotSpell result; + + return result; + } + switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -11142,7 +11149,7 @@ uint16 Bot::GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance) { } bool Bot::GetUltimateSpellTypeHold(uint16 spell_type, Mob* tar) { - if (!tar) { + if (!TargetValidation(tar)) { return GetSpellTypeHold(spell_type); } @@ -13001,7 +13008,7 @@ uint8 Bot::GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance) { } case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: - if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellTypeHold(BotSpellTypes::InCombatBuff))) { + if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { return 60; } @@ -13130,81 +13137,88 @@ uint8 Bot::GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance) { } uint16 Bot::GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar) { - if (!tar) { + if (!TargetValidation(tar)) { return GetSpellTypeDelay(spell_type); } - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - Mob* owner = tar->GetOwner(); + Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr; - return owner->IsClient() ? owner->CastToClient()->GetSpellTypeDelay(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeDelay( - GetPetBotSpellType(spell_type)); + if (owner && owner->IsOfClientBot()) { + return owner->IsClient() + ? owner->CastToClient()->GetSpellTypeDelay(GetPetBotSpellType(spell_type)) + : owner->CastToBot()->GetSpellTypeDelay(GetPetBotSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellTypeDelay(spell_type) : tar->CastToBot()->GetSpellTypeDelay( - spell_type - ); + if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() + ? tar->CastToClient()->GetSpellTypeDelay(spell_type) + : tar->CastToBot()->GetSpellTypeDelay(spell_type); } return GetSpellTypeDelay(spell_type); } -bool Bot::GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar) { - if (!tar) { +bool Bot::GetUltimateSpellTypeRecastCheck(uint16 spell_type, Mob* tar) { + if (!TargetValidation(tar)) { return SpellTypeRecastCheck(spell_type); } - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - Mob* owner = tar->GetOwner(); + Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr; - return owner->IsClient() ? owner->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) : owner->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); + if (owner && owner->IsOfClientBot()) { + return owner->IsClient() + ? owner->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) + : owner->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->SpellTypeRecastCheck(spell_type) : tar->CastToBot()->SpellTypeRecastCheck(spell_type); + if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() + ? tar->CastToClient()->SpellTypeRecastCheck(spell_type) + : tar->CastToBot()->SpellTypeRecastCheck(spell_type); } return SpellTypeRecastCheck(spell_type); } uint8 Bot::GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { + if (!TargetValidation(tar)) { return GetSpellTypeMinThreshold(spell_type); } - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - Mob* owner = tar->GetOwner(); + Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr; - return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMinThreshold( - GetPetBotSpellType(spell_type)); + if (owner && owner->IsOfClientBot()) { + return owner->IsClient() + ? owner->CastToClient()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type)) + : owner->CastToBot()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMinThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMinThreshold( - spell_type - ); + if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() + ? tar->CastToClient()->GetSpellTypeMinThreshold(spell_type) + : tar->CastToBot()->GetSpellTypeMinThreshold(spell_type); } return GetSpellTypeMinThreshold(spell_type); } uint8 Bot::GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { + if (!TargetValidation(tar)) { return GetSpellTypeMaxThreshold(spell_type); } - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - Mob* owner = tar->GetOwner(); + Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr; - return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMaxThreshold( - GetPetBotSpellType(spell_type)); + if (owner && owner->IsOfClientBot()) { + return owner->IsClient() + ? owner->CastToClient()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type)) + : owner->CastToBot()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMaxThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMaxThreshold( - spell_type - ); + if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() + ? tar->CastToClient()->GetSpellTypeMaxThreshold(spell_type) + : tar->CastToBot()->GetSpellTypeMaxThreshold(spell_type); } return GetSpellTypeMaxThreshold(spell_type); diff --git a/zone/bot.h b/zone/bot.h index b5e53e713..10e01447a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -643,7 +643,7 @@ public: uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar); - bool GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar); + bool GetUltimateSpellTypeRecastCheck(uint16 spell_type, Mob* tar); uint8 GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar); uint8 GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar); void SetIllusionBlock(bool value) { _illusionBlock = value; } diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 9fb954758..dd0c12bfd 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -220,7 +220,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) { - if (IsBotSpellTypeOtherBeneficial(BotSpellTypes::Teleport)) { + if (BotSpellTypeUsesTargetSettings(BotSpellTypes::Teleport)) { bot_iter->SetCastedSpellType(UINT16_MAX); } else { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 1013a36c3..56841172e 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -242,7 +242,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (IsBotSpellTypeOtherBeneficial(spell_type)) { + if (BotSpellTypeUsesTargetSettings(spell_type)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -288,7 +288,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (IsBotSpellTypeOtherBeneficial(spell_type)) { + if (BotSpellTypeUsesTargetSettings(spell_type)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -493,6 +493,10 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + if (!TargetValidation(tar)) { + return false; + } + bot_spell = GetSpellByHealType(spell_type, tar); if (!IsValidSpell(bot_spell.SpellId)) { @@ -1287,30 +1291,32 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty result.SpellIndex = 0; result.ManaCost = 0; - if (caster) { - std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - - int target_count = 0; + if (!caster->TargetValidation(tar)) { + return result; + } - for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) { - uint16 spell_id = bot_spell_list_itr->SpellId; + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); + int target_count = 0; + int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type); - if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { - target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) { + uint16 spell_id = bot_spell_list_itr->SpellId; - if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { - continue; - } + if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); + + if (target_count < required_count) { + continue; } - - result.SpellId = bot_spell_list_itr->SpellId; - result.SpellIndex = bot_spell_list_itr->SpellIndex; - result.ManaCost = bot_spell_list_itr->ManaCost; - - break; } + + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; + + break; } } @@ -1324,30 +1330,32 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16 result.SpellIndex = 0; result.ManaCost = 0; - if (caster) { - std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime); + if (!caster->TargetValidation(tar)) { + return result; + } - int target_count = 0; + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime); + int target_count = 0; + int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type); - for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { - uint16 spell_id = bot_spell_list_itr->SpellId; + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { + uint16 spell_id = bot_spell_list_itr->SpellId; - if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { - target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); + if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); - if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { - continue; - } + if (target_count < required_count) { + continue; } - - result.SpellId = bot_spell_list_itr->SpellId; - result.SpellIndex = bot_spell_list_itr->SpellIndex; - result.ManaCost = bot_spell_list_itr->ManaCost; - - break; } + + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; + + break; } } @@ -1361,30 +1369,32 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16 result.SpellIndex = 0; result.ManaCost = 0; - if (caster) { - std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CompleteHeal); - - int target_count = 0; + if (!caster->TargetValidation(tar)) { + return result; + } - for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) { - uint16 spell_id = bot_spell_list_itr->SpellId; + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CompleteHeal); + int target_count = 0; + int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type); - if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { - target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) { + uint16 spell_id = bot_spell_list_itr->SpellId; - if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { - continue; - } + if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); + + if (target_count < required_count) { + continue; } - - result.SpellId = bot_spell_list_itr->SpellId; - result.SpellIndex = bot_spell_list_itr->SpellIndex; - result.ManaCost = bot_spell_list_itr->ManaCost; - - break; } + + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; + + break; } }