Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions conf/playerbots.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +380 to +381
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configuration comment could be more descriptive to help users understand the relationship between RelaxedFollowDistance and FollowDistance. Consider clarifying that RelaxedFollowDistance should be less than FollowDistance, and explaining that this is the target distance bots will move to when they exceed the maximum formation distance. For example: "Distance to maintain when using 'relaxed follow' strategy. Bots move to this distance from master when they exceed the formation's max distance. Should be less than AiPlayerbot.FollowDistance to avoid constant re-positioning."

Suggested change
# Distance to maintain when using "relaxed follow" strategy
# Bots move to this distance from master when they get too far
# Distance to maintain when using "relaxed follow" strategy.
# Bots move to this distance from master when they exceed the formation's max distance.
# Should be less than AiPlayerbot.FollowDistance to avoid constant re-positioning.

Copilot uses AI. Check for mistakes.
AiPlayerbot.RelaxedFollowDistance = 1.0
AiPlayerbot.WhisperDistance = 6000.0
AiPlayerbot.ContactDistance = 0.45
AiPlayerbot.AoeRadius = 10
Expand Down
33 changes: 32 additions & 1 deletion src/Ai/Base/Actions/FollowActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +38 to +41
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relaxed follow logic could enter an infinite loop if relaxedFollowDistance is greater than or equal to maxDistance. When the bot is too far (distance > maxDistance), it moves to keepDistance from the master. If keepDistance >= maxDistance, the bot will still be outside the acceptable range after moving, causing it to immediately trigger movement again. Consider adding validation either in the configuration loading (PlayerbotAIConfig::Initialize) or here to ensure relaxedFollowDistance is always less than the formation's max distance.

Suggested change
{
// Calculate position at relaxedFollowDistance from master
// Move towards master but stop at configured distance
float keepDistance = sPlayerbotAIConfig->relaxedFollowDistance;
{
// Validate formation max distance before using it
if (maxDistance <= 0.0f)
return false;
// Calculate position at relaxedFollowDistance from master
// Move towards master but stop at configured distance
float keepDistance = sPlayerbotAIConfig->relaxedFollowDistance;
// Ensure relaxed follow distance does not exceed formation max distance
if (keepDistance >= maxDistance)
keepDistance = maxDistance * 0.9f;

Copilot uses AI. Check for mistakes.
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
{
Expand Down
8 changes: 8 additions & 0 deletions src/Ai/Base/Strategy/RelaxedFollowStrategy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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"
21 changes: 21 additions & 0 deletions src/Ai/Base/Strategy/RelaxedFollowStrategy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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
3 changes: 3 additions & 0 deletions src/Ai/Base/StrategyContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -122,6 +123,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
creators["worldbuff"] = &StrategyContext::world_buff;
creators["use bobber"] = &StrategyContext::bobber_strategy;
creators["master fishing"] = &StrategyContext::master_fishing;
creators["relaxed follow"] = &StrategyContext::relaxed_follow;
}

private:
Expand Down Expand Up @@ -192,6 +194,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
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<Strategy>
Expand Down
1 change: 1 addition & 0 deletions src/PlayerbotAIConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ bool PlayerbotAIConfig::Initialize()
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.0f);
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 0.75f);
followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
relaxedFollowDistance = sConfigMgr->GetOption<float>("AiPlayerbot.RelaxedFollowDistance", 1.0f);
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.45f);
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 10.0f);
Expand Down
2 changes: 1 addition & 1 deletion src/PlayerbotAIConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading