diff --git a/playerbot/PlayerbotAI.cpp b/playerbot/PlayerbotAI.cpp index ba77fd9b0..7c127eda8 100644 --- a/playerbot/PlayerbotAI.cpp +++ b/playerbot/PlayerbotAI.cpp @@ -23,6 +23,7 @@ #include "Group.h" #include "Pet.h" #include "SpellAuras.h" +#include "SpellMgr.h" #include "../ahbot/AhBot.h" #include "GuildTaskMgr.h" #include "PlayerbotDbStore.h" @@ -3157,25 +3158,9 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget, bool target = bot; Pet* pet = bot->GetPet(); - SpellEntry const *pSpellInfo = sServerFacade.LookupSpellInfo(spellId); - if (pet && pet->HasSpell(spellId)) - { - bool autocast = false; - for(AutoSpellList::iterator i = pet->m_autospells.begin(); i != pet->m_autospells.end(); ++i) - { - if (*i == spellId) - { - autocast = true; - break; - } - } - - pet->ToggleAutocast(spellId, !autocast); - ostringstream out; - out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for "; - out << chatHelper.formatSpell(pSpellInfo); - TellPlayer(GetMaster(), out); - return true; + if (pet && pet->HasSpell(spellId)) + { + return CastPetSpell(spellId, target); } aiObjectContext->GetValue("last movement")->Get().Set(NULL); @@ -3221,6 +3206,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget, bool return false; } + const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId); Spell *spell = new Spell(bot, pSpellInfo, false); SpellCastTargets targets; @@ -3374,25 +3360,9 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite return false; Pet* pet = bot->GetPet(); - SpellEntry const* pSpellInfo = sServerFacade.LookupSpellInfo(spellId); if (pet && pet->HasSpell(spellId)) { - bool autocast = false; - for (AutoSpellList::iterator i = pet->m_autospells.begin(); i != pet->m_autospells.end(); ++i) - { - if (*i == spellId) - { - autocast = true; - break; - } - } - - pet->ToggleAutocast(spellId, !autocast); - ostringstream out; - out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for "; - out << chatHelper.formatSpell(pSpellInfo); - TellPlayer(GetMaster(), out); - return true; + return CastPetSpell(spellId, nullptr); } aiObjectContext->GetValue("last movement")->Get().Set(NULL); @@ -3440,6 +3410,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite return false; } + const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId); Spell* spell = new Spell(bot, pSpellInfo, false); SpellCastTargets targets; @@ -3564,6 +3535,44 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite return true; } +bool PlayerbotAI::CastPetSpell(uint32 spellId, Unit* target) +{ + Pet* pet = bot->GetPet(); + if (pet && spellId && pet->HasSpell(spellId)) + { + auto IsAutocastActive = [&pet, &spellId]() -> bool + { + for (AutoSpellList::iterator i = pet->m_autospells.begin(); i != pet->m_autospells.end(); ++i) + { + if (*i == spellId) + { + return true; + } + } + + return false; + }; + + // Send pet spell action packet + uint8 flag = ACT_PASSIVE; + if (IsAutocastable(spellId)) + { + flag = IsAutocastActive() ? ACT_ENABLED : ACT_DISABLED; + } + + uint32 command = (flag << 24) | spellId; + + WorldPacket data(CMSG_PET_ACTION); + data << pet->GetObjectGuid(); + data << command; + data << (target ? target->GetObjectGuid() : ObjectGuid()); + bot->GetSession()->HandlePetAction(data); + return true; + } + + return false; +} + bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target) { #ifdef MANGOSBOT_TWO diff --git a/playerbot/PlayerbotAI.h b/playerbot/PlayerbotAI.h index 30819eb04..c90ffa156 100644 --- a/playerbot/PlayerbotAI.h +++ b/playerbot/PlayerbotAI.h @@ -398,6 +398,7 @@ class PlayerbotAI : public PlayerbotAIBase virtual bool CastSpell(string name, Unit* target, Item* itemTarget = NULL, bool waitForSpell = true, uint32* outSpellDuration = NULL, bool canUseReagentCheat = true); bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = NULL, bool waitForSpell = true, uint32* outSpellDuration = NULL, bool canUseReagentCheat = true); bool CastSpell(uint32 spellId, float x, float y, float z, Item* itemTarget = NULL, bool waitForSpell = true, uint32* outSpellDuration = NULL, bool canUseReagentCheat = true); + bool CastPetSpell(uint32 spellId, Unit* target); bool CastVehicleSpell(uint32 spellId, Unit* target); bool CastVehicleSpell(uint32 spellId, float x, float y, float z); diff --git a/playerbot/PlayerbotFactory.cpp b/playerbot/PlayerbotFactory.cpp index 3719a257d..5d167e31c 100644 --- a/playerbot/PlayerbotFactory.cpp +++ b/playerbot/PlayerbotFactory.cpp @@ -543,12 +543,13 @@ void PlayerbotFactory::AddConsumables() void PlayerbotFactory::InitPet() { + // Randomize a new pet (only for hunters) + if (bot->getClass() != CLASS_HUNTER) + return; + Pet* pet = bot->GetPet(); if (!pet) { - if (bot->getClass() != CLASS_HUNTER) - return; - Map* map = bot->GetMap(); if (!map) return; @@ -606,43 +607,23 @@ void PlayerbotFactory::InitPet() pet->setFaction(bot->GetFaction()); pet->SetLevel(bot->GetLevel()); pet->InitStatsForLevel(bot->GetLevel()); -#ifdef MANGOSBOT_TWO - -#else +#ifndef MANGOSBOT_TWO pet->SetLoyaltyLevel(BEST_FRIEND); #endif pet->SetPower(POWER_HAPPINESS, HAPPINESS_LEVEL_SIZE * 2); pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true); -#ifdef CMANGOS pet->GetMap()->Add((Creature*)pet); pet->AIM_Initialize(); -#endif -#ifdef MANGOS - pet->GetMap()->Add((Creature*)pet); - pet->AIM_Initialize(); -#endif pet->InitPetCreateSpells(); -#ifdef CMANGOS - pet->LearnPetPassives(); - pet->CastPetAuras(true); - pet->CastOwnerTalentAuras(); - pet->UpdateAllStats(); -#endif -#ifdef MANGOS pet->LearnPetPassives(); pet->CastPetAuras(true); pet->CastOwnerTalentAuras(); pet->UpdateAllStats(); -#endif bot->SetPet(pet); bot->SetPetGuid(pet->GetObjectGuid()); sLog.outDebug( "Bot %s: assign pet %d (%d level)", bot->GetName(), co->Entry, bot->GetLevel()); - pet->SavePetToDB(PET_SAVE_AS_CURRENT -#ifdef CMANGOS - , bot -#endif - ); + pet->SavePetToDB(PET_SAVE_AS_CURRENT, bot); bot->PetSpellInitialize(); break; } @@ -653,9 +634,7 @@ void PlayerbotFactory::InitPet() { pet->InitStatsForLevel(bot->GetLevel()); pet->SetLevel(bot->GetLevel()); -#ifdef MANGOSBOT_TWO - -#else +#ifndef MANGOSBOT_TWO pet->SetLoyaltyLevel(BEST_FRIEND); #endif pet->SetPower(POWER_HAPPINESS, HAPPINESS_LEVEL_SIZE * 2); @@ -695,7 +674,7 @@ void PlayerbotFactory::InitPetSpells() // TO DO // ... } -// Warlock pets should auto learn spells on WOTLK +// Warlock pets should auto learn spells in WOTLK #ifndef MANGOSBOT_TWO else if (bot->getClass() == CLASS_WARLOCK) { @@ -769,7 +748,7 @@ void PlayerbotFactory::InitPetSpells() spellList[PET_FELHUNTER].push_back(std::pair(64, 27280)); } - // Voidwalker + // Voidwalker spells { // Consume Shadows spellList[PET_VOIDWALKER].push_back(std::pair(18, 17767)); @@ -814,7 +793,7 @@ void PlayerbotFactory::InitPetSpells() spellList[PET_VOIDWALKER].push_back(std::pair(80, 47984)); } - // Succubus + // Succubus spells { // Lash of Pain spellList[PET_SUCCUBUS].push_back(std::pair(20, 7814)); @@ -841,7 +820,7 @@ void PlayerbotFactory::InitPetSpells() spellList[PET_SUCCUBUS].push_back(std::pair(70, 27275)); } - // Felguard + // Felguard spells { // Anguish spellList[PET_FELGUARD].push_back(std::pair(50, 33698)); diff --git a/playerbot/strategy/actions/ChatActionContext.h b/playerbot/strategy/actions/ChatActionContext.h index 53ba0f4fb..9fa1ca9bc 100644 --- a/playerbot/strategy/actions/ChatActionContext.h +++ b/playerbot/strategy/actions/ChatActionContext.h @@ -181,6 +181,7 @@ namespace ai creators["focus heal target"] = &ChatActionContext::focus_heal_target; creators["follow target"] = &ChatActionContext::follow_target; creators["self resurrect"] = &ChatActionContext::self_resurrect; + creators["pet"] = &ChatActionContext::pet; creators["guild invite"] = &ChatActionContext::guild_invite; creators["guild join"] = &ChatActionContext::guild_join; @@ -295,6 +296,7 @@ namespace ai static Action* focus_heal_target(PlayerbotAI* ai) { return new SetFocusHealTargetAction(ai); } static Action* follow_target(PlayerbotAI* ai) { return new SetFollowTargetAction(ai); } static Action* self_resurrect(PlayerbotAI* ai) { return new SelfResurrectAction(ai); } + static Action* pet(PlayerbotAI* ai) { return new SetPetAction(ai); } static Action* guild_invite(PlayerbotAI* ai) { return new GuildInviteAction(ai); } static Action* guild_join(PlayerbotAI* ai) { return new GuildJoinAction(ai); } diff --git a/playerbot/strategy/actions/GenericActions.cpp b/playerbot/strategy/actions/GenericActions.cpp index 52da70949..7755820b7 100644 --- a/playerbot/strategy/actions/GenericActions.cpp +++ b/playerbot/strategy/actions/GenericActions.cpp @@ -136,6 +136,8 @@ bool InitializePetAction::isUseful() return !hasTamedPet; } +// Warlock pets should auto learn spells in WOTLK +#ifndef MANGOSBOT_TWO else if (bot->getClass() == CLASS_WARLOCK) { // Only initialize if warlock has the pet summoned @@ -212,7 +214,7 @@ bool InitializePetAction::isUseful() spellList[PET_FELHUNTER].push_back(std::pair(64, 27280)); } - // Voidwalker + // Voidwalker spells { // Consume Shadows spellList[PET_VOIDWALKER].push_back(std::pair(18, 17767)); @@ -257,7 +259,7 @@ bool InitializePetAction::isUseful() spellList[PET_VOIDWALKER].push_back(std::pair(80, 47984)); } - // Succubus + // Succubus spells { // Lash of Pain spellList[PET_SUCCUBUS].push_back(std::pair(20, 7814)); @@ -284,7 +286,7 @@ bool InitializePetAction::isUseful() spellList[PET_SUCCUBUS].push_back(std::pair(70, 27275)); } - // Felguard + // Felguard spells { // Anguish spellList[PET_FELGUARD].push_back(std::pair(50, 33698)); @@ -330,7 +332,84 @@ bool InitializePetAction::isUseful() } } } +#endif } return false; } + +bool SetPetAction::Execute(Event& event) +{ + Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); + std::string command = event.getParam(); + + // Extract the command and the parameters + std::string parameter; + size_t spacePos = command.find_first_of(" "); + if (spacePos != std::string::npos) + { + parameter = command.substr(spacePos + 1); + command = command.substr(0, spacePos); + } + + Pet* pet = bot->GetPet(); + if (pet) + { + if (command == "autocast") + { + const std::string& spellName = parameter; + if (!spellName.empty()) + { + const uint32 spellId = AI_VALUE2(uint32, "spell id", spellName); + if (pet->HasSpell(spellId) && IsAutocastable(spellId)) + { + auto IsAutocastActive = [&pet, &spellId]() -> bool + { + for (AutoSpellList::iterator i = pet->m_autospells.begin(); i != pet->m_autospells.end(); ++i) + { + if (*i == spellId) + { + return true; + } + } + + return false; + }; + + const bool autocastActive = IsAutocastActive(); + pet->ToggleAutocast(spellId, !autocastActive); + + ostringstream out; + out << (autocastActive ? "Disabling" : "Enabling") << " pet autocast for "; + out << ChatHelper::formatSpell(sServerFacade.LookupSpellInfo(spellId)); + ai->TellPlayer(GetMaster(), out); + + return true; + } + else + { + ai->TellPlayer(requester, "I can't set to autocast that spell."); + } + } + else + { + ai->TellPlayer(requester, "Please specify a pet spell to set the autocast."); + } + } + else + { + ai->TellPlayer(requester, "Please specify a pet command (Like autocast)."); + } + } + else + { + ai->TellPlayer(requester, "I don't have any pets"); + } + + return false; +} + +bool SetPetAction::isUseful() +{ + return bot->GetPet(); +} diff --git a/playerbot/strategy/actions/GenericActions.h b/playerbot/strategy/actions/GenericActions.h index 57d960b30..167967cd5 100644 --- a/playerbot/strategy/actions/GenericActions.h +++ b/playerbot/strategy/actions/GenericActions.h @@ -143,4 +143,12 @@ namespace ai bool Execute(Event& event) override; bool isUseful() override; }; + + class SetPetAction : public Action + { + public: + SetPetAction(PlayerbotAI* ai) : Action(ai, "pet") {} + bool Execute(Event& event) override; + bool isUseful() override; + }; } diff --git a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp index c300cc929..1375150f8 100644 --- a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -220,6 +220,10 @@ void ChatCommandHandlerStrategy::InitReactionTriggers(std::list &t "wait for attack time", NextAction::array(0, new NextAction("wait for attack time", relevance), NULL))); + triggers.push_back(new TriggerNode( + "pet", + NextAction::array(0, new NextAction("pet", relevance), NULL))); + triggers.push_back(new TriggerNode( "focus heal", NextAction::array(0, new NextAction("focus heal target", relevance), NULL))); diff --git a/playerbot/strategy/triggers/ChatTriggerContext.h b/playerbot/strategy/triggers/ChatTriggerContext.h index 4a745089b..d0eeafe7b 100644 --- a/playerbot/strategy/triggers/ChatTriggerContext.h +++ b/playerbot/strategy/triggers/ChatTriggerContext.h @@ -74,6 +74,7 @@ namespace ai creators["guard"] = &ChatTriggerContext::guard; creators["free"] = &ChatTriggerContext::free; creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time; + creators["pet"] = &ChatTriggerContext::pet; creators["focus heal"] = &ChatTriggerContext::focus_heal_target; creators["follow target"] = &ChatTriggerContext::follow_target; creators["self res"] = &ChatTriggerContext::self_resurrect; @@ -170,6 +171,7 @@ namespace ai static Trigger* guard(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "guard"); } static Trigger* free(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "free"); } static Trigger* wait_for_attack_time(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "wait for attack time"); } + static Trigger* pet(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "pet"); } static Trigger* focus_heal_target(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "focus heal"); } static Trigger* follow_target(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "follow target"); } static Trigger* self_resurrect(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "self res"); }