Merge pull request #475 from liyunfan1223/dev-0814

Improve combat and spell
This commit is contained in:
Yunfan Li
2024-08-14 21:09:25 +08:00
committed by GitHub
82 changed files with 1097 additions and 730 deletions

View File

@@ -14,15 +14,15 @@ jobs:
matrix:
# the result of the matrix will be the combination of all attributes, so we get os*compiler builds
include:
- os: ubuntu-20.04
- os: ubuntu-22.04
c_compiler: clang
cpp_compiler: clang++
build_type: Release
- os: ubuntu-20.04
- os: ubuntu-22.04
c_compiler: gcc
cpp_compiler: g++
build_type: Release
- os: ubuntu-latest
- os: ubuntu-24.04
c_compiler: gcc
cpp_compiler: g++
build_type: Release

View File

@@ -271,6 +271,9 @@ AiPlayerbot.DispelAuraDuration = 700
# Delay between two bot actions
AiPlayerbot.ReactDelay = 100
# Dynamically adjust react delay for bots in different status to reduce server lags
AiPlayerbot.DynamicReactDelay = 1
# Inactivity delay
AiPlayerbot.PassiveDelay = 10000

View File

@@ -276,116 +276,116 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!player->InBattleground())
{
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana)
{
engine->addStrategy("auto save mana");
engine->addStrategy("smana", false);
}
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
{
engine->addStrategy("avoid aoe");
engine->addStrategy("aaoe", false);
}
engine->addStrategy("combat formation");
engine->addStrategy("formation", false);
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == 2)
{
engine->addStrategies("dps", "shadow debuff", "shadow aoe", nullptr);
engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr);
}
else if (tab == PRIEST_TAB_DISIPLINE)
{
engine->addStrategies("heal", nullptr);
engine->addStrategiesNoInit("heal", nullptr);
}
else
{
engine->addStrategies("holy heal", nullptr);
engine->addStrategiesNoInit("holy heal", nullptr);
}
engine->addStrategies("dps assist", "cure", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategies("arcane", "arcane aoe", nullptr);
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
else if (tab == 1)
engine->addStrategies("fire", "fire aoe", nullptr);
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
else
engine->addStrategies("frost", "frost aoe", nullptr);
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
engine->addStrategies("dps", "dps assist", "cure", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr);
engine->addStrategiesNoInit("tank", "tank assist", "aoe", "mark rti", nullptr);
else if (player->GetLevel() < 36 || tab == 0)
engine->addStrategies("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
else
engine->addStrategies("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0)
engine->addStrategies("caster", "caster aoe", "bmana", nullptr);
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
else if (tab == 2)
engine->addStrategies("heal", "bmana", nullptr);
engine->addStrategiesNoInit("heal", "bmana", nullptr);
else
engine->addStrategies("melee", "melee aoe", "bdps", nullptr);
engine->addStrategiesNoInit("melee", "melee aoe", "bdps", nullptr);
engine->addStrategies("dps assist", "cure", "totems", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", "totems", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1)
engine->addStrategies("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
else if (tab == 0)
engine->addStrategies("heal", "dps assist", "cure", "bcast", nullptr);
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
else
engine->addStrategies("dps", "dps assist", "cure", "baoe", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr);
break;
case CLASS_DRUID:
if (tab == 0)
{
engine->addStrategies("caster", "cure", "caster aoe", "dps assist", nullptr);
engine->addStrategy("caster debuff");
engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr);
engine->addStrategy("caster debuff", false);
}
else if (tab == 2)
engine->addStrategies("heal", "cure", "dps assist", nullptr);
engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr);
else
{
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
{
engine->addStrategies("cat", "dps assist", nullptr);
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
}
else
{
engine->addStrategies("bear", "tank assist", nullptr);
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
}
}
break;
case CLASS_HUNTER:
engine->addStrategies("dps", "aoe", "bdps", "dps assist", nullptr);
engine->addStrategy("dps debuff");
engine->addStrategiesNoInit("dps", "aoe", "bdps", "dps assist", nullptr);
engine->addStrategy("dps debuff", false);
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION)
{
engine->addStrategies("melee", "dps assist", "aoe", /*"behind",*/ nullptr);
engine->addStrategiesNoInit("melee", "dps assist", "aoe", /*"behind",*/ nullptr);
}
else
{
engine->addStrategies("dps", "dps assist", "aoe", /*"behind",*/ nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "aoe", /*"behind",*/ nullptr);
}
break;
case CLASS_WARLOCK:
engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", nullptr);
engine->addStrategiesNoInit("dps assist", "dps", "dps debuff", "aoe", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
engine->addStrategies("blood", "tank assist", nullptr);
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
else if (tab == 1)
engine->addStrategies("frost", "frost aoe", "dps assist", nullptr);
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
else
engine->addStrategies("unholy", "unholy aoe", "dps assist", nullptr);
engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr);
break;
}
@@ -395,9 +395,9 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!player->GetGroup())
{
// change for heal spec
engine->addStrategy("boost");
engine->addStrategy("dps assist");
engine->removeStrategy("threat");
engine->addStrategy("boost", false);
engine->addStrategy("dps assist", false);
engine->removeStrategy("threat", false);
// engine-
switch (player->getClass())
{
@@ -405,7 +405,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
if (tab != PRIEST_TAB_SHADOW)
{
engine->addStrategies("holy dps", "shadow debuff", "shadow aoe", nullptr);
engine->addStrategiesNoInit("holy dps", "shadow debuff", "shadow aoe", nullptr);
}
break;
}
@@ -413,8 +413,8 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
if (tab == DRUID_TAB_RESTORATION)
{
engine->addStrategies("caster", "caster aoe", nullptr);
engine->addStrategy("caster debuff");
engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
engine->addStrategy("caster debuff", false);
}
break;
}
@@ -422,7 +422,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
if (tab == SHAMAN_TAB_RESTORATION)
{
engine->addStrategies("caster", "caster aoe", "bmana", nullptr);
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
}
break;
}
@@ -430,7 +430,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
if (tab == PALADIN_TAB_HOLY)
{
engine->addStrategies("dps", "dps assist", "baoe", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "baoe", nullptr);
}
break;
}
@@ -452,40 +452,40 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
bgType = player->GetBattleground()->GetBgTypeID(true);
if (bgType == BATTLEGROUND_WS)
engine->addStrategy("warsong");
engine->addStrategy("warsong", false);
if (bgType == BATTLEGROUND_AB)
engine->addStrategy("arathi");
engine->addStrategy("arathi", false);
if (bgType == BATTLEGROUND_AV)
engine->addStrategy("alterac");
engine->addStrategy("alterac", false);
if (bgType == BATTLEGROUND_EY)
engine->addStrategy("eye");
engine->addStrategy("eye", false);
if (bgType == BATTLEGROUND_IC)
engine->addStrategy("isle");
engine->addStrategy("isle", false);
if (player->InArena())
{
engine->addStrategy("arena");
engine->addStrategy("arena", false);
}
engine->addStrategies("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist",
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist",
nullptr);
engine->removeStrategy("custom::say");
engine->removeStrategy("flee");
engine->removeStrategy("threat");
engine->addStrategy("boost");
engine->removeStrategy("custom::say", false);
engine->removeStrategy("flee", false);
engine->removeStrategy("threat", false);
engine->addStrategy("boost", false);
if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
engine->addStrategies("caster", "caster aoe", nullptr);
engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
if (player->getClass() == CLASS_DRUID && tab == 1)
engine->addStrategies(/*"behind",*/ "dps", nullptr);
engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
if (player->getClass() == CLASS_ROGUE)
engine->addStrategies(/*"behind",*/ "stealth", nullptr);
engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
}
}
@@ -493,6 +493,7 @@ Engine* AiFactory::createCombatEngine(Player* player, PlayerbotAI* const facade,
{
Engine* engine = new Engine(facade, aiObjectContext);
AddDefaultCombatStrategies(player, facade, engine);
engine->Init();
return engine;
}
@@ -503,103 +504,103 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
switch (player->getClass())
{
case CLASS_PRIEST:
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1)
{
nonCombatEngine->addStrategies("bthreat", "tank assist", "barmor", nullptr);
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20)
{
nonCombatEngine->addStrategy("bstats");
nonCombatEngine->addStrategy("bstats", false);
}
else
{
nonCombatEngine->addStrategy("bdps");
nonCombatEngine->addStrategy("bdps", false);
}
}
else if (tab == 0)
nonCombatEngine->addStrategies("dps assist", "bmana", "bcast", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "bmana", "bcast", nullptr);
else
nonCombatEngine->addStrategies("dps assist", "bdps", "baoe", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "bdps", "baoe", nullptr);
nonCombatEngine->addStrategies("cure", nullptr);
nonCombatEngine->addStrategiesNoInit("cure", nullptr);
break;
case CLASS_HUNTER:
nonCombatEngine->addStrategies("bdps", "dps assist", "pet", nullptr);
nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0 || tab == 2)
nonCombatEngine->addStrategy("bmana");
nonCombatEngine->addStrategy("bmana", false);
else
nonCombatEngine->addStrategy("bdps");
nonCombatEngine->addStrategy("bdps", false);
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE || tab == MAGE_TAB_FIRE)
nonCombatEngine->addStrategy("bdps");
nonCombatEngine->addStrategy("bdps", false);
else
nonCombatEngine->addStrategy("bmana");
nonCombatEngine->addStrategy("bmana", false);
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_DRUID:
if (tab == 1)
{
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
{
nonCombatEngine->addStrategy("dps assist");
nonCombatEngine->addStrategy("dps assist", false);
}
else
{
nonCombatEngine->addStrategy("tank assist");
nonCombatEngine->addStrategy("tank assist", false);
}
}
else
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
nonCombatEngine->addStrategy("tank assist");
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist");
nonCombatEngine->addStrategy("dps assist", false);
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICATION)
{
nonCombatEngine->addStrategies("bmana", nullptr);
nonCombatEngine->addStrategiesNoInit("bmana", nullptr);
}
else if (tab == WARLOCK_TAB_DEMONOLOGY)
{
nonCombatEngine->addStrategies("bdps", nullptr);
nonCombatEngine->addStrategiesNoInit("bdps", nullptr);
}
else if (tab == WARLOCK_TAB_DESTRUCTION)
{
nonCombatEngine->addStrategies("bhealth", nullptr);
nonCombatEngine->addStrategiesNoInit("bhealth", nullptr);
}
nonCombatEngine->addStrategies("dps assist", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
nonCombatEngine->addStrategy("tank assist");
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist");
nonCombatEngine->addStrategy("dps assist", false);
break;
default:
nonCombatEngine->addStrategy("dps assist");
nonCombatEngine->addStrategy("dps assist", false);
break;
}
if (!player->InBattleground())
{
nonCombatEngine->addStrategies("nc", "food", "chat", "follow", "default", "quest", "loot", "gather", "duel",
nonCombatEngine->addStrategiesNoInit("nc", "food", "chat", "follow", "default", "quest", "loot", "gather", "duel",
"buff", "mount", "emote", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana)
{
nonCombatEngine->addStrategy("auto save mana");
nonCombatEngine->addStrategy("smana", false);
}
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{
@@ -607,10 +608,10 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// let 25% of free bots start duels.
if (!urand(0, 3))
nonCombatEngine->addStrategy("start duel");
nonCombatEngine->addStrategy("start duel", false);
if (sPlayerbotAIConfig->randomBotJoinLfg)
nonCombatEngine->addStrategy("lfg");
nonCombatEngine->addStrategy("lfg", false);
if (!player->GetGroup() || player->GetGroup()->GetLeaderGUID() == player->GetGUID())
{
@@ -618,24 +619,24 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// if (!urand(0, 3))
// nonCombatEngine->addStrategy("attack tagged");
nonCombatEngine->addStrategy("pvp");
nonCombatEngine->addStrategy("pvp", false);
// nonCombatEngine->addStrategy("collision");
nonCombatEngine->addStrategy("grind");
nonCombatEngine->addStrategy("grind", false);
// nonCombatEngine->addStrategy("group");
// nonCombatEngine->addStrategy("guild");
if (sPlayerbotAIConfig->autoDoQuests)
{
// nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg");
nonCombatEngine->addStrategy("rpg", false);
}
else
{
nonCombatEngine->addStrategy("move random");
nonCombatEngine->addStrategy("move random", false);
}
if (sPlayerbotAIConfig->randomBotJoinBG)
nonCombatEngine->addStrategy("bg");
nonCombatEngine->addStrategy("bg", false);
// if (!master || GET_PLAYERBOT_AI(master))
// nonCombatEngine->addStrategy("maintenance");
@@ -651,7 +652,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
if (masterBotAI || sRandomPlayerbotMgr->IsRandomBot(player))
{
nonCombatEngine->addStrategy("pvp");
nonCombatEngine->addStrategy("pvp", false);
// nonCombatEngine->addStrategy("collision");
// nonCombatEngine->addStrategy("group");
// nonCombatEngine->addStrategy("guild");
@@ -671,7 +672,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
}
else
{
nonCombatEngine->addStrategy("pvp");
nonCombatEngine->addStrategy("pvp", false);
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
}
@@ -687,12 +688,12 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist",
nonCombatEngine->addStrategiesNoInit("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist",
"attack tagged", "emote", nullptr);
nonCombatEngine->removeStrategy("custom::say");
nonCombatEngine->removeStrategy("travel");
nonCombatEngine->removeStrategy("rpg");
nonCombatEngine->removeStrategy("grind");
nonCombatEngine->removeStrategy("custom::say", false);
nonCombatEngine->removeStrategy("travel", false);
nonCombatEngine->removeStrategy("rpg", false);
nonCombatEngine->removeStrategy("grind", false);
BattlegroundTypeId bgType = player->GetBattlegroundTypeId();
if (bgType == BATTLEGROUND_RB)
@@ -700,27 +701,27 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
if ((bgType <= BATTLEGROUND_EY || bgType == BATTLEGROUND_IC) &&
!player->InArena()) // do not add for not supported bg or arena
nonCombatEngine->addStrategy("battleground");
nonCombatEngine->addStrategy("battleground", false);
if (bgType == BATTLEGROUND_WS)
nonCombatEngine->addStrategy("warsong");
nonCombatEngine->addStrategy("warsong", false);
if (bgType == BATTLEGROUND_AV)
nonCombatEngine->addStrategy("alterac");
nonCombatEngine->addStrategy("alterac", false);
if (bgType == BATTLEGROUND_AB)
nonCombatEngine->addStrategy("arathi");
nonCombatEngine->addStrategy("arathi", false);
if (bgType == BATTLEGROUND_EY)
nonCombatEngine->addStrategy("eye");
nonCombatEngine->addStrategy("eye", false);
if (bgType == BATTLEGROUND_IC)
nonCombatEngine->addStrategy("isle");
nonCombatEngine->addStrategy("isle", false);
if (player->InArena())
{
nonCombatEngine->addStrategy("arena");
nonCombatEngine->removeStrategy("mount");
nonCombatEngine->addStrategy("arena", false);
nonCombatEngine->removeStrategy("mount", false);
}
}
}
@@ -730,17 +731,18 @@ Engine* AiFactory::createNonCombatEngine(Player* player, PlayerbotAI* const faca
Engine* nonCombatEngine = new Engine(facade, aiObjectContext);
AddDefaultNonCombatStrategies(player, facade, nonCombatEngine);
nonCombatEngine->Init();
return nonCombatEngine;
}
void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const facade, Engine* deadEngine)
{
(void)facade; // unused and remove warning
deadEngine->addStrategies("dead", "stay", "chat", "default", "follow", nullptr);
deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr);
if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup())
{
deadEngine->removeStrategy("follow");
deadEngine->removeStrategy("follow", false);
}
}
@@ -748,5 +750,6 @@ Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, A
{
Engine* deadEngine = new Engine(facade, AiObjectContext);
AddDefaultDeadStrategies(player, facade, deadEngine);
deadEngine->Init();
return deadEngine;
}

View File

@@ -342,16 +342,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
bool min = minimal;
UpdateAIInternal(elapsed, min);
inCombat = bot->IsInCombat();
// test fix lags because of BG
bool inBG = bot->InBattleground() || bot->InArena();
if (bot && !inCombat)
min = true;
if (HasRealPlayerMaster() || (sPlayerbotAIConfig->fastReactInBG && inBG))
min = false;
YieldThread(min);
YieldThread(GetReactDelay());
}
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
@@ -940,6 +931,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
if (!AllowActivity())
return;
WorldPacket p(packet);
if (!p.empty() && (p.GetOpcode() == SMSG_MESSAGECHAT || p.GetOpcode() == SMSG_GM_MESSAGECHAT))
{
@@ -1584,6 +1576,9 @@ void PlayerbotAI::ResetStrategies(bool load)
AiFactory::AddDefaultNonCombatStrategies(bot, this, engines[BOT_STATE_NON_COMBAT]);
AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]);
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
engines[i]->Init();
// if (load)
// sPlayerbotDbStore->Load(this);
}
@@ -2748,7 +2743,8 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
}
uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration();
if (CastingTime && bot->isMoving())
bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT;
if ((CastingTime || interruptOnMove) && bot->isMoving())
{
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{
@@ -4795,7 +4791,8 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
{
for (const auto& id : uPriorizedWizardOilIds)
{
if (oil = FindConsumable(id))
oil = FindConsumable(id);
if (oil)
return oil;
}
}
@@ -4804,7 +4801,8 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
{
for (const auto& id : uPriorizedManaOilIds)
{
if (oil = FindConsumable(id))
oil = FindConsumable(id);
if (oil)
return oil;
}
}
@@ -5691,3 +5689,81 @@ std::set<uint32> PlayerbotAI::GetCurrentIncompleteQuestIds()
return result;
}
uint32 PlayerbotAI::GetReactDelay()
{
uint32 base = sPlayerbotAIConfig->reactDelay;
// old calculate method
if (!sPlayerbotAIConfig->dynamicReactDelay)
{
inCombat = bot->IsInCombat();
bool min = false;
// test fix lags because of BG
bool inBG = bot->InBattleground() || bot->InArena();
if (bot && !inCombat)
min = true;
if (HasRealPlayerMaster() || (sPlayerbotAIConfig->fastReactInBG && inBG))
min = false;
if (min)
return base * 10;
return base;
}
float multiplier = 1.0f;
if (HasRealPlayerMaster())
{
multiplier = 1.0f;
return base * multiplier;
}
bool inBg = bot->InBattleground() || bot->InArena();
if (inBg)
{
multiplier = sPlayerbotAIConfig->fastReactInBG ? 1.0f : 10.0f;
return base * multiplier;
}
if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT)
{
multiplier = 5.0f;
return base * multiplier;
}
bool isResting = bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
if (!isResting)
{
multiplier = urand(5, 20);
return base * multiplier;
}
multiplier = urand(20, 200);
return base * multiplier;
}
void PlayerbotAI::PetFollow()
{
Pet* pet = bot->GetPet();
if (!pet)
return;
pet->AttackStop();
pet->InterruptNonMeleeSpells(false);
pet->ClearInPetCombat();
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
if (pet->ToPet())
pet->ToPet()->ClearCastWhenWillAvailable();
CharmInfo* charmInfo = pet->GetCharmInfo();
if (!charmInfo)
return;
charmInfo->SetCommandState(COMMAND_FOLLOW);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsReturning(true);
charmInfo->SetIsCommandFollow(true);
charmInfo->SetIsFollowing(false);
charmInfo->RemoveStayPosition();
charmInfo->SetForcedSpell(0);
charmInfo->SetForcedTargetGUID();
}

View File

@@ -554,11 +554,14 @@ public:
uint32 GetInventoryItemsCountWithId(uint32 itemId);
bool HasItemInInventory(uint32 itemId);
std::vector<std::pair<const Quest*, uint32>> GetCurrentQuestsRequiringItemId(uint32 itemId);
uint32 GetReactDelay();
std::vector<const Quest*> GetAllCurrentQuests();
std::vector<const Quest*> GetCurrentIncompleteQuests();
std::set<uint32> GetAllCurrentQuestIds();
std::set<uint32> GetCurrentIncompleteQuestIds();
void PetFollow();
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore, bool mixed = false);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);

View File

@@ -49,10 +49,10 @@ void PlayerbotAIBase::IncreaseNextCheckDelay(uint32 delay)
bool PlayerbotAIBase::CanUpdateAI() { return nextAICheckDelay == 0; }
void PlayerbotAIBase::YieldThread(bool delay)
void PlayerbotAIBase::YieldThread(uint32 delay)
{
if (nextAICheckDelay < sPlayerbotAIConfig->reactDelay)
nextAICheckDelay = delay ? sPlayerbotAIConfig->reactDelay * 10 : sPlayerbotAIConfig->reactDelay;
if (nextAICheckDelay < delay)
nextAICheckDelay = delay;
}
bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove; }

View File

@@ -7,6 +7,7 @@
#define _PLAYERBOT_PLAYERBOTAIBASE_H
#include "Define.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAIBase
{
@@ -16,7 +17,7 @@ public:
bool CanUpdateAI();
void SetNextCheckDelay(uint32 const delay);
void IncreaseNextCheckDelay(uint32 delay);
void YieldThread(bool delay = false);
void YieldThread(uint32 delay = sPlayerbotAIConfig->reactDelay);
virtual void UpdateAI(uint32 elapsed, bool minimal = false);
virtual void UpdateAIInternal(uint32 elapsed, bool minimal = false) = 0;
bool IsActive();

View File

@@ -59,7 +59,8 @@ bool PlayerbotAIConfig::Initialize()
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000);
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 500);
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
dynamicReactDelay = sConfigMgr->GetOption<bool>("AiPlayerbot.DynamicReactDelay", true);
passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
@@ -77,7 +78,7 @@ bool PlayerbotAIConfig::Initialize()
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f);
aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f);
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.0f);
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 1.5f);
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 0.75f);
followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f);

View File

@@ -58,6 +58,7 @@ public:
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
bool dynamicReactDelay;
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance,
tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance,
targetPosRecalcDistance, farDistance, healDistance, aggroDistance;

View File

@@ -29,6 +29,7 @@
#include "LFGMgr.h"
#include "MapMgr.h"
#include "PerformanceMonitor.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotCommandServer.h"
#include "PlayerbotFactory.h"
@@ -2333,6 +2334,11 @@ void RandomPlayerbotMgr::PrintStats()
uint32 taxi = 0;
uint32 moving = 0;
uint32 mounted = 0;
uint32 inBg = 0;
uint32 rest = 0;
uint32 engine_noncombat = 0;
uint32 engine_combat = 0;
uint32 engine_dead = 0;
uint32 stateCount[MAX_TRAVEL_STATE + 1] = {0};
std::vector<std::pair<Quest const*, int32>> questCount;
for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i)
@@ -2369,7 +2375,33 @@ void RandomPlayerbotMgr::PrintStats()
// if (!GetEventValue(botId, "dead"))
//++revive;
}
if (bot->IsInCombat())
{
++combat;
}
if (bot->isMoving())
{
++moving;
}
if (bot->IsMounted())
{
++mounted;
}
if (bot->InBattleground() || bot->InArena())
{
++inBg;
}
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
{
++rest;
}
if (botAI->GetState() == BOT_STATE_NON_COMBAT)
++engine_noncombat;
else if (botAI->GetState() == BOT_STATE_COMBAT)
++engine_combat;
else
++engine_dead;
uint8 spec = AiFactory::GetPlayerSpecTab(bot);
switch (bot->getClass())
{
@@ -2490,8 +2522,15 @@ void RandomPlayerbotMgr::PrintStats()
LOG_INFO("playerbots", " On taxi: {}", taxi);
LOG_INFO("playerbots", " On mount: {}", mounted);
LOG_INFO("playerbots", " In combat: {}", combat);
LOG_INFO("playerbots", " In BG: {}", inBg);
LOG_INFO("playerbots", " In Rest: {}", rest);
LOG_INFO("playerbots", " Dead: {}", dead);
LOG_INFO("playerbots", "Bots engine:", dead);
LOG_INFO("playerbots", " Non-combat: {}", engine_noncombat);
LOG_INFO("playerbots", " Combat: {}", engine_combat);
LOG_INFO("playerbots", " Dead: {}", engine_dead);
LOG_INFO("playerbots", "Bots questing:");
LOG_INFO("playerbots", " Picking quests: {}",
stateCount[TRAVEL_STATE_TRAVEL_PICK_UP_QUEST] + stateCount[TRAVEL_STATE_WORK_PICK_UP_QUEST]);
@@ -2746,3 +2785,4 @@ ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, Battlegrou
return battleMasterGUID;
}

View File

@@ -412,10 +412,10 @@ void PlayerbotFactory::Randomize(bool incremental)
void PlayerbotFactory::Refresh()
{
// Prepare();
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
InitEquipment(true);
}
// if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
// {
// InitEquipment(true);
// }
ClearInventory();
InitAmmo();
InitFood();

View File

@@ -225,7 +225,10 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
if (type_ != CollectorType::SPELL_HEAL)
stats[STATS_TYPE_CRIT] += 50;
return true;
break;
case 59620:
if (type_ == CollectorType::MELEE)
stats[STATS_TYPE_ATTACK_POWER] += 120;
return true;
case 67702: // Death's Verdict
stats[STATS_TYPE_ATTACK_POWER] += 225;
return true;
@@ -493,6 +496,13 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
case SPELL_AURA_MOD_INCREASE_HEALTH:
stats[STATS_TYPE_STAMINA] += val * multiplier / 15;
break;
case SPELL_AURA_SCHOOL_ABSORB:
{
int32 schoolType = effectInfo.MiscValue;
if (schoolType & SPELL_SCHOOL_MASK_NORMAL)
stats[STATS_TYPE_STAMINA] += val * multiplier / 15;
break;
}
case SPELL_AURA_MOD_ATTACK_POWER:
stats[STATS_TYPE_ATTACK_POWER] += val * multiplier;
break;

View File

@@ -9,6 +9,7 @@
#include "AiFactory.h"
#include "DBCStores.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "PlayerbotAI.h"
#include "SharedDefines.h"
@@ -231,7 +232,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.2f;
}
else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE ||
else if (cls == CLASS_WARLOCK ||
cls == CLASS_MAGE ||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
@@ -454,6 +456,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{
weight_ *= 0.1;
}
if (cls == CLASS_ROGUE && player_->HasAura(13964)
&& (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
{
weight_ *= 1.1;
}
if (cls == CLASS_WARRIOR && player_->HasAura(12785)
&& (proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2))
{
weight_ *= 1.1;
}
}
}

View File

@@ -409,26 +409,26 @@ ActionResult Engine::ExecuteAction(std::string const name, Event event, std::str
return result ? ACTION_RESULT_OK : ACTION_RESULT_FAILED;
}
void Engine::addStrategy(std::string const name)
void Engine::addStrategy(std::string const name, bool init)
{
removeStrategy(name);
removeStrategy(name, init);
if (Strategy* strategy = aiObjectContext->GetStrategy(name))
{
std::set<std::string> siblings = aiObjectContext->GetSiblingStrategy(name);
for (std::set<std::string>::iterator i = siblings.begin(); i != siblings.end(); i++)
removeStrategy(*i);
removeStrategy(*i, init);
LogAction("S:+%s", strategy->getName().c_str());
strategies[strategy->getName()] = strategy;
}
Init();
if (init)
Init();
}
void Engine::addStrategies(std::string first, ...)
{
addStrategy(first);
addStrategy(first, false);
va_list vl;
va_start(vl, first);
@@ -438,13 +438,34 @@ void Engine::addStrategies(std::string first, ...)
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur);
addStrategy(cur, false);
} while (cur);
Init();
va_end(vl);
}
void Engine::addStrategiesNoInit(std::string first, ...)
{
addStrategy(first, false);
va_list vl;
va_start(vl, first);
const char* cur;
do
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur, false);
} while (cur);
va_end(vl);
}
bool Engine::removeStrategy(std::string const name)
bool Engine::removeStrategy(std::string const name, bool init)
{
std::map<std::string, Strategy*>::iterator i = strategies.find(name);
if (i == strategies.end())
@@ -452,7 +473,8 @@ bool Engine::removeStrategy(std::string const name)
LogAction("S:-%s", name.c_str());
strategies.erase(i);
Init();
if (init)
Init();
return true;
}

View File

@@ -65,9 +65,10 @@ public:
Engine(PlayerbotAI* botAI, AiObjectContext* factory);
void Init();
void addStrategy(std::string const name);
void addStrategy(std::string const name, bool init = true);
void addStrategies(std::string first, ...);
bool removeStrategy(std::string const name);
void addStrategiesNoInit(std::string first, ...);
bool removeStrategy(std::string const name, bool init = true);
bool HasStrategy(std::string const name);
void removeAllStrategies();
void toggleStrategy(std::string const name);

View File

@@ -60,7 +60,7 @@ public:
creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive;
// creators["conserve mana"] = &StrategyContext::conserve_mana;
creators["auto save mana"] = &StrategyContext::auto_save_mana;
creators["smana"] = &StrategyContext::auto_save_mana;
creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat;
creators["default"] = &StrategyContext::world_packet;
@@ -112,9 +112,9 @@ public:
creators["group"] = &StrategyContext::group;
creators["guild"] = &StrategyContext::guild;
creators["grind"] = &StrategyContext::grind;
creators["avoid aoe"] = &StrategyContext::avoid_aoe;
creators["aaoe"] = &StrategyContext::avoid_aoe;
creators["move random"] = &StrategyContext::move_random;
creators["combat formation"] = &StrategyContext::combat_formation;
creators["formation"] = &StrategyContext::combat_formation;
}
private:

View File

@@ -405,14 +405,15 @@ public:
}
};
class RecentlyFleeInfo : public ManualSetValue<std::list<FleeInfo>>
class RecentlyFleeInfo : public ManualSetValue<std::list<FleeInfo>&>
{
public:
RecentlyFleeInfo(PlayerbotAI* botAI, std::list<FleeInfo> defaultValue = {},
std::string const name = "recently flee info")
: ManualSetValue<std::list<FleeInfo>>(botAI, defaultValue, name)
RecentlyFleeInfo(PlayerbotAI* botAI, std::string const name = "recently flee info")
: ManualSetValue<std::list<FleeInfo>&>(botAI, data, name)
{
}
private:
std::list<FleeInfo> data = {};
};
#endif

View File

@@ -8,10 +8,9 @@
#include "AddLootAction.h"
#include "AttackAction.h"
#include "AutoLearnSpellAction.h"
#include "ShareQuestAction.h"
#include "BattleGroundTactics.h"
#include "AutoTeleportForLevelAction.h"
#include "AutoMaintenanceOnLevelupAction.h"
#include "BattleGroundJoinAction.h"
#include "BattleGroundTactics.h"
#include "BuyAction.h"
@@ -92,7 +91,7 @@ public:
creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect;
creators["flee"] = &ActionContext::flee;
creators["flee with pet"] = &ActionContext::flee_with_pet;
creators["avoid aoe"] = &ActionContext::avoid_aoe;
creators["aaoe"] = &ActionContext::avoid_aoe;
creators["combat formation move"] = &ActionContext::combat_formation_move;
creators["disperse set"] = &ActionContext::disperse_set;
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
@@ -154,11 +153,12 @@ public:
creators["give water"] = &ActionContext::give_water;
creators["mount"] = &ActionContext::mount;
creators["war stomp"] = &ActionContext::war_stomp;
creators["blood fury"] = &ActionContext::blood_fury;
creators["berserking"] = &ActionContext::berserking;
creators["use trinket"] = &ActionContext::use_trinket;
creators["auto talents"] = &ActionContext::auto_talents;
creators["auto learn spell"] = &ActionContext::auto_learn_spell;
creators["auto share quest"] = &ActionContext::auto_share_quest;
creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level;
creators["auto upgrade equip"] = &ActionContext::auto_upgrade_equip;
creators["auto maintenance on levelup"] = &ActionContext::auto_maintenance_on_levelup;
creators["xp gain"] = &ActionContext::xp_gain;
creators["invite nearby"] = &ActionContext::invite_nearby;
creators["invite guild"] = &ActionContext::invite_guild;
@@ -324,11 +324,12 @@ private:
static Action* try_emergency(PlayerbotAI* botAI) { return new TryEmergencyAction(botAI); }
static Action* mount(PlayerbotAI* botAI) { return new CastSpellAction(botAI, "mount"); }
static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); }
static Action* blood_fury(PlayerbotAI* botAI) { return new CastBloodFuryAction(botAI); }
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); }
static Action* auto_upgrade_equip(PlayerbotAI* botAI) { return new AutoUpgradeEquipAction(botAI); }
static Action* auto_maintenance_on_levelup(PlayerbotAI* botAI) { return new AutoMaintenanceOnLevelupAction(botAI); }
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); }
static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); }

View File

@@ -7,9 +7,12 @@
#include "CreatureAI.h"
#include "Event.h"
#include "LastMovementValue.h"
#include "LootObjectStack.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
#include "Unit.h"
bool AttackAction::Execute(Event event)
@@ -109,24 +112,32 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bot->SetSelection(target->GetGUID());
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
if (oldTarget == target && botAI->GetState() == BOT_STATE_COMBAT && bot->GetVictim() == target)
return false;
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
LastMovement& lastMovement = AI_VALUE(LastMovement&, "last movement");
if (lastMovement.priority < MovementPriority::MOVEMENT_COMBAT && bot->isMoving())
{
AI_VALUE(LastMovement&, "last movement").clear();
bot->GetMotionMaster()->Clear(false);
bot->StopMoving();
}
bool melee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
bot->Attack(target, melee);
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
{
sServerFacade->SetFacingTo(bot, target);
}
botAI->ChangeEngine(BOT_STATE_COMBAT);
if (!bot->GetVictim())
{
return false;
}
bot->Attack(target, melee);
/* prevent pet dead immediately in group */
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) {
// with_pet = false;

View File

@@ -1,206 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AutoLearnSpellAction.h"
#include "Event.h"
#include "GuildMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "BroadcastHelper.h"
bool AutoLearnSpellAction::Execute(Event event)
{
std::string const param = event.getParam();
std::ostringstream out;
LearnSpells(&out);
if (!out.str().empty())
{
std::string const temp = out.str();
out.seekp(0);
out << "Learned spells: ";
out << temp;
out.seekp(-2, out.cur);
out << ".";
botAI->TellMaster(out);
}
return true;
}
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{
BroadcastHelper::BroadcastLevelup(botAI, bot);
if (sPlayerbotAIConfig->autoLearnTrainerSpells &&
sRandomPlayerbotMgr->IsRandomBot(bot))
LearnTrainerSpells(out);
if (sPlayerbotAIConfig->autoLearnQuestSpells &&
sRandomPlayerbotMgr->IsRandomBot(bot))
LearnQuestSpells(out);
}
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)
{
PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitClassSpells();
factory.InitAvailableSpells();
factory.InitSkills();
factory.InitPet();
// bot->LearnDefaultSkills();
// CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
// for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin(); i !=
// creatureTemplateContainer->end(); ++i)
// {
// CreatureTemplate const& co = i->second;
// if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS)
// continue;
// if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass())
// continue;
// uint32 trainerId = co.Entry;
// TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
// if (!trainer_spells)
// trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
// if (!trainer_spells)
// continue;
// for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr !=
// trainer_spells->spellList.end(); ++itr)
// {
// TrainerSpell const* tSpell = &itr->second;
// if (!tSpell)
// continue;
// if (!tSpell->learnedSpell[0] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[0]))
// continue;
// TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
// if (state != TRAINER_SPELL_GREEN)
// continue;
// SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
// bool learn = true;
// for (uint8 j = 0; j < 3; ++j)
// {
// if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j]))
// continue;
// if (spellInfo->Effects[j].Effect == SPELL_EFFECT_PROFICIENCY ||
// spellInfo->Effects[j].Effect == SPELL_EFFECT_SKILL_STEP ||
// spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD)
// {
// learn = false;
// break;
// }
// }
// if (!learn) {
// continue;
// }
// if (tSpell->learnedSpell[0]) {
// bot->learnSpell(tSpell->learnedSpell[0], false);
// }
// else {
// LOG_INFO("playerbots", "!tSpell->learnedSpell[0] {}", tSpell->spell);
// botAI->CastSpell(tSpell->spell, bot);
// }
// }
// }
}
void AutoLearnSpellAction::LearnQuestSpells(std::ostringstream* out)
{
// CreatureTemplate const* co = sCreatureStorage.LookupEntry<CreatureTemplate>(id);
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
{
uint32 questId = i->first;
Quest const* quest = i->second;
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10)
continue;
if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->GetLevel() ||
!bot->SatisfyQuestRace(quest, false))
continue;
if (quest->GetRewSpellCast() > 0)
{
LearnSpell(quest->GetRewSpellCast(), out);
}
else if (quest->GetRewSpell() > 0)
{
LearnSpell(quest->GetRewSpell(), out);
}
}
}
std::string const FormatSpell(SpellInfo const* sInfo)
{
std::ostringstream out;
std::string const rank = sInfo->Rank[0];
if (rank.empty())
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << "]|h|r";
else
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << " " << rank << "]|h|r";
return out.str();
}
void AutoLearnSpellAction::LearnSpell(uint32 spellId, std::ostringstream* out)
{
SpellInfo const* proto = sSpellMgr->GetSpellInfo(spellId);
if (!proto)
return;
bool learned = false;
for (uint8 j = 0; j < 3; ++j)
{
if (proto->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
{
uint32 learnedSpell = proto->Effects[j].TriggerSpell;
if (!bot->HasSpell(learnedSpell))
{
bot->learnSpell(learnedSpell);
*out << FormatSpell(sSpellMgr->GetSpellInfo(learnedSpell)) << ", ";
}
learned = true;
}
}
if (!learned)
{
if (!bot->HasSpell(spellId))
{
bot->learnSpell(spellId);
*out << FormatSpell(proto) << ", ";
}
}
}
bool AutoUpgradeEquipAction::Execute(Event event)
{
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
{
return false;
}
PlayerbotFactory factory(bot, bot->GetLevel());
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
factory.InitEquipment(true);
}
factory.InitAmmo();
return true;
}

View File

@@ -0,0 +1,171 @@
#include "AutoMaintenanceOnLevelupAction.h"
#include "GuildMgr.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "SharedDefines.h"
#include "BroadcastHelper.h"
bool AutoMaintenanceOnLevelupAction::Execute(Event event)
{
AutoPickTalents();
AutoLearnSpell();
AutoUpgradeEquip();
AutoTeleportForLevel();
return true;
}
void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel()
{
if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot))
{
return;
}
if (botAI->HasRealPlayerMaster())
{
return;
}
sRandomPlayerbotMgr->RandomTeleportForLevel(bot);
return;
}
void AutoMaintenanceOnLevelupAction::AutoPickTalents()
{
if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot))
return;
if (bot->GetFreeTalentPoints() <= 0)
return;
PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitTalentsTree(true, true, true);
factory.InitPetTalents();
}
void AutoMaintenanceOnLevelupAction::AutoLearnSpell()
{
std::ostringstream out;
LearnSpells(&out);
if (!out.str().empty())
{
std::string const temp = out.str();
out.seekp(0);
out << "Learned spells: ";
out << temp;
out.seekp(-2, out.cur);
out << ".";
botAI->TellMaster(out);
}
return;
}
void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out)
{
BroadcastHelper::BroadcastLevelup(botAI, bot);
if (sPlayerbotAIConfig->autoLearnTrainerSpells)
LearnTrainerSpells(out);
if (sPlayerbotAIConfig->autoLearnQuestSpells)
LearnQuestSpells(out);
}
void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out)
{
PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitClassSpells();
factory.InitAvailableSpells();
factory.InitSkills();
factory.InitPet();
}
void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
{
// CreatureTemplate const* co = sCreatureStorage.LookupEntry<CreatureTemplate>(id);
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
{
uint32 questId = i->first;
Quest const* quest = i->second;
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10)
continue;
if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->GetLevel() ||
!bot->SatisfyQuestRace(quest, false))
continue;
if (quest->GetRewSpellCast() > 0)
{
LearnSpell(quest->GetRewSpellCast(), out);
}
else if (quest->GetRewSpell() > 0)
{
LearnSpell(quest->GetRewSpell(), out);
}
}
}
std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* sInfo)
{
std::ostringstream out;
std::string const rank = sInfo->Rank[0];
if (rank.empty())
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << "]|h|r";
else
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << " " << rank << "]|h|r";
return out.str();
}
void AutoMaintenanceOnLevelupAction::LearnSpell(uint32 spellId, std::ostringstream* out)
{
SpellInfo const* proto = sSpellMgr->GetSpellInfo(spellId);
if (!proto)
return;
bool learned = false;
for (uint8 j = 0; j < 3; ++j)
{
if (proto->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
{
uint32 learnedSpell = proto->Effects[j].TriggerSpell;
if (!bot->HasSpell(learnedSpell))
{
bot->learnSpell(learnedSpell);
*out << FormatSpell(sSpellMgr->GetSpellInfo(learnedSpell)) << ", ";
}
learned = true;
}
}
if (!learned)
{
if (!bot->HasSpell(spellId))
{
bot->learnSpell(spellId);
*out << FormatSpell(proto) << ", ";
}
}
}
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
{
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
{
return;
}
PlayerbotFactory factory(bot, bot->GetLevel());
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
factory.InitEquipment(true);
}
factory.InitAmmo();
return;
}

View File

@@ -3,33 +3,33 @@
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AUTOLEARNSPELLACTION_H
#define _PLAYERBOT_AUTOLEARNSPELLACTION_H
#ifndef _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#define _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#include "Action.h"
class PlayerbotAI;
class AutoLearnSpellAction : public Action
class AutoMaintenanceOnLevelupAction : public Action
{
public:
AutoLearnSpellAction(PlayerbotAI* botAI, std::string const name = "auto learn spell") : Action(botAI, name) {}
AutoMaintenanceOnLevelupAction(PlayerbotAI* botAI, std::string const name = "auto maintenance on levelup")
: Action(botAI, name)
{
}
bool Execute(Event event);
private:
protected:
void AutoTeleportForLevel();
void AutoPickTalents();
void AutoLearnSpell();
void AutoUpgradeEquip();
void LearnSpells(std::ostringstream* out);
void LearnTrainerSpells(std::ostringstream* out);
void LearnQuestSpells(std::ostringstream* out);
void LearnSpell(uint32 spellId, std::ostringstream* out);
};
class AutoUpgradeEquipAction : public Action
{
public:
AutoUpgradeEquipAction(PlayerbotAI* botAI, std::string const name = "auto upgrade equip") : Action(botAI, name) {}
bool Execute(Event event);
std::string const FormatSpell(SpellInfo const* sInfo);
};
#endif

View File

@@ -1,21 +0,0 @@
#include "AutoTeleportForLevelAction.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "SharedDefines.h"
bool AutoTeleportForLevelAction::Execute(Event event)
{
if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot))
{
return false;
}
if (botAI->HasRealPlayerMaster())
{
return false;
}
sRandomPlayerbotMgr->RandomTeleportForLevel(bot);
return true;
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#define _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#include "Action.h"
class PlayerbotAI;
class AutoTeleportForLevelAction : public Action
{
public:
AutoTeleportForLevelAction(PlayerbotAI* botAI, std::string const name = "auto teleport for level")
: Action(botAI, name)
{
}
bool Execute(Event event);
};
#endif

View File

@@ -32,7 +32,7 @@ bool FollowChatShortcutAction::Execute(Event event)
if (!master)
return false;
botAI->Reset();
// botAI->Reset();
botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT);
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
@@ -57,7 +57,14 @@ bool FollowChatShortcutAction::Execute(Event event)
if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1)
return false;
moved = MoveTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
MovementPriority priority = botAI->GetState() == BOT_STATE_COMBAT ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL;
moved = MoveTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), false, false, false,
true, priority);
}
if (Pet* pet = bot->GetPet())
{
botAI->PetFollow();
}
if (moved)

View File

@@ -8,6 +8,7 @@
#include "BattlegroundWS.h"
#include "Event.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SpellAuraEffects.h"
@@ -17,35 +18,46 @@ bool CheckMountStateAction::Execute(Event event)
bool noattackers =
AI_VALUE2(bool, "combat", "self target") ? (AI_VALUE(uint8, "attacker count") > 0 ? false : true) : true;
bool enemy = AI_VALUE(Unit*, "enemy player target");
// ignore grind target in BG or bots will dismount near any creature (eg: the rams in AV)
bool dps = AI_VALUE(Unit*, "dps target");
// bool fartarget = (enemy && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player
// target"), 40.0f)) ||
// (dps && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "dps target"), 50.0f));
bool attackdistance = false;
bool shouldDismount = false;
bool shouldMount = false;
// bool chasedistance = false;
float attack_distance = 35.0f;
float attack_distance;
float mount_distance;
if (PlayerbotAI::IsMelee(bot))
{
attack_distance = 5.0f;
attack_distance = sPlayerbotAIConfig->meleeDistance + 2.0f;
mount_distance = sPlayerbotAIConfig->meleeDistance + 10.0f;
}
else
{
attack_distance = 30.0f;
attack_distance = sPlayerbotAIConfig->spellDistance + 2.0f;
mount_distance = sPlayerbotAIConfig->spellDistance + 10.0f;
}
// if (enemy)
// attack_distance /= 2;
Unit* currentTarget = AI_VALUE(Unit*, "current target");
if (dps || enemy)
if (currentTarget)
{
Unit* currentTarget = AI_VALUE(Unit*, "current target");
attackdistance =
(enemy || dps) && currentTarget &&
sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "current target"), attack_distance);
float combatReach = bot->GetCombatReach() + currentTarget->GetCombatReach();
attack_distance += combatReach;
float disToTarget = bot->GetExactDist(currentTarget);
shouldDismount = disToTarget <= attack_distance;
}
else
shouldDismount = false;
if (bot->IsMounted() && attackdistance)
if (currentTarget)
{
float combatReach = bot->GetCombatReach() + currentTarget->GetCombatReach();
mount_distance += combatReach;
float disToTarget = bot->GetExactDist(currentTarget);
shouldMount = disToTarget > mount_distance;
}
else
shouldMount = true;
if (bot->IsMounted() && shouldDismount)
{
WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
@@ -59,7 +71,7 @@ bool CheckMountStateAction::Execute(Event event)
return false;
// bool farFromMaster = sServerFacade->GetDistance2d(bot, master) > sPlayerbotAIConfig->sightDistance;
if (master->IsMounted() && !bot->IsMounted() && noattackers && !attackdistance && !bot->IsInCombat() &&
if (master->IsMounted() && !bot->IsMounted() && noattackers && shouldMount && !bot->IsInCombat() &&
botAI->GetState() != BOT_STATE_COMBAT)
{
return Mount();
@@ -71,16 +83,6 @@ bool CheckMountStateAction::Execute(Event event)
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
return true;
}
// if (!bot->IsMounted() && (chasedistance || (farFromMaster && botAI->HasStrategy("follow",
// BOT_STATE_NON_COMBAT))) && !bot->IsInCombat() && !dps)
// return Mount();
// if (!bot->IsFlying() && ((!farFromMaster && !master->IsMounted()) || attackdistance) && bot->IsMounted())
// {
// WorldPacket emptyPacket;
// bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
// return true;
// }
return false;
}
@@ -88,13 +90,13 @@ bool CheckMountStateAction::Execute(Event event)
// For random bots
if (!bot->InBattleground() && !master)
{
if (!bot->IsMounted() && noattackers && !attackdistance && !bot->IsInCombat())
if (!bot->IsMounted() && noattackers && shouldMount && !bot->IsInCombat())
{
return Mount();
}
}
if (bot->InBattleground() && !attackdistance && noattackers && !bot->IsInCombat() && !bot->IsMounted())
if (bot->InBattleground() && shouldMount && noattackers && !bot->IsInCombat() && !bot->IsMounted())
{
if (bot->GetBattlegroundTypeId() == BATTLEGROUND_WS)
{
@@ -108,24 +110,7 @@ bool CheckMountStateAction::Execute(Event event)
return Mount();
}
// if (!bot->InBattleground())
// {
// if (AI_VALUE(GuidPosition, "rpg target"))
// {
// if (sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "rpg target"),
// sPlayerbotAIConfig->farDistance) && noattackers && !dps && !enemy)
// return Mount();
// }
// if (((!AI_VALUE(GuidVector, "possible rpg targets").empty()) && noattackers && !dps && !enemy) && urand(0,
// 100) > 50)
// return Mount();
// }
// if (!bot->IsMounted() && !attackdistance && (fartarget || chasedistance))
// return Mount();
if (!bot->IsFlying() && attackdistance && bot->IsMounted() && (enemy || dps || (!noattackers && bot->IsInCombat())))
if (!bot->IsFlying() && shouldDismount && bot->IsMounted() && (enemy || dps || (!noattackers && bot->IsInCombat())))
{
WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);

View File

@@ -9,6 +9,8 @@
#include "Event.h"
#include "Formations.h"
#include "LastMovementValue.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
@@ -28,25 +30,15 @@ bool FollowAction::Execute(Event event)
WorldLocation loc = formation->GetLocation();
if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1)
return false;
MovementPriority priority = botAI->GetState() == BOT_STATE_COMBAT ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL;
moved = MoveTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), false, false, false,
true);
true, priority);
}
if (Pet* pet = bot->GetPet())
{
if (CreatureAI* creatureAI = ((Creature*)pet)->AI())
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetIsCommandFollow(true);
pet->GetCharmInfo()->IsReturning();
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
// pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
// pet->GetCharmInfo()->SetIsFollowing(true);
// pet->AttackStop();
// pet->GetCharmInfo()->IsReturning();
// pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
}
botAI->PetFollow();
}
// if (moved)
// botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);

View File

@@ -36,9 +36,9 @@ bool TogglePetSpellAutoCastAction::Execute(Event event)
continue;
bool shouldApply = true;
// imp's spell, felhunte's intelligence, ghoul's leap, cat stealth
// imp's spell, felhunte's intelligence, cat stealth
if (spellId == 4511 || spellId == 1742 || spellId == 54424 || spellId == 57564 || spellId == 57565 ||
spellId == 57566 || spellId == 57567 || spellId == 47482 || spellId == 24450)
spellId == 57566 || spellId == 57567 || spellId == 24450)
{
shouldApply = false;
}
@@ -72,7 +72,7 @@ bool PetAttackAction::Execute(Event event)
{
return false;
}
// pet->SetReactState(REACT_DEFENSIVE);
pet->SetReactState(REACT_PASSIVE);
pet->ClearUnitState(UNIT_STATE_FOLLOW);
pet->AttackStop();
pet->SetTarget(target->GetGUID());

View File

@@ -6,8 +6,13 @@
#include "GenericSpellActions.h"
#include "Event.h"
#include "ItemTemplate.h"
#include "ObjectDefines.h"
#include "Opcodes.h"
#include "Player.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "WorldPacket.h"
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
@@ -124,11 +129,35 @@ bool CastSpellAction::isUseful()
CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
{
range = ATTACK_DISTANCE;
// Unit* target = AI_VALUE(Unit*, "current target");
// if (target)
// range = bot->GetMeleeRange(target);
}
// range = target->GetCombinedCombatReach();
bool CastMeleeSpellAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (!bot->IsWithinMeleeRange(target))
return false;
return CastSpellAction::isUseful();
}
CastMeleeDebuffSpellAction::CastMeleeDebuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner, float needLifeTime) : CastDebuffSpellAction(botAI, spell, isOwner, needLifeTime)
{
range = ATTACK_DISTANCE;
}
bool CastMeleeDebuffSpellAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (!bot->IsWithinMeleeRange(target))
return false;
return CastDebuffSpellAction::isUseful();
}
bool CastAuraSpellAction::isUseful()
@@ -209,17 +238,7 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
NextAction** CastSpellAction::getPrerequisites()
{
if (spell == "mount")
return nullptr;
if (range > botAI->GetRange("spell"))
return nullptr;
else if (range > ATTACK_DISTANCE)
return NextAction::merge(NextAction::array(0, new NextAction("reach spell"), nullptr),
Action::getPrerequisites());
else
return NextAction::merge(NextAction::array(0, new NextAction("reach melee"), nullptr),
Action::getPrerequisites());
return nullptr;
}
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
@@ -271,6 +290,70 @@ bool CastVehicleSpellAction::Execute(Event event)
return botAI->CastVehicleSpell(spellId, GetTarget());
}
bool UseTrinketAction::Execute(Event event)
{
Item* trinket1 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TRINKET1);
if (trinket1 && UseTrinket(trinket1))
return true;
Item* trinket2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TRINKET2);
if (trinket2 && UseTrinket(trinket2))
return true;
return false;
}
bool UseTrinketAction::UseTrinket(Item* item)
{
if (bot->CanUseItem(item) != EQUIP_ERR_OK)
return false;
if (bot->IsNonMeleeSpellCast(true))
return false;
uint8 bagIndex = item->GetBagSlot();
uint8 slot = item->GetSlot();
uint8 spell_index = 0;
uint8 cast_count = 1;
ObjectGuid item_guid = item->GetGUID();
uint32 glyphIndex = 0;
uint8 castFlags = 0;
uint32 targetFlag = TARGET_FLAG_NONE;
uint32 spellId = 0;
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (item->GetTemplate()->Spells[i].SpellId > 0 && item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
{
spellId = item->GetTemplate()->Spells[i].SpellId;
if (!botAI->CanCastSpell(spellId, bot, false))
{
return false;
}
break;
}
}
if (!spellId)
return false;
WorldPacket packet(CMSG_USE_ITEM);
packet << bagIndex << slot << cast_count << spellId << item_guid << glyphIndex << castFlags;
Unit* target = AI_VALUE(Unit*, "current target");
if (target)
{
targetFlag = TARGET_FLAG_UNIT;
packet << targetFlag << target->GetGUID().WriteAsPacked();
}
else
{
targetFlag = TARGET_FLAG_NONE;
packet << targetFlag << bot->GetPackGUID();
}
bot->GetSession()->HandleUseItemOpcode(packet);
return true;
}
Value<Unit*>* BuffOnMainTankAction::GetTargetValue() { return context->GetValue<Unit*>("main tank", spell); }
bool CastDebuffSpellAction::isUseful()

View File

@@ -9,6 +9,7 @@
#include "Action.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "UseItemAction.h"
#include "Value.h"
class PlayerbotAI;
@@ -52,6 +53,7 @@ class CastMeleeSpellAction : public CastSpellAction
{
public:
CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell);
bool isUseful() override;
};
class CastDebuffSpellAction : public CastAuraSpellAction
@@ -67,6 +69,13 @@ private:
float needLifeTime;
};
class CastMeleeDebuffSpellAction : public CastDebuffSpellAction
{
public:
CastMeleeDebuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, float needLifeTime = 8.0f);
bool isUseful() override;
};
class CastDebuffSpellOnAttackerAction : public CastDebuffSpellAction
{
public:
@@ -245,10 +254,31 @@ public:
CastManaTapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mana tap") {}
};
class CastWarStompAction : public CastSpellAction
class CastWarStompAction : public CastMeleeSpellAction
{
public:
CastWarStompAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "war stomp") {}
CastWarStompAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "war stomp") {}
};
class CastBloodFuryAction : public CastBuffSpellAction
{
public:
CastBloodFuryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "blood fury") {}
};
class CastBerserkingAction : public CastBuffSpellAction
{
public:
CastBerserkingAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "berserking") {}
};
class UseTrinketAction : public Action
{
public:
UseTrinketAction(PlayerbotAI* botAI) : Action(botAI, "use trinket") {}
bool Execute(Event event) override;
protected:
bool UseTrinket(Item* trinket);
};
class CastSpellOnEnemyHealerAction : public CastSpellAction

View File

@@ -143,7 +143,10 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
if (!spellInfo)
continue;
if (spellInfo->IsPassive())
continue;
SkillLineAbilityEntry const* skillLine = skillSpells[itr->first];
if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill))
continue;

View File

@@ -94,12 +94,14 @@ bool MoveToRpgTargetAction::Execute(Event event)
x += cos(angle) * INTERACTION_DISTANCE * distance;
y += sin(angle) * INTERACTION_DISTANCE * distance;
bool exact = true;
if (!wo->GetMap()->CheckCollisionAndGetValidCoords(wo, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ(),
x, y, z))
{
x = wo->GetPositionX();
y = wo->GetPositionY();
z = wo->GetPositionZ();
exact = false;
}
// WaitForReach(distance);
@@ -108,7 +110,7 @@ bool MoveToRpgTargetAction::Execute(Event event)
// if (bot->IsWithinLOS(x, y, z))
// couldMove = MoveNear(mapId, x, y, z, 0);
// else
couldMove = MoveTo(mapId, x, y, z, false, false, false, true);
couldMove = MoveTo(mapId, x, y, z, false, false, false, exact);
if (!couldMove && WorldPosition(mapId, x, y, z).distance(bot) > INTERACTION_DISTANCE)
{

View File

@@ -61,23 +61,37 @@ void MovementAction::CreateWp(Player* wpOwner, float x, float y, float z, float
wpCreature->SetObjectScale(0.5f);
}
void MovementAction::JumpTo(uint32 mapId, float x, float y, float z)
bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPriority priority)
{
UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z))
{
return false;
}
if (IsDuplicateMove(mapId, x, y, z))
{
return false;
}
if (IsWaitingForLastMove(priority))
{
return false;
}
float botZ = bot->GetPositionZ();
float speed = bot->GetSpeed(MOVE_RUN);
MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear();
mm.MoveJump(x, y, z, speed, speed, 1);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
return true;
}
bool MovementAction::MoveNear(uint32 mapId, float x, float y, float z, float distance)
bool MovementAction::MoveNear(uint32 mapId, float x, float y, float z, float distance, MovementPriority priority)
{
float angle = GetFollowAngle();
return MoveTo(mapId, x + cos(angle) * distance, y + sin(angle) * distance, z);
return MoveTo(mapId, x + cos(angle) * distance, y + sin(angle) * distance, z, false, false, false, false, priority);
}
bool MovementAction::MoveNear(WorldObject* target, float distance)
bool MovementAction::MoveNear(WorldObject* target, float distance, MovementPriority priority)
{
if (!target)
return false;
@@ -99,7 +113,7 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
if (!bot->IsWithinLOS(x, y, z))
continue;
bool moved = MoveTo(target->GetMapId(), x, y, z);
bool moved = MoveTo(target->GetMapId(), x, y, z, false, false, false, false, priority);
if (moved)
return true;
}
@@ -161,7 +175,7 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged)
}
bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool react, bool normal_only,
bool exact_waypoint)
bool exact_waypoint, MovementPriority priority)
{
UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z))
@@ -172,21 +186,10 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
{
return false;
}
if (IsWaitingForLastMove())
if (IsWaitingForLastMove(priority))
{
return false;
}
// if (bot->Unit::IsFalling()) {
// bot->Say("I'm falling!, flag:" + std::to_string(bot->m_movementInfo.GetMovementFlags()), LANG_UNIVERSAL);
// return false;
// }
// if (bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_SWIMMING)) {
// bot->Say("I'm swimming", LANG_UNIVERSAL);
// }
// if (bot->Unit::IsFalling()) {
// bot->Say("I'm falling", LANG_UNIVERSAL);
// }
bool generatePath = !bot->IsFlying() && !bot->isSwimming();
bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 ||
(sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground());
@@ -202,11 +205,11 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
{
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
mm.Clear();
mm.MovePoint(mapId, x, y, z, generatePath);
float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN)) - sPlayerbotAIConfig->reactDelay;
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN));
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
return true;
}
}
@@ -225,11 +228,11 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
}
MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear();
mm.MovePoint(mapId, x, y, z, generatePath);
float delay = 1000.0f * MoveDelay(distance) - sPlayerbotAIConfig->reactDelay;
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
return true;
}
}
@@ -254,14 +257,13 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
botAI->InterruptSpell();
}
MotionMaster& mm = *bot->GetMotionMaster();
G3D::Vector3 endP = path.back();
mm.Clear();
mm.MoveSplinePath(&path);
// mm.MoveSplinePath(&path);
float delay = 1000.0f * MoveDelay(distance) - sPlayerbotAIConfig->reactDelay;
mm.MovePoint(0, endP.x, endP.y, endP.z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
return true;
}
}
@@ -759,7 +761,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// return true;
}
bool MovementAction::MoveTo(Unit* target, float distance)
bool MovementAction::MoveTo(Unit* target, float distance, MovementPriority priority)
{
if (!IsMovingAllowed(target))
return false;
@@ -793,7 +795,7 @@ bool MovementAction::MoveTo(Unit* target, float distance)
{
dz = tz;
}
return MoveTo(target->GetMapId(), dx, dy, dz);
return MoveTo(target->GetMapId(), dx, dy, dz, false, false, false, false, priority);
}
bool MovementAction::ReachCombatTo(Unit* target, float distance)
@@ -813,6 +815,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD)) // target is moving forward, predict the position
{
float needToGo = bot->GetExactDist(target) - distance;
float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f;
float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN);
@@ -845,7 +848,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
return false;
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance);
G3D::Vector3 endPos = path.GetPath().back();
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z);
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
float MovementAction::GetFollowAngle()
@@ -909,10 +912,13 @@ bool MovementAction::IsDuplicateMove(uint32 mapId, float x, float y, float z)
return true;
}
bool MovementAction::IsWaitingForLastMove()
bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
{
LastMovement& lastMove = *context->GetValue<LastMovement&>("last movement");
if (priority > lastMove.priority)
return false;
// heuristic 5s
if (lastMove.lastdelayTime + lastMove.msTime > getMSTime())
return true;
@@ -1216,7 +1222,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
botAI->InterruptSpell();
}
AI_VALUE(LastMovement&, "last movement").Set(target);
// AI_VALUE(LastMovement&, "last movement").Set(target);
ClearIdleState();
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE)
@@ -1506,7 +1512,17 @@ bool MovementAction::MoveAway(Unit* target)
float dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
float dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
float dz = bot->GetPositionZ();
if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true))
bool exact = true;
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
// disable prediction if position is invalid
dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
dz = bot->GetPositionZ();
exact = false;
}
if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
@@ -1514,11 +1530,21 @@ bool MovementAction::MoveAway(Unit* target)
{
continue;
}
exact = true;
angle = init_angle - delta;
dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
dz = bot->GetPositionZ();
if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true))
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
// disable prediction if position is invalid
dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
dz = bot->GetPositionZ();
exact = false;
}
if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
@@ -1526,13 +1552,13 @@ bool MovementAction::MoveAway(Unit* target)
return false;
}
bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float distance)
bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float distance, MovementPriority priority)
{
if (bot->GetDistance2d(x, y) <= distance)
{
return false;
}
return MoveNear(mapId, x, y, z, distance);
return MoveNear(mapId, x, y, z, distance, priority);
}
// float MovementAction::SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range, bool
@@ -1678,13 +1704,7 @@ bool FleeWithPetAction::Execute(Event event)
{
if (Pet* pet = bot->GetPet())
{
if (CreatureAI* creatureAI = ((Creature*)pet)->AI())
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetIsCommandFollow(true);
pet->GetCharmInfo()->IsReturning();
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
}
botAI->PetFollow();
}
return Flee(AI_VALUE(Unit*, "current target"));
@@ -1929,7 +1949,7 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
for (CheckAngle& checkAngle : possibleAngles)
{
float angle = checkAngle.angle;
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
std::list<FleeInfo>& infoList = AI_VALUE(std::list<FleeInfo>&, "recently flee info");
if (!CheckLastFlee(angle, infoList))
{
continue;
@@ -1985,7 +2005,7 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
for (CheckAngle& checkAngle : possibleAngles)
{
float angle = checkAngle.angle;
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
std::list<FleeInfo>& infoList = AI_VALUE(std::list<FleeInfo>&, "recently flee info");
if (!CheckLastFlee(angle, infoList))
{
continue;
@@ -2032,9 +2052,9 @@ bool MovementAction::FleePosition(Position pos, float radius)
if (bestPos != Position())
{
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false,
false, true))
false, true, false, MovementPriority::MOVEMENT_COMBAT))
{
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
std::list<FleeInfo>& infoList = AI_VALUE(std::list<FleeInfo>&, "recently flee info");
uint32 curTS = getMSTime();
while (!infoList.empty())
{
@@ -2346,10 +2366,10 @@ bool MoveOutOfCollisionAction::isUseful()
bool MoveRandomAction::Execute(Event event)
{
float distance = sPlayerbotAIConfig->tooCloseDistance + sPlayerbotAIConfig->grindDistance * urand(3, 10) / 10.0f;
float distance = sPlayerbotAIConfig->tooCloseDistance + urand(10, 30);
Map* map = bot->GetMap();
for (int i = 0; i < 10; ++i)
for (int i = 0; i < 3; ++i)
{
float x = bot->GetPositionX();
float y = bot->GetPositionY();
@@ -2362,11 +2382,7 @@ bool MoveRandomAction::Execute(Event event)
float oz = z;
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), x, y, z))
{
x = ox;
y = oy;
z = oz;
}
continue;
if (map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight()))
continue;
@@ -2385,7 +2401,7 @@ bool MoveInsideAction::Execute(Event event) { return MoveInside(bot->GetMapId(),
bool RotateAroundTheCenterPointAction::Execute(Event event)
{
uint32 next_point = GetCurrWaypoint();
if (MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ()))
if (MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
call_counters += 1;
return true;

View File

@@ -9,6 +9,7 @@
#include <cmath>
#include "Action.h"
#include "LastMovementValue.h"
#include "PlayerbotAIConfig.h"
class Player;
@@ -17,19 +18,20 @@ class Unit;
class WorldObject;
class Position;
class MovementAction : public Action
{
public:
MovementAction(PlayerbotAI* botAI, std::string const name);
protected:
void JumpTo(uint32 mapId, float x, float y, float z);
bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance);
bool JumpTo(uint32 mapId, float x, float y, float z, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveToLOS(WorldObject* target, bool ranged = false);
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
bool normal_only = false, bool exact_waypoint = false);
bool MoveTo(Unit* target, float distance = 0.0f);
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance);
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveTo(Unit* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
float GetFollowAngle();
bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance);
bool Follow(Unit* target, float distance, float angle);
@@ -40,13 +42,13 @@ protected:
bool IsMovingAllowed(Unit* target);
bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
bool IsDuplicateMove(uint32 mapId, float x, float y, float z);
bool IsWaitingForLastMove();
bool IsWaitingForLastMove(MovementPriority priority);
bool IsMovingAllowed();
bool Flee(Unit* target);
void ClearIdleState();
void UpdateMovementState();
bool MoveAway(Unit* target);
bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance);
bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false);
Position BestPositionForMeleeToFlee(Position pos, float radius);
Position BestPositionForRangedToFlee(Position pos, float radius);
@@ -94,7 +96,7 @@ class AvoidAoeAction : public MovementAction
{
public:
AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000)
: MovementAction(botAI, "avoid aoe"), moveInterval(moveInterval)
: MovementAction(botAI, "aaoe"), moveInterval(moveInterval)
{
}

View File

@@ -79,15 +79,13 @@ bool SummonAction::Execute(Event event)
if (Pet* pet = bot->GetPet())
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetIsCommandFollow(true);
pet->GetCharmInfo()->IsReturning();
botAI->PetFollow();
}
if (master->GetSession()->GetSecurity() >= SEC_PLAYER)
{
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
SET_AI_VALUE(std::list<FleeInfo>, "recently flee info", {});
AI_VALUE(std::list<FleeInfo>&, "recently flee info").clear();
return Teleport(master, bot);
}
@@ -215,10 +213,12 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
bool revive =
sPlayerbotAIConfig->reviveBotWhenSummoned == 2 ||
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive());
if (bot->isDead() && revive)
{
bot->ResurrectPlayer(1.0f, false);
botAI->TellMasterNoFacing("I live, again!");
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
}
player->GetMotionMaster()->Clear();

View File

@@ -110,11 +110,11 @@ public:
CastIcyTouchAction(PlayerbotAI* ai) : CastSpellAction(ai, "icy touch") {}
};
class CastIcyTouchOnAttackerAction : public CastDebuffSpellOnMeleeAttackerAction
class CastIcyTouchOnAttackerAction : public CastDebuffSpellOnAttackerAction
{
public:
CastIcyTouchOnAttackerAction(PlayerbotAI* botAI)
: CastDebuffSpellOnMeleeAttackerAction(botAI, "icy touch", true, .0f)
: CastDebuffSpellOnAttackerAction(botAI, "icy touch", true, .0f)
{
}
};
@@ -232,16 +232,17 @@ public:
CastDeathCoilAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death coil") {}
};
class CastBloodBoilAction : public CastSpellAction
class CastBloodBoilAction : public CastMeleeSpellAction
{
public:
CastBloodBoilAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blood boil") {}
CastBloodBoilAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "blood boil") {}
};
class CastDeathAndDecayAction : public CastSpellAction
{
public:
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastHornOfWinterAction : public CastSpellAction

View File

@@ -234,10 +234,10 @@ public:
NextAction** getAlternatives() override;
};
class CastBarskinAction : public CastBuffSpellAction
class CastBarkskinAction : public CastBuffSpellAction
{
public:
CastBarskinAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "barskin") {}
CastBarkskinAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "barkskin") {}
};
class CastInnervateAction : public CastSpellAction

View File

@@ -198,7 +198,7 @@ public:
creators["healing touch on party"] = &DruidAiObjectContextInternal::healing_touch_on_party;
creators["rebirth"] = &DruidAiObjectContextInternal::rebirth;
creators["revive"] = &DruidAiObjectContextInternal::revive;
creators["barskin"] = &DruidAiObjectContextInternal::barskin;
creators["barkskin"] = &DruidAiObjectContextInternal::barkskin;
creators["lacerate"] = &DruidAiObjectContextInternal::lacerate;
creators["hurricane"] = &DruidAiObjectContextInternal::hurricane;
creators["innervate"] = &DruidAiObjectContextInternal::innervate;
@@ -281,7 +281,7 @@ private:
static Action* healing_touch_on_party(PlayerbotAI* botAI) { return new CastHealingTouchOnPartyAction(botAI); }
static Action* rebirth(PlayerbotAI* botAI) { return new CastRebirthAction(botAI); }
static Action* revive(PlayerbotAI* botAI) { return new CastReviveAction(botAI); }
static Action* barskin(PlayerbotAI* botAI) { return new CastBarskinAction(botAI); }
static Action* barkskin(PlayerbotAI* botAI) { return new CastBarkskinAction(botAI); }
static Action* lacerate(PlayerbotAI* botAI) { return new CastLacerateAction(botAI); }
static Action* hurricane(PlayerbotAI* botAI) { return new CastHurricaneAction(botAI); }
static Action* innervate(PlayerbotAI* botAI) { return new CastInnervateAction(botAI); }

View File

@@ -43,10 +43,10 @@ public:
CastSwipeAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "swipe") {}
};
class CastDemoralizingRoarAction : public CastDebuffSpellAction
class CastDemoralizingRoarAction : public CastMeleeDebuffSpellAction
{
public:
CastDemoralizingRoarAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "demoralizing roar") {}
CastDemoralizingRoarAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "demoralizing roar") {}
};
class CastMangleBearAction : public CastMeleeSpellAction

View File

@@ -27,7 +27,7 @@ private:
{
return new ActionNode("survival instincts",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("barskin"), nullptr),
/*A*/ NextAction::array(0, new NextAction("barkskin"), nullptr),
/*C*/ nullptr);
}

View File

@@ -106,6 +106,9 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("barkskin", ACTION_HIGH + 7), nullptr)));
// triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("regrowth",
// ACTION_MEDIUM_HEAL + 2), nullptr))); triggers.push_back(new TriggerNode("party member low health",
// NextAction::array(0, new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1), nullptr)));

View File

@@ -70,7 +70,7 @@ AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
NextAction** AvoidAoeStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("avoid aoe", ACTION_EMERGENCY), nullptr);
return NextAction::array(0, new NextAction("aaoe", ACTION_EMERGENCY), nullptr);
}
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -23,7 +23,7 @@ class AvoidAoeStrategy : public Strategy
{
public:
explicit AvoidAoeStrategy(PlayerbotAI* ai);
const std::string getName() override { return "avoid aoe"; }
const std::string getName() override { return "aaoe"; }
NextAction** getDefaultActions() override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
@@ -33,7 +33,7 @@ class CombatFormationStrategy : public Strategy
{
public:
CombatFormationStrategy(PlayerbotAI* ai) : Strategy(ai) {}
const std::string getName() override { return "combat formation"; }
const std::string getName() override { return "formation"; }
NextAction** getDefaultActions() override;
};

View File

@@ -49,7 +49,7 @@ class PlayerbotAI;
class HealerAutoSaveManaMultiplier : public Multiplier
{
public:
HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "auto save mana") {}
HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "smana") {}
float GetValue(Action* action) override;
};
@@ -60,7 +60,7 @@ public:
HealerAutoSaveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
std::string const getName() override { return "auto save mana"; }
std::string const getName() override { return "smana"; }
};
#endif

View File

@@ -25,14 +25,18 @@ private:
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("lifeblood", 71.0f), nullptr)));
// triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("war stomp", 71.0f),
// nullptr)));
/*triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("war stomp", 71.0f),
* nullptr)));*/
/*triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("arcane torrent",
ACTION_EMERGENCY + 6), nullptr))); triggers.push_back(new TriggerNode("medium mana", NextAction::array(0, new
NextAction("mana tap", ACTION_EMERGENCY + 6), nullptr)));*/
new TriggerNode("low health", NextAction::array(0, new NextAction("lifeblood", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("war stomp", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(new TriggerNode(
"low mana", NextAction::array(0, new NextAction("arcane torrent", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(new TriggerNode(
"generic boost", NextAction::array(0, new NextAction("blood fury", ACTION_NORMAL + 5),
new NextAction("berserking", ACTION_NORMAL + 5),
new NextAction("use trinket", ACTION_NORMAL + 4),
nullptr)));
}
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)

View File

@@ -49,14 +49,10 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
//triggers.push_back(new TriggerNode("no non bot players around", NextAction::array(0, new NextAction("delay", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr)));
triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr)));
triggers.push_back(new TriggerNode("levelup", NextAction::array(0,
new NextAction("auto teleport for level", relevance + 3),
new NextAction("auto talents", relevance + 2),
new NextAction("auto learn spell", relevance + 1),
new NextAction("auto upgrade equip", relevance),
nullptr)));
// triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
triggers.push_back(
new TriggerNode("levelup", NextAction::array(0, new NextAction("auto maintenance on levelup", relevance + 3), nullptr)));
// triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI",
// relevance), nullptr)));
triggers.push_back(new TriggerNode("group list", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));

View File

@@ -10,7 +10,11 @@
class DpsHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
DpsHunterStrategyActionNodeFactory() { creators["aimed shot"] = &aimed_shot; }
DpsHunterStrategyActionNodeFactory()
{
creators["aimed shot"] = &aimed_shot;
creators["steady shot"] = &steady_shot;
}
private:
static ActionNode* aimed_shot([[maybe_unused]] PlayerbotAI* botAI)
@@ -20,6 +24,13 @@ private:
/*A*/ NextAction::array(0, new NextAction("multi-shot"), nullptr),
/*C*/ nullptr);
}
static ActionNode* steady_shot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("steady shot",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("arcane shot"), nullptr),
/*C*/ nullptr);
}
};
DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
@@ -32,7 +43,7 @@ NextAction** DpsHunterStrategy::getDefaultActions()
return NextAction::array(
0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f),
new NextAction("explosive shot", ACTION_DEFAULT + 0.4f), new NextAction("aimed shot", ACTION_DEFAULT + 0.3f),
new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
/*new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),*/ new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
new NextAction("auto shot", ACTION_DEFAULT), nullptr);
// return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new
// NextAction("auto attack", 9.0f), nullptr);

View File

@@ -97,6 +97,9 @@ void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("misdirection on main tank",
NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode("tranquilizing shot enrage",
NextAction::array(0, new NextAction("tranquilizing shot", 61.0f), NULL)));
triggers.push_back(new TriggerNode("tranquilizing shot magic",

View File

@@ -32,8 +32,11 @@ public:
BEGIN_RANGED_SPELL_ACTION(CastArcaneShotAction, "arcane shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastExplosiveShotAction, "explosive shot")
END_SPELL_ACTION()
class CastExplosiveShotAction : public CastDebuffSpellAction
{
public:
CastExplosiveShotAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
};
BEGIN_RANGED_SPELL_ACTION(CastAimedShotAction, "aimed shot")
END_SPELL_ACTION()
@@ -160,6 +163,12 @@ public:
CastRapidFireAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "rapid fire") {}
};
class CastDeterrenceAction : public CastBuffSpellAction
{
public:
CastDeterrenceAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "deterrence") {}
};
class CastReadinessAction : public CastBuffSpellAction
{
public:

View File

@@ -149,6 +149,7 @@ public:
creators["freezing trap"] = &HunterAiObjectContextInternal::freezing_trap;
creators["rapid fire"] = &HunterAiObjectContextInternal::rapid_fire;
creators["boost"] = &HunterAiObjectContextInternal::rapid_fire;
creators["deterrence"] = &HunterAiObjectContextInternal::deterrence;
creators["readiness"] = &HunterAiObjectContextInternal::readiness;
creators["aspect of the hawk"] = &HunterAiObjectContextInternal::aspect_of_the_hawk;
creators["aspect of the monkey"] = &HunterAiObjectContextInternal::aspect_of_the_monkey;
@@ -200,6 +201,7 @@ private:
static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); }
static Action* freezing_trap(PlayerbotAI* botAI) { return new CastFreezingTrap(botAI); }
static Action* rapid_fire(PlayerbotAI* botAI) { return new CastRapidFireAction(botAI); }
static Action* deterrence(PlayerbotAI* botAI) { return new CastDeterrenceAction(botAI); }
static Action* readiness(PlayerbotAI* botAI) { return new CastReadinessAction(botAI); }
static Action* aspect_of_the_hawk(PlayerbotAI* botAI) { return new CastAspectOfTheHawkAction(botAI); }
static Action* aspect_of_the_monkey(PlayerbotAI* botAI) { return new CastAspectOfTheMonkeyAction(botAI); }

View File

@@ -85,7 +85,7 @@ NextAction** DpsPaladinStrategy::getDefaultActions()
return NextAction::array(0, new NextAction("crusader strike", ACTION_DEFAULT + 0.4f),
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f),
new NextAction("divine storm", ACTION_DEFAULT + 0.2f),
new NextAction("melee consecration", ACTION_DEFAULT + 0.1f),
new NextAction("consecration", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), NULL);
}
@@ -112,7 +112,7 @@ void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance",
// ACTION_INTERRUPT + 2), nullptr)));
triggers.push_back(new TriggerNode(
"medium aoe", NextAction::array(0, new NextAction("melee consecration", ACTION_HIGH + 3), nullptr)));
"medium aoe", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 3), nullptr)));
triggers.push_back(
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode("target critical health",

View File

@@ -17,8 +17,8 @@ void GenericPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("divine shield",
// ACTION_CRITICAL_HEAL + 2), new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr)));
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("divine shield",
ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode("hammer of justice interrupt",
NextAction::array(0, new NextAction("hammer of justice", ACTION_INTERRUPT), nullptr)));

View File

@@ -153,13 +153,6 @@ Unit* CastRighteousDefenseAction::GetTarget()
return current_target->GetVictim();
}
bool CastMeleeConsecrationAction::isUseful()
{
Unit* target = GetTarget();
// float dis = distance + CONTACT_DISTANCE;
return target && bot->IsWithinMeleeRange(target); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float,
// "distance", GetTargetName()), distance);
}
bool CastDivineSacrificeAction::isUseful()
{

View File

@@ -44,13 +44,6 @@ SPELL_ACTION(CastHolyShockAction, "holy shock");
// consecration
MELEE_ACTION(CastConsecrationAction, "consecration");
class CastMeleeConsecrationAction : public CastSpellAction
{
public:
CastMeleeConsecrationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "consecration") {}
bool isUseful() override;
};
// repentance
SNARE_ACTION(CastRepentanceSnareAction, "repentance");
DEBUFF_ACTION(CastRepentanceAction, "repentance");
@@ -67,10 +60,10 @@ BUFF_ACTION(CastDivineFavorAction, "divine favor");
// fury
BUFF_ACTION(CastRighteousFuryAction, "righteous fury");
class CastDivineStormAction : public CastBuffSpellAction
class CastDivineStormAction : public CastMeleeSpellAction
{
public:
CastDivineStormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "divine storm") {}
CastDivineStormAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "divine storm") {}
};
class CastCrusaderStrikeAction : public CastMeleeSpellAction

View File

@@ -232,7 +232,6 @@ public:
creators["concentration aura"] = &PaladinAiObjectContextInternal::concentration_aura;
creators["holy wrath"] = &PaladinAiObjectContextInternal::holy_wrath;
creators["consecration"] = &PaladinAiObjectContextInternal::consecration;
creators["melee consecration"] = &PaladinAiObjectContextInternal::melee_consecration;
creators["cleanse disease"] = &PaladinAiObjectContextInternal::cleanse_disease;
creators["cleanse poison"] = &PaladinAiObjectContextInternal::cleanse_poison;
creators["cleanse magic"] = &PaladinAiObjectContextInternal::cleanse_magic;
@@ -331,7 +330,6 @@ private:
static Action* concentration_aura(PlayerbotAI* botAI) { return new CastConcentrationAuraAction(botAI); }
static Action* holy_wrath(PlayerbotAI* botAI) { return new CastHolyWrathAction(botAI); }
static Action* consecration(PlayerbotAI* botAI) { return new CastConsecrationAction(botAI); }
static Action* melee_consecration(PlayerbotAI* botAI) { return new CastMeleeConsecrationAction(botAI); }
static Action* cleanse_poison(PlayerbotAI* botAI) { return new CastCleansePoisonAction(botAI); }
static Action* cleanse_disease(PlayerbotAI* botAI) { return new CastCleanseDiseaseAction(botAI); }
static Action* cleanse_magic(PlayerbotAI* botAI) { return new CastCleanseMagicAction(botAI); }

View File

@@ -82,7 +82,7 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"light aoe", NextAction::array(0, new NextAction("avenger's shield", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("melee consecration", ACTION_HIGH + 7),
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 7),
new NextAction("avenger's shield", ACTION_HIGH + 6), nullptr)));
// triggers.push_back(new TriggerNode("avenger's shield", NextAction::array(0, new NextAction("avenger's shield",
// ACTION_HIGH + 7), nullptr)));
@@ -107,7 +107,7 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode("medium group heal occasion",
NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode(
"enough mana", NextAction::array(0, new NextAction("melee consecration", ACTION_HIGH + 4), nullptr)));
"enough mana", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -31,8 +31,9 @@ void GenericPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// NextAction("inner focus", 42.0f), nullptr))); triggers.push_back(new TriggerNode("medium mana",
// NextAction::array(0, new NextAction("symbol of hope", ACTION_EMERGENCY), nullptr))); triggers.push_back(new
// TriggerNode("low mana", NextAction::array(0, new NextAction("consume magic", 10.0f), nullptr)));
// triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("desperate prayer",
// ACTION_EMERGENCY), nullptr))); triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("desperate prayer",
ACTION_HIGH + 5), nullptr)));
// triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new
// NextAction("elune's grace", ACTION_EMERGENCY), nullptr))); triggers.push_back(new TriggerNode("chastise",
// NextAction::array(0, new NextAction("chastise", ACTION_INTERRUPT), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -73,7 +73,9 @@ void HolyHealPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"party member critical health",
NextAction::array(0, new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 5),
NextAction::array(0,
new NextAction("guardian spirit on party", ACTION_CRITICAL_HEAL + 6),
new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 5),
new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 3),
new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), NULL)));

View File

@@ -173,4 +173,11 @@ public:
CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastGuardianSpiritOnPartyAction : public HealPartyMemberAction
{
public:
CastGuardianSpiritOnPartyAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM) {}
};
#endif

View File

@@ -234,6 +234,7 @@ public:
creators["hymn of hope"] = &PriestAiObjectContextInternal::hymn_of_hope;
creators["divine hymn"] = &PriestAiObjectContextInternal::divine_hymn;
creators["mind sear"] = &PriestAiObjectContextInternal::mind_sear;
creators["guardian spirit on party"] = &PriestAiObjectContextInternal::guardian_spirit_on_party;
}
private:
@@ -339,6 +340,7 @@ private:
static Action* hymn_of_hope(PlayerbotAI* ai) { return new CastHymnOfHopeAction(ai); }
static Action* divine_hymn(PlayerbotAI* ai) { return new CastDivineHymnAction(ai); }
static Action* mind_sear(PlayerbotAI* ai) { return new CastMindSearAction(ai); }
static Action* guardian_spirit_on_party(PlayerbotAI* ai) { return new CastGuardianSpiritOnPartyAction(ai); }
};
PriestAiObjectContext::PriestAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)

View File

@@ -23,7 +23,7 @@ CURE_TRIGGER(CureDiseaseTrigger, "cure disease", DISPEL_DISEASE);
CURE_PARTY_TRIGGER(PartyMemberCureDiseaseTrigger, "cure disease", DISPEL_DISEASE);
BUFF_TRIGGER_A(InnerFireTrigger, "inner fire");
BUFF_TRIGGER_A(ShadowformTrigger, "shadowform");
BUFF_TRIGGER(PowerInfusionTrigger, "power infusion");
BOOST_TRIGGER(PowerInfusionTrigger, "power infusion");
BUFF_TRIGGER(InnerFocusTrigger, "inner focus");
BUFF_TRIGGER(ShadowProtectionTrigger, "shadow protection");
BUFF_PARTY_TRIGGER(ShadowProtectionOnPartyTrigger, "shadow protection");

View File

@@ -29,8 +29,10 @@ void ShadowPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// ACTION_MOVE + 9), nullptr)));
triggers.push_back(
new TriggerNode("shadowform", NextAction::array(0, new NextAction("shadowform", ACTION_HIGH), nullptr)));
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("dispersion", ACTION_EMERGENCY
// + 5), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("dispersion", ACTION_HIGH
+ 5), nullptr)));
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("dispersion", ACTION_HIGH
+ 5), nullptr)));
triggers.push_back(
new TriggerNode("vampiric embrace", NextAction::array(0, new NextAction("vampiric embrace", 16.0f), nullptr)));
triggers.push_back(

View File

@@ -1,6 +1,7 @@
#include "RaidNaxxActions.h"
#include "LastMovementValue.h"
#include "ObjectGuid.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
@@ -23,7 +24,7 @@ bool GrobbulusGoBehindAction::Execute(Event event)
float z = boss->GetPositionZ();
float rx = x + cos(orientation) * distance;
float ry = y + sin(orientation) * distance;
return MoveTo(bot->GetMapId(), rx, ry, z);
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint()
@@ -91,7 +92,7 @@ bool HeiganDanceMeleeAction::Execute(Event event)
}
assert(curr_safe >= 0 && curr_safe <= 3);
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(),
botAI->IsMainTank(bot) ? 0 : 0);
botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT);
}
bool HeiganDanceRangedAction::Execute(Event event)
@@ -99,10 +100,10 @@ bool HeiganDanceRangedAction::Execute(Event event)
CalculateSafe();
if (prev_phase != 1)
{
return MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f);
return MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
botAI->InterruptSpell();
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0);
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0, MovementPriority::MOVEMENT_COMBAT);
}
bool ThaddiusAttackNearestPetAction::isUseful()
@@ -128,7 +129,7 @@ bool ThaddiusAttackNearestPetAction::Execute(Event event)
Unit* target = helper.GetNearestPet();
if (!bot->IsWithinLOSInMap(target))
{
return MoveTo(target);
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
}
if (AI_VALUE(Unit*, "current target") != target)
{
@@ -137,12 +138,12 @@ bool ThaddiusAttackNearestPetAction::Execute(Event event)
if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target"))
{
std::pair<float, float> posForTank = helper.PetPhaseGetPosForTank();
return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ);
return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
if (botAI->IsRanged(bot))
{
std::pair<float, float> posForRanged = helper.PetPhaseGetPosForRanged();
return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ);
return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -170,22 +171,28 @@ bool ThaddiusMoveToPlatformAction::Execute(Event event)
{
if (is_left)
{
if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z))
if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation());
float distance = bot->GetExactDist2d(position[0].first, position[0].second);
if (distance < sPlayerbotAIConfig->contactDistance)
JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation());
}
}
else
{
if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z))
if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation());
float distance = bot->GetExactDist2d(position[1].first, position[1].second);
if (distance < sPlayerbotAIConfig->contactDistance)
JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation());
}
}
}
else
{
return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z);
return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return true;
}
@@ -225,7 +232,7 @@ bool ThaddiusMovePolarityAction::Execute(Event event)
idx = 2;
}
idx = idx * 2 + botAI->IsRanged(bot);
return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ());
return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
bool RazuviousUseObedienceCrystalAction::Execute(Event event)
@@ -322,7 +329,7 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event)
{
continue;
}
if (MoveTo(unit))
if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
@@ -352,7 +359,7 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event)
{
if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance)
{
return MoveNear(target, sPlayerbotAIConfig->spellDistance);
return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT);
}
else
{
@@ -396,7 +403,7 @@ bool HorsemanAttractAlternativelyAction::Execute(Event event)
}
helper.CalculatePosToGo(bot);
auto [posX, posY] = helper.CurrentAttractPos();
if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ))
if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
@@ -444,7 +451,7 @@ bool HorsemanAttactInOrderAction::Execute(Event event)
}
if (!bot->IsWithinLOSInMap(target))
{
return MoveNear(target, 22.0f);
return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT);
}
return Attack(target);
}
@@ -461,7 +468,7 @@ bool SapphironGroundPositionAction::Execute(Event event)
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT);
return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -485,14 +492,14 @@ bool SapphironGroundPositionAction::Execute(Event event)
distance = 5.0f;
}
return MoveTo(NAXX_MAP_ID, helper.center.first + cos(angle) * distance,
helper.center.second + sin(angle) * distance, helper.GENERIC_HEIGHT);
helper.center.second + sin(angle) * distance, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
{
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2]);
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -513,7 +520,7 @@ bool SapphironFlightPositionAction::Execute(Event event)
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
{
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2]);
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -548,7 +555,7 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
{
float angle = boss->GetAngle(playerWithIcebolt);
return MoveTo(NAXX_MAP_ID, playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f,
playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f, helper.GENERIC_HEIGHT);
playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -679,7 +686,7 @@ bool KelthuzadPositionAction::Execute(Event event)
{
if (AI_VALUE(Unit*, "current target") == nullptr)
{
return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f);
return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
}
}
else if (helper.IsPhaseTwo())
@@ -692,7 +699,7 @@ bool KelthuzadPositionAction::Execute(Event event)
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ());
return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
@@ -715,7 +722,7 @@ bool KelthuzadPositionAction::Execute(Event event)
float dx, dy;
dx = helper.center.first + cos(angle) * distance;
dy = helper.center.second + sin(angle) * distance;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ());
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsTank(bot))
{
@@ -725,7 +732,7 @@ bool KelthuzadPositionAction::Execute(Event event)
botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer()))
{
return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
@@ -747,7 +754,7 @@ bool KelthuzadPositionAction::Execute(Event event)
}
dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f;
dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ());
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -846,11 +853,11 @@ bool AnubrekhanPositionAction::Execute(Event event)
next_point = nearest;
}
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f);
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -964,12 +971,12 @@ bool GluthPositionAction::Execute(Event event)
if (raid25)
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
@@ -978,7 +985,7 @@ bool GluthPositionAction::Execute(Event event)
if (helper.BeforeDecimate())
{
return MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
@@ -987,7 +994,7 @@ bool GluthPositionAction::Execute(Event event)
uint32 nearest = FindNearestWaypoint();
uint32 next_point = (nearest + 1) % intervals;
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
bot->GetPositionZ());
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
@@ -998,19 +1005,19 @@ bool GluthPositionAction::Execute(Event event)
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0)
{
return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second,
bot->GetPositionZ(), 0.0f);
bot->GetPositionZ(), 0.0f, MovementPriority::MOVEMENT_COMBAT);
}
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
{
return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second,
bot->GetPositionZ(), 0.0f);
bot->GetPositionZ(), 0.0f, MovementPriority::MOVEMENT_COMBAT);
}
}
return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f);
return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsHeal(bot))
{
return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f);
return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -1051,12 +1058,12 @@ bool LoathebPositionAction::Execute(Event event)
{
if (AI_VALUE2(bool, "has aggro", "boss target"))
{
return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ());
return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
else if (botAI->IsRanged(bot))
{
return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f);
return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}

View File

@@ -66,8 +66,11 @@ void AssassinationRogueStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
new TriggerNode("medium threat", NextAction::array(0, new NextAction("vanish", ACTION_HIGH), NULL)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY),
new NextAction("feint", ACTION_EMERGENCY), NULL)));
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_HIGH + 9),
new NextAction("feint", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode("critical health", NextAction::array(0, new NextAction("cloak of shadows", ACTION_HIGH + 7), nullptr)));
triggers.push_back(
new TriggerNode("kick", NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), NULL)));

View File

@@ -108,8 +108,11 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode("medium threat", NextAction::array(0, new NextAction("vanish", ACTION_HIGH), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY),
new NextAction("feint", ACTION_EMERGENCY), nullptr)));
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_HIGH + 9),
new NextAction("feint", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode("critical health", NextAction::array(0, new NextAction("cloak of shadows", ACTION_HIGH + 7), nullptr)));
triggers.push_back(
new TriggerNode("kick", NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), nullptr)));
@@ -125,6 +128,9 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("light aoe", NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH + 3), nullptr)));
triggers.push_back(
new TriggerNode("blade flurry", NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8),

View File

@@ -17,6 +17,12 @@ public:
CastEvasionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "evasion") {}
};
class CastCloakOfShadowsAction : public CastBuffSpellAction
{
public:
CastCloakOfShadowsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cloak of shadows") {}
};
class CastHungerForBloodAction : public CastBuffSpellAction
{
public:

View File

@@ -76,12 +76,14 @@ public:
creators["off hand weapon no enchant"] = &RogueTriggerFactoryInternal::off_hand_weapon_no_enchant;
creators["tricks of the trade on main tank"] = &RogueTriggerFactoryInternal::tricks_of_the_trade_on_main_tank;
creators["adrenaline rush"] = &RogueTriggerFactoryInternal::adrenaline_rush;
creators["blade fury"] = &RogueTriggerFactoryInternal::blade_fury;
creators["target with combo points almost dead"] =
&RogueTriggerFactoryInternal::target_with_combo_points_almost_dead;
}
private:
static Trigger* adrenaline_rush(PlayerbotAI* botAI) { return new AdrenalineRushTrigger(botAI); }
static Trigger* blade_fury(PlayerbotAI* botAI) { return new BladeFuryTrigger(botAI); }
static Trigger* kick(PlayerbotAI* botAI) { return new KickInterruptSpellTrigger(botAI); }
static Trigger* rupture(PlayerbotAI* botAI) { return new RuptureTrigger(botAI); }
static Trigger* slice_and_dice(PlayerbotAI* botAI) { return new SliceAndDiceTrigger(botAI); }
@@ -122,6 +124,7 @@ public:
creators["eviscerate"] = &RogueAiObjectContextInternal::eviscerate;
creators["vanish"] = &RogueAiObjectContextInternal::vanish;
creators["evasion"] = &RogueAiObjectContextInternal::evasion;
creators["cloak of shadows"] = &RogueAiObjectContextInternal::cloak_of_shadows;
creators["kick"] = &RogueAiObjectContextInternal::kick;
creators["feint"] = &RogueAiObjectContextInternal::feint;
creators["backstab"] = &RogueAiObjectContextInternal::backstab;
@@ -161,6 +164,7 @@ private:
static Action* eviscerate(PlayerbotAI* botAI) { return new CastEviscerateAction(botAI); }
static Action* vanish(PlayerbotAI* botAI) { return new CastVanishAction(botAI); }
static Action* evasion(PlayerbotAI* botAI) { return new CastEvasionAction(botAI); }
static Action* cloak_of_shadows(PlayerbotAI* botAI) { return new CastCloakOfShadowsAction(botAI); }
static Action* kick(PlayerbotAI* botAI) { return new CastKickAction(botAI); }
static Action* feint(PlayerbotAI* botAI) { return new CastFeintAction(botAI); }
static Action* backstab(PlayerbotAI* botAI) { return new CastBackstabAction(botAI); }

View File

@@ -18,34 +18,34 @@ public:
bool isUseful() override;
};
class CastSinisterStrikeAction : public CastComboAction
class CastSinisterStrikeAction : public CastSpellAction
{
public:
CastSinisterStrikeAction(PlayerbotAI* botAI) : CastComboAction(botAI, "sinister strike") {}
CastSinisterStrikeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "sinister strike") {}
};
class CastMutilateAction : public CastComboAction
class CastMutilateAction : public CastSpellAction
{
public:
CastMutilateAction(PlayerbotAI* botAI) : CastComboAction(botAI, "mutilate") {}
CastMutilateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "mutilate") {}
};
class CastRiposteAction : public CastComboAction
class CastRiposteAction : public CastSpellAction
{
public:
CastRiposteAction(PlayerbotAI* botAI) : CastComboAction(botAI, "riposte") {}
CastRiposteAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "riposte") {}
};
class CastGougeAction : public CastComboAction
class CastGougeAction : public CastSpellAction
{
public:
CastGougeAction(PlayerbotAI* botAI) : CastComboAction(botAI, "gouge") {}
CastGougeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "gouge") {}
};
class CastBackstabAction : public CastComboAction
class CastBackstabAction : public CastSpellAction
{
public:
CastBackstabAction(PlayerbotAI* botAI) : CastComboAction(botAI, "backstab") {}
CastBackstabAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "backstab") {}
};
#endif

View File

@@ -36,6 +36,13 @@ public:
// bool isPossible();
};
class BladeFuryTrigger : public BoostTrigger
{
public:
BladeFuryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "blade fury") {}
};
class RuptureTrigger : public DebuffTrigger
{
public:

View File

@@ -6,6 +6,7 @@
#include "ShamanActions.h"
#include "Playerbots.h"
#include "Totem.h"
bool CastTotemAction::isUseful()
{
@@ -41,7 +42,24 @@ bool CastSearingTotemAction::isUseful()
return CastTotemAction::isUseful() && !AI_VALUE2(bool, "has totem", "flametongue totem");
}
bool CastMagmaTotemAction::isUseful() { return CastTotemAction::isUseful() && !AI_VALUE2(bool, "has totem", name); }
bool CastMagmaTotemAction::isUseful() {
Unit* target = AI_VALUE(Unit*, "current target");
if (!target || !bot->IsWithinMeleeRange(target))
return false;
return CastTotemAction::isUseful() && !AI_VALUE2(bool, "has totem", name);
}
bool CastFireNovaAction::isUseful() {
Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]);
if (!fireTotem)
return false;
if (bot->GetDistance(fireTotem) > 8.0f)
return false;
return CastMeleeSpellAction::isUseful();
}
bool CastCleansingTotemAction::isUseful()
{

View File

@@ -231,10 +231,11 @@ public:
bool isUseful() override;
};
class CastFireNovaAction : public CastSpellAction
class CastFireNovaAction : public CastMeleeSpellAction
{
public:
CastFireNovaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "fire nova") {}
CastFireNovaAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "fire nova") {}
bool isUseful() override;
};
class CastWindShearAction : public CastSpellAction

View File

@@ -308,7 +308,23 @@ std::string const TwoTriggers::getName()
return name;
}
bool BoostTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE(uint8, "balance") <= balance; }
bool BoostTrigger::IsActive()
{
if (!BuffTrigger::IsActive())
return false;
Unit* target = AI_VALUE(Unit*, "current target");
if (target && target->ToPlayer())
return true;
return AI_VALUE(uint8, "balance") <= balance;
}
bool GenericBoostTrigger::IsActive()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (target && target->ToPlayer())
return true;
return AI_VALUE(uint8, "balance") <= balance;
}
bool ItemCountTrigger::IsActive() { return AI_VALUE2(uint32, "item count", item) < count; }

View File

@@ -409,6 +409,21 @@ protected:
float balance;
};
class GenericBoostTrigger : public Trigger
{
public:
GenericBoostTrigger(PlayerbotAI* botAI, float balance = 50.f)
: Trigger(botAI, "generic boost", 1), balance(balance)
{
}
bool IsActive() override;
protected:
float balance;
};
class RandomTrigger : public Trigger
{
public:

View File

@@ -48,7 +48,7 @@ class LowHealthTrigger : public HealthInRangeTrigger
{
public:
LowHealthTrigger(PlayerbotAI* botAI, std::string const name = "low health",
float value = sPlayerbotAIConfig->lowHealth, float minValue = sPlayerbotAIConfig->criticalHealth)
float value = sPlayerbotAIConfig->lowHealth, float minValue = 0)
: HealthInRangeTrigger(botAI, name, value, minValue)
{
}
@@ -69,7 +69,7 @@ class MediumHealthTrigger : public LowHealthTrigger
{
public:
MediumHealthTrigger(PlayerbotAI* botAI)
: LowHealthTrigger(botAI, "medium health", sPlayerbotAIConfig->mediumHealth, sPlayerbotAIConfig->lowHealth)
: LowHealthTrigger(botAI, "medium health", sPlayerbotAIConfig->mediumHealth, 0)
{
}
};
@@ -89,7 +89,7 @@ class PartyMemberLowHealthTrigger : public HealthInRangeTrigger
public:
PartyMemberLowHealthTrigger(PlayerbotAI* botAI, std::string const name = "party member low health",
float value = sPlayerbotAIConfig->lowHealth,
float minValue = sPlayerbotAIConfig->criticalHealth)
float minValue = 0)
: HealthInRangeTrigger(botAI, name, value, minValue)
{
}
@@ -111,7 +111,7 @@ class PartyMemberMediumHealthTrigger : public PartyMemberLowHealthTrigger
public:
PartyMemberMediumHealthTrigger(PlayerbotAI* botAI)
: PartyMemberLowHealthTrigger(botAI, "party member medium health", sPlayerbotAIConfig->mediumHealth,
sPlayerbotAIConfig->lowHealth)
0)
{
}
};
@@ -121,7 +121,7 @@ class PartyMemberAlmostFullHealthTrigger : public PartyMemberLowHealthTrigger
public:
PartyMemberAlmostFullHealthTrigger(PlayerbotAI* botAI)
: PartyMemberLowHealthTrigger(botAI, "party member almost full health", sPlayerbotAIConfig->almostFullHealth,
sPlayerbotAIConfig->mediumHealth)
0)
{
}
};

View File

@@ -53,6 +53,8 @@ public:
creators["party member medium health"] = &TriggerContext::PartyMemberMediumHealth;
creators["party member almost full health"] = &TriggerContext::PartyMemberAlmostFullHealth;
creators["generic boost"] = &TriggerContext::generic_boost;
creators["protect party member"] = &TriggerContext::protect_party_member;
creators["light rage available"] = &TriggerContext::LightRageAvailable;
@@ -318,6 +320,7 @@ private:
{
return new PartyMemberAlmostFullHealthTrigger(botAI);
}
static Trigger* generic_boost(PlayerbotAI* botAI) { return new GenericBoostTrigger(botAI); }
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
{
return new PartyMemberCriticalHealthTrigger(botAI);

View File

@@ -66,8 +66,13 @@ uint8 BalancePercentValue::Calculate()
playerLevel += player->GetLevel();
}
uint32 memberCount = group->GetMembersCount();
playerLevel /= memberCount;
if (memberCount <= 10)
playerLevel *= memberCount;
else
playerLevel *= 10;
}
GuidVector v = context->GetValue<GuidVector>("attackers")->Get();
for (ObjectGuid const guid : v)
{
@@ -89,7 +94,7 @@ uint8 BalancePercentValue::Calculate()
level *= 3;
break;
case CREATURE_ELITE_WORLDBOSS:
level *= 50;
level *= 30;
break;
}

View File

@@ -19,7 +19,7 @@ Unit* AttackerWithoutAuraTargetValue::Calculate()
if (!unit || !unit->IsAlive())
continue;
if (bot->GetDistance(unit) > botAI->GetRange(range))
if (!bot->IsWithinCombatRange(unit, botAI->GetRange(range)))
continue;
if (unit->GetHealth() < max_health)

View File

@@ -24,6 +24,7 @@ LastMovement::LastMovement(LastMovement& other)
lastMoveShort = other.lastMoveShort;
nextTeleport = other.nextTeleport;
lastPath = other.lastPath;
priority = other.priority;
}
void LastMovement::clear()
@@ -41,6 +42,7 @@ void LastMovement::clear()
nextTeleport = 0;
msTime = 0;
lastdelayTime = 0;
priority = MovementPriority::MOVEMENT_NORMAL;
}
void LastMovement::Set(Unit* follow)
@@ -51,7 +53,7 @@ void LastMovement::Set(Unit* follow)
lastFollow = follow;
}
void LastMovement::Set(uint32 mapId, float x, float y, float z, float ori, float delayTime)
void LastMovement::Set(uint32 mapId, float x, float y, float z, float ori, float delayTime, MovementPriority pri)
{
lastMoveToMapId = mapId;
lastMoveToX = x;
@@ -62,6 +64,7 @@ void LastMovement::Set(uint32 mapId, float x, float y, float z, float ori, float
lastMoveShort = WorldPosition(mapId, x, y, z, ori);
msTime = getMSTime();
lastdelayTime = delayTime;
priority = pri;
}
void LastMovement::setShort(WorldPosition point)

View File

@@ -13,6 +13,15 @@
class PlayerbotAI;
class Unit;
// High priority movement can override the previous low priority one
enum class MovementPriority
{
MOVEMENT_IDLE,
MOVEMENT_NORMAL,
MOVEMENT_COMBAT,
MOVEMENT_FORCED
};
class LastMovement
{
public:
@@ -28,14 +37,14 @@ public:
lastMoveShort = other.lastMoveShort;
lastPath = other.lastPath;
nextTeleport = other.nextTeleport;
priority = other.priority;
return *this;
};
void clear();
void Set(Unit* follow);
void Set(uint32 mapId, float x, float y, float z, float ori, float delayTime);
void Set(uint32 mapId, float x, float y, float z, float ori, float delayTime, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
void setShort(WorldPosition point);
void setPath(TravelPath path);
@@ -53,6 +62,7 @@ public:
float lastdelayTime;
WorldPosition lastMoveShort;
uint32 msTime;
MovementPriority priority;
TravelPath lastPath;
time_t nextTeleport;
std::future<TravelPath> future;

View File

@@ -41,6 +41,8 @@ Unit* PartyMemberToHeal::Calculate()
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player->IsGameMaster())
continue;
if (player && player->IsAlive())
{
uint8 health = player->GetHealthPct();

View File

@@ -103,7 +103,8 @@ bool PartyMemberValue::Check(Unit* player)
{
// return player && player != bot && player->GetMapId() == bot->GetMapId() && bot->IsWithinDistInMap(player,
// sPlayerbotAIConfig->sightDistance, false);
return player && player->GetMapId() == bot->GetMapId() &&
bool isGM = player->ToPlayer() && player->ToPlayer()->IsGameMaster();
return player && player->GetMapId() == bot->GetMapId() && !isGM &&
bot->GetDistance(player) < sPlayerbotAIConfig->spellDistance * 2 &&
bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
}

View File

@@ -20,14 +20,21 @@ BUFF_ACTION(CastBerserkerStanceAction, "berserker stance");
// shouts
BUFF_ACTION(CastBattleShoutAction, "battle shout");
MELEE_ACTION_U(CastBattleShoutTauntAction, "battle shout", CastSpellAction::isUseful()); // useful to rebuff
DEBUFF_ACTION_R(CastDemoralizingShoutAction, "demoralizing shout", 8.0f); // low range debuff
class CastDemoralizingShoutWithoutLifeTimeCheckAction : public CastDebuffSpellAction
// DEBUFF_ACTION_R(CastDemoralizingShoutAction, "demoralizing shout", 8.0f); // low range debuff
class CastDemoralizingShoutAction : public CastMeleeDebuffSpellAction
{
public:
CastDemoralizingShoutAction(PlayerbotAI* botAI)
: CastMeleeDebuffSpellAction(botAI, "demoralizing shout") {}
};
class CastDemoralizingShoutWithoutLifeTimeCheckAction : public CastMeleeDebuffSpellAction
{
public:
CastDemoralizingShoutWithoutLifeTimeCheckAction(PlayerbotAI* botAI)
: CastDebuffSpellAction(botAI, "demoralizing shout", false, 0.0f)
: CastMeleeDebuffSpellAction(botAI, "demoralizing shout", false, 0.0f)
{
range = 8.0f;
}
};
@@ -63,7 +70,7 @@ DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw");
MELEE_ACTION(CastMortalStrikeAction, "mortal strike");
BUFF_ACTION(CastSweepingStrikesAction, "sweeping strikes");
// arms talents 3.3.5
BUFF_ACTION(CastBladestormAction, "bladestorm");
MELEE_ACTION(CastBladestormAction, "bladestorm");
// fury
MELEE_ACTION(CastCleaveAction, "cleave");