Skip to content

Commit 37120ec

Browse files
authored
More Cautious Bots (#270)
1 parent c4bb03f commit 37120ec

10 files changed

Lines changed: 134 additions & 9 deletions

File tree

src/modules/Bots/playerbot/AiFactory.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
122122

123123
engine->addStrategies("attack weak", "racials", "chat", "default", "aoe", "potions", "cast time", "conserve mana", "duel", "pvp", NULL);
124124

125+
if (sPlayerbotAIConfig.cautiousDefault)
126+
{
127+
engine->addStrategy("cautious");
128+
}
129+
125130
switch (player->getClass())
126131
{
127132
case CLASS_PRIEST:
@@ -253,6 +258,11 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
253258
{
254259
int tab = GetPlayerSpecTab(player);
255260

261+
if (sPlayerbotAIConfig.cautiousDefault)
262+
{
263+
nonCombatEngine->addStrategy("cautious");
264+
}
265+
256266
switch (player->getClass()){
257267
case CLASS_PALADIN:
258268
case CLASS_HUNTER:
@@ -277,7 +287,6 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
277287
{
278288
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig.randomBotNonCombatStrategies);
279289
}
280-
281290
}
282291

283292
Engine* AiFactory::createNonCombatEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext) {

src/modules/Bots/playerbot/PlayerbotAI.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class PlayerbotAI : public PlayerbotAIBase
137137
void ChangeStrategy(string name, BotState type);
138138
bool ContainsStrategy(StrategyType type);
139139
bool HasStrategy(string name, BotState type);
140+
bool HasStrategy(string name) { return HasStrategy(name, currentState); }
140141
void ResetStrategies();
141142
void ReInitCurrentEngine();
142143
void Reset();

src/modules/Bots/playerbot/PlayerbotAIConfig.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ PlayerbotAIConfig::PlayerbotAIConfig()
6767
logInGroupOnly(false),
6868
logValuesPerTick(false),
6969
fleeingEnabled(false),
70+
cautiousDefault(false),
7071
randomBotMinLevel(0),
7172
randomBotMaxLevel(0),
7273
randomChangeMultiplier(0.0f),
@@ -183,6 +184,7 @@ bool PlayerbotAIConfig::Initialize()
183184
logInGroupOnly = config.GetBoolDefault("AiPlayerbot.LogInGroupOnly", true);
184185
logValuesPerTick = config.GetBoolDefault("AiPlayerbot.LogValuesPerTick", false);
185186
fleeingEnabled = config.GetBoolDefault("AiPlayerbot.FleeingEnabled", true);
187+
cautiousDefault = config.GetBoolDefault("AiPlayerbot.Cautious", false);
186188
randomBotMinLevel = config.GetIntDefault("AiPlayerbot.RandomBotMinLevel", 1);
187189
randomBotMaxLevel = config.GetIntDefault("AiPlayerbot.RandomBotMaxLevel", 255);
188190
randomBotLoginAtStartup = config.GetBoolDefault("AiPlayerbot.RandomBotLoginAtStartup", true);

src/modules/Bots/playerbot/PlayerbotAIConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class PlayerbotAIConfig
7171
uint32 randomBotTeleLevel; ///< The teleport level for random bots.
7272
bool logInGroupOnly, logValuesPerTick;
7373
bool fleeingEnabled; ///< Indicates if fleeing is enabled for bots.
74+
bool cautiousDefault;
7475
std::string randomBotCombatStrategies, randomBotNonCombatStrategies;
7576
uint32 randomBotMinLevel, randomBotMaxLevel;
7677
float randomChangeMultiplier;

src/modules/Bots/playerbot/aiplayerbot.conf.dist.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ AiPlayerbot.CommandPrefix = ~
8888
# Bot can flee for enemy
8989
#AiPlayerbot.FleeingEnabled = 1
9090

91+
# Bot avoids pulling aggro during movement by default
92+
#AiPlayerbot.Cautious = 0
93+
9194
# Health/Mana levels
9295
#AiPlayerbot.CriticalHealth = 25
9396
#AiPlayerbot.LowHealth = 45

src/modules/Bots/playerbot/strategy/StrategyContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "generic/TellTargetStrategy.h"
3737
#include "generic/AttackEnemyPlayersStrategy.h"
3838
#include "generic/MoveRandomStrategy.h"
39+
#include "generic/CautiousStrategy.h"
3940

4041
namespace ai
4142
{
@@ -64,6 +65,7 @@ namespace ai
6465
creators["tell target"] = &StrategyContext::tell_target;
6566
creators["pvp"] = &StrategyContext::pvp;
6667
creators["move random"] = &StrategyContext::move_random;
68+
creators["cautious"] = &StrategyContext::cautious;
6769
}
6870

6971
private:
@@ -87,6 +89,7 @@ namespace ai
8789
static Strategy* ready_check(PlayerbotAI* ai) { return new ReadyCheckStrategy(ai); }
8890
static Strategy* pvp(PlayerbotAI* ai) { return new AttackEnemyPlayersStrategy(ai); }
8991
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
92+
static Strategy* cautious(PlayerbotAI* ai) { return new CautiousStrategy(ai); }
9093
};
9194

9295
class MovementStrategyContext : public NamedObjectContext<Strategy>

src/modules/Bots/playerbot/strategy/actions/MovementActions.cpp

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "WorldHandlers/Transports.h"
1111
#include "movement/MoveSplineInit.h"
1212
#include "movement/MoveSpline.h"
13+
#include "Creature.h"
1314

1415
using namespace ai;
1516

@@ -33,7 +34,7 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
3334
{
3435
bool moved = MoveTo(target->GetMapId(),
3536
target->GetPositionX() + cos(angle) * distance,
36-
target->GetPositionY()+ sin(angle) * distance,
37+
target->GetPositionY() + sin(angle) * distance,
3738
target->GetPositionZ());
3839
if (moved)
3940
{
@@ -43,14 +44,17 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
4344
return false;
4445
}
4546

46-
bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z)
47+
bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool unsafe)
4748
{
4849
bot->UpdateGroundPositionZ(x, y, z);
4950
if (!IsMovingAllowed(mapId, x, y, z))
5051
{
5152
return false;
5253
}
5354

55+
if (!unsafe && ai->HasStrategy("cautious") && IsAggroPosition(x, y))
56+
return false;
57+
5458
float distance = bot->GetDistance(x, y, z);
5559
if (distance > sPlayerbotAIConfig.contactDistance)
5660
{
@@ -77,7 +81,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z)
7781
mm.Clear();
7882

7983
float botZ = bot->GetPositionZ();
80-
mm.MovePoint(mapId, x, y, z);
84+
mm.MovePoint(mapId, x, y, z);
8185
}
8286

8387
AI_VALUE(LastMovement&, "last movement").Set(x, y, z, bot->GetOrientation());
@@ -93,7 +97,6 @@ bool MovementAction::MoveTo(Unit* target, float distance)
9397

9498
float bx = bot->GetPositionX();
9599
float by = bot->GetPositionY();
96-
float bz = bot->GetPositionZ();
97100

98101
float tx = target->GetPositionX();
99102
float ty = target->GetPositionY();
@@ -116,7 +119,20 @@ bool MovementAction::MoveTo(Unit* target, float distance)
116119
float dx = cos(angle) * needToGo + bx;
117120
float dy = sin(angle) * needToGo + by;
118121

119-
return MoveTo(target->GetMapId(), dx, dy, tz);
122+
if (needToGo > 0)
123+
{
124+
float safeDist = CalculateAggroFreeDistance(bx, by, angle, needToGo);
125+
if (safeDist < needToGo)
126+
{
127+
if(safeDist < sPlayerbotAIConfig.contactDistance)
128+
{
129+
return false;
130+
}
131+
dx = cos(angle) * safeDist + bx;
132+
dy = sin(angle) * safeDist + by;
133+
}
134+
}
135+
return MoveTo(target->GetMapId(), dx, dy, tz, true);
120136
}
121137

122138
float MovementAction::GetFollowAngle()
@@ -360,6 +376,11 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
360376
ai->InterruptSpell();
361377
}
362378

379+
float followX = target->GetPositionX() + cos(angle) * distance;
380+
float followY = target->GetPositionY() + sin(angle) * distance;
381+
if (IsAggroPosition(followX, followY))
382+
return false;
383+
363384
mm.MoveFollow(target, distance, angle);
364385

365386
AI_VALUE(LastMovement&, "last movement").Set(target);
@@ -419,6 +440,74 @@ bool MovementAction::Flee(Unit *target)
419440
return MoveTo(target->GetMapId(), rx, ry, rz);
420441
}
421442

443+
/*
444+
* Returns the farthest distance along the beeline from (bx,by) at the
445+
* given angle that doesn't enter any hostile creature's aggro zone.
446+
* Returns maxDist if the entire path is clear.
447+
*/
448+
float MovementAction::CalculateAggroFreeDistance(float bx, float by,
449+
float angle, float maxDist)
450+
{
451+
if( !ai->HasStrategy("cautious"))
452+
{
453+
return maxDist;
454+
}
455+
float cosA = cos(angle);
456+
float sinA = sin(angle);
457+
float safeDist = maxDist;
458+
459+
list<ObjectGuid> targets = AI_VALUE(list<ObjectGuid>, "possible targets");
460+
for (list<ObjectGuid>::iterator i = targets.begin(); i != targets.end(); ++i)
461+
{
462+
Unit* unit = ai->GetUnit(*i);
463+
if (!unit || !unit->IsAlive() || unit->IsInCombat() || !unit->IsHostileTo(bot) || unit == bot->getVictim())
464+
continue;
465+
466+
Creature* creature = dynamic_cast<Creature*>(unit);
467+
if (!creature || !creature->CanInitiateAttack())
468+
continue;
469+
470+
float aggroRange = creature->GetAttackDistance(bot);
471+
float ex = bx - creature->GetPositionX();
472+
float ey = by - creature->GetPositionY();
473+
float b = ex * cosA + ey * sinA;
474+
float c = ex * ex + ey * ey - aggroRange * aggroRange;
475+
476+
float disc = b * b - c;
477+
if (disc < 0)
478+
continue;
479+
480+
float sqrtDisc = sqrt(disc);
481+
float tEntry = -b - sqrtDisc;
482+
if (tEntry < 0)
483+
{
484+
continue;
485+
}
486+
487+
if (tEntry < safeDist)
488+
{
489+
safeDist = std::max(0.0f, tEntry - 2.0f);
490+
}
491+
}
492+
493+
return safeDist;
494+
}
495+
496+
bool MovementAction::IsAggroPosition(float x, float y)
497+
{
498+
float bx = bot->GetPositionX();
499+
float by = bot->GetPositionY();
500+
501+
float dx = x - bx;
502+
float dy = y - by;
503+
float dist = sqrt(dx * dx + dy * dy);
504+
if (dist < 0.1f)
505+
return false;
506+
507+
float angle = atan2(dy, dx);
508+
return CalculateAggroFreeDistance(bx, by, angle, dist) < dist;
509+
}
510+
422511
bool FleeAction::Execute(Event event)
423512
{
424513
return Flee(AI_VALUE(Unit*, "current target"));

src/modules/Bots/playerbot/strategy/actions/MovementActions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace ai
1515

1616
protected:
1717
bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig.followDistance);
18-
bool MoveTo(uint32 mapId, float x, float y, float z);
18+
bool MoveTo(uint32 mapId, float x, float y, float z, bool unsafe = false);
1919
bool MoveTo(Unit* target, float distance = 0.0f);
2020
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig.followDistance);
2121
float GetFollowAngle();
@@ -28,6 +28,8 @@ namespace ai
2828
bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
2929
bool IsMovingAllowed();
3030
bool Flee(Unit *target);
31+
float CalculateAggroFreeDistance(float bx, float by, float angle, float maxDist);
32+
bool IsAggroPosition(float x, float y);
3133

3234
protected:
3335
Player* bot;

src/modules/Bots/playerbot/strategy/actions/PositionAction.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ bool MoveToPositionAction::Execute(Event event)
3636
ai->TellMaster(out);
3737
return false;
3838
}
39-
40-
return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z);
39+
if (IsAggroPosition(pos.x, pos.y))
40+
{
41+
ai->TellMaster("Warning: that position is near hostile creatures.");
42+
}
43+
return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z, true);
4144
}
4245

46+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
namespace ai
4+
{
5+
class CautiousStrategy : public Strategy
6+
{
7+
public:
8+
CautiousStrategy(PlayerbotAI* ai) : Strategy(ai) {}
9+
virtual string getName() { return "cautious"; }
10+
};
11+
}

0 commit comments

Comments
 (0)