Files
mod-playerbots/src/strategy/shaman/GenericShamanStrategy.cpp
ThePenguinMan96 aa6f8153a1 Tame Chat Action / Pet Chat Action (stances/commands)
Hello everyone,

I am on a quest to make bot's pets completely functional, focuses on solving issues #1351 , #1230 , and #1137 . This PR achieves the following:

1. Changes the current "pet" chat command to "tame", which is more intuitive that only hunters can use it. The modes are "tame name (name)", "tame id (id)", "tame family (family)", "tame rename (new name)", and "tame abandon". Tame abandon is new - it simply abandons the current pet. Also, now, if you type in "tame family" by itself, it will list the available families. See pictures below for examples.
2. Added new "pet" chat command, with the following modes: "pet passive", "pet aggressive", "pet defensive", "pet stance" (shows current pet stance), "pet attack", "pet follow", and "pet stay". Previously, the pet's stance was not changeable, and there were some less than desired effects from summonable pets - see the issues above.
3. New config option: AiPlayerbot.DefaultPetStance, which changes the stance that all bot's pets are summoned as. This makes sure when feral spirits or treants are summoned by shamans and druids, they are immediately set to this configured stance. Set as 1 as default, which is defensive. (0 = Passive, 1 = Defensive, 2 = Aggressive)
4. New config option: AiPlayerbot.PetChatCommandDebug, which enables debug messages for the "pet" chat command. By default it is set to 0, which is disabled, but if you would like to see when pet's stances are changed, or when you tell the pet to attack/follow/stay, and when pet spells are auto-toggled, this is an option. I made this for myself mainly to test the command - if anyone notices any wierd pet behavior, this will be an option to help them report it as well.
5. Modified FollowActions to not constantly execute the petfollow action, overriding any stay or attack commands issued.
6. Modified GenericActions to have TogglePetSpellAutoCastAction optionally log when spells are toggled based on AiPlayerbot.PetChatCommandDebug.
7. Modified PetAttackAction to not attack if the pet is set to passive, and not override the pet's stance to passive every time it was executed.
8. Modified CombatStrategy.cpp to not constantly issue the petattack command, respecting the "pet stay" and "pet follow" commands. Pets will still automatically attack the enemy if set to aggressive or defensive.
9. Warlocks, Priests, Hunters, Shamans, Mages, Druids, and DKs (all classes that have summons): Added a "new pet" trigger that executes the "set pet stance" action. The "new pet" trigger happens only once, upon summoning a pet. It sets the pet's stance from AiPlayerbot.DefaultPetStance's value.
2025-08-01 01:18:16 -07:00

193 lines
9.2 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "GenericShamanStrategy.h"
#include "HealShamanStrategy.h"
#include "Playerbots.h"
#include "Strategy.h"
class GenericShamanStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericShamanStrategyActionNodeFactory()
{
creators["flametongue weapon"] = &flametongue_weapon;
creators["frostbrand weapon"] = &frostbrand_weapon;
creators["windfury weapon"] = &windfury_weapon;
creators["lesser healing wave"] = &lesser_healing_wave;
creators["lesser healing wave on party"] = &lesser_healing_wave_on_party;
creators["chain heal on party"] = &chain_heal;
creators["riptide"] = &riptide;
creators["riptide on party"] = &riptide_on_party;
creators["earth shock"] = &earth_shock;
creators["water shield"] = &water_shield;
}
private:
static ActionNode* earth_shock([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("earth shock",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("flame shock"), nullptr),
/*C*/ nullptr);
}
static ActionNode* flametongue_weapon([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("flametongue weapon",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("rockbiter weapon"), nullptr),
/*C*/ nullptr);
}
static ActionNode* frostbrand_weapon([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("frostbrand weapon",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("flametongue weapon"), nullptr),
/*C*/ nullptr);
}
static ActionNode* windfury_weapon([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("windfury weapon",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("flametongue weapon"), nullptr),
/*C*/ nullptr);
}
static ActionNode* lesser_healing_wave([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("lesser healing wave",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing wave"), nullptr),
/*C*/ nullptr);
}
static ActionNode* lesser_healing_wave_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("lesser healing wave on party",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing wave on party"), nullptr),
/*C*/ nullptr);
}
static ActionNode* chain_heal([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("chain heal on party",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("lesser healing wave on party"), nullptr),
/*C*/ nullptr);
}
static ActionNode* riptide([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("riptide",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing wave"), nullptr),
/*C*/ nullptr);
}
static ActionNode* riptide_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("riptide on party",
/*P*/ nullptr,
// /*A*/ NextAction::array(0, new NextAction("healing wave on party"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* water_shield([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("water shield",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("lightning shield"), nullptr),
/*C*/ nullptr);
}
};
GenericShamanStrategy::GenericShamanStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericShamanStrategyActionNodeFactory());
}
void GenericShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("wind shear", NextAction::array(0, new NextAction("wind shear", 23.0f), nullptr)));
triggers.push_back(
new TriggerNode("wind shear on enemy healer",
NextAction::array(0, new NextAction("wind shear on enemy healer", 23.0f), nullptr)));
triggers.push_back(new TriggerNode("purge", NextAction::array(0, new NextAction("purge", ACTION_DISPEL), nullptr)));
// triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0, new NextAction("lesser
// healing wave on party", 25.0f), nullptr))); triggers.push_back(new TriggerNode("party member low health",
// NextAction::array(0, new NextAction("riptide on party", 25.0f), nullptr))); triggers.push_back(new
// TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("chain heal on party", 27.0f), nullptr)));
// triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("lesser healing
// wave", 26.0f), nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new
// NextAction("riptide", 26.0f), nullptr)));
triggers.push_back(new TriggerNode("heroism", NextAction::array(0, new NextAction("heroism", 31.0f), nullptr)));
triggers.push_back(new TriggerNode("bloodlust", NextAction::array(0, new NextAction("bloodlust", 30.0f), nullptr)));
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 65.0f), nullptr)));
}
void ShamanBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("lightning shield", NextAction::array(0, new NextAction("lightning shield", 22.0f), nullptr)));
}
void ShamanBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("water shield", NextAction::array(0, new NextAction("water shield", 22.0f), nullptr)));
}
void ShamanCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("cure poison", NextAction::array(0, new NextAction("cure poison", 21.0f), nullptr)));
triggers.push_back(new TriggerNode("party member cure poison",
NextAction::array(0, new NextAction("cure poison on party", 21.0f), nullptr)));
triggers.push_back(new TriggerNode("cleanse spirit poison",
NextAction::array(0, new NextAction("cleanse spirit", 24.0f), nullptr)));
triggers.push_back(
new TriggerNode("party member cleanse spirit poison",
NextAction::array(0, new NextAction("cleanse spirit poison on party", 23.0f), nullptr)));
triggers.push_back(
new TriggerNode("cure disease", NextAction::array(0, new NextAction("cure disease", 31.0f), nullptr)));
triggers.push_back(new TriggerNode("party member cure disease",
NextAction::array(0, new NextAction("cure disease on party", 30.0f), nullptr)));
triggers.push_back(new TriggerNode("cleanse spirit disease",
NextAction::array(0, new NextAction("cleanse spirit", 24.0f), nullptr)));
triggers.push_back(
new TriggerNode("party member cleanse spirit disease",
NextAction::array(0, new NextAction("cleanse spirit disease on party", 23.0f), nullptr)));
triggers.push_back(new TriggerNode("cleanse spirit curse",
NextAction::array(0, new NextAction("cleanse spirit", 24.0f), nullptr)));
triggers.push_back(
new TriggerNode("party member cleanse spirit curse",
NextAction::array(0, new NextAction("cleanse spirit curse on party", 23.0f), nullptr)));
}
void ShamanHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("healer should attack",
NextAction::array(0,
new NextAction("flame shock", ACTION_DEFAULT + 0.2f),
new NextAction("lava burst", ACTION_DEFAULT + 0.1f),
new NextAction("lightning bolt", ACTION_DEFAULT), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe and healer should attack",
NextAction::array(0,
new NextAction("chain lightning", ACTION_DEFAULT + 0.3f), nullptr)));
}