automatically action for random bots

This commit is contained in:
Yunfan Li
2023-08-05 18:41:15 +08:00
parent 9598719801
commit d31fbc0353
12 changed files with 153 additions and 79 deletions

View File

@@ -109,7 +109,7 @@ AiPlayerbot.GearScoreCheck = 0
AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761" AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761"
# Randombots will group with nearby bots to do shared quests # Randombots will group with nearby bots to do shared quests
AiPlayerbot.RandomBotGroupNearby = 1 AiPlayerbot.RandomBotGroupNearby = 0
# Bots without a master will say their lines # Bots without a master will say their lines
AiPlayerbot.RandomBotSayWithoutMaster = 0 AiPlayerbot.RandomBotSayWithoutMaster = 0
@@ -142,19 +142,28 @@ AiPlayerbot.FreeFood = 1
# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells) # Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells)
AiPlayerbot.AutoTrainSpells = yes AiPlayerbot.AutoTrainSpells = yes
# Bot automatically picks talent points based on current spec (full = pick spec based on probability if multiple are like current spec, semi = only apply points if 1 spec looks like current spec, no = no auto talent points)
AiPlayerbot.AutoPickTalents = no
# Bots automatically learn trainable spells on levelup
# Default: 0 (disabled)
AiPlayerbot.AutoLearnTrainerSpells = 0
# Bots automatically learn classquest reward spells on levelup # Bots automatically learn classquest reward spells on levelup
# Default: 0 (disabled) # Default: 0 (disabled)
AiPlayerbot.AutoLearnQuestSpells = 0 AiPlayerbot.AutoLearnQuestSpells = 0
# Bots automatically learn trainable spells on levelup
# Default: 1 (enabled)
AiPlayerbot.AutoLearnTrainerSpells = 1
# Bots automatically teleport to another place for leveling on levelup
# Default: 1 (enabled)
AiPlayerbot.AutoTeleportForLevel = 1
# Bot automatically picks talent points on levelup
# Default: 1 (enabled)
AiPlayerbot.AutoPickTalents = 1
# Bot automatically upgrade equipments on levelup, may cause lagging
# Default: 0 (enabled)
AiPlayerbot.AutoUpgradeEquip = 0
# Random Bots will pick quests on their own and try to complete # Random Bots will pick quests on their own and try to complete
# Default: 0 (disabled) # Default: 1 (enabled)
AiPlayerbot.AutoDoQuests = 1 AiPlayerbot.AutoDoQuests = 1
################################################################################## ##################################################################################

View File

@@ -311,9 +311,11 @@ bool PlayerbotAIConfig::Initialize()
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", false); syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", false);
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false); syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes"); autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
autoPickTalents = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoPickTalents", "full"); autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", false); autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false); autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", false); autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", false);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false); syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true); freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);

View File

@@ -148,12 +148,14 @@ class PlayerbotAIConfig
bool syncQuestWithPlayer; bool syncQuestWithPlayer;
bool syncQuestForPlayer; bool syncQuestForPlayer;
std::string autoTrainSpells; std::string autoTrainSpells;
std::string autoPickTalents; bool autoPickTalents;
bool autoUpgradeEquip;
bool autoLearnTrainerSpells; bool autoLearnTrainerSpells;
bool autoDoQuests; bool autoDoQuests;
bool syncLevelWithPlayers; bool syncLevelWithPlayers;
bool freeFood; bool freeFood;
bool autoLearnQuestSpells; bool autoLearnQuestSpells;
bool autoTeleportForLevel;
bool randomBotSayWithoutMaster; bool randomBotSayWithoutMaster;
bool randomBotGroupNearby; bool randomBotGroupNearby;
uint32 tweakValue; //Debugging config uint32 tweakValue; //Debugging config

View File

@@ -1149,12 +1149,14 @@ void PlayerbotFactory::InitEquipmentNew(bool incremental)
void PlayerbotFactory::InitEquipment(bool incremental) void PlayerbotFactory::InitEquipment(bool incremental)
{ {
// todo(yunfan): to be refactored, too much time overhead // todo(yunfan): to be refactored, too much time overhead
DestroyItemsVisitor visitor(bot); // DestroyItemsVisitor visitor(bot);
IterateItems(&visitor, ITERATE_ALL_ITEMS); // IterateItems(&visitor, ITERATE_ALL_ITEMS);
std::map<uint8, std::vector<uint32> > items; std::map<uint8, std::vector<uint32> > items;
int tab = AiFactory::GetPlayerSpecTab(bot); int tab = AiFactory::GetPlayerSpecTab(bot);
// todo(yunfan): make cache for this
for(uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) for(uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
{ {
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)

View File

@@ -115,11 +115,14 @@ class PlayerbotFactory : public InventoryAction
static uint32 tradeSkills[]; static uint32 tradeSkills[];
static float CalculateItemScore(uint32 item_id, Player* bot); static float CalculateItemScore(uint32 item_id, Player* bot);
void InitTalentsTree(bool incremental = false, bool use_template = true);
void InitAvailableSpells();
void InitClassSpells();
void InitEquipment(bool incremental);
private: private:
void Prepare(); void Prepare();
void InitSecondEquipmentSet(); void InitSecondEquipmentSet();
void InitEquipment(bool incremental);
void InitEquipmentNew(bool incremental); void InitEquipmentNew(bool incremental);
bool CanEquipItem(ItemTemplate const* proto, uint32 desiredQuality); bool CanEquipItem(ItemTemplate const* proto, uint32 desiredQuality);
bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item); bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item);
@@ -129,10 +132,7 @@ class PlayerbotFactory : public InventoryAction
void InitSpells(); void InitSpells();
void ClearSpells(); void ClearSpells();
void ClearSkills(); void ClearSkills();
void InitAvailableSpells();
void InitClassSpells();
void InitSpecialSpells(); void InitSpecialSpells();
void InitTalentsTree(bool incremental = false, bool use_template = true);
void InitTalents(uint32 specNo); void InitTalents(uint32 specNo);
void InitTalentsByTemplate(uint32 specNo); void InitTalentsByTemplate(uint32 specNo);
void InitQuests(std::list<uint32>& questMap); void InitQuests(std::list<uint32>& questMap);

View File

@@ -800,7 +800,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
AddPlayerBot(botGUID, 0); AddPlayerBot(botGUID, 0);
SetEventValue(bot, "login", 1, sPlayerbotAIConfig->randomBotUpdateInterval); SetEventValue(bot, "login", 1, sPlayerbotAIConfig->randomBotUpdateInterval);
uint32 randomTime = urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime * 5); uint32 randomTime = urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime);
SetEventValue(bot, "update", 1, randomTime); SetEventValue(bot, "update", 1, randomTime);
return true; return true;
@@ -812,7 +812,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
return false; return false;
uint32 update = GetEventValue(bot, "update"); uint32 update = GetEventValue(bot, "update");
if (!update && !sPlayerbotAIConfig->disableRandomLevels) if (!update)
{ {
if (botAI) if (botAI)
botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Set(true); botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Set(true);

View File

@@ -58,6 +58,7 @@
#include "VehicleActions.h" #include "VehicleActions.h"
#include "WorldBuffAction.h" #include "WorldBuffAction.h"
#include "RaidNaxxActions.h" #include "RaidNaxxActions.h"
#include "AutoTeleportForLevelAction.h"
class PlayerbotAI; class PlayerbotAI;
@@ -146,6 +147,7 @@ class ActionContext : public NamedObjectContext<Action>
creators["war stomp"] = &ActionContext::war_stomp; creators["war stomp"] = &ActionContext::war_stomp;
creators["auto talents"] = &ActionContext::auto_talents; creators["auto talents"] = &ActionContext::auto_talents;
creators["auto learn spell"] = &ActionContext::auto_learn_spell; creators["auto learn spell"] = &ActionContext::auto_learn_spell;
creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level;
creators["xp gain"] = &ActionContext::xp_gain; creators["xp gain"] = &ActionContext::xp_gain;
creators["invite nearby"] = &ActionContext::invite_nearby; creators["invite nearby"] = &ActionContext::invite_nearby;
creators["invite guild"] = &ActionContext::invite_guild; creators["invite guild"] = &ActionContext::invite_guild;
@@ -308,6 +310,7 @@ class ActionContext : public NamedObjectContext<Action>
static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); } static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); } static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); } static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); }
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); } static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); } static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); }
static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); } static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); }

View File

@@ -4,6 +4,7 @@
#include "AutoLearnSpellAction.h" #include "AutoLearnSpellAction.h"
#include "Event.h" #include "Event.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h" #include "Playerbots.h"
bool AutoLearnSpellAction::Execute(Event event) bool AutoLearnSpellAction::Execute(Event event)
@@ -30,78 +31,81 @@ bool AutoLearnSpellAction::Execute(Event event)
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out) void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{ {
if (sPlayerbotAIConfig->autoLearnTrainerSpells)// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot))) if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnTrainerSpells(out); LearnTrainerSpells(out);
if (sPlayerbotAIConfig->autoLearnQuestSpells)// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot))) if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out); LearnQuestSpells(out);
} }
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out) void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)
{ {
bot->LearnDefaultSkills(); PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitClassSpells();
factory.InitAvailableSpells();
// bot->LearnDefaultSkills();
CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates(); // CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin(); i != creatureTemplateContainer->end(); ++i) // for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin(); i != creatureTemplateContainer->end(); ++i)
{ // {
CreatureTemplate const& co = i->second; // CreatureTemplate const& co = i->second;
if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS) // if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS)
continue; // continue;
if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass()) // if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass())
continue; // continue;
uint32 trainerId = co.Entry; // uint32 trainerId = co.Entry;
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); // TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
if (!trainer_spells) // if (!trainer_spells)
trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); // trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
if (!trainer_spells) // if (!trainer_spells)
continue; // continue;
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr) // for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
{ // {
TrainerSpell const* tSpell = &itr->second; // TrainerSpell const* tSpell = &itr->second;
if (!tSpell) // if (!tSpell)
continue; // continue;
if (!tSpell->learnedSpell[0] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[0])) // if (!tSpell->learnedSpell[0] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[0]))
continue; // continue;
TrainerSpellState state = bot->GetTrainerSpellState(tSpell); // TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
if (state != TRAINER_SPELL_GREEN) // if (state != TRAINER_SPELL_GREEN)
continue; // continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell); // SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
bool learn = true; // bool learn = true;
for (uint8 j = 0; j < 3; ++j) // for (uint8 j = 0; j < 3; ++j)
{ // {
if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j])) // if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j]))
continue; // continue;
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_PROFICIENCY || // if (spellInfo->Effects[j].Effect == SPELL_EFFECT_PROFICIENCY ||
spellInfo->Effects[j].Effect == SPELL_EFFECT_SKILL_STEP || // spellInfo->Effects[j].Effect == SPELL_EFFECT_SKILL_STEP ||
spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD) // spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD)
{ // {
learn = false; // learn = false;
break; // break;
} // }
} // }
if (!learn) { // if (!learn) {
continue; // continue;
} // }
if (tSpell->learnedSpell[0]) { // if (tSpell->learnedSpell[0]) {
bot->learnSpell(tSpell->learnedSpell[0], false); // bot->learnSpell(tSpell->learnedSpell[0], false);
} // }
else { // else {
LOG_INFO("playerbots", "!tSpell->learnedSpell[0] {}", tSpell->spell); // LOG_INFO("playerbots", "!tSpell->learnedSpell[0] {}", tSpell->spell);
botAI->CastSpell(tSpell->spell, bot); // botAI->CastSpell(tSpell->spell, bot);
} // }
} // }
} // }
} }
void AutoLearnSpellAction::LearnQuestSpells(std::ostringstream* out) void AutoLearnSpellAction::LearnQuestSpells(std::ostringstream* out)

View File

@@ -0,0 +1,24 @@
#include "AutoTeleportForLevelAction.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h"
#include "RandomPlayerbotMgr.h"
#include "Playerbots.h"
#include "SharedDefines.h"
bool AutoTeleportForLevelAction::Execute(Event event) {
AutoUpgradeEquip();
if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot)) {
return false;
}
sRandomPlayerbotMgr->RandomTeleportForLevel(bot);
return true;
}
void AutoTeleportForLevelAction::AutoUpgradeEquip() {
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) {
return;
}
PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE);
factory.InitEquipment(true);
}

View File

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

View File

@@ -5,6 +5,7 @@
#include "ChangeTalentsAction.h" #include "ChangeTalentsAction.h"
#include "ChatHelper.h" #include "ChatHelper.h"
#include "Event.h" #include "Event.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h" #include "Playerbots.h"
bool ChangeTalentsAction::Execute(Event event) bool ChangeTalentsAction::Execute(Event event)
@@ -82,7 +83,7 @@ bool ChangeTalentsAction::Execute(Event event)
out.str(""); out.str("");
out.clear(); out.clear();
if (paths.size() > 1 && sPlayerbotAIConfig->autoPickTalents != "full") if (paths.size() > 1 && false/*!sPlayerbotAIConfig->autoPickTalents*/)
{ {
out << "Found multiple specs: "; out << "Found multiple specs: ";
listPremadePaths(paths, &out); listPremadePaths(paths, &out);
@@ -278,7 +279,7 @@ bool ChangeTalentsAction::AutoSelectTalents(std::ostringstream* out)
specId = -1; specId = -1;
// specLink = ""; // specLink = "";
} }
else if (paths.size() > 1 && sPlayerbotAIConfig->autoPickTalents != "full" && !sRandomPlayerbotMgr->IsRandomBot(bot)) else if (paths.size() > 1 && false/*!sPlayerbotAIConfig->autoPickTalents*/ && !sRandomPlayerbotMgr->IsRandomBot(bot))
{ {
*out << "Found multiple specs: "; *out << "Found multiple specs: ";
listPremadePaths(paths, out); listPremadePaths(paths, out);
@@ -328,13 +329,14 @@ bool AutoSetTalentsAction::Execute(Event event)
{ {
std::ostringstream out; std::ostringstream out;
if (sPlayerbotAIConfig->autoPickTalents == "no" && !sRandomPlayerbotMgr->IsRandomBot(bot)) if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot))
return false; return false;
if (bot->GetFreeTalentPoints() <= 0) if (bot->GetFreeTalentPoints() <= 0)
return false; return false;
AutoSelectTalents(&out); PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitTalentsTree(true);
botAI->TellMaster(out); botAI->TellMaster(out);

View File

@@ -35,7 +35,11 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
//triggers.push_back(new TriggerNode("no non bot players around", NextAction::array(0, new NextAction("delay", relevance), nullptr))); //triggers.push_back(new TriggerNode("no non bot players around", NextAction::array(0, new NextAction("delay", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr))); triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr)));
triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr))); triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr)));
triggers.push_back(new TriggerNode("levelup", NextAction::array(0, new NextAction("auto talents", relevance), new NextAction("auto learn spell", relevance), nullptr))); triggers.push_back(new TriggerNode("levelup", NextAction::array(0,
new NextAction("auto talents", relevance),
new NextAction("auto learn spell", relevance),
new NextAction("auto teleport for level", relevance),
nullptr)));
triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr))); triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr))); triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));
triggers.push_back(new TriggerNode("revive from corpse", NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr))); triggers.push_back(new TriggerNode("revive from corpse", NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr)));