Skip to content

Commit

Permalink
Enable avoid aoe strategy and tweak the flee action to work with it
Browse files Browse the repository at this point in the history
  • Loading branch information
davidonete committed Oct 25, 2022
1 parent f912edd commit e2fc508
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 154 deletions.
2 changes: 1 addition & 1 deletion playerbot/AiFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa

if (!player->InBattleGround())
{
engine->addStrategies("racials", "chat", "default", "potions", /*"cast time",*/ "duel", "pvp", NULL);
engine->addStrategies("racials", "chat", "default", "potions", /*"cast time",*/ "duel", "pvp", "avoid aoe", NULL);
}

switch (player->getClass())
Expand Down
35 changes: 24 additions & 11 deletions playerbot/FleeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,31 @@ bool FleeManager::CalculateDestination(float* rx, float* ry, float* rz)

bool FleeManager::isUseful()
{
list<ObjectGuid> units = *bot->GetPlayerbotAI()->GetAiObjectContext()->GetValue<list<ObjectGuid> >("possible targets no los");
for (list<ObjectGuid>::iterator i = units.begin(); i != units.end(); ++i)
// It the bot is a victim of an aoe attack it should move no matter the target attack distance
bool const inAoe = bot->GetPlayerbotAI()->GetAiObjectContext()->GetValue<bool>("has area debuff", "self target")->Get();
if (!inAoe)
{
Unit* unit = bot->GetPlayerbotAI()->GetUnit(*i);
if (!unit)
continue;

if (startPosition.sqDistance(WorldPosition(unit)) < unit->GetAttackDistance(bot) * unit->GetAttackDistance(bot))
return true;
list<ObjectGuid> units = *bot->GetPlayerbotAI()->GetAiObjectContext()->GetValue<list<ObjectGuid> >("possible targets no los");
for (list<ObjectGuid>::iterator i = units.begin(); i != units.end(); ++i)
{
Unit* unit = bot->GetPlayerbotAI()->GetUnit(*i);
if (unit)
{
float const distanceSquared = startPosition.sqDistance(WorldPosition(unit));
float attackDistanceSquared = unit->GetAttackDistance(bot);
attackDistanceSquared *= attackDistanceSquared;
if (distanceSquared < attackDistanceSquared)
{
return true;
}

//float d = sServerFacade.GetDistance2d(unit, bot);
//if (sServerFacade.IsDistanceLessThan(d, sPlayerbotAIConfig.aggroDistance)) return true;
}
}

//float d = sServerFacade.GetDistance2d(unit, bot);
//if (sServerFacade.IsDistanceLessThan(d, sPlayerbotAIConfig.aggroDistance)) return true;
return false;
}
return false;

return true;
}
272 changes: 130 additions & 142 deletions playerbot/strategy/actions/MovementActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1401,199 +1401,187 @@ bool MovementAction::Flee(Unit *target)
ai->TellError("I am stuck while fleeing");
return false;
}
bool foundFlee = false;

time_t lastFlee = AI_VALUE(LastMovement&, "last movement").lastFlee;
time_t now = time(0);
uint32 fleeDelay = urand(2, sPlayerbotAIConfig.returnDelay / 1000);


if (lastFlee)
{
if ((now - lastFlee) <= fleeDelay)
{
return false;
}
}
}

const bool isHealer = ai->IsHeal(bot);
const bool isTank = ai->IsTank(bot);
const bool isDps = !isHealer && !isTank;
const bool isRanged = ai->IsRanged(bot);
const bool needHealer = !isHealer && AI_VALUE2(uint8, "health", "self target") < 50;

//HostileReference *ref = target->GetThreatManager().getCurrentVictim();
HostileReference *ref = sServerFacade.GetThreatManager(target).getCurrentVictim();
const bool isTarget = ref && ref->getTarget() == bot;

if (ref && ref->getTarget() == bot) // bot is target - try to flee to tank or master
Unit* fleeTarget = nullptr;
Group* group = bot->GetGroup();
if (group)
{
Group *group = bot->GetGroup();
if (group)
Unit* spareTarget = nullptr;
vector<Unit*> possibleTargets;
const float minFleeDistance = 5.0f;
const float maxFleeDistance = isTarget ? 40.0f : ai->GetRange("spell") * 1.5;
const float minRangedTargetDistance = ai->GetRange("spell") / 2 + sPlayerbotAIConfig.followDistance;

for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Unit* fleeTarget = nullptr;
float fleeDistance = 40.0f;
Player* groupMember = gref->getSource();

for (GroupReference *gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->getSource();
if (!player || player == bot || !sServerFacade.IsAlive(player) || bot->GetMapId() != player->GetMapId()) continue;
if (sServerFacade.GetDistance2d(bot, player) > 40.0f)
continue;
// Ignore group member if is not alive or on a different zone
if (!groupMember || groupMember == bot || groupMember == master || !sServerFacade.IsAlive(groupMember) || bot->GetMapId() != groupMember->GetMapId())
continue;

bool hasAoe = false;
if (PlayerbotAI* botAi = player->GetPlayerbotAI())
{
if (botAi->GetAiObjectContext()->GetValue<bool>("has area debuff", "self target")->Get())
hasAoe = true;
}
// Don't flee to group member if too close or too far
float const distanceToGroupMember = sServerFacade.GetDistance2d(bot, groupMember);
if (distanceToGroupMember < minFleeDistance || distanceToGroupMember > maxFleeDistance)
continue;

if (hasAoe)
if (PlayerbotAI* groupMemberBotAi = groupMember->GetPlayerbotAI())
{
// Ignore if the group member is affected by an aoe spell
if (groupMemberBotAi->GetAiObjectContext()->GetValue<bool>("has area debuff", "self target")->Get())
continue;
}

if (ai->IsTank(player))
// If the bot is currently being targeted
if(isTarget)
{
// Try to flee to tank
if (ai->IsTank(groupMember))
{
float distanceToTank = sServerFacade.GetDistance2d(bot, player);
float distanceToTank = sServerFacade.GetDistance2d(bot, groupMember);
float distanceToTarget = sServerFacade.GetDistance2d(bot, target);
if (distanceToTank < fleeDistance)
if (distanceToTank > minFleeDistance && distanceToTank < maxFleeDistance)
{
fleeTarget = player;
fleeDistance = distanceToTank;
possibleTargets.push_back(groupMember);
}
}
}

if (fleeTarget)
foundFlee = MoveNear(fleeTarget);

if ((!fleeTarget || !foundFlee) && master)
{
foundFlee = MoveNear(master);
}
}
}
else // bot is not targeted, try to flee dps/healers
{
bool isHealer = ai->IsHeal(bot);
bool isDps = !isHealer && !ai->IsTank(bot);
bool isTank = ai->IsTank(bot);
bool needHealer = !isHealer && AI_VALUE2(uint8, "health", "self target") < 50;
bool isRanged = ai->IsRanged(bot);

Group *group = bot->GetGroup();
if (group)
{
Unit* fleeTarget = nullptr;
float fleeDistance = ai->GetRange("spell") * 1.5;
Unit* spareTarget = nullptr;
float spareDistance = ai->GetRange("spell") * 2;
vector<Unit*> possibleTargets;

for (GroupReference *gref = group->GetFirstMember(); gref; gref = gref->next())
else
{
Player* player = gref->getSource();
if (!player || player == bot || !sServerFacade.IsAlive(player) || bot->GetMapId() != player->GetMapId()) continue;
if (sServerFacade.GetDistance2d(bot, player) > 40.0f)
continue;

bool hasAoe = false;
if (PlayerbotAI* botAi = player->GetPlayerbotAI())
{
if (botAi->GetAiObjectContext()->GetValue<bool>("has area debuff", "self target")->Get())
hasAoe = true;
}

if (hasAoe)
continue;

if ((isHealer && ai->IsHeal(player)) || needHealer)
// Try to flee to healers (group healers together or approach a healer if needed)
if ((isHealer && ai->IsHeal(groupMember)) || needHealer)
{
float distanceToHealer = sServerFacade.GetDistance2d(bot, player);
float distanceToTarget = sServerFacade.GetDistance2d(player, target);
if (distanceToHealer < fleeDistance && distanceToTarget > (ai->GetRange("spell") / 2 + sPlayerbotAIConfig.followDistance) && (needHealer || player->IsWithinLOSInMap(target, true)))
const float distanceToTarget = sServerFacade.GetDistance2d(groupMember, target);
if (distanceToTarget > minRangedTargetDistance && (needHealer || groupMember->IsWithinLOSInMap(target, true)))
{
fleeTarget = player;
fleeDistance = distanceToHealer;
possibleTargets.push_back(fleeTarget);
possibleTargets.push_back(groupMember);
}
}
else if (isRanged && ai->IsRanged(player))
// Try to flee to ranged (group ranged together)
else if (isRanged && ai->IsRanged(groupMember))
{
float distanceToRanged = sServerFacade.GetDistance2d(bot, player);
float distanceToTarget = sServerFacade.GetDistance2d(player, target);
if (distanceToRanged < fleeDistance && distanceToTarget > (ai->GetRange("spell") / 2 + sPlayerbotAIConfig.followDistance) && player->IsWithinLOSInMap(target, true))
const float distanceToTarget = sServerFacade.GetDistance2d(groupMember, target);
if (distanceToTarget > minRangedTargetDistance && groupMember->IsWithinLOSInMap(target, true))
{
fleeTarget = player;
fleeDistance = distanceToRanged;
possibleTargets.push_back(fleeTarget);
possibleTargets.push_back(groupMember);
}
}
// remember any group member in case no one else found
float distanceToFlee = sServerFacade.GetDistance2d(bot, player);
float distanceToTarget = sServerFacade.GetDistance2d(player, target);
if (distanceToFlee < spareDistance && distanceToTarget >(ai->GetRange("spell") / 2 + sPlayerbotAIConfig.followDistance) && player->IsWithinLOSInMap(target, true))
{
spareTarget = player;
spareDistance = distanceToFlee;
possibleTargets.push_back(fleeTarget);
}
}

if (!possibleTargets.empty())
fleeTarget = possibleTargets[urand(0, possibleTargets.size() - 1)];

if (!fleeTarget)
fleeTarget = spareTarget;

if (fleeTarget)
foundFlee = MoveNear(fleeTarget);

if ((!fleeTarget || !foundFlee) && master && sServerFacade.IsAlive(master) && master->IsWithinLOSInMap(target, true))
{
float distanceToTarget = sServerFacade.GetDistance2d(master, target);
if (distanceToTarget > (ai->GetRange("spell") / 2 + sPlayerbotAIConfig.followDistance))
foundFlee = MoveNear(master);
}
}
}

if ((foundFlee || lastFlee) && bot->GetGroup())
{
if (!lastFlee)
{
AI_VALUE(LastMovement&, "last movement").lastFlee = now;
if (!possibleTargets.empty())
{
fleeTarget = possibleTargets[urand(0, possibleTargets.size() - 1)];
}
else
{
if ((now - lastFlee) > fleeDelay)
// If nothing was found, let's try the master
if (master && sServerFacade.IsAlive(master) && master->IsWithinLOSInMap(target, true))
{
AI_VALUE(LastMovement&, "last movement").lastFlee = 0;
// Don't flee to group member if too close or too far
float const distanceToMaster = sServerFacade.GetDistance2d(bot, master);
if (distanceToMaster > minFleeDistance && distanceToMaster < maxFleeDistance)
{
if(isRanged)
{
const float distanceToTarget = sServerFacade.GetDistance2d(master, target);
if (distanceToTarget > minRangedTargetDistance)
{
fleeTarget = master;
}
}
else
{
fleeTarget = master;
}
}
}
else
return false;
}
}
bool fullDistance = false;
if (target->IsPlayer())
fullDistance = true;
if (WorldPosition(bot).isOverworld())
fullDistance = true;

FleeManager manager(bot, fullDistance ? (ai->GetRange("flee") * 2) : ai->GetRange("flee"), bot->GetAngle(target) + M_PI);

if (!manager.isUseful())
return false;

if (!urand(0, 50) && ai->HasStrategy("emote", BotState::BOT_STATE_NON_COMBAT))
bool succeeded = false;
if (fleeTarget)
{
vector<uint32> sounds;
sounds.push_back(304); // guard
sounds.push_back(306); // flee
ai->PlayEmote(sounds[urand(0, sounds.size() - 1)]);
succeeded = MoveNear(fleeTarget);
}

float rx, ry, rz;
if (!manager.CalculateDestination(&rx, &ry, &rz))
// Generate a position to flee
if(!succeeded)
{
ai->TellError("Nowhere to flee");
return false;
if (lastFlee && bot->GetGroup())
{
if (!lastFlee)
{
AI_VALUE(LastMovement&, "last movement").lastFlee = now;
}
else
{
if ((now - lastFlee) > fleeDelay)
{
AI_VALUE(LastMovement&, "last movement").lastFlee = 0;
}
else
{
succeeded = false;
}
}
}
bool fullDistance = false;
if (target->IsPlayer())
fullDistance = true;
if (WorldPosition(bot).isOverworld())
fullDistance = true;

FleeManager manager(bot, fullDistance ? (ai->GetRange("flee") * 2) : ai->GetRange("flee"), bot->GetAngle(target) + M_PI);
if (!manager.isUseful())
{
succeeded = false;
}

if (!urand(0, 50) && ai->HasStrategy("emote", BotState::BOT_STATE_NON_COMBAT))
{
vector<uint32> sounds;
sounds.push_back(304); // guard
sounds.push_back(306); // flee
ai->PlayEmote(sounds[urand(0, sounds.size() - 1)]);
}

float rx, ry, rz;
if (!manager.CalculateDestination(&rx, &ry, &rz))
{
ai->TellError("Nowhere to flee");
succeeded = false;
}

if(MoveTo(target->GetMapId(), rx, ry, rz))
{
AI_VALUE(LastMovement&, "last movement").lastFlee = time(0);
succeeded = true;
}
}

bool result = MoveTo(target->GetMapId(), rx, ry, rz);
if (result)
AI_VALUE(LastMovement&, "last movement").lastFlee = time(0);
return result;
return succeeded;
}

void MovementAction::ClearIdleState()
Expand Down

0 comments on commit e2fc508

Please sign in to comment.