Enable bots to do Outdoor pvp (#2217)

## Pull Request Description
Bots will now engage with outdoor pvp targets when in an area with them.
I carved this out of the guildrpg system Im working on since it should
work just fine as a standalone. Note this requires a core update
https://github.com/azerothcore/azerothcore-wotlk/pull/25103

## Feature Evaluation

Its not expensive. the status checks are fairly light and simple. Should
be on par with current rpg system actions

## How to Test the Changes

You can try to use selfbot to enable this while in EPL, or set the
probability of all other rpg actions to 0.


## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - [ ] No, not at all
    - [x] Minimal impact (**explain below**)
    - [ ] Moderate impact (**explain below**)

There is some impact, but should be minimal overall. 

- Does this change modify default bot behavior?
    - [ ] No
    - [x] Yes (**explain why**)
It will activate automatically based on default config. 


- Does this change add new decision branches or increase maintenance
complexity?
    - [ ] No
    - [x] Yes (**explain below**)

## AI Assistance
Was AI assistance used while working on this change?
- [ ] No
- [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->
Nothing beyond search functionality and autocomplete. 


## Final Checklist

- [x] Stability is not compromised.
- [x] Performance impact is understood, tested, and acceptable.
- [x] Added logic complexity is justified and explained.
- [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->

---------

Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: Revision <tkn963@gmail.com>
Co-authored-by: kadeshar <kadeshar@gmail.com>
This commit is contained in:
Keleborn 2026-04-10 22:16:58 -07:00 committed by GitHub
parent 74ccc6fbe9
commit 53a607e147
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 226 additions and 13 deletions

View File

@ -1038,6 +1038,7 @@ AiPlayerbot.EnableNewRpgStrategy = 1
# DoQuest (Default: 60 Select quest from the quest log and head to the location to attempt completion) # DoQuest (Default: 60 Select quest from the quest log and head to the location to attempt completion)
# TravelFlight (Default: 15 Go to the nearest flightmaster and fly to a level-appropriate area) # TravelFlight (Default: 15 Go to the nearest flightmaster and fly to a level-appropriate area)
# Rest (Default: 5 Take a break for a while and do nothing) # Rest (Default: 5 Take a break for a while and do nothing)
# OutdoorPvp (Default: 10 Participate in outdoor PvP capture points if already in an outdoor PvP zone)
AiPlayerbot.RpgStatusProbWeight.WanderRandom = 15 AiPlayerbot.RpgStatusProbWeight.WanderRandom = 15
AiPlayerbot.RpgStatusProbWeight.WanderNpc = 20 AiPlayerbot.RpgStatusProbWeight.WanderNpc = 20
AiPlayerbot.RpgStatusProbWeight.GoGrind = 15 AiPlayerbot.RpgStatusProbWeight.GoGrind = 15
@ -1045,6 +1046,7 @@ AiPlayerbot.RpgStatusProbWeight.GoCamp = 10
AiPlayerbot.RpgStatusProbWeight.DoQuest = 60 AiPlayerbot.RpgStatusProbWeight.DoQuest = 60
AiPlayerbot.RpgStatusProbWeight.TravelFlight = 15 AiPlayerbot.RpgStatusProbWeight.TravelFlight = 15
AiPlayerbot.RpgStatusProbWeight.Rest = 5 AiPlayerbot.RpgStatusProbWeight.Rest = 5
AiPlayerbot.RpgStatusProbWeight.OutdoorPvp = 10
# Bots' minimum and maximum level when teleporting in and out of a zone, according to the new RPG strategy # Bots' minimum and maximum level when teleporting in and out of a zone, according to the new RPG strategy
# Format: AiPlayerbot.ZoneBracket.zoneID = minLevel,maxLevel # Format: AiPlayerbot.ZoneBracket.zoneID = minLevel,maxLevel

View File

@ -63,6 +63,7 @@
#include "WorldBuffAction.h" #include "WorldBuffAction.h"
#include "XpGainAction.h" #include "XpGainAction.h"
#include "NewRpgAction.h" #include "NewRpgAction.h"
#include "NewRpgOutdoorPvP.h"
#include "FishingAction.h" #include "FishingAction.h"
#include "CancelChannelAction.h" #include "CancelChannelAction.h"
#include "WaitForAttackAction.h" #include "WaitForAttackAction.h"
@ -265,6 +266,7 @@ public:
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc; creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest; creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight; creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
creators["new rpg outdoor pvp"] = &ActionContext::new_rpg_outdoor_pvp;
creators["wait for attack keep safe distance"] = &ActionContext::wait_for_attack_keep_safe_distance; creators["wait for attack keep safe distance"] = &ActionContext::wait_for_attack_keep_safe_distance;
} }
@ -462,6 +464,7 @@ private:
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); } static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); } static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); } static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
static Action* new_rpg_outdoor_pvp(PlayerbotAI* ai) { return new NewRpgOutdoorPvpAction(ai); }
static Action* wait_for_attack_keep_safe_distance(PlayerbotAI* ai) { return new WaitForAttackKeepSafeDistanceAction(ai); } static Action* wait_for_attack_keep_safe_distance(PlayerbotAI* ai) { return new WaitForAttackKeepSafeDistanceAction(ai); }
}; };

View File

@ -230,6 +230,7 @@ public:
creators["wander npc status"] = &TriggerContext::wander_npc_status; creators["wander npc status"] = &TriggerContext::wander_npc_status;
creators["do quest status"] = &TriggerContext::do_quest_status; creators["do quest status"] = &TriggerContext::do_quest_status;
creators["travel flight status"] = &TriggerContext::travel_flight_status; creators["travel flight status"] = &TriggerContext::travel_flight_status;
creators["outdoor pvp status"] = &TriggerContext::outdoor_pvp_status;
creators["can self resurrect"] = &TriggerContext::can_self_resurrect; creators["can self resurrect"] = &TriggerContext::can_self_resurrect;
creators["can fish"] = &TriggerContext::can_fish; creators["can fish"] = &TriggerContext::can_fish;
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber; creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
@ -436,6 +437,7 @@ private:
static Trigger* wander_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_WANDER_NPC); } static Trigger* wander_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_WANDER_NPC); }
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); } static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); } static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); }
static Trigger* outdoor_pvp_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_OUTDOOR_PVP); }
static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); } static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); }
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); } static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); } static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }

View File

@ -62,7 +62,7 @@ bool NewRpgStatusUpdateAction::Execute(Event /*event*/)
{ {
case RPG_IDLE: case RPG_IDLE:
return RandomChangeStatus({RPG_GO_CAMP, RPG_GO_GRIND, RPG_WANDER_RANDOM, RPG_WANDER_NPC, RPG_DO_QUEST, return RandomChangeStatus({RPG_GO_CAMP, RPG_GO_GRIND, RPG_WANDER_RANDOM, RPG_WANDER_NPC, RPG_DO_QUEST,
RPG_TRAVEL_FLIGHT, RPG_REST}); RPG_TRAVEL_FLIGHT, RPG_REST, RPG_OUTDOOR_PVP});
case RPG_GO_GRIND: case RPG_GO_GRIND:
{ {
@ -140,6 +140,15 @@ bool NewRpgStatusUpdateAction::Execute(Event /*event*/)
} }
break; break;
} }
case RPG_OUTDOOR_PVP:
{
if (info.HasStatusPersisted(statusOutDoorPvPDuration))
{
info.ChangeToIdle();
return true;
}
break;
}
default: default:
break; break;
} }

View File

@ -47,10 +47,11 @@ public:
protected: protected:
// static NewRpgStatusTransitionProb transitionMat; // static NewRpgStatusTransitionProb transitionMat;
const int32 statusWanderNpcDuration = 5 * 60 * 1000; const int32 statusWanderNpcDuration = 5 * MINUTE * IN_MILLISECONDS ;
const int32 statusWanderRandomDuration = 5 * 60 * 1000; const int32 statusWanderRandomDuration = 5 * MINUTE * IN_MILLISECONDS ;
const int32 statusRestDuration = 30 * 1000; const int32 statusRestDuration = 30 * IN_MILLISECONDS ;
const int32 statusDoQuestDuration = 30 * 60 * 1000; const int32 statusDoQuestDuration = 30 * MINUTE * IN_MILLISECONDS ;
const int32 statusOutDoorPvPDuration = HOUR * IN_MILLISECONDS ;
}; };
class NewRpgGoGrindAction : public NewRpgBaseAction class NewRpgGoGrindAction : public NewRpgBaseAction

View File

@ -12,6 +12,7 @@
#include "NewRpgStrategy.h" #include "NewRpgStrategy.h"
#include "Object.h" #include "Object.h"
#include "ObjectAccessor.h" #include "ObjectAccessor.h"
#include "OutdoorPvPMgr.h"
#include "ObjectDefines.h" #include "ObjectDefines.h"
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
@ -222,12 +223,10 @@ bool NewRpgBaseAction::MoveWorldObjectTo(ObjectGuid guid, float distance)
return MoveTo(mapId, x, y, z, false, false, false, true); return MoveTo(mapId, x, y, z, false, false, false, true);
} }
bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority) bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority, WorldObject* center)
{ {
if (IsWaitingForLastMove(priority)) if (IsWaitingForLastMove(priority))
{
return false; return false;
}
Map* map = bot->GetMap(); Map* map = bot->GetMap();
const float x = bot->GetPositionX(); const float x = bot->GetPositionX();
@ -1160,6 +1159,11 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector<NewRpgStatus> candidateSta
bot->SetStandState(UNIT_STAND_STATE_SIT); bot->SetStandState(UNIT_STAND_STATE_SIT);
return true; return true;
} }
case RPG_OUTDOOR_PVP:
{
botAI->rpgInfo.ChangeToOutdoorPvp();
return true;
}
default: default:
{ {
botAI->rpgInfo.ChangeToRest(); botAI->rpgInfo.ChangeToRest();
@ -1220,6 +1224,17 @@ bool NewRpgBaseAction::CheckRpgStatusAvailable(NewRpgStatus status)
std::vector<uint32> path; std::vector<uint32> path;
return SelectRandomFlightTaxiNode(flightMaster, path); return SelectRandomFlightTaxiNode(flightMaster, path);
} }
case RPG_OUTDOOR_PVP:
{
if (!bot->IsPvP())
return false;
uint32 zoneId = bot->GetZoneId();
if (zoneId == AREA_NAGRAND)
return false;
OutdoorPvP* outdoorPvP = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneId);
return outdoorPvP != nullptr;
}
default: default:
return false; return false;
} }

View File

@ -31,7 +31,7 @@ protected:
/* MOVEMENT RELATED */ /* MOVEMENT RELATED */
bool MoveFarTo(WorldPosition dest); bool MoveFarTo(WorldPosition dest);
bool MoveWorldObjectTo(ObjectGuid guid, float distance = INTERACTION_DISTANCE); bool MoveWorldObjectTo(ObjectGuid guid, float distance = INTERACTION_DISTANCE);
bool MoveRandomNear(float moveStep = 50.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); bool MoveRandomNear(float moveStep = 50.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, WorldObject* center = nullptr);
bool ForceToWait(uint32 duration, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); bool ForceToWait(uint32 duration, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
/* QUEST RELATED CHECK */ /* QUEST RELATED CHECK */

View File

@ -0,0 +1,128 @@
#include "NewRpgOutdoorPvP.h"
#include "OutdoorPvP.h"
#include "OutdoorPvPMgr.h"
bool NewRpgOutdoorPvpAction::Execute(Event event)
{
if (!bot->IsPvP())
{
botAI->rpgInfo.ChangeToIdle();
return false;
}
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL) || !bot->IsOutdoorPvPActive())
return false;
uint32 zoneId = bot->GetZoneId();
OutdoorPvP* outdoorPvP = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneId);
if (!outdoorPvP || zoneId == AREA_NAGRAND)
{
botAI->rpgInfo.ChangeToIdle();
return false;
}
OutdoorPvP::OPvPCapturePointMap const& capturePointMap = outdoorPvP->GetCapturePoints();
NewRpgInfo& info = botAI->rpgInfo;
auto* dataPtr = std::get_if<NewRpgInfo::OutdoorPvP>(&info.data);
if (!dataPtr)
return false;
auto& data = *dataPtr;
// Re-resolve stored spawn ID from the capture point map each tick (avoids dangling pointers)
OPvPCapturePoint* objective = nullptr;
if (data.capturePointSpawnId && !capturePointMap.empty())
{
auto it = capturePointMap.find(data.capturePointSpawnId);
if (it != capturePointMap.end())
{
OPvPCapturePoint* capturePoint = it->second;
if (capturePoint && capturePoint->_capturePoint)
{
float threshold = capturePoint->GetMinValue();
float slider = capturePoint->GetSlider();
uint8 faction = bot->GetTeamId();
LOG_DEBUG("playerbots", "[NEW RPG] Bot {} with faction {} is evaluating existing RPG objective {} with threshold {} and slider value {}", bot->GetName(), faction, capturePoint->_capturePoint->GetName(), threshold, slider);
if ((faction == TEAM_HORDE && slider >= -threshold) ||
(faction == TEAM_ALLIANCE && slider <= threshold))
objective = capturePoint;
}
}
if (!objective)
data.capturePointSpawnId = 0;
}
if (!objective)
{
objective = SelectNewObjective(capturePointMap);
if (!objective)
{
botAI->rpgInfo.ChangeToIdle();
return true;
}
data.capturePointSpawnId = objective->m_capturePointSpawnId;
LOG_DEBUG("playerbots","[NEW RPG] Bot {} selected OutDoorPvP target capturePointSpawnId {}", bot->GetName(), data.capturePointSpawnId);
}
GameObject* objectiveGO = objective->_capturePoint;
if (!objectiveGO)
return false;
if (objectiveGO->GetGoType() != GAMEOBJECT_TYPE_CAPTURE_POINT)
return false;
float radius = objectiveGO->GetGOInfo()->capturePoint.radius / 2.0f;
if (!objectiveGO->IsWithinDistInMap(bot, radius))
return MoveFarTo(WorldPosition(objectiveGO));
return PatrolCapturePoint(objectiveGO, radius);
}
OPvPCapturePoint* NewRpgOutdoorPvpAction::SelectNewObjective(OutdoorPvP::OPvPCapturePointMap const& capturePointMap)
{
OPvPCapturePoint* objective = nullptr;
uint8 faction = bot->GetTeamId();
std::vector<OPvPCapturePoint*> candidateObjectives;
if (capturePointMap.empty())
{
botAI->rpgInfo.ChangeToIdle();
return objective;
}
for (auto const& [guid, point] : capturePointMap)
{
GameObject* capturePointObject = point->_capturePoint;
if (!capturePointObject)
continue;
float threshold = point->GetMinValue();
float slider = point->GetSlider();
if (faction == TEAM_HORDE && slider > -threshold)
candidateObjectives.push_back(point);
else if (faction == TEAM_ALLIANCE && slider < threshold)
candidateObjectives.push_back(point);
}
if (candidateObjectives.empty())
{
LOG_DEBUG("playerbots", "[New RPG] Bot {} found no valid outdoor PVP objectives to capture", bot->GetName());
botAI->rpgInfo.ChangeToIdle();
return objective;
}
int randomIndex = urand(0, candidateObjectives.size() - 1);
objective = candidateObjectives[randomIndex];
return objective;
}
bool NewRpgOutdoorPvpAction::PatrolCapturePoint(GameObject* objectiveGO, float radius)
{
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
return false;
// Randomly pause at the current spot before picking a new patrol point
if (urand(0, 2) == 0)
return ForceToWait(urand(3000, 6000));
float patrolRadius = radius * 0.8f;
if (MoveRandomNear(patrolRadius, MovementPriority::MOVEMENT_NORMAL, objectiveGO))
return true;
return ForceToWait(urand(3000, 6000));
}

View File

@ -0,0 +1,19 @@
#ifndef PLAYERBOT_NEWRPGOUTDOORPVP_H
#define PLAYERBOT_NEWRPGOUTDOORPVP_H
#include "NewRpgBaseAction.h"
#include "OutdoorPvP.h"
class NewRpgOutdoorPvpAction : public NewRpgBaseAction
{
public:
NewRpgOutdoorPvpAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg outdoor pvp") {}
virtual bool Execute(Event event) override;
OPvPCapturePoint* SelectNewObjective(OutdoorPvP::OPvPCapturePointMap const& capturePointMap);
private:
bool PatrolCapturePoint(GameObject* objectiveGO, float radius);
};
#endif

View File

@ -47,6 +47,14 @@ void NewRpgInfo::ChangeToTravelFlight(ObjectGuid fromFlightMaster, std::vector<u
data = flight; data = flight;
} }
void NewRpgInfo::ChangeToOutdoorPvp(ObjectGuid::LowType capturePointSpawnId)
{
startT = getMSTime();
OutdoorPvP pvp;
pvp.capturePointSpawnId = capturePointSpawnId;
data = pvp;
}
void NewRpgInfo::ChangeToRest() void NewRpgInfo::ChangeToRest()
{ {
startT = getMSTime(); startT = getMSTime();
@ -90,6 +98,7 @@ NewRpgStatus NewRpgInfo::GetStatus()
if constexpr (std::is_same_v<T, Rest>) return RPG_REST; if constexpr (std::is_same_v<T, Rest>) return RPG_REST;
if constexpr (std::is_same_v<T, DoQuest>) return RPG_DO_QUEST; if constexpr (std::is_same_v<T, DoQuest>) return RPG_DO_QUEST;
if constexpr (std::is_same_v<T, TravelFlight>) return RPG_TRAVEL_FLIGHT; if constexpr (std::is_same_v<T, TravelFlight>) return RPG_TRAVEL_FLIGHT;
if constexpr (std::is_same_v<T, OutdoorPvP>) return RPG_OUTDOOR_PVP;
return RPG_IDLE; return RPG_IDLE;
}, data); }, data);
} }
@ -153,6 +162,14 @@ std::string NewRpgInfo::ToString()
out << "\ntoNode: " << arg.path[arg.path.size() - 1]; out << "\ntoNode: " << arg.path[arg.path.size() - 1];
out << "\ninFlight: " << arg.inFlight; out << "\ninFlight: " << arg.inFlight;
} }
else if constexpr (std::is_same_v<T, OutdoorPvP>)
{
out << "OUTDOOR_PVP";
if (!arg.capturePointSpawnId)
out << "\nNo capture point assigned.";
else
out << "\ncapturePointSpawnId: " << arg.capturePointSpawnId;
}
else else
out << "UNKNOWN"; out << "UNKNOWN";
}, data); }, data);

View File

@ -58,6 +58,11 @@ struct NewRpgInfo
{ {
Rest() = default; Rest() = default;
}; };
// RPG_OUTDOOR_PVP
struct OutdoorPvP
{
ObjectGuid::LowType capturePointSpawnId{0};
};
struct Idle struct Idle
{ {
}; };
@ -79,7 +84,8 @@ struct NewRpgInfo
WanderRandom, WanderRandom,
DoQuest, DoQuest,
Rest, Rest,
TravelFlight TravelFlight,
OutdoorPvP
>; >;
RpgData data; RpgData data;
@ -91,6 +97,7 @@ struct NewRpgInfo
void ChangeToWanderRandom(); void ChangeToWanderRandom();
void ChangeToDoQuest(uint32 questId, const Quest* quest); void ChangeToDoQuest(uint32 questId, const Quest* quest);
void ChangeToTravelFlight(ObjectGuid fromFlightMaster, std::vector<uint32> path); void ChangeToTravelFlight(ObjectGuid fromFlightMaster, std::vector<uint32> path);
void ChangeToOutdoorPvp(ObjectGuid::LowType capturePointSpawnId = 0);
void ChangeToRest(); void ChangeToRest();
void ChangeToIdle(); void ChangeToIdle();
bool CanChangeTo(NewRpgStatus status); bool CanChangeTo(NewRpgStatus status);

View File

@ -65,6 +65,14 @@ void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
} }
) )
); );
triggers.push_back(
new TriggerNode(
"outdoor pvp status",
{
NextAction("new rpg outdoor pvp", 3.0f)
}
)
);
} }
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>&) void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>&)

View File

@ -2861,10 +2861,10 @@ void RandomPlayerbotMgr::PrintStats()
LOG_INFO("playerbots", "Bots rpg status:"); LOG_INFO("playerbots", "Bots rpg status:");
LOG_INFO("playerbots", LOG_INFO("playerbots",
" Idle: {}, Rest: {}, GoGrind: {}, GoCamp: {}, MoveRandom: {}, MoveNpc: {}, DoQuest: {}, " " Idle: {}, Rest: {}, GoGrind: {}, GoCamp: {}, MoveRandom: {}, MoveNpc: {}, DoQuest: {}, "
"TravelFlight: {}", "TravelFlight: {}, OutdoorPvP: {}",
rpgStatusCount[RPG_IDLE], rpgStatusCount[RPG_REST], rpgStatusCount[RPG_GO_GRIND], rpgStatusCount[RPG_IDLE], rpgStatusCount[RPG_REST], rpgStatusCount[RPG_GO_GRIND],
rpgStatusCount[RPG_GO_CAMP], rpgStatusCount[RPG_WANDER_RANDOM], rpgStatusCount[RPG_WANDER_NPC], rpgStatusCount[RPG_GO_CAMP], rpgStatusCount[RPG_WANDER_RANDOM], rpgStatusCount[RPG_WANDER_NPC],
rpgStatusCount[RPG_DO_QUEST], rpgStatusCount[RPG_TRAVEL_FLIGHT]); rpgStatusCount[RPG_DO_QUEST], rpgStatusCount[RPG_TRAVEL_FLIGHT], rpgStatusCount[RPG_OUTDOOR_PVP]);
LOG_INFO("playerbots", "Bots total quests:"); LOG_INFO("playerbots", "Bots total quests:");
LOG_INFO("playerbots", " Accepted: {}, Rewarded: {}, Dropped: {}", rpgStasticTotal.questAccepted, LOG_INFO("playerbots", " Accepted: {}, Rewarded: {}, Dropped: {}", rpgStasticTotal.questAccepted,

View File

@ -652,6 +652,7 @@ bool PlayerbotAIConfig::Initialize()
RpgStatusProbWeight[RPG_DO_QUEST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.DoQuest", 60); RpgStatusProbWeight[RPG_DO_QUEST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.DoQuest", 60);
RpgStatusProbWeight[RPG_TRAVEL_FLIGHT] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.TravelFlight", 15); RpgStatusProbWeight[RPG_TRAVEL_FLIGHT] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.TravelFlight", 15);
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5); RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
RpgStatusProbWeight[RPG_OUTDOOR_PVP] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.OutdoorPvp", 10);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false); syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", false); randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", false);

View File

@ -56,7 +56,8 @@ enum NewRpgStatus : int
RPG_TRAVEL_FLIGHT = 6, RPG_TRAVEL_FLIGHT = 6,
// Taking a break // Taking a break
RPG_REST = 7, RPG_REST = 7,
RPG_STATUS_END = 8 RPG_OUTDOOR_PVP = 8,
RPG_STATUS_END = 9
}; };
#define MAX_SPECNO 20 #define MAX_SPECNO 20