mod-playerbots/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp

155 lines
5.0 KiB
C++

/*
* 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 "MoveToTravelTargetAction.h"
#include "ChooseRpgTargetAction.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
bool MoveToTravelTargetAction::Execute(Event /*event*/)
{
TravelTarget* target = AI_VALUE(TravelTarget*, "travel target");
WorldPosition botLocation(bot);
WorldLocation location = *target->getPosition();
Group* group = bot->GetGroup();
if (group && !urand(0, 1) && bot == botAI->GetGroupLeader() && !bot->IsInCombat())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member == bot)
continue;
if (!member->IsAlive())
continue;
if (!member->isMoving())
continue;
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
if (memberBotAI && !memberBotAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
continue;
WorldPosition memberPos(member);
WorldPosition targetPos = *target->getPosition();
float memberDistance = botLocation.distance(memberPos);
if (memberDistance < 50.0f)
continue;
if (memberDistance > sPlayerbotAIConfig.reactDistance * 20)
continue;
// float memberAngle = botLocation.getAngleBetween(targetPos, memberPos);
// if (botLocation.getMapId() == targetPos.getMapId() && botLocation.getMapId() == memberPos.getMapId() &&
// memberAngle < static_cast<float>(M_PI) / 2) //We are heading that direction anyway.
// continue;
if (!urand(0, 5))
{
std::ostringstream out;
if (botAI->GetMaster() && !bot->GetGroup()->IsMember(botAI->GetMaster()->GetGUID()))
out << "Waiting a bit for ";
else
out << "Please hurry up ";
out << member->GetName();
botAI->TellMasterNoFacing(out);
}
target->setExpireIn(target->getTimeLeft() + sPlayerbotAIConfig.maxWaitForMove);
botAI->SetNextCheckDelay(sPlayerbotAIConfig.maxWaitForMove);
return true;
}
}
float maxDistance = target->getDestination()->getRadiusMin();
// Spread bots around the target but keep the offset stable per
// (bot, destination) pair. Previously the angle and radius were
// re-rolled every time the action re-entered (i.e. every tick the
// bot wasn't already moving), which made bots oscillate between
// two random points around the same quest POI instead of
// committing to one approach.
uint32 botLow = bot->GetGUID().GetCounter();
int32 destSeed = static_cast<int32>(location.GetPositionX()) * 73856093 ^
static_cast<int32>(location.GetPositionY()) * 19349663;
uint32 seed = botLow ^ static_cast<uint32>(destSeed);
float angle = 2.0f * static_cast<float>(M_PI) * static_cast<float>(seed % 1000) / 1000.0f;
float mod = 0.5f + static_cast<float>((seed / 1000) % 1000) / 2000.0f; // [0.5, 1.0]
if (target->getMaxTravelTime() > target->getTimeLeft()) // The bot is late. Speed it up.
{
// distance = sPlayerbotAIConfig.fleeDistance;
// angle = bot->GetAngle(location.GetPositionX(), location.GetPositionY());
// location = botLocation.getLocation();
}
float x = location.GetPositionX();
float y = location.GetPositionY();
float z = location.GetPositionZ();
float mapId = location.GetMapId();
x += cos(angle) * maxDistance * mod;
y += sin(angle) * maxDistance * mod;
bool canMove = false;
if (bot->IsWithinLOS(x, y, z))
canMove = MoveNear(mapId, x, y, z, 0);
else
canMove = MoveTo(mapId, x, y, z, false, false);
if (!canMove && !target->isForced())
{
target->incRetry(true);
if (target->isMaxRetry(true))
target->setStatus(TRAVEL_STATUS_COOLDOWN);
}
else
target->setRetry(true);
return canMove;
}
bool MoveToTravelTargetAction::isUseful()
{
if (!botAI->AllowActivity(TRAVEL_ACTIVITY))
return false;
if (!context->GetValue<TravelTarget*>("travel target")->Get()->isTraveling())
return false;
if (bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
return false;
if (bot->IsFlying())
return false;
if (bot->isMoving())
return false;
if (!AI_VALUE(bool, "can move around"))
return false;
LootObject loot = AI_VALUE(LootObject, "loot target");
if (loot.IsLootPossible(bot))
return false;
if (!ChooseRpgTargetAction::isFollowValid(bot,
*context->GetValue<TravelTarget*>("travel target")->Get()->getPosition()))
return false;
return true;
}