From bb729e35b92d525458527805b725f7d963399dcc Mon Sep 17 00:00:00 2001 From: Yunfan Li <56597220+liyunfan1223@users.noreply.github.com> Date: Tue, 18 Feb 2025 22:55:44 +0800 Subject: [PATCH] Fix stuck on knockback, enhance movement & flee and trap weave strats (#980) * Hunter trap weave strats * Do not allow actions to stack * Remove trap weave by default * Refactor on Engine Co-authored-by: SaW * Remove unused funcs in Queue * Remove ExpireActionTime config --------- Co-authored-by: SaW --- conf/playerbots.conf.dist | 3 -- src/AiFactory.cpp | 4 ++ src/PlayerbotAIConfig.cpp | 1 - src/PlayerbotAIConfig.h | 2 +- src/strategy/Engine.cpp | 23 +++++------ src/strategy/Queue.cpp | 39 ------------------- src/strategy/Queue.h | 13 ------- src/strategy/actions/AttackAction.cpp | 3 +- src/strategy/generic/CombatStrategy.cpp | 3 -- src/strategy/generic/RangedCombatStrategy.cpp | 2 +- src/strategy/hunter/GenericHunterStrategy.cpp | 34 ++++++++++++++-- src/strategy/hunter/GenericHunterStrategy.h | 10 +++++ src/strategy/hunter/HunterActions.cpp | 17 ++++++++ src/strategy/hunter/HunterActions.h | 22 +++++++++++ src/strategy/hunter/HunterAiObjectContext.cpp | 11 +++++- src/strategy/hunter/HunterTriggers.cpp | 10 +++++ src/strategy/hunter/HunterTriggers.h | 8 ++++ src/strategy/mage/FireMageStrategy.cpp | 8 ++-- src/strategy/mage/GenericMageStrategy.cpp | 3 ++ src/strategy/mage/MageActions.cpp | 15 +++++++ src/strategy/mage/MageActions.h | 7 ++++ src/strategy/mage/MageAiObjectContext.cpp | 2 + src/strategy/triggers/RangeTriggers.cpp | 14 ++++++- 23 files changed, 173 insertions(+), 81 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 35b75442..b7b70dd4 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -305,9 +305,6 @@ AiPlayerbot.DisableMoveSplinePath = 0 # default: 3 AiPlayerbot.MaxMovementSearchTime = 3 -# Action expiration time -AiPlayerbot.ExpireActionTime = 5000 - # Max dispel auras duration AiPlayerbot.DispelAuraDuration = 700 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 7c60ff58..08339879 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -369,6 +369,10 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa case CLASS_HUNTER: engine->addStrategiesNoInit("dps", "aoe", "bdps", "dps assist", nullptr); engine->addStrategy("dps debuff", false); + // if (tab == HUNTER_TAB_SURVIVAL) + // { + // engine->addStrategy("trap weave", false); + // } break; case CLASS_ROGUE: if (tab == ROGUE_TAB_ASSASSINATION) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 0ff76fb6..a7568a85 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -71,7 +71,6 @@ bool PlayerbotAIConfig::Initialize() maxWaitForMove = sConfigMgr->GetOption("AiPlayerbot.MaxWaitForMove", 5000); disableMoveSplinePath = sConfigMgr->GetOption("AiPlayerbot.DisableMoveSplinePath", 0); maxMovementSearchTime = sConfigMgr->GetOption("AiPlayerbot.MaxMovementSearchTime", 3); - expireActionTime = sConfigMgr->GetOption("AiPlayerbot.ExpireActionTime", 5000); dispelAuraDuration = sConfigMgr->GetOption("AiPlayerbot.DispelAuraDuration", 7000); reactDelay = sConfigMgr->GetOption("AiPlayerbot.ReactDelay", 100); dynamicReactDelay = sConfigMgr->GetOption("AiPlayerbot.DynamicReactDelay", true); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 527fb6cd..ce2a0cb6 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -57,7 +57,7 @@ public: bool enabled; bool allowAccountBots, allowGuildBots; bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat; - uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime, + uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay; bool dynamicReactDelay; float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance, diff --git a/src/strategy/Engine.cpp b/src/strategy/Engine.cpp index ccdc3793..f15293b8 100644 --- a/src/strategy/Engine.cpp +++ b/src/strategy/Engine.cpp @@ -87,27 +87,23 @@ Engine::~Engine(void) void Engine::Reset() { strategyTypeMask = 0; + ActionNode* action = nullptr; - do - { - action = queue.Pop(); - if (!action) - break; + while ((action = queue.Pop()) != nullptr) + { delete action; - } while (true); + } - for (std::vector::iterator i = triggers.begin(); i != triggers.end(); i++) + for (TriggerNode* trigger : triggers) { - TriggerNode* trigger = *i; delete trigger; } triggers.clear(); - for (std::vector::iterator i = multipliers.begin(); i != multipliers.end(); i++) + for (Multiplier* multiplier : multipliers) { - Multiplier* multiplier = *i; delete multiplier; } @@ -244,8 +240,13 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) if (!actionExecuted) LogAction("No actions executed"); + + ActionNode* action = nullptr; + while ((action = queue.Pop()) != nullptr) + { + delete action; + } - queue.RemoveExpired(); // Clean up expired actions in the queue return actionExecuted; } diff --git a/src/strategy/Queue.cpp b/src/strategy/Queue.cpp index b2c6e88d..f1273294 100644 --- a/src/strategy/Queue.cpp +++ b/src/strategy/Queue.cpp @@ -48,18 +48,6 @@ uint32 Queue::Size() return actions.size(); } -void Queue::RemoveExpired() -{ - if (!sPlayerbotAIConfig->expireActionTime) - { - return; - } - - std::list expiredBaskets; - collectExpiredBaskets(expiredBaskets); - removeAndDeleteBaskets(expiredBaskets); -} - // Private helper methods void Queue::updateExistingBasket(ActionBasket* existing, ActionBasket* newBasket) { @@ -105,30 +93,3 @@ ActionNode* Queue::extractAndDeleteBasket(ActionBasket* basket) delete basket; return action; } - -void Queue::collectExpiredBaskets(std::list& expiredBaskets) -{ - uint32 expiryTime = sPlayerbotAIConfig->expireActionTime; - for (ActionBasket* basket : actions) - { - if (basket->isExpired(expiryTime)) - { - expiredBaskets.push_back(basket); - } - } -} - -void Queue::removeAndDeleteBaskets(std::list& basketsToRemove) -{ - for (ActionBasket* basket : basketsToRemove) - { - actions.remove(basket); - - if (ActionNode* action = basket->getAction()) - { - delete action; - } - - delete basket; - } -} diff --git a/src/strategy/Queue.h b/src/strategy/Queue.h index 23776655..8da0aa6f 100644 --- a/src/strategy/Queue.h +++ b/src/strategy/Queue.h @@ -53,14 +53,6 @@ public: */ uint32 Size(); - /** - * @brief Removes and deletes expired actions from the queue - * - * Uses sPlayerbotAIConfig->expireActionTime to determine if actions have expired. - * Both the ActionNode and ActionBasket are deleted for expired actions. - */ - void RemoveExpired(); - private: /** * @brief Updates existing basket with new relevance and cleans up new basket @@ -78,11 +70,6 @@ private: */ ActionNode* extractAndDeleteBasket(ActionBasket* basket); - /** - * @brief Collects all expired baskets into the provided list - */ - void collectExpiredBaskets(std::list& expiredBaskets); - /** * @brief Removes and deletes all baskets in the provided list */ diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index 1473e85d..98fff5b9 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -143,7 +143,8 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) context->GetValue("available loot")->Get()->Add(guid); LastMovement& lastMovement = AI_VALUE(LastMovement&, "last movement"); - if (lastMovement.priority < MovementPriority::MOVEMENT_COMBAT && bot->isMoving()) + bool moveControlled = bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE; + if (lastMovement.priority < MovementPriority::MOVEMENT_COMBAT && bot->isMoving() && !moveControlled) { AI_VALUE(LastMovement&, "last movement").clear(); bot->GetMotionMaster()->Clear(false); diff --git a/src/strategy/generic/CombatStrategy.cpp b/src/strategy/generic/CombatStrategy.cpp index 29c525fb..6bbf09cd 100644 --- a/src/strategy/generic/CombatStrategy.cpp +++ b/src/strategy/generic/CombatStrategy.cpp @@ -75,9 +75,6 @@ NextAction** AvoidAoeStrategy::getDefaultActions() void AvoidAoeStrategy::InitTriggers(std::vector& triggers) { - // triggers.push_back(new TriggerNode( - // "has area debuff", - // NextAction::array(0, new NextAction("flee", ACTION_EMERGENCY + 5), NULL))); } void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) diff --git a/src/strategy/generic/RangedCombatStrategy.cpp b/src/strategy/generic/RangedCombatStrategy.cpp index f4aefb0e..09ec3a10 100644 --- a/src/strategy/generic/RangedCombatStrategy.cpp +++ b/src/strategy/generic/RangedCombatStrategy.cpp @@ -12,7 +12,7 @@ void RangedCombatStrategy::InitTriggers(std::vector& triggers) CombatStrategy::InitTriggers(triggers); triggers.push_back(new TriggerNode("enemy too close for spell", - NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); + NextAction::array(0, new NextAction("flee", ACTION_MOVE + 4), nullptr))); // triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing", // ACTION_MOVE + 7), nullptr))); } diff --git a/src/strategy/hunter/GenericHunterStrategy.cpp b/src/strategy/hunter/GenericHunterStrategy.cpp index 0974e2d4..9113e40f 100644 --- a/src/strategy/hunter/GenericHunterStrategy.cpp +++ b/src/strategy/hunter/GenericHunterStrategy.cpp @@ -6,6 +6,7 @@ #include "GenericHunterStrategy.h" #include "Playerbots.h" +#include "Strategy.h" class GenericHunterStrategyActionNodeFactory : public NamedObjectFactory { @@ -19,6 +20,7 @@ public: creators["wing clip"] = &wing_clip; creators["mongoose bite"] = &mongoose_bite; creators["raptor strike"] = &raptor_strike; + creators["explosive trap"] = &explosive_trap; } private: @@ -70,6 +72,15 @@ private: /*A*/ nullptr, /*C*/ nullptr); } + + static ActionNode* explosive_trap([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("explosive trap", + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("immolation trap"), nullptr), + /*C*/ nullptr); + } + }; GenericHunterStrategy::GenericHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) @@ -82,8 +93,11 @@ void GenericHunterStrategy::InitTriggers(std::vector& triggers) CombatStrategy::InitTriggers(triggers); triggers.push_back(new TriggerNode("enemy within melee", - NextAction::array(0, new NextAction("wing clip", ACTION_HIGH + 1), - new NextAction("mongoose bite", ACTION_HIGH), nullptr))); + NextAction::array(0, + new NextAction("explosive trap", ACTION_MOVE + 7), + new NextAction("mongoose bite", ACTION_HIGH + 2), + new NextAction("wing clip", ACTION_HIGH + 1), + nullptr))); triggers.push_back( new TriggerNode("medium threat", NextAction::array(0, new NextAction("feign death", 35.0f), nullptr))); triggers.push_back(new TriggerNode("hunters pet medium health", @@ -93,7 +107,10 @@ void GenericHunterStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("aspect of the viper", NextAction::array(0, new NextAction("aspect of the viper", ACTION_HIGH), NULL))); triggers.push_back(new TriggerNode("enemy too close for auto shot", - NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); + NextAction::array(0, + new NextAction("disengage", ACTION_MOVE + 5), + new NextAction("flee", ACTION_MOVE + 4), + nullptr))); triggers.push_back( new TriggerNode("low tank threat", NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL))); @@ -124,3 +141,14 @@ void HunterCcStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "freezing trap", NextAction::array(0, new NextAction("freezing trap on cc", ACTION_HIGH + 3), nullptr))); } + +void HunterTrapWeaveStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode( + "immolation trap no cd", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 3), nullptr))); + + // triggers.push_back(new TriggerNode( + // "scare beast", NextAction::array(0, new NextAction("scare beast on cc", ACTION_HIGH + 3), nullptr))); + // triggers.push_back(new TriggerNode( + // "freezing trap", NextAction::array(0, new NextAction("freezing trap on cc", ACTION_HIGH + 3), nullptr))); +} diff --git a/src/strategy/hunter/GenericHunterStrategy.h b/src/strategy/hunter/GenericHunterStrategy.h index 9ee16ba1..52148eed 100644 --- a/src/strategy/hunter/GenericHunterStrategy.h +++ b/src/strategy/hunter/GenericHunterStrategy.h @@ -40,4 +40,14 @@ public: std::string const getName() override { return "cc"; } }; +class HunterTrapWeaveStrategy : public Strategy +{ +public: + HunterTrapWeaveStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "trap weave"; } +}; + + #endif diff --git a/src/strategy/hunter/HunterActions.cpp b/src/strategy/hunter/HunterActions.cpp index eb3572f0..074a9bbd 100644 --- a/src/strategy/hunter/HunterActions.cpp +++ b/src/strategy/hunter/HunterActions.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "GenericSpellActions.h" +#include "PlayerbotAI.h" #include "Playerbots.h" bool CastHuntersMarkAction::isUseful() { return CastDebuffSpellAction::isUseful(); } @@ -46,6 +47,22 @@ bool CastAutoShotAction::isUseful() return AI_VALUE(uint32, "active spell") != AI_VALUE2(uint32, "spell id", getName()); } +bool CastDisengageAction::Execute(Event event) +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + // can cast spell check passed in isUseful() + bot->SetOrientation(bot->GetAngle(target)); + return CastSpellAction::Execute(event); +} + +bool CastDisengageAction::isUseful() +{ + return !botAI->HasStrategy("trap weave", BOT_STATE_COMBAT); +} + + Value* CastScareBeastCcAction::GetTargetValue() { return context->GetValue("cc target", "scare beast"); } bool CastScareBeastCcAction::Execute(Event event) { return botAI->CastSpell("scare beast", GetTarget()); } diff --git a/src/strategy/hunter/HunterActions.h b/src/strategy/hunter/HunterActions.h index 87595b3b..d8618c42 100644 --- a/src/strategy/hunter/HunterActions.h +++ b/src/strategy/hunter/HunterActions.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_HUNTERACTIONS_H #include "AiObject.h" +#include "Event.h" #include "GenericSpellActions.h" class PlayerbotAI; @@ -84,6 +85,27 @@ END_SPELL_ACTION() BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot") END_SPELL_ACTION() +class CastDisengageAction : public CastSpellAction +{ +public: + CastDisengageAction(PlayerbotAI* botAI): CastSpellAction(botAI, "disengage") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + +class CastImmolationTrapAction : public CastSpellAction +{ +public: + CastImmolationTrapAction(PlayerbotAI* botAI): CastSpellAction(botAI, "immolation trap") {} +}; + +class CastExplosiveTrapAction : public CastSpellAction +{ +public: + CastExplosiveTrapAction(PlayerbotAI* botAI): CastSpellAction(botAI, "explosive trap") {} +}; + class CastAspectOfTheHawkAction : public CastBuffSpellAction { public: diff --git a/src/strategy/hunter/HunterAiObjectContext.cpp b/src/strategy/hunter/HunterAiObjectContext.cpp index 2dcd3550..9b89b651 100644 --- a/src/strategy/hunter/HunterAiObjectContext.cpp +++ b/src/strategy/hunter/HunterAiObjectContext.cpp @@ -25,6 +25,7 @@ public: creators["boost"] = &HunterStrategyFactoryInternal::boost; creators["pet"] = &HunterStrategyFactoryInternal::pet; creators["cc"] = &HunterStrategyFactoryInternal::cc; + creators["trap weave"] = &HunterStrategyFactoryInternal::trap_weave; } private: @@ -35,6 +36,7 @@ private: static Strategy* boost(PlayerbotAI* botAI) { return new HunterBoostStrategy(botAI); } static Strategy* pet(PlayerbotAI* botAI) { return new HunterPetStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new HunterCcStrategy(botAI); } + static Strategy* trap_weave(PlayerbotAI* botAI) { return new HunterTrapWeaveStrategy(botAI); } }; class HunterBuffStrategyFactoryInternal : public NamedObjectContext @@ -88,6 +90,7 @@ public: creators["misdirection on main tank"] = &HunterTriggerFactoryInternal::misdirection_on_main_tank; creators["tranquilizing shot enrage"] = &HunterTriggerFactoryInternal::remove_enrage; creators["tranquilizing shot magic"] = &HunterTriggerFactoryInternal::remove_magic; + creators["immolation trap no cd"] = &HunterTriggerFactoryInternal::immolation_trap_no_cd; } private: @@ -122,6 +125,7 @@ private: static Trigger* misdirection_on_main_tank(PlayerbotAI* ai) { return new MisdirectionOnMainTankTrigger(ai); } static Trigger* remove_enrage(PlayerbotAI* ai) { return new TargetRemoveEnrageTrigger(ai); } static Trigger* remove_magic(PlayerbotAI* ai) { return new TargetRemoveMagicTrigger(ai); } + static Trigger* immolation_trap_no_cd(PlayerbotAI* ai) { return new ImmolationTrapNoCdTrigger(ai); } }; class HunterAiObjectContextInternal : public NamedObjectContext @@ -176,6 +180,9 @@ public: creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot; creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank; creators["silencing shot"] = &HunterAiObjectContextInternal::silencing_shot; + creators["disengage"] = &HunterAiObjectContextInternal::disengage; + creators["immolation trap"] = &HunterAiObjectContextInternal::immolation_trap; + creators["explosive trap"] = &HunterAiObjectContextInternal::explosive_trap; } private: @@ -225,7 +232,9 @@ private: static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); } static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); } static Action* silencing_shot(PlayerbotAI* ai) { return new CastSilencingShotAction(ai); } - + static Action* disengage(PlayerbotAI* ai) { return new CastDisengageAction(ai); } + static Action* immolation_trap(PlayerbotAI* ai) { return new CastImmolationTrapAction(ai); } + static Action* explosive_trap(PlayerbotAI* ai) { return new CastExplosiveTrapAction(ai); } }; HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/hunter/HunterTriggers.cpp b/src/strategy/hunter/HunterTriggers.cpp index 6c9354fa..4d0c26c2 100644 --- a/src/strategy/hunter/HunterTriggers.cpp +++ b/src/strategy/hunter/HunterTriggers.cpp @@ -5,13 +5,23 @@ #include "HunterTriggers.h" +#include "GenericSpellActions.h" #include "GenericTriggers.h" #include "HunterActions.h" +#include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "ServerFacade.h" #include "SharedDefines.h" +bool BlackArrowTrigger::IsActive() +{ + if (botAI->HasStrategy("trap weave", BOT_STATE_COMBAT)) + return false; + + return DebuffTrigger::IsActive(); +} + bool HunterAspectOfTheHawkTrigger::IsActive() { Unit* target = GetTarget(); diff --git a/src/strategy/hunter/HunterTriggers.h b/src/strategy/hunter/HunterTriggers.h index e9d09c91..d0d4db9c 100644 --- a/src/strategy/hunter/HunterTriggers.h +++ b/src/strategy/hunter/HunterTriggers.h @@ -75,6 +75,7 @@ class BlackArrowTrigger : public DebuffTrigger { public: BlackArrowTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "black arrow", 1, true) {} + bool IsActive() override; }; class HuntersMarkTrigger : public DebuffTrigger @@ -185,4 +186,11 @@ class TargetRemoveMagicTrigger : public TargetAuraDispelTrigger public: TargetRemoveMagicTrigger(PlayerbotAI* ai) : TargetAuraDispelTrigger(ai, "tranquilizing shot", DISPEL_MAGIC) {} }; + +class ImmolationTrapNoCdTrigger : public SpellNoCooldownTrigger +{ +public: + ImmolationTrapNoCdTrigger(PlayerbotAI* ai) : SpellNoCooldownTrigger(ai, "immolation trap") {} +}; + #endif diff --git a/src/strategy/mage/FireMageStrategy.cpp b/src/strategy/mage/FireMageStrategy.cpp index 5e7df0ae..1d3e132f 100644 --- a/src/strategy/mage/FireMageStrategy.cpp +++ b/src/strategy/mage/FireMageStrategy.cpp @@ -6,6 +6,7 @@ #include "FireMageStrategy.h" #include "Playerbots.h" +#include "Strategy.h" NextAction** FireMageStrategy::getDefaultActions() { @@ -32,11 +33,12 @@ void FireMageStrategy::InitTriggers(std::vector& triggers) void FireMageAoeStrategy::InitTriggers(std::vector& triggers) { + // higher priority to cast before move away triggers.push_back( new TriggerNode("medium aoe", NextAction::array(0, - new NextAction("dragon's breath", 24.0f), - new NextAction("flamestrike", 23.0f), - new NextAction("blast wave", 22.0f), + new NextAction("dragon's breath", ACTION_MOVE + 9), + new NextAction("flamestrike", ACTION_MOVE + 8), + new NextAction("blast wave", ACTION_MOVE + 7), new NextAction("living bomb on attackers", 21.0f), new NextAction("blizzard", 20.0f), nullptr))); } diff --git a/src/strategy/mage/GenericMageStrategy.cpp b/src/strategy/mage/GenericMageStrategy.cpp index 453dbf7a..8821a9e7 100644 --- a/src/strategy/mage/GenericMageStrategy.cpp +++ b/src/strategy/mage/GenericMageStrategy.cpp @@ -179,6 +179,9 @@ void GenericMageStrategy::InitTriggers(std::vector& triggers) new TriggerNode("fire ward", NextAction::array(0, new NextAction("fire ward", ACTION_EMERGENCY), nullptr))); triggers.push_back( new TriggerNode("frost ward", NextAction::array(0, new NextAction("frost ward", ACTION_EMERGENCY), nullptr))); + + triggers.push_back(new TriggerNode("enemy too close for spell", + NextAction::array(0, new NextAction("blink back", ACTION_MOVE + 5), nullptr))); } void MageCureStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/mage/MageActions.cpp b/src/strategy/mage/MageActions.cpp index 786b1717..10603199 100644 --- a/src/strategy/mage/MageActions.cpp +++ b/src/strategy/mage/MageActions.cpp @@ -4,6 +4,7 @@ */ #include "MageActions.h" +#include #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -17,6 +18,10 @@ bool CastFrostNovaAction::isUseful() Unit* target = AI_VALUE(Unit*, "current target"); if (target && target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1))) return false; + + if (target->isFrozen()) + return false; + return sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); } @@ -87,4 +92,14 @@ Unit* CastFocusMagicOnPartyAction::GetTarget() return healer; return target; +} + +bool CastBlinkBackAction::Execute(Event event) +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + // can cast spell check passed in isUseful() + bot->SetOrientation(bot->GetAngle(target) + M_PI); + return CastSpellAction::Execute(event); } \ No newline at end of file diff --git a/src/strategy/mage/MageActions.h b/src/strategy/mage/MageActions.h index 66f6157e..48efd9e8 100644 --- a/src/strategy/mage/MageActions.h +++ b/src/strategy/mage/MageActions.h @@ -309,4 +309,11 @@ public: Unit* GetTarget() override; }; +class CastBlinkBackAction : public CastSpellAction +{ +public: + CastBlinkBackAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blink") {} + + bool Execute(Event event) override; +}; #endif diff --git a/src/strategy/mage/MageAiObjectContext.cpp b/src/strategy/mage/MageAiObjectContext.cpp index 23c88cbe..2d09f1a0 100644 --- a/src/strategy/mage/MageAiObjectContext.cpp +++ b/src/strategy/mage/MageAiObjectContext.cpp @@ -206,6 +206,7 @@ public: creators["frost ward"] = &MageAiObjectContextInternal::frost_ward; creators["mirror image"] = &MageAiObjectContextInternal::mirror_image; creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party; + creators["blink back"] = &MageAiObjectContextInternal::blink_back; } private: @@ -264,6 +265,7 @@ private: } static Action* mirror_image(PlayerbotAI* botAI) { return new CastMirrorImageAction(botAI); } static Action* focus_magic_on_party(PlayerbotAI* botAI) { return new CastFocusMagicOnPartyAction(botAI); } + static Action* blink_back(PlayerbotAI* botAI) { return new CastBlinkBackAction(botAI); } }; MageAiObjectContext::MageAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/triggers/RangeTriggers.cpp b/src/strategy/triggers/RangeTriggers.cpp index 5d64283d..772170c3 100644 --- a/src/strategy/triggers/RangeTriggers.cpp +++ b/src/strategy/triggers/RangeTriggers.cpp @@ -9,6 +9,7 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "SharedDefines.h" static float GetSpeedInMotion(Unit* target) { @@ -56,8 +57,19 @@ bool EnemyTooCloseForSpellTrigger::IsActive() bool EnemyTooCloseForAutoShotTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; - return target && (target->GetVictim() != bot || target->isFrozen() || !target->CanFreeMove()) && + // hunter move away after casting immolation/explosive trap + bool trapToCast = bot->getClass() == CLASS_HUNTER; + uint32 spellId = AI_VALUE2(uint32, "spell id", "immolation trap"); + if (!spellId) + trapToCast = false; + + if (spellId && bot->HasSpellCooldown(spellId)) + trapToCast = false; + + return !trapToCast && (target->GetVictim() != bot || target->isFrozen() || !target->CanFreeMove()) && bot->IsWithinMeleeRange(target); // if (target->GetTarget() == bot->GetGUID() && !bot->GetGroup() && !target->HasUnitState(UNIT_STATE_ROOT) &&