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
commit 0eba23d738
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 1097 additions and 730 deletions

View File

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

View File

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

View File

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

View File

@ -342,16 +342,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
bool min = minimal; bool min = minimal;
UpdateAIInternal(elapsed, min); UpdateAIInternal(elapsed, min);
inCombat = bot->IsInCombat(); YieldThread(GetReactDelay());
// 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);
} }
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal) void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
@ -940,6 +931,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
if (!AllowActivity()) if (!AllowActivity())
return; return;
WorldPacket p(packet); WorldPacket p(packet);
if (!p.empty() && (p.GetOpcode() == SMSG_MESSAGECHAT || p.GetOpcode() == SMSG_GM_MESSAGECHAT)) 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::AddDefaultNonCombatStrategies(bot, this, engines[BOT_STATE_NON_COMBAT]);
AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]); AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]);
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
engines[i]->Init();
// if (load) // if (load)
// sPlayerbotDbStore->Load(this); // 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(); 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())) if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{ {
@ -4795,7 +4791,8 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
{ {
for (const auto& id : uPriorizedWizardOilIds) for (const auto& id : uPriorizedWizardOilIds)
{ {
if (oil = FindConsumable(id)) oil = FindConsumable(id);
if (oil)
return oil; return oil;
} }
} }
@ -4804,7 +4801,8 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
{ {
for (const auto& id : uPriorizedManaOilIds) for (const auto& id : uPriorizedManaOilIds)
{ {
if (oil = FindConsumable(id)) oil = FindConsumable(id);
if (oil)
return oil; return oil;
} }
} }
@ -5691,3 +5689,81 @@ std::set<uint32> PlayerbotAI::GetCurrentIncompleteQuestIds()
return result; 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); uint32 GetInventoryItemsCountWithId(uint32 itemId);
bool HasItemInInventory(uint32 itemId); bool HasItemInInventory(uint32 itemId);
std::vector<std::pair<const Quest*, uint32>> GetCurrentQuestsRequiringItemId(uint32 itemId); std::vector<std::pair<const Quest*, uint32>> GetCurrentQuestsRequiringItemId(uint32 itemId);
uint32 GetReactDelay();
std::vector<const Quest*> GetAllCurrentQuests(); std::vector<const Quest*> GetAllCurrentQuests();
std::vector<const Quest*> GetCurrentIncompleteQuests(); std::vector<const Quest*> GetCurrentIncompleteQuests();
std::set<uint32> GetAllCurrentQuestIds(); std::set<uint32> GetAllCurrentQuestIds();
std::set<uint32> GetCurrentIncompleteQuestIds(); std::set<uint32> GetCurrentIncompleteQuestIds();
void PetFollow();
private: private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore, bool mixed = false); static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore, bool mixed = false);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); 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; } bool PlayerbotAIBase::CanUpdateAI() { return nextAICheckDelay == 0; }
void PlayerbotAIBase::YieldThread(bool delay) void PlayerbotAIBase::YieldThread(uint32 delay)
{ {
if (nextAICheckDelay < sPlayerbotAIConfig->reactDelay) if (nextAICheckDelay < delay)
nextAICheckDelay = delay ? sPlayerbotAIConfig->reactDelay * 10 : sPlayerbotAIConfig->reactDelay; nextAICheckDelay = delay;
} }
bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove; } bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove; }

View File

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

View File

@ -59,7 +59,8 @@ bool PlayerbotAIConfig::Initialize()
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3); maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000); expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000); 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); passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000); repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000); errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
@ -77,7 +78,7 @@ bool PlayerbotAIConfig::Initialize()
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f); fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f);
aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f); aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f);
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.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); followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f); whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f); contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f);

View File

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

View File

@ -29,6 +29,7 @@
#include "LFGMgr.h" #include "LFGMgr.h"
#include "MapMgr.h" #include "MapMgr.h"
#include "PerformanceMonitor.h" #include "PerformanceMonitor.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "PlayerbotCommandServer.h" #include "PlayerbotCommandServer.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
@ -2333,6 +2334,11 @@ void RandomPlayerbotMgr::PrintStats()
uint32 taxi = 0; uint32 taxi = 0;
uint32 moving = 0; uint32 moving = 0;
uint32 mounted = 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}; uint32 stateCount[MAX_TRAVEL_STATE + 1] = {0};
std::vector<std::pair<Quest const*, int32>> questCount; std::vector<std::pair<Quest const*, int32>> questCount;
for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i) for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i)
@ -2369,7 +2375,33 @@ void RandomPlayerbotMgr::PrintStats()
// if (!GetEventValue(botId, "dead")) // if (!GetEventValue(botId, "dead"))
//++revive; //++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); uint8 spec = AiFactory::GetPlayerSpecTab(bot);
switch (bot->getClass()) switch (bot->getClass())
{ {
@ -2490,8 +2522,15 @@ void RandomPlayerbotMgr::PrintStats()
LOG_INFO("playerbots", " On taxi: {}", taxi); LOG_INFO("playerbots", " On taxi: {}", taxi);
LOG_INFO("playerbots", " On mount: {}", mounted); LOG_INFO("playerbots", " On mount: {}", mounted);
LOG_INFO("playerbots", " In combat: {}", combat); 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", " 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", "Bots questing:");
LOG_INFO("playerbots", " Picking quests: {}", LOG_INFO("playerbots", " Picking quests: {}",
stateCount[TRAVEL_STATE_TRAVEL_PICK_UP_QUEST] + stateCount[TRAVEL_STATE_WORK_PICK_UP_QUEST]); 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; return battleMasterGUID;
} }

View File

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

View File

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

View File

@ -9,6 +9,7 @@
#include "AiFactory.h" #include "AiFactory.h"
#include "DBCStores.h" #include "DBCStores.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "SharedDefines.h" #include "SharedDefines.h"
@ -231,7 +232,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.2f; 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_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance (cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
@ -454,6 +456,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{ {
weight_ *= 0.1; 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; 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)) if (Strategy* strategy = aiObjectContext->GetStrategy(name))
{ {
std::set<std::string> siblings = aiObjectContext->GetSiblingStrategy(name); std::set<std::string> siblings = aiObjectContext->GetSiblingStrategy(name);
for (std::set<std::string>::iterator i = siblings.begin(); i != siblings.end(); i++) 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()); LogAction("S:+%s", strategy->getName().c_str());
strategies[strategy->getName()] = strategy; strategies[strategy->getName()] = strategy;
} }
if (init)
Init(); Init();
} }
void Engine::addStrategies(std::string first, ...) void Engine::addStrategies(std::string first, ...)
{ {
addStrategy(first); addStrategy(first, false);
va_list vl; va_list vl;
va_start(vl, first); va_start(vl, first);
@ -438,13 +438,34 @@ void Engine::addStrategies(std::string first, ...)
{ {
cur = va_arg(vl, const char*); cur = va_arg(vl, const char*);
if (cur) 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); } while (cur);
va_end(vl); 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); std::map<std::string, Strategy*>::iterator i = strategies.find(name);
if (i == strategies.end()) if (i == strategies.end())
@ -452,7 +473,8 @@ bool Engine::removeStrategy(std::string const name)
LogAction("S:-%s", name.c_str()); LogAction("S:-%s", name.c_str());
strategies.erase(i); strategies.erase(i);
Init(); if (init)
Init();
return true; return true;
} }

View File

@ -65,9 +65,10 @@ public:
Engine(PlayerbotAI* botAI, AiObjectContext* factory); Engine(PlayerbotAI* botAI, AiObjectContext* factory);
void Init(); void Init();
void addStrategy(std::string const name); void addStrategy(std::string const name, bool init = true);
void addStrategies(std::string first, ...); 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); bool HasStrategy(std::string const name);
void removeAllStrategies(); void removeAllStrategies();
void toggleStrategy(std::string const name); void toggleStrategy(std::string const name);

View File

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

View File

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

View File

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

View File

@ -7,9 +7,12 @@
#include "CreatureAI.h" #include "CreatureAI.h"
#include "Event.h" #include "Event.h"
#include "LastMovementValue.h"
#include "LootObjectStack.h" #include "LootObjectStack.h"
#include "PlayerbotAI.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
#include "SharedDefines.h"
#include "Unit.h" #include "Unit.h"
bool AttackAction::Execute(Event event) bool AttackAction::Execute(Event event)
@ -109,24 +112,32 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bot->SetSelection(target->GetGUID()); bot->SetSelection(target->GetGUID());
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get(); 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*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("current target")->Set(target); context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid); 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); bool melee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
bot->Attack(target, melee);
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target)) if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
{ {
sServerFacade->SetFacingTo(bot, target); sServerFacade->SetFacingTo(bot, target);
} }
botAI->ChangeEngine(BOT_STATE_COMBAT); botAI->ChangeEngine(BOT_STATE_COMBAT);
if (!bot->GetVictim()) bot->Attack(target, melee);
{
return false;
}
/* prevent pet dead immediately in group */ /* prevent pet dead immediately in group */
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) { // if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) {
// with_pet = false; // 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. * and/or modify it under version 2 of the License, or (at your option), any later version.
*/ */
#ifndef _PLAYERBOT_AUTOLEARNSPELLACTION_H #ifndef _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#define _PLAYERBOT_AUTOLEARNSPELLACTION_H #define _PLAYERBOT_AUTOTELEPORTFORLEVELACTION_H
#include "Action.h" #include "Action.h"
class PlayerbotAI; class PlayerbotAI;
class AutoLearnSpellAction : public Action class AutoMaintenanceOnLevelupAction : public Action
{ {
public: 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); bool Execute(Event event);
private: protected:
void AutoTeleportForLevel();
void AutoPickTalents();
void AutoLearnSpell();
void AutoUpgradeEquip();
void LearnSpells(std::ostringstream* out); void LearnSpells(std::ostringstream* out);
void LearnTrainerSpells(std::ostringstream* out); void LearnTrainerSpells(std::ostringstream* out);
void LearnQuestSpells(std::ostringstream* out); void LearnQuestSpells(std::ostringstream* out);
void LearnSpell(uint32 spellId, std::ostringstream* out); void LearnSpell(uint32 spellId, std::ostringstream* out);
}; std::string const FormatSpell(SpellInfo const* sInfo);
class AutoUpgradeEquipAction : public Action
{
public:
AutoUpgradeEquipAction(PlayerbotAI* botAI, std::string const name = "auto upgrade equip") : Action(botAI, name) {}
bool Execute(Event event);
}; };
#endif #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) if (!master)
return false; return false;
botAI->Reset(); // botAI->Reset();
botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT); botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT);
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({}); botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
@ -57,7 +57,14 @@ bool FollowChatShortcutAction::Execute(Event event)
if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1) if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1)
return false; 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) if (moved)

View File

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

View File

@ -9,6 +9,8 @@
#include "Event.h" #include "Event.h"
#include "Formations.h" #include "Formations.h"
#include "LastMovementValue.h"
#include "PlayerbotAI.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
#include "SharedDefines.h" #include "SharedDefines.h"
@ -28,25 +30,15 @@ bool FollowAction::Execute(Event event)
WorldLocation loc = formation->GetLocation(); WorldLocation loc = formation->GetLocation();
if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1) if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1)
return false; 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, moved = MoveTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), false, false, false,
true); true, priority);
} }
if (Pet* pet = bot->GetPet()) if (Pet* pet = bot->GetPet())
{ {
if (CreatureAI* creatureAI = ((Creature*)pet)->AI()) botAI->PetFollow();
{
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());
}
} }
// if (moved) // if (moved)
// botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); // botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);

View File

@ -36,9 +36,9 @@ bool TogglePetSpellAutoCastAction::Execute(Event event)
continue; continue;
bool shouldApply = true; 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 || 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; shouldApply = false;
} }
@ -72,7 +72,7 @@ bool PetAttackAction::Execute(Event event)
{ {
return false; return false;
} }
// pet->SetReactState(REACT_DEFENSIVE); pet->SetReactState(REACT_PASSIVE);
pet->ClearUnitState(UNIT_STATE_FOLLOW); pet->ClearUnitState(UNIT_STATE_FOLLOW);
pet->AttackStop(); pet->AttackStop();
pet->SetTarget(target->GetGUID()); pet->SetTarget(target->GetGUID());

View File

@ -6,8 +6,13 @@
#include "GenericSpellActions.h" #include "GenericSpellActions.h"
#include "Event.h" #include "Event.h"
#include "ItemTemplate.h"
#include "ObjectDefines.h"
#include "Opcodes.h"
#include "Player.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
#include "WorldPacket.h"
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell) CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(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) CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
{ {
range = ATTACK_DISTANCE; 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() bool CastAuraSpellAction::isUseful()
@ -209,17 +238,7 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
NextAction** CastSpellAction::getPrerequisites() NextAction** CastSpellAction::getPrerequisites()
{ {
if (spell == "mount") return nullptr;
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());
} }
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue() Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
@ -271,6 +290,70 @@ bool CastVehicleSpellAction::Execute(Event event)
return botAI->CastVehicleSpell(spellId, GetTarget()); 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); } Value<Unit*>* BuffOnMainTankAction::GetTargetValue() { return context->GetValue<Unit*>("main tank", spell); }
bool CastDebuffSpellAction::isUseful() bool CastDebuffSpellAction::isUseful()

View File

@ -9,6 +9,7 @@
#include "Action.h" #include "Action.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "UseItemAction.h"
#include "Value.h" #include "Value.h"
class PlayerbotAI; class PlayerbotAI;
@ -52,6 +53,7 @@ class CastMeleeSpellAction : public CastSpellAction
{ {
public: public:
CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell); CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell);
bool isUseful() override;
}; };
class CastDebuffSpellAction : public CastAuraSpellAction class CastDebuffSpellAction : public CastAuraSpellAction
@ -67,6 +69,13 @@ private:
float needLifeTime; 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 class CastDebuffSpellOnAttackerAction : public CastDebuffSpellAction
{ {
public: public:
@ -245,10 +254,31 @@ public:
CastManaTapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mana tap") {} CastManaTapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mana tap") {}
}; };
class CastWarStompAction : public CastSpellAction class CastWarStompAction : public CastMeleeSpellAction
{ {
public: 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 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); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
if (!spellInfo) if (!spellInfo)
continue; continue;
if (spellInfo->IsPassive())
continue;
SkillLineAbilityEntry const* skillLine = skillSpells[itr->first]; SkillLineAbilityEntry const* skillLine = skillSpells[itr->first];
if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill)) if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill))
continue; continue;

View File

@ -94,12 +94,14 @@ bool MoveToRpgTargetAction::Execute(Event event)
x += cos(angle) * INTERACTION_DISTANCE * distance; x += cos(angle) * INTERACTION_DISTANCE * distance;
y += sin(angle) * INTERACTION_DISTANCE * distance; y += sin(angle) * INTERACTION_DISTANCE * distance;
bool exact = true;
if (!wo->GetMap()->CheckCollisionAndGetValidCoords(wo, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ(), if (!wo->GetMap()->CheckCollisionAndGetValidCoords(wo, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ(),
x, y, z)) x, y, z))
{ {
x = wo->GetPositionX(); x = wo->GetPositionX();
y = wo->GetPositionY(); y = wo->GetPositionY();
z = wo->GetPositionZ(); z = wo->GetPositionZ();
exact = false;
} }
// WaitForReach(distance); // WaitForReach(distance);
@ -108,7 +110,7 @@ bool MoveToRpgTargetAction::Execute(Event event)
// if (bot->IsWithinLOS(x, y, z)) // if (bot->IsWithinLOS(x, y, z))
// couldMove = MoveNear(mapId, x, y, z, 0); // couldMove = MoveNear(mapId, x, y, z, 0);
// else // 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) 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); 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 botZ = bot->GetPositionZ();
float speed = bot->GetSpeed(MOVE_RUN); float speed = bot->GetSpeed(MOVE_RUN);
MotionMaster& mm = *bot->GetMotionMaster(); MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear(); mm.Clear();
mm.MoveJump(x, y, z, speed, speed, 1); 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(); 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) if (!target)
return false; return false;
@ -99,7 +113,7 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
if (!bot->IsWithinLOS(x, y, z)) if (!bot->IsWithinLOS(x, y, z))
continue; continue;
bool moved = MoveTo(target->GetMapId(), x, y, z); bool moved = MoveTo(target->GetMapId(), x, y, z, false, false, false, false, priority);
if (moved) if (moved)
return true; 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 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(); UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z)) 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; return false;
} }
if (IsWaitingForLastMove()) if (IsWaitingForLastMove(priority))
{ {
return false; 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 generatePath = !bot->IsFlying() && !bot->isSwimming();
bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 ||
(sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); (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 MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
mm.Clear(); mm.Clear();
mm.MovePoint(mapId, x, y, z, generatePath); mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN)) - sPlayerbotAIConfig->reactDelay; float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN));
delay = std::max(.0f, delay); delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, 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; return true;
} }
} }
@ -225,11 +228,11 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
} }
MotionMaster& mm = *bot->GetMotionMaster(); MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear(); mm.Clear();
mm.MovePoint(mapId, x, y, z, generatePath); mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * MoveDelay(distance) - sPlayerbotAIConfig->reactDelay; float delay = 1000.0f * MoveDelay(distance);
delay = std::max(.0f, delay); delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, 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; return true;
} }
} }
@ -254,14 +257,13 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
botAI->InterruptSpell(); botAI->InterruptSpell();
} }
MotionMaster& mm = *bot->GetMotionMaster(); MotionMaster& mm = *bot->GetMotionMaster();
G3D::Vector3 endP = path.back();
mm.Clear(); mm.Clear();
mm.MoveSplinePath(&path); mm.MovePoint(0, endP.x, endP.y, endP.z, generatePath);
// mm.MoveSplinePath(&path); float delay = 1000.0f * MoveDelay(distance);
float delay = 1000.0f * MoveDelay(distance) - sPlayerbotAIConfig->reactDelay;
delay = std::max(.0f, delay); delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, 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; return true;
} }
} }
@ -759,7 +761,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// return true; // return true;
} }
bool MovementAction::MoveTo(Unit* target, float distance) bool MovementAction::MoveTo(Unit* target, float distance, MovementPriority priority)
{ {
if (!IsMovingAllowed(target)) if (!IsMovingAllowed(target))
return false; return false;
@ -793,7 +795,7 @@ bool MovementAction::MoveTo(Unit* target, float distance)
{ {
dz = tz; 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) 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 if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD)) // target is moving forward, predict the position
{ {
float needToGo = bot->GetExactDist(target) - distance; float needToGo = bot->GetExactDist(target) - distance;
float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f; float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f;
float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN); float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN);
@ -845,7 +848,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
return false; return false;
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance); path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance);
G3D::Vector3 endPos = path.GetPath().back(); 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() float MovementAction::GetFollowAngle()
@ -909,10 +912,13 @@ bool MovementAction::IsDuplicateMove(uint32 mapId, float x, float y, float z)
return true; return true;
} }
bool MovementAction::IsWaitingForLastMove() bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
{ {
LastMovement& lastMove = *context->GetValue<LastMovement&>("last movement"); LastMovement& lastMove = *context->GetValue<LastMovement&>("last movement");
if (priority > lastMove.priority)
return false;
// heuristic 5s // heuristic 5s
if (lastMove.lastdelayTime + lastMove.msTime > getMSTime()) if (lastMove.lastdelayTime + lastMove.msTime > getMSTime())
return true; return true;
@ -1216,7 +1222,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
botAI->InterruptSpell(); botAI->InterruptSpell();
} }
AI_VALUE(LastMovement&, "last movement").Set(target); // AI_VALUE(LastMovement&, "last movement").Set(target);
ClearIdleState(); ClearIdleState();
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) 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 dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
float dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance; float dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
float dz = bot->GetPositionZ(); 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; return true;
} }
@ -1514,11 +1530,21 @@ bool MovementAction::MoveAway(Unit* target)
{ {
continue; continue;
} }
exact = true;
angle = init_angle - delta; angle = init_angle - delta;
dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance; dx = bot->GetPositionX() + cos(angle) * sPlayerbotAIConfig->fleeDistance;
dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance; dy = bot->GetPositionY() + sin(angle) * sPlayerbotAIConfig->fleeDistance;
dz = bot->GetPositionZ(); 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; return true;
} }
@ -1526,13 +1552,13 @@ bool MovementAction::MoveAway(Unit* target)
return false; 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) if (bot->GetDistance2d(x, y) <= distance)
{ {
return false; 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 // 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 (Pet* pet = bot->GetPet())
{ {
if (CreatureAI* creatureAI = ((Creature*)pet)->AI()) botAI->PetFollow();
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetIsCommandFollow(true);
pet->GetCharmInfo()->IsReturning();
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
}
} }
return Flee(AI_VALUE(Unit*, "current target")); return Flee(AI_VALUE(Unit*, "current target"));
@ -1929,7 +1949,7 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
for (CheckAngle& checkAngle : possibleAngles) for (CheckAngle& checkAngle : possibleAngles)
{ {
float angle = checkAngle.angle; 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)) if (!CheckLastFlee(angle, infoList))
{ {
continue; continue;
@ -1985,7 +2005,7 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
for (CheckAngle& checkAngle : possibleAngles) for (CheckAngle& checkAngle : possibleAngles)
{ {
float angle = checkAngle.angle; 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)) if (!CheckLastFlee(angle, infoList))
{ {
continue; continue;
@ -2032,9 +2052,9 @@ bool MovementAction::FleePosition(Position pos, float radius)
if (bestPos != Position()) if (bestPos != Position())
{ {
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, 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(); uint32 curTS = getMSTime();
while (!infoList.empty()) while (!infoList.empty())
{ {
@ -2346,10 +2366,10 @@ bool MoveOutOfCollisionAction::isUseful()
bool MoveRandomAction::Execute(Event event) 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(); Map* map = bot->GetMap();
for (int i = 0; i < 10; ++i) for (int i = 0; i < 3; ++i)
{ {
float x = bot->GetPositionX(); float x = bot->GetPositionX();
float y = bot->GetPositionY(); float y = bot->GetPositionY();
@ -2362,11 +2382,7 @@ bool MoveRandomAction::Execute(Event event)
float oz = z; float oz = z;
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), x, y, z)) bot->GetPositionZ(), x, y, z))
{ continue;
x = ox;
y = oy;
z = oz;
}
if (map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight())) if (map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight()))
continue; continue;
@ -2385,7 +2401,7 @@ bool MoveInsideAction::Execute(Event event) { return MoveInside(bot->GetMapId(),
bool RotateAroundTheCenterPointAction::Execute(Event event) bool RotateAroundTheCenterPointAction::Execute(Event event)
{ {
uint32 next_point = GetCurrWaypoint(); 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; call_counters += 1;
return true; return true;

View File

@ -9,6 +9,7 @@
#include <cmath> #include <cmath>
#include "Action.h" #include "Action.h"
#include "LastMovementValue.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
class Player; class Player;
@ -17,19 +18,20 @@ class Unit;
class WorldObject; class WorldObject;
class Position; class Position;
class MovementAction : public Action class MovementAction : public Action
{ {
public: public:
MovementAction(PlayerbotAI* botAI, std::string const name); MovementAction(PlayerbotAI* botAI, std::string const name);
protected: protected:
void JumpTo(uint32 mapId, float x, float y, float z); 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); 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 MoveToLOS(WorldObject* target, bool ranged = false);
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = 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 normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveTo(Unit* target, float distance = 0.0f); bool MoveTo(Unit* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance); bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
float GetFollowAngle(); float GetFollowAngle();
bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance); bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance);
bool Follow(Unit* target, float distance, float angle); bool Follow(Unit* target, float distance, float angle);
@ -40,13 +42,13 @@ protected:
bool IsMovingAllowed(Unit* target); bool IsMovingAllowed(Unit* target);
bool IsMovingAllowed(uint32 mapId, float x, float y, float z); bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
bool IsDuplicateMove(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 IsMovingAllowed();
bool Flee(Unit* target); bool Flee(Unit* target);
void ClearIdleState(); void ClearIdleState();
void UpdateMovementState(); void UpdateMovementState();
bool MoveAway(Unit* target); 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); void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false);
Position BestPositionForMeleeToFlee(Position pos, float radius); Position BestPositionForMeleeToFlee(Position pos, float radius);
Position BestPositionForRangedToFlee(Position pos, float radius); Position BestPositionForRangedToFlee(Position pos, float radius);
@ -94,7 +96,7 @@ class AvoidAoeAction : public MovementAction
{ {
public: public:
AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) 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()) if (Pet* pet = bot->GetPet())
{ {
pet->SetReactState(REACT_PASSIVE); botAI->PetFollow();
pet->GetCharmInfo()->SetIsCommandFollow(true);
pet->GetCharmInfo()->IsReturning();
} }
if (master->GetSession()->GetSecurity() >= SEC_PLAYER) if (master->GetSession()->GetSecurity() >= SEC_PLAYER)
{ {
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({}); // 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); return Teleport(master, bot);
} }
@ -215,10 +213,12 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
bool revive = bool revive =
sPlayerbotAIConfig->reviveBotWhenSummoned == 2 || sPlayerbotAIConfig->reviveBotWhenSummoned == 2 ||
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive()); (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive());
if (bot->isDead() && revive) if (bot->isDead() && revive)
{ {
bot->ResurrectPlayer(1.0f, false); bot->ResurrectPlayer(1.0f, false);
botAI->TellMasterNoFacing("I live, again!"); botAI->TellMasterNoFacing("I live, again!");
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
} }
player->GetMotionMaster()->Clear(); player->GetMotionMaster()->Clear();

View File

@ -110,11 +110,11 @@ public:
CastIcyTouchAction(PlayerbotAI* ai) : CastSpellAction(ai, "icy touch") {} CastIcyTouchAction(PlayerbotAI* ai) : CastSpellAction(ai, "icy touch") {}
}; };
class CastIcyTouchOnAttackerAction : public CastDebuffSpellOnMeleeAttackerAction class CastIcyTouchOnAttackerAction : public CastDebuffSpellOnAttackerAction
{ {
public: public:
CastIcyTouchOnAttackerAction(PlayerbotAI* botAI) 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") {} CastDeathCoilAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death coil") {}
}; };
class CastBloodBoilAction : public CastSpellAction class CastBloodBoilAction : public CastMeleeSpellAction
{ {
public: public:
CastBloodBoilAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blood boil") {} CastBloodBoilAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "blood boil") {}
}; };
class CastDeathAndDecayAction : public CastSpellAction class CastDeathAndDecayAction : public CastSpellAction
{ {
public: public:
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {} CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
}; };
class CastHornOfWinterAction : public CastSpellAction class CastHornOfWinterAction : public CastSpellAction

View File

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

View File

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

View File

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

View File

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

View File

@ -106,6 +106,9 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{ {
CombatStrategy::InitTriggers(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", // 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", // 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))); // 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() 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) void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

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

View File

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

View File

@ -25,14 +25,18 @@ private:
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{ {
triggers.push_back( triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("lifeblood", 71.0f), 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", 71.0f), triggers.push_back(
// nullptr))); new TriggerNode("medium aoe", NextAction::array(0, new NextAction("war stomp", ACTION_NORMAL + 5), nullptr)));
/*triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("war stomp", 71.0f), triggers.push_back(new TriggerNode(
* nullptr)));*/ "low mana", NextAction::array(0, new NextAction("arcane torrent", ACTION_NORMAL + 5), 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 triggers.push_back(new TriggerNode(
NextAction("mana tap", ACTION_EMERGENCY + 6), nullptr)));*/ "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) 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("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("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("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr)));
triggers.push_back(new TriggerNode("levelup", NextAction::array(0, triggers.push_back(
new NextAction("auto teleport for level", relevance + 3), new TriggerNode("levelup", NextAction::array(0, new NextAction("auto maintenance on levelup", relevance + 3), nullptr)));
new NextAction("auto talents", relevance + 2), // triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI",
new NextAction("auto learn spell", relevance + 1), // relevance), nullptr)));
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("group list", 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("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))); 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> class DpsHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{ {
public: public:
DpsHunterStrategyActionNodeFactory() { creators["aimed shot"] = &aimed_shot; } DpsHunterStrategyActionNodeFactory()
{
creators["aimed shot"] = &aimed_shot;
creators["steady shot"] = &steady_shot;
}
private: private:
static ActionNode* aimed_shot([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* aimed_shot([[maybe_unused]] PlayerbotAI* botAI)
@ -20,6 +24,13 @@ private:
/*A*/ NextAction::array(0, new NextAction("multi-shot"), nullptr), /*A*/ NextAction::array(0, new NextAction("multi-shot"), nullptr),
/*C*/ 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) DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
@ -32,7 +43,7 @@ NextAction** DpsHunterStrategy::getDefaultActions()
return NextAction::array( return NextAction::array(
0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f), 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("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); new NextAction("auto shot", ACTION_DEFAULT), nullptr);
// return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new // return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new
// NextAction("auto attack", 9.0f), nullptr); // NextAction("auto attack", 9.0f), nullptr);

View File

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

View File

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

View File

@ -149,6 +149,7 @@ public:
creators["freezing trap"] = &HunterAiObjectContextInternal::freezing_trap; creators["freezing trap"] = &HunterAiObjectContextInternal::freezing_trap;
creators["rapid fire"] = &HunterAiObjectContextInternal::rapid_fire; creators["rapid fire"] = &HunterAiObjectContextInternal::rapid_fire;
creators["boost"] = &HunterAiObjectContextInternal::rapid_fire; creators["boost"] = &HunterAiObjectContextInternal::rapid_fire;
creators["deterrence"] = &HunterAiObjectContextInternal::deterrence;
creators["readiness"] = &HunterAiObjectContextInternal::readiness; creators["readiness"] = &HunterAiObjectContextInternal::readiness;
creators["aspect of the hawk"] = &HunterAiObjectContextInternal::aspect_of_the_hawk; creators["aspect of the hawk"] = &HunterAiObjectContextInternal::aspect_of_the_hawk;
creators["aspect of the monkey"] = &HunterAiObjectContextInternal::aspect_of_the_monkey; 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* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); }
static Action* freezing_trap(PlayerbotAI* botAI) { return new CastFreezingTrap(botAI); } static Action* freezing_trap(PlayerbotAI* botAI) { return new CastFreezingTrap(botAI); }
static Action* rapid_fire(PlayerbotAI* botAI) { return new CastRapidFireAction(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* readiness(PlayerbotAI* botAI) { return new CastReadinessAction(botAI); }
static Action* aspect_of_the_hawk(PlayerbotAI* botAI) { return new CastAspectOfTheHawkAction(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); } 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), return NextAction::array(0, new NextAction("crusader strike", ACTION_DEFAULT + 0.4f),
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f), new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f),
new NextAction("divine storm", ACTION_DEFAULT + 0.2f), 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); 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", // triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance",
// ACTION_INTERRUPT + 2), nullptr))); // ACTION_INTERRUPT + 2), nullptr)));
triggers.push_back(new TriggerNode( 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( triggers.push_back(
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr))); new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode("target critical health", triggers.push_back(new TriggerNode("target critical health",

View File

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

View File

@ -153,13 +153,6 @@ Unit* CastRighteousDefenseAction::GetTarget()
return current_target->GetVictim(); 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() bool CastDivineSacrificeAction::isUseful()
{ {

View File

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

View File

@ -232,7 +232,6 @@ public:
creators["concentration aura"] = &PaladinAiObjectContextInternal::concentration_aura; creators["concentration aura"] = &PaladinAiObjectContextInternal::concentration_aura;
creators["holy wrath"] = &PaladinAiObjectContextInternal::holy_wrath; creators["holy wrath"] = &PaladinAiObjectContextInternal::holy_wrath;
creators["consecration"] = &PaladinAiObjectContextInternal::consecration; creators["consecration"] = &PaladinAiObjectContextInternal::consecration;
creators["melee consecration"] = &PaladinAiObjectContextInternal::melee_consecration;
creators["cleanse disease"] = &PaladinAiObjectContextInternal::cleanse_disease; creators["cleanse disease"] = &PaladinAiObjectContextInternal::cleanse_disease;
creators["cleanse poison"] = &PaladinAiObjectContextInternal::cleanse_poison; creators["cleanse poison"] = &PaladinAiObjectContextInternal::cleanse_poison;
creators["cleanse magic"] = &PaladinAiObjectContextInternal::cleanse_magic; creators["cleanse magic"] = &PaladinAiObjectContextInternal::cleanse_magic;
@ -331,7 +330,6 @@ private:
static Action* concentration_aura(PlayerbotAI* botAI) { return new CastConcentrationAuraAction(botAI); } static Action* concentration_aura(PlayerbotAI* botAI) { return new CastConcentrationAuraAction(botAI); }
static Action* holy_wrath(PlayerbotAI* botAI) { return new CastHolyWrathAction(botAI); } static Action* holy_wrath(PlayerbotAI* botAI) { return new CastHolyWrathAction(botAI); }
static Action* consecration(PlayerbotAI* botAI) { return new CastConsecrationAction(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_poison(PlayerbotAI* botAI) { return new CastCleansePoisonAction(botAI); }
static Action* cleanse_disease(PlayerbotAI* botAI) { return new CastCleanseDiseaseAction(botAI); } static Action* cleanse_disease(PlayerbotAI* botAI) { return new CastCleanseDiseaseAction(botAI); }
static Action* cleanse_magic(PlayerbotAI* botAI) { return new CastCleanseMagicAction(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( triggers.push_back(new TriggerNode(
"light aoe", NextAction::array(0, new NextAction("avenger's shield", ACTION_HIGH + 5), nullptr))); "light aoe", NextAction::array(0, new NextAction("avenger's shield", ACTION_HIGH + 5), nullptr)));
triggers.push_back( 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))); 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", // triggers.push_back(new TriggerNode("avenger's shield", NextAction::array(0, new NextAction("avenger's shield",
// ACTION_HIGH + 7), nullptr))); // ACTION_HIGH + 7), nullptr)));
@ -107,7 +107,7 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode("medium group heal occasion", new TriggerNode("medium group heal occasion",
NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr))); NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode( 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", triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr))); NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode( 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("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 // 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))); // 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", 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 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("elune's grace", ACTION_EMERGENCY), nullptr))); triggers.push_back(new TriggerNode("chastise",
// NextAction::array(0, new NextAction("chastise", ACTION_INTERRUPT), nullptr))); // NextAction::array(0, new NextAction("chastise", ACTION_INTERRUPT), nullptr)));
triggers.push_back(new TriggerNode( triggers.push_back(new TriggerNode(

View File

@ -73,7 +73,9 @@ void HolyHealPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode( triggers.push_back(new TriggerNode(
"party member critical health", "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("flash heal on party", ACTION_CRITICAL_HEAL + 3),
new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), NULL))); 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") {} CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
}; };
class CastGuardianSpiritOnPartyAction : public HealPartyMemberAction
{
public:
CastGuardianSpiritOnPartyAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM) {}
};
#endif #endif

View File

@ -234,6 +234,7 @@ public:
creators["hymn of hope"] = &PriestAiObjectContextInternal::hymn_of_hope; creators["hymn of hope"] = &PriestAiObjectContextInternal::hymn_of_hope;
creators["divine hymn"] = &PriestAiObjectContextInternal::divine_hymn; creators["divine hymn"] = &PriestAiObjectContextInternal::divine_hymn;
creators["mind sear"] = &PriestAiObjectContextInternal::mind_sear; creators["mind sear"] = &PriestAiObjectContextInternal::mind_sear;
creators["guardian spirit on party"] = &PriestAiObjectContextInternal::guardian_spirit_on_party;
} }
private: private:
@ -339,6 +340,7 @@ private:
static Action* hymn_of_hope(PlayerbotAI* ai) { return new CastHymnOfHopeAction(ai); } static Action* hymn_of_hope(PlayerbotAI* ai) { return new CastHymnOfHopeAction(ai); }
static Action* divine_hymn(PlayerbotAI* ai) { return new CastDivineHymnAction(ai); } static Action* divine_hymn(PlayerbotAI* ai) { return new CastDivineHymnAction(ai); }
static Action* mind_sear(PlayerbotAI* ai) { return new CastMindSearAction(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) 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); CURE_PARTY_TRIGGER(PartyMemberCureDiseaseTrigger, "cure disease", DISPEL_DISEASE);
BUFF_TRIGGER_A(InnerFireTrigger, "inner fire"); BUFF_TRIGGER_A(InnerFireTrigger, "inner fire");
BUFF_TRIGGER_A(ShadowformTrigger, "shadowform"); BUFF_TRIGGER_A(ShadowformTrigger, "shadowform");
BUFF_TRIGGER(PowerInfusionTrigger, "power infusion"); BOOST_TRIGGER(PowerInfusionTrigger, "power infusion");
BUFF_TRIGGER(InnerFocusTrigger, "inner focus"); BUFF_TRIGGER(InnerFocusTrigger, "inner focus");
BUFF_TRIGGER(ShadowProtectionTrigger, "shadow protection"); BUFF_TRIGGER(ShadowProtectionTrigger, "shadow protection");
BUFF_PARTY_TRIGGER(ShadowProtectionOnPartyTrigger, "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))); // ACTION_MOVE + 9), nullptr)));
triggers.push_back( triggers.push_back(
new TriggerNode("shadowform", NextAction::array(0, new NextAction("shadowform", ACTION_HIGH), nullptr))); 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 triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("dispersion", ACTION_HIGH
// + 5), nullptr))); + 5), nullptr)));
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("dispersion", ACTION_HIGH
+ 5), nullptr)));
triggers.push_back( triggers.push_back(
new TriggerNode("vampiric embrace", NextAction::array(0, new NextAction("vampiric embrace", 16.0f), nullptr))); new TriggerNode("vampiric embrace", NextAction::array(0, new NextAction("vampiric embrace", 16.0f), nullptr)));
triggers.push_back( triggers.push_back(

View File

@ -1,6 +1,7 @@
#include "RaidNaxxActions.h" #include "RaidNaxxActions.h"
#include "LastMovementValue.h"
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
@ -23,7 +24,7 @@ bool GrobbulusGoBehindAction::Execute(Event event)
float z = boss->GetPositionZ(); float z = boss->GetPositionZ();
float rx = x + cos(orientation) * distance; float rx = x + cos(orientation) * distance;
float ry = y + sin(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() uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint()
@ -91,7 +92,7 @@ bool HeiganDanceMeleeAction::Execute(Event event)
} }
assert(curr_safe >= 0 && curr_safe <= 3); assert(curr_safe >= 0 && curr_safe <= 3);
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 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) bool HeiganDanceRangedAction::Execute(Event event)
@ -99,10 +100,10 @@ bool HeiganDanceRangedAction::Execute(Event event)
CalculateSafe(); CalculateSafe();
if (prev_phase != 1) 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(); 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() bool ThaddiusAttackNearestPetAction::isUseful()
@ -128,7 +129,7 @@ bool ThaddiusAttackNearestPetAction::Execute(Event event)
Unit* target = helper.GetNearestPet(); Unit* target = helper.GetNearestPet();
if (!bot->IsWithinLOSInMap(target)) if (!bot->IsWithinLOSInMap(target))
{ {
return MoveTo(target); return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
} }
if (AI_VALUE(Unit*, "current target") != target) 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")) if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target"))
{ {
std::pair<float, float> posForTank = helper.PetPhaseGetPosForTank(); 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)) if (botAI->IsRanged(bot))
{ {
std::pair<float, float> posForRanged = helper.PetPhaseGetPosForRanged(); 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; return false;
} }
@ -170,22 +171,28 @@ bool ThaddiusMoveToPlatformAction::Execute(Event event)
{ {
if (is_left) 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 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 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; return true;
} }
@ -225,7 +232,7 @@ bool ThaddiusMovePolarityAction::Execute(Event event)
idx = 2; idx = 2;
} }
idx = idx * 2 + botAI->IsRanged(bot); 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) bool RazuviousUseObedienceCrystalAction::Execute(Event event)
@ -322,7 +329,7 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event)
{ {
continue; continue;
} }
if (MoveTo(unit)) if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT))
{ {
return true; return true;
} }
@ -352,7 +359,7 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event)
{ {
if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance) if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance)
{ {
return MoveNear(target, sPlayerbotAIConfig->spellDistance); return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT);
} }
else else
{ {
@ -396,7 +403,7 @@ bool HorsemanAttractAlternativelyAction::Execute(Event event)
} }
helper.CalculatePosToGo(bot); helper.CalculatePosToGo(bot);
auto [posX, posY] = helper.CurrentAttractPos(); 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; return true;
} }
@ -444,7 +451,7 @@ bool HorsemanAttactInOrderAction::Execute(Event event)
} }
if (!bot->IsWithinLOSInMap(target)) if (!bot->IsWithinLOSInMap(target))
{ {
return MoveNear(target, 22.0f); return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT);
} }
return Attack(target); return Attack(target);
} }
@ -461,7 +468,7 @@ bool SapphironGroundPositionAction::Execute(Event event)
{ {
if (AI_VALUE2(bool, "has aggro", "current target")) 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; return false;
} }
@ -485,14 +492,14 @@ bool SapphironGroundPositionAction::Execute(Event event)
distance = 5.0f; distance = 5.0f;
} }
return MoveTo(NAXX_MAP_ID, helper.center.first + cos(angle) * distance, 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 else
{ {
std::vector<float> dest; std::vector<float> dest;
if (helper.FindPosToAvoidChill(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; return false;
@ -513,7 +520,7 @@ bool SapphironFlightPositionAction::Execute(Event event)
std::vector<float> dest; std::vector<float> dest;
if (helper.FindPosToAvoidChill(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; return false;
@ -548,7 +555,7 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
{ {
float angle = boss->GetAngle(playerWithIcebolt); float angle = boss->GetAngle(playerWithIcebolt);
return MoveTo(NAXX_MAP_ID, playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f, 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; return false;
@ -679,7 +686,7 @@ bool KelthuzadPositionAction::Execute(Event event)
{ {
if (AI_VALUE(Unit*, "current target") == nullptr) 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()) else if (helper.IsPhaseTwo())
@ -692,7 +699,7 @@ bool KelthuzadPositionAction::Execute(Event event)
{ {
if (AI_VALUE2(bool, "has aggro", "current target")) 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 else
{ {
@ -715,7 +722,7 @@ bool KelthuzadPositionAction::Execute(Event event)
float dx, dy; float dx, dy;
dx = helper.center.first + cos(angle) * distance; dx = helper.center.first + cos(angle) * distance;
dy = helper.center.second + sin(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)) else if (botAI->IsTank(bot))
{ {
@ -725,7 +732,7 @@ bool KelthuzadPositionAction::Execute(Event event)
botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer())) botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer()))
{ {
return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, 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 else
{ {
@ -747,7 +754,7 @@ bool KelthuzadPositionAction::Execute(Event event)
} }
dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f; dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f;
dy = shadow_fissure->GetPositionY() + sin(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; return false;
@ -846,11 +853,11 @@ bool AnubrekhanPositionAction::Execute(Event event)
next_point = nearest; next_point = nearest;
} }
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
bot->GetPositionZ()); bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
} }
else 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; return false;
@ -964,12 +971,12 @@ bool GluthPositionAction::Execute(Event event)
if (raid25) if (raid25)
{ {
return MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, return MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second,
bot->GetPositionZ()); bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
} }
else else
{ {
return MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, 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()) if (helper.BeforeDecimate())
{ {
return MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, return MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second,
bot->GetPositionZ()); bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
} }
else else
{ {
@ -987,7 +994,7 @@ bool GluthPositionAction::Execute(Event event)
uint32 nearest = FindNearestWaypoint(); uint32 nearest = FindNearestWaypoint();
uint32 next_point = (nearest + 1) % intervals; uint32 next_point = (nearest + 1) % intervals;
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, 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) if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0)
{ {
return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, 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) if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
{ {
return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, 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)) 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; return false;
} }
@ -1051,12 +1058,12 @@ bool LoathebPositionAction::Execute(Event event)
{ {
if (AI_VALUE2(bool, "has aggro", "boss target")) 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)) 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; 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))); new TriggerNode("medium threat", NextAction::array(0, new NextAction("vanish", ACTION_HIGH), NULL)));
triggers.push_back( triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY), new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_HIGH + 9),
new NextAction("feint", ACTION_EMERGENCY), NULL))); 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( triggers.push_back(
new TriggerNode("kick", NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), NULL))); 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))); new TriggerNode("medium threat", NextAction::array(0, new NextAction("vanish", ACTION_HIGH), nullptr)));
triggers.push_back( triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY), new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_HIGH + 9),
new NextAction("feint", ACTION_EMERGENCY), nullptr))); 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( triggers.push_back(
new TriggerNode("kick", NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), nullptr))); 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( triggers.push_back(
new TriggerNode("light aoe", NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH + 3), nullptr))); 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( triggers.push_back(new TriggerNode(
"enemy out of melee", "enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8), 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") {} CastEvasionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "evasion") {}
}; };
class CastCloakOfShadowsAction : public CastBuffSpellAction
{
public:
CastCloakOfShadowsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cloak of shadows") {}
};
class CastHungerForBloodAction : public CastBuffSpellAction class CastHungerForBloodAction : public CastBuffSpellAction
{ {
public: public:

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@
#include "ShamanActions.h" #include "ShamanActions.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "Totem.h"
bool CastTotemAction::isUseful() bool CastTotemAction::isUseful()
{ {
@ -41,7 +42,24 @@ bool CastSearingTotemAction::isUseful()
return CastTotemAction::isUseful() && !AI_VALUE2(bool, "has totem", "flametongue totem"); 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() bool CastCleansingTotemAction::isUseful()
{ {

View File

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

View File

@ -308,7 +308,23 @@ std::string const TwoTriggers::getName()
return name; 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; } bool ItemCountTrigger::IsActive() { return AI_VALUE2(uint32, "item count", item) < count; }

View File

@ -409,6 +409,21 @@ protected:
float balance; 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 class RandomTrigger : public Trigger
{ {
public: public:

View File

@ -48,7 +48,7 @@ class LowHealthTrigger : public HealthInRangeTrigger
{ {
public: public:
LowHealthTrigger(PlayerbotAI* botAI, std::string const name = "low health", 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) : HealthInRangeTrigger(botAI, name, value, minValue)
{ {
} }
@ -69,7 +69,7 @@ class MediumHealthTrigger : public LowHealthTrigger
{ {
public: public:
MediumHealthTrigger(PlayerbotAI* botAI) 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: public:
PartyMemberLowHealthTrigger(PlayerbotAI* botAI, std::string const name = "party member low health", PartyMemberLowHealthTrigger(PlayerbotAI* botAI, std::string const name = "party member low health",
float value = sPlayerbotAIConfig->lowHealth, float value = sPlayerbotAIConfig->lowHealth,
float minValue = sPlayerbotAIConfig->criticalHealth) float minValue = 0)
: HealthInRangeTrigger(botAI, name, value, minValue) : HealthInRangeTrigger(botAI, name, value, minValue)
{ {
} }
@ -111,7 +111,7 @@ class PartyMemberMediumHealthTrigger : public PartyMemberLowHealthTrigger
public: public:
PartyMemberMediumHealthTrigger(PlayerbotAI* botAI) PartyMemberMediumHealthTrigger(PlayerbotAI* botAI)
: PartyMemberLowHealthTrigger(botAI, "party member medium health", sPlayerbotAIConfig->mediumHealth, : PartyMemberLowHealthTrigger(botAI, "party member medium health", sPlayerbotAIConfig->mediumHealth,
sPlayerbotAIConfig->lowHealth) 0)
{ {
} }
}; };
@ -121,7 +121,7 @@ class PartyMemberAlmostFullHealthTrigger : public PartyMemberLowHealthTrigger
public: public:
PartyMemberAlmostFullHealthTrigger(PlayerbotAI* botAI) PartyMemberAlmostFullHealthTrigger(PlayerbotAI* botAI)
: PartyMemberLowHealthTrigger(botAI, "party member almost full health", sPlayerbotAIConfig->almostFullHealth, : 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 medium health"] = &TriggerContext::PartyMemberMediumHealth;
creators["party member almost full health"] = &TriggerContext::PartyMemberAlmostFullHealth; creators["party member almost full health"] = &TriggerContext::PartyMemberAlmostFullHealth;
creators["generic boost"] = &TriggerContext::generic_boost;
creators["protect party member"] = &TriggerContext::protect_party_member; creators["protect party member"] = &TriggerContext::protect_party_member;
creators["light rage available"] = &TriggerContext::LightRageAvailable; creators["light rage available"] = &TriggerContext::LightRageAvailable;
@ -318,6 +320,7 @@ private:
{ {
return new PartyMemberAlmostFullHealthTrigger(botAI); return new PartyMemberAlmostFullHealthTrigger(botAI);
} }
static Trigger* generic_boost(PlayerbotAI* botAI) { return new GenericBoostTrigger(botAI); }
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI) static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
{ {
return new PartyMemberCriticalHealthTrigger(botAI); return new PartyMemberCriticalHealthTrigger(botAI);

View File

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

View File

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

View File

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

View File

@ -13,6 +13,15 @@
class PlayerbotAI; class PlayerbotAI;
class Unit; 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 class LastMovement
{ {
public: public:
@ -28,14 +37,14 @@ public:
lastMoveShort = other.lastMoveShort; lastMoveShort = other.lastMoveShort;
lastPath = other.lastPath; lastPath = other.lastPath;
nextTeleport = other.nextTeleport; nextTeleport = other.nextTeleport;
priority = other.priority;
return *this; return *this;
}; };
void clear(); void clear();
void Set(Unit* follow); 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 setShort(WorldPosition point);
void setPath(TravelPath path); void setPath(TravelPath path);
@ -53,6 +62,7 @@ public:
float lastdelayTime; float lastdelayTime;
WorldPosition lastMoveShort; WorldPosition lastMoveShort;
uint32 msTime; uint32 msTime;
MovementPriority priority;
TravelPath lastPath; TravelPath lastPath;
time_t nextTeleport; time_t nextTeleport;
std::future<TravelPath> future; std::future<TravelPath> future;

View File

@ -41,6 +41,8 @@ Unit* PartyMemberToHeal::Calculate()
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{ {
Player* player = gref->GetSource(); Player* player = gref->GetSource();
if (player->IsGameMaster())
continue;
if (player && player->IsAlive()) if (player && player->IsAlive())
{ {
uint8 health = player->GetHealthPct(); 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, // return player && player != bot && player->GetMapId() == bot->GetMapId() && bot->IsWithinDistInMap(player,
// sPlayerbotAIConfig->sightDistance, false); // 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->GetDistance(player) < sPlayerbotAIConfig->spellDistance * 2 &&
bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
} }

View File

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