mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
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:
parent
74ccc6fbe9
commit
53a607e147
@ -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
|
||||||
|
|||||||
@ -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); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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); }
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 */
|
||||||
|
|||||||
128
src/Ai/World/Rpg/Action/NewRpgOutdoorPvP.cpp
Normal file
128
src/Ai/World/Rpg/Action/NewRpgOutdoorPvP.cpp
Normal 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));
|
||||||
|
}
|
||||||
19
src/Ai/World/Rpg/Action/NewRpgOutdoorPvP.h
Normal file
19
src/Ai/World/Rpg/Action/NewRpgOutdoorPvP.h
Normal 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
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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*>&)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user