diff --git a/src/Ai/Base/ActionContext.h b/src/Ai/Base/ActionContext.h index 3026cfd50..7cde4dbfd 100644 --- a/src/Ai/Base/ActionContext.h +++ b/src/Ai/Base/ActionContext.h @@ -175,6 +175,8 @@ public: creators["berserking"] = &ActionContext::berserking; creators["every man for himself"] = &ActionContext::every_man_for_himself; creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken; + creators["stoneform"] = &ActionContext::stoneform; + creators["escape artist"] = &ActionContext::escape_artist; creators["use trinket"] = &ActionContext::use_trinket; creators["auto talents"] = &ActionContext::auto_talents; creators["auto share quest"] = &ActionContext::auto_share_quest; @@ -380,6 +382,8 @@ private: static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); } static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); } static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); } + static Action* stoneform(PlayerbotAI* botAI) { return new CastStoneformAction(botAI); } + static Action* escape_artist(PlayerbotAI* botAI) { return new CastEscapeArtistAction(botAI); } static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); } static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); } static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); } diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index 657fda7cd..50621484f 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -491,32 +491,13 @@ bool CastVehicleSpellAction::Execute(Event /*event*/) bool CastEveryManForHimselfAction::isPossible() { uint32 spellId = AI_VALUE2(uint32, "spell id", spell); - return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId); -} - -bool CastEveryManForHimselfAction::isUseful() -{ - return (bot->HasAuraType(SPELL_AURA_MOD_STUN) || - bot->HasAuraType(SPELL_AURA_MOD_FEAR) || - bot->HasAuraType(SPELL_AURA_MOD_ROOT) || - bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || - bot->HasAuraType(SPELL_AURA_MOD_CHARM)) - && CastSpellAction::isUseful(); + return spellId && !HasSpellOrCategoryCooldown(bot, spellId); } bool CastWillOfTheForsakenAction::isPossible() { uint32 spellId = AI_VALUE2(uint32, "spell id", spell); - return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId); -} - -bool CastWillOfTheForsakenAction::isUseful() -{ - return (bot->HasAuraType(SPELL_AURA_MOD_FEAR) || - bot->HasAuraType(SPELL_AURA_MOD_CHARM) || - bot->HasAuraType(SPELL_AURA_AOE_CHARM) || - bot->HasAuraWithMechanic(1 << MECHANIC_SLEEP)) - && CastSpellAction::isUseful(); + return spellId && !HasSpellOrCategoryCooldown(bot, spellId); } bool UseTrinketAction::Execute(Event /*event*/) diff --git a/src/Ai/Base/Actions/GenericSpellActions.h b/src/Ai/Base/Actions/GenericSpellActions.h index c17d96907..1d6e464ef 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.h +++ b/src/Ai/Base/Actions/GenericSpellActions.h @@ -313,7 +313,6 @@ public: std::string const GetTargetName() override { return "self target"; } bool isPossible() override; - bool isUseful() override; }; class CastWillOfTheForsakenAction : public CastSpellAction @@ -323,7 +322,22 @@ public: std::string const GetTargetName() override { return "self target"; } bool isPossible() override; - bool isUseful() override; +}; + +class CastStoneformAction : public CastSpellAction +{ +public: + CastStoneformAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "stoneform") {} + + std::string const GetTargetName() override { return "self target"; } +}; + +class CastEscapeArtistAction : public CastSpellAction +{ +public: + CastEscapeArtistAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "escape artist") {} + + std::string const GetTargetName() override { return "self target"; } }; class UseTrinketAction : public Action diff --git a/src/Ai/Base/Strategy/RacialsStrategy.cpp b/src/Ai/Base/Strategy/RacialsStrategy.cpp index b5a84bbce..3382d3291 100644 --- a/src/Ai/Base/Strategy/RacialsStrategy.cpp +++ b/src/Ai/Base/Strategy/RacialsStrategy.cpp @@ -4,45 +4,104 @@ */ #include "RacialsStrategy.h" +#include "Playerbots.h" -class RacialsStrategyActionNodeFactory : public NamedObjectFactory +namespace { -public: - RacialsStrategyActionNodeFactory() { creators["lifeblood"] = &lifeblood; } - -private: - static ActionNode* lifeblood(PlayerbotAI* /*botAI*/) - { - return new ActionNode("lifeblood", - /*P*/ {}, - /*A*/ { NextAction("gift of the naaru") }, - /*C*/ {}); - } -}; - -void RacialsStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode("low health", { NextAction("lifeblood", ACTION_NORMAL + 5) })); - triggers.push_back( - new TriggerNode("medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) })); - triggers.push_back(new TriggerNode( - "low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) })); - - triggers.push_back(new TriggerNode( - "generic boost", { NextAction("blood fury", ACTION_NORMAL + 5), - NextAction("berserking", ACTION_NORMAL + 5), - NextAction("use trinket", ACTION_NORMAL + 4) })); - - triggers.push_back(new TriggerNode( - "loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) })); - - triggers.push_back(new TriggerNode( - "fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) })); - + constexpr uint32 SPELL_ARCANE_TORRENT_ENERGY = 25046; + constexpr uint32 SPELL_ARCANE_TORRENT_MANA = 28730; + constexpr uint32 SPELL_ARCANE_TORRENT_RUNIC_POWER = 50613; + constexpr uint32 SPELL_WAR_STOMP = 20549; + constexpr uint32 SPELL_BERSERKING = 26297; + constexpr uint32 SPELL_EVERY_MAN_FOR_HIMSELF = 59752; + constexpr uint32 SPELL_WILL_OF_THE_FORSAKEN = 7744; + constexpr uint32 SPELL_STONEFORM = 20594; + constexpr uint32 SPELL_ESCAPE_ARTIST = 20589; } RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI) { - actionNodeFactories.Add(new RacialsStrategyActionNodeFactory()); + // No custom ActionNodeFactory needed +} + +void RacialsStrategy::InitTriggers(std::vector& triggers) +{ + Player* bot = botAI->GetBot(); + + if (bot->HasSpell(SPELL_ARCANE_TORRENT_MANA)) + { + triggers.push_back(new TriggerNode( + "low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) })); + } + + if (bot->HasSpell(SPELL_ARCANE_TORRENT_ENERGY)) + { + triggers.push_back(new TriggerNode( + "low energy", { NextAction("arcane torrent", ACTION_NORMAL + 5) })); + } + + if (bot->HasSpell(SPELL_ARCANE_TORRENT_RUNIC_POWER)) + { + // No low runic power trigger exists; this trigger should be modified if one is added + triggers.push_back(new TriggerNode( + "generic boost", { NextAction("arcane torrent", ACTION_NORMAL + 5) })); + } + + if (bot->HasSpell(SPELL_WAR_STOMP)) + { + triggers.push_back(new TriggerNode( + "medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) })); + } + + if (bot->HasSpell(SPELL_BERSERKING)) + { + triggers.push_back(new TriggerNode( + "generic boost", { NextAction("berserking", ACTION_NORMAL + 5) })); + } + + if (bot->HasSpell(SPELL_EVERY_MAN_FOR_HIMSELF)) + { + triggers.push_back(new TriggerNode( + "loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) })); + } + + if (bot->HasSpell(SPELL_WILL_OF_THE_FORSAKEN)) + { + triggers.push_back(new TriggerNode( + "fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) })); + } + + if (bot->HasSpell(SPELL_STONEFORM)) + { + triggers.push_back(new TriggerNode( + "poison disease bleed", { NextAction("stoneform", ACTION_DISPEL) })); + } + + if (bot->HasSpell(SPELL_ESCAPE_ARTIST)) + { + triggers.push_back(new TriggerNode( + "movement impaired", { NextAction("escape artist", ACTION_EMERGENCY + 1) })); + } + + if (botAI->HasSpell("blood fury")) + { + triggers.push_back(new TriggerNode( + "generic boost", { NextAction("blood fury", ACTION_NORMAL + 5) })); + } + + if (botAI->HasSpell("gift of the naaru")) + { + // Currently targets self only + triggers.push_back(new TriggerNode( + "medium health", { NextAction("gift of the naaru", ACTION_LIGHT_HEAL + 5) })); + } + + if (botAI->HasSpell("lifeblood")) + { + triggers.push_back(new TriggerNode( + "medium health", { NextAction("lifeblood", ACTION_LIGHT_HEAL + 5) })); + } + + triggers.push_back(new TriggerNode( + "generic boost", { NextAction("use trinket", ACTION_NORMAL + 4) })); } diff --git a/src/Ai/Base/Trigger/GenericTriggers.cpp b/src/Ai/Base/Trigger/GenericTriggers.cpp index aac920b5b..308146467 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.cpp +++ b/src/Ai/Base/Trigger/GenericTriggers.cpp @@ -491,6 +491,19 @@ bool FearSleepSapTrigger::IsActive() bot->HasAuraWithMechanic(1 << MECHANIC_SAPPED); } +bool PoisonDiseaseBleedTrigger::IsActive() +{ + return botAI->HasAuraToDispel(bot, DISPEL_POISON) || + botAI->HasAuraToDispel(bot, DISPEL_DISEASE) || + bot->HasAuraWithMechanic(1 << MECHANIC_BLEED); +} + +bool MovementImpairedTrigger::IsActive() +{ + return botAI->IsMovementImpaired(bot) && + !botAI->HasAnyAuraOf(bot, "stealth", "prowl", nullptr); +} + bool HasAuraStackTrigger::IsActive() { return botAI->GetAura(getName(), GetTarget(), false, true, stack); diff --git a/src/Ai/Base/Trigger/GenericTriggers.h b/src/Ai/Base/Trigger/GenericTriggers.h index 3e662eb3b..418949a85 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.h +++ b/src/Ai/Base/Trigger/GenericTriggers.h @@ -752,6 +752,22 @@ public: bool IsActive() override; }; +class PoisonDiseaseBleedTrigger : public Trigger +{ +public: + PoisonDiseaseBleedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "poison disease bleed", 1) {} + + bool IsActive() override; +}; + +class MovementImpairedTrigger : public Trigger +{ +public: + MovementImpairedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "movement impaired", 1) {} + + bool IsActive() override; +}; + class IsSwimmingTrigger : public Trigger { public: diff --git a/src/Ai/Base/TriggerContext.h b/src/Ai/Base/TriggerContext.h index d440e5b5f..111597620 100644 --- a/src/Ai/Base/TriggerContext.h +++ b/src/Ai/Base/TriggerContext.h @@ -66,6 +66,8 @@ public: creators["loss of control"] = &TriggerContext::loss_of_control; creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep; creators["fear sleep sap"] = &TriggerContext::fear_sleep_sap; + creators["poison disease bleed"] = &TriggerContext::poison_disease_bleed; + creators["movement impaired"] = &TriggerContext::movement_impaired; creators["protect party member"] = &TriggerContext::protect_party_member; @@ -382,6 +384,8 @@ private: static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); } static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); } static Trigger* fear_sleep_sap(PlayerbotAI* botAI) { return new FearSleepSapTrigger(botAI); } + static Trigger* poison_disease_bleed(PlayerbotAI* botAI) { return new PoisonDiseaseBleedTrigger(botAI); } + static Trigger* movement_impaired(PlayerbotAI* botAI) { return new MovementImpairedTrigger(botAI); } static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI) { return new PartyMemberCriticalHealthTrigger(botAI);