mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 09:07:19 +00:00
PlayerbotAI::FindOilFor was making the server randomly crashing ChooseTravelTargetAction::getNewTarget: when active bot groupping was making the server crash as looking for unexisting params Several bug fixes and tweak to Quest and Group New fucntionnality: Bots will now share quests randomly to their party Bots will try to accomplish group member quest before moving on to new target Bots will try to sells items only after few levels ( 5 ) when in group When dropping a quest bots will try to select a new one they are on instead of idling for few time Bots will no longuer try to invite themselfs to group or if group is full Bots are now allowed to leave party by themself Bots in groupe if not leader are forbbiden to tag in bgs Bots in bot-groups no have a more limited range to look for grind target Polish logs
370 lines
10 KiB
C++
370 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "ChooseRpgTargetAction.h"
|
|
#include "BattlegroundMgr.h"
|
|
#include "BudgetValues.h"
|
|
#include "ChatHelper.h"
|
|
#include "Event.h"
|
|
#include "Formations.h"
|
|
#include "GuildCreateActions.h"
|
|
#include "PossibleRpgTargetsValue.h"
|
|
#include "Playerbots.h"
|
|
#include "RpgSubActions.h"
|
|
#include "Util.h"
|
|
#include "ServerFacade.h"
|
|
|
|
#include <random>
|
|
|
|
bool ChooseRpgTargetAction::HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids)
|
|
{
|
|
if (botAI->HasRealPlayerMaster())
|
|
return false;
|
|
|
|
uint32 num = 0;
|
|
|
|
for (auto& i : nearGuids)
|
|
{
|
|
Player* player = ObjectAccessor::FindPlayer(i);
|
|
if (!player)
|
|
continue;
|
|
|
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
|
if (!botAI)
|
|
continue;
|
|
|
|
if (!botAI->AllowActivity(GRIND_ACTIVITY))
|
|
continue;
|
|
|
|
if (PAI_VALUE(GuidPosition, "rpg target") != guid)
|
|
continue;
|
|
|
|
num++;
|
|
if (num >= max)
|
|
break;
|
|
}
|
|
|
|
return num > 0;
|
|
}
|
|
|
|
float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
|
|
{
|
|
GuidPosition currentRpgTarget = AI_VALUE(GuidPosition, "rpg target");
|
|
SET_AI_VALUE(GuidPosition, "rpg target", guidP);
|
|
|
|
Strategy* rpgStrategy;
|
|
|
|
std::vector<TriggerNode*> triggerNodes;
|
|
|
|
float maxRelevance = 0.0f;
|
|
|
|
for (auto& strategy : botAI->GetAiObjectContext()->GetSupportedStrategies())
|
|
{
|
|
if (strategy.find("rpg") == std::string::npos)
|
|
continue;
|
|
|
|
if (!botAI->HasStrategy(strategy, BotState::BOT_STATE_NON_COMBAT))
|
|
continue;
|
|
|
|
rpgStrategy = botAI->GetAiObjectContext()->GetStrategy(strategy);
|
|
|
|
rpgStrategy->InitTriggers(triggerNodes);
|
|
|
|
for (auto triggerNode : triggerNodes)
|
|
{
|
|
Trigger* trigger = context->GetTrigger(triggerNode->getName());
|
|
|
|
if (trigger)
|
|
{
|
|
triggerNode->setTrigger(trigger);
|
|
|
|
if (triggerNode->getFirstRelevance() < maxRelevance || triggerNode->getFirstRelevance() > 2.0f)
|
|
continue;
|
|
|
|
Trigger* trigger = triggerNode->getTrigger();
|
|
|
|
if (!trigger->IsActive())
|
|
continue;
|
|
|
|
NextAction** nextActions = triggerNode->getHandlers();
|
|
|
|
bool isRpg = false;
|
|
|
|
for (int32 i = 0; i < NextAction::size(nextActions); i++)
|
|
{
|
|
NextAction* nextAction = nextActions[i];
|
|
|
|
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
|
|
|
|
if (dynamic_cast<RpgEnabled*>(action))
|
|
isRpg = true;
|
|
}
|
|
NextAction::destroy(nextActions);
|
|
|
|
if (isRpg)
|
|
{
|
|
maxRelevance = triggerNode->getFirstRelevance();
|
|
rgpActionReason[guidP] = triggerNode->getName();
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto trigger : triggerNodes)
|
|
{
|
|
delete trigger;
|
|
}
|
|
|
|
triggerNodes.clear();
|
|
}
|
|
|
|
SET_AI_VALUE(GuidPosition, "rpg target", currentRpgTarget);
|
|
|
|
if (!maxRelevance)
|
|
return 0.0;
|
|
|
|
return floor((maxRelevance - 1.0) * 1000.0f);
|
|
}
|
|
|
|
bool ChooseRpgTargetAction::Execute(Event event)
|
|
{
|
|
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
|
|
Player* master = botAI->GetMaster();
|
|
GuidPosition masterRpgTarget;
|
|
if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported())
|
|
{
|
|
Player* player = botAI->GetMaster();
|
|
GuidPosition masterRpgTarget = PAI_VALUE(GuidPosition, "rpg target");
|
|
}
|
|
else
|
|
master = nullptr;
|
|
|
|
std::unordered_map<ObjectGuid, uint32> targets;
|
|
uint32 num = 0;
|
|
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
|
|
GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los");
|
|
GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players");
|
|
GuidSet& ignoreList = AI_VALUE(GuidSet&, "ignore rpg target");
|
|
|
|
for (auto target : possibleTargets)
|
|
targets[target] = 0.0f;
|
|
|
|
for (auto target : possibleObjects)
|
|
targets[target] = 0.0f;
|
|
|
|
for (auto target : possiblePlayers)
|
|
targets[target] = 0.0f;
|
|
|
|
if (targets.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (urand(0, 9))
|
|
{
|
|
for (auto target : ignoreList)
|
|
targets.erase(target);
|
|
}
|
|
|
|
SET_AI_VALUE(std::string, "next rpg action", this->getName());
|
|
|
|
bool hasGoodRelevance = false;
|
|
|
|
for (auto& target : targets)
|
|
{
|
|
Unit* unit = ObjectAccessor::GetUnit(*bot, target.first);
|
|
if (!unit)
|
|
continue;
|
|
|
|
GuidPosition guidP(unit);
|
|
if (!guidP || !guidP.getMap())
|
|
continue;
|
|
|
|
float priority = 1;
|
|
|
|
if (guidP.GetWorldObject() && !isFollowValid(bot, guidP.GetWorldObject()))
|
|
continue;
|
|
|
|
if (guidP.IsGameObject())
|
|
{
|
|
GameObject* go = guidP.GetGameObject();
|
|
if (!go || !go->isSpawned() || go->GetGoState() != GO_STATE_READY)
|
|
continue;
|
|
}
|
|
else if (guidP.IsPlayer())
|
|
{
|
|
Player* player = guidP.GetPlayer();
|
|
if (!player)
|
|
continue;
|
|
|
|
if (GET_PLAYERBOT_AI(player))
|
|
{
|
|
GuidPosition guidPP = PAI_VALUE(GuidPosition, "rpg target");
|
|
if (guidPP.IsPlayer())
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (possiblePlayers.size() > 200 || HasSameTarget(guidP, urand(5, 15), possiblePlayers))
|
|
continue;
|
|
|
|
float relevance = getMaxRelevance(guidP);
|
|
|
|
if (!hasGoodRelevance || relevance > 1)
|
|
target.second = relevance;
|
|
|
|
if (target.second > 1)
|
|
hasGoodRelevance = true;
|
|
}
|
|
|
|
SET_AI_VALUE(std::string, "next rpg action", "");
|
|
|
|
for (auto it = begin(targets); it != end(targets);)
|
|
{
|
|
//Remove empty targets.
|
|
if (it->second == 0)
|
|
it = targets.erase(it);
|
|
//Remove useless targets if there's any good ones
|
|
else if (hasGoodRelevance && it->second <= 1.0)
|
|
it = targets.erase(it);
|
|
//Remove useless targets if it's not masters target.
|
|
else if (!hasGoodRelevance && master && (!masterRpgTarget || it->first != masterRpgTarget))
|
|
it = targets.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
|
|
if (targets.empty())
|
|
{
|
|
LOG_DEBUG("playerbots", "{} can't choose RPG target: all {} targets are not available", bot->GetName().c_str(), possibleTargets.size());
|
|
RESET_AI_VALUE(GuidSet&, "ignore rpg target");
|
|
RESET_AI_VALUE(GuidPosition, "rpg target");
|
|
return false;
|
|
}
|
|
|
|
std::vector<GuidPosition> guidps;
|
|
std::vector<int32> relevances;
|
|
|
|
for (auto& target : targets)
|
|
{
|
|
Unit* unit = ObjectAccessor::GetUnit(*bot, target.first);
|
|
if (!unit)
|
|
continue;
|
|
|
|
GuidPosition guidP(unit);
|
|
if (!guidP)
|
|
continue;
|
|
|
|
guidps.push_back(guidP);
|
|
relevances.push_back(target.second);
|
|
}
|
|
|
|
std::mt19937 gen(time(0));
|
|
sTravelMgr->weighted_shuffle(guidps.begin(), guidps.end(), relevances.begin(), relevances.end(), gen);
|
|
|
|
GuidPosition guidP(guidps.front());
|
|
if (!guidP)
|
|
{
|
|
RESET_AI_VALUE(GuidPosition, "rpg target");
|
|
return false;
|
|
}
|
|
|
|
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT) && guidP.GetWorldObject())
|
|
{
|
|
std::ostringstream out;
|
|
out << "found: ";
|
|
out << chat->FormatWorldobject(guidP.GetWorldObject());
|
|
out << " " << relevances.front();
|
|
|
|
botAI->TellMasterNoFacing(out);
|
|
}
|
|
|
|
SET_AI_VALUE(GuidPosition, "rpg target", guidP);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ChooseRpgTargetAction::isUseful()
|
|
{
|
|
if (!botAI->AllowActivity(RPG_ACTIVITY))
|
|
return false;
|
|
|
|
GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");
|
|
|
|
if (guidP && guidP.distance(bot) < sPlayerbotAIConfig->reactDistance * 2)
|
|
return false;
|
|
|
|
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
|
|
|
|
//if (travelTarget->isTraveling() && AI_VALUE2(bool, "can free move to", *travelTarget->getPosition()))
|
|
//return false;
|
|
|
|
if (AI_VALUE(GuidVector, "possible rpg targets").empty())
|
|
return false;
|
|
|
|
//Not stay, not guard, not combat, not trading and group ready.
|
|
if (!AI_VALUE(bool, "can move around"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
|
|
{
|
|
if (!target)
|
|
return false;
|
|
|
|
return isFollowValid(bot, WorldPosition(target));
|
|
}
|
|
|
|
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
|
{
|
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
Player* master = botAI->GetGroupMaster();
|
|
Player* realMaster = botAI->GetMaster();
|
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
|
|
|
bool inDungeon = false;
|
|
|
|
if (botAI->HasActivePlayerMaster())
|
|
{
|
|
if (realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() && bot->GetMapId() == realMaster->GetMapId())
|
|
inDungeon = true;
|
|
|
|
if (realMaster && realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() && (realMaster->GetMapId() != pos.getMapId()))
|
|
return false;
|
|
}
|
|
|
|
if (!master || bot == master)
|
|
return true;
|
|
|
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
|
return true;
|
|
|
|
if (sqrt(bot->GetDistance(master)) > sPlayerbotAIConfig->rpgDistance * 2)
|
|
return false;
|
|
|
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
|
float distance = master->GetDistance2d(pos.getX(), pos.getY());
|
|
|
|
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
|
{
|
|
Player* player = master;
|
|
if (!master->isMoving() || PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
|
return true;
|
|
}
|
|
|
|
if ((inDungeon || !master->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == master && distance > 5.0f)
|
|
return false;
|
|
|
|
if (!master->isMoving() && distance < 25.0f)
|
|
return true;
|
|
|
|
if (distance < formation->GetMaxDistance())
|
|
return true;
|
|
|
|
return false;
|
|
}
|