diff --git a/zone/bot.cpp b/zone/bot.cpp index 225f0af14..2208b4ba1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7205,72 +7205,47 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { - - if (IsBotSpellTypeDetrimental(spellType, caster->GetClass())) { - LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); - return false; - } - - std::vector v; - - if (IsGroupTargetOnlyBotSpellType(spellType)) { - v = caster->GatherGroupSpellTargets(); - } - else { - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = caster->GatherSpellTargets(true); - } - else { - v = caster->GatherGroupSpellTargets(); - } - } - +bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList) { + bool result = false; Mob* tar = nullptr; - for (Mob* m : v) { + for (Mob* m : targetList) { tar = m; if (!tar) { continue; } - if (caster == tar) { - continue; - } - - uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; - - if (!caster->PrecastChecks(tar, spellType)) { - continue; - } - - if (caster->AICastSpell(tar, chanceToCast, spellType)) { - return true; - } - - if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { - tar = m->GetPet(); - - if (!tar) { + if (IsGroupTargetOnlyBotSpellType(spellType)) { + if (raid && (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName()))) { continue; } + } - if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { - continue; - } + result = AttemptAICastSpell(spellType, tar); - if (!caster->PrecastChecks(tar, spellType)) { - continue; - } + if (!result) { + if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { + tar = m->GetPet(); - if (caster->AICastSpell(tar, chanceToCast, spellType)) { - return true; + if (!tar) { + continue; + } + + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + continue; + } + + result = AttemptAICastSpell(spellType, tar); } } + + if (result) { + break; + } } - return false; + return result; } Mob* EntityList::GetMobByBotID(uint32 botID) { @@ -10835,31 +10810,36 @@ std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { return castOrder; } -bool Bot::AttemptAICastSpell(uint16 spellType) { +bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { bool result = false; - Mob* tar = GetTarget(); + if (!tar) { + if (GetTarget()) { + tar = GetTarget(); + } + else { + if (!IsBotSpellTypeBeneficial(spellType)) { + return result; + } + } + } if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } - if (IsBotSpellTypeBeneficial(spellType, GetClass())) { - if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { - if (GetClass() == Class::Bard) { - return result; - } + if (IsBotSpellTypeBeneficial(spellType)) { + if (GetClass() == Class::Bard) { + tar = this; + } - if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), spellType)) { - return result; - } - } + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + return result; } } else { - if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } diff --git a/zone/bot.h b/zone/bot.h index d9340e8a1..4920e808d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -398,9 +398,10 @@ public: // AI Methods bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - bool AttemptAICastSpell(uint16 spellType); + bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); + bool AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 4ec829a2f..2df5bfedc 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -633,6 +633,9 @@ bool Bot::AI_PursueCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -650,6 +653,10 @@ bool Bot::AI_PursueCastCheck() { result = AttemptAICastSpell(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + } + if (result) { break; } @@ -692,6 +699,9 @@ bool Bot::AI_IdleCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -711,11 +721,21 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (!IsBotSpellTypeBeneficial(currentCast.spellType)) { + continue; + } + result = AttemptAICastSpell(currentCast.spellType); if (result) { break; } + + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + + if (result) { + break; + } } if (!AIautocastspell_timer->Enabled()) { @@ -746,6 +766,9 @@ bool Bot::AI_EngagedCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -763,6 +786,10 @@ bool Bot::AI_EngagedCastCheck() { result = AttemptAICastSpell(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + } + if (result) { break; } diff --git a/zone/entity.h b/zone/entity.h index b55c1c7a6..c9d48bc7d 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,6 @@ private: Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob);