Compare commits

..

1 Commits

Author SHA1 Message Date
bashermens
5c9451b73f Quickfix, XP gain feature has huge performance impact 2025-12-25 02:00:40 +01:00
263 changed files with 7823 additions and 8677 deletions

View File

@@ -25,7 +25,6 @@
# CHEATS
# SPELLS
# FLIGHTPATH
# PROFESSIONS
# RANDOMBOT-SPECIFIC SETTINGS
# GENERAL
# LEVELS
@@ -45,7 +44,7 @@
# HUNTER
# ROGUE
# PRIEST
# DEATH KNIGHT
# DEATHKNIGHT
# SHAMAN
# MAGE
# WARLOCK
@@ -56,7 +55,7 @@
# HUNTER
# ROGUE
# PRIEST
# DEATH KNIGHT
# DEATHKNIGHT
# SHAMAN
# MAGE
# WARLOCK
@@ -580,24 +579,6 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
#
####################################################################################################
####################################################################################################
# PROFESSIONS
# Random bots currently do not get professions.
#
# EnableFishingWithMaster automatically adds the 'master fishing' strategy to bots that can fish that can.
# Default: 1 (Enabled)
AiPlayerbot.EnableFishingWithMaster = 1
#FishingDistance sets how far a bot without a master will search for water, while FishingDistanceFromMaster limits it to a closer range, and overrides the following distance to the same value. EndFishingWithMaster sets the distance from water a bot needs to have to automatically drop the 'master fishing' strategy.
AiPlayerbot.FishingDistanceFromMaster = 10.0
AiPlayerbot.FishingDistance = 40.0
AiPlayerbot.EndFishingWithMaster = 30.0
#
#
#
####################################################################################################
#######################################
# #
# RANDOMBOT-SPECIFIC SETTINGS #
@@ -1474,7 +1455,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
####################################################################################################
####################################################################################################
# DEATH KNIGHT
# DEATHKNIGHT
#
#
@@ -1797,7 +1778,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
####################################################################################################
####################################################################################################
# DEATH KNIGHT
# DEATHKNIGHT
#
#

View File

@@ -1,15 +0,0 @@
DELETE FROM ai_playerbot_texts WHERE name IN ('no_fishing_pole_error');
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('no_fishing_pole_error');
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
(1736, 'no_fishing_pole_error', "I don't have a Fishing Pole", 0, 0,
"낚싯대가 없습니다",
"Je nai pas de canne à pêche",
"Ich habe keine Angelrute",
"我沒有釣魚竿",
"我没有钓鱼竿",
"No tengo una caña de pescar",
"No tengo una caña de pescar",
"У меня нет удочки");
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('no_fishing_pole_error', 100);

View File

@@ -140,37 +140,37 @@ BotRoles AiFactory::GetPlayerRoles(Player* player)
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
role = BOT_ROLE_DPS;
else
role = BOT_ROLE_HEALER;
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_RESTORATION)
if (tab == 2)
role = BOT_ROLE_HEALER;
else
role = BOT_ROLE_DPS;
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
role = BOT_ROLE_TANK;
else
role = BOT_ROLE_DPS;
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_HOLY)
if (tab == 0)
role = BOT_ROLE_HEALER;
else if (tab == PALADIN_TAB_PROTECTION)
else if (tab == 1)
role = BOT_ROLE_TANK;
else if (tab == PALADIN_TAB_RETRIBUTION)
else if (tab == 2)
role = BOT_ROLE_DPS;
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
role = BOT_ROLE_DPS;
else if (tab == DRUID_TAB_FERAL)
else if (tab == 1)
role = (BotRoles)(BOT_ROLE_TANK | BOT_ROLE_DPS);
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
role = BOT_ROLE_HEALER;
break;
default:
@@ -188,83 +188,84 @@ std::string AiFactory::GetPlayerSpecName(Player* player)
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
specName = "shadow";
else if (tab == PRIEST_TAB_HOLY)
else if (tab == 1)
specName = "holy";
else
specName = "disc";
;
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_RESTORATION)
if (tab == 2)
specName = "resto";
else if (tab == SHAMAN_TAB_ENHANCEMENT)
else if (tab == 1)
specName = "enhance";
else
specName = "elem";
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
specName = "prot";
else if (tab == WARRIOR_TAB_FURY)
else if (tab == 1)
specName = "fury";
else
specName = "arms";
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_HOLY)
if (tab == 0)
specName = "holy";
else if (tab == PALADIN_TAB_PROTECTION)
else if (tab == 1)
specName = "prot";
else if (tab == PALADIN_TAB_RETRIBUTION)
else if (tab == 2)
specName = "retrib";
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
specName = "balance";
else if (tab == DRUID_TAB_FERAL)
else if (tab == 1)
specName = "feraldps";
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
specName = "resto";
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION)
if (tab == 0)
specName = "assas";
else if (tab == ROGUE_TAB_COMBAT)
else if (tab == 1)
specName = "combat";
else if (tab == ROGUE_TAB_SUBTLETY)
else if (tab == 2)
specName = "subtle";
break;
case CLASS_HUNTER:
if (tab == HUNTER_TAB_BEAST_MASTERY)
if (tab == 0)
specName = "beast";
else if (tab == HUNTER_TAB_MARKSMANSHIP)
else if (tab == 1)
specName = "marks";
else if (tab == HUNTER_TAB_SURVIVAL)
else if (tab == 2)
specName = "surv";
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
specName = "blooddps";
else if (tab == DEATH_KNIGHT_TAB_FROST)
else if (tab == 1)
specName = "frostdps";
else if (tab == DEATH_KNIGHT_TAB_UNHOLY)
else if (tab == 2)
specName = "unholydps";
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE)
if (tab == 0)
specName = "arcane";
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1)
specName = "fire";
else if (tab == MAGE_TAB_FROST)
else if (tab == 2)
specName = "frost";
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
if (tab == 0)
specName = "afflic";
else if (tab == WARLOCK_TAB_DEMONOLOGY)
else if (tab == 1)
specName = "demo";
else if (tab == WARLOCK_TAB_DESTRUCTION)
else if (tab == 2)
specName = "destro";
break;
default:
@@ -279,124 +280,147 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
uint8 tab = GetPlayerSpecTab(player);
if (!player->InBattleground())
{
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr);
}
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
{
engine->addStrategy("avoid aoe", false);
}
engine->addStrategy("formation", false);
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
{
engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr);
}
else if (tab == PRIEST_TAB_DISCIPLINE)
{
engine->addStrategiesNoInit("heal", nullptr);
}
else
{
engine->addStrategiesNoInit("holy heal", nullptr);
}
engine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE)
if (tab == 0) // Arcane
engine->addStrategiesNoInit("arcane", nullptr);
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1) // Fire
{
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
{
engine->addStrategiesNoInit("frostfire", nullptr);
}
else
{
engine->addStrategiesNoInit("fire", nullptr);
}
}
else
else // Frost
engine->addStrategiesNoInit("frost", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
else if (tab == 0 || !player->HasSpell(1680)) // Whirlwind
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
else
engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr);
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_ELEMENTAL)
if (tab == 0) // Elemental
engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr);
else if (tab == SHAMAN_TAB_RESTORATION)
else if (tab == 2) // Restoration
engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr);
else
else // Enhancement
engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr);
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_PROTECTION)
if (tab == 1)
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
else if (tab == PALADIN_TAB_HOLY)
else if (tab == 0)
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
else
engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr);
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
{
engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr);
engine->addStrategy("caster debuff", false);
}
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr);
else
{
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
if (player->HasSpell(768) /*cat form*/&& !player->HasAura(16931) /*thick hide*/)
{
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
}
else
{
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
}
}
break;
case CLASS_HUNTER:
if (tab == HUNTER_TAB_BEAST_MASTERY)
if (tab == 0) // Beast Mastery
engine->addStrategiesNoInit("bm", nullptr);
else if (tab == HUNTER_TAB_MARKSMANSHIP)
else if (tab == 1) // Marksmanship
engine->addStrategiesNoInit("mm", nullptr);
else
else if (tab == 2) // Survival
engine->addStrategiesNoInit("surv", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
{
engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr);
}
else
{
engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr);
}
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
if (tab == 0) // Affliction
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
else if (tab == WARLOCK_TAB_DEMONOLOGY)
else if (tab == 1) // Demonology
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
else
else if (tab == 2) // Destruction
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
else if (tab == DEATH_KNIGHT_TAB_FROST)
else if (tab == 1)
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
else
engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr);
break;
}
if (PlayerbotAI::IsTank(player, true))
{
engine->addStrategy("tank face", false);
}
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true))
{
engine->addStrategy("behind", false);
}
if (PlayerbotAI::IsHeal(player, true))
{
if (sPlayerbotAIConfig->autoSaveMana)
@@ -404,7 +428,6 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId()))
engine->addStrategy("healer dps", false);
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
if (!player->GetGroup())
@@ -413,13 +436,15 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
engine->addStrategy("boost", false);
engine->addStrategy("dps assist", false);
engine->removeStrategy("threat", false);
// engine-
switch (player->getClass())
{
case CLASS_PRIEST:
{
if (tab != PRIEST_TAB_SHADOW)
{
engine->addStrategiesNoInit("holy dps", "shadow debuff", "shadow aoe", nullptr);
}
break;
}
case CLASS_DRUID:
@@ -434,13 +459,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
case CLASS_SHAMAN:
{
if (tab == SHAMAN_TAB_RESTORATION)
{
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
}
break;
}
case CLASS_PALADIN:
{
if (tab == PALADIN_TAB_HOLY)
{
engine->addStrategiesNoInit("dps", "dps assist", "baoe", nullptr);
}
break;
}
default:
@@ -449,9 +478,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
}
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
}
else
{
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
}
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
@@ -478,15 +511,23 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (player->InArena())
{
engine->addStrategy("arena", false);
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "cast time", "dps assist", nullptr);
}
else
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist", nullptr);
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist",
nullptr);
engine->removeStrategy("custom::say", false);
engine->removeStrategy("flee", false);
engine->removeStrategy("threat", false);
engine->addStrategy("boost", false);
// if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
// engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
// if (player->getClass() == CLASS_DRUID && tab == 1)
// engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
// if (player->getClass() == CLASS_ROGUE)
// engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
}
}
@@ -508,15 +549,19 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_PROTECTION)
if (tab == 1)
{
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20)
{
nonCombatEngine->addStrategy("bhealth", false);
}
else
{
nonCombatEngine->addStrategy("bdps", false);
}
}
else if (tab == PALADIN_TAB_HOLY)
else if (tab == 0)
nonCombatEngine->addStrategiesNoInit("dps assist", "bmana", "bcast", nullptr);
else
nonCombatEngine->addStrategiesNoInit("dps assist", "bdps", "baoe", nullptr);
@@ -527,7 +572,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr);
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
if (tab == 0 || tab == 2)
nonCombatEngine->addStrategy("bmana", false);
else
nonCombatEngine->addStrategy("bdps", false);
@@ -543,34 +588,43 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_FERAL)
if (tab == 1)
{
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
{
nonCombatEngine->addStrategy("dps assist", false);
}
else
{
nonCombatEngine->addStrategy("tank assist", false);
}
}
else
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist", false);
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
{
nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DEMONOLOGY)
{
nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DESTRUCTION)
{
nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr);
}
nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist", false);
@@ -587,7 +641,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
}
if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true))
{
nonCombatEngine->addStrategy("save mana", false);
}
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{
@@ -613,14 +669,18 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategy("grind", false);
if (sPlayerbotAIConfig->enableNewRpgStrategy)
{
nonCombatEngine->addStrategy("new rpg", false);
}
else if (sPlayerbotAIConfig->autoDoQuests)
{
// nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg", false);
}
else
{
nonCombatEngine->addStrategy("move random", false);
}
if (sPlayerbotAIConfig->randomBotJoinBG)
nonCombatEngine->addStrategy("bg", false);
@@ -669,8 +729,11 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
}
}
else
{
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
// nonCombatEngine->addStrategy("battleground");
// nonCombatEngine->addStrategy("warsong");
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
@@ -727,7 +790,9 @@ void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const faca
deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr);
if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup())
{
deadEngine->removeStrategy("follow", false);
}
}
Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext)

View File

@@ -43,7 +43,6 @@
#include "PlayerbotAIConfig.h"
#include "PlayerbotDbStore.h"
#include "PlayerbotMgr.h"
#include "PlayerbotGuildMgr.h"
#include "Playerbots.h"
#include "PointMovementGenerator.h"
#include "PositionValue.h"
@@ -437,7 +436,7 @@ void PlayerbotAI::UpdateAIGroupMaster()
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
{
if (!bot || bot->IsBeingTeleported() || !bot->IsInWorld())
if (bot->IsBeingTeleported() || !bot->IsInWorld())
return;
std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I";
@@ -516,37 +515,23 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal
void PlayerbotAI::HandleCommands()
{
ExternalEventHelper helper(aiObjectContext);
for (auto it = chatCommands.begin(); it != chatCommands.end();)
{
time_t& checkTime = it->GetTime();
if (checkTime && time(nullptr) < checkTime)
if (checkTime && time(0) < checkTime)
{
++it;
continue;
}
Player* owner = it->GetOwner();
if (!owner)
{
it = chatCommands.erase(it);
continue;
}
const std::string& command = it->GetCommand();
if (command.empty())
{
it = chatCommands.erase(it);
continue;
}
Player* owner = it->GetOwner();
if (!helper.ParseChatCommand(command, owner) && it->GetType() == CHAT_MSG_WHISPER)
{
// ostringstream out; out << "Unknown command " << command;
// TellPlayer(out);
// helper.ParseChatCommand("help");
}
it = chatCommands.erase(it);
}
}
@@ -554,9 +539,6 @@ void PlayerbotAI::HandleCommands()
std::map<std::string, ChatMsg> chatMap;
void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang)
{
if (!bot)
return;
std::string filtered = text;
if (!IsAllowedCommand(filtered) && !GetSecurity()->CheckLevelFor(PlayerbotSecurityLevel::PLAYERBOT_SECURITY_INVITE,
@@ -728,82 +710,65 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr
void PlayerbotAI::HandleTeleportAck()
{
if (!bot || !bot->GetSession())
return;
// only for bots
if (IsRealPlayer())
return;
/*
* FAR TELEPORT (worldport / map change)
* Player may NOT be in world or grid here.
* Handle this FIRST.
*/
if (bot->IsBeingTeleportedFar())
{
bot->GetSession()->HandleMoveWorldportAck();
// Clearing motion generators and stopping movement which prevents
// conflicts between teleport and any active motion (walk, run, swim, flight, etc.)
bot->GetMotionMaster()->Clear(true);
bot->StopMoving();
// after worldport ACK the player should be in a valid map
if (!bot->GetMap())
{
LOG_ERROR("playerbot", "Bot {} has no map after worldport ACK", bot->GetGUID().ToString());
return;
}
// apply instance-related strategies after map attach
if (sPlayerbotAIConfig->applyInstanceStrategies)
ApplyInstanceStrategies(bot->GetMapId(), true);
if (sPlayerbotAIConfig->restrictHealerDPS)
EvaluateHealerDpsStrategy();
// reset AI state after teleport
Reset(true);
// clear movement only AFTER teleport is finalized and bot is in world
if (bot->IsInWorld() && bot->GetMotionMaster())
{
bot->GetMotionMaster()->Clear(true);
bot->StopMoving();
}
// simulate far teleport latency (cmangos-style)
SetNextCheckDelay(urand(2000, 5000));
return;
}
/*
* NEAR TELEPORT (same map / instance)
* Player MUST be in world (and in grid).
*/
// Near teleport (within map/instance)
if (bot->IsBeingTeleportedNear())
{
if (!bot->IsInWorld())
return;
// Previous versions manually added the bot to the map if it was not in the world.
// not needed: HandleMoveTeleportAckOpcode() safely attaches the player to the map
// and clears IsBeingTeleportedNear().
Player* plMover = bot->m_mover ? bot->m_mover->ToPlayer() : nullptr;
Player* plMover = bot->m_mover->ToPlayer();
if (!plMover)
return;
// Send the near teleport ACK packet
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
p << plMover->GetPackGUID();
p << uint32(0); // flags
p << uint32(0); // time
p << uint32(0);
p << uint32(0);
bot->GetSession()->HandleMoveTeleportAck(p);
// clear movement after successful relocation
if (bot->GetMotionMaster())
// Simulate teleport latency and prevent AI from running too early (used cmangos delays)
SetNextCheckDelay(urand(1000, 2000));
}
// Far teleport (worldport / different map)
if (bot->IsBeingTeleportedFar())
{
// Handle far teleport ACK:
// Moves the bot to the new map, clears IsBeingTeleportedFar(), updates session/map references
bot->GetSession()->HandleMoveWorldportAck();
// Ensure bot now has a valid map. If this fails, there is a core/session bug?
if (!bot->GetMap())
{
bot->GetMotionMaster()->Clear(true);
bot->StopMoving();
LOG_ERROR("playerbot", "Bot {} has no map after worldport ACK", bot->GetGUID().ToString());
}
// simulate near teleport latency
SetNextCheckDelay(urand(1000, 2000));
return;
// Instance strategies after teleport
if (sPlayerbotAIConfig->applyInstanceStrategies)
ApplyInstanceStrategies(bot->GetMapId(), true);
// healer DPS strategies if restrictions are enabled
if (sPlayerbotAIConfig->restrictHealerDPS)
EvaluateHealerDpsStrategy();
// Reset AI state to to before teleport conditions
Reset(true);
// Slightly longer delay to simulate far teleport latency (used cmangos delays)
SetNextCheckDelay(urand(2000, 5000));
}
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
}
void PlayerbotAI::Reset(bool full)
@@ -965,6 +930,7 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
fromPlayer->SendDirectMessage(&data);
return;
}
if (!IsAllowedCommand(filtered) &&
(!GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer)))
return;
@@ -1320,12 +1286,7 @@ void PlayerbotAI::SpellInterrupted(uint32 spellid)
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
if (!spell)
continue;
SpellInfo const* spellInfo = spell->GetSpellInfo();
if (!spellInfo)
continue;
if (spellInfo->Id == spellid)
if (spell->GetSpellInfo()->Id == spellid)
bot->InterruptSpell((CurrentSpellTypes)type);
}
// LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
@@ -1426,8 +1387,8 @@ void PlayerbotAI::DoNextAction(bool min)
return;
}
// Change engine if just ressed (no movement update when rooted)
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive && !bot->IsRooted())
// Change engine if just ressed
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive)
{
bot->SendMovementFlagUpdate();
@@ -1543,6 +1504,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 532:
strategyName = "karazhan"; // Karazhan
break;
case 533:
strategyName = "naxx"; // Naxxramas
break;
case 544:
strategyName = "magtheridon"; // Magtheridon's Lair
break;
@@ -1767,7 +1731,6 @@ bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
}
break;
}
return true;
}
@@ -1861,9 +1824,10 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
bool PlayerbotAI::HasAggro(Unit* unit)
{
if (!IsValidUnit(unit))
if (!unit)
{
return false;
}
bool isMT = IsMainTank(bot);
Unit* victim = unit->GetVictim();
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer()))))
@@ -2071,7 +2035,7 @@ bool PlayerbotAI::IsTank(Player* player, bool bySpec)
switch (player->getClass())
{
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == DEATHKNIGHT_TAB_BLOOD)
{
return true;
}
@@ -2179,7 +2143,7 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
}
break;
case CLASS_DEATH_KNIGHT:
if (tab != DEATH_KNIGHT_TAB_BLOOD)
if (tab != DEATHKNIGHT_TAB_BLOOD)
{
return true;
}
@@ -2839,12 +2803,7 @@ bool PlayerbotAI::TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel
bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel)
{
if (!master)
{
if (sPlayerbotAIConfig->randomBotSayWithoutMaster)
return TellMasterNoFacing(text, securityLevel);
}
if (!TellMasterNoFacing(text, securityLevel))
if (!master || !TellMasterNoFacing(text, securityLevel))
return false;
if (!bot->isMoving() && !bot->IsInCombat() && bot->GetMapId() == master->GetMapId() &&
@@ -2861,9 +2820,6 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu
bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
{
if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
return false;
if (!aurEff)
return false;
@@ -2871,8 +2827,6 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
return true;
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
if (!spellInfo)
return false;
uint32 stacks = aurEff->GetBase()->GetStackAmount();
if (stacks >= spellInfo->StackAmount)
@@ -2888,7 +2842,7 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, bool checkIsOwner, int maxAuraAmount,
bool checkDuration)
{
if (!IsValidUnit(unit))
if (!unit)
return false;
std::wstring wnamepart;
@@ -2984,7 +2938,7 @@ bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit)
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
{
if (!IsValidUnit(unit))
if (!unit)
return nullptr;
std::wstring wnamepart;
@@ -3002,9 +2956,6 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
for (AuraEffect const* aurEff : auras)
{
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
if (!spellInfo)
continue;
std::string const& auraName = spellInfo->SpellName[0];
// Directly skip if name mismatch (both length and content)
@@ -3085,9 +3036,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
if (!target)
target = bot;
if (!IsValidUnit(target))
return false;
if (Pet* pet = bot->GetPet())
if (pet->HasSpell(spellid))
return true;
@@ -3349,9 +3297,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool c
bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarget)
{
if (!IsValidUnit(target))
return false;
bool result = CastSpell(aiObjectContext->GetValue<uint32>("spell id", name)->Get(), target, itemTarget);
if (result)
{
@@ -3364,19 +3309,15 @@ bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarg
bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
{
if (!spellId)
{
return false;
}
if (!target)
target = bot;
if (!IsValidUnit(target))
return false;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
return false;
Pet* pet = bot->GetPet();
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (pet && pet->HasSpell(spellId))
{
// List of spell IDs for which we do NOT want to toggle auto-cast or send message
@@ -3779,9 +3720,6 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
if (!spellId)
return false;
if (!IsValidUnit(target))
return false;
Vehicle* vehicle = bot->GetVehicle();
if (!vehicle)
return false;
@@ -3792,12 +3730,12 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
return false;
Unit* vehicleBase = vehicle->GetBase();
Unit* spellTarget = target;
Unit* spellTarget = target;
if (!spellTarget)
spellTarget = vehicleBase;
if (!IsValidUnit(spellTarget))
if (!spellTarget)
return false;
if (vehicleBase->HasSpellCooldown(spellId))
@@ -3864,9 +3802,6 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
if (!spellId)
return false;
if (!IsValidUnit(target))
return false;
Vehicle* vehicle = bot->GetVehicle();
if (!vehicle)
return false;
@@ -3877,12 +3812,12 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
return false;
Unit* vehicleBase = vehicle->GetBase();
Unit* spellTarget = target;
Unit* spellTarget = target;
if (!spellTarget)
spellTarget = vehicleBase;
if (!IsValidUnit(spellTarget))
if (!spellTarget)
return false;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -4035,13 +3970,9 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo
void PlayerbotAI::WaitForSpellCast(Spell* spell)
{
if (!spell)
return;
SpellInfo const* spellInfo = spell->GetSpellInfo();
uint32 castTime = spell->GetCastTime();
if (spellInfo && spellInfo->IsChanneled())
if (spellInfo->IsChanneled())
{
int32 duration = spellInfo->GetDuration();
bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration);
@@ -4089,9 +4020,6 @@ void PlayerbotAI::RemoveAura(std::string const name)
bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell)
{
if (!IsValidUnit(target))
return false;
uint32 spellid = aiObjectContext->GetValue<uint32>("spell id", spell)->Get();
if (!spellid || !target->IsNonMeleeSpellCast(true))
return false;
@@ -4120,25 +4048,17 @@ bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const sp
bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
{
if (!IsValidUnit(target) || !target->IsAlive())
if (!target->IsInWorld())
{
return false;
if (!IsValidPlayer(bot))
return false;
}
bool isFriend = bot->IsFriendlyTo(target);
Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras();
if (!visibleAuras)
return false;
for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
{
if (!itr->second)
continue;
Aura* aura = itr->second->GetBase();
if (!aura || aura->IsPassive() || aura->IsRemoved())
if (aura->IsPassive())
continue;
if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() &&
@@ -4146,8 +4066,6 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
continue;
SpellInfo const* spellInfo = aura->GetSpellInfo();
if (!spellInfo)
continue;
bool isPositiveSpell = spellInfo->IsPositive();
if (isPositiveSpell && isFriend)
@@ -4159,7 +4077,6 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
if (canDispel(spellInfo, dispelType))
return true;
}
return false;
}
@@ -5790,7 +5707,7 @@ void PlayerbotAI::ImbueItem(Item* item) { ImbueItem(item, TARGET_FLAG_NONE, Obje
// item on unit
void PlayerbotAI::ImbueItem(Item* item, Unit* target)
{
if (!IsValidUnit(target))
if (!target)
return;
ImbueItem(item, TARGET_FLAG_UNIT, target->GetGUID());
@@ -5932,38 +5849,30 @@ int32 PlayerbotAI::GetNearGroupMemberCount(float dis)
bool PlayerbotAI::CanMove()
{
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
// states are set when handling certain aura effects. We don't check against
// UNIT_STATE_ROOT here, because this state is used by vehicles.
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
// do not allow if not vehicle driver
if (IsInVehicle() && !IsInVehicle(true))
return false;
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || bot->HasConfuseAura() ||
bot->IsCharmed() || bot->HasStunAura() || bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
// Common CC effects, ordered by frequency: rooted > charmed > frozen > polymorphed.
// NOTE: Can't find proper way to check if bot is rooted or charmed w/o additional
// vehicle check -- when a passenger is added, they become rooted and charmed.
if (!bot->GetVehicle() && (bot->IsRooted() || bot->IsCharmed()))
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
}
bool PlayerbotAI::IsRealGuild(uint32 guildId)
{
Guild* guild = sGuildMgr->GetGuildById(guildId);
if (!guild)
{
return false;
if (bot->isFrozen() || bot->IsPolymorphed())
}
uint32 leaderAccount = sCharacterCache->GetCharacterAccountIdByGuid(guild->GetLeaderGUID());
if (!leaderAccount)
return false;
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
return false;
// Traveling state: taxi flight and being teleported (relatively rare)
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
bot->IsBeingTeleported())
return false;
// Vehicle state: is in the vehicle and can control it (rare, content-specific)
if ((bot->GetVehicle() && !IsInVehicle(true)))
return false;
return true;
return !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
}
bool PlayerbotAI::IsInRealGuild()
@@ -5971,7 +5880,7 @@ bool PlayerbotAI::IsInRealGuild()
if (!bot->GetGuildId())
return false;
return sPlayerbotGuildMgr->IsRealGuild(bot->GetGuildId());
return IsRealGuild(bot->GetGuildId());
}
void PlayerbotAI::QueueChatResponse(const ChatQueuedReply chatReply) { chatReplies.push_back(std::move(chatReply)); }

View File

@@ -276,7 +276,7 @@ enum BotRoles : uint8
enum HUNTER_TABS
{
HUNTER_TAB_BEAST_MASTERY,
HUNTER_TAB_BEASTMASTERY,
HUNTER_TAB_MARKSMANSHIP,
HUNTER_TAB_SURVIVAL,
};
@@ -295,11 +295,11 @@ enum PRIEST_TABS
PRIEST_TAB_SHADOW,
};
enum DEATH_KNIGHT_TABS
enum DEATHKNIGHT_TABS
{
DEATH_KNIGHT_TAB_BLOOD,
DEATH_KNIGHT_TAB_FROST,
DEATH_KNIGHT_TAB_UNHOLY,
DEATHKNIGHT_TAB_BLOOD,
DEATHKNIGHT_TAB_FROST,
DEATHKNIGHT_TAB_UNHOLY,
};
enum DRUID_TABS
@@ -579,6 +579,7 @@ public:
void ResetJumpDestination() { jumpDestination = Position(); }
bool CanMove();
static bool IsRealGuild(uint32 guildId);
bool IsInRealGuild();
static std::vector<std::string> dispel_whitelist;
bool EqualLowercaseName(std::string s1, std::string s2);
@@ -616,15 +617,7 @@ private:
void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
bool _isBotInitializing = false;
inline bool IsValidUnit(const Unit* unit) const
{
return unit && unit->IsInWorld() && !unit->IsDuringRemoveFromWorld();
}
inline bool IsValidPlayer(const Player* player) const
{
return player && player->GetSession() && player->IsInWorld() && !player->IsDuringRemoveFromWorld() &&
!player->IsBeingTeleported();
}
protected:
Player* bot;
Player* master;

View File

@@ -10,7 +10,6 @@
#include "PlayerbotDungeonSuggestionMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "RandomItemMgr.h"
#include "RandomPlayerbotFactory.h"
#include "RandomPlayerbotMgr.h"
@@ -223,11 +222,6 @@ bool PlayerbotAIConfig::Initialize()
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
//////////////////////////// Professions
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
enableFishingWithMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableFishingWithMaster", true);
//////////////////////////// CHAT
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
@@ -667,7 +661,6 @@ bool PlayerbotAIConfig::Initialize()
sRandomPlayerbotMgr->Init();
}
sPlayerbotGuildMgr->Init();
sRandomItemMgr->Init();
sRandomItemMgr->InitAfterAhBot();
sPlayerbotTextMgr->LoadBotTexts();

View File

@@ -145,10 +145,6 @@ public:
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
int32 rpWarningCooldown;
// Professions
bool enableFishingWithMaster;
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
// chat
bool randomBotTalk;
bool randomBotEmote;
@@ -273,6 +269,7 @@ public:
bool deleteRandomBotAccounts;
uint32 randomBotGuildCount, randomBotGuildSizeMax;
bool deleteRandomBotGuilds;
std::vector<uint32> randomBotGuilds;
std::vector<uint32> pvpProhibitedZoneIds;
std::vector<uint32> pvpProhibitedAreaIds;
bool fastReactInBG;

View File

@@ -1,322 +0,0 @@
#include "PlayerbotGuildMgr.h"
#include "Player.h"
#include "PlayerbotAIConfig.h"
#include "DatabaseEnv.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
PlayerbotGuildMgr::PlayerbotGuildMgr(){}
void PlayerbotGuildMgr::Init()
{
_guildCache.clear();
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
DeleteBotGuilds();
LoadGuildNames();
ValidateGuildCache();
}
bool PlayerbotGuildMgr::CreateGuild(Player* player, std::string guildName)
{
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
{
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName,
player->GetName());
delete guild;
return false;
}
sGuildMgr->AddGuild(guild);
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName);
SetGuildEmblem(guild->GetId());
GuildCache entry;
entry.name = guildName;
entry.memberCount = 1;
entry.status = 1;
entry.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
entry.faction = player->GetTeamId();
_guildCache[guild->GetId()] = entry;
return true;
}
bool PlayerbotGuildMgr::SetGuildEmblem(uint32 guildId)
{
Guild* guild = sGuildMgr->GetGuildById(guildId);
if (!guild)
return false;
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
bc = urand(0, 17);
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
LOG_DEBUG("playerbots",
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), st, cl, br, bc, bg);
// populate guild table with a random tabard design
CharacterDatabase.Execute(
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
"WHERE guildid={}",
st, cl, br, bc, bg, guild->GetId());
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
// Immediate reading for log
if (QueryResult qr = CharacterDatabase.Query(
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
guild->GetId()))
{
Field* f = qr->Fetch();
LOG_DEBUG("playerbots",
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
}
return true;
}
std::string PlayerbotGuildMgr::AssignToGuild(Player* player)
{
if (!player)
return "";
uint8_t playerFaction = player->GetTeamId();
std::vector<GuildCache*> partiallyfilledguilds;
partiallyfilledguilds.reserve(_guildCache.size());
for (auto& keyValue : _guildCache)
{
GuildCache& cached = keyValue.second;
if (!cached.hasRealPlayer && cached.status == 1 && cached.faction == playerFaction)
partiallyfilledguilds.push_back(&cached);
}
if (!partiallyfilledguilds.empty())
{
size_t idx = static_cast<size_t>(urand(0, static_cast<int>(partiallyfilledguilds.size()) - 1));
return (partiallyfilledguilds[idx]->name);
}
size_t count = std::count_if(
_guildCache.begin(), _guildCache.end(),
[](const std::pair<const uint32, GuildCache>& pair)
{
return !pair.second.hasRealPlayer;
}
);
if (count < sPlayerbotAIConfig->randomBotGuildCount)
{
for (auto& key : _shuffled_guild_keys)
{
if (_guildNames[key])
{
LOG_INFO("playerbots","Assigning player [{}] to guild [{}]", player->GetName(), key);
return key;
}
}
LOG_ERROR("playerbots","No available guild names left.");
}
return "";
}
void PlayerbotGuildMgr::OnGuildUpdate(Guild* guild)
{
auto it = _guildCache.find(guild->GetId());
if (it == _guildCache.end())
return;
GuildCache& entry = it->second;
entry.memberCount = guild->GetMemberCount();
if (entry.memberCount < entry.maxMembers)
entry.status = 1;
else if (entry.memberCount >= entry.maxMembers)
entry.status = 2; // Full
std::string guildName = guild->GetName();
for (auto& it : _guildNames)
{
if (it.first == guildName)
{
it.second = false;
break;
}
}
}
void PlayerbotGuildMgr::ResetGuildCache()
{
for (auto it = _guildCache.begin(); it != _guildCache.end();)
{
GuildCache& cached = it->second;
cached.memberCount = 0;
cached.faction = 2;
cached.status = 0;
}
}
void PlayerbotGuildMgr::LoadGuildNames()
{
LOG_INFO("playerbots", "Loading guild names from playerbots_guild_names...");
QueryResult result = CharacterDatabase.Query("SELECT name_id, name FROM playerbots_guild_names");
if (!result)
{
LOG_ERROR("playerbots", "No entries found in playerbots_guild_names. List is empty.");
return;
}
do
{
Field* fields = result->Fetch();
_guildNames[fields[1].Get<std::string>()] = true;
} while (result->NextRow());
for (auto& pair : _guildNames)
_shuffled_guild_keys.push_back(pair.first);
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(_shuffled_guild_keys.begin(), _shuffled_guild_keys.end(), g);
LOG_INFO("playerbots", "Loaded {} guild entries from playerbots_guild_names table.", _guildNames.size());
}
void PlayerbotGuildMgr::ValidateGuildCache()
{
QueryResult result = CharacterDatabase.Query("SELECT guildid, name FROM guild");
if (!result)
{
LOG_ERROR("playerbots", "No guilds found in database, resetting guild cache");
ResetGuildCache();
return;
}
std::unordered_map<uint32, std::string> dbGuilds;
do
{
Field* fields = result->Fetch();
uint32 guildId = fields[0].Get<uint32>();
std::string guildName = fields[1].Get<std::string>();
dbGuilds[guildId] = guildName;
} while (result->NextRow());
for (auto it = dbGuilds.begin(); it != dbGuilds.end(); it++)
{
uint32 guildId = it->first;
GuildCache cache;
cache.name = it->second;
cache.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
Guild* guild = sGuildMgr ->GetGuildById(guildId);
if (!guild)
continue;
cache.memberCount = guild->GetMemberCount();
ObjectGuid leaderGuid = guild->GetLeaderGUID();
CharacterCacheEntry const* leaderEntry = sCharacterCache->GetCharacterCacheByGuid(leaderGuid);
uint32 leaderAccount = leaderEntry->AccountId;
cache.hasRealPlayer = !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
cache.faction = Player::TeamIdForRace(leaderEntry->Race);
if (cache.memberCount == 0)
cache.status = 0; // empty
else if (cache.memberCount < cache.maxMembers)
cache.status = 1; // partially filled
else
cache.status = 2; // full
_guildCache.insert_or_assign(guildId, cache);
for (auto& it : _guildNames)
{
if (it.first == cache.name)
{
it.second = false;
break;
}
}
}
}
void PlayerbotGuildMgr::DeleteBotGuilds()
{
LOG_INFO("playerbots", "Deleting random bot guilds...");
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
} while (result->NextRow());
}
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
guild->Disband();
}
LOG_INFO("playerbots", "Random bot guilds deleted");
}
bool PlayerbotGuildMgr::IsRealGuild(Player* bot)
{
if (!bot)
return false;
uint32 guildId = bot->GetGuildId();
if (!guildId)
return false;
return IsRealGuild(guildId);
}
bool PlayerbotGuildMgr::IsRealGuild(uint32 guildId)
{
if (!guildId)
return false;
auto it = _guildCache.find(guildId);
if (it == _guildCache.end())
return false;
return it->second.hasRealPlayer;
}
class BotGuildCacheWorldScript : public WorldScript
{
public:
BotGuildCacheWorldScript() : WorldScript("BotGuildCacheWorldScript"), _validateTimer(0){}
void OnUpdate(uint32 diff) override
{
_validateTimer += diff;
if (_validateTimer >= _validateInterval) // Validate every hour
{
_validateTimer = 0;
sPlayerbotGuildMgr->ValidateGuildCache();
LOG_INFO("playerbots", "Scheduled guild cache validation");
}
}
private:
uint32 _validateInterval = HOUR*IN_MILLISECONDS;
uint32 _validateTimer;
};
void PlayerBotsGuildValidationScript()
{
new BotGuildCacheWorldScript();
}

View File

@@ -1,52 +0,0 @@
#ifndef _PLAYERBOT_PLAYERBOTGUILDMGR_H
#define _PLAYERBOT_PLAYERBOTGUILDMGR_H
#include "Guild.h"
#include "Player.h"
#include "PlayerbotAI.h"
class PlayerbotAI;
class PlayerbotGuildMgr
{
public:
static PlayerbotGuildMgr* instance()
{
static PlayerbotGuildMgr instance;
return &instance;
}
void Init();
std::string AssignToGuild(Player* player);
void LoadGuildNames();
void ValidateGuildCache();
void ResetGuildCache();
bool CreateGuild(Player* player, std::string guildName);
void OnGuildUpdate (Guild* guild);
bool SetGuildEmblem(uint32 guildId);
void DeleteBotGuilds();
bool IsRealGuild(uint32 guildId);
bool IsRealGuild(Player* bot);
private:
PlayerbotGuildMgr();
std::unordered_map<std::string, bool> _guildNames;
struct GuildCache
{
std::string name;
uint8 status;
uint32 maxMembers = 0;
uint32 memberCount = 0;
uint8 faction = 0;
bool hasRealPlayer = false;
};
std::unordered_map<uint32 , GuildCache> _guildCache;
std::vector<std::string> _shuffled_guild_keys;
};
void PlayerBotsGuildValidationScript();
#define sPlayerbotGuildMgr PlayerbotGuildMgr::instance()
#endif

View File

@@ -32,7 +32,6 @@
#include "PlayerbotSecurity.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "RandomPlayerbotMgr.h"
#include "SharedDefines.h"
#include "WorldSession.h"
@@ -1194,7 +1193,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (ObjectAccessor::FindConnectedPlayer(guid))
continue;
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
if (guildId && sPlayerbotGuildMgr->IsRealGuild(guildId))
if (guildId && PlayerbotAI::IsRealGuild(guildId))
continue;
AddPlayerBot(guid, master->GetSession()->GetAccountId());
messages.push_back("Add class " + std::string(charname));

View File

@@ -25,7 +25,6 @@
#include "Metric.h"
#include "PlayerScript.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotGuildMgr.h"
#include "PlayerbotSpellCache.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "RandomPlayerbotMgr.h"
@@ -503,8 +502,6 @@ public:
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
};
void AddPlayerbotsSecureLoginScripts();
void AddPlayerbotsScripts()
{
new PlayerbotsDatabaseScript();
@@ -514,7 +511,6 @@ void AddPlayerbotsScripts()
new PlayerbotsWorldScript();
new PlayerbotsScript();
new PlayerBotsBGScript();
AddPlayerbotsSecureLoginScripts();
AddSC_playerbots_commandscript();
PlayerBotsGuildValidationScript();
}

View File

@@ -1,82 +0,0 @@
#include "ScriptMgr.h"
#include "Opcodes.h"
#include "Player.h"
#include "ObjectAccessor.h"
#include "Playerbots.h"
namespace
{
static Player* FindOnlineAltbotByGuid(ObjectGuid guid)
{
if (!guid)
return nullptr;
Player* p = ObjectAccessor::FindPlayer(guid);
if (!p)
return nullptr;
PlayerbotAI* ai = GET_PLAYERBOT_AI(p);
if (!ai || ai->IsRealPlayer())
return nullptr;
return p;
}
static void ForceLogoutViaPlayerbotHolder(Player* target)
{
if (!target)
return;
PlayerbotAI* ai = GET_PLAYERBOT_AI(target);
if (!ai)
return;
if (Player* master = ai->GetMaster())
{
if (PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(master))
{
mgr->LogoutPlayerBot(target->GetGUID());
return;
}
}
if (sRandomPlayerbotMgr)
{
sRandomPlayerbotMgr->LogoutPlayerBot(target->GetGUID());
return;
}
}
}
class PlayerbotsSecureLoginServerScript : public ServerScript
{
public:
PlayerbotsSecureLoginServerScript()
: ServerScript("PlayerbotsSecureLoginServerScript", { SERVERHOOK_CAN_PACKET_RECEIVE }) {}
bool CanPacketReceive(WorldSession* /*session*/, WorldPacket& packet) override
{
if (packet.GetOpcode() != CMSG_PLAYER_LOGIN)
return true;
auto const oldPos = packet.rpos();
ObjectGuid loginGuid;
packet >> loginGuid;
packet.rpos(oldPos);
if (!loginGuid)
return true;
Player* existingAltbot = FindOnlineAltbotByGuid(loginGuid);
if (existingAltbot)
ForceLogoutViaPlayerbotHolder(existingAltbot);
return true;
}
};
void AddPlayerbotsSecureLoginScripts()
{
new PlayerbotsSecureLoginServerScript();
}

View File

@@ -2834,20 +2834,22 @@ inline bool ContainsInternal(ItemTemplate const* proto, uint32 skillId)
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
if (!trainer)
if (itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
continue;
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill)
uint32 trainerId = itr->second.Entry;
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
if (!trainer_spells)
continue;
for (auto& spell : trainer->GetSpells())
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin();
iter != trainer_spells->spellList.end(); ++iter)
{
if (spell.ReqSkillLine != skillId)
TrainerSpell const* tSpell = &iter->second;
if (!tSpell || tSpell->reqSkill != skillId)
continue;
if (IsCraftedBy(proto, spell.SpellId))
if (IsCraftedBy(proto, tSpell->spell))
return true;
}
}

View File

@@ -11,7 +11,6 @@
#include "GuildMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "ScriptMgr.h"
#include "SharedDefines.h"
#include "SocialMgr.h"
@@ -755,6 +754,187 @@ void RandomPlayerbotFactory::CreateRandomBots()
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
}
void RandomPlayerbotFactory::CreateRandomGuilds()
{
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
} while (result->NextRow());
}
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
{
LOG_INFO("playerbots", "Deleting random bot guilds...");
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
guild->Disband();
}
LOG_INFO("playerbots", "Random bot guilds deleted");
}
std::unordered_set<uint32> botAccounts;
botAccounts.reserve(sPlayerbotAIConfig->randomBotAccounts.size());
for (uint32 acc : sPlayerbotAIConfig->randomBotAccounts)
botAccounts.insert(acc);
// Recount bot guilds directly from the database (does not depend on connected bots)
uint32 guildNumber = 0;
sPlayerbotAIConfig->randomBotGuilds.clear();
sPlayerbotAIConfig->randomBotGuilds.shrink_to_fit(); // avoids accumulating old capacity
if (!botAccounts.empty())
{
if (QueryResult res = CharacterDatabase.Query(
// We only retrieve what is necessary (guildid, leader account)
"SELECT g.guildid, c.account "
"FROM guild g JOIN characters c ON g.leaderguid = c.guid"))
{
do
{
Field* f = res->Fetch();
const uint32 guildId = f[0].Get<uint32>();
const uint32 accountId = f[1].Get<uint32>();
// Determine if guild leader's account is a bot account.
if (botAccounts.find(accountId) != botAccounts.end())
{
++guildNumber;
sPlayerbotAIConfig->randomBotGuilds.push_back(guildId);
}
} while (res->NextRow());
}
}
LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table",guildNumber, sPlayerbotAIConfig->randomBotGuildCount);
if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount)
{
LOG_DEBUG("playerbots", "No new random guilds required");
return;
}
// We list the available leaders (online bots, not in guilds)
GuidVector availableLeaders;
availableLeaders.reserve(randomBots.size()); // limit reallocs
for (const uint32 botLowGuid : randomBots)
{
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(botLowGuid);
if (sGuildMgr->GetGuildByLeader(leader))
{
// already GuildLeader -> ignored
continue;
}
else
{
if (Player* player = ObjectAccessor::FindPlayer(leader))
{
if (!player->GetGuildId())
availableLeaders.push_back(leader);
}
}
}
LOG_DEBUG("playerbots", "{} available leaders for new guilds found", availableLeaders.size());
// Create up to randomBotGuildCount by counting only EFFECTIVE creations
uint32 createdThisRun = 0;
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; /* ++guildNumber -> done only if creation */)
{
std::string const guildName = CreateRandomGuildName();
if (guildName.empty())
break; // no more names available in playerbots_guild_names
if (sGuildMgr->GetGuildByName(guildName))
continue; // name already taken, skip
if (availableLeaders.empty())
{
LOG_ERROR("playerbots", "No leaders for random guilds available");
break; // no more leaders: we can no longer progress without distorting the counter
}
uint32 index = urand(0, availableLeaders.size() - 1);
ObjectGuid leader = availableLeaders[index];
availableLeaders.erase(availableLeaders.begin() + index); // Removes the chosen leader to avoid re-selecting it repeatedly
Player* player = ObjectAccessor::FindPlayer(leader);
if (!player)
{
LOG_ERROR("playerbots", "ObjectAccessor Cannot find player to set leader for guild {} . Skipped...",
guildName.c_str());
// we will try with other leaders in the next round (guildNumber is not incremented)
continue;
}
if (player->GetGuildId())
{
// leader already in guild -> we don't advance the counter, we move on to the next one
continue;
}
LOG_DEBUG("playerbots", "Creating guild name='{}' leader='{}'...", guildName.c_str(), player->GetName().c_str());
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
{
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName.c_str(),
player->GetName().c_str());
delete guild;
continue;
}
sGuildMgr->AddGuild(guild);
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName.c_str());
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
bc = urand(0, 17);
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
LOG_DEBUG("playerbots",
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), st, cl, br, bc, bg);
// populate guild table with a random tabard design
CharacterDatabase.Execute(
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
"WHERE guildid={}",
st, cl, br, bc, bg, guild->GetId());
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
// Immediate reading for log
if (QueryResult qr = CharacterDatabase.Query(
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
guild->GetId()))
{
Field* f = qr->Fetch();
LOG_DEBUG("playerbots",
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
}
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
// The guild is only counted if it is actually created
++guildNumber;
++createdThisRun;
}
// Shows the true total and how many were created during this run
LOG_INFO("playerbots", "{} random bot guilds created this run)", createdThisRun);
}
std::string const RandomPlayerbotFactory::CreateRandomGuildName()
{
std::string guildName = "";

View File

@@ -51,6 +51,7 @@ public:
Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& names);
static void CreateRandomBots();
static void CreateRandomGuilds();
static void CreateRandomArenaTeams(ArenaType slot, uint32 count);
static std::string const CreateRandomGuildName();
static uint32 CalculateTotalAccountCount();

View File

@@ -1654,10 +1654,6 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
if (bot->IsBeingTeleported() || !bot->IsInWorld())
return;
// no teleport / movement update when rooted.
if (bot->IsRooted())
return;
// ignore when in queue for battle grounds.
if (bot->InBattlegroundQueue())
return;
@@ -2597,17 +2593,14 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
{
if (bot && GET_PLAYERBOT_AI(bot))
{
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
return false;
}
if (bot)
{
return IsRandomBot(bot->GetGUID().GetCounter());
}
if (!bot)
return false;
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI || botAI->IsRealPlayer())
return false;
return IsRandomBot(bot->GetGUID().GetCounter());
}
bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)

View File

@@ -41,17 +41,13 @@ bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2) { return
void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force)
{
if (!bot)
return;
float angle = bot->GetAngle(wo);
// if (!force && bot->isMoving())
// bot->SetFacingTo(bot->GetAngle(wo));
// else
// {
bot->SetOrientation(angle);
if (!bot->IsRooted())
bot->SendMovementFlagUpdate();
bot->SendMovementFlagUpdate();
// }
}

View File

@@ -218,11 +218,6 @@ bool WorldPosition::isUnderWater()
: false;
};
bool WorldPosition::IsValid()
{
return !(GetMapId() == MAPID_INVALID && GetPositionX() == 0 && GetPositionY() == 0 && GetPositionZ() == 0);
}
WorldPosition WorldPosition::relPoint(WorldPosition* center)
{
return WorldPosition(GetMapId(), GetPositionX() - center->GetPositionX(), GetPositionY() - center->GetPositionY(),
@@ -3409,14 +3404,13 @@ void TravelMgr::LoadQuestTravelTable()
{
Strategy* strat = con->GetStrategy(stratName);
const std::vector<NextAction> defaultActions = strat->getDefaultActions();
if (defaultActions.size() > 0)
{
for (NextAction nextAction : defaultActions)
if (strat->getDefaultActions())
for (uint32 i = 0; i < NextAction::size(strat->getDefaultActions()); i++)
{
NextAction* nextAction = strat->getDefaultActions()[i];
std::ostringstream aout;
aout << nextAction.getRelevance() << "," << nextAction.getName()
aout << nextAction->getRelevance() << "," << nextAction->getName()
<< ",,S:" << stratName;
if (actions.find(aout.str().c_str()) != actions.end())
@@ -3428,24 +3422,27 @@ void TravelMgr::LoadQuestTravelTable()
actions.insert_or_assign(aout.str().c_str(), classSpecLevel);
}
}
std::vector<TriggerNode*> triggers;
strat->InitTriggers(triggers);
for (TriggerNode*& triggerNode : triggers)
for (auto& triggerNode : triggers)
{
// out << " TN:" << triggerNode->getName();
if (Trigger* trigger = con->GetTrigger(triggerNode->getName()))
{
triggerNode->setTrigger(trigger);
std::vector<NextAction> nextActions = triggerNode->getHandlers();
NextAction** nextActions = triggerNode->getHandlers();
// for (uint32_t i = 0; i < nextActions.size(); ++i)
for (NextAction nextAction : nextActions)
for (uint32 i = 0; i < NextAction::size(nextActions); i++)
{
NextAction* nextAction = nextActions[i];
// out << " A:" << nextAction->getName() << "(" <<
// nextAction->getRelevance() << ")";
std::ostringstream aout;
aout << nextAction.getRelevance() << "," << nextAction.getName()
aout << nextAction->getRelevance() << "," << nextAction->getName()
<< "," << triggerNode->getName() << "," << stratName;
if (actions.find(aout.str().c_str()) != actions.end())

View File

@@ -141,7 +141,6 @@ public:
bool isOverworld();
bool isInWater();
bool isUnderWater();
bool IsValid();
WorldPosition relPoint(WorldPosition* center);
WorldPosition offset(WorldPosition* center);

View File

@@ -30,7 +30,6 @@
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotDbStore.h"
#include "PlayerbotGuildMgr.h"
#include "Playerbots.h"
#include "QuestDef.h"
#include "RandomItemMgr.h"
@@ -1103,6 +1102,8 @@ void PlayerbotFactory::ResetQuests()
}
}
void PlayerbotFactory::InitSpells() { InitAvailableSpells(); }
void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_template /*true*/, bool reset /*false*/)
{
uint32 specTab;
@@ -2524,35 +2525,66 @@ void PlayerbotFactory::InitAvailableSpells()
for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin();
i != creatureTemplateContainer->end(); ++i)
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(i->first);
if (!trainer)
CreatureTemplate const& co = i->second;
if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS)
continue;
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill &&
trainer->GetTrainerType() != Trainer::Type::Class)
if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass())
continue;
if (trainer->GetTrainerType() == Trainer::Type::Class &&
!trainer->IsTrainerValidForPlayer(bot))
continue;
trainerIdCache[bot->getClass()].push_back(i->first);
uint32 trainerId = co.Entry;
trainerIdCache[bot->getClass()].push_back(trainerId);
}
}
for (uint32 trainerId : trainerIdCache[bot->getClass()])
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(trainerId);
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
if (!trainer_spells)
trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
for (auto& spell : trainer->GetSpells())
if (!trainer_spells)
continue;
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin();
itr != trainer_spells->spellList.end(); ++itr)
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
TrainerSpell const* tSpell = &itr->second;
if (!tSpell)
continue;
if (spell.IsCastable())
bot->CastSpell(bot, spell.SpellId, true);
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].MiscValue != SKILL_RIDING) ||
spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD)
{
learn = false;
break;
}
}
if (!learn)
{
continue;
}
if (tSpell->IsCastable())
bot->CastSpell(bot, tSpell->spell, true);
else
bot->learnSpell(spell.SpellId, false);
bot->learnSpell(tSpell->learnedSpell[0], false);
}
}
}
@@ -3933,33 +3965,45 @@ void PlayerbotFactory::InitInventoryEquip()
void PlayerbotFactory::InitGuild()
{
if (bot->GetGuildId())
return;
// bot->SaveToDB(false, false);
// add guild tabard
if (bot->GetGuildId() && !bot->HasItemCount(5976, 1))
StoreItem(5976, 1);
if (sPlayerbotAIConfig->randomBotGuilds.empty())
RandomPlayerbotFactory::CreateRandomGuilds();
std::vector<uint32> guilds;
for (std::vector<uint32>::iterator i = sPlayerbotAIConfig->randomBotGuilds.begin();
i != sPlayerbotAIConfig->randomBotGuilds.end(); ++i)
guilds.push_back(*i);
if (guilds.empty())
{
if (!bot->HasItemCount(5976, 1) && bot->GetLevel() > 9)
StoreItem(5976, 1);
LOG_ERROR("playerbots", "No random guilds available");
return;
}
std::string guildName = sPlayerbotGuildMgr->AssignToGuild(bot);
if (guildName.empty())
return;
Guild* guild = sGuildMgr->GetGuildByName(guildName);
int index = urand(0, guilds.size() - 1);
uint32 guildId = guilds[index];
Guild* guild = sGuildMgr->GetGuildById(guildId);
if (!guild)
{
if (!sPlayerbotGuildMgr->CreateGuild(bot, guildName))
LOG_ERROR("playerbots","Failed to create guild {} for bot {}", guildName, bot->GetName());
LOG_ERROR("playerbots", "Invalid guild {}", guildId);
return;
}
else
{
if (guild->AddMember(bot->GetGUID(),urand(GR_OFFICER, GR_INITIATE)))
sPlayerbotGuildMgr->OnGuildUpdate(guild);
else
LOG_ERROR("playerbots","Bot {} failed to join guild {}.", bot->GetName(), guildName);
}
if (guild->GetMemberSize() < urand(10, sPlayerbotAIConfig->randomBotGuildSizeMax))
guild->AddMember(bot->GetGUID(), urand(GR_OFFICER, GR_INITIATE));
// add guild tabard
if (bot->GetGuildId() && bot->GetLevel() > 9 && urand(0, 4) && !bot->HasItemCount(5976, 1))
StoreItem(5976, 1);
// bot->SaveToDB(false, false);
}
void PlayerbotFactory::InitImmersive()

View File

@@ -95,6 +95,7 @@ private:
void InitTradeSkills();
void UpdateTradeSkills();
void SetRandomSkill(uint16 id);
void InitSpells();
void ClearSpells();
void ClearSkills();
void InitTalents(uint32 specNo);

View File

@@ -37,7 +37,7 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
tab = AiFactory::GetPlayerSpecTab(player);
collector_ = std::make_unique<StatsCollector>(type_, cls);
if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_UNHOLY)
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
hitOverflowType_ = CollectorType::SPELL;
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)
hitOverflowType_ = CollectorType::SPELL;
@@ -193,7 +193,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 0.01f;
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEAST_MASTERY || tab == HUNTER_TAB_SURVIVAL))
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTERY || tab == HUNTER_TAB_SURVIVAL))
{
stats_weights_[STATS_TYPE_AGILITY] += 2.5f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
@@ -249,7 +249,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
}
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY)
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury
{
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.6f;
@@ -261,7 +261,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
}
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS)
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm
{
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.3f;
@@ -273,7 +273,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 1.4f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
}
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_FROST)
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk
{
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.8f;
@@ -285,7 +285,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
}
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_UNHOLY)
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
{
stats_weights_[STATS_TYPE_AGILITY] += 0.9f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
@@ -297,7 +297,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
}
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION)
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
{
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
@@ -311,7 +311,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 9.0f;
}
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT))
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement
{
stats_weights_[STATS_TYPE_AGILITY] += 1.4f;
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
@@ -325,10 +325,9 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f;
}
else if (cls == CLASS_WARLOCK ||
(cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) ||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE))
else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
@@ -356,8 +355,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
}
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION))
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
@@ -366,7 +365,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
}
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) ||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
@@ -397,7 +396,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_EXPERTISE] += 3.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 2.0f;
}
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD)
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD)
{
stats_weights_[STATS_TYPE_AGILITY] += 2.0f;
stats_weights_[STATS_TYPE_STRENGTH] += 1.0f;
@@ -540,7 +539,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
// spec without double hand
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_FROST) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() &&
player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
@@ -557,7 +556,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))
{
weight_ *= 0.1;

View File

@@ -8,6 +8,90 @@
#include "Playerbots.h"
#include "Timer.h"
uint32 NextAction::size(NextAction** actions)
{
if (!actions)
return 0;
uint32 size = 0;
for (size = 0; actions[size];)
++size;
return size;
}
NextAction** NextAction::clone(NextAction** actions)
{
if (!actions)
return nullptr;
uint32 size = NextAction::size(actions);
NextAction** res = new NextAction*[size + 1];
for (uint32 i = 0; i < size; i++)
res[i] = new NextAction(*actions[i]);
res[size] = nullptr;
return res;
}
NextAction** NextAction::merge(NextAction** left, NextAction** right)
{
uint32 leftSize = NextAction::size(left);
uint32 rightSize = NextAction::size(right);
NextAction** res = new NextAction*[leftSize + rightSize + 1];
for (uint32 i = 0; i < leftSize; i++)
res[i] = new NextAction(*left[i]);
for (uint32 i = 0; i < rightSize; i++)
res[leftSize + i] = new NextAction(*right[i]);
res[leftSize + rightSize] = nullptr;
NextAction::destroy(left);
NextAction::destroy(right);
return res;
}
NextAction** NextAction::array(uint32 nil, ...)
{
va_list vl;
va_start(vl, nil);
uint32 size = 0;
NextAction* cur = nullptr;
do
{
cur = va_arg(vl, NextAction*);
++size;
} while (cur);
va_end(vl);
NextAction** res = new NextAction*[size];
va_start(vl, nil);
for (uint32 i = 0; i < size; i++)
res[i] = va_arg(vl, NextAction*);
va_end(vl);
return res;
}
void NextAction::destroy(NextAction** actions)
{
if (!actions)
return;
for (uint32 i = 0; actions[i]; i++)
delete actions[i];
delete[] actions;
}
Value<Unit*>* Action::GetTargetValue() { return context->GetValue<Unit*>(GetTargetName()); }
Unit* Action::GetTarget() { return GetTargetValue()->Get(); }
@@ -17,4 +101,4 @@ ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequ
{
}
bool ActionBasket::isExpired(uint32_t msecs) { return getMSTime() - created >= msecs; }
bool ActionBasket::isExpired(uint32 msecs) { return getMSTime() - created >= msecs; }

View File

@@ -3,7 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#pragma once
#ifndef _PLAYERBOT_ACTION_H
#define _PLAYERBOT_ACTION_H
#include "AiObject.h"
#include "Common.h"
@@ -23,26 +24,15 @@ public:
std::string const getName() { return name; }
float getRelevance() { return relevance; }
static std::vector<NextAction> merge(std::vector<NextAction> const& what, std::vector<NextAction> const& with)
{
std::vector<NextAction> result = {};
for (NextAction const& action : what)
{
result.push_back(action);
}
for (NextAction const& action : with)
{
result.push_back(action);
}
return result;
};
static uint32 size(NextAction** actions);
static NextAction** clone(NextAction** actions);
static NextAction** merge(NextAction** what, NextAction** with);
static NextAction** array(uint32 nil, ...);
static void destroy(NextAction** actions);
private:
float relevance;
std::string name;
std::string const name;
};
class Action : public AiNamedObject
@@ -62,9 +52,9 @@ public:
virtual bool Execute([[maybe_unused]] Event event) { return true; }
virtual bool isPossible() { return true; }
virtual bool isUseful() { return true; }
virtual std::vector<NextAction> getPrerequisites() { return {}; }
virtual std::vector<NextAction> getAlternatives() { return {}; }
virtual std::vector<NextAction> getContinuers() { return {}; }
virtual NextAction** getPrerequisites() { return nullptr; }
virtual NextAction** getAlternatives() { return nullptr; }
virtual NextAction** getContinuers() { return nullptr; }
virtual ActionThreatType getThreatType() { return ActionThreatType::None; }
void Update() {}
void Reset() {}
@@ -83,44 +73,39 @@ protected:
class ActionNode
{
public:
ActionNode(
std::string name,
std::vector<NextAction> prerequisites = {},
std::vector<NextAction> alternatives = {},
std::vector<NextAction> continuers = {}
) :
name(std::move(name)),
action(nullptr),
continuers(continuers),
alternatives(alternatives),
prerequisites(prerequisites)
{}
ActionNode(std::string const name, NextAction** prerequisites = nullptr, NextAction** alternatives = nullptr,
NextAction** continuers = nullptr)
: name(name), action(nullptr), continuers(continuers), alternatives(alternatives), prerequisites(prerequisites)
{
} // reorder arguments - whipowill
virtual ~ActionNode() = default;
virtual ~ActionNode()
{
NextAction::destroy(prerequisites);
NextAction::destroy(alternatives);
NextAction::destroy(continuers);
}
Action* getAction() { return action; }
void setAction(Action* action) { this->action = action; }
const std::string getName() { return name; }
std::string const getName() { return name; }
std::vector<NextAction> getContinuers()
NextAction** getContinuers() { return NextAction::merge(NextAction::clone(continuers), action->getContinuers()); }
NextAction** getAlternatives()
{
return NextAction::merge(this->continuers, action->getContinuers());
return NextAction::merge(NextAction::clone(alternatives), action->getAlternatives());
}
std::vector<NextAction> getAlternatives()
NextAction** getPrerequisites()
{
return NextAction::merge(this->alternatives, action->getAlternatives());
}
std::vector<NextAction> getPrerequisites()
{
return NextAction::merge(this->prerequisites, action->getPrerequisites());
return NextAction::merge(NextAction::clone(prerequisites), action->getPrerequisites());
}
private:
const std::string name;
std::string const name;
Action* action;
std::vector<NextAction> continuers;
std::vector<NextAction> alternatives;
std::vector<NextAction> prerequisites;
NextAction** continuers;
NextAction** alternatives;
NextAction** prerequisites;
};
class ActionBasket
@@ -136,12 +121,14 @@ public:
bool isSkipPrerequisites() { return skipPrerequisites; }
void AmendRelevance(float k) { relevance *= k; }
void setRelevance(float relevance) { this->relevance = relevance; }
bool isExpired(uint32_t msecs);
bool isExpired(uint32 msecs);
private:
ActionNode* action;
float relevance;
bool skipPrerequisites;
Event event;
uint32_t created;
uint32 created;
};
#endif

View File

@@ -42,6 +42,9 @@ protected:
// TRIGGERS
//
#define NEXT_TRIGGERS(name, relevance) \
virtual NextAction* getNextAction() { return new NextAction(name, relevance); }
#define BEGIN_TRIGGER(clazz, super) \
class clazz : public super \
{ \
@@ -75,6 +78,14 @@ protected:
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) {} \
}
#define BUFF_PARTY_TRIGGER_A(clazz, spell) \
class clazz : public BuffOnPartyTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) {} \
bool IsActive() override; \
}
#define DEBUFF_TRIGGER(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
@@ -285,6 +296,14 @@ protected:
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) {} \
}
#define HEAL_ACTION_U(clazz, spell, useful) \
class clazz : public CastHealingSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) {} \
bool isUseful() override { return useful; } \
}
#define HEAL_PARTY_ACTION(clazz, spell, estAmount, manaEfficiency) \
class clazz : public HealPartyMemberAction \
{ \
@@ -385,6 +404,14 @@ protected:
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) {} \
}
#define REACH_ACTION_U(clazz, spell, range, useful) \
class clazz : public CastReachTargetSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) {} \
bool isUseful() override { return useful; } \
}
#define ENEMY_HEALER_ACTION(clazz, spell) \
class clazz : public CastSpellOnEnemyHealerAction \
{ \
@@ -413,6 +440,10 @@ protected:
clazz(PlayerbotAI* botAI) : CastProtectSpellAction(botAI, spell) {} \
}
#define END_RANGED_SPELL_ACTION() \
} \
;
#define BEGIN_SPELL_ACTION(clazz, name) \
class clazz : public CastSpellAction \
{ \
@@ -441,4 +472,42 @@ protected:
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, name) {}
#define END_RANGED_SPELL_ACTION() \
} \
;
#define BEGIN_BUFF_ON_PARTY_ACTION(clazz, name) \
class clazz : public BuffOnPartyAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, name) {}
//
// Action node
//
// node_name , action, prerequisite
#define ACTION_NODE_P(name, spell, pre) \
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, /*P*/ NextAction::array(0, new NextAction(pre), nullptr), /*A*/ nullptr, \
/*C*/ nullptr); \
}
// node_name , action, alternative
#define ACTION_NODE_A(name, spell, alt) \
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, /*P*/ nullptr, /*A*/ NextAction::array(0, new NextAction(alt), nullptr), \
/*C*/ nullptr); \
}
// node_name , action, continuer
#define ACTION_NODE_C(name, spell, con) \
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, /*P*/ nullptr, /*A*/ nullptr, \
/*C*/ NextAction::array(0, new NextAction(con), nullptr)); \
}
#endif

View File

@@ -43,6 +43,8 @@
#include "raids/magtheridon/RaidMagtheridonTriggerContext.h"
#include "raids/gruulslair/RaidGruulsLairActionContext.h"
#include "raids/gruulslair/RaidGruulsLairTriggerContext.h"
#include "raids/naxxramas/RaidNaxxActionContext.h"
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
@@ -115,6 +117,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new RaidKarazhanActionContext());
actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidGruulsLairActionContext());
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidVoAActionContext());
@@ -149,6 +152,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new RaidKarazhanTriggerContext());
triggerContexts.Add(new RaidMagtheridonTriggerContext());
triggerContexts.Add(new RaidGruulsLairTriggerContext());
triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidVoATriggerContext());

View File

@@ -6,40 +6,36 @@
#include "CustomStrategy.h"
#include <regex>
#include <stdexcept>
#include "Playerbots.h"
std::map<std::string, std::string> CustomStrategy::actionLinesCache;
NextAction toNextAction(std::string const action)
NextAction* toNextAction(std::string const action)
{
std::vector<std::string> tokens = split(action, '!');
if (tokens[0].empty())
throw std::invalid_argument("Invalid action");
if (tokens.size() == 2)
return NextAction(tokens[0], atof(tokens[1].c_str()));
if (tokens.size() == 1)
return NextAction(tokens[0], ACTION_NORMAL);
if (tokens.size() == 2 && !tokens[0].empty())
return new NextAction(tokens[0], atof(tokens[1].c_str()));
else if (tokens.size() == 1 && !tokens[0].empty())
return new NextAction(tokens[0], ACTION_NORMAL);
LOG_ERROR("playerbots", "Invalid action {}", action.c_str());
throw std::invalid_argument("Invalid action");
return nullptr;
}
std::vector<NextAction> toNextActionArray(const std::string actions)
NextAction** toNextActionArray(std::string const actions)
{
const std::vector<std::string> tokens = split(actions, ',');
std::vector<NextAction> res = {};
std::vector<std::string> tokens = split(actions, ',');
NextAction** res = new NextAction*[tokens.size() + 1];
for (const std::string token : tokens)
uint32 index = 0;
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
{
res.push_back(toNextAction(token));
if (NextAction* na = toNextAction(*i))
res[index++] = na;
}
res[index++] = nullptr;
return res;
}

View File

@@ -258,45 +258,48 @@ ActionNode* Engine::CreateActionNode(std::string const name)
return node;
return new ActionNode(name,
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
bool Engine::MultiplyAndPush(
std::vector<NextAction> actions,
float forceRelevance,
bool skipPrerequisites,
Event event,
char const* pushType
)
bool Engine::MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event,
char const* pushType)
{
bool pushed = false;
for (NextAction nextAction : actions)
if (actions)
{
ActionNode* action = this->CreateActionNode(nextAction.getName());
this->InitializeAction(action);
float k = nextAction.getRelevance();
if (forceRelevance > 0.0f)
for (uint32 j = 0; actions[j]; j++)
{
k = forceRelevance;
if (NextAction* nextAction = actions[j])
{
ActionNode* action = CreateActionNode(nextAction->getName());
InitializeAction(action);
float k = nextAction->getRelevance();
if (forceRelevance > 0.0f)
{
k = forceRelevance;
}
if (k > 0)
{
LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
pushed = true;
}
else
{
delete action;
}
delete nextAction;
}
else
break;
}
if (k > 0)
{
this->LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
pushed = true;
continue;
}
delete action;
delete[] actions;
}
return pushed;
@@ -527,10 +530,10 @@ std::vector<std::string> Engine::GetStrategies()
void Engine::PushAgain(ActionNode* actionNode, float relevance, Event event)
{
std::vector<NextAction> nextAction = { NextAction(actionNode->getName(), relevance) };
NextAction** nextAction = new NextAction*[2];
nextAction[0] = new NextAction(actionNode->getName(), relevance);
nextAction[1] = nullptr;
MultiplyAndPush(nextAction, relevance, true, event, "again");
delete actionNode;
}
@@ -560,13 +563,6 @@ bool Engine::ListenAndExecute(Action* action, Event event)
{
bool actionExecuted = false;
if (action == nullptr)
{
LOG_ERROR("playerbots", "Action is nullptr");
return actionExecuted;
}
if (actionExecutionListeners.Before(action, event))
{
actionExecuted = actionExecutionListeners.AllowExecution(action, event) ? action->Execute(event) : true;

View File

@@ -90,7 +90,7 @@ public:
bool testMode;
private:
bool MultiplyAndPush(std::vector<NextAction> actions, float forceRelevance, bool skipPrerequisites, Event event,
bool MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event,
const char* pushType);
void Reset();
void ProcessTriggers(bool minimal);

View File

@@ -28,112 +28,90 @@ public:
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("melee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healthstone",
/*P*/ {},
/*A*/ { NextAction("healing potion") },
/*C*/ {}
);
return new ActionNode("healthstone",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing potion"), nullptr),
/*C*/ nullptr);
}
static ActionNode* follow_master_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"be near",
/*P*/ {},
/*A*/ { NextAction("follow") },
/*C*/ {}
);
return new ActionNode("be near",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("follow"), nullptr),
/*C*/ nullptr);
}
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"attack anything",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("attack anything",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* move_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move random",
/*P*/ {},
/*A*/ { NextAction("stay line") },
/*C*/ {}
);
return new ActionNode("move random",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("stay line"), nullptr),
/*C*/ nullptr);
}
static ActionNode* move_to_loot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move to loot",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("move to loot",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"food",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("food",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"drink",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("drink",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mana potion",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("mana potion",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing potion",
/*P*/ {},
/*A*/ { NextAction("food") },
/*C*/ {}
);
return new ActionNode("healing potion",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("food"), nullptr),
/*C*/ nullptr);
}
static ActionNode* flee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"flee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("flee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};

View File

@@ -60,7 +60,7 @@ public:
Strategy(PlayerbotAI* botAI);
virtual ~Strategy() {}
virtual std::vector<NextAction> getDefaultActions() { return {}; }
virtual NextAction** getDefaultActions() { return nullptr; }
virtual void InitTriggers([[maybe_unused]] std::vector<TriggerNode*>& triggers) {}
virtual void InitMultipliers([[maybe_unused]] std::vector<Multiplier*>& multipliers) {}
virtual std::string const getName() = 0;

View File

@@ -120,8 +120,6 @@ public:
creators["formation"] = &StrategyContext::combat_formation;
creators["move from group"] = &StrategyContext::move_from_group;
creators["worldbuff"] = &StrategyContext::world_buff;
creators["use bobber"] = &StrategyContext::bobber_strategy;
creators["master fishing"] = &StrategyContext::master_fishing;
}
private:
@@ -190,8 +188,6 @@ private:
static Strategy* combat_formation(PlayerbotAI* botAI) { return new CombatFormationStrategy(botAI); }
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
};
class MovementStrategyContext : public NamedObjectContext<Strategy>

View File

@@ -3,7 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#pragma once
#ifndef _PLAYERBOT_TRIGGER_H
#define _PLAYERBOT_TRIGGER_H
#include "Action.h"
#include "Common.h"
@@ -14,11 +15,7 @@ class Unit;
class Trigger : public AiNamedObject
{
public:
Trigger(
PlayerbotAI* botAI,
const std::string name = "trigger",
int32_t checkInterval = 1
);
Trigger(PlayerbotAI* botAI, std::string const name = "trigger", int32 checkInterval = 1);
virtual ~Trigger() {}
@@ -26,7 +23,7 @@ public:
virtual void ExternalEvent([[maybe_unused]] std::string const param, [[maybe_unused]] Player* owner = nullptr) {}
virtual void ExternalEvent([[maybe_unused]] WorldPacket& packet, [[maybe_unused]] Player* owner = nullptr) {}
virtual bool IsActive() { return false; }
virtual std::vector<NextAction> getHandlers() { return {}; }
virtual NextAction** getHandlers() { return nullptr; }
void Update() {}
virtual void Reset() {}
virtual Unit* GetTarget();
@@ -36,49 +33,32 @@ public:
bool needCheck(uint32 now);
protected:
int32_t checkInterval;
uint32_t lastCheckTime;
int32 checkInterval;
uint32 lastCheckTime;
};
class TriggerNode
{
public:
TriggerNode(
const std::string& name,
std::vector<NextAction> handlers = {}
) :
trigger(nullptr),
handlers(std::move(handlers)),
name(name)
{}
TriggerNode(std::string const name, NextAction** handlers = nullptr)
: trigger(nullptr), handlers(handlers), name(name)
{
} // reorder args - whipowill
virtual ~TriggerNode() { NextAction::destroy(handlers); }
Trigger* getTrigger() { return trigger; }
void setTrigger(Trigger* trigger) { this->trigger = trigger; }
const std::string getName() { return name; }
std::string const getName() { return name; }
std::vector<NextAction> getHandlers()
{
std::vector<NextAction> result = this->handlers;
NextAction** getHandlers() { return NextAction::merge(NextAction::clone(handlers), trigger->getHandlers()); }
if (trigger != nullptr)
{
std::vector<NextAction> extra = trigger->getHandlers();
result.insert(result.end(), extra.begin(), extra.end());
}
return result;
}
float getFirstRelevance()
{
if (this->handlers.size() > 0)
return this->handlers[0].getRelevance();
return -1;
}
float getFirstRelevance() { return handlers[0] ? handlers[0]->getRelevance() : -1; }
private:
Trigger* trigger;
std::vector<NextAction> handlers;
const std::string name;
NextAction** handlers;
std::string const name;
};
#endif

View File

@@ -59,7 +59,7 @@ bool AcceptInvitationAction::Execute(Event event)
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
{
Teleport(inviter, bot, true);
Teleport(inviter, bot);
}
return true;
}

View File

@@ -46,6 +46,7 @@
#include "OutfitAction.h"
#include "PositionAction.h"
#include "DropQuestAction.h"
#include "RaidNaxxActions.h"
#include "RandomBotUpdateAction.h"
#include "ReachTargetActions.h"
#include "ReleaseSpiritAction.h"
@@ -63,7 +64,6 @@
#include "WorldBuffAction.h"
#include "XpGainAction.h"
#include "NewRpgAction.h"
#include "FishingAction.h"
#include "CancelChannelAction.h"
class PlayerbotAI;
@@ -191,11 +191,6 @@ public:
creators["buy tabard"] = &ActionContext::buy_tabard;
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
creators["clean quest log"] = &ActionContext::clean_quest_log;
creators["move near water"] = &ActionContext::move_near_water;
creators["go fishing"] = &ActionContext::go_fishing;
creators["use fishing bobber"] = &ActionContext::use_fishing_bobber;
creators["end master fishing"] = &ActionContext::end_master_fishing;
creators["remove bobber strategy"] = &ActionContext::remove_bobber_strategy;
creators["roll"] = &ActionContext::roll_action;
creators["cancel channel"] = &ActionContext::cancel_channel;
@@ -385,11 +380,6 @@ private:
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
static Action* move_near_water(PlayerbotAI* botAI) { return new MoveNearWaterAction(botAI); }
static Action* go_fishing(PlayerbotAI* botAI) { return new FishingAction(botAI);}
static Action* use_fishing_bobber(PlayerbotAI* botAI) { return new UseBobberAction(botAI);}
static Action* end_master_fishing(PlayerbotAI* botAI) { return new EndMasterFishingAction(botAI); }
static Action* remove_bobber_strategy(PlayerbotAI* botAI) { return new RemoveBobberStrategyAction(botAI); }
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
// BG Tactics

View File

@@ -159,7 +159,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
bot->StopMoving();
}
if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
sServerFacade->SetFacingTo(bot, target);
botAI->ChangeEngine(BOT_STATE_COMBAT);

View File

@@ -185,6 +185,7 @@ public:
creators["guild remove"] = &ChatActionContext::guild_remove;
creators["guild leave"] = &ChatActionContext::guild_leave;
creators["rtsc"] = &ChatActionContext::rtsc;
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
creators["join"] = &ChatActionContext::join;
@@ -297,6 +298,7 @@ private:
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }

View File

@@ -241,6 +241,20 @@ bool MaxDpsChatShortcutAction::Execute(Event event)
return true;
}
bool NaxxChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
botAI->TellMasterNoFacing("Add Naxx Strategies!");
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
return true;
}
bool BwlChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();

View File

@@ -85,6 +85,13 @@ public:
bool Execute(Event event) override;
};
class NaxxChatShortcutAction : public Action
{
public:
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
virtual bool Execute(Event event);
};
class BwlChatShortcutAction : public Action
{
public:

View File

@@ -78,17 +78,20 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
if (!trigger->IsActive())
continue;
std::vector<NextAction> nextActions = triggerNode->getHandlers();
NextAction** nextActions = triggerNode->getHandlers();
bool isRpg = false;
for (NextAction nextAction : nextActions)
for (int32 i = 0; i < NextAction::size(nextActions); i++)
{
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
NextAction* nextAction = nextActions[i];
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
if (dynamic_cast<RpgEnabled*>(action))
isRpg = true;
}
NextAction::destroy(nextActions);
if (isRpg)
{

View File

@@ -344,27 +344,6 @@ bool EquipUpgradesAction::Execute(Event event)
return false;
}
if (event.GetSource() == "item push result")
{
WorldPacket p(event.getPacket());
p.rpos(0);
ObjectGuid playerGuid;
uint32 received, created, sendChatMessage, itemSlot, itemId;
uint8 bagSlot;
p >> playerGuid;
p >> received;
p >> created;
p >> sendChatMessage;
p >> bagSlot;
p >> itemSlot;
p >> itemId;
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
return false;
}
CollectItemsVisitor visitor;
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);

View File

@@ -1,493 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FishingAction.h"
#include "FishValues.h"
#include "Event.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "ItemPackets.h"
#include "LastMovementValue.h"
#include "Map.h"
#include "MovementActions.h"
#include "Object.h"
#include "PlayerbotAI.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "Position.h"
uint32 const FISHING_SPELL = 7620;
uint32 const FISHING_POLE = 6256;
uint32 const FISHING_BOBBER = 35591;
float const MIN_DISTANCE_TO_WATER = 10.0f; // Minimum spell distance
float const MAX_DISTANCE_TO_WATER = 20.0f; // Maximum spell distance
float const HEIGHT_ABOVE_WATER_TOLERANCE = 1.0f; // Can stand in up to 1 unit of water and still fish.
float const SEARCH_INCREMENT = 2.5f;
float const HEIGHT_SEARCH_BUFFER = 10.0f; // Height buffer to prevent potentially missing the model the bot is standing on.
float const SEARCH_LAND_BUFFER = 0.5f;
uint32 const FISHING_LOCATION_TIMEOUT = 180000; //Three minutes
static bool IsFishingPole(Item* const item)
{
if (!item)
return false;
const ItemTemplate* proto = item->GetTemplate();
return proto && proto->Class == ITEM_CLASS_WEAPON &&
proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE;
}
float HasFishableWaterOrLand(float x, float y, float z, Map* map, uint32 phaseMask, bool checkForLand=false)
{
if (!map)
return INVALID_HEIGHT;
LiquidData const& liq = map->GetLiquidData(phaseMask, x, y, z+HEIGHT_ABOVE_WATER_TOLERANCE, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
float ground = map->GetHeight(phaseMask, x, y, z + HEIGHT_SEARCH_BUFFER, true);
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER)
{
if (checkForLand)
return ground;
return INVALID_HEIGHT;
}
if (checkForLand)
{
if (ground > liq.Level - HEIGHT_ABOVE_WATER_TOLERANCE)
return ground;
return INVALID_HEIGHT;
}
if (liq.Level + HEIGHT_ABOVE_WATER_TOLERANCE > ground)
{
if (abs(liq.DepthLevel) < 0.5f) // too shallow to fish in.
return INVALID_HEIGHT;
return liq.Level;
}
return INVALID_HEIGHT;
}
bool HasLosToWater(Player* bot, float wx, float wy, float waterZ)
{
float z = bot->GetCollisionHeight() + bot->GetPositionZ();
return bot->GetMap()->isInLineOfSight(
bot->GetPositionX(), bot->GetPositionY(), z,
wx, wy, waterZ,
bot->GetPhaseMask(),
LINEOFSIGHT_ALL_CHECKS,
VMAP::ModelIgnoreFlags::Nothing);
}
WorldPosition FindLandFromPosition(PlayerbotAI* botAI, float startDistance, float endDistance, float increment, float orientation, WorldPosition targetPos, float fishingSearchWindow, bool checkLOS = true)
{
Player* bot = botAI->GetBot();
Map* map = bot->GetMap();
uint32 phaseMask = bot->GetPhaseMask();
Player* master = botAI->GetMaster();
float targetX = targetPos.GetPositionX();
float targetY = targetPos.GetPositionY();
float targetZ = targetPos.GetPositionZ();
for (float dist = startDistance; dist <= endDistance; dist += increment)
{
//step backwards from position to bot to find edge of shore.
float checkX = targetX - dist * cos(orientation);
float checkY = targetY - dist * sin(orientation);
float groundZ = map->GetHeight(phaseMask, checkX, checkY, targetZ + HEIGHT_SEARCH_BUFFER, true);
if (groundZ == INVALID_HEIGHT)
continue;
LiquidData const& liq = map->GetLiquidData(phaseMask, checkX, checkY, targetZ, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER || groundZ > liq.DepthLevel + HEIGHT_ABOVE_WATER_TOLERANCE)
{
if (checkLOS)
{
bool hasLOS = map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
if (!hasLOS)
continue;
}
// Add a distance check for the position to prevent the bot from moving out of range to the master.
if (master && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
continue;
return WorldPosition(bot->GetMapId(), checkX, checkY, groundZ);
}
}
return WorldPosition();
}
WorldPosition FindLandRadialFromPosition (PlayerbotAI* botAI, WorldPosition targetPos, float startDistance, float endDistance, float increment, float fishingSearchWindow, int angles = 16)
{
Player* bot = botAI->GetBot();
const int numDirections = angles;
std::vector<WorldPosition> boundaryPoints;
Player* master = botAI->GetMaster();
if (!master)
return WorldPosition();
Map* map = bot->GetMap();
uint32 phaseMask = bot->GetPhaseMask();
float targetX = targetPos.GetPositionX();
float targetY = targetPos.GetPositionY();
float targetZ = targetPos.GetPositionZ();
for (float dist = startDistance; dist <= endDistance; dist += increment)
{
for (int i = 0; i < numDirections; ++i)
{
float angle = (2.0f * M_PI * i) / numDirections;
float checkX = targetX - cos(angle) * dist;
float checkY = targetY - sin(angle) * dist;
float groundZ = HasFishableWaterOrLand(checkX, checkY, targetZ, map, phaseMask, true);
if (groundZ == INVALID_HEIGHT)
continue;
if (map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
continue;
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, groundZ));
}
if (!boundaryPoints.empty())
break;
}
if (boundaryPoints.empty())
return WorldPosition();
if (boundaryPoints.size() == 1)
return boundaryPoints[0];
float minDistance = FLT_MAX;
WorldLocation closestPoint = WorldPosition();
for (auto const& pos : boundaryPoints)
{
float distance = bot->GetExactDist2d(&pos);
if (distance < minDistance)
{
minDistance = distance;
closestPoint = pos;
}
}
return closestPoint;
}
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS, int numDirections)
{
std::vector<WorldPosition> boundaryPoints;
float dist = minDistance;
while (dist <= maxDistance)
{
for (int i = 0; i < numDirections; ++i)
{
float angle = (2.0f * M_PI * i) / numDirections;
float checkX = x + cos(angle) * dist;
float checkY = y + sin(angle) * dist;
float waterZ = HasFishableWaterOrLand(checkX, checkY, z, map, phaseMask);
if (waterZ == INVALID_HEIGHT)
continue;
if (checkLOS && !HasLosToWater(bot, checkX, checkY, waterZ))
continue;
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, waterZ));
}
if (!boundaryPoints.empty())
break;
dist += increment;
}
if (boundaryPoints.empty())
return WorldPosition();
if (boundaryPoints.size() == 1)
return boundaryPoints[0];
// return the central point in the identified positions in to try to be perpendicular to the shore.
return boundaryPoints[boundaryPoints.size() / 2];
}
WorldPosition FindFishingHole(PlayerbotAI* botAI)
{
Player* player = botAI->GetBot();
GuidVector gos = PAI_VALUE(GuidVector, "nearest game objects no los");
GameObject* nearestFishingHole = nullptr;
float minDist = std::numeric_limits<float>::max();
for (auto const& guid : gos)
{
GameObject* go = botAI->GetGameObject(guid);
if (!go)
continue;
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
{
float dist = player->GetDistance2d(go);
if (dist < minDist)
{
minDist = dist;
nearestFishingHole = go;
}
}
}
if (nearestFishingHole)
return WorldPosition(nearestFishingHole->GetMapId(), nearestFishingHole->GetPositionX(), nearestFishingHole->GetPositionY(), nearestFishingHole->GetPositionZ());
return WorldPosition();
}
bool MoveNearWaterAction::Execute(Event event)
{
WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot");
if (landSpot.IsValid())
return MoveTo(landSpot.GetMapId(), landSpot.GetPositionX(), landSpot.GetPositionY(), landSpot.GetPositionZ());
return false;
}
bool MoveNearWaterAction::isUseful()
{
if (!AI_VALUE(bool, "can fish"))
return false;
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) || pos != bot->GetPosition();
}
bool MoveNearWaterAction::isPossible()
{
Player* master = botAI->GetMaster();
float fishingSearchWindow;
if (master)
fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster;
else
fishingSearchWindow = sPlayerbotAIConfig->fishingDistance;
WorldPosition fishingHole = FindFishingHole(botAI);
if (fishingHole.IsValid())
{
float distance = bot->GetExactDist2d(&fishingHole);
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
// Water spot is in range, and we have LOS to it. Set bot position to fishing spot and do not move
if (distance >= MIN_DISTANCE_TO_WATER &&
distance <= MAX_DISTANCE_TO_WATER && hasLOS)
{
SET_AI_VALUE(WorldPosition, "fishing spot", WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())));
return false;
}
// Water spot is out of range, lets look for a spot to move to for the fishing hole.
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
{
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
if (landSpot.IsValid())
{
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
return true;
}
}
}
// Lets find some water where we can fish.
WorldPosition water = FindWaterRadial(
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
bot->GetMap(), bot->GetPhaseMask(),
MIN_DISTANCE_TO_WATER,
fishingSearchWindow + MAX_DISTANCE_TO_WATER,
SEARCH_INCREMENT, false);
if (!water.IsValid())
return false;
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
WorldPosition landSpot =
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);
if (landSpot.IsValid())
{
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
return true;
}
return false;
}
bool EquipFishingPoleAction::Execute(Event event)
{
if (!_pole)
return false;
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
eqPacket << _pole->GetGUID() << uint8(EQUIPMENT_SLOT_MAINHAND);
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
nicePacket.Read();
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
return true;
}
bool EquipFishingPoleAction::isUseful()
{
Item* mainHand = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
if (IsFishingPole(mainHand))
return false;
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
{
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
if (IsFishingPole(item))
{
_pole = item;
return true;
}
}
}
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag* pBag = bot->GetBagByPos(bag))
{
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
{
if (Item* item = pBag->GetItemByPos(j))
{
if (IsFishingPole(item))
{
_pole = item;
return true;
}
}
}
}
}
if (sRandomPlayerbotMgr->IsRandomBot(bot))
{
bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole
return true;
}
Player* master = botAI->GetMaster();
if (!master)
return false;
std::string masterName = master->GetName();
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
"no_fishing_pole_error", "I don't have a Fishing Pole",{});
botAI->Whisper(text, masterName);
return false;
}
bool FishingAction::Execute(Event event)
{
WorldPosition target = WorldPosition();
WorldPosition fishingHole = FindFishingHole(botAI);
if (fishingHole.IsValid())
{
Position pos = fishingHole;
float distance = bot->GetExactDist2d(&pos);
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
if (distance < MAX_DISTANCE_TO_WATER &&
distance > MIN_DISTANCE_TO_WATER && hasLOS)
target = fishingHole;
}
if (!target.IsValid())
{
target = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), bot->GetMap(), bot->GetPhaseMask(),
MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true, 32);
if (!target.IsValid())
return false;
}
Position pos = target;
if (!bot->HasInArc(1.0, &pos, 1.0))
{
float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY());
bot->SetOrientation(angle);
if (!bot->IsRooted())
bot->SendMovementFlagUpdate();
}
EquipFishingPoleAction equipAction(botAI);
if (equipAction.isUseful())
return equipAction.Execute(event);
botAI->CastSpell(FISHING_SPELL, bot);
botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT);
return true;
}
bool FishingAction::isUseful()
{
if (!AI_VALUE(bool, "can fish"))
return false;
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
return pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition();
}
bool UseBobberAction::isUseful()
{
return AI_VALUE(bool, "can use fishing bobber");
}
bool UseBobberAction::Execute(Event event)
{
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
for (auto const& guid : gos)
{
if (GameObject* go = botAI->GetGameObject(guid))
{
if (go->GetEntry() != FISHING_BOBBER)
continue;
if (go->GetOwnerGUID() != bot->GetGUID())
continue;
if (go->getLootState() == GO_READY)
{
go->Use(bot);
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
return true;
}
}
}
return false;
}
bool EndMasterFishingAction::Execute(Event event)
{
botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT);
return true;
}
bool EndMasterFishingAction::isUseful()
{
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
if (pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition())
return false;
WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f);
return !nearWater.IsValid();
}
bool RemoveBobberStrategyAction::Execute(Event event)
{
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
return true;
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FISHINGACTION_H
#define _PLAYERBOT_FISHINGACTION_H
#include "Action.h"
#include "MovementActions.h"
#include "Event.h"
#include "Playerbots.h"
extern const uint32 FISHING_SPELL;
extern const uint32 FISHING_POLE;
extern const uint32 FISHING_BOBBER;
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
class PlayerbotAI;
class FishingAction : public Action
{
public:
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
bool Execute(Event event) override;
bool isUseful() override;
};
class EquipFishingPoleAction : public Action
{
public:
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
bool Execute(Event event) override;
bool isUseful() override;
private:
Item* _pole = nullptr;
};
class MoveNearWaterAction : public MovementAction
{
public:
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
};
class UseBobberAction : public Action
{
public:
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
bool Execute(Event event) override;
bool isUseful() override;
};
class EndMasterFishingAction : public Action
{
public:
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
bool Execute(Event event) override;
bool isUseful() override;
};
class RemoveBobberStrategyAction : public Action
{
public:
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -97,8 +97,6 @@ bool FollowAction::isUseful()
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
}
if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT))
return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster);
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
}

View File

@@ -265,6 +265,11 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
}
}
NextAction** CastSpellAction::getPrerequisites()
{
return nullptr;
}
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
{
return context->GetValue<Unit*>("attacker without aura", spell);

View File

@@ -27,11 +27,7 @@ public:
bool isUseful() override;
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
std::vector<NextAction> getPrerequisites() override
{
return {};
}
NextAction** getPrerequisites() override;
std::string const getSpell() { return spell; }
protected:
@@ -197,12 +193,10 @@ public:
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
std::string const GetTargetName() override { return "party member to resurrect"; }
std::vector<NextAction> getPrerequisites() override
NextAction** getPrerequisites() override
{
return NextAction::merge(
{ NextAction("reach party member to resurrect") },
Action::getPrerequisites()
);
return NextAction::merge(NextAction::array(0, new NextAction("reach party member to resurrect"), NULL),
Action::getPrerequisites());
}
};

View File

@@ -946,7 +946,36 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
bool MovementAction::IsMovingAllowed()
{
return botAI->CanMove();
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
// states are set when handling certain aura effects. We don't check against
// UNIT_STATE_ROOT here, because this state is used by vehicles.
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
return false;
// Common CC effects, ordered by frequency: rooted > frozen > polymorphed
if (bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed())
return false;
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
return false;
// Traveling state: taxi flight and being teleported (relatively rare)
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
bot->IsBeingTeleported())
return false;
// Vehicle state: is in the vehicle and can control it (rare, content-specific).
// We need to check charmed state AFTER vehicle one, cuz that's how it works:
// passengers are set to charmed by vehicle with CHARM_TYPE_VEHICLE.
if ((bot->GetVehicle() && !botAI->IsInVehicle(true)) || bot->IsCharmed())
return false;
return true;
}
bool MovementAction::Follow(Unit* target, float distance) { return Follow(target, distance, GetFollowAngle()); }

View File

@@ -68,15 +68,17 @@ bool RpgAction::SetNextRpgAction()
triggerNode->setTrigger(trigger);
std::vector<NextAction> nextActions = triggerNode->getHandlers();
NextAction** nextActions = triggerNode->getHandlers();
Trigger* trigger = triggerNode->getTrigger();
bool isChecked = false;
for (NextAction nextAction : nextActions)
for (int32 i = 0; i < NextAction::size(nextActions); i++)
{
if (nextAction.getRelevance() > 5.0f)
NextAction* nextAction = nextActions[i];
if (nextAction->getRelevance() > 5.0f)
continue;
if (!isChecked && !trigger->IsActive())
@@ -84,13 +86,14 @@ bool RpgAction::SetNextRpgAction()
isChecked = true;
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
if (!dynamic_cast<RpgEnabled*>(action) || !action->isPossible() || !action->isUseful())
continue;
actions.push_back(action);
relevances.push_back((nextAction.getRelevance() - 1) * 500);
relevances.push_back((nextAction->getRelevance() - 1) * 500);
}
NextAction::destroy(nextActions);
}
}

View File

@@ -14,8 +14,6 @@
#include "PositionValue.h"
#include "ByteBuffer.h"
std::set<uint32> const FISHING_SPELLS = {7620, 7731, 7732, 18248, 33095, 51294};
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
bool important)
{
@@ -59,16 +57,6 @@ bool SeeSpellAction::Execute(Event event)
// if (!botAI->HasStrategy("RTSC", botAI->GetState()))
// return false;
if (FISHING_SPELLS.find(spellId) != FISHING_SPELLS.end())
{
if (AI_VALUE(bool, "can fish") && sPlayerbotAIConfig->enableFishingWithMaster)
{
botAI->ChangeStrategy("+master fishing", BOT_STATE_NON_COMBAT);
return true;
}
return false;
}
if (spellId != RTSC_MOVE_SPELL)
return false;

View File

@@ -10,7 +10,7 @@
#include "PlayerbotFactory.h"
#include "Playerbots.h"
void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg)
void TrainerAction::Learn(uint32 cost, TrainerSpell const* tSpell, std::ostringstream& msg)
{
if (sPlayerbotAIConfig->autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold))
{
@@ -23,7 +23,7 @@ void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostring
bot->ModifyMoney(-int32(cost));
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell.SpellId);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
if (!spellInfo)
return;
@@ -41,8 +41,10 @@ void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostring
}
}
if (!learned && !bot->HasSpell(tSpell.SpellId))
bot->learnSpell(tSpell.SpellId);
if (!learned && !bot->HasSpell(tSpell->spell))
{
bot->learnSpell(tSpell->spell);
}
msg << " - learned";
}
@@ -51,35 +53,37 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell
{
TellHeader(creature);
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer)
return;
TrainerSpellData const* trainer_spells = creature->GetTrainerSpells();
float fDiscountMod = bot->GetReputationPriceDiscount(creature);
uint32 totalCost = 0;
for (auto& spell : trainer->GetSpells())
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin();
itr != trainer_spells->spellList.end(); ++itr)
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
TrainerSpell const* tSpell = &itr->second;
if (!tSpell)
continue;
if (!spells.empty() && spells.find(spell.SpellId) == spells.end())
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
if (state != TRAINER_SPELL_GREEN)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId);
uint32 spellId = tSpell->spell;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
continue;
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod));
if (!spells.empty() && spells.find(tSpell->spell) == spells.end())
continue;
uint32 cost = uint32(floor(tSpell->spellCost * fDiscountMod));
totalCost += cost;
std::ostringstream out;
out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost);
if (action)
(this->*action)(cost, spell, out);
(this->*action)(cost, tSpell, out);
botAI->TellMaster(out);
}
@@ -108,14 +112,15 @@ bool TrainerAction::Execute(Event event)
if (!creature || !creature->IsTrainer())
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer || !trainer->IsTrainerValidForPlayer(bot))
if (!creature->IsValidTrainerForPlayer(bot))
{
botAI->TellError("This trainer cannot teach me");
return false;
}
std::vector<Trainer::Spell> trainer_spells = trainer->GetSpells();
if (trainer_spells.empty())
// check present spell in trainer spell list
TrainerSpellData const* cSpells = creature->GetTrainerSpells();
if (!cSpells)
{
botAI->TellError("No spells can be learned from this trainer");
return false;
@@ -128,7 +133,7 @@ bool TrainerAction::Execute(Event event)
if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr->IsRandomBot(bot) ||
(sPlayerbotAIConfig->autoTrainSpells != "no" &&
(trainer->GetTrainerType() != Trainer::Type::Tradeskill ||
(creature->GetCreatureTemplate()->trainer_type != TRAINER_TYPE_TRADESKILLS ||
!botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make
// config dependent.
Iterate(creature, &TrainerAction::Learn, spells);

View File

@@ -8,7 +8,6 @@
#include "Action.h"
#include "ChatHelper.h"
#include "Trainer.h"
class Creature;
class PlayerbotAI;
@@ -23,9 +22,9 @@ public:
bool Execute(Event event) override;
private:
typedef void (TrainerAction::*TrainerSpellAction)(uint32, const Trainer::Spell, std::ostringstream& msg);
typedef void (TrainerAction::*TrainerSpellAction)(uint32, TrainerSpell const*, std::ostringstream& msg);
void Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells);
void Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg);
void Learn(uint32 cost, TrainerSpell const* tSpell, std::ostringstream& msg);
void TellHeader(Creature* creature);
void TellFooter(uint32 totalCost);
};

View File

@@ -52,7 +52,7 @@ bool UseMeetingStoneAction::Execute(Event event)
if (!goInfo || goInfo->entry != 179944)
return false;
return Teleport(master, bot, false);
return Teleport(master, bot);
}
bool SummonAction::Execute(Event event)
@@ -70,16 +70,16 @@ bool SummonAction::Execute(Event event)
{
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
AI_VALUE(std::list<FleeInfo>&, "recently flee info").clear();
return Teleport(master, bot, true);
return Teleport(master, bot);
}
if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true))
if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot))
{
botAI->TellMasterNoFacing("Hello!");
return true;
}
if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true))
if (SummonUsingGos(bot, master) || SummonUsingNpcs(bot, master))
{
botAI->TellMasterNoFacing("Welcome!");
return true;
@@ -88,7 +88,7 @@ bool SummonAction::Execute(Event event)
return false;
}
bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserveAuras)
bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
{
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
@@ -98,14 +98,14 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserv
for (GameObject* go : targets)
{
if (go->isSpawned() && go->GetGoType() == GAMEOBJECT_TYPE_MEETINGSTONE)
return Teleport(summoner, player, preserveAuras);
return Teleport(summoner, player);
}
botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you");
return false;
}
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras)
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
{
if (!sPlayerbotAIConfig->summonAtInnkeepersEnabled)
return false;
@@ -139,7 +139,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser
Spell spell(player, spellInfo, TRIGGERED_NONE);
spell.SendSpellCooldown();
return Teleport(summoner, player, preserveAuras);
return Teleport(summoner, player);
}
}
@@ -147,7 +147,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser
return false;
}
bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras)
bool SummonAction::Teleport(Player* summoner, Player* player)
{
// Player* master = GetMaster();
if (!summoner)
@@ -208,11 +208,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras
player->GetMotionMaster()->Clear();
AI_VALUE(LastMovement&, "last movement").clear();
if (!preserveAuras)
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED |
AURA_INTERRUPT_FLAG_CHANGE_MAP);
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
player->TeleportTo(mapId, x, y, z, 0);
if (botAI->HasStrategy("stay", botAI->GetState()))

View File

@@ -19,9 +19,9 @@ public:
bool Execute(Event event) override;
protected:
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
bool Teleport(Player* summoner, Player* player);
bool SummonUsingGos(Player* summoner, Player* player);
bool SummonUsingNpcs(Player* summoner, Player* player);
};
class UseMeetingStoneAction : public SummonAction

View File

@@ -12,10 +12,25 @@ class BloodDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
public:
BloodDKStrategyActionNodeFactory()
{
// creators["melee"] = &melee;
// creators["blood strike"] = &blood_strike;
creators["rune strike"] = &rune_strike;
creators["heart strike"] = &heart_strike;
creators["death strike"] = &death_strike;
// creators["death grip"] = &death_grip;
// creators["plague strike"] = &plague_strike;
// creators["pestilence"] = &pestilence;
creators["icy touch"] = &icy_touch;
// creators["obliterate"] = &obliterate;
// creators["blood boil"] = &blood_boil;
// creators["mark of_blood"] = &mark_of_blood;
// creators["blood presence"] = &blood_presence;
// creators["rune tap"] = &rune_tap;
// creators["vampiric blood"] = &vampiric_blood;
// creators["death pact"] = &death_pact;
// creators["death rune_mastery"] = &death_rune_mastery;
// creators["hysteria"] = &hysteria;
// creators["dancing weapon"] = &dancing_weapon;
creators["dark command"] = &dark_command;
creators["taunt spell"] = &dark_command;
}
@@ -23,61 +38,39 @@ public:
private:
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rune strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("rune strike",
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("icy touch",
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"heart strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("heart strike",
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"death strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("death strike",
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* dark_command([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"dark command",
{
NextAction("frost presence")
},
/*A*/ {
NextAction("death grip")
},
/*C*/ {}
);
return new ActionNode("dark command",
/*P*/ NextAction::array(0, new NextAction("frost presence"), NULL),
/*A*/ NextAction::array(0, new NextAction("death grip"), NULL),
/*C*/ NULL);
}
};
@@ -86,80 +79,33 @@ BloodDKStrategy::BloodDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
actionNodeFactories.Add(new BloodDKStrategyActionNodeFactory());
}
std::vector<NextAction> BloodDKStrategy::getDefaultActions()
NextAction** BloodDKStrategy::getDefaultActions()
{
return {
NextAction("rune strike", ACTION_DEFAULT + 0.8f),
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
NextAction("heart strike", ACTION_DEFAULT + 0.6f),
NextAction("blood strike", ACTION_DEFAULT + 0.5f),
NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
NextAction("death coil", ACTION_DEFAULT + 0.3f),
NextAction("plague strike", ACTION_DEFAULT + 0.2f),
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
return NextAction::array(
0, new NextAction("rune strike", ACTION_DEFAULT + 0.8f), new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
new NextAction("heart strike", ACTION_DEFAULT + 0.6f), new NextAction("blood strike", ACTION_DEFAULT + 0.5f),
new NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
new NextAction("death coil", ACTION_DEFAULT + 0.3f), new NextAction("plague strike", ACTION_DEFAULT + 0.2f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
}
void BloodDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"rune strike", NextAction::array(0, new NextAction("rune strike", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(
new TriggerNode(
"rune strike",
{
NextAction("rune strike", ACTION_NORMAL + 3)
}
)
);
new TriggerNode("blood tap", NextAction::array(0, new NextAction("blood tap", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"blood tap",
{
NextAction("blood tap", ACTION_HIGH + 5)
}
)
);
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr)));
triggers.push_back(
new TriggerNode(
"lose aggro",
{
NextAction("dark command", ACTION_HIGH + 3)
}
)
);
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 4),
new NextAction("death strike", ACTION_HIGH + 3), nullptr)));
triggers.push_back(
new TriggerNode(
"low health",
{
NextAction("army of the dead", ACTION_HIGH + 4),
NextAction("death strike", ACTION_HIGH + 3)
}
)
);
new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"critical health",
{
NextAction("vampiric blood", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"icy touch",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"plague strike",
{
NextAction("plague strike", ACTION_HIGH + 2)
}
)
);
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
}

View File

@@ -17,7 +17,7 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "blood"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_TANK | STRATEGY_TYPE_MELEE; }
};

View File

@@ -11,40 +11,39 @@
#include "SpellInfo.h"
#include "SpellMgr.h"
std::vector<NextAction> CastDeathchillAction::getPrerequisites()
NextAction** CastDeathchillAction::getPrerequisites()
{
return NextAction::merge({ NextAction("frost presence") },
return NextAction::merge(NextAction::array(0, new NextAction("frost presence"), nullptr),
CastSpellAction::getPrerequisites());
}
std::vector<NextAction> CastUnholyMeleeSpellAction::getPrerequisites()
NextAction** CastUnholyMeleeSpellAction::getPrerequisites()
{
return NextAction::merge({ NextAction("unholy presence") },
return NextAction::merge(NextAction::array(0, new NextAction("unholy presence"), nullptr),
CastMeleeSpellAction::getPrerequisites());
}
std::vector<NextAction> CastFrostMeleeSpellAction::getPrerequisites()
NextAction** CastFrostMeleeSpellAction::getPrerequisites()
{
return NextAction::merge({ NextAction("frost presence") },
return NextAction::merge(NextAction::array(0, new NextAction("frost presence"), nullptr),
CastMeleeSpellAction::getPrerequisites());
}
std::vector<NextAction> CastBloodMeleeSpellAction::getPrerequisites()
NextAction** CastBloodMeleeSpellAction::getPrerequisites()
{
return NextAction::merge({ NextAction("blood presence") },
return NextAction::merge(NextAction::array(0, new NextAction("blood presence"), nullptr),
CastMeleeSpellAction::getPrerequisites());
}
bool CastRaiseDeadAction::Execute(Event event)
{
const bool result = CastBuffSpellAction::Execute(event);
bool result = CastBuffSpellAction::Execute(event);
if (!result)
{
return false;
const uint32_t spellId = AI_VALUE2(uint32_t, "spell id", spell);
}
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
// SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellId);
bot->AddSpellCooldown(spellId, 0, 3 * 60 * 1000);
return true;
}

View File

@@ -34,7 +34,7 @@ class CastDeathchillAction : public CastBuffSpellAction
public:
CastDeathchillAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "deathchill") {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
};
class CastDarkCommandAction : public CastSpellAction
@@ -52,7 +52,7 @@ class CastUnholyMeleeSpellAction : public CastMeleeSpellAction
public:
CastUnholyMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
};
// Frost presence
@@ -61,7 +61,7 @@ class CastFrostMeleeSpellAction : public CastMeleeSpellAction
public:
CastFrostMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
};
// Blood presence
@@ -70,7 +70,7 @@ class CastBloodMeleeSpellAction : public CastMeleeSpellAction
public:
CastBloodMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
};
class CastRuneStrikeAction : public CastMeleeSpellAction
@@ -79,6 +79,10 @@ public:
CastRuneStrikeAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "rune strike") {}
};
// debuff
// BEGIN_DEBUFF_ACTION(CastPestilenceAction, "pestilence")
// END_SPELL_ACTION()
class CastPestilenceAction : public CastSpellAction
{
public:
@@ -86,12 +90,20 @@ public:
ActionThreatType getThreatType() override { return ActionThreatType::None; }
};
// debuff
// BEGIN_DEBUFF_ACTION(CastHowlingBlastAction, "howling blast")
// END_SPELL_ACTION()
class CastHowlingBlastAction : public CastSpellAction
{
public:
CastHowlingBlastAction(PlayerbotAI* ai) : CastSpellAction(ai, "howling blast") {}
};
// debuff it
// BEGIN_DEBUFF_ACTION(CastIcyTouchAction, "icy touch")
// END_SPELL_ACTION()
class CastIcyTouchAction : public CastSpellAction
{
public:
@@ -114,6 +126,8 @@ class CastPlagueStrikeAction : public CastSpellAction
public:
CastPlagueStrikeAction(PlayerbotAI* ai) : CastSpellAction(ai, "plague strike") {}
};
// BEGIN_DEBUFF_ACTION(CastPlagueStrikeAction, "plague strike")
// END_SPELL_ACTION()
class CastPlagueStrikeOnAttackerAction : public CastDebuffSpellOnMeleeAttackerAction
{

View File

@@ -16,68 +16,66 @@ public:
creators["obliterate"] = &obliterate;
creators["howling blast"] = &howling_blast;
creators["frost strike"] = &frost_strike;
// creators["chains of ice"] = &chains_of_ice;
creators["rune strike"] = &rune_strike;
// creators["icy clutch"] = &icy_clutch;
// creators["horn of winter"] = &horn_of_winter;
// creators["killing machine"] = &killing_machine;
// creators["frost presence"] = &frost_presence;
// creators["deathchill"] = &deathchill;
// creators["icebound fortitude"] = &icebound_fortitude;
// creators["mind freeze"] = &mind_freeze;
// creators["hungering cold"] = &hungering_cold;
creators["unbreakable armor"] = &unbreakable_armor;
// creators["improved icy talons"] = &improved_icy_talons;
}
private:
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("icy touch",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* obliterate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"obliterate",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("obliterate",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rune strike",
/*P*/ { NextAction("blood presence") },
/*A*/ { NextAction("melee") },
/*C*/ {}
);
return new ActionNode("rune strike",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* frost_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"frost strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("frost strike",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* howling_blast([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"howling blast",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("howling blast",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"unbreakable armor",
/*P*/ { NextAction("blood tap") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("unbreakable armor",
/*P*/ NextAction::array(0, new NextAction("blood tap"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -86,84 +84,41 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
actionNodeFactories.Add(new FrostDKStrategyActionNodeFactory());
}
std::vector<NextAction> FrostDKStrategy::getDefaultActions()
NextAction** FrostDKStrategy::getDefaultActions()
{
return {
NextAction("obliterate", ACTION_DEFAULT + 0.7f),
NextAction("frost strike", ACTION_DEFAULT + 0.4f),
NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
return NextAction::array(
0, new NextAction("obliterate", ACTION_DEFAULT + 0.7f),
new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
}
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"unbreakable armor",
{
NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f)
}
)
);
triggers.push_back(new TriggerNode(
"unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f), nullptr)));
triggers.push_back(new TriggerNode(
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_DEFAULT + 0.5f), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(new TriggerNode(
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
triggers.push_back(
new TriggerNode(
"freezing fog",
{
NextAction("howling blast", ACTION_DEFAULT + 0.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"high blood rune",
{
NextAction("blood strike", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"army of the dead",
{
NextAction("army of the dead", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"icy touch",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"plague strike",
{
NextAction("plague strike", ACTION_HIGH + 2)
}
)
);
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
// triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune
// weapon", ACTION_NORMAL + 4), nullptr)));
}
void FrostDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("howling blast", ACTION_HIGH + 4)
}
)
);
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("howling blast", ACTION_HIGH + 4), nullptr)));
}

View File

@@ -17,7 +17,7 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "frost"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_MELEE; }
};

View File

@@ -20,17 +20,17 @@ private:
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("bone shield",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("horn of winter",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -44,18 +44,19 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("no pet", { NextAction("raise dead", ACTION_NORMAL + 1) }));
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(
new TriggerNode("horn of winter", { NextAction("horn of winter", 21.0f) }));
new TriggerNode("horn of winter", NextAction::array(0, new NextAction("horn of winter", 21.0f), nullptr)));
triggers.push_back(
new TriggerNode("bone shield", { NextAction("bone shield", 21.0f) }));
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", 21.0f), nullptr)));
triggers.push_back(
new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f) }));
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), NULL)));
triggers.push_back(
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), NULL)));
}
void DKBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// triggers.push_back(new TriggerNode("improved icy talons", NextAction::array(0, new NextAction("improved icy
// talons", 19.0f), nullptr)));
}

View File

@@ -54,105 +54,105 @@ private:
static ActionNode* death_coil([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death coil",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* death_grip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death grip",
/*P*/ {},
/*A*/ { NextAction("icy touch") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("icy touch"), nullptr),
/*C*/ nullptr);
}
static ActionNode* plague_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("plague strike",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icy touch",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("heart strike",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* pestilence([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("pestilence",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("horn of winter",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("bone shield",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* killing_machine([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("killing machine",
/*P*/ {},
/*A*/ { NextAction("improved icy talons") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("improved icy talons"), nullptr),
/*C*/ nullptr);
}
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("corpse explosion",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* death_and_decay([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death and decay",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* anti_magic_zone([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("anti magic zone",
/*P*/ {},
/*A*/ { NextAction("anti magic shell") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("anti magic shell"), nullptr),
/*C*/ nullptr);
}
static ActionNode* icebound_fortitude([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icebound fortitude",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -165,29 +165,36 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
MeleeCombatStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("high aoe", NextAction::array(0, new NextAction("anti magic shell",
// ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode("death coil", NextAction::array(0, new
// NextAction("death coil", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode("critical aoe heal",
// NextAction::array(0, new NextAction("anti magic zone", ACTION_EMERGENCY + 1), nullptr)));
triggers.push_back(
new TriggerNode("no pet", { NextAction("raise dead", ACTION_NORMAL + 5) }));
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(
new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f) }));
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("mind freeze", { NextAction("mind freeze", ACTION_HIGH + 1) }));
new TriggerNode("mind freeze", NextAction::array(0, new NextAction("mind freeze", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("mind freeze on enemy healer",
{ NextAction("mind freeze on enemy healer", ACTION_HIGH + 1) }));
NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"horn of winter", { NextAction("horn of winter", ACTION_NORMAL + 1) }));
"horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode("critical health",
{ NextAction("death pact", ACTION_HIGH + 5) }));
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode("low health", { NextAction("icebound fortitude", ACTION_HIGH + 5),
NextAction("rune tap", ACTION_HIGH + 4) }));
new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5),
new NextAction("rune tap", ACTION_HIGH + 4), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe", { NextAction("death and decay", ACTION_HIGH + 9),
NextAction("pestilence", ACTION_NORMAL + 4),
NextAction("blood boil", ACTION_NORMAL + 3) }));
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9),
new NextAction("pestilence", ACTION_NORMAL + 4),
new NextAction("blood boil", ACTION_NORMAL + 3), nullptr)));
// triggers.push_back(new TriggerNode("light aoe", NextAction::array(0,
// new NextAction("pestilence", ACTION_NORMAL + 4),
// nullptr)));
triggers.push_back(
new TriggerNode("pestilence glyph", { NextAction("pestilence", ACTION_HIGH + 9) }));
new TriggerNode("pestilence glyph", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL)));
}

View File

@@ -1,4 +1,4 @@
/*
#/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it and/or modify it under version 3 of the License, or (at your option), any later version.
*/
@@ -11,8 +11,21 @@ class UnholyDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
public:
UnholyDKStrategyActionNodeFactory()
{
// Unholy
// creators["bone shield"] = &bone_shield;
// creators["plague strike"] = &plague_strike;
// creators["death grip"] = &death_grip;
// creators["death coil"] = &death_coil;
creators["death strike"] = &death_strike;
// creators["unholy blight"] = &unholy_blight;
creators["scourge strike"] = &scourge_strike;
// creators["death and decay"] = &death_and_decay;
// creators["unholy pressence"] = &unholy_pressence;
// creators["raise dead"] = &raise_dead;
// creators["army of the dead"] = &army of the dead;
// creators["summon gargoyle"] = &army of the dead;
// creators["anti magic shell"] = &anti_magic_shell;
// creators["anti magic zone"] = &anti_magic_zone;
creators["ghoul frenzy"] = &ghoul_frenzy;
creators["corpse explosion"] = &corpse_explosion;
creators["icy touch"] = &icy_touch;
@@ -21,49 +34,39 @@ public:
private:
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"death strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("death strike",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ghoul frenzy",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("ghoul frenzy",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"corpse explosion",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("corpse explosion",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"scourge strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("scourge strike",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("icy touch",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -72,121 +75,69 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
actionNodeFactories.Add(new UnholyDKStrategyActionNodeFactory());
}
std::vector<NextAction> UnholyDKStrategy::getDefaultActions()
NextAction** UnholyDKStrategy::getDefaultActions()
{
return {
NextAction("death and decay", ACTION_HIGH + 5),
NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
NextAction("death coil", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
return NextAction::array(
0, new NextAction("death and decay", ACTION_HIGH + 5),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
// new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
new NextAction("death coil", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
}
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"death and decay cooldown", NextAction::array(0,
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
new NextAction("blood strike", ACTION_DEFAULT + 0.6f),
new NextAction("plague strike", ACTION_DEFAULT + 0.5f),
nullptr)));
triggers.push_back(new TriggerNode("dd cd and no desolation",
NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr)));
// triggers.push_back(
// new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
// triggers.push_back(new TriggerNode(
// "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"high frost rune", NextAction::array(0,
new NextAction("icy touch", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"high unholy rune", NextAction::array(0,
new NextAction("plague strike", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(
new TriggerNode(
"death and decay cooldown",
{
NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
NextAction("blood strike", ACTION_DEFAULT + 0.6f),
NextAction("plague strike", ACTION_DEFAULT + 0.5f),
}
)
);
new TriggerNode("dd cd and plague strike 3s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode(
"dd cd and no desolation",
{
NextAction("blood strike", ACTION_DEFAULT + 0.75f)
}
)
);
new TriggerNode("dd cd and icy touch 3s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode(
"high frost rune",
{
NextAction("icy touch", ACTION_NORMAL + 3)
}
)
);
new TriggerNode("no rune", NextAction::array(0, new NextAction("empower rune weapon", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
triggers.push_back(
new TriggerNode(
"high blood rune",
{
NextAction("blood strike", ACTION_NORMAL + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"high unholy rune",
{
NextAction("plague strike", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode("dd cd and plague strike 3s",
{
NextAction("plague strike", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode("dd cd and icy touch 3s",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode("no rune",
{
NextAction("empower rune weapon", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"army of the dead",
{
NextAction("army of the dead", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode("bone shield",
{
NextAction("bone shield", ACTION_HIGH + 3)
}
)
);
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 3), nullptr)));
}
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"loot available",
{
NextAction("corpse explosion", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("death and decay", ACTION_NORMAL + 3),
NextAction("corpse explosion", ACTION_NORMAL + 3)
}
)
);
triggers.push_back(new TriggerNode(
"loot available", NextAction::array(0, new NextAction("corpse explosion", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode(
"medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_NORMAL + 3),
new NextAction("corpse explosion", ACTION_NORMAL + 3), nullptr)));
}

View File

@@ -17,7 +17,7 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "unholy"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_MELEE; }
};

View File

@@ -30,132 +30,107 @@ public:
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("melee",
/*P*/ NextAction::array(0, new NextAction("feral charge - bear"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* feral_charge_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"feral charge - bear",
/*P*/ {},
/*A*/ { NextAction("reach melee") },
/*C*/ {}
);
return new ActionNode("feral charge - bear",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("reach melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* swipe_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"swipe (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("swipe (bear)",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("faerie fire (feral)",
/*P*/ NextAction::array(0, new NextAction("feral charge - bear"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"bear form",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("bear form",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"dire bear form",
/*P*/ { NextAction("caster form") },
/*A*/ { NextAction("bear form") },
/*C*/ {}
);
return new ActionNode("dire bear form",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NextAction::array(0, new NextAction("bear form"), nullptr),
/*C*/ nullptr);
}
static ActionNode* mangle_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("mangle (bear)",
/*P*/ nullptr,
// /*A*/ NextAction::array(0, new NextAction("lacerate"), nullptr),
nullptr,
/*C*/ nullptr);
}
static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"maul",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
return new ActionNode("maul",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* bash([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"bash",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
return new ActionNode("bash",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* swipe([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"swipe",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
return new ActionNode("swipe",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* lacerate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"lacerate",
/*P*/ {},
/*A*/ { NextAction("maul") },
/*C*/ {}
);
return new ActionNode("lacerate",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("maul"), nullptr),
/*C*/ nullptr);
}
static ActionNode* growl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"growl",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("growl",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* demoralizing_roar([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"demoralizing roar",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("demoralizing roar",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -164,93 +139,38 @@ BearTankDruidStrategy::BearTankDruidStrategy(PlayerbotAI* botAI) : FeralDruidStr
actionNodeFactories.Add(new BearTankDruidStrategyActionNodeFactory());
}
std::vector<NextAction> BearTankDruidStrategy::getDefaultActions()
NextAction** BearTankDruidStrategy::getDefaultActions()
{
return {
NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f),
NextAction("lacerate", ACTION_DEFAULT + 0.3f),
NextAction("maul", ACTION_DEFAULT + 0.2f),
NextAction("enrage", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
return NextAction::array(
0, new NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f), new NextAction("lacerate", ACTION_DEFAULT + 0.3f),
new NextAction("maul", ACTION_DEFAULT + 0.2f), new NextAction("enrage", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
}
void BearTankDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("feral charge - bear", ACTION_NORMAL + 8), nullptr)));
// triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", ACTION_HIGH + 9),
// nullptr)));
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - bear", ACTION_NORMAL + 8)
}
)
);
new TriggerNode("bear form", NextAction::array(0, new NextAction("dire bear form", ACTION_HIGH + 8), nullptr)));
triggers.push_back(new TriggerNode(
"low health", NextAction::array(0, new NextAction("frenzied regeneration", ACTION_HIGH + 7), nullptr)));
triggers.push_back(new TriggerNode(
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_HIGH + 7), nullptr)));
triggers.push_back(
new TriggerNode(
"bear form",
{
NextAction("dire bear form", ACTION_HIGH + 8)
}
)
);
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("growl", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode(
"low health",
{
NextAction("frenzied regeneration", ACTION_HIGH + 7)
}
)
);
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("demoralizing roar", ACTION_HIGH + 6),
new NextAction("swipe (bear)", ACTION_HIGH + 6), nullptr)));
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_HIGH + 7)
}
)
);
new TriggerNode("light aoe", NextAction::array(0, new NextAction("swipe (bear)", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"lose aggro",
{
NextAction("growl", ACTION_HIGH + 8)
}
)
);
new TriggerNode("bash", NextAction::array(0, new NextAction("bash", ACTION_INTERRUPT + 2), nullptr)));
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("demoralizing roar", ACTION_HIGH + 6),
NextAction("swipe (bear)", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("swipe (bear)", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"bash",
{
NextAction("bash", ACTION_INTERRUPT + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"bash on enemy healer",
{
NextAction("bash on enemy healer", ACTION_INTERRUPT + 1)
}
)
);
new TriggerNode("bash on enemy healer",
NextAction::array(0, new NextAction("bash on enemy healer", ACTION_INTERRUPT + 1), nullptr)));
}

View File

@@ -17,7 +17,7 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "bear"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_TANK | STRATEGY_TYPE_MELEE; }
};

View File

@@ -28,102 +28,82 @@ public:
private:
static ActionNode* faerie_fire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("faerie fire",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* hibernate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"hibernate",
/*P*/ { NextAction("moonkin form") },
/*A*/ { NextAction("entangling roots") },
/*C*/ {}
);
return new ActionNode("hibernate",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ NextAction::array(0, new NextAction("entangling roots"), nullptr),
/*C*/ nullptr);
}
static ActionNode* entangling_roots([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"entangling roots",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("entangling roots",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"entangling roots on cc",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("entangling roots on cc",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* wrath([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"wrath",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("wrath",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* starfall([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"starfall",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("starfall",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* insect_swarm([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"insect swarm",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("insect swarm",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* moonfire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"moonfire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("moonfire",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* starfire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"starfire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("starfire",
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* moonkin_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"moonkin form",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("moonkin form",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -133,122 +113,55 @@ CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrat
actionNodeFactories.Add(new ShapeshiftDruidStrategyActionNodeFactory());
}
std::vector<NextAction> CasterDruidStrategy::getDefaultActions()
NextAction** CasterDruidStrategy::getDefaultActions()
{
return {
NextAction("starfall", ACTION_HIGH + 1.0f),
NextAction("force of nature", ACTION_DEFAULT + 1.0f),
NextAction("wrath", ACTION_DEFAULT + 0.1f),
};
return NextAction::array(0,
new NextAction("starfall", ACTION_HIGH + 1.0f),
new NextAction("force of nature", ACTION_DEFAULT + 1.0f),
new NextAction("wrath", ACTION_DEFAULT + 0.1f),
// new NextAction("starfire", ACTION_NORMAL),
nullptr);
}
void CasterDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
// ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("eclipse (lunar) cooldown",
NextAction::array(0, new NextAction("starfire", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(new TriggerNode("eclipse (solar) cooldown",
NextAction::array(0, new NextAction("wrath", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(new TriggerNode(
"insect swarm", NextAction::array(0, new NextAction("insect swarm", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"eclipse (lunar) cooldown",
{
NextAction("starfire", ACTION_DEFAULT + 0.2f)
}
)
);
new TriggerNode("moonfire", NextAction::array(0, new NextAction("moonfire", ACTION_NORMAL + 4), nullptr)));
triggers.push_back(
new TriggerNode(
"eclipse (solar) cooldown",
{
NextAction("wrath", ACTION_DEFAULT + 0.2f)
}
)
);
new TriggerNode("eclipse (solar)", NextAction::array(0, new NextAction("wrath", ACTION_NORMAL + 6), nullptr)));
triggers.push_back(new TriggerNode("eclipse (lunar)",
NextAction::array(0, new NextAction("starfire", ACTION_NORMAL + 6), nullptr)));
triggers.push_back(
new TriggerNode(
"insect swarm",
{
NextAction("insect swarm", ACTION_NORMAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"moonfire",
{
NextAction("moonfire", ACTION_NORMAL + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (solar)",
{
NextAction("wrath", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (lunar)",
{
NextAction("starfire", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"medium mana",
{
NextAction("innervate", ACTION_HIGH + 9)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy too close for spell",
{
NextAction("flee", ACTION_MOVE + 9)
}
)
);
new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 9), nullptr)));
triggers.push_back(new TriggerNode("enemy too close for spell",
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
}
void CasterDruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"hurricane channel check",
{
NextAction("cancel channel", ACTION_HIGH + 2)
}
)
);
new TriggerNode("hurricane channel check", NextAction::array(0, new NextAction("cancel channel", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("hurricane", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
NextAction("moonfire on attacker", ACTION_NORMAL + 3)
}
)
);
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"light aoe", NextAction::array(0, new NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
new NextAction("moonfire on attacker", ACTION_NORMAL + 3), NULL)));
}
void CasterDruidDebuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"faerie fire",
{
NextAction("faerie fire", ACTION_HIGH)
}
)
);
new TriggerNode("faerie fire", NextAction::array(0, new NextAction("faerie fire", ACTION_HIGH), nullptr)));
}

View File

@@ -18,7 +18,7 @@ public:
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "caster"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; }
};

View File

@@ -28,112 +28,90 @@ public:
private:
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("faerie fire (feral)",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ { NextAction("feral charge - cat") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("melee",
/*P*/ NextAction::array(0, new NextAction("feral charge - cat"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"feral charge - cat",
/*P*/ {},
/*A*/ { NextAction("reach melee") },
/*C*/ {}
);
return new ActionNode("feral charge - cat",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("reach melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cat form",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {}
);
return new ActionNode("cat form",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"claw",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
return new ActionNode("claw",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
/*C*/ nullptr);
}
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("mangle (cat)",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("rake",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ { NextAction("rip") },
/*C*/ {}
);
return new ActionNode("ferocious bite",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("rip"), nullptr),
/*C*/ nullptr);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("rip",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"pounce",
/*P*/ {},
/*A*/ { NextAction("ravage") },
/*C*/ {}
);
return new ActionNode("pounce",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("ravage"), nullptr),
/*C*/ nullptr);
}
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ravage",
/*P*/ {},
/*A*/ { NextAction("shred") },
/*C*/ {}
);
return new ActionNode("ravage",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("shred"), nullptr),
/*C*/ nullptr);
}
};
@@ -142,11 +120,9 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg
actionNodeFactories.Add(new CatDpsDruidStrategyActionNodeFactory());
}
std::vector<NextAction> CatDpsDruidStrategy::getDefaultActions()
NextAction** CatDpsDruidStrategy::getDefaultActions()
{
return {
NextAction("tiger's fury", ACTION_DEFAULT + 0.1f)
};
return NextAction::array(0, new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr);
}
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@@ -154,161 +130,50 @@ void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
FeralDruidStrategy::InitTriggers(triggers);
// Default priority
triggers.push_back(new TriggerNode("almost full energy available",
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
triggers.push_back(new TriggerNode("combo points not full",
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
triggers.push_back(new TriggerNode("almost full energy available",
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
triggers.push_back(new TriggerNode("combo points not full and high energy",
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
triggers.push_back(new TriggerNode("almost full energy available",
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(new TriggerNode("combo points not full and high energy",
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy",
{
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("claw", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy",
{
NextAction("claw", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f)
}
)
);
new TriggerNode("faerie fire (feral)",
NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr)));
// Main spell
triggers.push_back(
new TriggerNode(
"cat form", {
NextAction("cat form", ACTION_HIGH + 8)
}
)
);
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode(
"savage roar", {
NextAction("savage roar", ACTION_HIGH + 7)
}
)
);
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
triggers.push_back(new TriggerNode("combo points available",
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
triggers.push_back(new TriggerNode(
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"combo points available",
{
NextAction("rip", ACTION_HIGH + 6)
}
)
);
new TriggerNode("target with combo points almost dead",
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode("mangle (cat)",
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode(
"ferocious bite time",
{
NextAction("ferocious bite", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"target with combo points almost dead",
{
NextAction("ferocious bite", ACTION_HIGH + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"mangle (cat)",
{
NextAction("mangle (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"rake",
{
NextAction("rake", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"medium threat",
{
NextAction("cower", ACTION_HIGH + 1)
}
)
);
new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr)));
// AOE
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("swipe (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("rake on attacker", ACTION_HIGH + 2)
}
)
);
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
triggers.push_back(new TriggerNode(
"light aoe", NextAction::array(0, new NextAction("rake on attacker", ACTION_HIGH + 2), nullptr)));
// Reach target
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9), nullptr)));
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - cat", ACTION_HIGH + 9)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("dash", ACTION_HIGH + 8)
}
)
);
new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("dash", ACTION_HIGH + 8), nullptr)));
}
void CatAoeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) {}

View File

@@ -18,7 +18,7 @@ public:
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "cat"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; }
};

View File

@@ -11,15 +11,15 @@
#include "AoeValues.h"
#include "TargetValue.h"
std::vector<NextAction> CastAbolishPoisonAction::getAlternatives()
NextAction** CastAbolishPoisonAction::getAlternatives()
{
return NextAction::merge({ NextAction("cure poison") },
return NextAction::merge(NextAction::array(0, new NextAction("cure poison"), nullptr),
CastSpellAction::getPrerequisites());
}
std::vector<NextAction> CastAbolishPoisonOnPartyAction::getAlternatives()
NextAction** CastAbolishPoisonOnPartyAction::getAlternatives()
{
return NextAction::merge({ NextAction("cure poison on party") },
return NextAction::merge(NextAction::array(0, new NextAction("cure poison on party"), nullptr),
CastSpellAction::getPrerequisites());
}
@@ -60,15 +60,15 @@ bool CastStarfallAction::isUseful()
return true;
}
std::vector<NextAction> CastReviveAction::getPrerequisites()
NextAction** CastReviveAction::getPrerequisites()
{
return NextAction::merge({ NextAction("caster form") },
return NextAction::merge(NextAction::array(0, new NextAction("caster form"), nullptr),
ResurrectPartyMemberAction::getPrerequisites());
}
std::vector<NextAction> CastRebirthAction::getPrerequisites()
NextAction** CastRebirthAction::getPrerequisites()
{
return NextAction::merge({ NextAction("caster form") },
return NextAction::merge(NextAction::array(0, new NextAction("caster form"), nullptr),
ResurrectPartyMemberAction::getPrerequisites());
}

View File

@@ -74,7 +74,7 @@ class CastReviveAction : public ResurrectPartyMemberAction
public:
CastReviveAction(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, "revive") {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
};
class CastRebirthAction : public ResurrectPartyMemberAction
@@ -82,7 +82,7 @@ class CastRebirthAction : public ResurrectPartyMemberAction
public:
CastRebirthAction(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, "rebirth") {}
std::vector<NextAction> getPrerequisites() override;
NextAction** getPrerequisites() override;
bool isUseful() override;
};
@@ -223,7 +223,7 @@ class CastAbolishPoisonAction : public CastCureSpellAction
{
public:
CastAbolishPoisonAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "abolish poison") {}
std::vector<NextAction> getAlternatives() override;
NextAction** getAlternatives() override;
};
class CastAbolishPoisonOnPartyAction : public CurePartyMemberAction
@@ -233,7 +233,7 @@ public:
{
}
std::vector<NextAction> getAlternatives() override;
NextAction** getAlternatives() override;
};
class CastBarkskinAction : public CastBuffSpellAction

View File

@@ -17,9 +17,9 @@ bool CastBearFormAction::isUseful()
return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget());
}
std::vector<NextAction> CastDireBearFormAction::getAlternatives()
NextAction** CastDireBearFormAction::getAlternatives()
{
return NextAction::merge({ NextAction("bear form") },
return NextAction::merge(NextAction::array(0, new NextAction("bear form"), nullptr),
CastSpellAction::getAlternatives());
}

View File

@@ -24,7 +24,7 @@ class CastDireBearFormAction : public CastBuffSpellAction
public:
CastDireBearFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "dire bear form") {}
std::vector<NextAction> getAlternatives() override;
NextAction** getAlternatives() override;
};
class CastCatFormAction : public CastBuffSpellAction

View File

@@ -26,65 +26,65 @@ private:
static ActionNode* survival_instincts([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("survival instincts",
/*P*/ {},
/*A*/ { NextAction("barkskin") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("barkskin"), nullptr),
/*C*/ nullptr);
}
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("thorns",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* omen_of_clarity([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("omen of clarity",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("prowl",
/*P*/ { NextAction("cat form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("cat form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};
@@ -98,16 +98,20 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
// ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", { NextAction("reach melee", ACTION_HIGH + 1) }));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
// enemy contact", ACTION_NORMAL + 8), nullptr)));
triggers.push_back(new TriggerNode(
"critical health", { NextAction("survival instincts", ACTION_EMERGENCY + 1) }));
"critical health", NextAction::array(0, new NextAction("survival instincts", ACTION_EMERGENCY + 1), nullptr)));
triggers.push_back(new TriggerNode(
"omen of clarity", { NextAction("omen of clarity", ACTION_HIGH + 9) }));
"omen of clarity", NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 9), nullptr)));
triggers.push_back(new TriggerNode("player has flag",
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
triggers.push_back(new TriggerNode("enemy flagcarrier near",
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
triggers.push_back(
new TriggerNode("berserk", { NextAction("berserk", ACTION_HIGH + 6) }));
new TriggerNode("berserk", NextAction::array(0, new NextAction("berserk", ACTION_HIGH + 6), nullptr)));
}

View File

@@ -27,49 +27,49 @@ private:
static ActionNode* regrowth([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("regrowth",
/*P*/ { NextAction("caster form") },
/*A*/ { NextAction("healing touch") },
/*C*/ { NextAction("melee", 10.0f) });
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NextAction::array(0, new NextAction("healing touch"), nullptr),
/*C*/ NextAction::array(0, new NextAction("melee", 10.0f), nullptr));
}
static ActionNode* rejuvenation([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rejuvenation",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healing_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("healing touch",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ { NextAction("healing touch on party") },
/*C*/ { NextAction("melee", 10.0f) });
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr),
/*C*/ NextAction::array(0, new NextAction("melee", 10.0f), nullptr));
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("healing touch on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};

View File

@@ -29,69 +29,76 @@ private:
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("thorns",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* thorns_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("thorns on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mark_of_the_wild([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mark of the wild",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mark_of_the_wild_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mark of the wild on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NULL,
/*C*/ NULL);
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NULL,
/*C*/ NULL);
}
static ActionNode* remove_curse_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("remove curse on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NULL,
/*C*/ NULL);
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NULL,
/*C*/ NULL);
}
static ActionNode* revive([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("revive",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ NULL,
/*C*/ NULL);
}
// static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
// {
// return new ActionNode ("innervate",
// /*P*/ nullptr,
// /*A*/ NextAction::array(0, new NextAction("drink"), nullptr),
// /*C*/ nullptr);
// }
};
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
@@ -103,73 +110,79 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild", { NextAction("mark of the wild", 14.0f) }));
triggers.push_back(new TriggerNode("party member cure poison", { NextAction("abolish poison on party", 20.0f) }));
triggers.push_back(new TriggerNode("party member dead", { NextAction("revive", ACTION_CRITICAL_HEAL + 10) }));
triggers.push_back(new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr)));
// triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", 12.0f), nullptr)));
// triggers.push_back(new TriggerNode("cure poison", NextAction::array(0, new NextAction("abolish poison", 21.0f),
// nullptr)));
triggers.push_back(new TriggerNode("party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr)));
triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr)));
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY
// + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic
// form", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("party member critical health",
{
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
}));
NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
nullptr)));
triggers.push_back(
new TriggerNode("party member low health",
{
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
}));
NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
nullptr)));
triggers.push_back(
new TriggerNode("party member medium health",
{ NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
}));
NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
nullptr)));
triggers.push_back(
new TriggerNode("party member almost full health",
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2) }));
NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL)));
triggers.push_back(
new TriggerNode("party member remove curse",
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr)));
triggers.push_back(
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("party member critical health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
}));
triggers.push_back(new TriggerNode("party member low health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
}));
triggers.push_back(new TriggerNode("party member medium health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
}));
triggers.push_back(new TriggerNode("party member almost full health", {
NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
}));
triggers.push_back(new TriggerNode("party member remove curse", {
NextAction("remove curse on party", ACTION_DISPEL + 7),
}));
triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
nullptr)));
triggers.push_back(new TriggerNode("party member low health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
nullptr)));
triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
nullptr)));
triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
nullptr)));
triggers.push_back(new TriggerNode("party member remove curse", NextAction::array(0,
new NextAction("remove curse on party", ACTION_DISPEL + 7),
nullptr)));
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
if (specTab == 0 || specTab == 2) // Balance or Restoration
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
if (specTab == 1) // Feral
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr)));
}
@@ -182,13 +195,13 @@ void GenericDruidBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild on party", {
NextAction("mark of the wild on party", 13.0f),
}));
triggers.push_back(new TriggerNode("thorns on main tank", {
NextAction("thorns on main tank", 11.0f),
}));
triggers.push_back(new TriggerNode("thorns", {
NextAction("thorns", 10.0f),
}));
triggers.push_back(new TriggerNode("mark of the wild on party", NextAction::array(0,
new NextAction("mark of the wild on party", 13.0f),
nullptr)));
triggers.push_back(new TriggerNode("thorns on main tank", NextAction::array(0,
new NextAction("thorns on main tank", 11.0f),
nullptr)));
triggers.push_back(new TriggerNode("thorns", NextAction::array(0,
new NextAction("thorns", 10.0f),
nullptr)));
}

View File

@@ -27,73 +27,73 @@ private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("melee",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* caster_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("caster form",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* rebirth([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rebirth",
/*P*/ {},
/*A*/ {},
/*C*/ {});
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("entangling roots on cc",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("innervate",
/*P*/ {},
/*A*/ { NextAction("mana potion") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("mana potion"), nullptr),
/*C*/ nullptr);
}
};
@@ -107,52 +107,70 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
CombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) }));
new TriggerNode("low health", NextAction::array(0, new NextAction("barkskin", ACTION_HIGH + 7), nullptr)));
// triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("regrowth",
// ACTION_MEDIUM_HEAL + 2), nullptr))); triggers.push_back(new TriggerNode("party member low health",
// NextAction::array(0, new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1), nullptr)));
// triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("regrowth",
// ACTION_CRITICAL_HEAL + 2), new NextAction("healing touch", ACTION_CRITICAL_HEAL + 2), nullptr)));
// triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0, new NextAction("regrowth
// on party", ACTION_CRITICAL_HEAL + 1), new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 1),
// nullptr))); triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new
// NextAction("rebirth", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode("low mana",
// NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY + 5), nullptr)));
triggers.push_back(new TriggerNode("combat party member dead",
{ NextAction("rebirth", ACTION_HIGH + 9) }));
NextAction::array(0, new NextAction("rebirth", ACTION_HIGH + 9), NULL)));
triggers.push_back(new TriggerNode("being attacked",
{ NextAction("nature's grasp", ACTION_HIGH + 1) }));
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
NextAction::array(0, new NextAction("nature's grasp", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
}
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// triggers.push_back(new TriggerNode("cure poison", NextAction::array(0, new NextAction("abolish poison",
// ACTION_DISPEL + 2), nullptr)));
triggers.push_back(
new TriggerNode("party member cure poison",
{ NextAction("abolish poison on party", ACTION_DISPEL + 1) }));
NextAction::array(0, new NextAction("abolish poison on party", ACTION_DISPEL + 1), nullptr)));
triggers.push_back(
new TriggerNode("party member remove curse",
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL)));
}
void DruidBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"nature's swiftness", { NextAction("nature's swiftness", ACTION_HIGH + 9) }));
"nature's swiftness", NextAction::array(0, new NextAction("nature's swiftness", ACTION_HIGH + 9), nullptr)));
}
void DruidCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) }));
"entangling roots", NextAction::array(0, new NextAction("entangling roots on cc", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) }));
"entangling roots kite", NextAction::array(0, new NextAction("entangling roots", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) }));
"hibernate", NextAction::array(0, new NextAction("hibernate on cc", ACTION_HIGH + 3), nullptr)));
}
void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("healer should attack",
{
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
NextAction("wrath", ACTION_DEFAULT + 0.1f),
NextAction("starfire", ACTION_DEFAULT),
}));
NextAction::array(0,
new NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
new NextAction("moonfire", ACTION_DEFAULT + 0.2f),
new NextAction("wrath", ACTION_DEFAULT + 0.1f),
new NextAction("starfire", ACTION_DEFAULT),
nullptr)));
// long cast time
// triggers.push_back(
// new TriggerNode("medium aoe and healer should attack",
// NextAction::array(0,
// new NextAction("hurricane", ACTION_DEFAULT + 0.7f),
// nullptr)));
}

View File

@@ -12,16 +12,40 @@ class HealDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
public:
HealDruidStrategyActionNodeFactory() {
creators["nourish on party"] = &nourtish_on_party;
// creators["wild growth on party"] = &wild_growth_on_party;
// creators["rejuvenation on party"] = &rejuvenation_on_party;
// creators["regrowth on party"] = &regrowth_on_party;
}
private:
static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("nourish on party",
/*P*/ {},
/*A*/ { NextAction("healing touch on party") },
/*C*/ {});
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr),
/*C*/ nullptr);
}
// static ActionNode* wild_growth_on_party([[maybe_unused]] PlayerbotAI* botAI)
// {
// return new ActionNode("wild growth on party",
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
// /*A*/ nullptr,
// /*C*/ nullptr);
// }
// static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
// {
// return new ActionNode("rejuvenation on party",
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
// /*A*/ nullptr,
// /*C*/ nullptr);
// }
// static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
// {
// return new ActionNode("regrowth on party",
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
// /*A*/ nullptr,
// /*C*/ nullptr);
// }
};
HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
@@ -33,69 +57,73 @@ void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
// triggers.push_back(
// new TriggerNode("tree form", NextAction::array(0, new NextAction("tree form", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"party member to heal out of spell range",
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9), nullptr)));
// CRITICAL
triggers.push_back(
new TriggerNode("party member critical health",
{
NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f),
NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4),
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3),
NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2),
NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1),
}));
NextAction::array(0,
new NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f),
new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4),
new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3),
new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2),
new NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1),
// new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 0),
nullptr)));
triggers.push_back(
new TriggerNode("party member critical health",
{ NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) }));
NextAction::array(0, new NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4), nullptr)));
triggers.push_back(new TriggerNode(
"group heal setting",
{
NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f),
NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f),
}));
NextAction::array(0,
new NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f),
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f),
new NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f),
nullptr)));
triggers.push_back(
new TriggerNode("medium group heal setting",
{
NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f),
NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f) }));
NextAction::array(0,
new NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f),
new NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f), nullptr)));
// LOW
triggers.push_back(
new TriggerNode("party member low health",
{ NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f),
NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2),
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f),
}));
NextAction::array(0, new NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f),
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f),
new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2),
new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f),
nullptr)));
// MEDIUM
triggers.push_back(
new TriggerNode("party member medium health",
{
NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f),
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f) }));
NextAction::array(0,
new NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f),
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f),
new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f), nullptr)));
// almost full
triggers.push_back(
new TriggerNode("party member almost full health",
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f),
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f),
NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f) }));
NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f),
new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f),
new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f), nullptr)));
triggers.push_back(
new TriggerNode("medium mana", { NextAction("innervate", ACTION_HIGH + 5) }));
new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode("enemy too close for spell",
{ NextAction("flee", ACTION_MOVE + 9) }));
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
}

View File

@@ -9,24 +9,16 @@
MeleeDruidStrategy::MeleeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
std::vector<NextAction> MeleeDruidStrategy::getDefaultActions()
NextAction** MeleeDruidStrategy::getDefaultActions()
{
return {
NextAction("faerie fire", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
return NextAction::array(0, new NextAction("faerie fire", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
}
void MeleeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"omen of clarity",
{
NextAction("omen of clarity", ACTION_HIGH + 9)
}
)
);
triggers.push_back(new TriggerNode(
"omen of clarity", NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 9), nullptr)));
CombatStrategy::InitTriggers(triggers);
}

View File

@@ -15,7 +15,7 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "melee"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
};
#endif

View File

@@ -29,112 +29,90 @@ public:
private:
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cat form",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("cat form",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("mangle (cat)",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"shred",
/*P*/ {},
/*A*/ { NextAction("claw") },
/*C*/ {}
);
return new ActionNode("shred",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("claw"), nullptr),
/*C*/ nullptr);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("rake",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("rip",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ { NextAction("rip") },
/*C*/ {}
);
return new ActionNode("ferocious bite",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("rip"), nullptr),
/*C*/ nullptr);
}
static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"savage roar",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("savage roar",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
return new ActionNode("faerie fire (feral)",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing touch on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
return new ActionNode("healing touch on party",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
return new ActionNode("regrowth on party",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
return new ActionNode("rejuvenation on party",
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
/*A*/ nullptr,
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
}
};
@@ -143,15 +121,12 @@ OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDrui
actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory());
}
std::vector<NextAction> OffhealDruidCatStrategy::getDefaultActions()
NextAction** OffhealDruidCatStrategy::getDefaultActions()
{
return {
NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
NextAction("shred", ACTION_DEFAULT + 0.4f),
NextAction("rake", ACTION_DEFAULT + 0.3f),
NextAction("melee", ACTION_DEFAULT),
NextAction("cat form", ACTION_DEFAULT - 0.1f)
};
return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
new NextAction("shred", ACTION_DEFAULT + 0.4f),
new NextAction("rake", ACTION_DEFAULT + 0.3f), new NextAction("melee", ACTION_DEFAULT),
new NextAction("cat form", ACTION_DEFAULT - 0.1f), nullptr);
}
void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@@ -159,149 +134,46 @@ void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"cat form",
{
NextAction("cat form", ACTION_HIGH + 8)
}
)
);
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode(
"savage roar",
{
NextAction("savage roar", ACTION_HIGH + 7)
}
)
);
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
triggers.push_back(new TriggerNode("combo points available",
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
triggers.push_back(new TriggerNode(
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"combo points available",
{
NextAction("rip", ACTION_HIGH + 6)
}
)
);
new TriggerNode("target with combo points almost dead",
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode("mangle (cat)",
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode("almost full energy available",
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
triggers.push_back(new TriggerNode("combo points not full",
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
triggers.push_back(new TriggerNode(
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_NORMAL), nullptr)));
triggers.push_back(new TriggerNode("enemy out of melee",
NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9),
new NextAction("dash", ACTION_HIGH + 8), nullptr)));
triggers.push_back(
new TriggerNode(
"ferocious bite time",
{
NextAction("ferocious bite", ACTION_HIGH + 5)
}
)
);
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
triggers.push_back(new TriggerNode(
"low energy", NextAction::array(0, new NextAction("tiger's fury", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode(
"party member critical health",
NextAction::array(0, new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5), nullptr)));
triggers.push_back(new TriggerNode(
"party member low health",
NextAction::array(0, new NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5), nullptr)));
triggers.push_back(
new TriggerNode(
"target with combo points almost dead",
{
NextAction("ferocious bite", ACTION_HIGH + 4)
}
)
);
new TriggerNode("party member medium health",
NextAction::array(0, new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8), nullptr)));
triggers.push_back(new TriggerNode(
"party member to heal out of spell range",
NextAction::array(0, new NextAction("reach party member to heal", ACTION_EMERGENCY + 3), nullptr)));
triggers.push_back(
new TriggerNode(
"mangle (cat)",
{
NextAction("mangle (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"rake",
{
NextAction("rake", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_NORMAL)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - cat", ACTION_HIGH + 9),
NextAction("dash", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("swipe (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"low energy",
{
NextAction("tiger's fury", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"party member critical health",
{
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"party member low health",
{
NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"party member medium health",
{
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"party member to heal out of spell range",
{
NextAction("reach party member to heal", ACTION_EMERGENCY + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"low mana",
{
NextAction("innervate", ACTION_HIGH + 4)
}
)
);
new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 4), nullptr)));
}

View File

@@ -17,7 +17,7 @@
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "offheal"; }
std::vector<NextAction> getDefaultActions() override;
NextAction** getDefaultActions() override;
uint32 GetType() const override
{
return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE;

View File

@@ -7,9 +7,9 @@ void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// TODO: Add CC trigger while web wraps are casting?
// TODO: Bring healer closer than ranged dps to avoid fixates?
triggers.push_back(new TriggerNode("krik'thir web wrap",
{ NextAction("attack web wrap", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("attack web wrap", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("krik'thir watchers",
{ NextAction("krik'thir priority", ACTION_RAID + 4) }));
NextAction::array(0, new NextAction("krik'thir priority", ACTION_RAID + 4), nullptr)));
// Hadronox
// The core AC triggers are very buggy with this boss, but default strat seems to play correctly
@@ -19,9 +19,9 @@ void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// and cast time is instant so no way to check currently casting location.
// May need to hook boss AI.. might be able to just heal through it for now.
// triggers.push_back(new TriggerNode("anub'arak impale",
// { NextAction("TODO", ACTION_MOVE + 5) }));
// NextAction::array(0, new NextAction("TODO", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("anub'arak pound",
{ NextAction("dodge pound", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("dodge pound", ACTION_MOVE + 5), nullptr)));
}
void WotlkDungeonANStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -8,12 +8,12 @@ void WotlkDungeonCoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Salramm the Fleshcrafter
triggers.push_back(new TriggerNode("explode ghoul",
{ NextAction("explode ghoul spread", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("explode ghoul spread", ACTION_MOVE + 5), nullptr)));
// Chrono-Lord Epoch
// Not sure if this actually works, I think I've seen him charge melee characters..?
triggers.push_back(new TriggerNode("epoch ranged",
{ NextAction("epoch stack", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("epoch stack", ACTION_MOVE + 5), nullptr)));
// Mal'Ganis

View File

@@ -5,32 +5,32 @@ void WotlkDungeonDTKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
// Trollgore
triggers.push_back(new TriggerNode("corpse explode",
{ NextAction("corpse explode spread", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("corpse explode spread", ACTION_MOVE + 5), nullptr)));
// Novos the Summoner
// TODO: Can be improved - it's a pretty easy fight but complex to program, revisit if needed
triggers.push_back(new TriggerNode("arcane field",
{ NextAction("avoid arcane field", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("avoid arcane field", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("arcane field",
{ NextAction("novos positioning", ACTION_MOVE + 4) }));
NextAction::array(0, new NextAction("novos positioning", ACTION_MOVE + 4), nullptr)));
triggers.push_back(new TriggerNode("arcane field",
{ NextAction("novos target priority", ACTION_NORMAL + 1) }));
NextAction::array(0, new NextAction("novos target priority", ACTION_NORMAL + 1), nullptr)));
// King Dred
// TODO: Fear ward / tremor totem, or general anti-fear strat development
//The Prophet Tharon'ja
triggers.push_back(new TriggerNode("gift of tharon'ja",
{ NextAction("touch of life", ACTION_NORMAL + 5) }));
NextAction::array(0, new NextAction("touch of life", ACTION_NORMAL + 5), nullptr)));
triggers.push_back(new TriggerNode("gift of tharon'ja",
{ NextAction("bone armor", ACTION_NORMAL + 4) }));
NextAction::array(0, new NextAction("bone armor", ACTION_NORMAL + 4), nullptr)));
// Run ranged chars (who would normally stand at range) into melee, to dps in skeleton form
triggers.push_back(new TriggerNode("tharon'ja out of melee",
{ NextAction("reach melee", ACTION_NORMAL + 3) }));
NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(new TriggerNode("gift of tharon'ja",
{ NextAction("taunt", ACTION_NORMAL + 2) }));
NextAction::array(0, new NextAction("taunt", ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode("gift of tharon'ja",
{ NextAction("slaying strike", ACTION_NORMAL + 2) }));
NextAction::array(0, new NextAction("slaying strike", ACTION_NORMAL + 2), nullptr)));
}
void WotlkDungeonDTKStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -4,16 +4,17 @@
void WotlkDungeonFoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("move from bronjahm",
{ NextAction("move from bronjahm", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("move from bronjahm", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("switch to soul fragment",
{ NextAction("attack corrupted soul fragment", ACTION_RAID + 2) }));
NextAction::array(0, new NextAction("attack corrupted soul fragment", ACTION_RAID + 2), nullptr)));
triggers.push_back(new TriggerNode("bronjahm position",
{ NextAction("bronjahm group position", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("bronjahm group position", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode("devourer of souls",
{ NextAction("devourer of souls", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("devourer of souls", ACTION_RAID + 1), nullptr)));
}
void WotlkDungeonFoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new BronjahmMultiplier(botAI));
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
}

View File

@@ -11,13 +11,13 @@ void WotlkDungeonGDStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// TODO: Might need to add target priority for heroic on the snakes or to burn down boss.
// Will re-test in heroic, decent dps groups should be able to blast him down with no funky strats.
triggers.push_back(new TriggerNode("poison nova",
{ NextAction("avoid poison nova", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("avoid poison nova", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("snake wrap",
{ NextAction("attack snake wrap", ACTION_RAID + 4) }));
NextAction::array(0, new NextAction("attack snake wrap", ACTION_RAID + 4), nullptr)));
// Gal'darah
triggers.push_back(new TriggerNode("whirling slash",
{ NextAction("avoid whirling slash", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("avoid whirling slash", ACTION_RAID + 5), nullptr)));
// Eck the Ferocious (Heroic only)
}

View File

@@ -5,30 +5,30 @@ void WotlkDungeonHoLStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
// General Bjarngrim
triggers.push_back(new TriggerNode("stormforged lieutenant",
{ NextAction("bjarngrim target", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("bjarngrim target", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("whirlwind",
{ NextAction("avoid whirlwind", ACTION_RAID + 4) }));
NextAction::array(0, new NextAction("avoid whirlwind", ACTION_RAID + 4), nullptr)));
// Volkhan
triggers.push_back(new TriggerNode("volkhan",
{ NextAction("volkhan target", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("volkhan target", ACTION_RAID + 5), nullptr)));
// Ionar
triggers.push_back(new TriggerNode("ionar disperse",
{ NextAction("disperse position", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("disperse position", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("ionar tank aggro",
{ NextAction("ionar tank position", ACTION_MOVE + 4) }));
NextAction::array(0, new NextAction("ionar tank position", ACTION_MOVE + 4), nullptr)));
triggers.push_back(new TriggerNode("static overload",
{ NextAction("static overload spread", ACTION_MOVE + 3) }));
NextAction::array(0, new NextAction("static overload spread", ACTION_MOVE + 3), nullptr)));
// TODO: Targeted player can dodge the ball, but a single player soaking it isn't too bad to heal
triggers.push_back(new TriggerNode("ball lightning",
{ NextAction("ball lightning spread", ACTION_MOVE + 2) }));
NextAction::array(0, new NextAction("ball lightning spread", ACTION_MOVE + 2), nullptr)));
// Loken
triggers.push_back(new TriggerNode("lightning nova",
{ NextAction("avoid lightning nova", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("avoid lightning nova", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("loken ranged",
{ NextAction("loken stack", ACTION_MOVE + 4) }));
NextAction::array(0, new NextAction("loken stack", ACTION_MOVE + 4), nullptr)));
}
void WotlkDungeonHoLStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -9,7 +9,7 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Krystallus
// TODO: I think bots need to dismiss pets on this, or they nuke players they are standing close to
triggers.push_back(new TriggerNode("ground slam",
{ NextAction("shatter spread", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("shatter spread", ACTION_RAID + 5), nullptr)));
// Tribunal of Ages
// Seems fine, maybe add focus targeting strat if needed on heroic.
@@ -19,7 +19,7 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Sjonnir The Ironshaper
// Possibly tank in place in the middle of the room, assign a dps to adds?
triggers.push_back(new TriggerNode("lightning ring",
{ NextAction("avoid lightning ring", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("avoid lightning ring", ACTION_RAID + 5), nullptr)));
}
void WotlkDungeonHoSStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -7,39 +7,39 @@ void WotlkDungeonNexStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// or
// Alliance Commander (Horde N)/Commander Stoutbeard (Horde H)
triggers.push_back(new TriggerNode("faction commander whirlwind",
{ NextAction("move from whirlwind", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("move from whirlwind", ACTION_MOVE + 5), nullptr)));
// TODO: Handle fear? (tremor totems, fear ward etc.)
// Grand Magus Telestra
triggers.push_back(new TriggerNode("telestra firebomb",
{ NextAction("firebomb spread", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("firebomb spread", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("telestra split phase",
{ NextAction("telestra split target", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("telestra split target", ACTION_RAID + 1), nullptr)));
// TODO: Add priority interrupt on the frost split's Blizzard casts
// Anomalus
triggers.push_back(new TriggerNode("chaotic rift",
{ NextAction("chaotic rift target", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("chaotic rift target", ACTION_RAID + 1), nullptr)));
// Ormorok the Tree-Shaper
// Tank trigger to stack inside boss. Can also add return action to prevent boss repositioning
// if it becomes too much of a problem. He usually dies before he's up against a wall though
triggers.push_back(new TriggerNode("ormorok spikes",
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
// Non-tank trigger to stack. Avoiding the spikes at range is.. harder than it seems.
// TODO: This turns hunters into melee marshmallows, have not come up with a better solution yet
triggers.push_back(new TriggerNode("ormorok stack",
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
// TODO: Add handling for spell reflect... best to spam low level/weak spells but don't want
// to hardcode spells per class, might be difficult to dynamically generate this.
// Will revisit if I find my altbots killing themselves in heroic, just heal through it for now
// Keristrasza
triggers.push_back(new TriggerNode("intense cold",
{ NextAction("intense cold jump", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("intense cold jump", ACTION_MOVE + 5), nullptr)));
// Flank dragon positioning
triggers.push_back(new TriggerNode("keristrasza positioning",
{ NextAction("rear flank", ACTION_MOVE + 4) }));
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr)));
// TODO: Add frost resist aura for paladins?
}

View File

@@ -6,28 +6,28 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Drakos the Interrogator
// TODO: May need work, TBA.
triggers.push_back(new TriggerNode("unstable sphere",
{ NextAction("avoid unstable sphere", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("avoid unstable sphere", ACTION_MOVE + 5), nullptr)));
// DRAKES
triggers.push_back(new TriggerNode("drake mount",
{ NextAction("mount drake", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("mount drake", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("drake dismount",
{ NextAction("dismount drake", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("dismount drake", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("group flying",
{ NextAction("occ fly drake", ACTION_NORMAL + 1) }));
NextAction::array(0, new NextAction("occ fly drake", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode("drake combat",
{ NextAction("occ drake attack", ACTION_NORMAL + 5) }));
NextAction::array(0, new NextAction("occ drake attack", ACTION_NORMAL + 5), nullptr)));
// Varos Cloudstrider
// Seems to be no way to identify the marked cores, may need to hook boss AI..
// triggers.push_back(new TriggerNode("varos cloudstrider",
// { NextAction("avoid energize cores", ACTION_RAID + 5) }));
// NextAction::array(0, new NextAction("avoid energize cores", ACTION_RAID + 5), nullptr)));
// Mage-Lord Urom
triggers.push_back(new TriggerNode("arcane explosion",
{ NextAction("avoid arcane explosion", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("avoid arcane explosion", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("time bomb",
{ NextAction("time bomb spread", ACTION_MOVE + 4) }));
NextAction::array(0, new NextAction("time bomb spread", ACTION_MOVE + 4), nullptr)));
// Ley-Guardian Eregos
}

View File

@@ -5,7 +5,7 @@ void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
// Elder Nadox
triggers.push_back(new TriggerNode("nadox guardian",
{ NextAction("attack nadox guardian", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("attack nadox guardian", ACTION_RAID + 5), nullptr)));
// Prince Taldaram
// Flame Orb spawns in melee, doesn't have a clear direction until it starts moving.
@@ -14,13 +14,13 @@ void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Jedoga Shadowseeker
triggers.push_back(new TriggerNode("jedoga volunteer",
{ NextAction("attack jedoga volunteer", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("attack jedoga volunteer", ACTION_RAID + 5), nullptr)));
// Herald Volazj
// Trash mobs before him have a big telegraphed shadow crash spell,
// this can be avoided and is intended to be dodged
triggers.push_back(new TriggerNode("shadow crash",
{ NextAction("avoid shadow crash", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("avoid shadow crash", ACTION_MOVE + 5), nullptr)));
// Volazj is not implemented properly in AC, insanity phase does nothing.
// Amanitar (Heroic Only)

View File

@@ -4,13 +4,14 @@
void WotlkDungeonPoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("ick and krick",
{ NextAction("ick and krick", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("ick and krick", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("tyrannus",
{ NextAction("tyrannus", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("tyrannus", ACTION_RAID + 5), nullptr)));
}
void WotlkDungeonPoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new IckAndKrickMultiplier(botAI));
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
}

View File

@@ -4,18 +4,19 @@
void WotlkDungeonToCStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
triggers.push_back(new TriggerNode("toc lance",
{ NextAction("toc lance", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("toc lance", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("toc ue lance",
{ NextAction("toc ue lance", ACTION_RAID + 2) }));
NextAction::array(0, new NextAction("toc ue lance", ACTION_RAID + 2), nullptr)));
triggers.push_back(new TriggerNode("toc mount near",
{ NextAction("toc mount", ACTION_RAID + 4) }));
NextAction::array(0, new NextAction("toc mount", ACTION_RAID + 4), nullptr)));
triggers.push_back(new TriggerNode("toc mounted",
{ NextAction("toc mounted", ACTION_RAID + 6) }));
NextAction::array(0, new NextAction("toc mounted", ACTION_RAID + 6), nullptr)));
triggers.push_back(new TriggerNode("toc eadric",
{ NextAction("toc eadric", ACTION_RAID + 3) }));
NextAction::array(0, new NextAction("toc eadric", ACTION_RAID + 3), nullptr)));
}
void WotlkDungeonToCStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
{
//multipliers.push_back(new toc...); if needed in the future
}

View File

@@ -5,32 +5,32 @@ void WotlkDungeonUKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
// Prince Keleseth
triggers.push_back(new TriggerNode("keleseth frost tomb",
{ NextAction("attack frost tomb", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("attack frost tomb", ACTION_RAID + 1), nullptr)));
// Skarvald the Constructor & Dalronn the Controller
triggers.push_back(new TriggerNode("dalronn priority",
{ NextAction("attack dalronn", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("attack dalronn", ACTION_RAID + 1), nullptr)));
// Ingvar the Plunderer
// Doesn't work yet, this action doesn't get processed until the existing cast finishes
// triggers.push_back(new TriggerNode("ingvar staggering roar",
// { NextAction("ingvar stop casting", ACTION_RAID + 1) }));
// NextAction::array(0, new NextAction("ingvar stop casting", ACTION_RAID + 1), nullptr)));
// No easy way to check LoS here, the pillars do not seem to count as gameobjects.
// Not implemented for now, unsure if this is needed as a good group can probably burst through the boss
// and just eat the debuff.
// triggers.push_back(new TriggerNode("ingvar dreadful roar",
// { NextAction("ingvar hide los", ACTION_RAID + 1) }));
// NextAction::array(0, new NextAction("ingvar hide los", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode("ingvar smash tank",
{ NextAction("ingvar dodge smash", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("ingvar dodge smash", ACTION_MOVE + 5), nullptr)));
triggers.push_back(new TriggerNode("ingvar smash tank return",
{ NextAction("ingvar smash return", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("ingvar smash return", ACTION_MOVE + 5), nullptr)));
// Buggy... if not behind target, ai can get stuck running towards and away from target.
// I think for ranged chars, a custom action should be added that doesn't attempt to run into melee.
// This is a bandaid for now, needs to be improved.
triggers.push_back(new TriggerNode("not behind ingvar",
{ NextAction("set behind", ACTION_MOVE + 1) }));
NextAction::array(0, new NextAction("set behind", ACTION_MOVE + 1), nullptr)));
}

View File

@@ -10,14 +10,14 @@ void WotlkDungeonUPStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Skadi the Ruthless
// TODO: Harpoons launchable via GameObject. For now players should do them
triggers.push_back(new TriggerNode("freezing cloud",
{ NextAction("avoid freezing cloud", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("avoid freezing cloud", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("skadi whirlwind",
{ NextAction("avoid skadi whirlwind", ACTION_RAID + 4) }));
NextAction::array(0, new NextAction("avoid skadi whirlwind", ACTION_RAID + 4), nullptr)));
// King Ymiron
// May need to avoid orb.. unclear if the generic avoid AoE does this well
triggers.push_back(new TriggerNode("ymiron bane",
{ NextAction("stop attack", ACTION_RAID + 5) }));
NextAction::array(0, new NextAction("stop attack", ACTION_RAID + 5), nullptr)));
}
void WotlkDungeonUPStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -6,14 +6,14 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Erekem
// This boss has many purgable buffs, purging/dispels could be merged into generic strats though
triggers.push_back(new TriggerNode("erekem target",
{ NextAction("attack erekem", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("attack erekem", ACTION_RAID + 1), nullptr)));
// Moragg
// TODO: This guy has Optic Link which may require moving, add if needed
// Ichoron
triggers.push_back(new TriggerNode("ichoron target",
{ NextAction("attack ichor globule", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("attack ichor globule", ACTION_RAID + 1), nullptr)));
// Xevozz
// TODO: Revisit in heroics, waypoints back and forth on stairs. Need to test with double beacon spawn
@@ -23,13 +23,13 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Zuramat the Obliterator
triggers.push_back(new TriggerNode("shroud of darkness",
{ NextAction("stop attack", ACTION_HIGH + 5) }));
NextAction::array(0, new NextAction("stop attack", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode("void shift",
{ NextAction("attack void sentry", ACTION_RAID + 1) }));
NextAction::array(0, new NextAction("attack void sentry", ACTION_RAID + 1), nullptr)));
// Cyanigosa
triggers.push_back(new TriggerNode("cyanigosa positioning",
{ NextAction("rear flank", ACTION_MOVE + 5) }));
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 5), nullptr)));
}
void WotlkDungeonVHStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -10,5 +10,5 @@
void AttackEnemyPlayersStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("enemy player near",
{ NextAction("attack enemy player", 55.0f) }));
NextAction::array(0, new NextAction("attack enemy player", 55.0f), nullptr)));
}

View File

@@ -9,79 +9,83 @@
void BGStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("often", { NextAction("bg join", relevance)}));
triggers.push_back(new TriggerNode("bg invite active", { NextAction("bg status check", relevance)}));
triggers.push_back(new TriggerNode("timer", { NextAction("bg strategy check", relevance)}));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg join", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg invite active", NextAction::array(0, new NextAction("bg status check", relevance), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("bg strategy check", relevance), nullptr)));
}
BGStrategy::BGStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
void BattlegroundStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg waiting", { NextAction("bg move to start", ACTION_BG)}));
triggers.push_back(new TriggerNode("bg active", { NextAction("bg move to objective", ACTION_BG)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg check objective", ACTION_BG + 1)}));
triggers.push_back(new TriggerNode("dead", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("bg waiting", NextAction::array(0, new NextAction("bg move to start", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg move to objective", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg check objective", ACTION_BG + 1), nullptr)));
triggers.push_back(new TriggerNode("dead", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
// triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy
// flag carrier", 80.0f), nullptr))); triggers.push_back(new TriggerNode("team flagcarrier near",
// NextAction::array(0, new NextAction("bg protect fc", 40.0f), nullptr))); triggers.push_back(new
// TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", 90.0f), nullptr)));
}
void WarsongStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY )}));
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f)}));
triggers.push_back(new TriggerNode("team flagcarrier near", { NextAction("bg protect fc", ACTION_RAID)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("player has flag", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY ), nullptr)));
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f), nullptr)));
triggers.push_back(new TriggerNode("team flagcarrier near", NextAction::array(0, new NextAction("bg protect fc", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
}
void AlteracStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("alliance no snowfall gy", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("alliance no snowfall gy", NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
}
void ArathiStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
}
void EyeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID)}));
triggers.push_back(new TriggerNode("player has flag",{ NextAction("bg move to objective", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode("player has flag",NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
}
//TODO: Do Priorities
void IsleStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("timer", { NextAction("enter vehicle", ACTION_MOVE + 8.0f)}));
triggers.push_back(new TriggerNode("random", { NextAction("leave vehicle", ACTION_MOVE + 7.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("hurl boulder", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("fire cannon", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("napalm", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("enemy is close", { NextAction("steam blast", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("ram", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("enemy is close", { NextAction("ram", ACTION_MOVE + 9.1f)}));
triggers.push_back(new TriggerNode("enemy out of melee", { NextAction("steam rush", ACTION_MOVE + 9.2f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("incendiary rocket", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("rocket blast", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("enter vehicle", ACTION_MOVE + 8.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("leave vehicle", ACTION_MOVE + 7.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("hurl boulder", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("fire cannon", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("napalm", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("steam blast", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.1f), nullptr)));
triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("steam rush", ACTION_MOVE + 9.2f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("incendiary rocket", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("rocket blast", ACTION_MOVE + 9.0f), nullptr)));
// this is bugged: it doesn't work, and stops glaive throw working (which is needed to take down gate)
// triggers.push_back(new TriggerNode("in vehicle", { NextAction("blade salvo", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("glaive throw", ACTION_MOVE + 9.0f)}));
// triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("blade salvo", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("glaive throw", ACTION_MOVE + 9.0f), nullptr)));
}
void ArenaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("no possible targets", { NextAction("arena tactics", ACTION_BG)}));
new TriggerNode("no possible targets", NextAction::array(0, new NextAction("arena tactics", ACTION_BG), nullptr)));
}

View File

@@ -16,9 +16,9 @@ private:
static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* botAI)
{
return new ActionNode("tank attack chat shortcut",
/*P*/ {},
/*A*/ {},
/*C*/ { NextAction("attack my target", 100.0f) });
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ NextAction::array(0, new NextAction("attack my target", 100.0f), nullptr));
}
};
@@ -26,86 +26,88 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
{
PassTroughStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
NextAction("query item usage", relevance) }));
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
NextAction("loot", relevance) }));
triggers.push_back(new TriggerNode("u", { NextAction("use", relevance) }));
triggers.push_back(new TriggerNode("c", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("rep", NextAction::array(0, new NextAction("reputation", relevance), nullptr)));
triggers.push_back(new TriggerNode("q", NextAction::array(0, new NextAction("query quest", relevance),
new NextAction("query item usage", relevance), nullptr)));
triggers.push_back(new TriggerNode("add all loot", NextAction::array(0, new NextAction("add all loot", relevance),
new NextAction("loot", relevance), nullptr)));
triggers.push_back(new TriggerNode("u", NextAction::array(0, new NextAction("use", relevance), nullptr)));
triggers.push_back(new TriggerNode("c", NextAction::array(0, new NextAction("item count", relevance), nullptr)));
triggers.push_back(
new TriggerNode("items", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("inv", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("e", { NextAction("equip", relevance) }));
triggers.push_back(new TriggerNode("ue", { NextAction("unequip", relevance) }));
triggers.push_back(new TriggerNode("t", { NextAction("trade", relevance) }));
triggers.push_back(new TriggerNode("nt", { NextAction("trade", relevance) }));
triggers.push_back(new TriggerNode("s", { NextAction("sell", relevance) }));
triggers.push_back(new TriggerNode("b", { NextAction("buy", relevance) }));
triggers.push_back(new TriggerNode("r", { NextAction("reward", relevance) }));
new TriggerNode("items", NextAction::array(0, new NextAction("item count", relevance), nullptr)));
triggers.push_back(new TriggerNode("inv", NextAction::array(0, new NextAction("item count", relevance), nullptr)));
triggers.push_back(new TriggerNode("e", NextAction::array(0, new NextAction("equip", relevance), nullptr)));
triggers.push_back(new TriggerNode("ue", NextAction::array(0, new NextAction("unequip", relevance), nullptr)));
triggers.push_back(new TriggerNode("t", NextAction::array(0, new NextAction("trade", relevance), nullptr)));
triggers.push_back(new TriggerNode("nt", NextAction::array(0, new NextAction("trade", relevance), nullptr)));
triggers.push_back(new TriggerNode("s", NextAction::array(0, new NextAction("sell", relevance), nullptr)));
triggers.push_back(new TriggerNode("b", NextAction::array(0, new NextAction("buy", relevance), nullptr)));
triggers.push_back(new TriggerNode("r", NextAction::array(0, new NextAction("reward", relevance), nullptr)));
triggers.push_back(
new TriggerNode("attack", { NextAction("attack my target", relevance) }));
new TriggerNode("attack", NextAction::array(0, new NextAction("attack my target", relevance), nullptr)));
triggers.push_back(
new TriggerNode("accept", { NextAction("accept quest", relevance) }));
new TriggerNode("accept", NextAction::array(0, new NextAction("accept quest", relevance), nullptr)));
triggers.push_back(
new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
new TriggerNode("follow", NextAction::array(0, new NextAction("follow chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
new TriggerNode("stay", NextAction::array(0, new NextAction("stay chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
new TriggerNode("move from group", NextAction::array(0, new NextAction("move from group chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
new TriggerNode("flee", NextAction::array(0, new NextAction("flee chat shortcut", relevance), nullptr)));
triggers.push_back(new TriggerNode(
"tank attack", { NextAction("tank attack chat shortcut", relevance) }));
"tank attack", NextAction::array(0, new NextAction("tank attack chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
new TriggerNode("grind", NextAction::array(0, new NextAction("grind chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("talk", { NextAction("gossip hello", relevance),
NextAction("talk to quest giver", relevance) }));
new TriggerNode("talk", NextAction::array(0, new NextAction("gossip hello", relevance),
new NextAction("talk to quest giver", relevance), nullptr)));
triggers.push_back(
new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
new TriggerNode("enter vehicle", NextAction::array(0, new NextAction("enter vehicle", relevance), nullptr)));
triggers.push_back(
new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
new TriggerNode("leave vehicle", NextAction::array(0, new NextAction("leave vehicle", relevance), nullptr)));
triggers.push_back(
new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
new TriggerNode("cast", NextAction::array(0, new NextAction("cast custom spell", relevance), nullptr)));
triggers.push_back(
new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
new TriggerNode("castnc", NextAction::array(0, new NextAction("cast custom nc spell", relevance), nullptr)));
triggers.push_back(
new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
new TriggerNode("revive", NextAction::array(0, new NextAction("spirit healer", relevance), nullptr)));
triggers.push_back(
new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
new TriggerNode("runaway", NextAction::array(0, new NextAction("runaway chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
new TriggerNode("warning", NextAction::array(0, new NextAction("runaway chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
new TriggerNode("max dps", NextAction::array(0, new NextAction("max dps chat shortcut", relevance), nullptr)));
triggers.push_back(
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
new TriggerNode("attackers", NextAction::array(0, new NextAction("tell attackers", relevance), nullptr)));
triggers.push_back(
new TriggerNode("target", { NextAction("tell target", relevance) }));
new TriggerNode("target", NextAction::array(0, new NextAction("tell target", relevance), nullptr)));
triggers.push_back(
new TriggerNode("ready", { NextAction("ready check", relevance) }));
new TriggerNode("ready", NextAction::array(0, new NextAction("ready check", relevance), nullptr)));
triggers.push_back(
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL)));
triggers.push_back(
new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
triggers.push_back(
new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
new TriggerNode("dps", NextAction::array(0, new NextAction("tell estimated dps", relevance), NULL)));
triggers.push_back(
new TriggerNode("open items", { NextAction("open items", relevance) }));
new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
triggers.push_back(
new TriggerNode("qi", { NextAction("query item usage", relevance) }));
new TriggerNode("open items", NextAction::array(0, new NextAction("open items", relevance), nullptr)));
triggers.push_back(
new TriggerNode("unlock items", { NextAction("unlock items", relevance) }));
new TriggerNode("qi", NextAction::array(0, new NextAction("query item usage", relevance), nullptr)));
triggers.push_back(
new TriggerNode("unlock traded item", { NextAction("unlock traded item", relevance) }));
new TriggerNode("unlock items", NextAction::array(0, new NextAction("unlock items", relevance), nullptr)));
triggers.push_back(
new TriggerNode("wipe", { NextAction("wipe", relevance) }));
triggers.push_back(new TriggerNode("tame", { NextAction("tame", relevance) }));
triggers.push_back(new TriggerNode("glyphs", { NextAction("glyphs", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("glyph equip", { NextAction("glyph equip", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("pet", { NextAction("pet", relevance) }));
triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) }));
triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) }));
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
triggers.push_back(
new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr)));
triggers.push_back(new TriggerNode("tame", NextAction::array(0, new NextAction("tame", relevance), nullptr)));
triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs
triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs
triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr)));
triggers.push_back(new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", relevance), nullptr)));
triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr)));
}
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)

Some files were not shown because too many files have changed in this diff Show More