mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Pull strategy migration (#2310)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.
DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.
Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.
Before submitting, make sure your changes aligns with these principles.
-->
## Pull Request Description
<!-- Describe what this change does and why it is needed -->
Pull strategy migration from cmangos for tank specializations
## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->
1. Invite bot tank
2. Use `reset boAI` or `nc +pull,+pull back` + `co +pull,+pull back`
3. Order bot to pull using command `pull my target` or `pull rti target`
4. Bot should run to mob, use ranged skill and back to point where he
started pull
Without `pull back` strategy bot run to mob, use ranged skill and wait
on mob until he come to bot
## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
- - [x] No, not at all
- - [ ] Minimal impact (**explain below**)
- - [ ] Moderate impact (**explain below**)
- Does this change modify default bot behavior?
- - [x] No
- - [ ] Yes (**explain why**)
- Does this change add new decision branches or increase maintenance
complexity?
- - [x] No
- - [ ] Yes (**explain below**)
## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [ ] No
- - [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->
Help with migration and solving some problems
<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->
## Final Checklist
- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
Stability test after randomize new bots
<img width="465" height="172" alt="obraz"
src="https://github.com/user-attachments/assets/6e39a8c0-f23b-47cc-852a-71fa98044a31"
/>
---------
Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
This commit is contained in:
parent
665a702a65
commit
19249e90a0
@ -0,0 +1,102 @@
|
||||
-- #########################################################
|
||||
-- Playerbots - Add pull command texts
|
||||
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
|
||||
-- zhTW, esES, esMX, ruRU)
|
||||
-- #########################################################
|
||||
|
||||
DELETE FROM ai_playerbot_texts WHERE name IN (
|
||||
'pull_no_target_error',
|
||||
'pull_target_too_far_error',
|
||||
'pull_invalid_target_error',
|
||||
'pull_action_unavailable_error'
|
||||
);
|
||||
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
|
||||
'pull_no_target_error',
|
||||
'pull_target_too_far_error',
|
||||
'pull_invalid_target_error',
|
||||
'pull_action_unavailable_error'
|
||||
);
|
||||
|
||||
-- pull_no_target_error
|
||||
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 (
|
||||
1755,
|
||||
'pull_no_target_error',
|
||||
'You have no target',
|
||||
0, 0,
|
||||
'대상이 없습니다',
|
||||
'Vous n''avez pas de cible',
|
||||
'Du hast kein Ziel',
|
||||
'你没有目标',
|
||||
'你沒有目標',
|
||||
'No tienes objetivo',
|
||||
'No tienes objetivo',
|
||||
'У вас нет цели');
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_no_target_error', 100);
|
||||
|
||||
-- pull_target_too_far_error
|
||||
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 (
|
||||
1756,
|
||||
'pull_target_too_far_error',
|
||||
'The target is too far away',
|
||||
0, 0,
|
||||
'대상이 너무 멀리 있습니다',
|
||||
'La cible est trop loin',
|
||||
'Das Ziel ist zu weit entfernt',
|
||||
'目标太远了',
|
||||
'目標太遠了',
|
||||
'El objetivo está demasiado lejos',
|
||||
'El objetivo está demasiado lejos',
|
||||
'Цель слишком далеко');
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_target_too_far_error', 100);
|
||||
|
||||
-- pull_invalid_target_error
|
||||
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 (
|
||||
1757,
|
||||
'pull_invalid_target_error',
|
||||
'The target can''t be pulled',
|
||||
0, 0,
|
||||
'해당 대상은 풀링할 수 없습니다',
|
||||
'La cible ne peut pas être attirée',
|
||||
'Das Ziel kann nicht gepullt werden',
|
||||
'该目标无法被拉怪',
|
||||
'該目標無法被拉怪',
|
||||
'No se puede hacer pull al objetivo',
|
||||
'No se puede hacer pull al objetivo',
|
||||
'Эту цель нельзя пуллить');
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_invalid_target_error', 100);
|
||||
|
||||
-- pull_action_unavailable_error: %action_name is replaced with the configured pull action
|
||||
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 (
|
||||
1758,
|
||||
'pull_action_unavailable_error',
|
||||
'Can''t perform pull action ''%action_name''',
|
||||
0, 0,
|
||||
'''%action_name'' 풀 액션을 수행할 수 없습니다',
|
||||
'Impossible d''effectuer l''action d''engagement ''%action_name''',
|
||||
'Die Pull-Aktion ''%action_name'' kann nicht ausgeführt werden',
|
||||
'无法执行拉怪动作“%action_name”',
|
||||
'無法執行拉怪動作「%action_name」',
|
||||
'No se puede realizar la acción de pull ''%action_name''',
|
||||
'No se puede realizar la acción de pull ''%action_name''',
|
||||
'Невозможно выполнить действие пула ''%action_name''');
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_action_unavailable_error', 100);
|
||||
@ -45,6 +45,7 @@
|
||||
#include "NonCombatActions.h"
|
||||
#include "OutfitAction.h"
|
||||
#include "PositionAction.h"
|
||||
#include "PullActions.h"
|
||||
#include "DropQuestAction.h"
|
||||
#include "RandomBotUpdateAction.h"
|
||||
#include "ReachTargetActions.h"
|
||||
@ -105,6 +106,13 @@ public:
|
||||
creators["shoot"] = &ActionContext::shoot;
|
||||
creators["lifeblood"] = &ActionContext::lifeblood;
|
||||
creators["arcane torrent"] = &ActionContext::arcane_torrent;
|
||||
creators["pull my target"] = &ActionContext::pull_my_target;
|
||||
creators["pull rti target"] = &ActionContext::pull_rti_target;
|
||||
creators["pull start"] = &ActionContext::pull_start;
|
||||
creators["pull action"] = &ActionContext::pull_action;
|
||||
creators["pull end"] = &ActionContext::pull_end;
|
||||
creators["return to pull position"] = &ActionContext::return_to_pull_position;
|
||||
creators["reach pull"] = &ActionContext::reach_pull;
|
||||
creators["end pull"] = &ActionContext::end_pull;
|
||||
creators["healthstone"] = &ActionContext::healthstone;
|
||||
creators["healing potion"] = &ActionContext::healing_potion;
|
||||
@ -313,6 +321,13 @@ private:
|
||||
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
||||
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
||||
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
||||
static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); }
|
||||
static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); }
|
||||
static Action* pull_start(PlayerbotAI* botAI) { return new PullStartAction(botAI); }
|
||||
static Action* pull_action(PlayerbotAI* botAI) { return new PullAction(botAI); }
|
||||
static Action* pull_end(PlayerbotAI* botAI) { return new PullEndAction(botAI); }
|
||||
static Action* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionAction(botAI); }
|
||||
static Action* reach_pull(PlayerbotAI* botAI) { return new ReachPullAction(botAI); }
|
||||
static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); }
|
||||
static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); }
|
||||
static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); }
|
||||
|
||||
@ -273,7 +273,7 @@ bool BuffOnPartyAction::Execute(Event /*event*/)
|
||||
}
|
||||
// End greater buff fix
|
||||
|
||||
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot")
|
||||
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
||||
{
|
||||
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||
{
|
||||
@ -283,17 +283,40 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
spell += " gun";
|
||||
shootSpellId = 3018;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
spell += " bow";
|
||||
shootSpellId = 3018;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
spell += " crossbow";
|
||||
shootSpellId = 3018;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||
spell = "throw";
|
||||
shootSpellId = 2764;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CastShootAction::isPossible()
|
||||
{
|
||||
if (shootSpellId)
|
||||
return botAI->CanCastSpell(shootSpellId, GetTarget(), false);
|
||||
|
||||
return CastSpellAction::isPossible();
|
||||
}
|
||||
|
||||
bool CastShootAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (shootSpellId)
|
||||
return botAI->CastSpell(shootSpellId, GetTarget());
|
||||
|
||||
return botAI->CastSpell(spell, GetTarget());
|
||||
}
|
||||
|
||||
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
||||
{
|
||||
return context->GetValue<Unit*>("attacker without aura", spell);
|
||||
|
||||
@ -253,7 +253,12 @@ class CastShootAction : public CastSpellAction
|
||||
public:
|
||||
CastShootAction(PlayerbotAI* botAI);
|
||||
|
||||
bool isPossible() override;
|
||||
bool Execute(Event event) override;
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::None; }
|
||||
|
||||
private:
|
||||
uint32 shootSpellId;
|
||||
};
|
||||
|
||||
class CastLifeBloodAction : public CastHealingSpellAction
|
||||
|
||||
321
src/Ai/Base/Actions/PullActions.cpp
Normal file
321
src/Ai/Base/Actions/PullActions.cpp
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* 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 "AttackersValue.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "PositionValue.h"
|
||||
#include "PullActions.h"
|
||||
#include "PullStrategy.h"
|
||||
#include "RtiTargetValue.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
float GetPullReachDistance(Player* bot, Unit* target, PullStrategy const* strategy)
|
||||
{
|
||||
if (!bot || !target || !strategy)
|
||||
return 0.0f;
|
||||
|
||||
float const combatDistance = bot->GetCombatReach() + target->GetCombatReach();
|
||||
return std::max(0.0f, strategy->GetRange() - combatDistance);
|
||||
}
|
||||
|
||||
bool IsWithinPullRange(Player* bot, Unit* target, PullStrategy const* strategy)
|
||||
{
|
||||
return bot && target && strategy && bot->GetExactDist(target) <= strategy->GetRange();
|
||||
}
|
||||
}
|
||||
|
||||
bool PullRequestAction::Execute(Event event)
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* target = GetPullTarget(event);
|
||||
if (!target || !target->IsInWorld())
|
||||
{
|
||||
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||
"pull_no_target_error", "You have no target", {});
|
||||
botAI->TellError(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
float const maxPullDistance = sPlayerbotAIConfig.reactDistance * 3.0f;
|
||||
if (target->GetMapId() != bot->GetMapId() || bot->GetDistance(target) > maxPullDistance)
|
||||
{
|
||||
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||
"pull_target_too_far_error", "The target is too far away", {});
|
||||
botAI->TellError(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AttackersValue::IsPossibleTarget(target, bot))
|
||||
{
|
||||
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||
"pull_invalid_target_error", "The target can't be pulled", {});
|
||||
botAI->TellError(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strategy->CanDoPullAction(target))
|
||||
{
|
||||
std::string const actionName = strategy->GetPullActionName();
|
||||
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||
"pull_action_unavailable_error",
|
||||
"Can't perform pull action '%action_name'",
|
||||
{{"%action_name", actionName}});
|
||||
botAI->TellError(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||
PositionInfo pullPosition = posMap["pull"];
|
||||
pullPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId());
|
||||
posMap["pull"] = pullPosition;
|
||||
|
||||
strategy->RequestPull(target);
|
||||
context->GetValue<Unit*>("current target")->Set(target);
|
||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay);
|
||||
return true;
|
||||
}
|
||||
|
||||
Unit* PullMyTargetAction::GetPullTarget(Event event)
|
||||
{
|
||||
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
|
||||
if (event.GetSource() == "attack anything")
|
||||
return botAI->GetCreature(event.getObject());
|
||||
|
||||
return requester ? requester->GetSelectedUnit() : nullptr;
|
||||
}
|
||||
|
||||
Unit* PullRtiTargetAction::GetPullTarget(Event /*event*/)
|
||||
{
|
||||
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
||||
if (rtiTarget)
|
||||
return rtiTarget;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
std::string const rti = AI_VALUE(std::string, "rti");
|
||||
int32 const index = RtiTargetValue::GetRtiIndex(rti);
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
|
||||
ObjectGuid const guid = group->GetTargetIcon(index);
|
||||
return guid.IsEmpty() ? nullptr : botAI->GetUnit(guid);
|
||||
}
|
||||
|
||||
bool PullStartAction::Execute(Event event)
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return false;
|
||||
|
||||
Unit* target = strategy->GetTarget();
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
std::string const preActionName = strategy->GetPreActionName();
|
||||
if (!preActionName.empty() && !botAI->DoSpecificAction(preActionName, event, true))
|
||||
return false;
|
||||
|
||||
if (Pet* pet = bot->GetPet())
|
||||
{
|
||||
Creature* creature = pet->ToCreature();
|
||||
if (creature)
|
||||
{
|
||||
strategy->SetPetReactState(creature->GetReactState());
|
||||
creature->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
strategy->OnPullStarted();
|
||||
return true;
|
||||
}
|
||||
|
||||
PullAction::PullAction(PlayerbotAI* botAI, std::string const name) : CastSpellAction(botAI, name) { InitPullAction(); }
|
||||
|
||||
Unit* PullAction::GetTarget()
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return nullptr;
|
||||
|
||||
return strategy->GetTarget();
|
||||
}
|
||||
|
||||
std::vector<NextAction> PullAction::getPrerequisites()
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||
if (!strategy || !target)
|
||||
return {};
|
||||
|
||||
return IsWithinPullRange(bot, target, strategy) ? std::vector<NextAction>{}
|
||||
: std::vector<NextAction>{ NextAction("reach pull", ACTION_MOVE) };
|
||||
}
|
||||
|
||||
bool PullAction::Execute(Event event)
|
||||
{
|
||||
InitPullAction();
|
||||
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return false;
|
||||
|
||||
Unit* target = strategy->GetTarget();
|
||||
if (!target || !target->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (target->IsInCombat())
|
||||
return false;
|
||||
|
||||
if (!IsWithinPullRange(bot, target, strategy))
|
||||
{
|
||||
strategy->RequestPull(target, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->isMoving())
|
||||
{
|
||||
bot->StopMoving();
|
||||
strategy->RequestPull(target, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
context->GetValue<Unit*>("current target")->Set(target);
|
||||
if (!botAI->DoSpecificAction(strategy->GetPullActionName(), event, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PullAction::isPossible()
|
||||
{
|
||||
InitPullAction();
|
||||
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return false;
|
||||
|
||||
Unit* target = strategy->GetTarget();
|
||||
std::string const spellName = strategy->GetSpellName();
|
||||
if (!target || !target->IsInWorld() || target->GetMapId() != bot->GetMapId() || spellName.empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PullAction::InitPullAction()
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return;
|
||||
|
||||
std::string const spellName = strategy->GetSpellName();
|
||||
if (spellName.empty())
|
||||
return;
|
||||
|
||||
spell = spellName;
|
||||
|
||||
bool isShoot = (spellName == "shoot" || spellName == "shoot bow" ||
|
||||
spellName == "shoot gun" || spellName == "shoot crossbow" ||
|
||||
spellName == "throw");
|
||||
range = botAI->GetRange(isShoot ? "shoot" : "spell");
|
||||
}
|
||||
|
||||
bool PullEndAction::Execute(Event /*event*/)
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return false;
|
||||
|
||||
Unit* pullTarget = strategy->GetTarget();
|
||||
|
||||
if (!strategy->HasPullStarted() && !strategy->IsPullPendingToStart() && !strategy->HasTarget())
|
||||
return false;
|
||||
|
||||
if (Pet* pet = bot->GetPet())
|
||||
{
|
||||
Creature* creature = pet->ToCreature();
|
||||
if (creature)
|
||||
creature->SetReactState(strategy->GetPetReactState());
|
||||
}
|
||||
|
||||
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||
PositionInfo pullPosition = posMap["pull"];
|
||||
if (pullPosition.isSet())
|
||||
posMap.erase("pull");
|
||||
|
||||
if (pullTarget && context->GetValue<Unit*>("current target")->Get() == pullTarget)
|
||||
context->GetValue<Unit*>("current target")->Set(nullptr);
|
||||
|
||||
strategy->OnPullEnded();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReturnToPullPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||
if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId())
|
||||
return false;
|
||||
|
||||
return MoveTo(pullPosition.mapId, pullPosition.x, pullPosition.y, pullPosition.z,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true);
|
||||
}
|
||||
|
||||
bool ReturnToPullPositionAction::isUseful()
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||
if (!strategy || !target || !target->IsInCombat())
|
||||
return false;
|
||||
|
||||
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||
return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() &&
|
||||
bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance;
|
||||
}
|
||||
|
||||
bool ReachPullAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* target = GetTarget();
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!target || !strategy)
|
||||
return false;
|
||||
|
||||
float const reachDistance = GetPullReachDistance(bot, target, strategy);
|
||||
return ReachCombatTo(target, reachDistance);
|
||||
}
|
||||
|
||||
bool ReachPullAction::isUseful()
|
||||
{
|
||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||
return false;
|
||||
|
||||
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr)
|
||||
return false;
|
||||
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||
return target && !IsWithinPullRange(bot, target, strategy);
|
||||
}
|
||||
|
||||
Unit* ReachPullAction::GetTarget()
|
||||
{
|
||||
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy)
|
||||
return nullptr;
|
||||
|
||||
return strategy->GetTarget();
|
||||
}
|
||||
90
src/Ai/Base/Actions/PullActions.h
Normal file
90
src/Ai/Base/Actions/PullActions.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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_PULLACTIONS_H
|
||||
#define _PLAYERBOT_PULLACTIONS_H
|
||||
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
|
||||
class PullRequestAction : public Action
|
||||
{
|
||||
public:
|
||||
PullRequestAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
virtual Unit* GetPullTarget(Event event) = 0;
|
||||
};
|
||||
|
||||
class PullMyTargetAction : public PullRequestAction
|
||||
{
|
||||
public:
|
||||
PullMyTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull my target") {}
|
||||
|
||||
private:
|
||||
Unit* GetPullTarget(Event event) override;
|
||||
};
|
||||
|
||||
class PullRtiTargetAction : public PullRequestAction
|
||||
{
|
||||
public:
|
||||
PullRtiTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull rti target") {}
|
||||
|
||||
private:
|
||||
Unit* GetPullTarget(Event event) override;
|
||||
};
|
||||
|
||||
class PullStartAction : public Action
|
||||
{
|
||||
public:
|
||||
PullStartAction(PlayerbotAI* botAI, std::string const name = "pull start") : Action(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class PullAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
PullAction(PlayerbotAI* botAI, std::string const name = "pull action");
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
Unit* GetTarget() override;
|
||||
|
||||
private:
|
||||
void InitPullAction();
|
||||
};
|
||||
|
||||
class PullEndAction : public Action
|
||||
{
|
||||
public:
|
||||
PullEndAction(PlayerbotAI* botAI, std::string const name = "pull end") : Action(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ReachPullAction : public ReachTargetAction
|
||||
{
|
||||
public:
|
||||
ReachPullAction(PlayerbotAI* botAI) : ReachTargetAction(botAI, "reach pull", botAI->GetRange("spell")) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
Unit* GetTarget() override;
|
||||
};
|
||||
|
||||
class ReturnToPullPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ReturnToPullPositionAction(PlayerbotAI* botAI) : MovementAction(botAI, "return to pull position") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -43,6 +43,7 @@
|
||||
#include "NewRpgAction.h"
|
||||
#include "PassLeadershipToMasterAction.h"
|
||||
#include "PositionAction.h"
|
||||
#include "PullActions.h"
|
||||
#include "QueryItemUsageAction.h"
|
||||
#include "QueryQuestAction.h"
|
||||
#include "RangeAction.h"
|
||||
@ -138,6 +139,8 @@ public:
|
||||
creators["autogear"] = &ChatActionContext::autogear;
|
||||
creators["equip upgrade"] = &ChatActionContext::equip_upgrade;
|
||||
creators["attack my target"] = &ChatActionContext::attack_my_target;
|
||||
creators["pull my target"] = &ChatActionContext::pull_my_target;
|
||||
creators["pull rti target"] = &ChatActionContext::pull_rti_target;
|
||||
creators["chat"] = &ChatActionContext::chat;
|
||||
creators["home"] = &ChatActionContext::home;
|
||||
creators["destroy"] = &ChatActionContext::destroy;
|
||||
@ -250,6 +253,8 @@ private:
|
||||
static Action* home(PlayerbotAI* botAI) { return new SetHomeAction(botAI); }
|
||||
static Action* chat(PlayerbotAI* botAI) { return new ChangeChatAction(botAI); }
|
||||
static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); }
|
||||
static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); }
|
||||
static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); }
|
||||
static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); }
|
||||
static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); }
|
||||
static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); }
|
||||
|
||||
@ -66,6 +66,9 @@ public:
|
||||
creators["autogear"] = &ChatTriggerContext::autogear;
|
||||
creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade;
|
||||
creators["attack"] = &ChatTriggerContext::attack;
|
||||
creators["pull"] = &ChatTriggerContext::pull;
|
||||
creators["pull back"] = &ChatTriggerContext::pull_back;
|
||||
creators["pull rti"] = &ChatTriggerContext::pull_rti;
|
||||
creators["chat"] = &ChatTriggerContext::chat;
|
||||
creators["accept"] = &ChatTriggerContext::accept;
|
||||
creators["home"] = &ChatTriggerContext::home;
|
||||
@ -209,6 +212,9 @@ private:
|
||||
static Trigger* accept(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "accept"); }
|
||||
static Trigger* chat(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "chat"); }
|
||||
static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); }
|
||||
static Trigger* pull(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull"); }
|
||||
static Trigger* pull_back(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull back"); }
|
||||
static Trigger* pull_rti(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull rti"); }
|
||||
static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); }
|
||||
static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); }
|
||||
static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); }
|
||||
|
||||
@ -81,6 +81,12 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("pull", { NextAction("pull my target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("pull back", { NextAction("pull my target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("pull rti", { NextAction("pull rti target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||
triggers.push_back(
|
||||
|
||||
@ -5,8 +5,184 @@
|
||||
|
||||
#include "PullStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "PassiveMultiplier.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
class PullStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
PullStrategyActionNodeFactory()
|
||||
{
|
||||
creators["pull start"] = &pull_start;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* pull_start(PlayerbotAI* /*botAI*/)
|
||||
{
|
||||
return new ActionNode("pull start", {}, {}, { NextAction("pull action", ACTION_NORMAL) });
|
||||
}
|
||||
};
|
||||
|
||||
PullStrategy::PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction)
|
||||
: Strategy(botAI), action(action), preAction(preAction)
|
||||
{
|
||||
actionNodeFactories.Add(new PullStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
PullStrategy* PullStrategy::Get(PlayerbotAI* botAI)
|
||||
{
|
||||
if (!botAI)
|
||||
return nullptr;
|
||||
|
||||
if (PullStrategy* strategy = dynamic_cast<PullStrategy*>(botAI->GetStrategy("pull", BOT_STATE_NON_COMBAT)))
|
||||
{
|
||||
if (strategy->IsPullPendingToStart() || strategy->HasPullStarted() || strategy->HasTarget())
|
||||
return strategy;
|
||||
}
|
||||
|
||||
return dynamic_cast<PullStrategy*>(botAI->GetStrategy("pull", BOT_STATE_COMBAT));
|
||||
}
|
||||
|
||||
Unit* PullStrategy::GetTarget() const
|
||||
{
|
||||
ObjectGuid const guid = botAI->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Get();
|
||||
if (guid.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
Unit* target = botAI->GetUnit(guid);
|
||||
Player* bot = botAI->GetBot();
|
||||
if (!bot || !target || !target->IsInWorld() || target->GetMapId() != bot->GetMapId())
|
||||
return nullptr;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
bool PullStrategy::HasTarget() const { return GetTarget() != nullptr; }
|
||||
|
||||
void PullStrategy::SetTarget(Unit* target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Set(target ? target->GetGUID() : ObjectGuid::Empty);
|
||||
}
|
||||
|
||||
std::string PullStrategy::GetPullActionName() const
|
||||
{
|
||||
return action;
|
||||
}
|
||||
|
||||
std::string PullStrategy::GetSpellName() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
std::string spellName = GetPullActionName();
|
||||
if (!bot || spellName != "shoot")
|
||||
return spellName;
|
||||
|
||||
Item* equippedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
if (!equippedWeapon)
|
||||
return spellName;
|
||||
|
||||
ItemTemplate const* itemTemplate = equippedWeapon->GetTemplate();
|
||||
if (!itemTemplate)
|
||||
return spellName;
|
||||
|
||||
switch (itemTemplate->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||
return "throw";
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
return "shoot gun";
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
return "shoot bow";
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
return "shoot crossbow";
|
||||
default:
|
||||
return spellName;
|
||||
}
|
||||
}
|
||||
|
||||
float PullStrategy::GetRange() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
std::string const spellName = GetSpellName();
|
||||
if (bot && !spellName.empty())
|
||||
{
|
||||
uint32 const spellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", spellName)->Get();
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
|
||||
return bot->GetSpellMaxRangeForTarget(GetTarget(), spellInfo) - CONTACT_DISTANCE;
|
||||
}
|
||||
|
||||
return (action == "shoot" ? botAI->GetRange("shoot") : botAI->GetRange("spell")) - CONTACT_DISTANCE;
|
||||
}
|
||||
|
||||
std::string PullStrategy::GetPreActionName() const
|
||||
{
|
||||
return preAction;
|
||||
}
|
||||
|
||||
bool PullStrategy::CanDoPullAction(Unit* target)
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
if (!bot || !target)
|
||||
return false;
|
||||
|
||||
if (!target->IsInWorld() || target->GetMapId() != bot->GetMapId())
|
||||
return false;
|
||||
|
||||
if (bot->getClass() != CLASS_DRUID && bot->getClass() != CLASS_PALADIN &&
|
||||
GetPullActionName() == "shoot" && !bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string const spellName = GetSpellName();
|
||||
if (spellName.empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PullStrategy::RequestPull(Unit* target, bool resetTime)
|
||||
{
|
||||
SetTarget(target);
|
||||
pendingToStart = true;
|
||||
if (resetTime)
|
||||
pullStartTime = time(nullptr);
|
||||
}
|
||||
|
||||
void PullStrategy::OnPullStarted() { pendingToStart = false; }
|
||||
|
||||
void PullStrategy::OnPullEnded()
|
||||
{
|
||||
pullStartTime = 0;
|
||||
pendingToStart = false;
|
||||
SetTarget(nullptr);
|
||||
}
|
||||
|
||||
PullMultiplier::PullMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "pull") {}
|
||||
|
||||
float PullMultiplier::GetValue(Action* action)
|
||||
{
|
||||
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy || !strategy->HasTarget() || !action)
|
||||
return 1.0f;
|
||||
|
||||
std::string const actionName = action->getName();
|
||||
if (actionName == "pull my target" ||
|
||||
actionName == "pull rti target" ||
|
||||
actionName == "reach pull" ||
|
||||
actionName == "pull start" ||
|
||||
actionName == "pull action" ||
|
||||
actionName == "return to pull position" ||
|
||||
actionName == "pull end" ||
|
||||
actionName == "follow" ||
|
||||
actionName == "set facing")
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
class MagePullMultiplier : public PassiveMultiplier
|
||||
{
|
||||
@ -24,8 +200,16 @@ float MagePullMultiplier::GetValue(Action* action)
|
||||
if (!action)
|
||||
return 1.0f;
|
||||
|
||||
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||
if (!strategy || !strategy->HasTarget())
|
||||
return 1.0f;
|
||||
|
||||
std::string const name = action->getName();
|
||||
if (actionName == name || name == "reach spell" || name == "change strategy")
|
||||
if (actionName == name || name == "pull action" || name == "pull start" || name == "pull end" ||
|
||||
name == "pull my target" || name == "pull rti target" ||
|
||||
name == "reach spell" || name == "reach pull" ||
|
||||
name == "return to pull position" || name == "follow" ||
|
||||
name == "set facing" || name == "change strategy")
|
||||
return 1.0f;
|
||||
|
||||
return PassiveMultiplier::GetValue(action);
|
||||
@ -34,18 +218,32 @@ float MagePullMultiplier::GetValue(Action* action)
|
||||
std::vector<NextAction> PullStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction(action, 105.0f),
|
||||
NextAction("follow", 104.0f),
|
||||
NextAction("end pull", 103.0f),
|
||||
NextAction("pull action", 105.0f),
|
||||
};
|
||||
}
|
||||
|
||||
void PullStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) { CombatStrategy::InitTriggers(triggers); }
|
||||
void PullStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"pull start",
|
||||
{
|
||||
NextAction("pull start", 106.0f),
|
||||
NextAction("pull action", ACTION_MOVE)
|
||||
}
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"pull end",
|
||||
{
|
||||
NextAction("pull end", 107.0f)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
void PullStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new PullMultiplier(botAI));
|
||||
multipliers.push_back(new MagePullMultiplier(botAI, action));
|
||||
CombatStrategy::InitMultipliers(multipliers);
|
||||
}
|
||||
|
||||
void PossibleAddsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
@ -61,3 +259,15 @@ void PossibleAddsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void PullBackStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
Strategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"return to pull position",
|
||||
{
|
||||
NextAction("return to pull position", ACTION_MOVE + 5.0f)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@ -6,22 +6,65 @@
|
||||
#ifndef _PLAYERBOT_PULLSTRATEGY_H
|
||||
#define _PLAYERBOT_PULLSTRATEGY_H
|
||||
|
||||
#include "CombatStrategy.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class Action;
|
||||
class Multiplier;
|
||||
class Unit;
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class PullStrategy : public CombatStrategy
|
||||
class PullStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
PullStrategy(PlayerbotAI* botAI, std::string const action) : CombatStrategy(botAI), action(action) {}
|
||||
PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction = "");
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
std::string const getName() override { return "pull"; }
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_NONCOMBAT; }
|
||||
|
||||
static PullStrategy* Get(PlayerbotAI* botAI);
|
||||
static uint8 GetMaxPullTime() { return 15; }
|
||||
|
||||
time_t GetPullStartTime() const { return pullStartTime; }
|
||||
bool IsPullPendingToStart() const { return pendingToStart; }
|
||||
bool HasPullStarted() const { return pullStartTime > 0; }
|
||||
|
||||
bool CanDoPullAction(Unit* target);
|
||||
Unit* GetTarget() const;
|
||||
bool HasTarget() const;
|
||||
|
||||
virtual std::string GetPullActionName() const;
|
||||
std::string GetSpellName() const;
|
||||
float GetRange() const;
|
||||
virtual std::string GetPreActionName() const;
|
||||
|
||||
void RequestPull(Unit* target, bool resetTime = true);
|
||||
void OnPullStarted();
|
||||
void OnPullEnded();
|
||||
|
||||
ReactStates GetPetReactState() const { return petReactState; }
|
||||
void SetPetReactState(ReactStates reactState) { petReactState = reactState; }
|
||||
|
||||
private:
|
||||
void SetTarget(Unit* target);
|
||||
|
||||
private:
|
||||
std::string const action;
|
||||
std::string const preAction;
|
||||
bool pendingToStart = false;
|
||||
time_t pullStartTime = 0;
|
||||
ReactStates petReactState = REACT_DEFENSIVE;
|
||||
};
|
||||
|
||||
class PullMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
PullMultiplier(PlayerbotAI* botAI);
|
||||
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class PossibleAddsStrategy : public Strategy
|
||||
@ -33,4 +76,13 @@ public:
|
||||
std::string const getName() override { return "adds"; }
|
||||
};
|
||||
|
||||
class PullBackStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
PullBackStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "pull back"; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -82,6 +82,7 @@ float WaitForAttackMultiplier::GetValue(Action* action)
|
||||
actionName != "set facing" &&
|
||||
actionName != "pull my target" &&
|
||||
actionName != "pull rti target" &&
|
||||
actionName != "reach pull" &&
|
||||
actionName != "pull start" &&
|
||||
actionName != "pull action" &&
|
||||
actionName != "pull end")
|
||||
|
||||
@ -95,6 +95,7 @@ public:
|
||||
creators["sit"] = &StrategyContext::sit;
|
||||
creators["mark rti"] = &StrategyContext::mark_rti;
|
||||
creators["adds"] = &StrategyContext::possible_adds;
|
||||
creators["pull back"] = &StrategyContext::pull_back;
|
||||
creators["close"] = &StrategyContext::close;
|
||||
creators["ranged"] = &StrategyContext::ranged;
|
||||
creators["behind"] = &StrategyContext::behind;
|
||||
@ -171,6 +172,7 @@ private:
|
||||
static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); }
|
||||
static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); }
|
||||
static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); }
|
||||
static Strategy* pull_back(PlayerbotAI* botAI) { return new PullBackStrategy(botAI); }
|
||||
static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); }
|
||||
static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); }
|
||||
static Strategy* battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); }
|
||||
|
||||
68
src/Ai/Base/Trigger/PullTriggers.cpp
Normal file
68
src/Ai/Base/Trigger/PullTriggers.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 "PullTriggers.h"
|
||||
|
||||
#include "PositionValue.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PullStrategy.h"
|
||||
|
||||
bool PullStartTrigger::IsActive()
|
||||
{
|
||||
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||
return strategy && strategy->IsPullPendingToStart();
|
||||
}
|
||||
|
||||
bool PullEndTrigger::IsActive()
|
||||
{
|
||||
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||
Player* bot = botAI->GetBot();
|
||||
if (!bot)
|
||||
return false;
|
||||
|
||||
if (!strategy || !strategy->HasPullStarted())
|
||||
return false;
|
||||
|
||||
Unit* target = strategy->GetTarget();
|
||||
if (!target || !target->IsInWorld())
|
||||
return true;
|
||||
|
||||
time_t const secondsSincePullStarted = time(nullptr) - strategy->GetPullStartTime();
|
||||
if (secondsSincePullStarted >= PullStrategy::GetMaxPullTime())
|
||||
return true;
|
||||
|
||||
float distanceToPullTarget = bot->GetDistance(target);
|
||||
if (distanceToPullTarget > ATTACK_DISTANCE && !target->IsNonMeleeSpellCast(false, false, true) &&
|
||||
(!botAI->IsRanged(bot) || distanceToPullTarget > botAI->GetRange("spell")))
|
||||
return false;
|
||||
|
||||
if (!botAI->HasStrategy("pull back", BOT_STATE_COMBAT))
|
||||
return true;
|
||||
|
||||
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||
if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId())
|
||||
return true;
|
||||
|
||||
return bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) <= botAI->GetRange("follow");
|
||||
}
|
||||
|
||||
bool ReturnToPullPositionTrigger::IsActive()
|
||||
{
|
||||
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||
Player* bot = botAI->GetBot();
|
||||
if (!bot)
|
||||
return false;
|
||||
|
||||
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||
if (!strategy || !strategy->HasPullStarted() || !target || !target->IsInCombat() ||
|
||||
!botAI->HasStrategy("pull back", BOT_STATE_COMBAT))
|
||||
return false;
|
||||
|
||||
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||
return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() &&
|
||||
bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance;
|
||||
}
|
||||
35
src/Ai/Base/Trigger/PullTriggers.h
Normal file
35
src/Ai/Base/Trigger/PullTriggers.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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_PULLTRIGGERS_H
|
||||
#define _PLAYERBOT_PULLTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
class PullStartTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PullStartTrigger(PlayerbotAI* botAI, std::string const name = "pull start") : Trigger(botAI, name) {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class PullEndTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PullEndTrigger(PlayerbotAI* botAI, std::string const name = "pull end") : Trigger(botAI, name) {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ReturnToPullPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ReturnToPullPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "return to pull position") {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -16,6 +16,7 @@
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "NewRpgTriggers.h"
|
||||
#include "PvpTriggers.h"
|
||||
#include "PullTriggers.h"
|
||||
#include "RpgTriggers.h"
|
||||
#include "RtiTriggers.h"
|
||||
#include "StuckTriggers.h"
|
||||
@ -129,6 +130,9 @@ public:
|
||||
creators["has attackers"] = &TriggerContext::has_attackers;
|
||||
creators["no possible targets"] = &TriggerContext::no_possible_targets;
|
||||
creators["possible adds"] = &TriggerContext::possible_adds;
|
||||
creators["pull start"] = &TriggerContext::pull_start;
|
||||
creators["pull end"] = &TriggerContext::pull_end;
|
||||
creators["return to pull position"] = &TriggerContext::return_to_pull_position;
|
||||
|
||||
creators["no drink"] = &TriggerContext::no_drink;
|
||||
creators["no food"] = &TriggerContext::no_food;
|
||||
@ -280,6 +284,9 @@ private:
|
||||
static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); }
|
||||
static Trigger* no_possible_targets(PlayerbotAI* botAI) { return new NoPossibleTargetsTrigger(botAI); }
|
||||
static Trigger* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsTrigger(botAI); }
|
||||
static Trigger* pull_start(PlayerbotAI* botAI) { return new PullStartTrigger(botAI); }
|
||||
static Trigger* pull_end(PlayerbotAI* botAI) { return new PullEndTrigger(botAI); }
|
||||
static Trigger* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionTrigger(botAI); }
|
||||
static Trigger* can_loot(PlayerbotAI* botAI) { return new CanLootTrigger(botAI); }
|
||||
static Trigger* far_from_loot_target(PlayerbotAI* botAI) { return new FarFromCurrentLootTrigger(botAI); }
|
||||
static Trigger* far_from_master(PlayerbotAI* botAI) { return new FarFromMasterTrigger(botAI); }
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
#include "BloodDKStrategy.h"
|
||||
#include "DKActions.h"
|
||||
#include "DKTriggers.h"
|
||||
#include "DeathKnightPullStrategy.h"
|
||||
#include "FrostDKStrategy.h"
|
||||
#include "GenericDKNonCombatStrategy.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PullStrategy.h"
|
||||
#include "UnholyDKStrategy.h"
|
||||
|
||||
class DeathKnightStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
@ -28,7 +28,7 @@ public:
|
||||
|
||||
private:
|
||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDKNonCombatStrategy(botAI); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "icy touch"); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new DeathKnightPullStrategy(botAI); }
|
||||
static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostDKAoeStrategy(botAI); }
|
||||
static Strategy* unholy_aoe(PlayerbotAI* botAI) { return new UnholyDKAoeStrategy(botAI); }
|
||||
};
|
||||
|
||||
43
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp
Normal file
43
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 "DeathKnightPullStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::string DeathKnightPullStrategy::GetPullActionName() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
Unit* target = GetTarget();
|
||||
if (!bot || !target ||
|
||||
(!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT)))
|
||||
{
|
||||
return PullStrategy::GetPullActionName();
|
||||
}
|
||||
|
||||
uint32 const deathGripSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "death grip")->Get();
|
||||
if (deathGripSpellId && bot->HasSpell(deathGripSpellId) &&
|
||||
botAI->CanCastSpell(deathGripSpellId, target))
|
||||
{
|
||||
return "death grip";
|
||||
}
|
||||
|
||||
uint32 const icyTouchSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "icy touch")->Get();
|
||||
if (!icyTouchSpellId || !bot->HasSpell(icyTouchSpellId) ||
|
||||
!botAI->CanCastSpell(icyTouchSpellId, target))
|
||||
{
|
||||
uint32 const darkCommandSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "dark command")->Get();
|
||||
if (darkCommandSpellId && bot->HasSpell(darkCommandSpellId) &&
|
||||
botAI->CanCastSpell(darkCommandSpellId, target))
|
||||
{
|
||||
return "dark command";
|
||||
}
|
||||
}
|
||||
|
||||
return PullStrategy::GetPullActionName();
|
||||
}
|
||||
19
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h
Normal file
19
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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_DEATH_KNIGHT_PULL_STRATEGY_H
|
||||
#define _PLAYERBOT_DEATH_KNIGHT_PULL_STRATEGY_H
|
||||
|
||||
#include "PullStrategy.h"
|
||||
|
||||
class DeathKnightPullStrategy : public PullStrategy
|
||||
{
|
||||
public:
|
||||
DeathKnightPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "icy touch") {}
|
||||
|
||||
std::string GetPullActionName() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -19,6 +19,7 @@
|
||||
#include "MeleeDruidStrategy.h"
|
||||
#include "OffhealDruidCatStrategy.h"
|
||||
#include "Playerbots.h"
|
||||
#include "DruidPullStrategy.h"
|
||||
|
||||
class DruidStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
{
|
||||
@ -26,6 +27,7 @@ public:
|
||||
DruidStrategyFactoryInternal()
|
||||
{
|
||||
creators["nc"] = &DruidStrategyFactoryInternal::nc;
|
||||
creators["pull"] = &DruidStrategyFactoryInternal::pull;
|
||||
creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe;
|
||||
creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe;
|
||||
creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff;
|
||||
@ -40,6 +42,7 @@ public:
|
||||
|
||||
private:
|
||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new DruidPullStrategy(botAI); }
|
||||
static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); }
|
||||
static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); }
|
||||
static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); }
|
||||
|
||||
46
src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp
Normal file
46
src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "DruidPullStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::string DruidPullStrategy::GetPullActionName() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
std::string actionName = PullStrategy::GetPullActionName();
|
||||
if (!bot)
|
||||
return actionName;
|
||||
|
||||
uint32 const faerieFireFeralId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "faerie fire (feral)")->Get();
|
||||
if (faerieFireFeralId && bot->HasSpell(faerieFireFeralId) &&
|
||||
(botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT)))
|
||||
{
|
||||
actionName = "faerie fire (feral)";
|
||||
}
|
||||
|
||||
Unit* target = GetTarget();
|
||||
uint32 const faerieFireSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", actionName)->Get();
|
||||
if (target && (!faerieFireSpellId || !bot->HasSpell(faerieFireSpellId) ||
|
||||
!botAI->CanCastSpell(faerieFireSpellId, target)))
|
||||
{
|
||||
uint32 const growlSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "growl")->Get();
|
||||
if (growlSpellId && bot->HasSpell(growlSpellId) && botAI->CanCastSpell(growlSpellId, target))
|
||||
return "growl";
|
||||
}
|
||||
|
||||
return actionName;
|
||||
}
|
||||
|
||||
std::string DruidPullStrategy::GetPreActionName() const
|
||||
{
|
||||
if (GetPullActionName() == "faerie fire")
|
||||
return "";
|
||||
|
||||
return PullStrategy::GetPreActionName();
|
||||
}
|
||||
20
src/Ai/Class/Druid/Strategy/DruidPullStrategy.h
Normal file
20
src/Ai/Class/Druid/Strategy/DruidPullStrategy.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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_DRUID_PULL_STRATEGY_H
|
||||
#define _PLAYERBOT_DRUID_PULL_STRATEGY_H
|
||||
|
||||
#include "PullStrategy.h"
|
||||
|
||||
class DruidPullStrategy : public PullStrategy
|
||||
{
|
||||
public:
|
||||
DruidPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "faerie fire", "dire bear form") {}
|
||||
|
||||
std::string GetPullActionName() const override;
|
||||
std::string GetPreActionName() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -12,6 +12,7 @@
|
||||
#include "OffhealRetPaladinStrategy.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "PaladinBuffStrategies.h"
|
||||
#include "PaladinPullStrategy.h"
|
||||
#include "PaladinTriggers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "TankPaladinStrategy.h"
|
||||
@ -22,6 +23,7 @@ public:
|
||||
PaladinStrategyFactoryInternal()
|
||||
{
|
||||
creators["nc"] = &PaladinStrategyFactoryInternal::nc;
|
||||
creators["pull"] = &PaladinStrategyFactoryInternal::pull;
|
||||
creators["cure"] = &PaladinStrategyFactoryInternal::cure;
|
||||
creators["boost"] = &PaladinStrategyFactoryInternal::boost;
|
||||
creators["cc"] = &PaladinStrategyFactoryInternal::cc;
|
||||
@ -31,6 +33,7 @@ public:
|
||||
|
||||
private:
|
||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericPaladinNonCombatStrategy(botAI); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new PaladinPullStrategy(botAI); }
|
||||
static Strategy* cure(PlayerbotAI* botAI) { return new PaladinCureStrategy(botAI); }
|
||||
static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); }
|
||||
static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); }
|
||||
|
||||
46
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp
Normal file
46
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "PaladinPullStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::string PaladinPullStrategy::GetPullActionName() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
Unit* target = GetTarget();
|
||||
if (!bot || !target ||
|
||||
(!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT)))
|
||||
{
|
||||
return PullStrategy::GetPullActionName();
|
||||
}
|
||||
|
||||
uint32 const avengersShieldSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "avenger's shield")->Get();
|
||||
if (avengersShieldSpellId && bot->HasSpell(avengersShieldSpellId) &&
|
||||
botAI->CanCastSpell(avengersShieldSpellId, target))
|
||||
{
|
||||
return "avenger's shield";
|
||||
}
|
||||
|
||||
uint32 const handOfReckoningSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "hand of reckoning")->Get();
|
||||
if (handOfReckoningSpellId && bot->HasSpell(handOfReckoningSpellId) &&
|
||||
botAI->CanCastSpell(handOfReckoningSpellId, target))
|
||||
{
|
||||
return "hand of reckoning";
|
||||
}
|
||||
|
||||
return PullStrategy::GetPullActionName();
|
||||
}
|
||||
|
||||
std::string PaladinPullStrategy::GetPreActionName() const
|
||||
{
|
||||
if (botAI->HasStrategy("tank", BOT_STATE_COMBAT) || botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT))
|
||||
return "";
|
||||
|
||||
return PullStrategy::GetPreActionName();
|
||||
}
|
||||
20
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h
Normal file
20
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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_PALADIN_PULL_STRATEGY_H
|
||||
#define _PLAYERBOT_PALADIN_PULL_STRATEGY_H
|
||||
|
||||
#include "PullStrategy.h"
|
||||
|
||||
class PaladinPullStrategy : public PullStrategy
|
||||
{
|
||||
public:
|
||||
PaladinPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "judgement", "seal of righteousness") {}
|
||||
|
||||
std::string GetPullActionName() const override;
|
||||
std::string GetPreActionName() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
27
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp
Normal file
27
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 "WarriorPullStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
std::string WarriorPullStrategy::GetPullActionName() const
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
Unit* target = GetTarget();
|
||||
if (!bot || !target)
|
||||
return PullStrategy::GetPullActionName();
|
||||
|
||||
uint32 const heroicThrowSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "heroic throw")->Get();
|
||||
if (heroicThrowSpellId && bot->HasSpell(heroicThrowSpellId) &&
|
||||
botAI->CanCastSpell(heroicThrowSpellId, target))
|
||||
{
|
||||
return "heroic throw";
|
||||
}
|
||||
|
||||
return PullStrategy::GetPullActionName();
|
||||
}
|
||||
19
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h
Normal file
19
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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_WARRIOR_PULL_STRATEGY_H
|
||||
#define _PLAYERBOT_WARRIOR_PULL_STRATEGY_H
|
||||
|
||||
#include "PullStrategy.h"
|
||||
|
||||
class WarriorPullStrategy : public PullStrategy
|
||||
{
|
||||
public:
|
||||
WarriorPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "shoot") {}
|
||||
|
||||
std::string GetPullActionName() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -10,8 +10,8 @@
|
||||
#include "GenericWarriorNonCombatStrategy.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PullStrategy.h"
|
||||
#include "TankWarriorStrategy.h"
|
||||
#include "WarriorPullStrategy.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "WarriorTriggers.h"
|
||||
|
||||
@ -28,7 +28,7 @@ public:
|
||||
private:
|
||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarriorNonCombatStrategy(botAI); }
|
||||
static Strategy* warrior_aoe(PlayerbotAI* botAI) { return new WarrirorAoeStrategy(botAI); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new WarriorPullStrategy(botAI); }
|
||||
};
|
||||
|
||||
class WarriorCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
|
||||
@ -428,6 +428,12 @@ void Engine::toggleStrategy(std::string const name)
|
||||
|
||||
bool Engine::HasStrategy(std::string const name) { return strategies.find(name) != strategies.end(); }
|
||||
|
||||
Strategy* Engine::GetStrategy(std::string const name)
|
||||
{
|
||||
std::map<std::string, Strategy*>::iterator i = strategies.find(name);
|
||||
return i != strategies.end() ? i->second : nullptr;
|
||||
}
|
||||
|
||||
void Engine::ProcessTriggers(bool minimal)
|
||||
{
|
||||
std::unordered_map<Trigger*, Event> fires;
|
||||
|
||||
@ -70,6 +70,7 @@ public:
|
||||
void addStrategiesNoInit(std::string first, ...);
|
||||
bool removeStrategy(std::string const name, bool init = true);
|
||||
bool HasStrategy(std::string const name);
|
||||
Strategy* GetStrategy(std::string const name);
|
||||
void removeAllStrategies();
|
||||
void toggleStrategy(std::string const name);
|
||||
std::string const ListStrategies();
|
||||
|
||||
@ -315,7 +315,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr);
|
||||
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
|
||||
else
|
||||
@ -333,7 +333,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "bthreat", "barmor", "cure", nullptr);
|
||||
else if (tab == PALADIN_TAB_HOLY)
|
||||
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
|
||||
else
|
||||
@ -352,7 +352,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
|
||||
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
|
||||
engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", nullptr);
|
||||
}
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
@ -383,7 +383,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
|
||||
engine->addStrategiesNoInit("blood", "tank assist", "pull", "pull back", nullptr);
|
||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
|
||||
else
|
||||
@ -510,7 +510,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
|
||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "pull", "barmor", nullptr);
|
||||
if (player->GetLevel() >= 20)
|
||||
nonCombatEngine->addStrategy("bhealth", false);
|
||||
else
|
||||
@ -548,14 +548,14 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||
}
|
||||
else
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||
else
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
break;
|
||||
@ -571,7 +571,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||
else
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
break;
|
||||
|
||||
@ -1795,6 +1795,11 @@ bool PlayerbotAI::ContainsStrategy(StrategyType type)
|
||||
|
||||
bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); }
|
||||
|
||||
Strategy* PlayerbotAI::GetStrategy(std::string const name, BotState type)
|
||||
{
|
||||
return engines[type] ? engines[type]->GetStrategy(name) : nullptr;
|
||||
}
|
||||
|
||||
void PlayerbotAI::ResetStrategies(bool load)
|
||||
{
|
||||
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
|
||||
|
||||
@ -405,6 +405,7 @@ public:
|
||||
void ChangeStrategy(std::string const name, BotState type);
|
||||
void ClearStrategies(BotState type);
|
||||
std::vector<std::string> GetStrategies(BotState type);
|
||||
Strategy* GetStrategy(std::string const name, BotState type);
|
||||
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
|
||||
void EvaluateHealerDpsStrategy();
|
||||
bool ContainsStrategy(StrategyType type);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user