diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index a2f06885..4365cf95 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -192,9 +192,12 @@ AiPlayerbot.AutoInitOnly = 0 # Default: 1.0 (same with the player) 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 -AiPlayerbot.AutoTrainSpells = yes +# +# AllowLearnTrainerSpells +# Description: Allow the bot to learn trainers' spells as long as it has the money. +# Default: 1 - (Enabled) +# 0 - (Disabled) +AiPlayerbot.AllowLearnTrainerSpells = 1 # # diff --git a/src/Ai/Base/Actions/RpgSubActions.cpp b/src/Ai/Base/Actions/RpgSubActions.cpp index de16ed17..bc16ee75 100644 --- a/src/Ai/Base/Actions/RpgSubActions.cpp +++ b/src/Ai/Base/Actions/RpgSubActions.cpp @@ -5,6 +5,7 @@ #include "RpgSubActions.h" +#include "BudgetValues.h" #include "ChooseRpgTargetAction.h" #include "EmoteAction.h" #include "Formations.h" @@ -53,7 +54,11 @@ ObjectGuid RpgHelper::guid() { return (ObjectGuid)guidP(); } bool RpgHelper::InRange() { - return guidP() ? (guidP().sqDistance2d(bot) < INTERACTION_DISTANCE * INTERACTION_DISTANCE) : false; + GuidPosition gp = guidP(); + if (!gp) + return false; + + return gp.sqDistance2d(bot) < INTERACTION_DISTANCE * INTERACTION_DISTANCE; } void RpgHelper::setFacingTo(GuidPosition guidPosition) @@ -250,6 +255,60 @@ Event RpgSellAction::ActionEvent(Event /*event*/) { return Event("rpg action", " 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(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"; } bool RpgHealAction::Execute(Event /*event*/) diff --git a/src/Ai/Base/Actions/RpgSubActions.h b/src/Ai/Base/Actions/RpgSubActions.h index 8d5fbda4..d5add642 100644 --- a/src/Ai/Base/Actions/RpgSubActions.h +++ b/src/Ai/Base/Actions/RpgSubActions.h @@ -165,6 +165,9 @@ class RpgTrainAction : public RpgSubAction public: RpgTrainAction(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgSubAction(botAI, name) {} + bool isPossible() override; + bool isUseful() override; + private: std::string const ActionName() override; }; diff --git a/src/Ai/Base/Actions/TrainerAction.cpp b/src/Ai/Base/Actions/TrainerAction.cpp index 0f325609..4f46bba1 100644 --- a/src/Ai/Base/Actions/TrainerAction.cpp +++ b/src/Ai/Base/Actions/TrainerAction.cpp @@ -9,77 +9,120 @@ #include "Event.h" #include "PlayerbotFactory.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)) - { - if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost) - { - msg << " - too expensive"; - return; - } + std::string const param = event.getParam(); - bot->ModifyMoney(-int32(cost)); - } + Creature* target = GetCreatureTarget(); + if (!target) + return false; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell.SpellId); - if (!spellInfo) - return; + Trainer::Trainer* trainer = sObjectMgr->GetTrainer(target->GetEntry()); + if (!trainer) + return false; - bool learned = false; - for (uint8 j = 0; j < 3; ++j) - { - if (spellInfo->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL) - { - uint32 learnedSpell = spellInfo->Effects[j].TriggerSpell; - if (!bot->HasSpell(learnedSpell)) - { - bot->learnSpell(learnedSpell); - learned = true; - } - } - } + // 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); - if (!learned && !bot->HasSpell(tSpell.SpellId)) - bot->learnSpell(tSpell.SpellId); + 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())); - msg << " - learned"; + Iterate(target, learnSpells, spellId); + + return true; } -void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells) +bool TrainerAction::isUseful() +{ + Creature* target = GetCreatureTarget(); + if (!target) + return false; + + if (!target->IsInWorld() || target->IsDuringRemoveFromWorld() || !target->IsAlive()) + return false; + + return target->IsTrainer(); +} + +bool TrainerAction::isPossible() +{ + Creature* target = GetCreatureTarget(); + 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() +{ + // There are just two scenarios: the bot has a master or it doesn't. If the + // 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(); +} + +Creature* TrainerAction::GetCreatureTarget() +{ + Unit* target = GetTarget(); + return target ? target->ToCreature() : nullptr; +} + +void TrainerAction::Iterate(Creature* creature, bool learnSpells, uint32 spellId) { TellHeader(creature); Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry()); - if (!trainer) return; - float fDiscountMod = bot->GetReputationPriceDiscount(creature); + float reputationDiscount = bot->GetReputationPriceDiscount(creature); uint32 totalCost = 0; 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; - if (!spells.empty() && spells.find(spell.SpellId) == spells.end()) + if (!trainer->CanTeachSpell(bot, trainerSpell)) continue; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId); + if (spellId && trainerSpell->SpellId != spellId) + continue; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(trainerSpell->SpellId); if (!spellInfo) continue; - uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod)); + uint32 cost = static_cast(floor(trainerSpell->MoneyCost * reputationDiscount)); totalCost += cost; std::ostringstream out; out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost); - if (action) - (this->*action)(cost, spell, out); + if (learnSpells) + Learn(spellInfo, cost, out); botAI->TellMaster(out); } @@ -87,55 +130,25 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell TellFooter(totalCost); } -bool TrainerAction::Execute(Event event) +void TrainerAction::Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out) { - std::string const text = event.getParam(); - - Player* master = GetMaster(); - - Creature* creature = botAI->GetCreature(bot->GetTarget()); - - if (master) + if (!botAI->HasCheat(BotCheatMask::gold)) { - creature = master->GetSelectedUnit() ? master->GetSelectedUnit()->ToCreature() : nullptr; - } - // if (AI_VALUE(GuidPosition, "rpg target") != bot->GetTarget()) - // if (master) - // creature = botAI->GetCreature(master->GetTarget()); - // else - // return false; + if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost) + { + out << " - too expensive"; + return; + } - if (!creature || !creature->IsTrainer()) - return false; - - Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry()); - - if (!trainer || !trainer->IsTrainerValidForPlayer(bot)) - return false; - - std::vector trainer_spells = trainer->GetSpells(); - - if (trainer_spells.empty()) - { - botAI->TellError("No spells can be learned from this trainer"); - return false; + bot->ModifyMoney(-static_cast(cost)); } - uint32 spell = chat->parseSpell(text); - SpellIds spells; - if (spell) - spells.insert(spell); - - if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) || - (sPlayerbotAIConfig.autoTrainSpells != "no" && - (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); + if (spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL)) + bot->CastSpell(bot, spellInfo->Id, true); else - Iterate(creature, nullptr, spells); + bot->learnSpell(spellInfo->Id, false); - return true; + out << " - learned"; } void TrainerAction::TellHeader(Creature* creature) @@ -245,7 +258,8 @@ bool MaintenanceAction::Execute(Event /*event*/) if (sPlayerbotAIConfig.altMaintenanceKeyring) factory.InitKeyring(); - if (sPlayerbotAIConfig.altMaintenanceGemsEnchants && bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) + if (sPlayerbotAIConfig.altMaintenanceGemsEnchants && + bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) factory.ApplyEnchantAndGemsNew(); } diff --git a/src/Ai/Base/Actions/TrainerAction.h b/src/Ai/Base/Actions/TrainerAction.h index e6046c3a..bfb6538d 100644 --- a/src/Ai/Base/Actions/TrainerAction.h +++ b/src/Ai/Base/Actions/TrainerAction.h @@ -8,7 +8,6 @@ #include "Action.h" #include "ChatHelper.h" -#include "Trainer.h" class Creature; class PlayerbotAI; @@ -21,11 +20,14 @@ public: TrainerAction(PlayerbotAI* botAI) : Action(botAI, "trainer") {} bool Execute(Event event) override; + bool isUseful() override; + bool isPossible() override; + Unit* GetTarget() override; private: - typedef void (TrainerAction::*TrainerSpellAction)(uint32, const Trainer::Spell, std::ostringstream& msg); - void Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells); - void Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg); + Creature* GetCreatureTarget(); + void Iterate(Creature* creature, bool learnSpells, uint32 spellId); + void Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out); void TellHeader(Creature* creature); void TellFooter(uint32 totalCost); }; diff --git a/src/Ai/Base/Trigger/RpgTriggers.cpp b/src/Ai/Base/Trigger/RpgTriggers.cpp index 0151c6c5..1031bd5e 100644 --- a/src/Ai/Base/Trigger/RpgTriggers.cpp +++ b/src/Ai/Base/Trigger/RpgTriggers.cpp @@ -163,52 +163,19 @@ bool RpgRepairTrigger::IsActive() 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() { - GuidPosition guidP(getGuidP()); - - if (!guidP.HasNpcFlag(UNIT_NPC_FLAG_TRAINER)) + GuidPosition gp = getGuidP(); + if (!gp) return false; - CreatureTemplate const* cInfo = guidP.GetCreatureTemplate(); - - if (!IsTrainerOf(cInfo, bot)) + if (!gp.HasNpcFlag(UNIT_NPC_FLAG_TRAINER)) return false; - Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cInfo->Entry); - FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction); - float fDiscountMod = bot->GetReputationPriceDiscount(factionTemplate); + if (!AI_VALUE(bool, "can train")) + return false; - 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 false; + return true; } bool RpgHealTrigger::IsActive() diff --git a/src/Ai/Base/Trigger/RpgTriggers.h b/src/Ai/Base/Trigger/RpgTriggers.h index ce8cd2c1..31734836 100644 --- a/src/Ai/Base/Trigger/RpgTriggers.h +++ b/src/Ai/Base/Trigger/RpgTriggers.h @@ -134,8 +134,6 @@ class RpgTrainTrigger : public RpgTrigger public: RpgTrainTrigger(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgTrigger(botAI, name) {} - static bool IsTrainerOf(CreatureTemplate const* cInfo, Player* pPlayer); - bool IsActive() override; }; diff --git a/src/Ai/Base/Value/BudgetValues.cpp b/src/Ai/Base/Value/BudgetValues.cpp index daa7aca8..fbc04f49 100644 --- a/src/Ai/Base/Value/BudgetValues.cpp +++ b/src/Ai/Base/Value/BudgetValues.cpp @@ -9,7 +9,8 @@ uint32 MaxGearRepairCostValue::Calculate() { - uint32 TotalCost = 0; + uint32 totalCost = 0; + for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++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)); - TotalCost += costs; + totalCost += costs; } - return TotalCost; + return totalCost; } uint32 RepairCostValue::Calculate() { - uint32 TotalCost = 0; + uint32 totalCost = 0; + for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) { uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i); @@ -86,45 +88,49 @@ uint32 RepairCostValue::Calculate() dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)]; uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod)); - TotalCost += costs; + totalCost += costs; } - return TotalCost; + return totalCost; } uint32 TrainCostValue::Calculate() { - uint32 TotalCost = 0; + uint32 totalCost = 0; - std::set spells; + std::unordered_set 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); + if (!trainer) + continue; + + if (trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot)) + continue; + + for (auto& spell : trainer->GetSpells()) { - Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first); - - if (!trainer) + Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId); + if (!trainerSpell) continue; - if (trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot)) + if (!trainer->CanTeachSpell(bot, trainerSpell)) continue; - for (auto& spell : trainer->GetSpells()) - { - if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId))) - continue; + if (spells.find(trainerSpell->SpellId) != spells.end()) + continue; - if (spells.find(spell.SpellId) != spells.end()) - continue; - - TotalCost += spell.MoneyCost; - spells.insert(spell.SpellId); - } + totalCost += trainerSpell->MoneyCost; + spells.insert(trainerSpell->SpellId); } } - return TotalCost; + return totalCost; } uint32 MoneyNeededForValue::Calculate() diff --git a/src/Ai/Base/Value/MaintenanceValues.cpp b/src/Ai/Base/Value/MaintenanceValues.cpp index 6439bef2..35ce1ee4 100644 --- a/src/Ai/Base/Value/MaintenanceValues.cpp +++ b/src/Ai/Base/Value/MaintenanceValues.cpp @@ -44,6 +44,11 @@ bool CanSellValue::Calculate() 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 CanFightEliteValue::Calculate() diff --git a/src/Ai/Base/Value/MaintenanceValues.h b/src/Ai/Base/Value/MaintenanceValues.h index 5916f7aa..7efcb593 100644 --- a/src/Ai/Base/Value/MaintenanceValues.h +++ b/src/Ai/Base/Value/MaintenanceValues.h @@ -58,6 +58,14 @@ public: bool Calculate() override; }; +class CanTrainValue : public BoolCalculatedValue +{ +public: + CanTrainValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can train", 2 * 2000) {} + + bool Calculate() override; +}; + class CanFightEqualValue : public BoolCalculatedValue { public: diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index b67aee9b..115294bd 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -51,6 +51,7 @@ #include "LineTargetValue.h" #include "LogLevelValue.h" #include "LootStrategyValue.h" +#include "LootValues.h" #include "MaintenanceValues.h" #include "ManaSaveLevelValue.h" #include "NearestAdsValue.h" @@ -272,6 +273,7 @@ public: creators["can repair"] = &ValueContext::can_repair; creators["should sell"] = &ValueContext::should_sell; creators["can sell"] = &ValueContext::can_sell; + creators["can train"] = &ValueContext::can_train; creators["can fight equal"] = &ValueContext::can_fight_equal; creators["can fight elite"] = &ValueContext::can_fight_elite; creators["can fight boss"] = &ValueContext::can_fight_boss; @@ -519,6 +521,7 @@ private: static UntypedValue* can_repair(PlayerbotAI* botAI) { return new CanRepairValue(botAI); } static UntypedValue* should_sell(PlayerbotAI* botAI) { return new ShouldSellValue(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_elite(PlayerbotAI* botAI) { return new CanFightEliteValue(botAI); } static UntypedValue* can_fight_boss(PlayerbotAI* botAI) { return new CanFightBossValue(botAI); } diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index bd27bece..06645cd5 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -262,10 +262,10 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver) // that removes the distance check and keeps all other checks switch (questGiver->GetTypeId()) { - case TYPEID_UNIT: + case TYPEID_UNIT: // Player::GetNPCIfCanInteractWith { ObjectGuid guid = questGiver->GetGUID(); - uint32 npcflagmask = UNIT_NPC_FLAG_QUESTGIVER; + // unit checks if (!guid) return false; @@ -292,7 +292,7 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver) return false; // appropriate npc type - if (npcflagmask && !creature->HasNpcFlag(NPCFlags(npcflagmask))) + if (!creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER)) return false; // 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) 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; } - case TYPEID_GAMEOBJECT: + case TYPEID_GAMEOBJECT: // Player::GetGameObjectIfCanInteractWith { ObjectGuid guid = questGiver->GetGUID(); - GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER; + 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 if (go->GetGOInfo()->IconName == "Point") - { return false; - } return true; } } + return false; } // unused for now diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index b5113694..0a43be71 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -620,7 +620,7 @@ bool PlayerbotAIConfig::Initialize() syncQuestWithPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestWithPlayer", true); syncQuestForPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestForPlayer", false); dropObsoleteQuests = sConfigMgr->GetOption("AiPlayerbot.DropObsoleteQuests", true); - autoTrainSpells = sConfigMgr->GetOption("AiPlayerbot.AutoTrainSpells", "yes"); + allowLearnTrainerSpells = sConfigMgr->GetOption("AiPlayerbot.AllowLearnTrainerSpells", true); autoPickTalents = sConfigMgr->GetOption("AiPlayerbot.AutoPickTalents", true); autoUpgradeEquip = sConfigMgr->GetOption("AiPlayerbot.AutoUpgradeEquip", false); hunterWolfPet = sConfigMgr->GetOption("AiPlayerbot.HunterWolfPet", 0); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 27177565..e190aa47 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -352,7 +352,7 @@ public: bool syncQuestWithPlayer; bool syncQuestForPlayer; bool dropObsoleteQuests; - std::string autoTrainSpells; + bool allowLearnTrainerSpells; bool autoPickTalents; bool autoUpgradeEquip; int32 hunterWolfPet;