From 66d41e1d79868500200b067e8cd31c41c33bf2be Mon Sep 17 00:00:00 2001 From: NoxMax <50133316+NoxMax@users.noreply.github.com> Date: Fri, 8 May 2026 23:42:18 -0600 Subject: [PATCH] Feat: Selective reset to default of combat or non-combat strategies (#2365) ## Pull Request Description Adds the commands `co !` and `nc !`, which would reset either the combat or non-combat strategies of a follower bot, without affecting the other strategies or any other values. Also ChangeStrategyAction.cpp was refactored for duplicate code by introducing the helper function `HandleStrategyCommon`, that gets called by `ChangeCombatStrategyAction` and `ChangeNonCombatStrategyAction` ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. `reset botAI` already resets strategies back to default, but it resets ALL strategies and wipes values such as formations, stances, and everything else under the `value` key in playerbots_db_store>value. The new commands don't run across many bots, only on the bot the command is run on. ## How to Test the Changes 1. Run either `co ?` and `nc ?` to see current list of combat and non-combat strategies the bot has. 2. Add and remove strategies to both `co` and `nc`. 3. Confirm your changes with `co ?` and `nc ?`. 4. Run `co !` only. 5. Run `co ?` to confirm combat strategies have been reset to default, and `nc ?` to confirm it has not been affected. Then run `nc !` to reset it as well. 6. Do another test [inside an instance](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands#raid-specific-strategies). Remove a bunch of `nc` strategies, including the strategy for the raid itself. 7. Run `nc !` and check that the defaults have been reset, but that the instance strategy has been re-added as well. ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [x] No, not at all - - [ ] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - - [ ] No - - [x] Yes (**explain below**) Technically it adds a new case to ChangeCombatStrategyAction, but it's straightforward. ## AI Assistance Was AI assistance used while working on this change? - - [ ] No - - [x] Yes (**explain below**) Review only in case I was missing something, and then to easily refactor duplicate code with HandleStrategyCommon. ## Final Checklist - - [x] Stability is not compromised. - - [x] Performance impact is understood, tested, and acceptable. - - [x] Added logic complexity is justified and explained. - - [x] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers [Commands wiki](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands#strategies) need to be modified to read: --- You can query the bot to report what strategies are currently being used: ``` co ? nc ? ``` You can reset either of the bot's strategies back to defaults: ``` co ! nc ! ``` --- Tangentially I also recommend [this section](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands#non-combat-strategies) to be edit to this for more accuracy: --- General strategy | description :---|:--- ``food`` | enable bot's ability to eat/drink ``pvp`` | enable bot's ability to engage in PVP combat. Note: PVP mode wouldn't appear active until the bot starts combat ``loot`` | enable bot's ability to loot. Note: adding or removing that strategy for randombots requires GM level --- src/Ai/Base/Actions/ChangeStrategyAction.cpp | 60 +++++++++----------- src/Bot/PlayerbotAI.cpp | 21 +++++++ src/Bot/PlayerbotAI.h | 1 + 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/Ai/Base/Actions/ChangeStrategyAction.cpp b/src/Ai/Base/Actions/ChangeStrategyAction.cpp index b4fe5faaf..e81096b2a 100644 --- a/src/Ai/Base/Actions/ChangeStrategyAction.cpp +++ b/src/Ai/Base/Actions/ChangeStrategyAction.cpp @@ -9,28 +9,36 @@ #include "PlayerbotRepository.h" #include "Playerbots.h" +// Helper function for prefixes used by combat and non-combat strategy commands. +static void HandleStrategyCommon(PlayerbotAI* botAI, std::string const& text, BotState state) +{ + std::vector splitted = split(text, ','); + for (std::vector::iterator i = splitted.begin(); i != splitted.end(); i++) + { + const char* name = i->c_str(); + switch (name[0]) + { + case '+': + case '-': + case '~': + PlayerbotRepository::instance().Save(botAI); + break; + case '!': + botAI->SelectiveResetStrategies(state); + PlayerbotRepository::instance().Save(botAI); + break; + case '?': + break; + } + } +} + bool ChangeCombatStrategyAction::Execute(Event event) { std::string const text = event.getParam(); botAI->ChangeStrategy(text.empty() ? getName() : text, BOT_STATE_COMBAT); if (event.GetSource() == "co") - { - std::vector splitted = split(text, ','); - for (std::vector::iterator i = splitted.begin(); i != splitted.end(); i++) - { - const char* name = i->c_str(); - switch (name[0]) - { - case '+': - case '-': - case '~': - PlayerbotRepository::instance().Save(botAI); - break; - case '?': - break; - } - } - } + HandleStrategyCommon(botAI, text, BOT_STATE_COMBAT); return true; } @@ -52,23 +60,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) botAI->ChangeStrategy(text, BOT_STATE_NON_COMBAT); if (event.GetSource() == "nc") - { - std::vector splitted = split(text, ','); - for (std::vector::iterator i = splitted.begin(); i != splitted.end(); i++) - { - const char* name = i->c_str(); - switch (name[0]) - { - case '+': - case '-': - case '~': - PlayerbotRepository::instance().Save(botAI); - break; - case '?': - break; - } - } - } + HandleStrategyCommon(botAI, text, BOT_STATE_NON_COMBAT); return true; } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 8b20b2e88..5937f0ef6 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -1585,6 +1585,27 @@ void PlayerbotAI::ClearStrategies(BotState type) e->removeAllStrategies(); } +// Resets only the combat or non-combat engine: wipe strategies, repopulate with class/spec defaults, +// re-apply current map's instance strategy (if any), and call Init() to rebuild trigger/action lists. +void PlayerbotAI::SelectiveResetStrategies(BotState type) +{ + Engine* e = engines[type]; + if (!e) + return; + + e->removeAllStrategies(); + + if (type == BOT_STATE_COMBAT) + AiFactory::AddDefaultCombatStrategies(bot, this, e); + else if (type == BOT_STATE_NON_COMBAT) + AiFactory::AddDefaultNonCombatStrategies(bot, this, e); + + if (sPlayerbotAIConfig.applyInstanceStrategies) + ApplyInstanceStrategies(bot->GetMapId()); + + e->Init(); +} + std::vector PlayerbotAI::GetStrategies(BotState type) { Engine* e = engines[type]; diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index dc7770ed8..01924c46f 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -404,6 +404,7 @@ public: std::string const qualifier = ""); void ChangeStrategy(std::string const name, BotState type); void ClearStrategies(BotState type); + void SelectiveResetStrategies(BotState type); std::vector GetStrategies(BotState type); Strategy* GetStrategy(std::string const name, BotState type); void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);