Merge pull request #2172 from mod-playerbots/test-staging

Update master from Test staging and Core Update
This commit is contained in:
kadeshar 2026-02-27 22:55:19 +01:00 committed by GitHub
commit d8c668cf96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 489 additions and 267 deletions

View File

@ -6,10 +6,6 @@ on:
- reopened - reopened
- synchronize - synchronize
- ready_for_review - ready_for_review
paths:
- src/**
- "!README.md"
- "!docs/**"
concurrency: concurrency:
group: "codestyle-cppcheck-${{ github.event.pull_request.number }}" group: "codestyle-cppcheck-${{ github.event.pull_request.number }}"
@ -22,13 +18,27 @@ jobs:
if: github.event.pull_request.draft == false if: github.event.pull_request.draft == false
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
cpp:
- 'src/**'
- '!README.md'
- '!docs/**'
- name: Setup python - name: Setup python
if: steps.filter.outputs.cpp == 'true'
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: '3.10' python-version: '3.10'
- name: AzerothCore codestyle - name: AzerothCore codestyle
if: steps.filter.outputs.cpp == 'true'
run: python ./apps/codestyle/codestyle-cpp.py run: python ./apps/codestyle/codestyle-cpp.py
- name: C++ Advanced - name: C++ Advanced
if: steps.filter.outputs.cpp == 'true'
run: | run: |
sudo apt update -y sudo apt update -y
sudo apt install -y cppcheck sudo apt install -y cppcheck

View File

@ -192,9 +192,12 @@ AiPlayerbot.AutoInitOnly = 0
# Default: 1.0 (same with the player) # Default: 1.0 (same with the player)
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0 AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
# 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 # AllowLearnTrainerSpells
AiPlayerbot.AutoTrainSpells = yes # Description: Allow the bot to learn trainers' spells as long as it has the money.
# Default: 1 - (Enabled)
# 0 - (Disabled)
AiPlayerbot.AllowLearnTrainerSpells = 1
# #
# #
@ -563,6 +566,34 @@ AiPlayerbot.AutoGearScoreLimit = 0
# Default: food, taxi, and raid are enabled # Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid" AiPlayerbot.BotCheats = "food,taxi,raid"
# Attunement quests (comma-separated list of quest IDs)
# Default:
# Caverns of Time - Part 1
# - 10279, To The Master's Lair
# - 10277, The Caverns of Time
#
# Caverns of Time - Part 2 (Escape from Durnholde Keep)
# - 10282, Old Hillsbrad
# - 10283, Taretha's Diversion
# - 10284, Escape from Durnholde
# - 10285, Return to Andormu
#
# Caverns of Time - Part 2 (The Black Morass)
# - 10296, The Black Morass
# - 10297, The Opening of the Dark Portal
# - 10298, Hero of the Brood
#
# Magister's Terrace Attunement
# - 11481, Crisis at the Sunwell
# - 11482, Duty Calls
# - 11488, Magisters' Terrace
# - 11490, The Scryer's Scryer
# - 11492, Hard to Kill
#
# Serpentshrine Cavern
# - 10901, The Cudgel of Kar'desh
AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901
# #
# #
# #

View File

@ -125,6 +125,7 @@ public:
creators["runaway"] = &ActionContext::runaway; creators["runaway"] = &ActionContext::runaway;
creators["stay"] = &ActionContext::stay; creators["stay"] = &ActionContext::stay;
creators["sit"] = &ActionContext::sit; creators["sit"] = &ActionContext::sit;
creators["aggressive target"] = &ActionContext::aggressive_target;
creators["attack anything"] = &ActionContext::attack_anything; creators["attack anything"] = &ActionContext::attack_anything;
creators["attack least hp target"] = &ActionContext::attack_least_hp_target; creators["attack least hp target"] = &ActionContext::attack_least_hp_target;
creators["attack enemy player"] = &ActionContext::attack_enemy_player; creators["attack enemy player"] = &ActionContext::attack_enemy_player;
@ -315,6 +316,7 @@ private:
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); } static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); } static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); } static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); }
static Action* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetAction(botAI); }
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); } static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); } static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); } static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }

View File

@ -30,6 +30,14 @@ bool AttackEnemyFlagCarrierAction::isUseful()
PlayerHasFlag::IsCapturingFlag(bot); PlayerHasFlag::IsCapturingFlag(bot);
} }
bool AggressiveTargetAction::isUseful()
{
if (bot->IsInCombat())
return false;
return true;
}
bool DropTargetAction::Execute(Event /*event*/) bool DropTargetAction::Execute(Event /*event*/)
{ {
Unit* target = context->GetValue<Unit*>("current target")->Get(); Unit* target = context->GetValue<Unit*>("current target")->Get();

View File

@ -35,6 +35,15 @@ public:
std::string const GetTargetName() override { return "tank target"; } std::string const GetTargetName() override { return "tank target"; }
}; };
class AggressiveTargetAction : public AttackAction
{
public:
AggressiveTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "aggressive target") {}
std::string const GetTargetName() override { return "aggressive target"; }
bool isUseful() override;
};
class AttackAnythingAction : public AttackAction class AttackAnythingAction : public AttackAction
{ {
public: public:

View File

@ -7,10 +7,13 @@
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "InstancePackets.h"
bool ResetInstancesAction::Execute(Event /*event*/) bool ResetInstancesAction::Execute(Event /*event*/)
{ {
WorldPacket packet(CMSG_RESET_INSTANCES, 0); WorldPacket packet(CMSG_RESET_INSTANCES, 0);
bot->GetSession()->HandleResetInstancesOpcode(packet); WorldPackets::Instance::ResetInstances resetInstance(std::move(packet));
bot->GetSession()->HandleResetInstancesOpcode(resetInstance);
return true; return true;
} }

View File

@ -5,6 +5,7 @@
#include "RpgSubActions.h" #include "RpgSubActions.h"
#include "BudgetValues.h"
#include "ChooseRpgTargetAction.h" #include "ChooseRpgTargetAction.h"
#include "EmoteAction.h" #include "EmoteAction.h"
#include "Formations.h" #include "Formations.h"
@ -53,7 +54,12 @@ ObjectGuid RpgHelper::guid() { return (ObjectGuid)guidP(); }
bool RpgHelper::InRange() bool RpgHelper::InRange()
{ {
return guidP() ? (guidP().sqDistance2d(bot) < INTERACTION_DISTANCE * INTERACTION_DISTANCE) : false; GuidPosition targetGuid = guidP();
if (!targetGuid)
return false;
return bot->GetExactDist2dSq(targetGuid.GetPositionX(), targetGuid.GetPositionY()) <
INTERACTION_DISTANCE * INTERACTION_DISTANCE;
} }
void RpgHelper::setFacingTo(GuidPosition guidPosition) void RpgHelper::setFacingTo(GuidPosition guidPosition)
@ -250,6 +256,60 @@ Event RpgSellAction::ActionEvent(Event /*event*/) { return Event("rpg action", "
std::string const RpgRepairAction::ActionName() { return "repair"; } std::string const RpgRepairAction::ActionName() { return "repair"; }
bool RpgTrainAction::isUseful()
{
if (!rpg->InRange())
return false;
Creature* creature = rpg->guidP().GetCreature();
if (!creature)
return false;
if (!creature->IsInWorld() || creature->IsDuringRemoveFromWorld() || !creature->IsAlive())
return false;
return true;
}
bool RpgTrainAction::isPossible()
{
GuidPosition gp = rpg->guidP();
CreatureTemplate const* cinfo = gp.GetCreatureTemplate();
if (!cinfo)
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cinfo->Entry);
if (!trainer)
return false;
if (!trainer->IsTrainerValidForPlayer(bot))
return false;
FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cinfo->faction);
float reputationDiscount = bot->GetReputationPriceDiscount(factionTemplate);
uint32 currentGold = AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells);
for (auto& spell : trainer->GetSpells())
{
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue;
if (!trainer->CanTeachSpell(bot, trainerSpell))
continue;
if (currentGold < static_cast<uint32>(floor(trainerSpell->MoneyCost * reputationDiscount)))
continue;
// we only check if at least one spell can be learned from the trainer;
// otherwise, the train action should not be allowed
return true;
}
return false;
}
std::string const RpgTrainAction::ActionName() { return "trainer"; } std::string const RpgTrainAction::ActionName() { return "trainer"; }
bool RpgHealAction::Execute(Event /*event*/) bool RpgHealAction::Execute(Event /*event*/)

View File

@ -165,6 +165,9 @@ class RpgTrainAction : public RpgSubAction
public: public:
RpgTrainAction(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgSubAction(botAI, name) {} RpgTrainAction(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgSubAction(botAI, name) {}
bool isPossible() override;
bool isUseful() override;
private: private:
std::string const ActionName() override; std::string const ActionName() override;
}; };

View File

@ -9,77 +9,120 @@
#include "Event.h" #include "Event.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "Trainer.h"
void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg) bool TrainerAction::Execute(Event event)
{ {
if (sPlayerbotAIConfig.autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold)) std::string const param = event.getParam();
{
if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost) Creature* target = GetCreatureTarget();
{ if (!target)
msg << " - too expensive"; return false;
return;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(target->GetEntry());
if (!trainer)
return false;
// NOTE: Original version uses SpellIds here, but occasionally only inserts
// a single spell ID value from parameters. If someone wants to impl multiple
// spells as parameters, check SkipSpellsListAction::parseIds as an example.
uint32 spellId = chat->parseSpell(param);
bool learnSpells = param.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) ||
(sPlayerbotAIConfig.allowLearnTrainerSpells &&
// TODO: Rewrite to only exclude start primary profession skills and make config dependent.
(trainer->GetTrainerType() != Trainer::Type::Tradeskill || !botAI->HasActivePlayerMaster()));
Iterate(target, learnSpells, spellId);
return true;
} }
bot->ModifyMoney(-int32(cost)); bool TrainerAction::isUseful()
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell.SpellId);
if (!spellInfo)
return;
bool learned = false;
for (uint8 j = 0; j < 3; ++j)
{ {
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL) Creature* target = GetCreatureTarget();
if (!target)
return false;
if (!target->IsInWorld() || target->IsDuringRemoveFromWorld() || !target->IsAlive())
return false;
return target->IsTrainer();
}
bool TrainerAction::isPossible()
{ {
uint32 learnedSpell = spellInfo->Effects[j].TriggerSpell; Creature* target = GetCreatureTarget();
if (!bot->HasSpell(learnedSpell)) if (!target)
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(target->GetEntry());
if (!trainer)
return false;
if (!trainer->IsTrainerValidForPlayer(bot))
return false;
if (trainer->GetSpells().empty())
return false;
return true;
}
Unit* TrainerAction::GetTarget()
{ {
bot->learnSpell(learnedSpell); // There are just two scenarios: the bot has a master or it doesn't. If the
learned = true; // bot has a master, the master should target a unit; otherwise, the bot
} // should target the unit itself.
} if (Player* master = GetMaster())
return master->GetSelectedUnit();
return bot->GetSelectedUnit();
} }
if (!learned && !bot->HasSpell(tSpell.SpellId)) Creature* TrainerAction::GetCreatureTarget()
bot->learnSpell(tSpell.SpellId); {
Unit* target = GetTarget();
msg << " - learned"; return target ? target->ToCreature() : nullptr;
} }
void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells) void TrainerAction::Iterate(Creature* creature, bool learnSpells, uint32 spellId)
{ {
TellHeader(creature); TellHeader(creature);
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry()); Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer) if (!trainer)
return; return;
float fDiscountMod = bot->GetReputationPriceDiscount(creature); float reputationDiscount = bot->GetReputationPriceDiscount(creature);
uint32 totalCost = 0; uint32 totalCost = 0;
for (auto& spell : trainer->GetSpells()) for (auto& spell : trainer->GetSpells())
{ {
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId))) // simplified version of Trainer::TeachSpell method
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue; continue;
if (!spells.empty() && spells.find(spell.SpellId) == spells.end()) if (!trainer->CanTeachSpell(bot, trainerSpell))
continue; continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId); if (spellId && trainerSpell->SpellId != spellId)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(trainerSpell->SpellId);
if (!spellInfo) if (!spellInfo)
continue; continue;
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod)); uint32 cost = static_cast<uint32>(floor(trainerSpell->MoneyCost * reputationDiscount));
totalCost += cost; totalCost += cost;
std::ostringstream out; std::ostringstream out;
out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost); out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost);
if (action) if (learnSpells)
(this->*action)(cost, spell, out); Learn(spellInfo, cost, out);
botAI->TellMaster(out); botAI->TellMaster(out);
} }
@ -87,55 +130,25 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell
TellFooter(totalCost); TellFooter(totalCost);
} }
bool TrainerAction::Execute(Event event) void TrainerAction::Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out)
{ {
std::string const text = event.getParam(); if (!botAI->HasCheat(BotCheatMask::gold))
Player* master = GetMaster();
Creature* creature = botAI->GetCreature(bot->GetTarget());
if (master)
{ {
creature = master->GetSelectedUnit() ? master->GetSelectedUnit()->ToCreature() : nullptr; if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost)
}
// if (AI_VALUE(GuidPosition, "rpg target") != bot->GetTarget())
// if (master)
// creature = botAI->GetCreature(master->GetTarget());
// else
// return false;
if (!creature || !creature->IsTrainer())
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer || !trainer->IsTrainerValidForPlayer(bot))
return false;
std::vector<Trainer::Spell> trainer_spells = trainer->GetSpells();
if (trainer_spells.empty())
{ {
botAI->TellError("No spells can be learned from this trainer"); out << " - too expensive";
return false; return;
} }
uint32 spell = chat->parseSpell(text); bot->ModifyMoney(-static_cast<int32>(cost));
SpellIds spells; }
if (spell)
spells.insert(spell);
if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) || if (spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
(sPlayerbotAIConfig.autoTrainSpells != "no" && bot->CastSpell(bot, spellInfo->Id, true);
(trainer->GetTrainerType() != Trainer::Type::Tradeskill ||
!botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make
// config dependent.
Iterate(creature, &TrainerAction::Learn, spells);
else else
Iterate(creature, nullptr, spells); bot->learnSpell(spellInfo->Id, false);
return true; out << " - learned";
} }
void TrainerAction::TellHeader(Creature* creature) void TrainerAction::TellHeader(Creature* creature)
@ -245,7 +258,8 @@ bool MaintenanceAction::Execute(Event /*event*/)
if (sPlayerbotAIConfig.altMaintenanceKeyring) if (sPlayerbotAIConfig.altMaintenanceKeyring)
factory.InitKeyring(); factory.InitKeyring();
if (sPlayerbotAIConfig.altMaintenanceGemsEnchants && bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) if (sPlayerbotAIConfig.altMaintenanceGemsEnchants &&
bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
factory.ApplyEnchantAndGemsNew(); factory.ApplyEnchantAndGemsNew();
} }

View File

@ -8,7 +8,6 @@
#include "Action.h" #include "Action.h"
#include "ChatHelper.h" #include "ChatHelper.h"
#include "Trainer.h"
class Creature; class Creature;
class PlayerbotAI; class PlayerbotAI;
@ -21,11 +20,14 @@ public:
TrainerAction(PlayerbotAI* botAI) : Action(botAI, "trainer") {} TrainerAction(PlayerbotAI* botAI) : Action(botAI, "trainer") {}
bool Execute(Event event) override; bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
Unit* GetTarget() override;
private: private:
typedef void (TrainerAction::*TrainerSpellAction)(uint32, const Trainer::Spell, std::ostringstream& msg); Creature* GetCreatureTarget();
void Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells); void Iterate(Creature* creature, bool learnSpells, uint32 spellId);
void Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg); void Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out);
void TellHeader(Creature* creature); void TellHeader(Creature* creature);
void TellFooter(uint32 totalCost); void TellFooter(uint32 totalCost);
}; };

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "AggressiveStrategy.h"
#include "Playerbots.h"
void AggressiveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"no target",
{
NextAction("aggressive target", 4.0f)
}
)
);
}

View File

@ -0,0 +1,22 @@
/*
* 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_AGGRESSIVESTRATEGY_H
#define _PLAYERBOT_AGGRESSIVESTRATEGY_H
#include "NonCombatStrategy.h"
class PlayerbotAI;
class AggressiveStrategy : public NonCombatStrategy
{
public:
AggressiveStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {}
std::string const getName() override { return "aggressive"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
};
#endif

View File

@ -6,6 +6,7 @@
#ifndef _PLAYERBOT_STRATEGYCONTEXT_H #ifndef _PLAYERBOT_STRATEGYCONTEXT_H
#define _PLAYERBOT_STRATEGYCONTEXT_H #define _PLAYERBOT_STRATEGYCONTEXT_H
#include "AggressiveStrategy.h"
#include "AttackEnemyPlayersStrategy.h" #include "AttackEnemyPlayersStrategy.h"
#include "BattlegroundStrategy.h" #include "BattlegroundStrategy.h"
#include "CastTimeStrategy.h" #include "CastTimeStrategy.h"
@ -61,6 +62,7 @@ public:
creators["gather"] = &StrategyContext::gather; creators["gather"] = &StrategyContext::gather;
creators["emote"] = &StrategyContext::emote; creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive; creators["passive"] = &StrategyContext::passive;
creators["aggressive"] = &StrategyContext::aggressive;
creators["save mana"] = &StrategyContext::auto_save_mana; creators["save mana"] = &StrategyContext::auto_save_mana;
creators["food"] = &StrategyContext::food; creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat; creators["chat"] = &StrategyContext::chat;
@ -144,6 +146,7 @@ private:
static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); } static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); }
static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); } static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); }
static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); } static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); }
static Strategy* aggressive(PlayerbotAI* botAI) { return new AggressiveStrategy(botAI); }
// static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); } // static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); }
static Strategy* auto_save_mana(PlayerbotAI* botAI) { return new HealerAutoSaveManaStrategy(botAI); } static Strategy* auto_save_mana(PlayerbotAI* botAI) { return new HealerAutoSaveManaStrategy(botAI); }
static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); } static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); }

View File

@ -163,54 +163,21 @@ bool RpgRepairTrigger::IsActive()
return false; return false;
} }
bool RpgTrainTrigger::IsTrainerOf(CreatureTemplate const* cInfo, Player* pPlayer)
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cInfo->Entry);
if (trainer->GetTrainerType() == Trainer::Type::Mount && trainer->GetTrainerRequirement() != pPlayer->getRace())
{
if (FactionTemplateEntry const* faction_template = sFactionTemplateStore.LookupEntry(cInfo->faction))
if (pPlayer->GetReputationRank(faction_template->faction) == REP_EXALTED)
return true;
return false;
}
return trainer->IsTrainerValidForPlayer(pPlayer);
}
bool RpgTrainTrigger::IsActive() bool RpgTrainTrigger::IsActive()
{ {
GuidPosition guidP(getGuidP()); GuidPosition gp = getGuidP();
if (!gp)
if (!guidP.HasNpcFlag(UNIT_NPC_FLAG_TRAINER))
return false; return false;
CreatureTemplate const* cInfo = guidP.GetCreatureTemplate(); if (!gp.HasNpcFlag(UNIT_NPC_FLAG_TRAINER))
if (!IsTrainerOf(cInfo, bot))
return false; return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cInfo->Entry); if (!AI_VALUE(bool, "can train"))
FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction); return false;
float fDiscountMod = bot->GetReputationPriceDiscount(factionTemplate);
for (auto& spell : trainer->GetSpells())
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
continue;
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod));
if (cost > AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells))
continue;
return true; return true;
} }
return false;
}
bool RpgHealTrigger::IsActive() bool RpgHealTrigger::IsActive()
{ {
if (!botAI->HasStrategy("heal", BOT_STATE_COMBAT)) if (!botAI->HasStrategy("heal", BOT_STATE_COMBAT))

View File

@ -134,8 +134,6 @@ class RpgTrainTrigger : public RpgTrigger
public: public:
RpgTrainTrigger(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgTrigger(botAI, name) {} RpgTrainTrigger(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgTrigger(botAI, name) {}
static bool IsTrainerOf(CreatureTemplate const* cInfo, Player* pPlayer);
bool IsActive() override; bool IsActive() override;
}; };

View File

@ -0,0 +1,66 @@
/*
* 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 "AggressiveTargetValue.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
Unit* AggressiveTargetValue::Calculate()
{
Player* master = GetMaster();
if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() ||
!GET_PLAYERBOT_AI(master)))
master = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
if (targets.empty())
return nullptr;
float aggroRange = sPlayerbotAIConfig.aggroDistance;
float distance = 0;
Unit* result = nullptr;
for (ObjectGuid const guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
continue;
if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid &&
bot->GetReactionTo(unit) >= REP_NEUTRAL)
continue;
if (!bot->IsHostileTo(unit) && unit->GetNpcFlags() != UNIT_NPC_FLAG_NONE)
continue;
if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE)
continue;
if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) &&
ServerFacade::instance().GetDistance2d(master, unit) > aggroRange)
continue;
if (!bot->IsWithinLOSInMap(unit))
continue;
if (bot->GetDistance(unit) > aggroRange)
continue;
float newdistance = bot->GetDistance(unit);
if (!result || (newdistance < distance))
{
distance = newdistance;
result = unit;
}
}
return result;
}

View File

@ -0,0 +1,22 @@
/*
* 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_AGGRESSIVETARGETVALUE_H
#define _PLAYERBOT_AGGRESSIVETARGETVALUE_H
#include "TargetValue.h"
class PlayerbotAI;
class Unit;
class AggressiveTargetValue : public TargetValue
{
public:
AggressiveTargetValue(PlayerbotAI* botAI, std::string const name = "aggressive target") : TargetValue(botAI, name) {}
Unit* Calculate() override;
};
#endif

View File

@ -9,7 +9,8 @@
uint32 MaxGearRepairCostValue::Calculate() uint32 MaxGearRepairCostValue::Calculate()
{ {
uint32 TotalCost = 0; uint32 totalCost = 0;
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{ {
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i); uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
@ -43,15 +44,16 @@ uint32 MaxGearRepairCostValue::Calculate()
uint32 costs = uint32(maxDurability * dmultiplier * double(dQualitymodEntry->quality_mod)); uint32 costs = uint32(maxDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
TotalCost += costs; totalCost += costs;
} }
return TotalCost; return totalCost;
} }
uint32 RepairCostValue::Calculate() uint32 RepairCostValue::Calculate()
{ {
uint32 TotalCost = 0; uint32 totalCost = 0;
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{ {
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i); uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
@ -86,24 +88,25 @@ uint32 RepairCostValue::Calculate()
dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)]; dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod)); uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
TotalCost += costs; totalCost += costs;
} }
return TotalCost; return totalCost;
} }
uint32 TrainCostValue::Calculate() uint32 TrainCostValue::Calculate()
{ {
uint32 TotalCost = 0; uint32 totalCost = 0;
std::set<uint32> spells; std::unordered_set<uint32> spells;
if (CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates()) CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
{ for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
{ {
if (!(itr->second.npcflag & UNIT_NPC_FLAG_TRAINER))
continue;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first); Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
if (!trainer) if (!trainer)
continue; continue;
@ -112,19 +115,22 @@ uint32 TrainCostValue::Calculate()
for (auto& spell : trainer->GetSpells()) for (auto& spell : trainer->GetSpells())
{ {
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId))) Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue; continue;
if (spells.find(spell.SpellId) != spells.end()) if (!trainer->CanTeachSpell(bot, trainerSpell))
continue; continue;
TotalCost += spell.MoneyCost; if (spells.find(trainerSpell->SpellId) != spells.end())
spells.insert(spell.SpellId); continue;
}
totalCost += trainerSpell->MoneyCost;
spells.insert(trainerSpell->SpellId);
} }
} }
return TotalCost; return totalCost;
} }
uint32 MoneyNeededForValue::Calculate() uint32 MoneyNeededForValue::Calculate()

View File

@ -44,6 +44,11 @@ bool CanSellValue::Calculate()
AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_AH))) > 1; AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_AH))) > 1;
} }
bool CanTrainValue::Calculate()
{
return AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) > 0;
}
bool CanFightEqualValue::Calculate() { return AI_VALUE(uint8, "durability") > 20; } bool CanFightEqualValue::Calculate() { return AI_VALUE(uint8, "durability") > 20; }
bool CanFightEliteValue::Calculate() bool CanFightEliteValue::Calculate()

View File

@ -58,6 +58,14 @@ public:
bool Calculate() override; bool Calculate() override;
}; };
class CanTrainValue : public BoolCalculatedValue
{
public:
CanTrainValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can train", 2 * 2000) {}
bool Calculate() override;
};
class CanFightEqualValue : public BoolCalculatedValue class CanFightEqualValue : public BoolCalculatedValue
{ {
public: public:

View File

@ -7,6 +7,7 @@
#define _PLAYERBOT_VALUECONTEXT_H #define _PLAYERBOT_VALUECONTEXT_H
#include "ActiveSpellValue.h" #include "ActiveSpellValue.h"
#include "AggressiveTargetValue.h"
#include "AlwaysLootListValue.h" #include "AlwaysLootListValue.h"
#include "AoeHealValues.h" #include "AoeHealValues.h"
#include "AoeValues.h" #include "AoeValues.h"
@ -51,6 +52,7 @@
#include "LineTargetValue.h" #include "LineTargetValue.h"
#include "LogLevelValue.h" #include "LogLevelValue.h"
#include "LootStrategyValue.h" #include "LootStrategyValue.h"
#include "LootValues.h"
#include "MaintenanceValues.h" #include "MaintenanceValues.h"
#include "ManaSaveLevelValue.h" #include "ManaSaveLevelValue.h"
#include "NearestAdsValue.h" #include "NearestAdsValue.h"
@ -143,6 +145,7 @@ public:
creators["pet target"] = &ValueContext::pet_target; creators["pet target"] = &ValueContext::pet_target;
creators["old target"] = &ValueContext::old_target; creators["old target"] = &ValueContext::old_target;
creators["grind target"] = &ValueContext::grind_target; creators["grind target"] = &ValueContext::grind_target;
creators["aggressive target"] = &ValueContext::aggressive_target;
creators["rti target"] = &ValueContext::rti_target; creators["rti target"] = &ValueContext::rti_target;
creators["rti cc target"] = &ValueContext::rti_cc_target; creators["rti cc target"] = &ValueContext::rti_cc_target;
creators["duel target"] = &ValueContext::duel_target; creators["duel target"] = &ValueContext::duel_target;
@ -272,6 +275,7 @@ public:
creators["can repair"] = &ValueContext::can_repair; creators["can repair"] = &ValueContext::can_repair;
creators["should sell"] = &ValueContext::should_sell; creators["should sell"] = &ValueContext::should_sell;
creators["can sell"] = &ValueContext::can_sell; creators["can sell"] = &ValueContext::can_sell;
creators["can train"] = &ValueContext::can_train;
creators["can fight equal"] = &ValueContext::can_fight_equal; creators["can fight equal"] = &ValueContext::can_fight_equal;
creators["can fight elite"] = &ValueContext::can_fight_elite; creators["can fight elite"] = &ValueContext::can_fight_elite;
creators["can fight boss"] = &ValueContext::can_fight_boss; creators["can fight boss"] = &ValueContext::can_fight_boss;
@ -457,6 +461,7 @@ private:
static UntypedValue* current_cc_target(PlayerbotAI* botAI) { return new CurrentCcTargetValue(botAI); } static UntypedValue* current_cc_target(PlayerbotAI* botAI) { return new CurrentCcTargetValue(botAI); }
static UntypedValue* pet_target(PlayerbotAI* botAI) { return new PetTargetValue(botAI); } static UntypedValue* pet_target(PlayerbotAI* botAI) { return new PetTargetValue(botAI); }
static UntypedValue* grind_target(PlayerbotAI* botAI) { return new GrindTargetValue(botAI); } static UntypedValue* grind_target(PlayerbotAI* botAI) { return new GrindTargetValue(botAI); }
static UntypedValue* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetValue(botAI); }
static UntypedValue* rti_target(PlayerbotAI* botAI) { return new RtiTargetValue(botAI); } static UntypedValue* rti_target(PlayerbotAI* botAI) { return new RtiTargetValue(botAI); }
static UntypedValue* rti_cc_target(PlayerbotAI* botAI) { return new RtiCcTargetValue(botAI); } static UntypedValue* rti_cc_target(PlayerbotAI* botAI) { return new RtiCcTargetValue(botAI); }
static UntypedValue* duel_target(PlayerbotAI* botAI) { return new DuelTargetValue(botAI); } static UntypedValue* duel_target(PlayerbotAI* botAI) { return new DuelTargetValue(botAI); }
@ -519,6 +524,7 @@ private:
static UntypedValue* can_repair(PlayerbotAI* botAI) { return new CanRepairValue(botAI); } static UntypedValue* can_repair(PlayerbotAI* botAI) { return new CanRepairValue(botAI); }
static UntypedValue* should_sell(PlayerbotAI* botAI) { return new ShouldSellValue(botAI); } static UntypedValue* should_sell(PlayerbotAI* botAI) { return new ShouldSellValue(botAI); }
static UntypedValue* can_sell(PlayerbotAI* botAI) { return new CanSellValue(botAI); } static UntypedValue* can_sell(PlayerbotAI* botAI) { return new CanSellValue(botAI); }
static UntypedValue* can_train(PlayerbotAI* botAI) { return new CanTrainValue(botAI); }
static UntypedValue* can_fight_equal(PlayerbotAI* botAI) { return new CanFightEqualValue(botAI); } static UntypedValue* can_fight_equal(PlayerbotAI* botAI) { return new CanFightEqualValue(botAI); }
static UntypedValue* can_fight_elite(PlayerbotAI* botAI) { return new CanFightEliteValue(botAI); } static UntypedValue* can_fight_elite(PlayerbotAI* botAI) { return new CanFightEliteValue(botAI); }
static UntypedValue* can_fight_boss(PlayerbotAI* botAI) { return new CanFightBossValue(botAI); } static UntypedValue* can_fight_boss(PlayerbotAI* botAI) { return new CanFightBossValue(botAI); }

View File

@ -201,8 +201,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
continue; continue;
// Check if position is within maximum allowed distance from boss // Check if position is within maximum allowed distance from boss
if (boss && sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) + if (boss && boss->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY()) > MAX_BOSS_DISTANCE)
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
continue; continue;
// Score this position based on: // Score this position based on:
@ -215,8 +214,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
float minOrbDist = std::numeric_limits<float>::max(); float minOrbDist = std::numeric_limits<float>::max();
for (Unit* orb : orbs) for (Unit* orb : orbs)
{ {
float orbDist = sqrt(pow(potentialPos.GetPositionX() - orb->GetPositionX(), 2) + float orbDist = orb->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY());
pow(potentialPos.GetPositionY() - orb->GetPositionY(), 2));
minOrbDist = std::min(minOrbDist, orbDist); minOrbDist = std::min(minOrbDist, orbDist);
} }
score += minOrbDist * 2.0f; // Weight orb distance more heavily score += minOrbDist * 2.0f; // Weight orb distance more heavily
@ -232,8 +230,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
// Factor in proximity to boss (closer is better, as long as we're safe from orbs) // Factor in proximity to boss (closer is better, as long as we're safe from orbs)
if (boss) if (boss)
{ {
float bossDist = sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) + float bossDist = boss->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY());
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2));
// Add points for being closer to boss (inverse relationship) // Add points for being closer to boss (inverse relationship)
// but only if we're safely away from orbs // but only if we're safely away from orbs
if (minOrbDist > SAFE_DISTANCE) if (minOrbDist > SAFE_DISTANCE)

View File

@ -128,7 +128,7 @@ namespace GruulsLairHelpers
Unit* krosh = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "krosh firehand")->Get(); Unit* krosh = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "krosh firehand")->Get();
if (krosh && krosh->IsAlive()) if (krosh && krosh->IsAlive())
{ {
float dist = sqrt(pow(pos.GetPositionX() - krosh->GetPositionX(), 2) + pow(pos.GetPositionY() - krosh->GetPositionY(), 2)); float dist = krosh->GetDistance2d(pos.GetPositionX(), pos.GetPositionY());
if (dist < KROSH_SAFE_DISTANCE) if (dist < KROSH_SAFE_DISTANCE)
isSafe = false; isSafe = false;
} }
@ -136,7 +136,7 @@ namespace GruulsLairHelpers
Unit* maulgar = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "high king maulgar")->Get(); Unit* maulgar = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "high king maulgar")->Get();
if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive()) if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive())
{ {
float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2)); float dist = maulgar->GetDistance2d(pos.GetPositionX(), pos.GetPositionY());
if (dist < MAULGAR_SAFE_DISTANCE) if (dist < MAULGAR_SAFE_DISTANCE)
isSafe = false; isSafe = false;
} }
@ -182,7 +182,7 @@ namespace GruulsLairHelpers
if (IsPositionSafe(botAI, bot, candidatePos)) if (IsPositionSafe(botAI, bot, candidatePos))
{ {
float movementDistance = sqrt(pow(destX - bot->GetPositionX(), 2) + pow(destY - bot->GetPositionY(), 2)); float movementDistance = bot->GetDistance2d(destX, destY);
if (movementDistance < bestScore) if (movementDistance < bestScore)
{ {
bestScore = movementDistance; bestScore = movementDistance;

View File

@ -1800,8 +1800,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit*)
Unit* puddle = botAI->GetUnit(puddleGuid); Unit* puddle = botAI->GetUnit(puddleGuid);
if (puddle && botAI->GetAura("Ooze Flood", puddle)) if (puddle && botAI->GetAura("Ooze Flood", puddle))
{ {
float puddleDistance = std::sqrt(std::pow(newX - puddle->GetPositionX(), 2) + float puddleDistance = puddle->GetDistance2d(newX, newY);
std::pow(newY - puddle->GetPositionY(), 2));
if (puddleDistance < puddleSafeDistance) if (puddleDistance < puddleSafeDistance)
{ {
isSafeFromPuddles = false; isSafeFromPuddles = false;
@ -1921,8 +1920,7 @@ bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle,
float moveZ = bot->GetPositionZ(); float moveZ = bot->GetPositionZ();
// Check distances and line of sight // Check distances and line of sight
float newPuddleDistance = float newPuddleDistance = puddle->GetDistance2d(moveX, moveY);
sqrt(pow(moveX - puddle->GetPositionX(), 2) + pow(moveY - puddle->GetPositionY(), 2));
float newCenterDistance = sqrt(pow(moveX - ICC_ROTFACE_CENTER_POSITION.GetPositionX(), 2) + float newCenterDistance = sqrt(pow(moveX - ICC_ROTFACE_CENTER_POSITION.GetPositionX(), 2) +
pow(moveY - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), 2)); pow(moveY - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), 2));
@ -2125,8 +2123,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni
// Ensure the target position is at least 30 yards away from the puddle // Ensure the target position is at least 30 yards away from the puddle
if (puddle) if (puddle)
{ {
float puddleDistance = std::sqrt(std::pow(targetX - puddle->GetPositionX(), 2) + float puddleDistance = puddle->GetDistance2d(targetX, targetY);
std::pow(targetY - puddle->GetPositionY(), 2));
if (puddleDistance < puddleSafeDistance) if (puddleDistance < puddleSafeDistance)
{ {
// Adjust the target position to move further away from the puddle // Adjust the target position to move further away from the puddle
@ -2203,8 +2200,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation()
if (!puddle || !botAI->HasAura("Ooze Flood", puddle)) if (!puddle || !botAI->HasAura("Ooze Flood", puddle))
continue; continue;
float puddleDistance = float puddleDistance = puddle->GetDistance2d(moveX, moveY);
std::sqrt(std::pow(moveX - puddle->GetPositionX(), 2) + std::pow(moveY - puddle->GetPositionY(), 2));
if (puddleDistance < 30.0f) if (puddleDistance < 30.0f)
{ {
// Adjust the position to move further away from the puddle // Adjust the position to move further away from the puddle
@ -2407,7 +2403,7 @@ bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float
if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) if (Aura* grow = unit->GetAura(SPELL_GROW_AURA))
safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER); safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER);
float dist = sqrt(pow(x - unit->GetPositionX(), 2) + pow(y - unit->GetPositionY(), 2)); float dist = unit->GetDistance2d(x, y);
if (dist < safeDistance) if (dist < safeDistance)
return true; return true;
} }
@ -2650,7 +2646,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud)
float minGasBombDist = FLT_MAX; float minGasBombDist = FLT_MAX;
for (Unit* bomb : gasBombs) for (Unit* bomb : gasBombs)
{ {
float bombDist = sqrt(pow(testX - bomb->GetPositionX(), 2) + pow(testY - bomb->GetPositionY(), 2)); float bombDist = bomb->GetDistance2d(testX, testY);
if (bombDist < minGasBombDist) if (bombDist < minGasBombDist)
minGasBombDist = bombDist; minGasBombDist = bombDist;
} }
@ -2717,8 +2713,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud)
float minEmergencyGasBombDist = FLT_MAX; float minEmergencyGasBombDist = FLT_MAX;
for (Unit* bomb : gasBombs) for (Unit* bomb : gasBombs)
{ {
float bombDist = sqrt(pow(emergencyPos.GetPositionX() - bomb->GetPositionX(), 2) + float bombDist = bomb->GetDistance2d(emergencyPos.GetPositionX(), emergencyPos.GetPositionY());
pow(emergencyPos.GetPositionY() - bomb->GetPositionY(), 2));
if (bombDist < minEmergencyGasBombDist) if (bombDist < minEmergencyGasBombDist)
minEmergencyGasBombDist = bombDist; minEmergencyGasBombDist = bombDist;
} }
@ -4487,8 +4482,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura
// Maintain minimum distance from center position (if too close to center, move out) // Maintain minimum distance from center position (if too close to center, move out)
float centerX = ICC_BQL_CENTER_POSITION.GetPositionX(); float centerX = ICC_BQL_CENTER_POSITION.GetPositionX();
float centerY = ICC_BQL_CENTER_POSITION.GetPositionY(); float centerY = ICC_BQL_CENTER_POSITION.GetPositionY();
float centerDist = float centerDist = bot->GetDistance2d(centerX, centerY);
std::sqrt(std::pow(bot->GetPositionX() - centerX, 2) + std::pow(bot->GetPositionY() - centerY, 2));
if (centerDist < MIN_CENTER_DISTANCE && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) if (centerDist < MIN_CENTER_DISTANCE && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f))
{ {
float dx = bot->GetPositionX() - centerX; float dx = bot->GetPositionX() - centerX;
@ -6904,7 +6898,7 @@ bool IccLichKingShadowTrapAction::Execute(Event /*event*/)
Unit* trap = botAI->GetUnit(trapGuid); Unit* trap = botAI->GetUnit(trapGuid);
if (!trap) if (!trap)
continue; continue;
float distToTrap = sqrt(pow(testX - trap->GetPositionX(), 2) + pow(testY - trap->GetPositionY(), 2)); float distToTrap = trap->GetDistance2d(testX, testY);
if (distToTrap < SAFE_DISTANCE) if (distToTrap < SAFE_DISTANCE)
{ {
isSafe = false; isSafe = false;

View File

@ -132,7 +132,7 @@ namespace MagtheridonHelpers
} }
for (Unit* hazard : debrisHazards) for (Unit* hazard : debrisHazards)
{ {
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2)); float dist = hazard->GetDistance2d(x, y);
if (dist < 9.0f) if (dist < 9.0f)
return false; return false;
} }
@ -145,7 +145,7 @@ namespace MagtheridonHelpers
if (!go || go->GetEntry() != GO_BLAZE) if (!go || go->GetEntry() != GO_BLAZE)
continue; continue;
float dist = std::sqrt(std::pow(x - go->GetPositionX(), 2) + std::pow(y - go->GetPositionY(), 2)); float dist = go->GetDistance2d(x, y);
if (dist < 5.0f) if (dist < 5.0f)
return false; return false;
} }

View File

@ -262,10 +262,10 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
// that removes the distance check and keeps all other checks // that removes the distance check and keeps all other checks
switch (questGiver->GetTypeId()) switch (questGiver->GetTypeId())
{ {
case TYPEID_UNIT: case TYPEID_UNIT: // Player::GetNPCIfCanInteractWith
{ {
ObjectGuid guid = questGiver->GetGUID(); ObjectGuid guid = questGiver->GetGUID();
uint32 npcflagmask = UNIT_NPC_FLAG_QUESTGIVER;
// unit checks // unit checks
if (!guid) if (!guid)
return false; return false;
@ -292,7 +292,7 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
return false; return false;
// appropriate npc type // appropriate npc type
if (npcflagmask && !creature->HasNpcFlag(NPCFlags(npcflagmask))) if (!creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER))
return false; return false;
// not allow interaction under control, but allow with own pets // not allow interaction under control, but allow with own pets
@ -303,35 +303,24 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
if (creature->GetReactionTo(bot) <= REP_UNFRIENDLY) if (creature->GetReactionTo(bot) <= REP_UNFRIENDLY)
return false; return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
// pussywizard: many npcs have missing conditions for class training and rogue trainer can for eg. train
// dual wield to a shaman :/ too many to change in sql and watch in the future pussywizard: this function is
// not used when talking, but when already taking action (buy spell, reset talents, show spell list)
if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) &&
trainer->GetTrainerType() == Trainer::Type::Class &&
!trainer->IsTrainerValidForPlayer(bot))
return false;
return true; return true;
} }
case TYPEID_GAMEOBJECT: case TYPEID_GAMEOBJECT: // Player::GetGameObjectIfCanInteractWith
{ {
ObjectGuid guid = questGiver->GetGUID(); ObjectGuid guid = questGiver->GetGUID();
GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER;
if (GameObject* go = bot->GetMap()->GetGameObject(guid)) if (GameObject* go = bot->GetMap()->GetGameObject(guid))
{ {
if (go->GetGoType() == type) if (go->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
{ {
// Players cannot interact with gameobjects that use the "Point" icon // Players cannot interact with gameobjects that use the "Point" icon
if (go->GetGOInfo()->IconName == "Point") if (go->GetGOInfo()->IconName == "Point")
{
return false; return false;
}
return true; return true;
} }
} }
return false; return false;
} }
// unused for now // unused for now

View File

@ -4739,38 +4739,13 @@ void PlayerbotFactory::InitAttunementQuests()
uint32 currentXP = bot->GetUInt32Value(PLAYER_XP); uint32 currentXP = bot->GetUInt32Value(PLAYER_XP);
// List of attunement quest IDs
std::list<uint32> attunementQuestsTBC = {
// Caverns of Time - Part 1
10279, // To The Master's Lair
10277, // The Caverns of Time
// Caverns of Time - Part 2 (Escape from Durnholde Keep)
10282, // Old Hillsbrad
10283, // Taretha's Diversion
10284, // Escape from Durnholde
10285, // Return to Andormu
// Caverns of Time - Part 2 (The Black Morass)
10296, // The Black Morass
10297, // The Opening of the Dark Portal
10298, // Hero of the Brood
// Magister's Terrace Attunement
11481, // Crisis at the Sunwell
11482, // Duty Calls
11488, // Magisters' Terrace
11490, // The Scryer's Scryer
11492 // Hard to Kill
};
// Complete all level-appropriate attunement quests for the bot // Complete all level-appropriate attunement quests for the bot
if (level >= 60) if (level >= 60)
{ {
std::list<uint32> questsToComplete; std::list<uint32> questsToComplete;
// Check each quest status before adding to the completion list // Check each quest status before adding to the completion list
for (uint32 questId : attunementQuestsTBC) for (uint32 questId : sPlayerbotAIConfig.attunementQuests)
{ {
QuestStatus questStatus = bot->GetQuestStatus(questId); QuestStatus questStatus = bot->GetQuestStatus(questId);

View File

@ -45,18 +45,18 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
switch (proto->Spells[j].SpellTrigger) switch (proto->Spells[j].SpellTrigger)
{ {
case ITEM_SPELLTRIGGER_ON_USE: case ITEM_SPELLTRIGGER_ON_USE:
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, proto->Spells[j].SpellCooldown); CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(proto->Spells[j].SpellCooldown));
break; break;
case ITEM_SPELLTRIGGER_ON_EQUIP: case ITEM_SPELLTRIGGER_ON_EQUIP:
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 0); CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(0));
break; break;
case ITEM_SPELLTRIGGER_CHANCE_ON_HIT: case ITEM_SPELLTRIGGER_CHANCE_ON_HIT:
if (type_ & CollectorType::MELEE) if (type_ & CollectorType::MELEE)
{ {
if (proto->Spells[j].SpellPPMRate > 0.01f) if (proto->Spells[j].SpellPPMRate > 0.01f)
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / proto->Spells[j].SpellPPMRate); CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(static_cast<int>(60000 / proto->Spells[j].SpellPPMRate)));
else else
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / 1.8f); // Default PPM = 1.8 CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(static_cast<int>(60000 / 1.8f))); // Default PPM = 1.8
} }
break; break;
default: default:
@ -71,7 +71,7 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
} }
} }
void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 spellCooldown) void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, Milliseconds spellCooldown)
{ {
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@ -81,21 +81,21 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
if (SpecialSpellFilter(spellId)) if (SpecialSpellFilter(spellId))
return; return;
const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); const SpellProcEntry* eventEntry = sSpellMgr->GetSpellProcEntry(spellInfo->Id);
uint32 triggerCooldown = eventEntry ? eventEntry->cooldown : 0; Milliseconds triggerCooldown = eventEntry ? eventEntry->Cooldown : 0ms;
bool canNextTrigger = true; bool canNextTrigger = true;
uint32 procFlags; uint32 procFlags;
uint32 procChance; uint32 procChance;
if (eventEntry && eventEntry->procFlags) if (eventEntry && eventEntry->ProcFlags)
procFlags = eventEntry->procFlags; procFlags = eventEntry->ProcFlags;
else else
procFlags = spellInfo->ProcFlags; procFlags = spellInfo->ProcFlags;
if (eventEntry && eventEntry->customChance) if (eventEntry && eventEntry->Chance)
procChance = eventEntry->customChance; procChance = eventEntry->Chance;
else else
procChance = spellInfo->ProcChance; procChance = spellInfo->ProcChance;
bool lowChance = procChance <= 5; bool lowChance = procChance <= 5;
@ -142,11 +142,11 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
break; break;
float coverage; float coverage;
if (spellCooldown <= 2000 || spellInfo->GetDuration() == -1) if (spellCooldown.count() <= 2000 || spellInfo->GetDuration() == -1)
coverage = 1.0f; coverage = 1.0f;
else else
coverage = coverage =
std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown)); std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown.count()));
multiplier *= coverage; multiplier *= coverage;
HandleApplyAura(effectInfo, multiplier, canNextTrigger, triggerCooldown); HandleApplyAura(effectInfo, multiplier, canNextTrigger, triggerCooldown);
@ -155,9 +155,9 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_HEAL: case SPELL_EFFECT_HEAL:
{ {
/// @todo Handle spell without cooldown /// @todo Handle spell without cooldown
if (!spellCooldown) if (!spellCooldown.count())
break; break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f); float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo); int32 val = AverageValue(effectInfo);
float transfer_multiplier = 1; float transfer_multiplier = 1;
stats[STATS_TYPE_HEAL_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier; stats[STATS_TYPE_HEAL_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier;
@ -166,11 +166,11 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_ENERGIZE: case SPELL_EFFECT_ENERGIZE:
{ {
/// @todo Handle spell without cooldown /// @todo Handle spell without cooldown
if (!spellCooldown) if (!spellCooldown.count())
break; break;
if (effectInfo.MiscValue != POWER_MANA) if (effectInfo.MiscValue != POWER_MANA)
break; break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f); float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo); int32 val = AverageValue(effectInfo);
float transfer_multiplier = 0.2; float transfer_multiplier = 0.2;
stats[STATS_TYPE_MANA_REGENERATION] += (float)val / normalizedCd * multiplier * transfer_multiplier; stats[STATS_TYPE_MANA_REGENERATION] += (float)val / normalizedCd * multiplier * transfer_multiplier;
@ -179,9 +179,9 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_SCHOOL_DAMAGE: case SPELL_EFFECT_SCHOOL_DAMAGE:
{ {
/// @todo Handle spell without cooldown /// @todo Handle spell without cooldown
if (!spellCooldown) if (!spellCooldown.count())
break; break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f); float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo); int32 val = AverageValue(effectInfo);
if (type_ & (CollectorType::MELEE | CollectorType::RANGED)) if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
{ {
@ -352,12 +352,12 @@ bool StatsCollector::SpecialEnchantFilter(uint32 enchantSpellId)
bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict) bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict)
{ {
const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); const SpellProcEntry* eventEntry = sSpellMgr->GetSpellProcEntry(spellInfo->Id);
uint32 spellFamilyName = 0; uint32 spellFamilyName = 0;
if (eventEntry) if (eventEntry)
{ {
spellFamilyName = eventEntry->spellFamilyName; spellFamilyName = eventEntry->SpellFamilyName;
flag96 spellFamilyMask = eventEntry->spellFamilyMask; flag96 spellFamilyMask = eventEntry->SpellFamilyMask;
if (spellFamilyName != 0) if (spellFamilyName != 0)
{ {
if (!CheckSpellValidation(spellFamilyName, spellFamilyMask, strict)) if (!CheckSpellValidation(spellFamilyName, spellFamilyMask, strict))
@ -548,7 +548,7 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
} }
void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
uint32 triggerCooldown) Milliseconds triggerCooldown)
{ {
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA) if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
return; return;

View File

@ -65,7 +65,7 @@ public:
StatsCollector(StatsCollector& stats) = default; StatsCollector(StatsCollector& stats) = default;
void Reset(); void Reset();
void CollectItemStats(ItemTemplate const* proto); void CollectItemStats(ItemTemplate const* proto);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1); void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, Milliseconds spellCooldown = -1ms);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0); void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0);
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true); bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true); bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
@ -79,7 +79,7 @@ private:
bool SpecialEnchantFilter(uint32 enchantSpellId); bool SpecialEnchantFilter(uint32 enchantSpellId);
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
uint32 triggerCooldown); Milliseconds triggerCooldown);
float AverageValue(const SpellEffectInfo& effectInfo); float AverageValue(const SpellEffectInfo& effectInfo);
private: private:

View File

@ -228,13 +228,13 @@ WorldPosition WorldPosition::offset(WorldPosition* center)
float WorldPosition::size() float WorldPosition::size()
{ {
return sqrt(pow(GetPositionX(), 2.0) + pow(GetPositionY(), 2.0) + pow(GetPositionZ(), 2.0)); return GetExactDist(0.0f, 0.0f, 0.0f);
} }
float WorldPosition::distance(WorldPosition* center) float WorldPosition::distance(WorldPosition* center)
{ {
if (GetMapId() == center->GetMapId()) if (GetMapId() == center->GetMapId())
return relPoint(center).size(); return GetExactDist(center->GetPositionX(), center->GetPositionY(), center->GetPositionZ());
// this -> mapTransfer | mapTransfer -> center // this -> mapTransfer | mapTransfer -> center
return TravelMgr::instance().mapTransDistance(*this, *center); return TravelMgr::instance().mapTransDistance(*this, *center);
@ -243,7 +243,7 @@ float WorldPosition::distance(WorldPosition* center)
float WorldPosition::fDist(WorldPosition* center) float WorldPosition::fDist(WorldPosition* center)
{ {
if (GetMapId() == center->GetMapId()) if (GetMapId() == center->GetMapId())
return sqrt(sqDistance2d(center)); return GetExactDist2d(center->GetPositionX(), center->GetPositionY());
// this -> mapTransfer | mapTransfer -> center // this -> mapTransfer | mapTransfer -> center
return TravelMgr::instance().fastMapTransDistance(*this, *center); return TravelMgr::instance().fastMapTransDistance(*this, *center);

View File

@ -179,8 +179,7 @@ public:
// Quick square distance in 2d plane. // Quick square distance in 2d plane.
float sqDistance2d(WorldPosition center) float sqDistance2d(WorldPosition center)
{ {
return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) + return GetExactDist2dSq(center.GetPositionX(), center.GetPositionY());
(GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY());
} }
// Quick square distance calculation without map check. Used for getting the minimum distant points. // Quick square distance calculation without map check. Used for getting the minimum distant points.
@ -193,8 +192,7 @@ public:
float sqDistance2d(WorldPosition* center) float sqDistance2d(WorldPosition* center)
{ {
return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) + return GetExactDist2dSq(center->GetPositionX(), center->GetPositionY());
(GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY());
} }
float sqDistance(WorldPosition* center) float sqDistance(WorldPosition* center)

View File

@ -179,6 +179,9 @@ bool PlayerbotAIConfig::Initialize()
"179490,141596,160836,160845,179516,176224,181085,176112,128308,128403," "179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,"
"165739,165738,175245,175970,176325,176327,123329,2560"), "165739,165738,175245,175970,176325,176327,123329,2560"),
disallowedGameObjects); disallowedGameObjects);
LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901"),
attunementQuests);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false); botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true); randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500); minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
@ -620,7 +623,7 @@ bool PlayerbotAIConfig::Initialize()
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true); syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false); syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true); dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true);
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes"); allowLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowLearnTrainerSpells", true);
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true); autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false); autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0); hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);

View File

@ -98,6 +98,7 @@ public:
std::set<uint32> aoeAvoidSpellWhitelist; std::set<uint32> aoeAvoidSpellWhitelist;
bool tellWhenAvoidAoe; bool tellWhenAvoidAoe;
std::set<uint32> disallowedGameObjects; std::set<uint32> disallowedGameObjects;
std::set<uint32> attunementQuests;
uint32 openGoSpell; uint32 openGoSpell;
bool randomBotAutologin; bool randomBotAutologin;
@ -352,7 +353,7 @@ public:
bool syncQuestWithPlayer; bool syncQuestWithPlayer;
bool syncQuestForPlayer; bool syncQuestForPlayer;
bool dropObsoleteQuests; bool dropObsoleteQuests;
std::string autoTrainSpells; bool allowLearnTrainerSpells;
bool autoPickTalents; bool autoPickTalents;
bool autoUpgradeEquip; bool autoUpgradeEquip;
int32 hunterWolfPet; int32 hunterWolfPet;