mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
18 Commits
1041b0b070
...
62ef4b63b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62ef4b63b1 | ||
|
|
714bb6bca3 | ||
|
|
585027fab7 | ||
|
|
571735cd57 | ||
|
|
b430118124 | ||
|
|
180be33d9d | ||
|
|
9e251dc397 | ||
|
|
92fa97c3aa | ||
|
|
ff001afd46 | ||
|
|
32d10080a4 | ||
|
|
1bbed177c8 | ||
|
|
a3ca438bef | ||
|
|
28ec9b34b8 | ||
|
|
a1f9ff4542 | ||
|
|
0afaf753c6 | ||
|
|
240bb2dfca | ||
|
|
4a63ee37e2 | ||
|
|
9bba4b78dd |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
|||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN (
|
||||||
|
'send_mail_disabled'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
|
||||||
|
'send_mail_disabled'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||||
|
(1899, 'send_mail_disabled', 'I cannot send mail', 0, 0, '우편을 보낼 수 없습니다', 'Je ne peux pas envoyer de courrier', 'Ich kann keine Post senden', '我不能寄送邮件', '我不能寄送郵件', 'No puedo enviar correo', 'No puedo enviar correo', 'Я не могу отправить почту');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
|
||||||
|
('send_mail_disabled', 100);
|
||||||
@ -12,8 +12,8 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/)
|
|||||||
{
|
{
|
||||||
AutoPickTalents();
|
AutoPickTalents();
|
||||||
AutoLearnSpell();
|
AutoLearnSpell();
|
||||||
AutoUpgradeEquip();
|
|
||||||
AutoTeleportForLevel();
|
AutoTeleportForLevel();
|
||||||
|
AutoUpgradeEquip();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -21,13 +21,11 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/)
|
|||||||
void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel()
|
void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel()
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig.autoTeleportForLevel || !sRandomPlayerbotMgr.IsRandomBot(bot))
|
if (!sPlayerbotAIConfig.autoTeleportForLevel || !sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if (botAI->HasRealPlayerMaster())
|
if (botAI->HasRealPlayerMaster())
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
sRandomPlayerbotMgr.RandomTeleportForLevel(bot);
|
sRandomPlayerbotMgr.RandomTeleportForLevel(bot);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -89,21 +87,17 @@ void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
|||||||
{
|
{
|
||||||
Quest const* quest = i->second;
|
Quest const* quest = i->second;
|
||||||
|
|
||||||
// only process class-specific quests to learn class-related spells, cuz
|
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10 ||
|
||||||
// we don't want all these bunch of entries to be handled!
|
quest->GetMinLevel() > bot->GetLevel())
|
||||||
if (!quest->GetRequiredClasses())
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// skip quests that are repeatable, too low level, or above bots' level
|
|
||||||
if (quest->IsRepeatable() || quest->GetMinLevel() < 10 || quest->GetMinLevel() > bot->GetLevel())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// skip if bot doesnt satisfy class, race, or skill requirements
|
|
||||||
if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) ||
|
if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) ||
|
||||||
!bot->SatisfyQuestSkill(quest, false))
|
!bot->SatisfyQuestSkill(quest, false))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// use the same logic and impl from Player::learnQuestRewardedSpells
|
|
||||||
|
|
||||||
int32 spellId = quest->GetRewSpellCast();
|
int32 spellId = quest->GetRewSpellCast();
|
||||||
if (!spellId)
|
if (!spellId)
|
||||||
@ -113,31 +107,26 @@ void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
|||||||
if (!spellInfo)
|
if (!spellInfo)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// xinef: find effect with learn spell and check if we have this spell
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
{
|
{
|
||||||
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell &&
|
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell &&
|
||||||
!bot->HasSpell(spellInfo->Effects[i].TriggerSpell))
|
!bot->HasSpell(spellInfo->Effects[i].TriggerSpell))
|
||||||
{
|
{
|
||||||
// pusywizard: don't re-add profession specialties!
|
|
||||||
if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell))
|
if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell))
|
||||||
if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL)
|
if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL)
|
||||||
break; // pussywizard: break and not cast the spell (found is false)
|
break;
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// xinef: we know the spell, continue
|
|
||||||
if (!found)
|
if (!found)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bot->CastSpell(bot, spellId, true);
|
bot->CastSpell(bot, spellId, true);
|
||||||
|
|
||||||
// Check if RewardDisplaySpell is set to output the proper spell learned
|
|
||||||
// after processing quests. Output the original RewardSpell otherwise.
|
|
||||||
uint32 rewSpellId = quest->GetRewSpell();
|
uint32 rewSpellId = quest->GetRewSpell();
|
||||||
if (rewSpellId)
|
if (rewSpellId)
|
||||||
{
|
{
|
||||||
@ -167,12 +156,11 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s
|
|||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig.autoUpgradeEquip || !sRandomPlayerbotMgr.IsRandomBot(bot))
|
if (!sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||||
|
|
||||||
// Clean up old consumables before adding new ones
|
|
||||||
factory.CleanupConsumables();
|
factory.CleanupConsumables();
|
||||||
|
|
||||||
factory.InitAmmo();
|
factory.InitAmmo();
|
||||||
@ -181,9 +169,6 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
|||||||
factory.InitConsumables();
|
factory.InitConsumables();
|
||||||
factory.InitPotions();
|
factory.InitPotions();
|
||||||
|
|
||||||
if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
if (sPlayerbotAIConfig.autoUpgradeEquip)
|
||||||
{
|
|
||||||
if (sPlayerbotAIConfig.incrementalGearInit)
|
|
||||||
factory.InitEquipment(true);
|
factory.InitEquipment(true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@ -154,9 +154,11 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
calculator.SetOverflowPenalty(false);
|
calculator.SetOverflowPenalty(false);
|
||||||
|
|
||||||
// Calculate item scores once and store them
|
// Calculate item scores once and store them
|
||||||
float newItemScore = calculator.CalculateItem(itemId);
|
float newItemScore = calculator.CalculateItem(itemId, item->GetItemRandomPropertyId());
|
||||||
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
|
float mainHandScore = mainHandItem
|
||||||
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
|
? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId, mainHandItem->GetItemRandomPropertyId()) : 0.0f;
|
||||||
|
float offHandScore = offHandItem
|
||||||
|
? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId, offHandItem->GetItemRandomPropertyId()) : 0.0f;
|
||||||
|
|
||||||
// Determine where this weapon can go
|
// Determine where this weapon can go
|
||||||
bool canGoMain = (invType == INVTYPE_WEAPON ||
|
bool canGoMain = (invType == INVTYPE_WEAPON ||
|
||||||
|
|||||||
@ -86,7 +86,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event /*event*/)
|
|||||||
|
|
||||||
uint32 spellId = itr->first;
|
uint32 spellId = itr->first;
|
||||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
if (!spellInfo->IsAutocastable())
|
if (!spellInfo || !spellInfo->IsAutocastable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool shouldApply = true;
|
bool shouldApply = true;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemTemplate.h"
|
#include "ItemTemplate.h"
|
||||||
@ -23,10 +24,118 @@
|
|||||||
using ai::buff::MakeAuraQualifierForBuff;
|
using ai::buff::MakeAuraQualifierForBuff;
|
||||||
using ai::spell::HasSpellOrCategoryCooldown;
|
using ai::spell::HasSpellOrCategoryCooldown;
|
||||||
|
|
||||||
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
namespace
|
||||||
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
|
||||||
{
|
{
|
||||||
|
std::unordered_set<uint32> const& GetMixedTriggerTrinketSpellIds()
|
||||||
|
{
|
||||||
|
static std::unordered_set<uint32> const mixedTriggerSpellIds = []()
|
||||||
|
{
|
||||||
|
std::unordered_set<uint32> onUseSpellIds;
|
||||||
|
std::unordered_set<uint32> onEquipSpellIds;
|
||||||
|
std::unordered_set<uint32> mixedSpellIds;
|
||||||
|
|
||||||
|
auto const* itemTemplates = sObjectMgr->GetItemTemplateStore();
|
||||||
|
if (!itemTemplates)
|
||||||
|
return mixedSpellIds;
|
||||||
|
|
||||||
|
auto const markSpellId = [&](int32 spellId, uint8 spellTrigger)
|
||||||
|
{
|
||||||
|
if (spellId <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (spellTrigger == ITEM_SPELLTRIGGER_ON_USE)
|
||||||
|
{
|
||||||
|
if (onEquipSpellIds.find(spellId) != onEquipSpellIds.end())
|
||||||
|
mixedSpellIds.insert(spellId);
|
||||||
|
|
||||||
|
onUseSpellIds.insert(spellId);
|
||||||
}
|
}
|
||||||
|
else if (spellTrigger == ITEM_SPELLTRIGGER_ON_EQUIP)
|
||||||
|
{
|
||||||
|
if (onUseSpellIds.find(spellId) != onUseSpellIds.end())
|
||||||
|
mixedSpellIds.insert(spellId);
|
||||||
|
|
||||||
|
onEquipSpellIds.insert(spellId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto const& itr : *itemTemplates)
|
||||||
|
{
|
||||||
|
ItemTemplate const& proto = itr.second;
|
||||||
|
if (proto.InventoryType != INVTYPE_TRINKET)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (uint8 spellIndex = 0; spellIndex < MAX_ITEM_PROTO_SPELLS; ++spellIndex)
|
||||||
|
{
|
||||||
|
auto const& spellData = proto.Spells[spellIndex];
|
||||||
|
markSpellId(spellData.SpellId, spellData.SpellTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mixedSpellIds;
|
||||||
|
}();
|
||||||
|
|
||||||
|
return mixedTriggerSpellIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsManaRestoreEffect(SpellEffectInfo const& effectInfo)
|
||||||
|
{
|
||||||
|
return (effectInfo.Effect == SPELL_EFFECT_ENERGIZE &&
|
||||||
|
effectInfo.MiscValue == POWER_MANA) ||
|
||||||
|
(effectInfo.Effect == SPELL_EFFECT_APPLY_AURA &&
|
||||||
|
effectInfo.ApplyAuraName == SPELL_AURA_PERIODIC_ENERGIZE &&
|
||||||
|
effectInfo.MiscValue == POWER_MANA);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsManaEfficiencyEffect(SpellEffectInfo const& effectInfo)
|
||||||
|
{
|
||||||
|
return effectInfo.Effect == SPELL_EFFECT_APPLY_AURA &&
|
||||||
|
(((effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_REGEN ||
|
||||||
|
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_REGEN_PERCENT) &&
|
||||||
|
effectInfo.MiscValue == POWER_MANA) ||
|
||||||
|
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_COST_SCHOOL ||
|
||||||
|
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT ||
|
||||||
|
effectInfo.ApplyAuraName == SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDefensiveTankEffect(SpellEffectInfo const& effectInfo)
|
||||||
|
{
|
||||||
|
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 const tankRatingsMask =
|
||||||
|
(1u << CR_DEFENSE_SKILL) |
|
||||||
|
(1u << CR_DODGE) |
|
||||||
|
(1u << CR_PARRY) |
|
||||||
|
(1u << CR_BLOCK) |
|
||||||
|
(1u << CR_HIT_TAKEN_MELEE) |
|
||||||
|
(1u << CR_HIT_TAKEN_RANGED) |
|
||||||
|
(1u << CR_HIT_TAKEN_SPELL) |
|
||||||
|
(1u << CR_CRIT_TAKEN_MELEE) |
|
||||||
|
(1u << CR_CRIT_TAKEN_RANGED) |
|
||||||
|
(1u << CR_CRIT_TAKEN_SPELL);
|
||||||
|
|
||||||
|
switch (effectInfo.ApplyAuraName)
|
||||||
|
{
|
||||||
|
case SPELL_AURA_MOD_RESISTANCE:
|
||||||
|
return (effectInfo.MiscValue & SPELL_SCHOOL_MASK_NORMAL) != 0;
|
||||||
|
case SPELL_AURA_MOD_RATING:
|
||||||
|
return (effectInfo.MiscValue & tankRatingsMask) != 0;
|
||||||
|
case SPELL_AURA_MOD_INCREASE_HEALTH:
|
||||||
|
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
|
||||||
|
case SPELL_AURA_MOD_PARRY_PERCENT:
|
||||||
|
case SPELL_AURA_MOD_DODGE_PERCENT:
|
||||||
|
case SPELL_AURA_MOD_BLOCK_PERCENT:
|
||||||
|
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
|
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell) {}
|
||||||
|
|
||||||
bool CastSpellAction::Execute(Event /*event*/)
|
bool CastSpellAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -53,18 +162,12 @@ bool CastSpellAction::Execute(Event /*event*/)
|
|||||||
|
|
||||||
wstrToLower(wnamepart);
|
wstrToLower(wnamepart);
|
||||||
|
|
||||||
if (!Utf8FitTo(spell, wnamepart))
|
if (!Utf8FitTo(spell, wnamepart) || spellInfo->Effects[0].Effect != SPELL_EFFECT_CREATE_ITEM)
|
||||||
continue;
|
|
||||||
|
|
||||||
if (spellInfo->Effects[0].Effect != SPELL_EFFECT_CREATE_ITEM)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint32 itemId = spellInfo->Effects[0].ItemType;
|
uint32 itemId = spellInfo->Effects[0].ItemType;
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
if (!proto)
|
if (!proto || bot->CanUseItem(proto) != EQUIP_ERR_OK)
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->CanUseItem(proto) != EQUIP_ERR_OK)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (spellInfo->Id > castId)
|
if (spellInfo->Id > castId)
|
||||||
@ -92,10 +195,7 @@ bool CastSpellAction::isUseful()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Unit* spellTarget = GetTarget();
|
Unit* spellTarget = GetTarget();
|
||||||
if (!spellTarget)
|
if (!spellTarget || !spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId())
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId())
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// float combatReach = bot->GetCombatReach() + target->GetCombatReach();
|
// float combatReach = bot->GetCombatReach() + target->GetCombatReach();
|
||||||
@ -143,10 +243,7 @@ CastMeleeSpellAction::CastMeleeSpellAction(
|
|||||||
bool CastMeleeSpellAction::isUseful()
|
bool CastMeleeSpellAction::isUseful()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target || !bot->IsWithinMeleeRange(target))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!bot->IsWithinMeleeRange(target))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return CastSpellAction::isUseful();
|
return CastSpellAction::isUseful();
|
||||||
@ -162,10 +259,7 @@ CastMeleeDebuffSpellAction::CastMeleeDebuffSpellAction(
|
|||||||
bool CastMeleeDebuffSpellAction::isUseful()
|
bool CastMeleeDebuffSpellAction::isUseful()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target || !bot->IsWithinMeleeRange(target))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!bot->IsWithinMeleeRange(target))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return CastDebuffSpellAction::isUseful();
|
return CastDebuffSpellAction::isUseful();
|
||||||
@ -175,14 +269,55 @@ bool CastAuraSpellAction::isUseful()
|
|||||||
{
|
{
|
||||||
if (!GetTarget() || !CastSpellAction::isUseful())
|
if (!GetTarget() || !CastSpellAction::isUseful())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration);
|
Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration);
|
||||||
if (!aura)
|
if (!aura || (beforeDuration && aura->GetDuration() < beforeDuration))
|
||||||
return true;
|
|
||||||
if (beforeDuration && aura->GetDuration() < beforeDuration)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CastBuffSpellAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target || !CastSpellAction::isUseful())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Aura* aura = botAI->GetAura(spell, target, isOwner, checkDuration);
|
||||||
|
return !aura || (beforeDuration && aura->GetDuration() < beforeDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBuffSpellAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
return botAI->CastSpell(spell, GetTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupBuffSpellAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target || !CastSpellAction::isUseful())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ai::buff::IsGroupVariantEnabled(bot, spell))
|
||||||
|
{
|
||||||
|
std::string const groupVariant = ai::buff::GroupVariantFor(spell);
|
||||||
|
if (!groupVariant.empty() && botAI->HasAura(groupVariant, target, false, isOwner, -1, checkDuration))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aura* aura = botAI->GetAura(spell, target, isOwner, checkDuration);
|
||||||
|
if (!aura || (beforeDuration && aura->GetDuration() < beforeDuration))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupBuffSpellAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
std::string const castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, spell);
|
||||||
|
return botAI->CastSpell(castName, GetTarget());
|
||||||
|
}
|
||||||
|
|
||||||
CastEnchantItemMainHandAction::CastEnchantItemMainHandAction(
|
CastEnchantItemMainHandAction::CastEnchantItemMainHandAction(
|
||||||
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||||
|
|
||||||
@ -248,25 +383,16 @@ Value<Unit*>* CurePartyMemberAction::GetTargetValue()
|
|||||||
return context->GetValue<Unit*>("party member to dispel", dispelType);
|
return context->GetValue<Unit*>("party member to dispel", dispelType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make Bots Paladin, druid, mage use the greater buff rank spell
|
|
||||||
// TODO Priest doen't verify il he have components
|
|
||||||
Value<Unit*>* BuffOnPartyAction::GetTargetValue()
|
Value<Unit*>* BuffOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>("party member without aura", spell);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* GroupBuffOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", MakeAuraQualifierForBuff(spell));
|
return context->GetValue<Unit*>("party member without aura", MakeAuraQualifierForBuff(spell));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuffOnPartyAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
std::string castName = spell; // default = mono
|
|
||||||
|
|
||||||
auto SendGroupRP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(
|
|
||||||
bot, botAI, castName, /*announceOnMissing=*/true, SendGroupRP);
|
|
||||||
|
|
||||||
return botAI->CastSpell(castName, GetTarget());
|
|
||||||
}
|
|
||||||
// End greater buff fix
|
|
||||||
|
|
||||||
CastShootAction::CastShootAction(
|
CastShootAction::CastShootAction(
|
||||||
PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
||||||
{
|
{
|
||||||
@ -365,16 +491,7 @@ bool CastVehicleSpellAction::Execute(Event /*event*/)
|
|||||||
bool CastEveryManForHimselfAction::isPossible()
|
bool CastEveryManForHimselfAction::isPossible()
|
||||||
{
|
{
|
||||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||||
if (!spellId)
|
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!bot->HasSpell(spellId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (HasSpellOrCategoryCooldown(bot, spellId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastEveryManForHimselfAction::isUseful()
|
bool CastEveryManForHimselfAction::isUseful()
|
||||||
@ -390,16 +507,7 @@ bool CastEveryManForHimselfAction::isUseful()
|
|||||||
bool CastWillOfTheForsakenAction::isPossible()
|
bool CastWillOfTheForsakenAction::isPossible()
|
||||||
{
|
{
|
||||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||||
if (!spellId)
|
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!bot->HasSpell(spellId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (HasSpellOrCategoryCooldown(bot, spellId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastWillOfTheForsakenAction::isUseful()
|
bool CastWillOfTheForsakenAction::isUseful()
|
||||||
@ -427,70 +535,141 @@ bool UseTrinketAction::Execute(Event /*event*/)
|
|||||||
|
|
||||||
bool UseTrinketAction::UseTrinket(Item* item)
|
bool UseTrinketAction::UseTrinket(Item* item)
|
||||||
{
|
{
|
||||||
if (bot->CanUseItem(item) != EQUIP_ERR_OK)
|
if (bot->CanUseItem(item) != EQUIP_ERR_OK || bot->IsNonMeleeSpellCast(true))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (bot->IsNonMeleeSpellCast(true))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint8 bagIndex = item->GetBagSlot();
|
uint8 bagIndex = item->GetBagSlot();
|
||||||
uint8 slot = item->GetSlot();
|
uint8 slot = item->GetSlot();
|
||||||
// uint8 spell_index = 0; //not used, line marked for removal.
|
|
||||||
uint8 cast_count = 1;
|
uint8 cast_count = 1;
|
||||||
ObjectGuid item_guid = item->GetGUID();
|
ObjectGuid item_guid = item->GetGUID();
|
||||||
uint32 glyphIndex = 0;
|
uint32 glyphIndex = 0;
|
||||||
uint8 castFlags = 0;
|
uint8 castFlags = 0;
|
||||||
uint32 targetFlag = TARGET_FLAG_NONE;
|
uint32 targetFlag = TARGET_FLAG_NONE;
|
||||||
uint32 spellId = 0;
|
uint32 spellId = 0;
|
||||||
|
int32 itemSpellCooldown = 0;
|
||||||
|
uint32 itemSpellCategory = 0;
|
||||||
|
int32 itemSpellCategoryCooldown = 0;
|
||||||
|
|
||||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||||
{
|
{
|
||||||
if (item->GetTemplate()->Spells[i].SpellId > 0 &&
|
if (item->GetTemplate()->Spells[i].SpellId > 0 &&
|
||||||
item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
|
item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
|
||||||
{
|
{
|
||||||
spellId = item->GetTemplate()->Spells[i].SpellId;
|
spellId = item->GetTemplate()->Spells[i].SpellId;
|
||||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
itemSpellCooldown = item->GetTemplate()->Spells[i].SpellCooldown;
|
||||||
|
itemSpellCategory = item->GetTemplate()->Spells[i].SpellCategory;
|
||||||
|
itemSpellCategoryCooldown = item->GetTemplate()->Spells[i].SpellCategoryCooldown;
|
||||||
|
uint64 const itemCooldownKey = (static_cast<uint64>(item->GetEntry()) << 32) | spellId;
|
||||||
|
uint32 const now = getMSTime();
|
||||||
|
|
||||||
|
if (itemSpellCooldown > 0)
|
||||||
|
{
|
||||||
|
auto const itemCooldownItr = trinketItemCooldownExpiries.find(itemCooldownKey);
|
||||||
|
if (itemCooldownItr != trinketItemCooldownExpiries.end())
|
||||||
|
{
|
||||||
|
if (itemCooldownItr->second > now)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
trinketItemCooldownExpiries.erase(itemCooldownItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemSpellCategory && itemSpellCategoryCooldown > 0)
|
||||||
|
{
|
||||||
|
auto const categoryCooldownItr = trinketCategoryCooldownExpiries.find(itemSpellCategory);
|
||||||
|
if (categoryCooldownItr != trinketCategoryCooldownExpiries.end())
|
||||||
|
{
|
||||||
|
if (categoryCooldownItr->second > now)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
trinketCategoryCooldownExpiries.erase(categoryCooldownItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
if (!spellInfo || !spellInfo->IsPositive())
|
if (!spellInfo || !spellInfo->IsPositive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool applyAura = false;
|
bool applyAura = false;
|
||||||
|
bool restoresMana = false;
|
||||||
|
bool improvesManaEfficiency = false;
|
||||||
|
bool defensiveTankEffect = false;
|
||||||
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
||||||
{
|
{
|
||||||
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
|
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
|
||||||
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA)
|
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA)
|
||||||
{
|
|
||||||
applyAura = true;
|
applyAura = true;
|
||||||
break;
|
|
||||||
}
|
restoresMana = restoresMana || IsManaRestoreEffect(effectInfo);
|
||||||
|
improvesManaEfficiency = improvesManaEfficiency || IsManaEfficiencyEffect(effectInfo);
|
||||||
|
defensiveTankEffect = defensiveTankEffect || IsDefensiveTankEffect(effectInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!applyAura)
|
if (!applyAura && !restoresMana)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 spellProcFlag = spellInfo->ProcFlags;
|
if (restoresMana || improvesManaEfficiency)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(bool, "has mana", "self target"))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Handle items with procflag "if you kill a target that grants honor or experience"
|
uint8 const manaPct = AI_VALUE2(uint8, "mana", "self target");
|
||||||
// Bots will "learn" the trinket proc, so CanCastSpell() will be true
|
if ((restoresMana && manaPct >= sPlayerbotAIConfig.mediumMana) ||
|
||||||
// e.g. on Item https://www.wowhead.com/wotlk/item=44074/oracle-talisman-of-ablution leading to
|
manaPct >= sPlayerbotAIConfig.highMana)
|
||||||
// constant casting of the proc spell onto themselfes https://www.wowhead.com/wotlk/spell=59787/oracle-ablutions
|
|
||||||
// This will lead to multiple hundreds of entries in m_appliedAuras -> Once killing an enemy -> Big diff time spikes
|
|
||||||
if (spellProcFlag != 0) return false;
|
|
||||||
|
|
||||||
if (!botAI->CanCastSpell(spellId, bot, false))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defensiveTankEffect)
|
||||||
|
{
|
||||||
|
uint8 const healthPct = AI_VALUE2(uint8, "health", "self target");
|
||||||
|
if (healthPct > sPlayerbotAIConfig.lowHealth)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& mixedTriggerTrinketSpellIds = GetMixedTriggerTrinketSpellIds();
|
||||||
|
// Exclude trinkets that expose the same spell as both ON_EQUIP and ON_USE across
|
||||||
|
// item templates. Those are equip/proc effects leaking into the active-use path,
|
||||||
|
// as seen with the error versions of Oracle Talisman of Ablution (44870) and
|
||||||
|
// Frenzyheart Insignia of Fury (44869).
|
||||||
|
if (mixedTriggerTrinketSpellIds.find(spellId) != mixedTriggerTrinketSpellIds.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->CanCastSpell(spellId, bot, false, nullptr, item))
|
||||||
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spellId)
|
if (!spellId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorldPacket packet(CMSG_USE_ITEM);
|
WorldPacket packet(CMSG_USE_ITEM);
|
||||||
packet << bagIndex << slot << cast_count << spellId << item_guid << glyphIndex << castFlags;
|
packet << bagIndex << slot << cast_count << spellId << item_guid << glyphIndex << castFlags;
|
||||||
|
|
||||||
targetFlag = TARGET_FLAG_NONE;
|
targetFlag = TARGET_FLAG_NONE;
|
||||||
packet << targetFlag << bot->GetPackGUID();
|
packet << targetFlag << bot->GetPackGUID();
|
||||||
|
|
||||||
bot->GetSession()->HandleUseItemOpcode(packet);
|
bot->GetSession()->HandleUseItemOpcode(packet);
|
||||||
|
|
||||||
|
uint32 const now = getMSTime();
|
||||||
|
uint32 const cooldownDelay = bot->GetSpellCooldownDelay(spellId);
|
||||||
|
if (cooldownDelay > 0)
|
||||||
|
{
|
||||||
|
if (itemSpellCooldown > 0)
|
||||||
|
{
|
||||||
|
uint64 const itemCooldownKey = (static_cast<uint64>(item->GetEntry()) << 32) | spellId;
|
||||||
|
trinketItemCooldownExpiries[itemCooldownKey] = now + static_cast<uint32>(itemSpellCooldown);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemSpellCategory && itemSpellCategoryCooldown > 0)
|
||||||
|
{
|
||||||
|
trinketCategoryCooldownExpiries[itemSpellCategory] = now + static_cast<uint32>(itemSpellCategoryCooldown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,9 +679,8 @@ bool CastDebuffSpellAction::isUseful()
|
|||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target || !target->IsAlive() || !target->IsInWorld())
|
if (!target || !target->IsAlive() || !target->IsInWorld())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return CastAuraSpellAction::isUseful() &&
|
return CastAuraSpellAction::isUseful() &&
|
||||||
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,9 +69,7 @@ class CastDebuffSpellAction : public CastAuraSpellAction
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastDebuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, float needLifeTime = 8.0f)
|
CastDebuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, float needLifeTime = 8.0f)
|
||||||
: CastAuraSpellAction(botAI, spell, isOwner), needLifeTime(needLifeTime)
|
: CastAuraSpellAction(botAI, spell, isOwner), needLifeTime(needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -90,9 +88,7 @@ class CastDebuffSpellOnAttackerAction : public CastDebuffSpellAction
|
|||||||
public:
|
public:
|
||||||
CastDebuffSpellOnAttackerAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = true,
|
CastDebuffSpellOnAttackerAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = true,
|
||||||
float needLifeTime = 8.0f)
|
float needLifeTime = 8.0f)
|
||||||
: CastDebuffSpellAction(botAI, spell, isOwner, needLifeTime)
|
: CastDebuffSpellAction(botAI, spell, isOwner, needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
std::string const getName() override { return spell + " on attacker"; }
|
std::string const getName() override { return spell + " on attacker"; }
|
||||||
@ -104,9 +100,7 @@ class CastDebuffSpellOnMeleeAttackerAction : public CastDebuffSpellAction
|
|||||||
public:
|
public:
|
||||||
CastDebuffSpellOnMeleeAttackerAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = true,
|
CastDebuffSpellOnMeleeAttackerAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = true,
|
||||||
float needLifeTime = 8.0f)
|
float needLifeTime = 8.0f)
|
||||||
: CastDebuffSpellAction(botAI, spell, isOwner, needLifeTime)
|
: CastDebuffSpellAction(botAI, spell, isOwner, needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
std::string const getName() override { return spell + " on attacker"; }
|
std::string const getName() override { return spell + " on attacker"; }
|
||||||
@ -119,6 +113,19 @@ public:
|
|||||||
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false, uint32 beforeDuration = 0);
|
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false, uint32 beforeDuration = 0);
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
|
bool isUseful() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GroupBuffSpellAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GroupBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false,
|
||||||
|
uint32 beforeDuration = 0)
|
||||||
|
: CastBuffSpellAction(botAI, spell, checkIsOwner, beforeDuration) {}
|
||||||
|
|
||||||
|
bool isUseful() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastEnchantItemMainHandAction : public CastSpellAction
|
class CastEnchantItemMainHandAction : public CastSpellAction
|
||||||
@ -151,8 +158,6 @@ public:
|
|||||||
// Yunfan: Mana efficiency tell the bot how to save mana. The higher the better.
|
// Yunfan: Mana efficiency tell the bot how to save mana. The higher the better.
|
||||||
HealingManaEfficiency manaEfficiency;
|
HealingManaEfficiency manaEfficiency;
|
||||||
uint8 estAmount;
|
uint8 estAmount;
|
||||||
|
|
||||||
// protected:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastAoeHealSpellAction : public CastHealingSpellAction
|
class CastAoeHealSpellAction : public CastHealingSpellAction
|
||||||
@ -192,9 +197,7 @@ class HealPartyMemberAction : public CastHealingSpellAction, public PartyMemberA
|
|||||||
public:
|
public:
|
||||||
HealPartyMemberAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount = 15.0f,
|
HealPartyMemberAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount = 15.0f,
|
||||||
HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM, bool isOwner = true)
|
HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM, bool isOwner = true)
|
||||||
: CastHealingSpellAction(botAI, spell, estAmount, manaEfficiency, isOwner), PartyMemberActionNameSupport(spell)
|
: CastHealingSpellAction(botAI, spell, estAmount, manaEfficiency, isOwner), PartyMemberActionNameSupport(spell) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "party member to heal"; }
|
std::string const GetTargetName() override { return "party member to heal"; }
|
||||||
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
||||||
@ -219,9 +222,7 @@ class CurePartyMemberAction : public CastSpellAction, public PartyMemberActionNa
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CurePartyMemberAction(PlayerbotAI* botAI, std::string const spell, uint32 dispelType)
|
CurePartyMemberAction(PlayerbotAI* botAI, std::string const spell, uint32 dispelType)
|
||||||
: CastSpellAction(botAI, spell), PartyMemberActionNameSupport(spell), dispelType(dispelType)
|
: CastSpellAction(botAI, spell), PartyMemberActionNameSupport(spell), dispelType(dispelType) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
||||||
@ -230,7 +231,6 @@ protected:
|
|||||||
uint32 dispelType;
|
uint32 dispelType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make Bots Paladin, druid, mage use the greater buff rank spell
|
|
||||||
class BuffOnPartyAction : public CastBuffSpellAction, public PartyMemberActionNameSupport
|
class BuffOnPartyAction : public CastBuffSpellAction, public PartyMemberActionNameSupport
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -238,10 +238,18 @@ public:
|
|||||||
: CastBuffSpellAction(botAI, spell), PartyMemberActionNameSupport(spell) {}
|
: CastBuffSpellAction(botAI, spell), PartyMemberActionNameSupport(spell) {}
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
bool Execute(Event event) override;
|
|
||||||
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
||||||
};
|
};
|
||||||
// End Fix
|
|
||||||
|
class GroupBuffOnPartyAction : public GroupBuffSpellAction, public PartyMemberActionNameSupport
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GroupBuffOnPartyAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
|
: GroupBuffSpellAction(botAI, spell), PartyMemberActionNameSupport(spell) {}
|
||||||
|
|
||||||
|
Value<Unit*>* GetTargetValue() override;
|
||||||
|
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
||||||
|
};
|
||||||
|
|
||||||
class CastShootAction : public CastSpellAction
|
class CastShootAction : public CastSpellAction
|
||||||
{
|
{
|
||||||
@ -323,8 +331,13 @@ class UseTrinketAction : public Action
|
|||||||
public:
|
public:
|
||||||
UseTrinketAction(PlayerbotAI* botAI) : Action(botAI, "use trinket") {}
|
UseTrinketAction(PlayerbotAI* botAI) : Action(botAI, "use trinket") {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool UseTrinket(Item* trinket);
|
bool UseTrinket(Item* trinket);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<uint64, uint32> trinketItemCooldownExpiries;
|
||||||
|
std::unordered_map<uint32, uint32> trinketCategoryCooldownExpiries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastSpellOnEnemyHealerAction : public CastSpellAction
|
class CastSpellOnEnemyHealerAction : public CastSpellAction
|
||||||
@ -461,12 +474,11 @@ class BuffOnMainTankAction : public CastBuffSpellAction, public MainTankActionNa
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffOnMainTankAction(PlayerbotAI* ai, std::string spell, bool checkIsOwner = false)
|
BuffOnMainTankAction(PlayerbotAI* ai, std::string spell, bool checkIsOwner = false)
|
||||||
: CastBuffSpellAction(ai, spell, checkIsOwner), MainTankActionNameSupport(spell)
|
: CastBuffSpellAction(ai, spell, checkIsOwner), MainTankActionNameSupport(spell) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Value<Unit*>* GetTargetValue();
|
virtual Value<Unit*>* GetTargetValue();
|
||||||
virtual std::string const getName() { return MainTankActionNameSupport::getName(); }
|
virtual std::string const getName() { return MainTankActionNameSupport::getName(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -44,6 +44,21 @@ bool ResetAiAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Player* master = botAI->GetMaster())
|
||||||
|
{
|
||||||
|
Group* botGroup = bot->GetGroup();
|
||||||
|
Group* masterGroup = master->GetGroup();
|
||||||
|
if (botGroup && (!masterGroup || masterGroup != botGroup))
|
||||||
|
botAI->SetMaster(nullptr);
|
||||||
|
}
|
||||||
|
if (sRandomPlayerbotMgr.IsRandomBot(bot) && !bot->InBattleground())
|
||||||
|
{
|
||||||
|
if (bot->GetGroup() && (!botAI->GetMaster() || GET_PLAYERBOT_AI(botAI->GetMaster())))
|
||||||
|
{
|
||||||
|
if (Player* newMaster = botAI->FindNewMaster())
|
||||||
|
botAI->SetMaster(newMaster);
|
||||||
|
}
|
||||||
|
}
|
||||||
PlayerbotRepository::instance().Reset(botAI);
|
PlayerbotRepository::instance().Reset(botAI);
|
||||||
botAI->ResetStrategies(false);
|
botAI->ResetStrategies(false);
|
||||||
botAI->TellMaster("AI was reset to defaults");
|
botAI->TellMaster("AI was reset to defaults");
|
||||||
|
|||||||
@ -34,24 +34,23 @@ bool SendMailAction::Execute(Event event)
|
|||||||
Player* receiver = GetMaster();
|
Player* receiver = GetMaster();
|
||||||
Player* tellTo = receiver;
|
Player* tellTo = receiver;
|
||||||
|
|
||||||
std::vector<std::string> ss = split(text, ' ');
|
|
||||||
if (ss.size() > 1)
|
|
||||||
{
|
|
||||||
if (Player* p = ObjectAccessor::FindPlayer(ObjectGuid(uint64(ss[ss.size() - 1].c_str()))))
|
|
||||||
receiver = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!receiver)
|
if (!receiver)
|
||||||
receiver = event.getOwner();
|
receiver = event.getOwner();
|
||||||
|
|
||||||
if (!receiver || receiver == bot)
|
if (!receiver || receiver == bot)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!tellTo)
|
if (!tellTo)
|
||||||
tellTo = receiver;
|
tellTo = receiver;
|
||||||
|
|
||||||
|
if (!sPlayerbotAIConfig.botSendMailEnabled)
|
||||||
|
{
|
||||||
|
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"send_mail_disabled", "I cannot send mail", {}),
|
||||||
|
LANG_UNIVERSAL, tellTo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mailboxFound && !randomBot)
|
if (!mailboxFound && !randomBot)
|
||||||
{
|
{
|
||||||
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
|||||||
@ -16,7 +16,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) }));
|
new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("group set leader", { /*NextAction("leader", relevance),*/ }));
|
new TriggerNode("group set leader", { NextAction("reset botAI", relevance) }));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"not enough money", { NextAction("tell not enough money", relevance) }));
|
"not enough money", { NextAction("tell not enough money", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
#include "CreatureAI.h"
|
#include "CreatureAI.h"
|
||||||
#include "ItemVisitors.h"
|
#include "ItemVisitors.h"
|
||||||
#include "LastSpellCastValue.h"
|
#include "LastSpellCastValue.h"
|
||||||
@ -41,52 +42,50 @@ bool LowEnergyTrigger::IsActive()
|
|||||||
|
|
||||||
bool NoPetTrigger::IsActive()
|
bool NoPetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return (bot->GetMinionGUID().IsEmpty()) && (!AI_VALUE(Unit*, "pet target")) && (!bot->GetGuardianPet()) &&
|
return bot->GetMinionGUID().IsEmpty() && !AI_VALUE(Unit*, "pet target") && !bot->GetGuardianPet() &&
|
||||||
(!bot->GetFirstControlled()) && (!AI_VALUE2(bool, "mounted", "self target"));
|
!bot->GetFirstControlled() && !AI_VALUE2(bool, "mounted", "self target");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasPetTrigger::IsActive()
|
bool HasPetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return (AI_VALUE(Unit*, "pet target")) && !AI_VALUE2(bool, "mounted", "self target");
|
return AI_VALUE(Unit*, "pet target") && !AI_VALUE2(bool, "mounted", "self target");
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PetAttackTrigger::IsActive()
|
bool PetAttackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Guardian* pet = bot->GetGuardianPet();
|
Guardian* pet = bot->GetGuardianPet();
|
||||||
if (!pet)
|
if (!pet)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (!target)
|
if (!target)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (pet->GetVictim() == target && pet->GetCharmInfo()->IsCommandAttack())
|
if (pet->GetVictim() == target && pet->GetCharmInfo()->IsCommandAttack())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighManaTrigger::IsActive()
|
bool HighManaTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.highMana;
|
return AI_VALUE2(bool, "has mana", "self target") &&
|
||||||
|
AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.highMana;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlmostFullManaTrigger::IsActive()
|
bool AlmostFullManaTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 85;
|
return AI_VALUE2(bool, "has mana", "self target") &&
|
||||||
|
AI_VALUE2(uint8, "mana", "self target") > 85;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnoughManaTrigger::IsActive()
|
bool EnoughManaTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.highMana;
|
return AI_VALUE2(bool, "has mana", "self target") &&
|
||||||
|
AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.highMana;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RageAvailable::IsActive() { return AI_VALUE2(uint8, "rage", "self target") >= amount; }
|
bool RageAvailable::IsActive() { return AI_VALUE2(uint8, "rage", "self target") >= amount; }
|
||||||
@ -101,9 +100,8 @@ bool TargetWithComboPointsLowerHealTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (!target || !target->IsAlive() || !target->IsInWorld())
|
if (!target || !target->IsAlive() || !target->IsInWorld())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return ComboPointsAvailableTrigger::IsActive() &&
|
return ComboPointsAvailableTrigger::IsActive() &&
|
||||||
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime;
|
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime;
|
||||||
}
|
}
|
||||||
@ -164,19 +162,27 @@ bool BuffTrigger::IsActive()
|
|||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
if (!SpellTrigger::IsActive())
|
|
||||||
return false;
|
|
||||||
Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration);
|
Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration);
|
||||||
if (!aura)
|
if (!aura || (beforeDuration && aura->GetDuration() < beforeDuration))
|
||||||
return true;
|
|
||||||
if (beforeDuration && aura->GetDuration() < beforeDuration)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()
|
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", spell);
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura", ai::buff::MakeAuraQualifierForBuff(spell));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuffOnPartyTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (ai::buff::ShouldDeferPartyBuffEvaluationForRecentLogin(bot, target, spell))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return BuffTrigger::IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProtectPartyMemberTrigger::IsActive() { return AI_VALUE(Unit*, "party member to protect"); }
|
bool ProtectPartyMemberTrigger::IsActive() { return AI_VALUE(Unit*, "party member to protect"); }
|
||||||
@ -209,13 +215,14 @@ bool MediumThreatTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
if (!AI_VALUE(Unit*, "main tank"))
|
if (!AI_VALUE(Unit*, "main tank"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return MyAttackerCountTrigger::IsActive();
|
return MyAttackerCountTrigger::IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LowTankThreatTrigger::IsActive()
|
bool LowTankThreatTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* mt = AI_VALUE(Unit*, "main tank");
|
Unit* mainTank = AI_VALUE(Unit*, "main tank");
|
||||||
if (!mt)
|
if (!mainTank)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||||
@ -224,7 +231,7 @@ bool LowTankThreatTrigger::IsActive()
|
|||||||
|
|
||||||
ThreatManager& mgr = current_target->GetThreatMgr();
|
ThreatManager& mgr = current_target->GetThreatMgr();
|
||||||
float threat = mgr.GetThreat(bot);
|
float threat = mgr.GetThreat(bot);
|
||||||
float tankThreat = mgr.GetThreat(mt);
|
float tankThreat = mgr.GetThreat(mainTank);
|
||||||
return tankThreat == 0.0f || threat > tankThreat * 0.5f;
|
return tankThreat == 0.0f || threat > tankThreat * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,9 +239,8 @@ bool AoeTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||||
if (!current_target)
|
if (!current_target)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||||
int attackers_count = 0;
|
int attackers_count = 0;
|
||||||
for (ObjectGuid const guid : attackers)
|
for (ObjectGuid const guid : attackers)
|
||||||
@ -242,11 +248,10 @@ bool AoeTrigger::IsActive()
|
|||||||
Unit* unit = botAI->GetUnit(guid);
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
if (!unit || !unit->IsAlive())
|
if (!unit || !unit->IsAlive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (unit->GetDistance(current_target->GetPosition()) <= range)
|
if (unit->GetDistance(current_target->GetPosition()) <= range)
|
||||||
{
|
|
||||||
attackers_count++;
|
attackers_count++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return attackers_count >= amount;
|
return attackers_count >= amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,20 +279,19 @@ bool DebuffTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target || !target->IsAlive() || !target->IsInWorld())
|
if (!target || !target->IsAlive() || !target->IsInWorld())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
return BuffTrigger::IsActive() &&
|
||||||
|
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DebuffOnBossTrigger::IsActive()
|
bool DebuffOnBossTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!DebuffTrigger::IsActive())
|
if (!DebuffTrigger::IsActive())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
Creature* c = GetTarget()->ToCreature();
|
Creature* creature = GetTarget()->ToCreature();
|
||||||
return c && ((c->IsDungeonBoss()) || (c->isWorldBoss()));
|
return creature && (creature->IsDungeonBoss() || creature->isWorldBoss());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpellTrigger::IsActive() { return GetTarget(); }
|
bool SpellTrigger::IsActive() { return GetTarget(); }
|
||||||
@ -317,9 +321,7 @@ bool SpellCooldownTrigger::IsActive()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RandomTrigger::RandomTrigger(PlayerbotAI* botAI, std::string const name, int32 probability)
|
RandomTrigger::RandomTrigger(PlayerbotAI* botAI, std::string const name, int32 probability)
|
||||||
: Trigger(botAI, name), probability(probability), lastCheck(getMSTime())
|
: Trigger(botAI, name), probability(probability), lastCheck(getMSTime()) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RandomTrigger::IsActive()
|
bool RandomTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@ -330,6 +332,7 @@ bool RandomTrigger::IsActive()
|
|||||||
int32 k = (int32)(probability / sPlayerbotAIConfig.randomChangeMultiplier);
|
int32 k = (int32)(probability / sPlayerbotAIConfig.randomChangeMultiplier);
|
||||||
if (k < 1)
|
if (k < 1)
|
||||||
k = 1;
|
k = 1;
|
||||||
|
|
||||||
return (rand() % k) == 0;
|
return (rand() % k) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,9 +371,11 @@ bool BoostTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
if (!BuffTrigger::IsActive())
|
if (!BuffTrigger::IsActive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (target && target->ToPlayer())
|
if (target && target->ToPlayer())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return AI_VALUE(uint8, "balance") <= balance;
|
return AI_VALUE(uint8, "balance") <= balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,20 +384,19 @@ bool GenericBoostTrigger::IsActive()
|
|||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (target && target->ToPlayer())
|
if (target && target->ToPlayer())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return AI_VALUE(uint8, "balance") <= balance;
|
return AI_VALUE(uint8, "balance") <= balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HealerShouldAttackTrigger::IsActive()
|
bool HealerShouldAttackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
// nobody can help me
|
|
||||||
if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig.sightDistance) <= 1)
|
if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig.sightDistance) <= 1)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (AI_VALUE2(uint8, "health", "party member to heal") < sPlayerbotAIConfig.almostFullHealth)
|
if (AI_VALUE2(uint8, "health", "party member to heal") < sPlayerbotAIConfig.almostFullHealth)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// special check for resto druid (dont remove tree of life frequently)
|
if (bot->GetAura(33891)) // Tree of Life
|
||||||
if (bot->GetAura(33891))
|
|
||||||
{
|
{
|
||||||
LastSpellCast& lastSpell = botAI->GetAiObjectContext()->GetValue<LastSpellCast&>("last spell cast")->Get();
|
LastSpellCast& lastSpell = botAI->GetAiObjectContext()->GetValue<LastSpellCast&>("last spell cast")->Get();
|
||||||
if (lastSpell.timer + 5 > time(nullptr))
|
if (lastSpell.timer + 5 > time(nullptr))
|
||||||
@ -401,7 +405,6 @@ bool HealerShouldAttackTrigger::IsActive()
|
|||||||
|
|
||||||
int manaThreshold;
|
int manaThreshold;
|
||||||
int balance = AI_VALUE(uint8, "balance");
|
int balance = AI_VALUE(uint8, "balance");
|
||||||
// higher threshold in higher pressure
|
|
||||||
if (balance <= 50)
|
if (balance <= 50)
|
||||||
manaThreshold = 85;
|
manaThreshold = 85;
|
||||||
else if (balance <= 100)
|
else if (balance <= 100)
|
||||||
@ -425,13 +428,7 @@ bool InterruptSpellTrigger::IsActive()
|
|||||||
bool DeflectSpellTrigger::IsActive()
|
bool DeflectSpellTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target || !target->IsNonMeleeSpellCast(true) || target->GetTarget() != bot->GetGUID())
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!target->IsNonMeleeSpellCast(true))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (target->GetTarget() != bot->GetGUID())
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 spellid = context->GetValue<uint32>("spell id", spell)->Get();
|
uint32 spellid = context->GetValue<uint32>("spell id", spell)->Get();
|
||||||
@ -462,6 +459,7 @@ bool DeflectSpellTrigger::IsActive()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,17 +493,16 @@ bool FearSleepSapTrigger::IsActive()
|
|||||||
|
|
||||||
bool HasAuraStackTrigger::IsActive()
|
bool HasAuraStackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
return botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
||||||
// sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "HasAuraStackTrigger::IsActive %s %d", getName(), aura ?
|
|
||||||
// aura->GetStackAmount() : -1);
|
|
||||||
return aura;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimerTrigger::IsActive()
|
bool TimerTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (time(nullptr) != lastCheck)
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
|
if (now != lastCheck)
|
||||||
{
|
{
|
||||||
lastCheck = time(nullptr);
|
lastCheck = now;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,9 +549,8 @@ bool IsBehindTargetTrigger::IsActive()
|
|||||||
bool IsNotBehindTargetTrigger::IsActive()
|
bool IsNotBehindTargetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
return target && !AI_VALUE2(bool, "behind", "current target");
|
return target && !AI_VALUE2(bool, "behind", "current target");
|
||||||
}
|
}
|
||||||
@ -562,9 +558,8 @@ bool IsNotBehindTargetTrigger::IsActive()
|
|||||||
bool IsNotFacingTargetTrigger::IsActive()
|
bool IsNotFacingTargetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return !AI_VALUE2(bool, "facing", "current target");
|
return !AI_VALUE2(bool, "facing", "current target");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,12 +576,14 @@ bool NoPossibleTargetsTrigger::IsActive()
|
|||||||
return !targets.size();
|
return !targets.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PossibleAddsTrigger::IsActive() { return AI_VALUE(bool, "possible adds") && !AI_VALUE(ObjectGuid, "pull target"); }
|
bool PossibleAddsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE(bool, "possible adds") && !AI_VALUE(ObjectGuid, "pull target");
|
||||||
|
}
|
||||||
|
|
||||||
bool NotDpsTargetActiveTrigger::IsActive()
|
bool NotDpsTargetActiveTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
// do not switch if enemy target
|
|
||||||
if (target && target->IsAlive())
|
if (target && target->IsAlive())
|
||||||
{
|
{
|
||||||
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
||||||
@ -604,7 +601,6 @@ bool NotDpsAoeTargetActiveTrigger::IsActive()
|
|||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
||||||
|
|
||||||
// do not switch if enemy target
|
|
||||||
if (target && target == enemy && target->IsAlive())
|
if (target && target == enemy && target->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -638,7 +634,10 @@ Value<Unit*>* InterruptEnemyHealerTrigger::GetTargetValue()
|
|||||||
return context->GetValue<Unit*>("enemy healer target", spell);
|
return context->GetValue<Unit*>("enemy healer target", spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RandomBotUpdateTrigger::IsActive() { return RandomTrigger::IsActive() && AI_VALUE(bool, "random bot update"); }
|
bool RandomBotUpdateTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return RandomTrigger::IsActive() && AI_VALUE(bool, "random bot update");
|
||||||
|
}
|
||||||
|
|
||||||
bool NoNonBotPlayersAroundTrigger::IsActive()
|
bool NoNonBotPlayersAroundTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@ -718,43 +717,24 @@ bool AmmoCountTrigger::IsActive()
|
|||||||
|
|
||||||
bool NewPetTrigger::IsActive()
|
bool NewPetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
// Get the bot player object from the AI
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
if (!bot)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Try to get the current pet; initialize guardian and GUID to null/empty
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
Guardian* guardian = nullptr;
|
|
||||||
ObjectGuid currentPetGuid = ObjectGuid::Empty;
|
ObjectGuid currentPetGuid = ObjectGuid::Empty;
|
||||||
|
|
||||||
// If bot has a pet, get its GUID
|
if (Pet* pet = bot->GetPet())
|
||||||
if (pet)
|
|
||||||
{
|
|
||||||
currentPetGuid = pet->GetGUID();
|
currentPetGuid = pet->GetGUID();
|
||||||
}
|
else if (Guardian* guardian = bot->GetGuardianPet())
|
||||||
else
|
|
||||||
{
|
|
||||||
// If no pet, try to get a guardian pet and its GUID
|
|
||||||
guardian = bot->GetGuardianPet();
|
|
||||||
if (guardian)
|
|
||||||
currentPetGuid = guardian->GetGUID();
|
currentPetGuid = guardian->GetGUID();
|
||||||
}
|
|
||||||
|
|
||||||
// If the current pet or guardian GUID has changed (including becoming empty), reset the trigger state
|
|
||||||
if (currentPetGuid != lastPetGuid)
|
if (currentPetGuid != lastPetGuid)
|
||||||
{
|
{
|
||||||
triggered = false;
|
triggered = false;
|
||||||
lastPetGuid = currentPetGuid;
|
lastPetGuid = currentPetGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's a valid current pet/guardian (non-empty GUID) and we haven't triggered yet, activate trigger
|
|
||||||
if (currentPetGuid != ObjectGuid::Empty && !triggered)
|
if (currentPetGuid != ObjectGuid::Empty && !triggered)
|
||||||
{
|
{
|
||||||
triggered = true;
|
triggered = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, do not activate
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,9 +20,7 @@ class StatAvailable : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StatAvailable(PlayerbotAI* botAI, int32 amount, std::string const name = "stat available")
|
StatAvailable(PlayerbotAI* botAI, int32 amount, std::string const name = "stat available")
|
||||||
: Trigger(botAI, name), amount(amount)
|
: Trigger(botAI, name), amount(amount) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int32 amount;
|
int32 amount;
|
||||||
@ -118,8 +116,8 @@ public:
|
|||||||
class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger
|
class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f)
|
TargetWithComboPointsLowerHealTrigger(PlayerbotAI* botAI, int32 combo_point = 5, float lifeTime = 8.0f)
|
||||||
: ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime)
|
: ComboPointsAvailableTrigger(botAI, combo_point), lifeTime(lifeTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
@ -196,7 +194,6 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: check other targets
|
|
||||||
class InterruptSpellTrigger : public SpellTrigger
|
class InterruptSpellTrigger : public SpellTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -217,9 +214,7 @@ class AttackerCountTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AttackerCountTrigger(PlayerbotAI* botAI, int32 amount, float distance = sPlayerbotAIConfig.sightDistance)
|
AttackerCountTrigger(PlayerbotAI* botAI, int32 amount, float distance = sPlayerbotAIConfig.sightDistance)
|
||||||
: Trigger(botAI), amount(amount), distance(distance)
|
: Trigger(botAI), amount(amount), distance(distance) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
std::string const getName() override { return "attacker count"; }
|
std::string const getName() override { return "attacker count"; }
|
||||||
@ -269,9 +264,7 @@ class AoeTrigger : public AttackerCountTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AoeTrigger(PlayerbotAI* botAI, int32 amount = 3, float range = 15.0f)
|
AoeTrigger(PlayerbotAI* botAI, int32 amount = 3, float range = 15.0f)
|
||||||
: AttackerCountTrigger(botAI, amount), range(range)
|
: AttackerCountTrigger(botAI, amount), range(range) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
std::string const getName() override { return "aoe"; }
|
std::string const getName() override { return "aoe"; }
|
||||||
@ -317,7 +310,8 @@ public:
|
|||||||
class BuffTrigger : public SpellTrigger
|
class BuffTrigger : public SpellTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
|
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1,
|
||||||
|
bool checkIsOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
|
||||||
: SpellTrigger(botAI, spell, checkInterval)
|
: SpellTrigger(botAI, spell, checkInterval)
|
||||||
{
|
{
|
||||||
this->checkIsOwner = checkIsOwner;
|
this->checkIsOwner = checkIsOwner;
|
||||||
@ -339,11 +333,10 @@ class BuffOnPartyTrigger : public BuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffOnPartyTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1)
|
BuffOnPartyTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1)
|
||||||
: BuffTrigger(botAI, spell, checkInterval)
|
: BuffTrigger(botAI, spell, checkInterval) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
|
bool IsActive() override;
|
||||||
std::string const getName() override { return spell + " on party"; }
|
std::string const getName() override { return spell + " on party"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -393,9 +386,7 @@ class DebuffTrigger : public BuffTrigger
|
|||||||
public:
|
public:
|
||||||
DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false,
|
DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false,
|
||||||
float needLifeTime = 8.0f, uint32 beforeDuration = 0)
|
float needLifeTime = 8.0f, uint32 beforeDuration = 0)
|
||||||
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner, false, beforeDuration), needLifeTime(needLifeTime)
|
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner, false, beforeDuration), needLifeTime(needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "current target"; }
|
std::string const GetTargetName() override { return "current target"; }
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
@ -408,9 +399,7 @@ class DebuffOnBossTrigger : public DebuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DebuffOnBossTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false)
|
DebuffOnBossTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false)
|
||||||
: DebuffTrigger(botAI, spell, checkInterval, checkIsOwner)
|
: DebuffTrigger(botAI, spell, checkInterval, checkIsOwner) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -419,9 +408,7 @@ class DebuffOnAttackerTrigger : public DebuffTrigger
|
|||||||
public:
|
public:
|
||||||
DebuffOnAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true,
|
DebuffOnAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true,
|
||||||
float needLifeTime = 8.0f)
|
float needLifeTime = 8.0f)
|
||||||
: DebuffTrigger(botAI, spell, 1, checkIsOwner, needLifeTime)
|
: DebuffTrigger(botAI, spell, 1, checkIsOwner, needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
std::string const getName() override { return spell + " on attacker"; }
|
std::string const getName() override { return spell + " on attacker"; }
|
||||||
@ -432,9 +419,7 @@ class DebuffOnMeleeAttackerTrigger : public DebuffTrigger
|
|||||||
public:
|
public:
|
||||||
DebuffOnMeleeAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true,
|
DebuffOnMeleeAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true,
|
||||||
float needLifeTime = 8.0f)
|
float needLifeTime = 8.0f)
|
||||||
: DebuffTrigger(botAI, spell, 1, checkIsOwner, needLifeTime)
|
: DebuffTrigger(botAI, spell, 1, checkIsOwner, needLifeTime) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
std::string const getName() override { return spell + " on attacker"; }
|
std::string const getName() override { return spell + " on attacker"; }
|
||||||
@ -444,9 +429,7 @@ class BoostTrigger : public BuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BoostTrigger(PlayerbotAI* botAI, std::string const spell, float balance = 50.f)
|
BoostTrigger(PlayerbotAI* botAI, std::string const spell, float balance = 50.f)
|
||||||
: BuffTrigger(botAI, spell, 1), balance(balance)
|
: BuffTrigger(botAI, spell, 1), balance(balance) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
@ -458,9 +441,7 @@ class GenericBoostTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GenericBoostTrigger(PlayerbotAI* botAI, float balance = 50.f)
|
GenericBoostTrigger(PlayerbotAI* botAI, float balance = 50.f)
|
||||||
: Trigger(botAI, "generic boost", 1), balance(balance)
|
: Trigger(botAI, "generic boost", 1), balance(balance) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
@ -472,9 +453,7 @@ class HealerShouldAttackTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HealerShouldAttackTrigger(PlayerbotAI* botAI)
|
HealerShouldAttackTrigger(PlayerbotAI* botAI)
|
||||||
: Trigger(botAI, "healer should attack", 1)
|
: Trigger(botAI, "healer should attack", 1) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -580,7 +559,7 @@ public:
|
|||||||
class HasPetTrigger : public Trigger
|
class HasPetTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HasPetTrigger(PlayerbotAI* ai) : Trigger(ai, "has pet", 5 * 1000) {}
|
HasPetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "has pet", 5 * 1000) {}
|
||||||
|
|
||||||
virtual bool IsActive() override;
|
virtual bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -588,7 +567,7 @@ public:
|
|||||||
class PetAttackTrigger : public Trigger
|
class PetAttackTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PetAttackTrigger(PlayerbotAI* ai) : Trigger(ai, "pet attack") {}
|
PetAttackTrigger(PlayerbotAI* botAI) : Trigger(botAI, "pet attack") {}
|
||||||
|
|
||||||
virtual bool IsActive() override;
|
virtual bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -597,9 +576,7 @@ class ItemCountTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ItemCountTrigger(PlayerbotAI* botAI, std::string const item, int32 count, int32 interval = 30 * 1000)
|
ItemCountTrigger(PlayerbotAI* botAI, std::string const item, int32 count, int32 interval = 30 * 1000)
|
||||||
: Trigger(botAI, item, interval), item(item), count(count)
|
: Trigger(botAI, item, interval), item(item), count(count) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
std::string const getName() override { return "item count"; }
|
std::string const getName() override { return "item count"; }
|
||||||
@ -613,9 +590,7 @@ class AmmoCountTrigger : public ItemCountTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AmmoCountTrigger(PlayerbotAI* botAI, std::string const item, uint32 count = 1, int32 interval = 30 * 1000)
|
AmmoCountTrigger(PlayerbotAI* botAI, std::string const item, uint32 count = 1, int32 interval = 30 * 1000)
|
||||||
: ItemCountTrigger(botAI, item, count, interval)
|
: ItemCountTrigger(botAI, item, count, interval) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -623,9 +598,7 @@ class HasAuraTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HasAuraTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1)
|
HasAuraTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1)
|
||||||
: Trigger(botAI, spell, checkInterval)
|
: Trigger(botAI, spell, checkInterval) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
@ -634,10 +607,8 @@ public:
|
|||||||
class HasAuraStackTrigger : public Trigger
|
class HasAuraStackTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HasAuraStackTrigger(PlayerbotAI* ai, std::string spell, int stack, int checkInterval = 1)
|
HasAuraStackTrigger(PlayerbotAI* botAI, std::string spell, int stack, int checkInterval = 1)
|
||||||
: Trigger(ai, spell, checkInterval), stack(stack)
|
: Trigger(botAI, spell, checkInterval), stack(stack) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
@ -858,9 +829,7 @@ class StayTimeTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StayTimeTrigger(PlayerbotAI* botAI, uint32 delay, std::string const name)
|
StayTimeTrigger(PlayerbotAI* botAI, uint32 delay, std::string const name)
|
||||||
: Trigger(botAI, name, 5 * 1000), delay(delay)
|
: Trigger(botAI, name, 5 * 1000), delay(delay) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
@ -877,7 +846,7 @@ public:
|
|||||||
class ReturnToStayPositionTrigger : public Trigger
|
class ReturnToStayPositionTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReturnToStayPositionTrigger(PlayerbotAI* ai) : Trigger(ai, "return to stay position", 2) {}
|
ReturnToStayPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "return to stay position", 2) {}
|
||||||
|
|
||||||
virtual bool IsActive() override;
|
virtual bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -892,9 +861,7 @@ class GiveItemTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GiveItemTrigger(PlayerbotAI* botAI, std::string const name, std::string const item)
|
GiveItemTrigger(PlayerbotAI* botAI, std::string const name, std::string const item)
|
||||||
: Trigger(botAI, name, 2 * 1000), item(item)
|
: Trigger(botAI, name, 2 * 1000), item(item) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
@ -962,9 +929,7 @@ class BuffOnMainTankTrigger : public BuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffOnMainTankTrigger(PlayerbotAI* botAI, std::string spell, bool checkIsOwner = false, int checkInterval = 1)
|
BuffOnMainTankTrigger(PlayerbotAI* botAI, std::string spell, bool checkIsOwner = false, int checkInterval = 1)
|
||||||
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner)
|
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Value<Unit*>* GetTargetValue();
|
virtual Value<Unit*>* GetTargetValue();
|
||||||
@ -973,7 +938,7 @@ public:
|
|||||||
class SelfResurrectTrigger : public Trigger
|
class SelfResurrectTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SelfResurrectTrigger(PlayerbotAI* ai) : Trigger(ai, "can self resurrect") {}
|
SelfResurrectTrigger(PlayerbotAI* botAI) : Trigger(botAI, "can self resurrect") {}
|
||||||
|
|
||||||
bool IsActive() override { return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); }
|
bool IsActive() override { return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); }
|
||||||
};
|
};
|
||||||
@ -981,7 +946,7 @@ public:
|
|||||||
class NewPetTrigger : public Trigger
|
class NewPetTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewPetTrigger(PlayerbotAI* ai) : Trigger(ai, "new pet"), lastPetGuid(ObjectGuid::Empty), triggered(false) {}
|
NewPetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "new pet"), lastPetGuid(ObjectGuid::Empty), triggered(false) {}
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
|
|||||||
@ -4,23 +4,89 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GenericBuffUtils.h"
|
#include "GenericBuffUtils.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "Player.h"
|
|
||||||
#include "Group.h"
|
|
||||||
#include "SpellMgr.h"
|
|
||||||
#include "Chat.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "ServerFacade.h"
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
#include "GameTime.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "SpellMgr.h"
|
||||||
|
#include "Unit.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
#include "Config.h"
|
|
||||||
#include "PlayerbotTextMgr.h"
|
|
||||||
|
|
||||||
namespace ai::buff
|
namespace ai::buff
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Prevents bots from immediately casting already-present buffs upon logging in
|
||||||
|
constexpr uint32 POST_LOGIN_BUFF_GRACE_MS = 5 * IN_MILLISECONDS;
|
||||||
|
|
||||||
|
bool IsWithinPostLoginBuffGrace(Player* player)
|
||||||
|
{
|
||||||
|
if (!player)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return getMSTimeDiff(
|
||||||
|
player->GetInGameTime(), GameTime::GetGameTimeMS().count()) < POST_LOGIN_BUFF_GRACE_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasEnoughSameMapMissingPlayersForGroupVariant(
|
||||||
|
Player* bot, PlayerbotAI* botAI, std::string const& baseName,
|
||||||
|
std::string const& groupName, uint32 requiredCount = 3)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 missingCount = 0;
|
||||||
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
|
{
|
||||||
|
Player* member = gref->GetSource();
|
||||||
|
if (!member || !member->IsInWorld() || !member->IsAlive() ||
|
||||||
|
member->GetMap() != bot->GetMap())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->HasAura(baseName, member) || botAI->HasAura(groupName, member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (++missingCount >= requiredCount)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsEligibleGroupForPartyBuffs(Group const* group)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (sPlayerbotAIConfig.autoPartyBuffs)
|
||||||
|
{
|
||||||
|
case AutoPartyBuffMode::RAID_ONLY:
|
||||||
|
return group->isRaidGroup();
|
||||||
|
case AutoPartyBuffMode::GROUP_OR_RAID:
|
||||||
|
return true;
|
||||||
|
case AutoPartyBuffMode::DISABLED:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsGroupVariantEnabled(Player* bot, std::string const& name)
|
||||||
|
{
|
||||||
|
if (!IsEligibleGroupForPartyBuffs(bot->GetGroup()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !GroupVariantFor(name).empty();
|
||||||
|
}
|
||||||
|
|
||||||
std::string MakeAuraQualifierForBuff(std::string const& name)
|
std::string MakeAuraQualifierForBuff(std::string const& name)
|
||||||
{
|
{
|
||||||
// Paladin
|
// Paladin
|
||||||
@ -34,27 +100,89 @@ namespace ai::buff
|
|||||||
if (name == "arcane intellect") return "arcane intellect,arcane brilliance";
|
if (name == "arcane intellect") return "arcane intellect,arcane brilliance";
|
||||||
// Priest
|
// Priest
|
||||||
if (name == "power word: fortitude") return "power word: fortitude,prayer of fortitude";
|
if (name == "power word: fortitude") return "power word: fortitude,prayer of fortitude";
|
||||||
|
if (name == "divine spirit") return "divine spirit,prayer of spirit";
|
||||||
|
if (name == "shadow protection") return "shadow protection,prayer of shadow protection";
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GroupVariantFor(std::string const& name)
|
std::string GroupVariantFor(std::string const& name)
|
||||||
{
|
{
|
||||||
// Paladin
|
|
||||||
if (name == "blessing of kings") return "greater blessing of kings";
|
|
||||||
if (name == "blessing of might") return "greater blessing of might";
|
|
||||||
if (name == "blessing of wisdom") return "greater blessing of wisdom";
|
|
||||||
if (name == "blessing of sanctuary") return "greater blessing of sanctuary";
|
|
||||||
// Druid
|
// Druid
|
||||||
if (name == "mark of the wild") return "gift of the wild";
|
if (name == "mark of the wild") return "gift of the wild";
|
||||||
// Mage
|
// Mage
|
||||||
if (name == "arcane intellect") return "arcane brilliance";
|
if (name == "arcane intellect") return "arcane brilliance";
|
||||||
// Priest
|
// Priest
|
||||||
if (name == "power word: fortitude") return "prayer of fortitude";
|
if (name == "power word: fortitude") return "prayer of fortitude";
|
||||||
|
if (name == "divine spirit") return "prayer of spirit";
|
||||||
|
if (name == "shadow protection") return "prayer of shadow protection";
|
||||||
|
|
||||||
|
// Paladin blessings are intentionally not included here because they are
|
||||||
|
// coordinated by the auto greater blessing system instead.
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NeedsPostLoginBuffGrace(std::string const& name)
|
||||||
|
{
|
||||||
|
static char const* const trackedBuffs[] = {
|
||||||
|
"mark of the wild",
|
||||||
|
"arcane intellect",
|
||||||
|
"power word: fortitude",
|
||||||
|
"prayer of fortitude",
|
||||||
|
"divine spirit",
|
||||||
|
"prayer of spirit",
|
||||||
|
"shadow protection",
|
||||||
|
"prayer of shadow protection",
|
||||||
|
"blessing of kings",
|
||||||
|
"blessing of might",
|
||||||
|
"blessing of wisdom",
|
||||||
|
"blessing of sanctuary"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (char const* trackedBuff : trackedBuffs)
|
||||||
|
{
|
||||||
|
if (name.find(trackedBuff) != std::string::npos)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldDeferPartyBuffEvaluationForRecentLogin(
|
||||||
|
Player* bot, Unit* target, std::string const& spell)
|
||||||
|
{
|
||||||
|
if (!NeedsPostLoginBuffGrace(spell))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (IsWithinPostLoginBuffGrace(bot))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Player* playerTarget = target ? target->ToPlayer() : nullptr;
|
||||||
|
return IsWithinPostLoginBuffGrace(playerTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldDeferGreaterBlessingAssignmentForRecentLogin(Player* bot)
|
||||||
|
{
|
||||||
|
if (IsWithinPostLoginBuffGrace(bot))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
|
{
|
||||||
|
Player* member = gref->GetSource();
|
||||||
|
if (!member || !member->IsInWorld())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IsWithinPostLoginBuffGrace(member))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasRequiredReagents(Player* bot, uint32 spellId)
|
bool HasRequiredReagents(Player* bot, uint32 spellId)
|
||||||
{
|
{
|
||||||
if (!spellId)
|
if (!spellId)
|
||||||
@ -72,75 +200,33 @@ namespace ai::buff
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No reagent required
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UpgradeToGroupIfAppropriate(
|
std::string UpgradeToGroupIfAppropriate(
|
||||||
Player* bot,
|
Player* bot, PlayerbotAI* botAI, std::string const& baseName)
|
||||||
PlayerbotAI* botAI,
|
|
||||||
std::string const& baseName,
|
|
||||||
bool announceOnMissing,
|
|
||||||
std::function<void(std::string const&)> announce)
|
|
||||||
{
|
{
|
||||||
std::string castName = baseName;
|
if (!IsGroupVariantEnabled(bot, baseName))
|
||||||
Group* g = bot->GetGroup();
|
return baseName;
|
||||||
if (!g || g->GetMembersCount() < static_cast<uint32>(sPlayerbotAIConfig.minBotsForGreaterBuff))
|
|
||||||
return castName; // Group too small: stay in solo mode
|
|
||||||
|
|
||||||
if (std::string const groupName = GroupVariantFor(baseName); !groupName.empty())
|
std::string const groupName = GroupVariantFor(baseName);
|
||||||
{
|
if (groupName.empty())
|
||||||
uint32 const groupVariantSpellId = botAI->GetAiObjectContext()
|
return baseName;
|
||||||
|
|
||||||
|
// Prefer singles until at least three living, in-world group members on the bot's map
|
||||||
|
// are missing both the single-target buff and its group variant.
|
||||||
|
if (!HasEnoughSameMapMissingPlayersForGroupVariant(bot, botAI, baseName, groupName))
|
||||||
|
return baseName;
|
||||||
|
|
||||||
|
uint32 const groupSpellId = botAI->GetAiObjectContext()
|
||||||
->GetValue<uint32>("spell id", groupName)->Get();
|
->GetValue<uint32>("spell id", groupName)->Get();
|
||||||
|
|
||||||
// We check usefulness on the **basic** buff (not the greater version),
|
if (groupSpellId && HasRequiredReagents(bot, groupSpellId))
|
||||||
// because "spell cast useful" may return false for the greater variant.
|
|
||||||
bool const usefulBase = botAI->GetAiObjectContext()
|
|
||||||
->GetValue<bool>("spell cast useful", baseName)->Get();
|
|
||||||
|
|
||||||
if (groupVariantSpellId && HasRequiredReagents(bot, groupVariantSpellId))
|
|
||||||
{
|
|
||||||
// Learned + reagents OK -> switch to greater
|
|
||||||
return groupName;
|
return groupName;
|
||||||
}
|
|
||||||
|
|
||||||
// Missing reagents -> announce if (a) greater is known, (b) base buff is useful,
|
return baseName;
|
||||||
// (c) announce was requested, (d) a callback is provided.
|
|
||||||
if (announceOnMissing && groupVariantSpellId && usefulBase && announce)
|
|
||||||
{
|
|
||||||
static std::map<std::pair<uint32, std::string>, time_t> s_lastWarn; // par bot & par buff
|
|
||||||
time_t now = std::time(nullptr);
|
|
||||||
uint32 botLow = static_cast<uint32>(bot->GetGUID().GetCounter());
|
|
||||||
time_t& last = s_lastWarn[ std::make_pair(botLow, groupName) ];
|
|
||||||
if (!last || now - last >= sPlayerbotAIConfig.rpWarningCooldown) // Configurable anti-spam
|
|
||||||
{
|
|
||||||
// DB Key choice in regard of the buff
|
|
||||||
std::string key;
|
|
||||||
if (groupName.find("greater blessing") != std::string::npos)
|
|
||||||
key = "rp_missing_reagent_greater_blessing";
|
|
||||||
else if (groupName == "gift of the wild")
|
|
||||||
key = "rp_missing_reagent_gift_of_the_wild";
|
|
||||||
else if (groupName == "arcane brilliance")
|
|
||||||
key = "rp_missing_reagent_arcane_brilliance";
|
|
||||||
else
|
|
||||||
key = "rp_missing_reagent_generic";
|
|
||||||
|
|
||||||
// Placeholders
|
|
||||||
std::map<std::string, std::string> placeholders;
|
|
||||||
placeholders["%group_spell"] = groupName;
|
|
||||||
placeholders["%base_spell"] = baseName;
|
|
||||||
|
|
||||||
std::string announceText = PlayerbotTextMgr::instance().GetBotTextOrDefault(key,
|
|
||||||
"Out of components for %group_spell. Using %base_spell!", placeholders);
|
|
||||||
|
|
||||||
announce(announceText);
|
|
||||||
last = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return castName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,63 +6,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Group.h"
|
|
||||||
#include "Chat.h"
|
|
||||||
#include "Language.h"
|
|
||||||
|
|
||||||
class Player;
|
class Player;
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
class Unit;
|
||||||
|
|
||||||
namespace ai::buff
|
namespace ai::buff
|
||||||
{
|
{
|
||||||
|
|
||||||
// Build an aura qualifier "single + greater" to avoid double-buffing
|
bool IsGroupVariantEnabled(Player* bot, std::string const& name);
|
||||||
|
|
||||||
std::string MakeAuraQualifierForBuff(std::string const& name);
|
std::string MakeAuraQualifierForBuff(std::string const& name);
|
||||||
|
|
||||||
// Returns the group spell name for a given single-target buff.
|
|
||||||
// If no group equivalent exists, returns "".
|
|
||||||
std::string GroupVariantFor(std::string const& name);
|
std::string GroupVariantFor(std::string const& name);
|
||||||
|
|
||||||
// Checks if the bot has the required reagents to cast a spell (by its spellId).
|
bool NeedsPostLoginBuffGrace(std::string const& name);
|
||||||
// Returns false if the spellId is invalid.
|
|
||||||
|
bool ShouldDeferPartyBuffEvaluationForRecentLogin(
|
||||||
|
Player* bot,
|
||||||
|
Unit* target,
|
||||||
|
std::string const& spell);
|
||||||
|
|
||||||
|
bool ShouldDeferGreaterBlessingAssignmentForRecentLogin(Player* bot);
|
||||||
|
|
||||||
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
||||||
|
|
||||||
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
|
||||||
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
|
||||||
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
|
||||||
// (if provided) to notify the party/raid.
|
|
||||||
std::string UpgradeToGroupIfAppropriate(
|
std::string UpgradeToGroupIfAppropriate(
|
||||||
Player* bot,
|
Player* bot,
|
||||||
PlayerbotAI* botAI,
|
PlayerbotAI* botAI,
|
||||||
std::string const& baseName,
|
std::string const& baseName);
|
||||||
bool announceOnMissing = false,
|
|
||||||
std::function<void(std::string const&)> announce = {}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ai::spell
|
namespace ai::spell
|
||||||
{
|
{
|
||||||
bool HasSpellOrCategoryCooldown(Player* bot, uint32 spellId);
|
bool HasSpellOrCategoryCooldown(Player* bot, uint32 spellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ai::chat {
|
|
||||||
inline std::function<void(std::string const&)> MakeGroupAnnouncer(Player* me)
|
|
||||||
{
|
|
||||||
return [me](std::string const& msg)
|
|
||||||
{
|
|
||||||
if (Group* g = me->GetGroup())
|
|
||||||
{
|
|
||||||
WorldPacket data;
|
|
||||||
ChatMsg type = g->isRaidGroup() ? CHAT_MSG_RAID : CHAT_MSG_PARTY;
|
|
||||||
ChatHandler::BuildChatPacket(data, type, LANG_UNIVERSAL, me, /*receiver=*/nullptr, msg.c_str());
|
|
||||||
g->BroadcastPacket(&data, true, -1, me->GetGUID());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
me->Say(msg, LANG_UNIVERSAL);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -87,16 +87,16 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastMarkOfTheWildAction : public CastBuffSpellAction
|
class CastMarkOfTheWildAction : public GroupBuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastMarkOfTheWildAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mark of the wild") {}
|
CastMarkOfTheWildAction(PlayerbotAI* botAI) : GroupBuffSpellAction(botAI, "mark of the wild") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastMarkOfTheWildOnPartyAction : public BuffOnPartyAction
|
class CastMarkOfTheWildOnPartyAction : public GroupBuffOnPartyAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastMarkOfTheWildOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "mark of the wild") {}
|
CastMarkOfTheWildOnPartyAction(PlayerbotAI* botAI) : GroupBuffOnPartyAction(botAI, "mark of the wild") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastSurvivalInstinctsAction : public CastBuffSpellAction
|
class CastSurvivalInstinctsAction : public CastBuffSpellAction
|
||||||
|
|||||||
@ -9,11 +9,6 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
|
|
||||||
bool MarkOfTheWildOnPartyTrigger::IsActive()
|
|
||||||
{
|
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("gift of the wild", GetTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MarkOfTheWildTrigger::IsActive()
|
bool MarkOfTheWildTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return BuffTrigger::IsActive() && !botAI->HasAura("gift of the wild", GetTarget());
|
return BuffTrigger::IsActive() && !botAI->HasAura("gift of the wild", GetTarget());
|
||||||
@ -23,15 +23,13 @@ class PlayerbotAI;
|
|||||||
class MarkOfTheWildOnPartyTrigger : public BuffOnPartyTrigger
|
class MarkOfTheWildOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MarkOfTheWildOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "mark of the wild", 2 * 2000) {}
|
MarkOfTheWildOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "mark of the wild", 4 * 2000) {}
|
||||||
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MarkOfTheWildTrigger : public BuffTrigger
|
class MarkOfTheWildTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MarkOfTheWildTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "mark of the wild", 2 * 2000) {}
|
MarkOfTheWildTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "mark of the wild", 4 * 2000) {}
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -40,16 +40,16 @@ public:
|
|||||||
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
|
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastArcaneIntellectAction : public CastBuffSpellAction
|
class CastArcaneIntellectAction : public GroupBuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastArcaneIntellectAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane intellect") {}
|
CastArcaneIntellectAction(PlayerbotAI* botAI) : GroupBuffSpellAction(botAI, "arcane intellect") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastArcaneIntellectOnPartyAction : public BuffOnPartyAction
|
class CastArcaneIntellectOnPartyAction : public GroupBuffOnPartyAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "arcane intellect") {}
|
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : GroupBuffOnPartyAction(botAI, "arcane intellect") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastFocusMagicOnPartyAction : public CastSpellAction
|
class CastFocusMagicOnPartyAction : public CastSpellAction
|
||||||
@ -31,11 +31,6 @@ bool NoManaGemTrigger::IsActive()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArcaneIntellectOnPartyTrigger::IsActive()
|
|
||||||
{
|
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("arcane brilliance", GetTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArcaneIntellectTrigger::IsActive()
|
bool ArcaneIntellectTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return BuffTrigger::IsActive() && !botAI->HasAura("arcane brilliance", GetTarget());
|
return BuffTrigger::IsActive() && !botAI->HasAura("arcane brilliance", GetTarget());
|
||||||
@ -19,14 +19,13 @@ class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI)
|
ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "arcane intellect", 2 * 2000) {}
|
: BuffOnPartyTrigger(botAI, "arcane intellect", 4 * 2000) {}
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArcaneIntellectTrigger : public BuffTrigger
|
class ArcaneIntellectTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArcaneIntellectTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane intellect", 2 * 2000) {}
|
ArcaneIntellectTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane intellect", 4 * 2000) {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1,529 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
|
||||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "PaladinActions.h"
|
|
||||||
|
|
||||||
#include "AiFactory.h"
|
|
||||||
#include "Event.h"
|
|
||||||
#include "PaladinHelper.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "SharedDefines.h"
|
|
||||||
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
|
||||||
#include "Ai/Base/Util/GenericBuffUtils.h"
|
|
||||||
#include "Group.h"
|
|
||||||
#include "ObjectAccessor.h"
|
|
||||||
|
|
||||||
using ai::buff::MakeAuraQualifierForBuff;
|
|
||||||
|
|
||||||
// Helper : detect tank role on the target (player bot or not) return true if spec is tank or if the bot have tank strategies (bear/tank/tank face).
|
|
||||||
static inline bool IsTankRole(Player* p)
|
|
||||||
{
|
|
||||||
if (!p) return false;
|
|
||||||
if (p->HasTankSpec())
|
|
||||||
return true;
|
|
||||||
if (PlayerbotAI* otherAI = GET_PLAYERBOT_AI(p))
|
|
||||||
{
|
|
||||||
if (otherAI->HasStrategy("tank", BOT_STATE_NON_COMBAT) ||
|
|
||||||
otherAI->HasStrategy("tank", BOT_STATE_COMBAT) ||
|
|
||||||
otherAI->HasStrategy("tank face", BOT_STATE_NON_COMBAT) ||
|
|
||||||
otherAI->HasStrategy("tank face", BOT_STATE_COMBAT) ||
|
|
||||||
otherAI->HasStrategy("bear", BOT_STATE_NON_COMBAT) ||
|
|
||||||
otherAI->HasStrategy("bear", BOT_STATE_COMBAT))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added for solo paladin patch : determine if he's the only paladin on party
|
|
||||||
static inline bool IsOnlyPaladinInGroup(Player* bot)
|
|
||||||
{
|
|
||||||
if (!bot) return false;
|
|
||||||
Group* g = bot->GetGroup();
|
|
||||||
if (!g) return true; // solo
|
|
||||||
uint32 pals = 0u;
|
|
||||||
for (GroupReference* r = g->GetFirstMember(); r; r = r->next())
|
|
||||||
{
|
|
||||||
Player* p = r->GetSource();
|
|
||||||
if (!p || !p->IsInWorld()) continue;
|
|
||||||
if (p->getClass() == CLASS_PALADIN) ++pals;
|
|
||||||
}
|
|
||||||
return pals == 1u;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfMight(Unit* target)
|
|
||||||
{
|
|
||||||
if (!target->ToPlayer())
|
|
||||||
{
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(target->ToPlayer());
|
|
||||||
switch (target->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_MAGE:
|
|
||||||
case CLASS_PRIEST:
|
|
||||||
case CLASS_WARLOCK:
|
|
||||||
return "blessing of wisdom";
|
|
||||||
break;
|
|
||||||
case CLASS_SHAMAN:
|
|
||||||
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
|
|
||||||
{
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLASS_DRUID:
|
|
||||||
if (tab == DRUID_TAB_RESTORATION || tab == DRUID_TAB_BALANCE)
|
|
||||||
{
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLASS_PALADIN:
|
|
||||||
if (tab == PALADIN_TAB_HOLY)
|
|
||||||
{
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfWisdom(Unit* target)
|
|
||||||
{
|
|
||||||
if (!target->ToPlayer())
|
|
||||||
{
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(target->ToPlayer());
|
|
||||||
switch (target->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_WARRIOR:
|
|
||||||
case CLASS_ROGUE:
|
|
||||||
case CLASS_DEATH_KNIGHT:
|
|
||||||
case CLASS_HUNTER:
|
|
||||||
return "blessing of might";
|
|
||||||
break;
|
|
||||||
case CLASS_SHAMAN:
|
|
||||||
if (tab == SHAMAN_TAB_ENHANCEMENT)
|
|
||||||
{
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLASS_DRUID:
|
|
||||||
if (tab == DRUID_TAB_FERAL)
|
|
||||||
{
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLASS_PALADIN:
|
|
||||||
if (tab == PALADIN_TAB_PROTECTION || tab == PALADIN_TAB_RETRIBUTION)
|
|
||||||
{
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfSanctuary(Unit* target, Player* bot)
|
|
||||||
{
|
|
||||||
if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
Player* tp = target->ToPlayer();
|
|
||||||
if (!tp)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot))
|
|
||||||
{
|
|
||||||
if (Unit* mt = ai->GetAiObjectContext()->GetValue<Unit*>("main tank")->Get())
|
|
||||||
{
|
|
||||||
if (mt == target)
|
|
||||||
return "blessing of sanctuary";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tp->HasTankSpec())
|
|
||||||
return "blessing of sanctuary";
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
|
|
||||||
return context->GetValue<Unit*>("party member without aura", MakeAuraQualifierForBuff(spell));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfMightAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string castName = GetActualBlessingOfMight(target);
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
return botAI->CastSpell(castName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfMightOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
return context->GetValue<Unit*>(
|
|
||||||
"party member without aura",
|
|
||||||
"blessing of might,greater blessing of might,blessing of wisdom,greater blessing of wisdom,blessing of sanctuary,greater blessing of sanctuary"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfMightOnPartyAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string castName = GetActualBlessingOfMight(target);
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
return botAI->CastSpell(castName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfWisdomAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string castName = GetActualBlessingOfWisdom(target);
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
return botAI->CastSpell(castName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfWisdomOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
return context->GetValue<Unit*>(
|
|
||||||
"party member without aura",
|
|
||||||
"blessing of wisdom,greater blessing of wisdom,blessing of might,greater blessing of might,blessing of sanctuary,greater blessing of sanctuary"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfWisdomOnPartyAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Player* targetPlayer = target->ToPlayer();
|
|
||||||
|
|
||||||
if (Group* g = bot->GetGroup())
|
|
||||||
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (botAI->HasStrategy("bmana", BOT_STATE_NON_COMBAT) &&
|
|
||||||
targetPlayer && IsTankRole(targetPlayer))
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Wisdom/bmana] Skip tank {} (Kings only)", target->GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string castName = GetActualBlessingOfWisdom(target);
|
|
||||||
if (castName.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
return botAI->CastSpell(castName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
return context->GetValue<Unit*>(
|
|
||||||
"party member without aura",
|
|
||||||
"blessing of sanctuary,greater blessing of sanctuary"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
{
|
|
||||||
// Fallback: GetTarget() can be null if no one needs a buff.
|
|
||||||
// Keep a valid pointer for the checks/logs that follow.
|
|
||||||
target = bot;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player* targetPlayer = target ? target->ToPlayer() : nullptr;
|
|
||||||
|
|
||||||
// Small helpers to check relevant auras
|
|
||||||
const auto HasKingsAura = [&](Unit* u) -> bool {
|
|
||||||
return botAI->HasAura("blessing of kings", u) || botAI->HasAura("greater blessing of kings", u);
|
|
||||||
};
|
|
||||||
const auto HasSanctAura = [&](Unit* u) -> bool {
|
|
||||||
return botAI->HasAura("blessing of sanctuary", u) || botAI->HasAura("greater blessing of sanctuary", u);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Group* g = bot->GetGroup())
|
|
||||||
{
|
|
||||||
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Sanct] Initial target not in group, ignoring");
|
|
||||||
target = bot;
|
|
||||||
targetPlayer = bot->ToPlayer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Player* self = bot->ToPlayer())
|
|
||||||
{
|
|
||||||
bool selfHasSanct = HasSanctAura(self);
|
|
||||||
bool needSelf = IsTankRole(self) && !selfHasSanct;
|
|
||||||
|
|
||||||
LOG_DEBUG("playerbots", "[Sanct] {} isTank={} selfHasSanct={} needSelf={}",
|
|
||||||
bot->GetName(), IsTankRole(self), selfHasSanct, needSelf);
|
|
||||||
|
|
||||||
if (needSelf)
|
|
||||||
{
|
|
||||||
target = self;
|
|
||||||
targetPlayer = self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to re-target a valid tank in group if needed
|
|
||||||
bool targetOk = false;
|
|
||||||
if (targetPlayer)
|
|
||||||
{
|
|
||||||
bool hasSanct = HasSanctAura(targetPlayer);
|
|
||||||
targetOk = IsTankRole(targetPlayer) && !hasSanct;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetOk)
|
|
||||||
{
|
|
||||||
if (Group* g = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* gref = g->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* p = gref->GetSource();
|
|
||||||
if (!p) continue;
|
|
||||||
if (!p->IsInWorld() || !p->IsAlive()) continue;
|
|
||||||
if (!IsTankRole(p)) continue;
|
|
||||||
|
|
||||||
bool hasSanct = HasSanctAura(p);
|
|
||||||
if (!hasSanct)
|
|
||||||
{
|
|
||||||
target = p; // prioritize this tank
|
|
||||||
targetPlayer = p;
|
|
||||||
targetOk = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bool hasKings = HasKingsAura(target);
|
|
||||||
bool hasSanct = HasSanctAura(target);
|
|
||||||
bool knowSanct = bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY);
|
|
||||||
LOG_DEBUG("playerbots", "[Sanct] Final target={} hasKings={} hasSanct={} knowSanct={}",
|
|
||||||
target->GetName(), hasKings, hasSanct, knowSanct);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string castName = GetActualBlessingOfSanctuary(target, bot);
|
|
||||||
// If internal logic didn't recognize the tank (e.g., bear druid), force single-target Sanctuary
|
|
||||||
if (castName.empty())
|
|
||||||
{
|
|
||||||
if (targetPlayer)
|
|
||||||
{
|
|
||||||
if (IsTankRole(targetPlayer))
|
|
||||||
castName = "blessing of sanctuary"; // force single-target
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (targetPlayer && !IsTankRole(targetPlayer))
|
|
||||||
{
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
castName = "blessing of sanctuary";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = botAI->CastSpell(castName, target);
|
|
||||||
LOG_DEBUG("playerbots", "[Sanct] Cast {} on {} result={}", castName, target->GetName(), ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfKingsOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
return context->GetValue<Unit*>(
|
|
||||||
"party member without aura",
|
|
||||||
"blessing of kings,greater blessing of kings,blessing of sanctuary,greater blessing of sanctuary"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastBlessingOfKingsOnPartyAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Group* g = bot->GetGroup();
|
|
||||||
if (!g)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Added for patch solo paladin, never buff itself to not remove his sanctuary buff
|
|
||||||
if (botAI->HasStrategy("bstats", BOT_STATE_NON_COMBAT) && IsOnlyPaladinInGroup(bot))
|
|
||||||
{
|
|
||||||
if (target->GetGUID() == bot->GetGUID())
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Kings/bstats-solo] Skip self to keep Sanctuary on {}", bot->GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End solo paladin patch
|
|
||||||
|
|
||||||
Player* targetPlayer = target->ToPlayer();
|
|
||||||
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const bool hasBmana = botAI->HasStrategy("bmana", BOT_STATE_NON_COMBAT);
|
|
||||||
const bool hasBstats = botAI->HasStrategy("bstats", BOT_STATE_NON_COMBAT);
|
|
||||||
|
|
||||||
if (hasBmana)
|
|
||||||
{
|
|
||||||
if (!targetPlayer || !IsTankRole(targetPlayer))
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Kings/bmana] Skip non-tank {}", target->GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetPlayer)
|
|
||||||
{
|
|
||||||
const bool isTank = IsTankRole(targetPlayer);
|
|
||||||
const bool hasSanctFromMe =
|
|
||||||
target->HasAura(SPELL_BLESSING_OF_SANCTUARY, bot->GetGUID()) ||
|
|
||||||
target->HasAura(SPELL_GREATER_BLESSING_OF_SANCTUARY, bot->GetGUID());
|
|
||||||
const bool hasSanctAny =
|
|
||||||
botAI->HasAura("blessing of sanctuary", target) ||
|
|
||||||
botAI->HasAura("greater blessing of sanctuary", target);
|
|
||||||
|
|
||||||
if (isTank && hasSanctFromMe)
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Kings] Skip: {} has my Sanctuary and is a tank", target->GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasBstats && isTank && hasSanctAny)
|
|
||||||
{
|
|
||||||
LOG_DEBUG("playerbots", "[Kings] Skip (bstats): {} already has Sanctuary and is a tank", target->GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string castName = "blessing of kings";
|
|
||||||
|
|
||||||
bool allowGreater = true;
|
|
||||||
|
|
||||||
if (hasBmana)
|
|
||||||
allowGreater = false;
|
|
||||||
|
|
||||||
if (allowGreater && hasBstats && targetPlayer)
|
|
||||||
{
|
|
||||||
switch (targetPlayer->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_WARRIOR:
|
|
||||||
case CLASS_PALADIN:
|
|
||||||
case CLASS_DRUID:
|
|
||||||
case CLASS_DEATH_KNIGHT:
|
|
||||||
allowGreater = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowGreater)
|
|
||||||
{
|
|
||||||
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
|
||||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
|
||||||
}
|
|
||||||
|
|
||||||
return botAI->CastSpell(castName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastSealSpellAction::isUseful() { return AI_VALUE2(bool, "combat", "self target"); }
|
|
||||||
|
|
||||||
Value<Unit*>* CastTurnUndeadAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
|
|
||||||
|
|
||||||
Unit* CastHandOfFreedomOnPartyAction::GetTarget()
|
|
||||||
{
|
|
||||||
bool const selfImpaired = botAI->IsMovementImpaired(bot);
|
|
||||||
bool const hasSelfHand = selfImpaired && ai::paladin::HasAnyPaladinHandFromCaster(bot, bot);
|
|
||||||
|
|
||||||
if (!bot->GetGroup())
|
|
||||||
{
|
|
||||||
if (selfImpaired && !hasSelfHand)
|
|
||||||
return bot;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selfImpaired && !hasSelfHand)
|
|
||||||
return bot;
|
|
||||||
|
|
||||||
return CastBuffSpellAction::GetTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastHandOfFreedomOnPartyAction::GetTargetValue()
|
|
||||||
{
|
|
||||||
return context->GetValue<Unit*>("party member snared target");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastHandOfFreedomOnPartyAction::isUseful()
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return CastBuffSpellAction::isUseful() && !ai::paladin::HasAnyPaladinHandFromCaster(target, bot);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* CastRighteousDefenseAction::GetTarget()
|
|
||||||
{
|
|
||||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
|
||||||
if (!current_target)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return current_target->GetVictim();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastDivineSacrificeAction::isUseful()
|
|
||||||
{
|
|
||||||
return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() &&
|
|
||||||
!botAI->HasAura("divine guardian", GetTarget(), false, false, -1, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastCancelDivineSacrificeAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
botAI->RemoveAura("divine sacrifice");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CastCancelDivineSacrificeAction::isUseful()
|
|
||||||
{
|
|
||||||
return botAI->HasAura("divine sacrifice", GetTarget(), false, true, -1, true);
|
|
||||||
}
|
|
||||||
609
src/Ai/Class/Paladin/Actions/PaladinActions.cpp
Normal file
609
src/Ai/Class/Paladin/Actions/PaladinActions.cpp
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PaladinActions.h"
|
||||||
|
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "Event.h"
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
|
#include "PaladinGreaterBlessingAction.h"
|
||||||
|
#include "PaladinHelper.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
|
static bool IsBlessingTargetCandidate(Player* bot, Player* player)
|
||||||
|
{
|
||||||
|
if (!player || !player->IsAlive() || player->GetMapId() != bot->GetMapId())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (player->IsGameMaster())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->GetDistance(player) < sPlayerbotAIConfig.spellDistance * 2 &&
|
||||||
|
bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(),
|
||||||
|
player->GetPositionZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasBlessingAura(
|
||||||
|
PlayerbotAI* botAI, Unit* target, std::initializer_list<char const*> auraNames)
|
||||||
|
{
|
||||||
|
for (char const* auraName : auraNames)
|
||||||
|
{
|
||||||
|
if (botAI->HasAura(auraName, target))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsGreaterBlessingMode(Player* bot)
|
||||||
|
{
|
||||||
|
return ai::gbless::IsEligibleGroupForAutoBlessings(bot->GetGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Predicate>
|
||||||
|
static Unit* FindBlessingTarget(
|
||||||
|
Player* bot, PlayerbotAI* botAI, Predicate&& predicate)
|
||||||
|
{
|
||||||
|
std::vector<Player*> masters;
|
||||||
|
std::vector<Player*> healers;
|
||||||
|
std::vector<Player*> tanks;
|
||||||
|
std::vector<Player*> others;
|
||||||
|
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
auto addPlayer = [&](Player* player)
|
||||||
|
{
|
||||||
|
if (!IsBlessingTargetCandidate(bot, player))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (player == master)
|
||||||
|
masters.push_back(player);
|
||||||
|
else if (botAI->IsHeal(player))
|
||||||
|
healers.push_back(player);
|
||||||
|
else if (botAI->IsTank(player))
|
||||||
|
tanks.push_back(player);
|
||||||
|
else
|
||||||
|
others.push_back(player);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
addPlayer(ref->GetSource());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addPlayer(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<Player*>*> orderedLists = {
|
||||||
|
&masters, &healers, &tanks, &others };
|
||||||
|
for (std::vector<Player*>* players : orderedLists)
|
||||||
|
{
|
||||||
|
for (Player* player : *players)
|
||||||
|
{
|
||||||
|
if (predicate(player))
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsTankRole(Player* player)
|
||||||
|
{
|
||||||
|
if (!player)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (player->HasTankSpec())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (PlayerbotAI* otherAI = GET_PLAYERBOT_AI(player))
|
||||||
|
{
|
||||||
|
if (otherAI->HasStrategy("tank", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank", BOT_STATE_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank face", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank face", BOT_STATE_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("bear", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("bear", BOT_STATE_COMBAT))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsOnlyPaladinInGroup(Player* bot)
|
||||||
|
{
|
||||||
|
if (!bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32 paladins = 0u;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* player = ref->GetSource();
|
||||||
|
if (!player || !player->IsInWorld()) continue;
|
||||||
|
if (player->getClass() == CLASS_PALADIN) ++paladins;
|
||||||
|
}
|
||||||
|
|
||||||
|
return paladins == 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string const GetActualBlessingOfMight(Unit* target)
|
||||||
|
{
|
||||||
|
if (!target->ToPlayer())
|
||||||
|
return "blessing of might";
|
||||||
|
|
||||||
|
uint8 tab = AiFactory::GetPlayerSpecTab(target->ToPlayer());
|
||||||
|
switch (target->getClass())
|
||||||
|
{
|
||||||
|
case CLASS_MAGE:
|
||||||
|
case CLASS_PRIEST:
|
||||||
|
case CLASS_WARLOCK:
|
||||||
|
return "blessing of wisdom";
|
||||||
|
break;
|
||||||
|
case CLASS_SHAMAN:
|
||||||
|
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
|
||||||
|
return "blessing of wisdom";
|
||||||
|
break;
|
||||||
|
case CLASS_DRUID:
|
||||||
|
if (tab == DRUID_TAB_RESTORATION || tab == DRUID_TAB_BALANCE)
|
||||||
|
return "blessing of wisdom";
|
||||||
|
break;
|
||||||
|
case CLASS_PALADIN:
|
||||||
|
if (tab == PALADIN_TAB_HOLY)
|
||||||
|
return "blessing of wisdom";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "blessing of might";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string const GetActualBlessingOfWisdom(Unit* target)
|
||||||
|
{
|
||||||
|
if (!target->ToPlayer())
|
||||||
|
return "blessing of might";
|
||||||
|
|
||||||
|
uint8 tab = AiFactory::GetPlayerSpecTab(target->ToPlayer());
|
||||||
|
switch (target->getClass())
|
||||||
|
{
|
||||||
|
case CLASS_WARRIOR:
|
||||||
|
case CLASS_ROGUE:
|
||||||
|
case CLASS_DEATH_KNIGHT:
|
||||||
|
case CLASS_HUNTER:
|
||||||
|
return "blessing of might";
|
||||||
|
break;
|
||||||
|
case CLASS_SHAMAN:
|
||||||
|
if (tab == SHAMAN_TAB_ENHANCEMENT)
|
||||||
|
return "blessing of might";
|
||||||
|
break;
|
||||||
|
case CLASS_DRUID:
|
||||||
|
if (tab == DRUID_TAB_FERAL)
|
||||||
|
return "blessing of might";
|
||||||
|
break;
|
||||||
|
case CLASS_PALADIN:
|
||||||
|
if (tab == PALADIN_TAB_PROTECTION || tab == PALADIN_TAB_RETRIBUTION)
|
||||||
|
return "blessing of might";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "blessing of wisdom";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string const GetActualBlessingOfSanctuary(Unit* target, Player* bot)
|
||||||
|
{
|
||||||
|
if (!bot->HasSpell(ai::paladin::SPELL_BLESSING_OF_SANCTUARY))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
Player* targetPlayer = target->ToPlayer();
|
||||||
|
if (!targetPlayer)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (auto* botAI = GET_PLAYERBOT_AI(bot))
|
||||||
|
{
|
||||||
|
if (Unit* mainTank =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<Unit*>("main tank")->Get())
|
||||||
|
{
|
||||||
|
if (mainTank == target)
|
||||||
|
return "blessing of sanctuary";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetPlayer->HasTankSpec())
|
||||||
|
return "blessing of sanctuary";
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastBlessingOfMightOnPartyAction::GetTarget()
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return FindBlessingTarget(bot, botAI, [&](Player* player)
|
||||||
|
{
|
||||||
|
return !HasBlessingAura(botAI, player,
|
||||||
|
{ "blessing of might", "greater blessing of might",
|
||||||
|
"blessing of wisdom", "greater blessing of wisdom",
|
||||||
|
"blessing of sanctuary", "greater blessing of sanctuary" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfMightAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfMight(target);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastBlessingOfMightOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of might,greater blessing of might,blessing of wisdom,"
|
||||||
|
"greater blessing of wisdom,blessing of sanctuary,"
|
||||||
|
"greater blessing of sanctuary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfMightOnPartyAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfMight(target);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfWisdomAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfWisdom(target);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastBlessingOfWisdomOnPartyAction::GetTarget()
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return FindBlessingTarget(bot, botAI, [&](Player* player)
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("bwisdom", BOT_STATE_NON_COMBAT) && IsTankRole(player))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !HasBlessingAura(botAI, player,
|
||||||
|
{ "blessing of might", "greater blessing of might",
|
||||||
|
"blessing of wisdom", "greater blessing of wisdom",
|
||||||
|
"blessing of sanctuary", "greater blessing of sanctuary" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastBlessingOfWisdomOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of wisdom,greater blessing of wisdom,blessing of might,greater blessing of might,"
|
||||||
|
"blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfWisdomOnPartyAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* targetPlayer = target->ToPlayer();
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
if (targetPlayer && !group->IsMember(targetPlayer->GetGUID()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->HasStrategy("bwisdom", BOT_STATE_NON_COMBAT) &&
|
||||||
|
targetPlayer && IsTankRole(targetPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfWisdom(target);
|
||||||
|
if (castName.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!bot->HasSpell(ai::paladin::SPELL_BLESSING_OF_SANCTUARY))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
target = bot;
|
||||||
|
|
||||||
|
Player* targetPlayer = target ? target->ToPlayer() : nullptr;
|
||||||
|
|
||||||
|
const auto HasKingsAura = [&](Unit* unit) -> bool {
|
||||||
|
return botAI->HasAura("blessing of kings", unit) ||
|
||||||
|
botAI->HasAura("greater blessing of kings", unit);
|
||||||
|
};
|
||||||
|
const auto HasSanctAura = [&](Unit* unit) -> bool {
|
||||||
|
return botAI->HasAura("blessing of sanctuary", unit) ||
|
||||||
|
botAI->HasAura("greater blessing of sanctuary", unit);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
if (targetPlayer && !group->IsMember(targetPlayer->GetGUID()))
|
||||||
|
{
|
||||||
|
target = bot;
|
||||||
|
targetPlayer = bot->ToPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Player* self = bot->ToPlayer())
|
||||||
|
{
|
||||||
|
bool selfHasSanct = HasSanctAura(self);
|
||||||
|
bool needSelf = IsTankRole(self) && !selfHasSanct;
|
||||||
|
|
||||||
|
if (needSelf)
|
||||||
|
{
|
||||||
|
target = self;
|
||||||
|
targetPlayer = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool targetOk = false;
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
bool hasSanct = HasSanctAura(targetPlayer);
|
||||||
|
targetOk = IsTankRole(targetPlayer) && !hasSanct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetOk)
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* player = ref->GetSource();
|
||||||
|
if (!player) continue;
|
||||||
|
if (!player->IsInWorld() || !player->IsAlive()) continue;
|
||||||
|
if (!IsTankRole(player)) continue;
|
||||||
|
|
||||||
|
bool hasSanct = HasSanctAura(player);
|
||||||
|
if (!hasSanct)
|
||||||
|
{
|
||||||
|
target = player;
|
||||||
|
targetPlayer = player;
|
||||||
|
targetOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetActualBlessingOfSanctuary(target, bot).empty())
|
||||||
|
{
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
if (IsTankRole(targetPlayer))
|
||||||
|
return botAI->CastSpell("blessing of sanctuary", target);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->CastSpell("blessing of sanctuary", target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastBlessingOfSanctuaryOnPartyAction::GetTarget()
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!bot->HasSpell(ai::paladin::SPELL_BLESSING_OF_SANCTUARY))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return FindBlessingTarget(bot, botAI, [&](Player* player)
|
||||||
|
{
|
||||||
|
return IsTankRole(player) &&
|
||||||
|
!HasBlessingAura(botAI, player,
|
||||||
|
{ "blessing of sanctuary", "greater blessing of sanctuary" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastBlessingOfKingsOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of kings,greater blessing of kings,"
|
||||||
|
"blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastBlessingOfKingsOnPartyAction::GetTarget()
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const bool hasBwisdom = botAI->HasStrategy("bwisdom", BOT_STATE_NON_COMBAT);
|
||||||
|
const bool hasBkings = botAI->HasStrategy("bkings", BOT_STATE_NON_COMBAT);
|
||||||
|
const bool onlyPaladinInGroup = IsOnlyPaladinInGroup(bot);
|
||||||
|
|
||||||
|
return FindBlessingTarget(bot, botAI, [&](Player* player)
|
||||||
|
{
|
||||||
|
const bool isTank = IsTankRole(player);
|
||||||
|
const bool hasKingsOrSanct = HasBlessingAura(botAI, player,
|
||||||
|
{ "blessing of kings", "greater blessing of kings",
|
||||||
|
"blessing of sanctuary", "greater blessing of sanctuary" });
|
||||||
|
|
||||||
|
if (hasKingsOrSanct)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hasBwisdom)
|
||||||
|
return isTank;
|
||||||
|
|
||||||
|
if (hasBkings)
|
||||||
|
{
|
||||||
|
if (isTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (onlyPaladinInGroup && player == bot)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfKingsOnPartyAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (IsGreaterBlessingMode(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->HasStrategy("bkings", BOT_STATE_NON_COMBAT) &&
|
||||||
|
IsOnlyPaladinInGroup(bot))
|
||||||
|
{
|
||||||
|
if (target->GetGUID() == bot->GetGUID())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* targetPlayer = target->ToPlayer();
|
||||||
|
if (targetPlayer && !group->IsMember(targetPlayer->GetGUID()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool hasBwisdom = botAI->HasStrategy("bwisdom", BOT_STATE_NON_COMBAT);
|
||||||
|
const bool hasBkings = botAI->HasStrategy("bkings", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
|
if (hasBwisdom && (!targetPlayer || !IsTankRole(targetPlayer)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
const bool isTank = IsTankRole(targetPlayer);
|
||||||
|
const bool hasSanctFromMe =
|
||||||
|
target->HasAura(ai::paladin::SPELL_BLESSING_OF_SANCTUARY, bot->GetGUID()) ||
|
||||||
|
target->HasAura(ai::paladin::SPELL_GREATER_BLESSING_OF_SANCTUARY, bot->GetGUID());
|
||||||
|
const bool hasSanctAny =
|
||||||
|
botAI->HasAura("blessing of sanctuary", target) ||
|
||||||
|
botAI->HasAura("greater blessing of sanctuary", target);
|
||||||
|
|
||||||
|
if (isTank && hasSanctFromMe)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hasBkings && isTank && hasSanctAny)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->CastSpell("blessing of kings", target);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastSealSpellAction::isUseful()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(bool, "combat", "self target");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastTurnUndeadAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>("cc target", getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastHandOfFreedomOnPartyAction::GetTarget()
|
||||||
|
{
|
||||||
|
bool const selfImpaired = botAI->IsMovementImpaired(bot);
|
||||||
|
bool const hasSelfHand =
|
||||||
|
selfImpaired && ai::paladin::HasAnyPaladinHandFromCaster(bot, bot);
|
||||||
|
|
||||||
|
if (!bot->GetGroup())
|
||||||
|
{
|
||||||
|
if (selfImpaired && !hasSelfHand)
|
||||||
|
return bot;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfImpaired && !hasSelfHand)
|
||||||
|
return bot;
|
||||||
|
|
||||||
|
return CastBuffSpellAction::GetTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastHandOfFreedomOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>("party member snared target");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastHandOfFreedomOnPartyAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return CastBuffSpellAction::isUseful() &&
|
||||||
|
!ai::paladin::HasAnyPaladinHandFromCaster(target, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* CastRighteousDefenseAction::GetTarget()
|
||||||
|
{
|
||||||
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!current_target)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return current_target->GetVictim();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastDivineSacrificeAction::isUseful()
|
||||||
|
{
|
||||||
|
return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() &&
|
||||||
|
!botAI->HasAura("divine guardian", GetTarget(), false, false, -1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastCancelDivineSacrificeAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
botAI->RemoveAura("divine sacrifice");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastCancelDivineSacrificeAction::isUseful()
|
||||||
|
{
|
||||||
|
return botAI->HasAura("divine sacrifice", GetTarget(), false, true, -1, true);
|
||||||
|
}
|
||||||
@ -8,10 +8,6 @@
|
|||||||
|
|
||||||
#include "AiObject.h"
|
#include "AiObject.h"
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "SharedDefines.h"
|
|
||||||
|
|
||||||
class PlayerbotAI;
|
|
||||||
class Unit;
|
|
||||||
|
|
||||||
// seals
|
// seals
|
||||||
BUFF_ACTION(CastSealOfRighteousnessAction, "seal of righteousness");
|
BUFF_ACTION(CastSealOfRighteousnessAction, "seal of righteousness");
|
||||||
@ -88,24 +84,13 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastBlessingOnPartyAction : public BuffOnPartyAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastBlessingOnPartyAction(PlayerbotAI* botAI, std::string const name)
|
|
||||||
: BuffOnPartyAction(botAI, name), name(name) {}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastBlessingOfMightOnPartyAction : public BuffOnPartyAction
|
class CastBlessingOfMightOnPartyAction : public BuffOnPartyAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastBlessingOfMightOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of might") {}
|
CastBlessingOfMightOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of might") {}
|
||||||
|
|
||||||
std::string const getName() override { return "blessing of might on party"; }
|
std::string const getName() override { return "blessing of might on party"; }
|
||||||
|
Unit* GetTarget() override;
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
@ -124,6 +109,7 @@ public:
|
|||||||
CastBlessingOfWisdomOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of wisdom") {}
|
CastBlessingOfWisdomOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of wisdom") {}
|
||||||
|
|
||||||
std::string const getName() override { return "blessing of wisdom on party"; }
|
std::string const getName() override { return "blessing of wisdom on party"; }
|
||||||
|
Unit* GetTarget() override;
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
@ -134,12 +120,13 @@ public:
|
|||||||
CastBlessingOfKingsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "blessing of kings") {}
|
CastBlessingOfKingsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "blessing of kings") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastBlessingOfKingsOnPartyAction : public CastBlessingOnPartyAction
|
class CastBlessingOfKingsOnPartyAction : public BuffOnPartyAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastBlessingOfKingsOnPartyAction(PlayerbotAI* botAI) : CastBlessingOnPartyAction(botAI, "blessing of kings") {}
|
CastBlessingOfKingsOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of kings") {}
|
||||||
|
|
||||||
std::string const getName() override { return "blessing of kings on party"; }
|
std::string const getName() override { return "blessing of kings on party"; }
|
||||||
|
Unit* GetTarget() override;
|
||||||
Value<Unit*>* GetTargetValue() override; // added for Sanctuary priority
|
Value<Unit*>* GetTargetValue() override; // added for Sanctuary priority
|
||||||
bool Execute(Event event) override; // added for 2 paladins logic
|
bool Execute(Event event) override; // added for 2 paladins logic
|
||||||
};
|
};
|
||||||
@ -156,6 +143,7 @@ public:
|
|||||||
CastBlessingOfSanctuaryOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of sanctuary") {}
|
CastBlessingOfSanctuaryOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of sanctuary") {}
|
||||||
|
|
||||||
std::string const getName() override { return "blessing of sanctuary on party"; }
|
std::string const getName() override { return "blessing of sanctuary on party"; }
|
||||||
|
Unit* GetTarget() override;
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
1116
src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp
Normal file
1116
src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
267
src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.h
Normal file
267
src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.h
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_PALADINGREATERBLESSINGACTION_H
|
||||||
|
#define _PLAYERBOT_PALADINGREATERBLESSINGACTION_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
|
class UntypedValue;
|
||||||
|
|
||||||
|
namespace ai::gbless
|
||||||
|
{
|
||||||
|
enum RoleProfile : uint8
|
||||||
|
{
|
||||||
|
ROLE_CASTER = 0,
|
||||||
|
ROLE_PHYSICAL_DPS = 1,
|
||||||
|
ROLE_HYBRID_DPS = 2,
|
||||||
|
ROLE_DRUID_TANK = 3,
|
||||||
|
ROLE_WARRIOR_DK_TANK = 4,
|
||||||
|
ROLE_PALADIN_TANK = 5,
|
||||||
|
|
||||||
|
ROLE_PROFILE_COUNT = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BlessingType : uint8
|
||||||
|
{
|
||||||
|
BLESSING_NONE = 0,
|
||||||
|
BLESSING_MIGHT_SINGLE = 1,
|
||||||
|
BLESSING_MIGHT_GREATER = 2,
|
||||||
|
BLESSING_WISDOM_SINGLE = 3,
|
||||||
|
BLESSING_WISDOM_GREATER = 4,
|
||||||
|
BLESSING_KINGS_SINGLE = 5,
|
||||||
|
BLESSING_KINGS_GREATER = 6,
|
||||||
|
BLESSING_SANCTUARY_SINGLE = 7,
|
||||||
|
BLESSING_SANCTUARY_GREATER = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BaseBlessingCategory : uint8
|
||||||
|
{
|
||||||
|
BASE_NONE = 0,
|
||||||
|
BASE_MIGHT = 1,
|
||||||
|
BASE_WISDOM = 2,
|
||||||
|
BASE_KINGS = 3,
|
||||||
|
BASE_SANCTUARY = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr BaseBlessingCategory BaseBlessingOf(BlessingType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BLESSING_MIGHT_SINGLE:
|
||||||
|
case BLESSING_MIGHT_GREATER: return BASE_MIGHT;
|
||||||
|
case BLESSING_WISDOM_SINGLE:
|
||||||
|
case BLESSING_WISDOM_GREATER: return BASE_WISDOM;
|
||||||
|
case BLESSING_KINGS_SINGLE:
|
||||||
|
case BLESSING_KINGS_GREATER: return BASE_KINGS;
|
||||||
|
case BLESSING_SANCTUARY_SINGLE:
|
||||||
|
case BLESSING_SANCTUARY_GREATER: return BASE_SANCTUARY;
|
||||||
|
default: return BASE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool IsSingleVariant(BlessingType type)
|
||||||
|
{
|
||||||
|
return type == BLESSING_MIGHT_SINGLE || type == BLESSING_WISDOM_SINGLE ||
|
||||||
|
type == BLESSING_KINGS_SINGLE || type == BLESSING_SANCTUARY_SINGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool IsGreaterVariant(BlessingType type)
|
||||||
|
{
|
||||||
|
return type == BLESSING_MIGHT_GREATER || type == BLESSING_WISDOM_GREATER ||
|
||||||
|
type == BLESSING_KINGS_GREATER || type == BLESSING_SANCTUARY_GREATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BlessingType ToSingleVariant(BaseBlessingCategory category)
|
||||||
|
{
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case BASE_MIGHT: return BLESSING_MIGHT_SINGLE;
|
||||||
|
case BASE_WISDOM: return BLESSING_WISDOM_SINGLE;
|
||||||
|
case BASE_KINGS: return BLESSING_KINGS_SINGLE;
|
||||||
|
case BASE_SANCTUARY: return BLESSING_SANCTUARY_SINGLE;
|
||||||
|
default: return BLESSING_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BlessingType ToSingleVariant(BlessingType type)
|
||||||
|
{
|
||||||
|
return ToSingleVariant(BaseBlessingOf(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BlessingType ToGreaterVariant(BaseBlessingCategory category)
|
||||||
|
{
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case BASE_MIGHT: return BLESSING_MIGHT_GREATER;
|
||||||
|
case BASE_WISDOM: return BLESSING_WISDOM_GREATER;
|
||||||
|
case BASE_KINGS: return BLESSING_KINGS_GREATER;
|
||||||
|
case BASE_SANCTUARY: return BLESSING_SANCTUARY_GREATER;
|
||||||
|
default: return BLESSING_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BlessingType ToGreaterVariant(BlessingType type)
|
||||||
|
{
|
||||||
|
return ToGreaterVariant(BaseBlessingOf(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string BlessingSpellName(BlessingType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BLESSING_MIGHT_SINGLE: return "blessing of might";
|
||||||
|
case BLESSING_MIGHT_GREATER: return "greater blessing of might";
|
||||||
|
case BLESSING_WISDOM_SINGLE: return "blessing of wisdom";
|
||||||
|
case BLESSING_WISDOM_GREATER: return "greater blessing of wisdom";
|
||||||
|
case BLESSING_KINGS_SINGLE: return "blessing of kings";
|
||||||
|
case BLESSING_KINGS_GREATER: return "greater blessing of kings";
|
||||||
|
case BLESSING_SANCTUARY_SINGLE: return "blessing of sanctuary";
|
||||||
|
case BLESSING_SANCTUARY_GREATER: return "greater blessing of sanctuary";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BaseBlessingPriorityEntry
|
||||||
|
{
|
||||||
|
BaseBlessingCategory priorities[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr BaseBlessingPriorityEntry BASE_BLESSING_PRIORITIES[ROLE_PROFILE_COUNT] =
|
||||||
|
{
|
||||||
|
// All casters
|
||||||
|
{{ BASE_KINGS, BASE_WISDOM, BASE_SANCTUARY, BASE_MIGHT }},
|
||||||
|
// Physical DPS (no mana)
|
||||||
|
{{ BASE_MIGHT, BASE_KINGS, BASE_SANCTUARY, BASE_NONE }},
|
||||||
|
// Hybrid DPS
|
||||||
|
{{ BASE_MIGHT, BASE_KINGS, BASE_WISDOM, BASE_SANCTUARY }},
|
||||||
|
// Druid tanks
|
||||||
|
{{ BASE_KINGS, BASE_MIGHT, BASE_SANCTUARY, BASE_WISDOM, }},
|
||||||
|
// Warrior and DK tanks
|
||||||
|
{{ BASE_KINGS, BASE_MIGHT, BASE_SANCTUARY, BASE_NONE }},
|
||||||
|
// Paladin tanks
|
||||||
|
{{ BASE_SANCTUARY, BASE_MIGHT, BASE_WISDOM, BASE_KINGS }},
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint32 SPELL_IMPROVED_MIGHT_R1 = 20042;
|
||||||
|
constexpr uint32 SPELL_IMPROVED_MIGHT_R2 = 20045;
|
||||||
|
constexpr uint32 SPELL_IMPROVED_WISDOM_R1 = 20244;
|
||||||
|
constexpr uint32 SPELL_IMPROVED_WISDOM_R2 = 20245;
|
||||||
|
|
||||||
|
inline RoleProfile ResolveRoleProfile(Player* player)
|
||||||
|
{
|
||||||
|
if (!player)
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
uint8 cls = player->getClass();
|
||||||
|
int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
|
bool isTank = PlayerbotAI::IsTank(player);
|
||||||
|
|
||||||
|
switch (cls)
|
||||||
|
{
|
||||||
|
case CLASS_WARRIOR:
|
||||||
|
if (isTank)
|
||||||
|
return ROLE_WARRIOR_DK_TANK;
|
||||||
|
return ROLE_PHYSICAL_DPS;
|
||||||
|
|
||||||
|
case CLASS_DEATH_KNIGHT:
|
||||||
|
if (isTank)
|
||||||
|
return ROLE_WARRIOR_DK_TANK;
|
||||||
|
return ROLE_PHYSICAL_DPS;
|
||||||
|
|
||||||
|
case CLASS_SHAMAN:
|
||||||
|
if (tab == SHAMAN_TAB_ENHANCEMENT)
|
||||||
|
return ROLE_HYBRID_DPS;
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
case CLASS_PALADIN:
|
||||||
|
if (isTank)
|
||||||
|
return ROLE_PALADIN_TANK;
|
||||||
|
if (tab == PALADIN_TAB_HOLY)
|
||||||
|
return ROLE_CASTER;
|
||||||
|
return ROLE_HYBRID_DPS;
|
||||||
|
|
||||||
|
case CLASS_DRUID:
|
||||||
|
if (tab == DRUID_TAB_FERAL)
|
||||||
|
return isTank ? ROLE_DRUID_TANK : ROLE_HYBRID_DPS;
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
case CLASS_ROGUE:
|
||||||
|
return ROLE_PHYSICAL_DPS;
|
||||||
|
|
||||||
|
case CLASS_HUNTER:
|
||||||
|
return ROLE_HYBRID_DPS;
|
||||||
|
|
||||||
|
case CLASS_MAGE:
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
case CLASS_WARLOCK:
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
case CLASS_PRIEST:
|
||||||
|
return ROLE_CASTER;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ROLE_CASTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GreaterBlessingPlayerAssignment
|
||||||
|
{
|
||||||
|
Player* player = nullptr;
|
||||||
|
BlessingType blessing = BLESSING_NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CachedBlessingBucketAssignment
|
||||||
|
{
|
||||||
|
uint8 classId = 0;
|
||||||
|
RoleProfile role = ROLE_CASTER;
|
||||||
|
bool byRole = false;
|
||||||
|
BlessingType blessing = BLESSING_NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CachedBlessingAssignments
|
||||||
|
{
|
||||||
|
uint32 groupKey = 0;
|
||||||
|
bool valid = false;
|
||||||
|
std::vector<CachedBlessingBucketAssignment> assignments;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CachedPendingBlessingAssignment
|
||||||
|
{
|
||||||
|
uint32 groupKey = 0;
|
||||||
|
bool valid = false;
|
||||||
|
GreaterBlessingPlayerAssignment assignment;
|
||||||
|
std::string spellName;
|
||||||
|
};
|
||||||
|
|
||||||
|
UntypedValue* greater_blessing_assignments_value(PlayerbotAI* botAI);
|
||||||
|
UntypedValue* greater_blessing_pending_assignment_value(PlayerbotAI* botAI);
|
||||||
|
bool IsEligibleGroupForAutoBlessings(Group const* group);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CastGreaterBlessingAssignmentAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastGreaterBlessingAssignmentAction(PlayerbotAI* botAI);
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
bool HasPendingAssignment();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool FindPendingAssignment(
|
||||||
|
ai::gbless::GreaterBlessingPlayerAssignment& outAssignment,
|
||||||
|
std::string& outSpellName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "DpsPaladinStrategy.h"
|
#include "DpsPaladinStrategy.h"
|
||||||
#include "GenericPaladinNonCombatStrategy.h"
|
#include "GenericPaladinNonCombatStrategy.h"
|
||||||
|
#include "PaladinGreaterBlessingAction.h"
|
||||||
#include "HealPaladinStrategy.h"
|
#include "HealPaladinStrategy.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "OffhealRetPaladinStrategy.h"
|
#include "OffhealRetPaladinStrategy.h"
|
||||||
@ -70,17 +71,17 @@ class PaladinBuffStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
|||||||
public:
|
public:
|
||||||
PaladinBuffStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
|
PaladinBuffStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
|
||||||
{
|
{
|
||||||
creators["bhealth"] = &PaladinBuffStrategyFactoryInternal::bhealth;
|
creators["bsanc"] = &PaladinBuffStrategyFactoryInternal::bsanc;
|
||||||
creators["bmana"] = &PaladinBuffStrategyFactoryInternal::bmana;
|
creators["bwisdom"] = &PaladinBuffStrategyFactoryInternal::bwisdom;
|
||||||
creators["bdps"] = &PaladinBuffStrategyFactoryInternal::bdps;
|
creators["bmight"] = &PaladinBuffStrategyFactoryInternal::bmight;
|
||||||
creators["bstats"] = &PaladinBuffStrategyFactoryInternal::bstats;
|
creators["bkings"] = &PaladinBuffStrategyFactoryInternal::bkings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* bhealth(PlayerbotAI* botAI) { return new PaladinBuffHealthStrategy(botAI); }
|
static Strategy* bsanc(PlayerbotAI* botAI) { return new PaladinBuffHealthStrategy(botAI); }
|
||||||
static Strategy* bmana(PlayerbotAI* botAI) { return new PaladinBuffManaStrategy(botAI); }
|
static Strategy* bwisdom(PlayerbotAI* botAI) { return new PaladinBuffManaStrategy(botAI); }
|
||||||
static Strategy* bdps(PlayerbotAI* botAI) { return new PaladinBuffDpsStrategy(botAI); }
|
static Strategy* bmight(PlayerbotAI* botAI) { return new PaladinBuffDpsStrategy(botAI); }
|
||||||
static Strategy* bstats(PlayerbotAI* botAI) { return new PaladinBuffStatsStrategy(botAI); }
|
static Strategy* bkings(PlayerbotAI* botAI) { return new PaladinBuffStatsStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
class PaladinCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||||
@ -154,6 +155,7 @@ public:
|
|||||||
creators["blessing of sanctuary on party"] = &PaladinTriggerFactoryInternal::blessing_of_sanctuary_on_party;
|
creators["blessing of sanctuary on party"] = &PaladinTriggerFactoryInternal::blessing_of_sanctuary_on_party;
|
||||||
|
|
||||||
creators["avenging wrath"] = &PaladinTriggerFactoryInternal::avenging_wrath;
|
creators["avenging wrath"] = &PaladinTriggerFactoryInternal::avenging_wrath;
|
||||||
|
creators["greater blessing needed"] = &PaladinTriggerFactoryInternal::greater_blessing_needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -211,8 +213,8 @@ private:
|
|||||||
static Trigger* repentance_on_enemy_healer(PlayerbotAI* botAI) { return new RepentanceOnHealerTrigger(botAI); }
|
static Trigger* repentance_on_enemy_healer(PlayerbotAI* botAI) { return new RepentanceOnHealerTrigger(botAI); }
|
||||||
static Trigger* repentance_on_snare_target(PlayerbotAI* botAI) { return new RepentanceSnareTrigger(botAI); }
|
static Trigger* repentance_on_snare_target(PlayerbotAI* botAI) { return new RepentanceSnareTrigger(botAI); }
|
||||||
static Trigger* repentance_interrupt(PlayerbotAI* botAI) { return new RepentanceInterruptTrigger(botAI); }
|
static Trigger* repentance_interrupt(PlayerbotAI* botAI) { return new RepentanceInterruptTrigger(botAI); }
|
||||||
static Trigger* beacon_of_light_on_main_tank(PlayerbotAI* ai) { return new BeaconOfLightOnMainTankTrigger(ai); }
|
static Trigger* beacon_of_light_on_main_tank(PlayerbotAI* botAI) { return new BeaconOfLightOnMainTankTrigger(botAI); }
|
||||||
static Trigger* sacred_shield_on_main_tank(PlayerbotAI* ai) { return new SacredShieldOnMainTankTrigger(ai); }
|
static Trigger* sacred_shield_on_main_tank(PlayerbotAI* botAI) { return new SacredShieldOnMainTankTrigger(botAI); }
|
||||||
static Trigger* hand_of_freedom_on_party(PlayerbotAI* botAI) { return new HandOfFreedomOnPartyTrigger(botAI); }
|
static Trigger* hand_of_freedom_on_party(PlayerbotAI* botAI) { return new HandOfFreedomOnPartyTrigger(botAI); }
|
||||||
|
|
||||||
static Trigger* blessing_of_kings_on_party(PlayerbotAI* botAI) { return new BlessingOfKingsOnPartyTrigger(botAI); }
|
static Trigger* blessing_of_kings_on_party(PlayerbotAI* botAI) { return new BlessingOfKingsOnPartyTrigger(botAI); }
|
||||||
@ -227,6 +229,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Trigger* avenging_wrath(PlayerbotAI* botAI) { return new AvengingWrathTrigger(botAI); }
|
static Trigger* avenging_wrath(PlayerbotAI* botAI) { return new AvengingWrathTrigger(botAI); }
|
||||||
|
static Trigger* greater_blessing_needed(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return new GreaterBlessingNeededTrigger(botAI);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinAiObjectContextInternal : public NamedObjectContext<Action>
|
class PaladinAiObjectContextInternal : public NamedObjectContext<Action>
|
||||||
@ -316,6 +322,8 @@ public:
|
|||||||
creators["divine sacrifice"] = &PaladinAiObjectContextInternal::divine_sacrifice;
|
creators["divine sacrifice"] = &PaladinAiObjectContextInternal::divine_sacrifice;
|
||||||
creators["cancel divine sacrifice"] = &PaladinAiObjectContextInternal::cancel_divine_sacrifice;
|
creators["cancel divine sacrifice"] = &PaladinAiObjectContextInternal::cancel_divine_sacrifice;
|
||||||
creators["hand of freedom on party"] = &PaladinAiObjectContextInternal::hand_of_freedom_on_party;
|
creators["hand of freedom on party"] = &PaladinAiObjectContextInternal::hand_of_freedom_on_party;
|
||||||
|
creators["cast greater blessing assignment"] =
|
||||||
|
&PaladinAiObjectContextInternal::cast_greater_blessing_assignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -414,15 +422,41 @@ private:
|
|||||||
static Action* sanctity_aura(PlayerbotAI* botAI) { return new CastSanctityAuraAction(botAI); }
|
static Action* sanctity_aura(PlayerbotAI* botAI) { return new CastSanctityAuraAction(botAI); }
|
||||||
static Action* holy_shock(PlayerbotAI* botAI) { return new CastHolyShockAction(botAI); }
|
static Action* holy_shock(PlayerbotAI* botAI) { return new CastHolyShockAction(botAI); }
|
||||||
static Action* holy_shock_on_party(PlayerbotAI* botAI) { return new CastHolyShockOnPartyAction(botAI); }
|
static Action* holy_shock_on_party(PlayerbotAI* botAI) { return new CastHolyShockOnPartyAction(botAI); }
|
||||||
static Action* divine_plea(PlayerbotAI* ai) { return new CastDivinePleaAction(ai); }
|
static Action* divine_plea(PlayerbotAI* botAI) { return new CastDivinePleaAction(botAI); }
|
||||||
static Action* shield_of_righteousness(PlayerbotAI* ai) { return new ShieldOfRighteousnessAction(ai); }
|
static Action* shield_of_righteousness(PlayerbotAI* botAI) { return new ShieldOfRighteousnessAction(botAI); }
|
||||||
static Action* beacon_of_light_on_main_tank(PlayerbotAI* ai) { return new CastBeaconOfLightOnMainTankAction(ai); }
|
static Action* beacon_of_light_on_main_tank(PlayerbotAI* botAI) { return new CastBeaconOfLightOnMainTankAction(botAI); }
|
||||||
static Action* sacred_shield_on_main_tank(PlayerbotAI* ai) { return new CastSacredShieldOnMainTankAction(ai); }
|
static Action* sacred_shield_on_main_tank(PlayerbotAI* botAI) { return new CastSacredShieldOnMainTankAction(botAI); }
|
||||||
static Action* avenging_wrath(PlayerbotAI* ai) { return new CastAvengingWrathAction(ai); }
|
static Action* avenging_wrath(PlayerbotAI* botAI) { return new CastAvengingWrathAction(botAI); }
|
||||||
static Action* divine_illumination(PlayerbotAI* ai) { return new CastDivineIlluminationAction(ai); }
|
static Action* divine_illumination(PlayerbotAI* botAI) { return new CastDivineIlluminationAction(botAI); }
|
||||||
static Action* divine_sacrifice(PlayerbotAI* ai) { return new CastDivineSacrificeAction(ai); }
|
static Action* divine_sacrifice(PlayerbotAI* botAI) { return new CastDivineSacrificeAction(botAI); }
|
||||||
static Action* cancel_divine_sacrifice(PlayerbotAI* ai) { return new CastCancelDivineSacrificeAction(ai); }
|
static Action* cancel_divine_sacrifice(PlayerbotAI* botAI) { return new CastCancelDivineSacrificeAction(botAI); }
|
||||||
static Action* hand_of_freedom_on_party(PlayerbotAI* ai) { return new CastHandOfFreedomOnPartyAction(ai); }
|
static Action* hand_of_freedom_on_party(PlayerbotAI* botAI) { return new CastHandOfFreedomOnPartyAction(botAI); }
|
||||||
|
static Action* cast_greater_blessing_assignment(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return new CastGreaterBlessingAssignmentAction(botAI);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PaladinValueContextInternal : public NamedObjectContext<UntypedValue>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PaladinValueContextInternal()
|
||||||
|
{
|
||||||
|
creators["greater blessing assignments"] = &PaladinValueContextInternal::greater_blessing_assignments;
|
||||||
|
creators["greater blessing pending assignment"] =
|
||||||
|
&PaladinValueContextInternal::greater_blessing_pending_assignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static UntypedValue* greater_blessing_assignments(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return ai::gbless::greater_blessing_assignments_value(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UntypedValue* greater_blessing_pending_assignment(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return ai::gbless::greater_blessing_pending_assignment_value(botAI);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SharedNamedObjectContextList<Strategy> PaladinAiObjectContext::sharedStrategyContexts;
|
SharedNamedObjectContextList<Strategy> PaladinAiObjectContext::sharedStrategyContexts;
|
||||||
@ -467,4 +501,5 @@ void PaladinAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContext
|
|||||||
void PaladinAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
void PaladinAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
||||||
{
|
{
|
||||||
AiObjectContext::BuildSharedValueContexts(valueContexts);
|
AiObjectContext::BuildSharedValueContexts(valueContexts);
|
||||||
|
valueContexts.Add(new PaladinValueContextInternal());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ static constexpr uint32 SPELL_HAND_OF_PROTECTION = 1022;
|
|||||||
static constexpr uint32 SPELL_HAND_OF_SALVATION = 1038;
|
static constexpr uint32 SPELL_HAND_OF_SALVATION = 1038;
|
||||||
static constexpr uint32 SPELL_HAND_OF_FREEDOM = 1044;
|
static constexpr uint32 SPELL_HAND_OF_FREEDOM = 1044;
|
||||||
static constexpr uint32 SPELL_HAND_OF_SACRIFICE = 6940;
|
static constexpr uint32 SPELL_HAND_OF_SACRIFICE = 6940;
|
||||||
|
static constexpr uint32 SPELL_BLESSING_OF_SANCTUARY = 20911;
|
||||||
|
static constexpr uint32 SPELL_GREATER_BLESSING_OF_SANCTUARY = 25899;
|
||||||
|
|
||||||
inline bool HasHandFromCaster(Unit* target, Player* caster, std::initializer_list<uint32> spellIds)
|
inline bool HasHandFromCaster(Unit* target, Player* caster, std::initializer_list<uint32> spellIds)
|
||||||
{
|
{
|
||||||
@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
#include "PaladinTriggers.h"
|
#include "PaladinTriggers.h"
|
||||||
|
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
|
#include "PaladinGreaterBlessingAction.h"
|
||||||
#include "PaladinActions.h"
|
#include "PaladinActions.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "PaladinHelper.h"
|
#include "PaladinHelper.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool SealTrigger::IsActive()
|
bool SealTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@ -28,7 +29,8 @@ bool CrusaderAuraTrigger::IsActive()
|
|||||||
bool BlessingTrigger::IsActive()
|
bool BlessingTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
return SpellTrigger::IsActive() && !botAI->HasAnyAuraOf(target, "blessing of might", "blessing of wisdom",
|
return SpellTrigger::IsActive() &&
|
||||||
|
!botAI->HasAnyAuraOf(target, "blessing of might", "blessing of wisdom",
|
||||||
"blessing of kings", "blessing of sanctuary", nullptr);
|
"blessing of kings", "blessing of sanctuary", nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +64,8 @@ bool HandOfFreedomOnPartyTrigger::IsActive()
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (target != bot && bot->GetExactDist2dSq(target->GetPositionX(), target->GetPositionY()) > 30.0f * 30.0f)
|
if (target != bot &&
|
||||||
|
bot->GetExactDist2dSq(target->GetPositionX(), target->GetPositionY()) > 30.0f * 30.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!botAI->CanCastSpell("hand of freedom", target))
|
if (!botAI->CanCastSpell("hand of freedom", target))
|
||||||
@ -75,3 +78,29 @@ bool NotSensingUndeadTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
return !botAI->HasAura("sense undead", bot);
|
return !botAI->HasAura("sense undead", bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GreaterBlessingNeededTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!ai::gbless::IsEligibleGroupForAutoBlessings(bot->GetGroup()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ai::buff::ShouldDeferGreaterBlessingAssignmentForRecentLogin(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
uint32 const groupKey = group ? group->GetLeaderGUID().GetCounter() : 0;
|
||||||
|
|
||||||
|
Value<ai::gbless::CachedPendingBlessingAssignment>* pendingValue =
|
||||||
|
context->GetValue<ai::gbless::CachedPendingBlessingAssignment>("greater blessing pending assignment");
|
||||||
|
if (!pendingValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ai::gbless::CachedPendingBlessingAssignment pendingAssignment = pendingValue->Get();
|
||||||
|
if (pendingAssignment.groupKey != groupKey)
|
||||||
|
{
|
||||||
|
pendingValue->Reset();
|
||||||
|
pendingAssignment = pendingValue->Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pendingAssignment.valid && pendingAssignment.groupKey == groupKey;
|
||||||
|
}
|
||||||
@ -13,32 +13,6 @@
|
|||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfMight(Unit* target)
|
|
||||||
{
|
|
||||||
switch (target->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_MAGE:
|
|
||||||
case CLASS_PRIEST:
|
|
||||||
case CLASS_WARLOCK:
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfWisdom(Unit* target)
|
|
||||||
{
|
|
||||||
switch (target->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_WARRIOR:
|
|
||||||
case CLASS_ROGUE:
|
|
||||||
case CLASS_DEATH_KNIGHT:
|
|
||||||
return "blessing of might";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "blessing of wisdom";
|
|
||||||
}
|
|
||||||
|
|
||||||
BUFF_TRIGGER(HolyShieldTrigger, "holy shield");
|
BUFF_TRIGGER(HolyShieldTrigger, "holy shield");
|
||||||
BUFF_TRIGGER(RighteousFuryTrigger, "righteous fury");
|
BUFF_TRIGGER(RighteousFuryTrigger, "righteous fury");
|
||||||
|
|
||||||
@ -212,42 +186,55 @@ DEBUFF_TRIGGER(AvengerShieldTrigger, "avenger's shield");
|
|||||||
class BeaconOfLightOnMainTankTrigger : public BuffOnMainTankTrigger
|
class BeaconOfLightOnMainTankTrigger : public BuffOnMainTankTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BeaconOfLightOnMainTankTrigger(PlayerbotAI* ai)
|
BeaconOfLightOnMainTankTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnMainTankTrigger(ai, "beacon of light", true) {}
|
: BuffOnMainTankTrigger(botAI, "beacon of light", true) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SacredShieldOnMainTankTrigger : public BuffOnMainTankTrigger
|
class SacredShieldOnMainTankTrigger : public BuffOnMainTankTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SacredShieldOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "sacred shield", false) {}
|
SacredShieldOnMainTankTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffOnMainTankTrigger(botAI, "sacred shield", false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlessingOfKingsOnPartyTrigger : public BuffOnPartyTrigger
|
class BlessingOfKingsOnPartyTrigger : public BlessingOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BlessingOfKingsOnPartyTrigger(PlayerbotAI* botAI)
|
BlessingOfKingsOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "blessing of kings", 2 * 2000) {}
|
: BlessingOnPartyTrigger(botAI)
|
||||||
|
{
|
||||||
|
spell = "blessing of kings";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlessingOfWisdomOnPartyTrigger : public BuffOnPartyTrigger
|
class BlessingOfWisdomOnPartyTrigger : public BlessingOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BlessingOfWisdomOnPartyTrigger(PlayerbotAI* botAI)
|
BlessingOfWisdomOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "blessing of might,blessing of wisdom", 2 * 2000) {}
|
: BlessingOnPartyTrigger(botAI)
|
||||||
|
{
|
||||||
|
spell = "blessing of might,blessing of wisdom";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlessingOfMightOnPartyTrigger : public BuffOnPartyTrigger
|
class BlessingOfMightOnPartyTrigger : public BlessingOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BlessingOfMightOnPartyTrigger(PlayerbotAI* botAI)
|
BlessingOfMightOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "blessing of might,blessing of wisdom", 2 * 2000) {}
|
: BlessingOnPartyTrigger(botAI)
|
||||||
|
{
|
||||||
|
spell = "blessing of might,blessing of wisdom";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlessingOfSanctuaryOnPartyTrigger : public BuffOnPartyTrigger
|
class BlessingOfSanctuaryOnPartyTrigger : public BlessingOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BlessingOfSanctuaryOnPartyTrigger(PlayerbotAI* botAI)
|
BlessingOfSanctuaryOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "blessing of sanctuary", 2 * 2000) {}
|
: BlessingOnPartyTrigger(botAI)
|
||||||
|
{
|
||||||
|
spell = "blessing of sanctuary";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class HandOfFreedomOnPartyTrigger : public Trigger
|
class HandOfFreedomOnPartyTrigger : public Trigger
|
||||||
@ -266,4 +253,13 @@ public:
|
|||||||
AvengingWrathTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "avenging wrath") {}
|
AvengingWrathTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "avenging wrath") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GreaterBlessingNeededTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GreaterBlessingNeededTrigger(PlayerbotAI* botAI)
|
||||||
|
: Trigger(botAI, "greater blessing needed", 4) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -30,4 +30,7 @@ void GenericPaladinNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
|
|||||||
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", ACTION_IDLE + 1.0f) }));
|
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", ACTION_IDLE + 1.0f) }));
|
||||||
if (specTab == PALADIN_TAB_PROTECTION || specTab == PALADIN_TAB_RETRIBUTION)
|
if (specTab == PALADIN_TAB_PROTECTION || specTab == PALADIN_TAB_RETRIBUTION)
|
||||||
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", ACTION_IDLE + 1.0f) }));
|
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", ACTION_IDLE + 1.0f) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("greater blessing needed",
|
||||||
|
{ NextAction("cast greater blessing assignment", ACTION_NORMAL) }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public:
|
|||||||
PaladinBuffManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
PaladinBuffManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "bmana"; }
|
std::string const getName() override { return "bwisdom"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinBuffHealthStrategy : public Strategy
|
class PaladinBuffHealthStrategy : public Strategy
|
||||||
@ -25,7 +25,7 @@ public:
|
|||||||
PaladinBuffHealthStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
PaladinBuffHealthStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "bhealth"; }
|
std::string const getName() override { return "bsanc"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinBuffDpsStrategy : public Strategy
|
class PaladinBuffDpsStrategy : public Strategy
|
||||||
@ -34,7 +34,7 @@ public:
|
|||||||
PaladinBuffDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
PaladinBuffDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "bdps"; }
|
std::string const getName() override { return "bmight"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinBuffArmorStrategy : public Strategy
|
class PaladinBuffArmorStrategy : public Strategy
|
||||||
@ -88,7 +88,7 @@ public:
|
|||||||
PaladinBuffStatsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
PaladinBuffStatsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "bstats"; }
|
std::string const getName() override { return "bkings"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaladinShadowResistanceStrategy : public Strategy
|
class PaladinShadowResistanceStrategy : public Strategy
|
||||||
|
|||||||
@ -95,7 +95,8 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(
|
||||||
|
new TriggerNode(
|
||||||
"light aoe",
|
"light aoe",
|
||||||
{
|
{
|
||||||
NextAction("avenger's shield", ACTION_HIGH + 5)
|
NextAction("avenger's shield", ACTION_HIGH + 5)
|
||||||
@ -122,21 +123,6 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode(
|
new TriggerNode(
|
||||||
"medium health",
|
"medium health",
|
||||||
{ NextAction("holy shield", ACTION_HIGH + 4)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode(
|
|
||||||
"low health",
|
|
||||||
{
|
|
||||||
NextAction("holy shield", ACTION_HIGH + 4)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode(
|
|
||||||
"critical health",
|
|
||||||
{
|
{
|
||||||
NextAction("holy shield", ACTION_HIGH + 4)
|
NextAction("holy shield", ACTION_HIGH + 4)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,19 @@
|
|||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
// disc
|
// disc
|
||||||
BUFF_ACTION(CastPowerWordFortitudeAction, "power word: fortitude");
|
class CastPowerWordFortitudeAction : public GroupBuffSpellAction
|
||||||
BUFF_PARTY_ACTION(CastPowerWordFortitudeOnPartyAction, "power word: fortitude");
|
{
|
||||||
BUFF_PARTY_ACTION(CastPrayerOfFortitudeOnPartyAction, "prayer of fortitude");
|
public:
|
||||||
|
CastPowerWordFortitudeAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffSpellAction(botAI, "power word: fortitude") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastPowerWordFortitudeOnPartyAction : public GroupBuffOnPartyAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastPowerWordFortitudeOnPartyAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffOnPartyAction(botAI, "power word: fortitude") {}
|
||||||
|
};
|
||||||
BUFF_ACTION(CastPowerWordShieldAction, "power word: shield");
|
BUFF_ACTION(CastPowerWordShieldAction, "power word: shield");
|
||||||
|
|
||||||
BUFF_ACTION(CastInnerFireAction, "inner fire");
|
BUFF_ACTION(CastInnerFireAction, "inner fire");
|
||||||
@ -26,9 +36,19 @@ CC_ACTION(CastShackleUndeadAction, "shackle undead");
|
|||||||
SPELL_ACTION_U(CastManaBurnAction, "mana burn",
|
SPELL_ACTION_U(CastManaBurnAction, "mana burn",
|
||||||
AI_VALUE2(uint8, "mana", "self target") < 50 && AI_VALUE2(uint8, "mana", "current target") >= 20);
|
AI_VALUE2(uint8, "mana", "self target") < 50 && AI_VALUE2(uint8, "mana", "current target") >= 20);
|
||||||
BUFF_ACTION(CastLevitateAction, "levitate");
|
BUFF_ACTION(CastLevitateAction, "levitate");
|
||||||
BUFF_ACTION(CastDivineSpiritAction, "divine spirit");
|
class CastDivineSpiritAction : public GroupBuffSpellAction
|
||||||
BUFF_PARTY_ACTION(CastDivineSpiritOnPartyAction, "divine spirit");
|
{
|
||||||
BUFF_PARTY_ACTION(CastPrayerOfSpiritOnPartyAction, "prayer of spirit");
|
public:
|
||||||
|
CastDivineSpiritAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffSpellAction(botAI, "divine spirit") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastDivineSpiritOnPartyAction : public GroupBuffOnPartyAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastDivineSpiritOnPartyAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffOnPartyAction(botAI, "divine spirit") {}
|
||||||
|
};
|
||||||
// disc 2.4.3
|
// disc 2.4.3
|
||||||
SPELL_ACTION(CastMassDispelAction, "mass dispel");
|
SPELL_ACTION(CastMassDispelAction, "mass dispel");
|
||||||
|
|
||||||
@ -103,13 +123,23 @@ SPELL_ACTION(CastMindBlastAction, "mind blast");
|
|||||||
SPELL_ACTION(CastPsychicScreamAction, "psychic scream");
|
SPELL_ACTION(CastPsychicScreamAction, "psychic scream");
|
||||||
DEBUFF_ACTION(CastMindSootheAction, "mind soothe");
|
DEBUFF_ACTION(CastMindSootheAction, "mind soothe");
|
||||||
BUFF_ACTION_U(CastFadeAction, "fade", bot->GetGroup());
|
BUFF_ACTION_U(CastFadeAction, "fade", bot->GetGroup());
|
||||||
BUFF_ACTION(CastShadowProtectionAction, "shadow protection");
|
class CastShadowProtectionAction : public GroupBuffSpellAction
|
||||||
BUFF_PARTY_ACTION(CastShadowProtectionOnPartyAction, "shadow protection");
|
{
|
||||||
BUFF_PARTY_ACTION(CastPrayerOfShadowProtectionAction, "prayer of shadow protection");
|
public:
|
||||||
|
CastShadowProtectionAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffSpellAction(botAI, "shadow protection") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastShadowProtectionOnPartyAction : public GroupBuffOnPartyAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastShadowProtectionOnPartyAction(PlayerbotAI* botAI)
|
||||||
|
: GroupBuffOnPartyAction(botAI, "shadow protection") {}
|
||||||
|
};
|
||||||
|
|
||||||
// shadow talents
|
// shadow talents
|
||||||
SPELL_ACTION(CastMindFlayAction, "mind flay");
|
SPELL_ACTION(CastMindFlayAction, "mind flay");
|
||||||
DEBUFF_ACTION(CastVampiricEmbraceAction, "vampiric embrace");
|
BUFF_ACTION(CastVampiricEmbraceAction, "vampiric embrace");
|
||||||
BUFF_ACTION(CastShadowformAction, "shadowform");
|
BUFF_ACTION(CastShadowformAction, "shadowform");
|
||||||
SPELL_ACTION(CastSilenceAction, "silence");
|
SPELL_ACTION(CastSilenceAction, "silence");
|
||||||
ENEMY_HEALER_ACTION(CastSilenceOnEnemyHealerAction, "silence");
|
ENEMY_HEALER_ACTION(CastSilenceOnEnemyHealerAction, "silence");
|
||||||
@ -92,8 +92,6 @@ public:
|
|||||||
creators["shadow protection"] = &PriestTriggerFactoryInternal::shadow_protection;
|
creators["shadow protection"] = &PriestTriggerFactoryInternal::shadow_protection;
|
||||||
creators["shadow protection on party"] = &PriestTriggerFactoryInternal::shadow_protection_on_party;
|
creators["shadow protection on party"] = &PriestTriggerFactoryInternal::shadow_protection_on_party;
|
||||||
creators["shackle undead"] = &PriestTriggerFactoryInternal::shackle_undead;
|
creators["shackle undead"] = &PriestTriggerFactoryInternal::shackle_undead;
|
||||||
creators["prayer of fortitude on party"] = &PriestTriggerFactoryInternal::prayer_of_fortitude_on_party;
|
|
||||||
creators["prayer of spirit on party"] = &PriestTriggerFactoryInternal::prayer_of_spirit_on_party;
|
|
||||||
creators["holy fire"] = &PriestTriggerFactoryInternal::holy_fire;
|
creators["holy fire"] = &PriestTriggerFactoryInternal::holy_fire;
|
||||||
creators["touch of weakness"] = &PriestTriggerFactoryInternal::touch_of_weakness;
|
creators["touch of weakness"] = &PriestTriggerFactoryInternal::touch_of_weakness;
|
||||||
creators["hex of weakness"] = &PriestTriggerFactoryInternal::hex_of_weakness;
|
creators["hex of weakness"] = &PriestTriggerFactoryInternal::hex_of_weakness;
|
||||||
@ -136,8 +134,6 @@ private:
|
|||||||
static Trigger* shadow_protection_on_party(PlayerbotAI* botAI) { return new ShadowProtectionOnPartyTrigger(botAI); }
|
static Trigger* shadow_protection_on_party(PlayerbotAI* botAI) { return new ShadowProtectionOnPartyTrigger(botAI); }
|
||||||
static Trigger* shadow_protection(PlayerbotAI* botAI) { return new ShadowProtectionTrigger(botAI); }
|
static Trigger* shadow_protection(PlayerbotAI* botAI) { return new ShadowProtectionTrigger(botAI); }
|
||||||
static Trigger* shackle_undead(PlayerbotAI* botAI) { return new ShackleUndeadTrigger(botAI); }
|
static Trigger* shackle_undead(PlayerbotAI* botAI) { return new ShackleUndeadTrigger(botAI); }
|
||||||
static Trigger* prayer_of_fortitude_on_party(PlayerbotAI* botAI) { return new PrayerOfFortitudeTrigger(botAI); }
|
|
||||||
static Trigger* prayer_of_spirit_on_party(PlayerbotAI* botAI) { return new PrayerOfSpiritTrigger(botAI); }
|
|
||||||
static Trigger* feedback(PlayerbotAI* botAI) { return new FeedbackTrigger(botAI); }
|
static Trigger* feedback(PlayerbotAI* botAI) { return new FeedbackTrigger(botAI); }
|
||||||
static Trigger* fear_ward(PlayerbotAI* botAI) { return new FearWardTrigger(botAI); }
|
static Trigger* fear_ward(PlayerbotAI* botAI) { return new FearWardTrigger(botAI); }
|
||||||
static Trigger* shadowguard(PlayerbotAI* botAI) { return new ShadowguardTrigger(botAI); }
|
static Trigger* shadowguard(PlayerbotAI* botAI) { return new ShadowguardTrigger(botAI); }
|
||||||
@ -207,8 +203,6 @@ public:
|
|||||||
creators["shadow protection"] = &PriestAiObjectContextInternal::shadow_protection;
|
creators["shadow protection"] = &PriestAiObjectContextInternal::shadow_protection;
|
||||||
creators["shadow protection on party"] = &PriestAiObjectContextInternal::shadow_protection_on_party;
|
creators["shadow protection on party"] = &PriestAiObjectContextInternal::shadow_protection_on_party;
|
||||||
creators["shackle undead"] = &PriestAiObjectContextInternal::shackle_undead;
|
creators["shackle undead"] = &PriestAiObjectContextInternal::shackle_undead;
|
||||||
creators["prayer of fortitude on party"] = &PriestAiObjectContextInternal::prayer_of_fortitude_on_party;
|
|
||||||
creators["prayer of spirit on party"] = &PriestAiObjectContextInternal::prayer_of_spirit_on_party;
|
|
||||||
creators["power infusion on party"] = &PriestAiObjectContextInternal::power_infusion_on_party;
|
creators["power infusion on party"] = &PriestAiObjectContextInternal::power_infusion_on_party;
|
||||||
creators["silence"] = &PriestAiObjectContextInternal::silence;
|
creators["silence"] = &PriestAiObjectContextInternal::silence;
|
||||||
creators["silence on enemy healer"] = &PriestAiObjectContextInternal::silence_on_enemy_healer;
|
creators["silence on enemy healer"] = &PriestAiObjectContextInternal::silence_on_enemy_healer;
|
||||||
@ -311,11 +305,6 @@ private:
|
|||||||
static Action* fade(PlayerbotAI* botAI) { return new CastFadeAction(botAI); }
|
static Action* fade(PlayerbotAI* botAI) { return new CastFadeAction(botAI); }
|
||||||
static Action* inner_fire(PlayerbotAI* botAI) { return new CastInnerFireAction(botAI); }
|
static Action* inner_fire(PlayerbotAI* botAI) { return new CastInnerFireAction(botAI); }
|
||||||
static Action* shackle_undead(PlayerbotAI* botAI) { return new CastShackleUndeadAction(botAI); }
|
static Action* shackle_undead(PlayerbotAI* botAI) { return new CastShackleUndeadAction(botAI); }
|
||||||
static Action* prayer_of_spirit_on_party(PlayerbotAI* botAI) { return new CastPrayerOfSpiritOnPartyAction(botAI); }
|
|
||||||
static Action* prayer_of_fortitude_on_party(PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new CastPrayerOfFortitudeOnPartyAction(botAI);
|
|
||||||
}
|
|
||||||
static Action* feedback(PlayerbotAI* botAI) { return new CastFeedbackAction(botAI); }
|
static Action* feedback(PlayerbotAI* botAI) { return new CastFeedbackAction(botAI); }
|
||||||
static Action* elunes_grace(PlayerbotAI* botAI) { return new CastElunesGraceAction(botAI); }
|
static Action* elunes_grace(PlayerbotAI* botAI) { return new CastElunesGraceAction(botAI); }
|
||||||
static Action* starshards(PlayerbotAI* botAI) { return new CastStarshardsAction(botAI); }
|
static Action* starshards(PlayerbotAI* botAI) { return new CastStarshardsAction(botAI); }
|
||||||
|
|||||||
@ -8,10 +8,9 @@
|
|||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool PowerWordFortitudeOnPartyTrigger::IsActive()
|
bool ShadowProtectionTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("power word : fortitude", GetTarget()) &&
|
return BuffTrigger::IsActive() && !botAI->HasAura("prayer of shadow protection", GetTarget());
|
||||||
!botAI->HasAura("prayer of fortitude", GetTarget());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PowerWordFortitudeTrigger::IsActive()
|
bool PowerWordFortitudeTrigger::IsActive()
|
||||||
@ -20,43 +19,12 @@ bool PowerWordFortitudeTrigger::IsActive()
|
|||||||
!botAI->HasAura("prayer of fortitude", GetTarget());
|
!botAI->HasAura("prayer of fortitude", GetTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivineSpiritOnPartyTrigger::IsActive()
|
|
||||||
{
|
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("divine spirit", GetTarget()) &&
|
|
||||||
!botAI->HasAura("prayer of spirit", GetTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DivineSpiritTrigger::IsActive()
|
bool DivineSpiritTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return BuffTrigger::IsActive() && !botAI->HasAura("divine spirit", GetTarget()) &&
|
return BuffTrigger::IsActive() && !botAI->HasAura("divine spirit", GetTarget()) &&
|
||||||
!botAI->HasAura("prayer of spirit", GetTarget());
|
!botAI->HasAura("prayer of spirit", GetTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrayerOfFortitudeTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target || !target->IsPlayer())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("prayer of fortitude", GetTarget()) &&
|
|
||||||
botAI->GetBot()->IsInSameGroupWith((Player*)GetTarget()) &&
|
|
||||||
botAI->GetBuffedCount((Player*)GetTarget(), "prayer of fortitude") < 4 &&
|
|
||||||
!botAI->GetBuffedCount((Player*)GetTarget(), "power word: fortitude");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PrayerOfSpiritTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Unit* target = GetTarget();
|
|
||||||
if (!target || !target->IsPlayer())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("prayer of spirit", GetTarget()) &&
|
|
||||||
botAI->GetBot()->IsInSameGroupWith((Player*)GetTarget()) &&
|
|
||||||
// botAI->GetManaPercent() > 50 &&
|
|
||||||
botAI->GetBuffedCount((Player*)GetTarget(), "prayer of spirit") < 4 &&
|
|
||||||
!botAI->GetBuffedCount((Player*)GetTarget(), "divine spirit");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InnerFireTrigger::IsActive()
|
bool InnerFireTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
@ -27,8 +27,6 @@ BUFF_TRIGGER_A(InnerFireTrigger, "inner fire");
|
|||||||
BUFF_TRIGGER_A(ShadowformTrigger, "shadowform");
|
BUFF_TRIGGER_A(ShadowformTrigger, "shadowform");
|
||||||
BOOST_TRIGGER(PowerInfusionTrigger, "power infusion");
|
BOOST_TRIGGER(PowerInfusionTrigger, "power infusion");
|
||||||
BUFF_TRIGGER(InnerFocusTrigger, "inner focus");
|
BUFF_TRIGGER(InnerFocusTrigger, "inner focus");
|
||||||
BUFF_TRIGGER(ShadowProtectionTrigger, "shadow protection");
|
|
||||||
BUFF_PARTY_TRIGGER(ShadowProtectionOnPartyTrigger, "shadow protection");
|
|
||||||
CC_TRIGGER(ShackleUndeadTrigger, "shackle undead");
|
CC_TRIGGER(ShackleUndeadTrigger, "shackle undead");
|
||||||
INTERRUPT_TRIGGER(SilenceTrigger, "silence");
|
INTERRUPT_TRIGGER(SilenceTrigger, "silence");
|
||||||
INTERRUPT_HEALER_TRIGGER(SilenceEnemyHealerTrigger, "silence");
|
INTERRUPT_HEALER_TRIGGER(SilenceEnemyHealerTrigger, "silence");
|
||||||
@ -44,20 +42,34 @@ SNARE_TRIGGER(ChastiseTrigger, "chastise");
|
|||||||
|
|
||||||
BOOST_TRIGGER_A(ShadowfiendTrigger, "shadowfiend");
|
BOOST_TRIGGER_A(ShadowfiendTrigger, "shadowfiend");
|
||||||
|
|
||||||
|
class ShadowProtectionTrigger : public BuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadowProtectionTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffTrigger(botAI, "shadow protection", 4 * 2000) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadowProtectionOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadowProtectionOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffOnPartyTrigger(botAI, "shadow protection", 4 * 2000) {}
|
||||||
|
};
|
||||||
|
|
||||||
class PowerWordFortitudeOnPartyTrigger : public BuffOnPartyTrigger
|
class PowerWordFortitudeOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PowerWordFortitudeOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "power word: fortitude", 4 * 2000)
|
PowerWordFortitudeOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
{
|
: BuffOnPartyTrigger(botAI, "power word: fortitude", 4 * 2000) {}
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PowerWordFortitudeTrigger : public BuffTrigger
|
class PowerWordFortitudeTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PowerWordFortitudeTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "power word: fortitude", 4 * 2000) {}
|
PowerWordFortitudeTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffTrigger(botAI, "power word: fortitude", 4 * 2000) {}
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -65,31 +77,15 @@ public:
|
|||||||
class DivineSpiritOnPartyTrigger : public BuffOnPartyTrigger
|
class DivineSpiritOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DivineSpiritOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "divine spirit", 4 * 2000) {}
|
DivineSpiritOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffOnPartyTrigger(botAI, "divine spirit", 4 * 2000) {}
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DivineSpiritTrigger : public BuffTrigger
|
class DivineSpiritTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DivineSpiritTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "divine spirit", 4 * 2000) {}
|
DivineSpiritTrigger(PlayerbotAI* botAI)
|
||||||
|
: BuffTrigger(botAI, "divine spirit", 4 * 2000) {}
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrayerOfFortitudeTrigger : public BuffOnPartyTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PrayerOfFortitudeTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "prayer of fortitude", 3 * 2000) {}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrayerOfSpiritTrigger : public BuffOnPartyTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PrayerOfSpiritTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "prayer of spirit", 2 * 2000) {}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
@ -106,9 +102,7 @@ class MindSearChannelCheckTrigger : public Trigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MindSearChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
|
MindSearChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
|
||||||
: Trigger(botAI, "mind sear channel check"), minEnemies(minEnemies)
|
: Trigger(botAI, "mind sear channel check"), minEnemies(minEnemies) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ void PriestNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("inner fire",{ NextAction("inner fire", 10.0f) }));
|
new TriggerNode("inner fire",{ NextAction("inner fire", 10.0f) }));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("vampiric embrace", { NextAction("vampiric embrace", 16.0f) }));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"party member dead",{ NextAction("remove shadowform", ACTION_CRITICAL_HEAL + 11),
|
"party member dead",{ NextAction("remove shadowform", ACTION_CRITICAL_HEAL + 11),
|
||||||
NextAction("resurrection", ACTION_CRITICAL_HEAL + 10) }));
|
NextAction("resurrection", ACTION_CRITICAL_HEAL + 10) }));
|
||||||
@ -54,12 +56,6 @@ void PriestBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
NonCombatStrategy::InitTriggers(triggers);
|
NonCombatStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("prayer of fortitude on party",
|
|
||||||
{ NextAction("prayer of fortitude on party", 12.0f) }));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("prayer of spirit on party",
|
|
||||||
{ NextAction("prayer of spirit on party", 14.0f) }));
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("power word: fortitude on party",
|
new TriggerNode("power word: fortitude on party",
|
||||||
{ NextAction("power word: fortitude on party", 11.0f) }));
|
{ NextAction("power word: fortitude on party", 11.0f) }));
|
||||||
|
|||||||
@ -30,8 +30,6 @@ public:
|
|||||||
creators["flash heal"] = &flash_heal;
|
creators["flash heal"] = &flash_heal;
|
||||||
creators["flash heal on party"] = &flash_heal_on_party;
|
creators["flash heal on party"] = &flash_heal_on_party;
|
||||||
creators["circle of healing on party"] = &circle_of_healing;
|
creators["circle of healing on party"] = &circle_of_healing;
|
||||||
creators["prayer of fortitude on party"] = &prayer_of_fortitude_on_party;
|
|
||||||
creators["prayer of spirit on party"] = &prayer_of_spirit_on_party;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -134,20 +132,6 @@ private:
|
|||||||
/*A*/ {},
|
/*A*/ {},
|
||||||
/*C*/ {});
|
/*C*/ {});
|
||||||
}
|
}
|
||||||
static ActionNode* prayer_of_fortitude_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("prayer of fortitude on party",
|
|
||||||
/*P*/ { NextAction("remove shadowform") },
|
|
||||||
/*A*/ { NextAction("power word: fortitude on party") },
|
|
||||||
/*C*/ {});
|
|
||||||
}
|
|
||||||
static ActionNode* prayer_of_spirit_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("prayer of spirit on party",
|
|
||||||
/*P*/ { NextAction("remove shadowform") },
|
|
||||||
/*A*/ { NextAction("divine spirit on party") },
|
|
||||||
/*C*/ {});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -51,14 +51,6 @@ void ShadowPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode(
|
|
||||||
"vampiric embrace",
|
|
||||||
{
|
|
||||||
NextAction("vampiric embrace", 16.0f)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode(
|
new TriggerNode(
|
||||||
"silence",
|
"silence",
|
||||||
|
|||||||
@ -161,9 +161,7 @@ void ShamanAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
}
|
}
|
||||||
else if (tab == SHAMAN_TAB_ENHANCEMENT)
|
else if (tab == SHAMAN_TAB_ENHANCEMENT)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("medium aoe",{ NextAction("magma totem", 24.0f),
|
triggers.push_back(new TriggerNode("medium aoe",{ NextAction("fire nova", 23.0f), }));
|
||||||
NextAction("fire nova", 23.0f), }));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("maelstrom weapon 5 and medium aoe", { NextAction("chain lightning", 22.0f), }));
|
triggers.push_back(new TriggerNode("maelstrom weapon 5 and medium aoe", { NextAction("chain lightning", 22.0f), }));
|
||||||
triggers.push_back(new TriggerNode("maelstrom weapon 4 and medium aoe", { NextAction("chain lightning", 21.0f), }));
|
triggers.push_back(new TriggerNode("maelstrom weapon 4 and medium aoe", { NextAction("chain lightning", 21.0f), }));
|
||||||
triggers.push_back(new TriggerNode("enemy within melee", { NextAction("fire nova", 5.1f), }));
|
triggers.push_back(new TriggerNode("enemy within melee", { NextAction("fire nova", 5.1f), }));
|
||||||
|
|||||||
@ -112,6 +112,7 @@ std::vector<NextAction> ArmsWarriorStrategy::getDefaultActions()
|
|||||||
return {
|
return {
|
||||||
NextAction("bladestorm", ACTION_DEFAULT + 0.2f),
|
NextAction("bladestorm", ACTION_DEFAULT + 0.2f),
|
||||||
NextAction("mortal strike", ACTION_DEFAULT + 0.1f),
|
NextAction("mortal strike", ACTION_DEFAULT + 0.1f),
|
||||||
|
NextAction("sunder armor", ACTION_DEFAULT + 0.05f),
|
||||||
NextAction("melee", ACTION_DEFAULT)
|
NextAction("melee", ACTION_DEFAULT)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "WarriorActions.h"
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
|
#include "AiFactory.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool CastBerserkerRageAction::isPossible()
|
bool CastBerserkerRageAction::isPossible()
|
||||||
@ -36,6 +37,27 @@ bool CastBerserkerRageAction::isUseful()
|
|||||||
|
|
||||||
bool CastSunderArmorAction::isUseful()
|
bool CastSunderArmorAction::isUseful()
|
||||||
{
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsTank(bot, false))
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || member == bot || !member->IsAlive() || !member->IsInWorld() ||
|
||||||
|
member->GetMapId() != bot->GetMapId())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member->getClass() == CLASS_WARRIOR &&
|
||||||
|
botAI->IsTank(member, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Aura* aura = botAI->GetAura("sunder armor", GetTarget(), false, true);
|
Aura* aura = botAI->GetAura("sunder armor", GetTarget(), false, true);
|
||||||
return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000;
|
return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000;
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "AuchenaiCryptsActions.h"
|
#include "ACActions.h"
|
||||||
|
|
||||||
class TbcDungeonAuchenaiCryptsActionContext : public NamedObjectContext<Action>
|
class TbcDungeonAuchenaiCryptsActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "AiFactory.h"
|
#include "AiFactory.h"
|
||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
#include "AuchenaiCryptsActions.h"
|
#include "ACActions.h"
|
||||||
|
|
||||||
// Shirrak the Dead Watcher
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
|
|
||||||
// Shirrak the Dead Watcher
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
#include "AuchenaiCryptsMultipliers.h"
|
#include "ACMultipliers.h"
|
||||||
#include "AuchenaiCryptsActions.h"
|
#include "ACActions.h"
|
||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
#include "FollowActions.h"
|
#include "FollowActions.h"
|
||||||
@ -1,6 +1,6 @@
|
|||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
#include "AuchenaiCryptsStrategy.h"
|
#include "ACStrategy.h"
|
||||||
#include "AuchenaiCryptsMultipliers.h"
|
#include "ACMultipliers.h"
|
||||||
|
|
||||||
void TbcDungeonAuchenaiCryptsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void TbcDungeonAuchenaiCryptsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "TriggerContext.h"
|
#include "TriggerContext.h"
|
||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
|
|
||||||
class TbcDungeonAuchenaiCryptsTriggerContext : public NamedObjectContext<Trigger>
|
class TbcDungeonAuchenaiCryptsTriggerContext : public NamedObjectContext<Trigger>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "AuchenaiCryptsTriggers.h"
|
#include "ACTriggers.h"
|
||||||
#include "AiObject.h"
|
#include "AiObject.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "OldKingdomActions.h"
|
#include "AKActions.h"
|
||||||
|
|
||||||
class WotlkDungeonOKActionContext : public NamedObjectContext<Action>
|
class WotlkDungeonOKActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "OldKingdomActions.h"
|
#include "AKActions.h"
|
||||||
|
|
||||||
bool AttackNadoxGuardianAction::Execute(Event /*event*/)
|
bool AttackNadoxGuardianAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -5,7 +5,7 @@
|
|||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "OldKingdomTriggers.h"
|
#include "AKTriggers.h"
|
||||||
|
|
||||||
class AttackNadoxGuardianAction : public AttackAction
|
class AttackNadoxGuardianAction : public AttackAction
|
||||||
{
|
{
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#include "OldKingdomMultipliers.h"
|
#include "AKMultipliers.h"
|
||||||
#include "OldKingdomActions.h"
|
#include "AKActions.h"
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "ChooseTargetActions.h"
|
#include "ChooseTargetActions.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "OldKingdomTriggers.h"
|
#include "AKTriggers.h"
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
|
||||||
float ElderNadoxMultiplier::GetValue(Action* action)
|
float ElderNadoxMultiplier::GetValue(Action* action)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "OldKingdomStrategy.h"
|
#include "AKStrategy.h"
|
||||||
#include "OldKingdomMultipliers.h"
|
#include "AKMultipliers.h"
|
||||||
|
|
||||||
void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||||
{
|
{
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "OldKingdomTriggers.h"
|
#include "AKTriggers.h"
|
||||||
|
|
||||||
class WotlkDungeonOKTriggerContext : public NamedObjectContext<Trigger>
|
class WotlkDungeonOKTriggerContext : public NamedObjectContext<Trigger>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "OldKingdomTriggers.h"
|
#include "AKTriggers.h"
|
||||||
#include "AiObject.h"
|
#include "AiObject.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "AzjolNerubActions.h"
|
#include "ANActions.h"
|
||||||
|
|
||||||
class WotlkDungeonANActionContext : public NamedObjectContext<Action>
|
class WotlkDungeonANActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "AzjolNerubActions.h"
|
#include "ANActions.h"
|
||||||
|
|
||||||
bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); }
|
bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||||
bool AttackWebWrapAction::Execute(Event /*event*/)
|
bool AttackWebWrapAction::Execute(Event /*event*/)
|
||||||
@ -5,7 +5,7 @@
|
|||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "AzjolNerubTriggers.h"
|
#include "ANTriggers.h"
|
||||||
|
|
||||||
class AttackWebWrapAction : public AttackAction
|
class AttackWebWrapAction : public AttackAction
|
||||||
{
|
{
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#include "AzjolNerubMultipliers.h"
|
#include "ANMultipliers.h"
|
||||||
#include "AzjolNerubActions.h"
|
#include "ANActions.h"
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "ChooseTargetActions.h"
|
#include "ChooseTargetActions.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "AzjolNerubTriggers.h"
|
#include "ANTriggers.h"
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
|
||||||
float KrikthirMultiplier::GetValue(Action* action)
|
float KrikthirMultiplier::GetValue(Action* action)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "AzjolNerubStrategy.h"
|
#include "ANStrategy.h"
|
||||||
#include "AzjolNerubMultipliers.h"
|
#include "ANMultipliers.h"
|
||||||
|
|
||||||
void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||||
{
|
{
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "AzjolNerubTriggers.h"
|
#include "ANTriggers.h"
|
||||||
|
|
||||||
class WotlkDungeonANTriggerContext : public NamedObjectContext<Trigger>
|
class WotlkDungeonANTriggerContext : public NamedObjectContext<Trigger>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "AzjolNerubTriggers.h"
|
#include "ANTriggers.h"
|
||||||
#include "AiObject.h"
|
#include "AiObject.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "CullingOfStratholmeActions.h"
|
#include "CoSActions.h"
|
||||||
|
|
||||||
class WotlkDungeonCoSActionContext : public NamedObjectContext<Action>
|
class WotlkDungeonCoSActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "CullingOfStratholmeActions.h"
|
#include "CoSActions.h"
|
||||||
|
|
||||||
bool ExplodeGhoulSpreadAction::Execute(Event /*event*/)
|
bool ExplodeGhoulSpreadAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -6,7 +6,7 @@
|
|||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "CullingOfStratholmeTriggers.h"
|
#include "CoSTriggers.h"
|
||||||
|
|
||||||
class ExplodeGhoulSpreadAction : public MovementAction
|
class ExplodeGhoulSpreadAction : public MovementAction
|
||||||
{
|
{
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#include "CullingOfStratholmeMultipliers.h"
|
#include "CoSMultipliers.h"
|
||||||
#include "CullingOfStratholmeActions.h"
|
#include "CoSActions.h"
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "ChooseTargetActions.h"
|
#include "ChooseTargetActions.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "CullingOfStratholmeTriggers.h"
|
#include "CoSTriggers.h"
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
|
||||||
float EpochMultiplier::GetValue(Action* action)
|
float EpochMultiplier::GetValue(Action* action)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user