diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 81668a9559..910cc07a19 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -377,6 +377,9 @@ AiPlayerbot.AggroDistance = 22 AiPlayerbot.TooCloseDistance = 5.0 AiPlayerbot.MeleeDistance = 0.75 AiPlayerbot.FollowDistance = 1.5 +# Distance to maintain when using "relaxed follow" strategy +# Bots move to this distance from master when they get too far +AiPlayerbot.RelaxedFollowDistance = 1.0 AiPlayerbot.WhisperDistance = 6000.0 AiPlayerbot.ContactDistance = 0.45 AiPlayerbot.AoeRadius = 10 diff --git a/src/Ai/Base/Actions/FollowActions.cpp b/src/Ai/Base/Actions/FollowActions.cpp index 2593ea28e5..053ab2dbb6 100644 --- a/src/Ai/Base/Actions/FollowActions.cpp +++ b/src/Ai/Base/Actions/FollowActions.cpp @@ -23,7 +23,38 @@ bool FollowAction::Execute(Event event) bool moved = false; if (!target.empty()) { - moved = Follow(AI_VALUE(Unit*, target)); + // Relaxed follow: stay in radius, don't adjust angle/formation + if (botAI->HasStrategy("relaxed follow", BOT_STATE_NON_COMBAT)) + { + Unit* followTarget = AI_VALUE(Unit*, target); + if (!followTarget) + return false; + + // Only move if bot is too far away from target + float distance = sServerFacade->GetDistance2d(bot, followTarget); + float maxDistance = formation->GetMaxDistance(); + + if (distance > maxDistance) + { + // Calculate position at relaxedFollowDistance from master + // Move towards master but stop at configured distance + float keepDistance = sPlayerbotAIConfig->relaxedFollowDistance; + float angle = followTarget->GetAngle(bot); // Angle from master to bot + float targetX = followTarget->GetPositionX() + cos(angle) * keepDistance; + float targetY = followTarget->GetPositionY() + sin(angle) * keepDistance; + float targetZ = followTarget->GetPositionZ(); + + moved = MoveTo(followTarget->GetMapId(), + targetX, targetY, targetZ, + false, false, false, true, + botAI->GetState() == BOT_STATE_COMBAT ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL, + true); + } + } + else + { + moved = Follow(AI_VALUE(Unit*, target)); + } } else { diff --git a/src/Ai/Base/Strategy/RelaxedFollowStrategy.cpp b/src/Ai/Base/Strategy/RelaxedFollowStrategy.cpp new file mode 100644 index 0000000000..535aa335bf --- /dev/null +++ b/src/Ai/Base/Strategy/RelaxedFollowStrategy.cpp @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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 "RelaxedFollowStrategy.h" + +#include "Playerbots.h" diff --git a/src/Ai/Base/Strategy/RelaxedFollowStrategy.h b/src/Ai/Base/Strategy/RelaxedFollowStrategy.h new file mode 100644 index 0000000000..0257b16346 --- /dev/null +++ b/src/Ai/Base/Strategy/RelaxedFollowStrategy.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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_RELAXEDFOLLOWSTRATEGY_H +#define _PLAYERBOT_RELAXEDFOLLOWSTRATEGY_H + +#include "Strategy.h" + +class PlayerbotAI; + +class RelaxedFollowStrategy : public Strategy +{ +public: + RelaxedFollowStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "relaxed follow"; } +}; + +#endif diff --git a/src/Ai/Base/StrategyContext.h b/src/Ai/Base/StrategyContext.h index 0cc6855f34..3953263562 100644 --- a/src/Ai/Base/StrategyContext.h +++ b/src/Ai/Base/StrategyContext.h @@ -39,6 +39,7 @@ #include "RTSCStrategy.h" #include "RacialsStrategy.h" #include "RangedCombatStrategy.h" +#include "RelaxedFollowStrategy.h" #include "ReturnStrategy.h" #include "RpgStrategy.h" #include "RunawayStrategy.h" @@ -122,6 +123,7 @@ class StrategyContext : public NamedObjectContext creators["worldbuff"] = &StrategyContext::world_buff; creators["use bobber"] = &StrategyContext::bobber_strategy; creators["master fishing"] = &StrategyContext::master_fishing; + creators["relaxed follow"] = &StrategyContext::relaxed_follow; } private: @@ -192,6 +194,7 @@ class StrategyContext : public NamedObjectContext static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); } static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); } static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); } + static Strategy* relaxed_follow(PlayerbotAI* botAI) { return new RelaxedFollowStrategy(botAI); } }; class MovementStrategyContext : public NamedObjectContext diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index b9ef257d95..d8fbd52da8 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -98,6 +98,7 @@ bool PlayerbotAIConfig::Initialize() tooCloseDistance = sConfigMgr->GetOption("AiPlayerbot.TooCloseDistance", 5.0f); meleeDistance = sConfigMgr->GetOption("AiPlayerbot.MeleeDistance", 0.75f); followDistance = sConfigMgr->GetOption("AiPlayerbot.FollowDistance", 1.5f); + relaxedFollowDistance = sConfigMgr->GetOption("AiPlayerbot.RelaxedFollowDistance", 1.0f); whisperDistance = sConfigMgr->GetOption("AiPlayerbot.WhisperDistance", 6000.0f); contactDistance = sConfigMgr->GetOption("AiPlayerbot.ContactDistance", 0.45f); aoeRadius = sConfigMgr->GetOption("AiPlayerbot.AoeRadius", 10.0f); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index fb112fc907..2346ef1591 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -85,7 +85,7 @@ class PlayerbotAIConfig dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay; bool dynamicReactDelay; float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance, - tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance, + tooCloseDistance, meleeDistance, followDistance, relaxedFollowDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance, targetPosRecalcDistance, farDistance, healDistance, aggroDistance; uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth; uint32 lowMana, mediumMana, highMana;