Merge pull request #759 from liyunfan1223/new_rpg_strats

New rpg strategy
This commit is contained in:
Yunfan Li
2024-12-17 20:43:38 +08:00
committed by GitHub
41 changed files with 1046 additions and 190 deletions

View File

@@ -31,6 +31,7 @@
#include "MeleeCombatStrategy.h"
#include "MoveFromGroupStrategy.h"
#include "NamedObjectContext.h"
#include "NewRpgStrategy.h"
#include "NonCombatStrategy.h"
#include "PassiveStrategy.h"
#include "PullStrategy.h"
@@ -82,6 +83,7 @@ public:
creators["reveal"] = &StrategyContext::reveal;
creators["collision"] = &StrategyContext::collision;
creators["rpg"] = &StrategyContext::rpg;
creators["new rpg"] = &StrategyContext::new_rpg;
creators["travel"] = &StrategyContext::travel;
creators["explore"] = &StrategyContext::explore;
creators["map"] = &StrategyContext::map;
@@ -152,6 +154,7 @@ private:
static Strategy* reveal(PlayerbotAI* botAI) { return new RevealStrategy(botAI); }
static Strategy* collision(PlayerbotAI* botAI) { return new CollisionStrategy(botAI); }
static Strategy* rpg(PlayerbotAI* botAI) { return new RpgStrategy(botAI); }
static Strategy* new_rpg(PlayerbotAI* botAI) { return new NewRpgStrategy(botAI); }
static Strategy* travel(PlayerbotAI* botAI) { return new TravelStrategy(botAI); }
static Strategy* explore(PlayerbotAI* botAI) { return new ExploreStrategy(botAI); }
static Strategy* map(PlayerbotAI* botAI) { return new MapStrategy(botAI); }

View File

@@ -62,6 +62,7 @@
#include "VehicleActions.h"
#include "WorldBuffAction.h"
#include "XpGainAction.h"
#include "NewRpgAction.h"
class PlayerbotAI;
@@ -240,6 +241,12 @@ public:
creators["toggle pet spell"] = &ActionContext::toggle_pet_spell;
creators["pet attack"] = &ActionContext::pet_attack;
creators["new rpg status update"] = &ActionContext::new_rpg_status_update;
creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind;
creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper;
creators["new rpg move random"] = &ActionContext::new_rpg_move_random;
creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc;
}
private:
@@ -415,6 +422,12 @@ private:
static Action* toggle_pet_spell(PlayerbotAI* ai) { return new TogglePetSpellAutoCastAction(ai); }
static Action* pet_attack(PlayerbotAI* ai) { return new PetAttackAction(ai); }
static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); }
static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); }
static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); }
static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); }
static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); }
};
#endif

View File

@@ -38,6 +38,7 @@
#include "LootStrategyAction.h"
#include "MailAction.h"
#include "NamedObjectContext.h"
#include "NewRpgAction.h"
#include "PassLeadershipToMasterAction.h"
#include "PositionAction.h"
#include "QueryItemUsageAction.h"
@@ -88,6 +89,7 @@ public:
creators["reputation"] = &ChatActionContext::reputation;
creators["log"] = &ChatActionContext::log;
creators["los"] = &ChatActionContext::los;
creators["rpg status"] = &ChatActionContext::rpg_status;
creators["aura"] = &ChatActionContext::aura;
creators["drop"] = &ChatActionContext::drop;
creators["clean quest log"] = &ChatActionContext::clean_quest_log;
@@ -258,6 +260,7 @@ private:
static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); }
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
static Action* aura(PlayerbotAI* ai) { return new TellAuraAction(ai); }
static Action* ll(PlayerbotAI* botAI) { return new LootStrategyAction(botAI); }
static Action* ss(PlayerbotAI* botAI) { return new SkipSpellsListAction(botAI); }

View File

@@ -177,7 +177,7 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged)
}
bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool react, bool normal_only,
bool exact_waypoint, MovementPriority priority)
bool exact_waypoint, MovementPriority priority, bool lessDelay)
{
UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z))
@@ -210,6 +210,10 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN));
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -233,6 +237,10 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -264,6 +272,10 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, endP.x, endP.y, endP.z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -822,7 +834,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
PathGenerator path(bot);
path.CalculatePath(tx, ty, tz, false);
PathType type = path.GetPathType();
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE | PATHFIND_SHORTCUT;
if (!(type & typeOk))
return false;
float shortenTo = distance;
@@ -838,7 +850,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo);
G3D::Vector3 endPos = path.GetPath().back();
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
MovementPriority::MOVEMENT_COMBAT, true);
}
float MovementAction::GetFollowAngle()
@@ -2013,8 +2025,15 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
}
bool strict = checkAngle.strict;
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()};
float dx = bot->GetPositionX() + cos(angle) * fleeDis;
float dy = bot->GetPositionY() + sin(angle) * fleeDis;
float dz = bot->GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
continue;
}
Position fleePos{dx, dy, dz};
if (strict && currentTarget &&
fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() >
sPlayerbotAIConfig->tooCloseDistance &&
@@ -2069,8 +2088,15 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
}
bool strict = checkAngle.strict;
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()};
float dx = bot->GetPositionX() + cos(angle) * fleeDis;
float dy = bot->GetPositionY() + sin(angle) * fleeDis;
float dz = bot->GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
continue;
}
Position fleePos{dx, dy, dz};
if (strict && currentTarget &&
fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->spellDistance)
{
@@ -2082,6 +2108,7 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
{
continue;
}
if (pos.GetExactDist(fleePos) > farestDis)
{
farestDis = pos.GetExactDist(fleePos);

View File

@@ -32,7 +32,7 @@ protected:
bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveToLOS(WorldObject* target, bool ranged = false);
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, bool lessDelay = false);
bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
float GetFollowAngle();

View File

@@ -188,7 +188,7 @@ bool FindCorpseAction::Execute(Event event)
if (!moved)
{
moved = botAI->DoSpecificAction("spirit healer");
moved = botAI->DoSpecificAction("spirit healer", Event(), true);
}
}
}
@@ -347,16 +347,16 @@ bool SpiritHealerAction::Execute(Event event)
if (moved)
return true;
if (!botAI->HasActivePlayerMaster())
{
context->GetValue<uint32>("death count")->Set(dCount + 1);
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
}
// if (!botAI->HasActivePlayerMaster())
// {
context->GetValue<uint32>("death count")->Set(dCount + 1);
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
// }
LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str());
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
// bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str());
botAI->TellError("Cannot find any spirit healer nearby");
// botAI->TellError("Cannot find any spirit healer nearby");
return false;
}

View File

@@ -101,7 +101,7 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
// ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
// enemy contact", ACTION_NORMAL + 8), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -106,6 +106,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("reputation");
supported.push_back("log");
supported.push_back("los");
supported.push_back("rpg status");
supported.push_back("aura");
supported.push_back("drop");
supported.push_back("share");

View File

@@ -22,7 +22,7 @@ void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_MOVE + 7), nullptr)));
triggers.push_back(
new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", ACTION_NORMAL), nullptr)));
new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", 40.0f), nullptr)));
// triggers.push_back(new TriggerNode("combat long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f),
// new NextAction("repop", 0.8f), nullptr)));
}

View File

@@ -11,10 +11,10 @@ NextAction** GrindingStrategy::getDefaultActions() { return nullptr; }
void GrindingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("drink", 4.2f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("food", 4.1f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("drink", 10.2f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("food", 10.1f), nullptr)));
triggers.push_back(
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 4.0f), nullptr)));
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 10.0f), nullptr)));
}
void MoveRandomStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -14,7 +14,7 @@ void MeleeCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
// ACTION_MOVE + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
// enemy contact", ACTION_NORMAL + 8), nullptr)));
}

View File

@@ -59,8 +59,9 @@ FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(b
NextAction** FrostMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.1f),
new NextAction("shoot", ACTION_DEFAULT), nullptr);
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.2f),
new NextAction("shoot", ACTION_DEFAULT + 0.1f),
new NextAction("fireball", ACTION_DEFAULT), nullptr);
}
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -131,5 +131,5 @@ void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL)));
triggers.push_back(new TriggerNode("enemy out of melee",
NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), NULL)));
NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), NULL)));
}

View File

@@ -113,5 +113,5 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
}

View File

@@ -88,6 +88,6 @@ void AssassinationRogueStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back(new TriggerNode(
"enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8),
new NextAction("reach melee", ACTION_NORMAL + 7), NULL)));
NextAction::array(0, new NextAction("stealth", ACTION_HIGH + 3), new NextAction("sprint", ACTION_HIGH + 2),
new NextAction("reach melee", ACTION_HIGH + 1), NULL)));
}

View File

@@ -134,8 +134,8 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8),
new NextAction("reach melee", ACTION_NORMAL + 7), nullptr)));
NextAction::array(0, new NextAction("stealth", ACTION_HIGH + 3), new NextAction("sprint", ACTION_HIGH + 2),
new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode("expose armor",
NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr)));

View File

@@ -0,0 +1,380 @@
#include "NewRpgAction.h"
#include <cmath>
#include <cstdint>
#include "NewRpgStrategy.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
#include "PathGenerator.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "Random.h"
#include "RandomPlayerbotMgr.h"
#include "Timer.h"
#include "TravelMgr.h"
#include "World.h"
bool TellRpgStatusAction::Execute(Event event)
{
std::string out = botAI->rpgInfo.ToString();
botAI->TellMasterNoFacing(out);
return true;
}
bool NewRpgStatusUpdateAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
switch (info.status)
{
case NewRpgStatus::IDLE:
{
uint32 roll = urand(1, 100);
// IDLE -> NEAR_NPC
// if ((!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime()) && roll <= 30)
if (roll <= 30)
{
info.lastNearNpc = getMSTime();
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
if (!possibleTargets.empty())
{
info.status = NewRpgStatus::NEAR_NPC;
return true;
}
}
// IDLE -> GO_INNKEEPER
else if (bot->GetLevel() >= 6 && roll <= 40)
{
WorldPosition pos = SelectRandomInnKeeperPos();
if (pos != WorldPosition() && bot->GetExactDist(pos) > 50.0f)
{
info.lastGoInnKeeper = getMSTime();
info.status = NewRpgStatus::GO_INNKEEPER;
info.innKeeperPos = pos;
return true;
}
}
// IDLE -> GO_GRIND
else if (roll <= 90)
{
WorldPosition pos = SelectRandomGrindPos();
if (pos != WorldPosition())
{
info.lastGoGrind = getMSTime();
info.status = NewRpgStatus::GO_GRIND;
info.grindPos = pos;
return true;
}
}
// IDLE -> REST
info.status = NewRpgStatus::REST;
info.lastRest = getMSTime();
bot->SetStandState(UNIT_STAND_STATE_SIT);
return true;
}
case NewRpgStatus::GO_GRIND:
{
WorldPosition& originalPos = info.grindPos;
assert(info.grindPos != WorldPosition());
// GO_GRIND -> NEAR_RANDOM
if (bot->GetExactDist(originalPos) < 10.0f)
{
info.status = NewRpgStatus::NEAR_RANDOM;
info.lastNearRandom = getMSTime();
info.grindPos = WorldPosition();
return true;
}
// // just choose another grindPos
// if (!info.lastGoGrind || info.lastGoGrind + setGrindInterval < getMSTime())
// {
// WorldPosition pos = SelectRandomGrindPos();
// if (pos == WorldPosition())
// break;
// info.status = NewRpgStatus::GO_GRIND;
// info.lastGoGrind = getMSTime();
// info.grindPos = pos;
// return true;
// }
break;
}
case NewRpgStatus::GO_INNKEEPER:
{
WorldPosition& originalPos = info.innKeeperPos;
assert(info.innKeeperPos != WorldPosition());
// GO_INNKEEPER -> NEAR_NPC
if (bot->GetExactDist(originalPos) < 10.0f)
{
info.lastNearNpc = getMSTime();
info.status = NewRpgStatus::NEAR_NPC;
info.innKeeperPos = WorldPosition();
return true;
}
break;
}
case NewRpgStatus::NEAR_RANDOM:
{
// NEAR_RANDOM -> IDLE
if (info.lastNearRandom + statusNearRandomDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
case NewRpgStatus::NEAR_NPC:
{
if (info.lastNearNpc + statusNearNpcDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
case NewRpgStatus::REST:
{
// REST -> IDLE
if (info.lastRest + statusRestDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
default:
break;
}
return false;
}
WorldPosition NewRpgStatusUpdateAction::SelectRandomGrindPos()
{
const std::vector<WorldLocation>& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()];
std::vector<WorldLocation> lo_prepared_locs, hi_prepared_locs;
for (auto& loc : locs)
{
if (bot->GetMapId() != loc.GetMapId())
continue;
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
bot->GetZoneId())
continue;
if (bot->GetExactDist(loc) < 500.0f)
{
hi_prepared_locs.push_back(loc);
}
if (bot->GetExactDist(loc) < 2500.0f)
{
lo_prepared_locs.push_back(loc);
}
}
WorldPosition dest;
if (urand(1, 100) <= 50 && !hi_prepared_locs.empty())
{
uint32 idx = urand(0, hi_prepared_locs.size() - 1);
dest = hi_prepared_locs[idx];
}
else if (!lo_prepared_locs.empty())
{
uint32 idx = urand(0, lo_prepared_locs.size() - 1);
dest = lo_prepared_locs[idx];
}
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random grind pos Map:{} X:{} Y:{} Z:{} ({}+{} available in {})",
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
hi_prepared_locs.size(), lo_prepared_locs.size() - hi_prepared_locs.size(), locs.size());
return dest;
}
WorldPosition NewRpgStatusUpdateAction::SelectRandomInnKeeperPos()
{
const std::vector<WorldLocation>& locs = IsAlliance(bot->getRace())
? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()]
: sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()];
std::vector<WorldLocation> prepared_locs;
for (auto& loc : locs)
{
if (bot->GetMapId() != loc.GetMapId())
continue;
float range = bot->GetLevel() <= 5 ? 500.0f : 2500.0f;
if (bot->GetExactDist(loc) < range)
{
prepared_locs.push_back(loc);
}
}
WorldPosition dest;
if (!prepared_locs.empty())
{
uint32 idx = urand(0, prepared_locs.size() - 1);
dest = prepared_locs[idx];
}
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random inn keeper pos Map:{} X:{} Y:{} Z:{} ({} available in {})",
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
prepared_locs.size(), locs.size());
return dest;
}
bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest)
{
float dis = bot->GetExactDist(dest);
if (dis < pathFinderDis)
{
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
false, true);
}
// performance optimization
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
{
return false;
}
float minDelta = M_PI;
const float x = bot->GetPositionX();
const float y = bot->GetPositionY();
const float z = bot->GetPositionZ();
float rx, ry, rz;
bool found = false;
int attempt = 10;
while (--attempt)
{
float angle = bot->GetAngle(&dest);
float delta = (rand_norm() - 0.5) * M_PI * 2;
angle += delta;
float dis = rand_norm() * pathFinderDis;
float dx = x + cos(angle) * dis;
float dy = y + sin(angle) * dis;
float dz = z + 5.0f;
bot->UpdateAllowedPositionZ(dx, dy, dz);
PathGenerator path(bot);
path.CalculatePath(dx, dy, dz);
PathType type = path.GetPathType();
bool canReach = type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL;
if (canReach && fabs(delta) <= minDelta)
{
found = true;
const G3D::Vector3& endPos = path.GetActualEndPosition();
rx = endPos.x;
ry = endPos.y;
rz = endPos.z;
minDelta = fabs(delta);
}
}
if (found)
{
return MoveTo(bot->GetMapId(), rx, ry, rz, false, false, false, true);
}
// don't fallback to direct move
// float angle = bot->GetAngle(&dest);
// return MoveTo(bot->GetMapId(), x + cos(angle) * pathFinderDis, y + sin(angle) * pathFinderDis, z);
return false;
}
bool NewRpgGoGrindAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.grindPos); }
bool NewRpgGoInnKeeperAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.innKeeperPos); }
bool NewRpgMoveRandomAction::Execute(Event event)
{
float distance = rand_norm() * moveStep;
Map* map = bot->GetMap();
const float x = bot->GetPositionX();
const float y = bot->GetPositionY();
const float z = bot->GetPositionZ();
int attempts = 5;
while (--attempts)
{
float angle = (float)rand_norm() * 2 * static_cast<float>(M_PI);
float dx = x + distance * cos(angle);
float dy = y + distance * sin(angle);
float dz = z;
if (!map->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
dx, dy, dz))
continue;
if (map->IsInWater(bot->GetPhaseMask(), dx, dy, dz, bot->GetCollisionHeight()))
continue;
bool moved = MoveTo(bot->GetMapId(), dx, dy, dz, false, false, false, true);
if (moved)
return true;
}
return false;
}
bool NewRpgMoveNpcAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
if (!info.npcPos)
{
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
if (possibleTargets.empty())
return false;
int idx = urand(0, possibleTargets.size() - 1);
ObjectGuid guid = possibleTargets[idx];
Unit* unit = botAI->GetUnit(guid);
if (unit)
{
info.npcPos = GuidPosition(unit);
info.lastReachNpc = 0;
}
else
return false;
}
if (bot->GetDistance(info.npcPos) <= INTERACTION_DISTANCE)
{
if (!info.lastReachNpc)
{
info.lastReachNpc = getMSTime();
return true;
}
if (info.lastReachNpc && info.lastReachNpc + stayTime > getMSTime())
return false;
info.npcPos = GuidPosition();
info.lastReachNpc = 0;
}
else
{
assert(info.npcPos);
Unit* unit = botAI->GetUnit(info.npcPos);
if (!unit)
return false;
float x = unit->GetPositionX();
float y = unit->GetPositionY();
float z = unit->GetPositionZ();
float mapId = unit->GetMapId();
float angle = 0.f;
if (bot->IsWithinLOS(x, y, z))
{
if (!unit->isMoving())
angle = unit->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target
else
angle = unit->GetOrientation() +
(M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement)
}
else
angle = 2 * M_PI * rand_norm(); // A circle around the target.
float rnd = rand_norm();
x += cos(angle) * INTERACTION_DISTANCE * rnd;
y += sin(angle) * INTERACTION_DISTANCE * rnd;
// bool exact = true;
if (!unit->GetMap()->CheckCollisionAndGetValidCoords(unit, unit->GetPositionX(), unit->GetPositionY(),
unit->GetPositionZ(), x, y, z))
{
x = unit->GetPositionX();
y = unit->GetPositionY();
z = unit->GetPositionZ();
// exact = false;
}
return MoveTo(mapId, x, y, z, false, false, false, true);
}
return true;
}

View File

@@ -0,0 +1,78 @@
#ifndef _PLAYERBOT_NEWRPGACTION_H
#define _PLAYERBOT_NEWRPGACTION_H
#include "Duration.h"
#include "MovementActions.h"
#include "NewRpgStrategy.h"
#include "TravelMgr.h"
#include "PlayerbotAI.h"
class TellRpgStatusAction : public Action
{
public:
TellRpgStatusAction(PlayerbotAI* botAI) : Action(botAI, "rpg status") {}
bool Execute(Event event) override;
};
class NewRpgStatusUpdateAction : public Action
{
public:
NewRpgStatusUpdateAction(PlayerbotAI* botAI) : Action(botAI, "new rpg status update") {}
bool Execute(Event event) override;
protected:
// const int32 setGrindInterval = 5 * 60 * 1000;
// const int32 setNpcInterval = 1 * 60 * 1000;
const int32 statusNearNpcDuration = 2 * 60 * 1000;
const int32 statusNearRandomDuration = 2 * 60 * 1000;
const int32 statusRestDuration = 30 * 1000;
WorldPosition SelectRandomGrindPos();
WorldPosition SelectRandomInnKeeperPos();
};
class NewRpgGoFarAwayPosAction : public MovementAction
{
public:
NewRpgGoFarAwayPosAction(PlayerbotAI* botAI, std::string name) : MovementAction(botAI, name) {}
// bool Execute(Event event) override;
bool MoveFarTo(WorldPosition dest);
protected:
// WorldPosition dest;
float pathFinderDis = 70.0f; // path finder
};
class NewRpgGoGrindAction : public NewRpgGoFarAwayPosAction
{
public:
NewRpgGoGrindAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go grind") {}
bool Execute(Event event) override;
};
class NewRpgGoInnKeeperAction : public NewRpgGoFarAwayPosAction
{
public:
NewRpgGoInnKeeperAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go innkeeper") {}
bool Execute(Event event) override;
};
class NewRpgMoveRandomAction : public MovementAction
{
public:
NewRpgMoveRandomAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move random") {}
bool Execute(Event event) override;
protected:
const float moveStep = 50.0f;
};
class NewRpgMoveNpcAction : public MovementAction
{
public:
NewRpgMoveNpcAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move npcs") {}
bool Execute(Event event) override;
protected:
const uint32 stayTime = 8 * 1000;
};
#endif

View File

@@ -0,0 +1,35 @@
/*
* 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 "NewRpgStrategy.h"
#include "Playerbots.h"
NewRpgStrategy::NewRpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
NextAction** NewRpgStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("new rpg status update", 5.0f), nullptr);
}
void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("go grind status", NextAction::array(0, new NextAction("new rpg go grind", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("go innkeeper status", NextAction::array(0, new NextAction("new rpg go innkeeper", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("near random status", NextAction::array(0, new NextAction("new rpg move random", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("near npc status", NextAction::array(0, new NextAction("new rpg move npc", 1.0f), nullptr)));
}
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
// multipliers.push_back(new RpgActionMultiplier(botAI));
}

View File

@@ -0,0 +1,98 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_NEWRPGSTRATEGY_H
#define _PLAYERBOT_NEWRPGSTRATEGY_H
#include <cstdint>
#include "Strategy.h"
#include "TravelMgr.h"
class PlayerbotAI;
enum class NewRpgStatus
{
// Going to far away place
GO_GRIND,
GO_INNKEEPER,
// Exploring nearby
NEAR_RANDOM,
NEAR_NPC,
// Taking a break
REST,
// Initial status
IDLE
};
struct NewRpgInfo
{
NewRpgStatus status{NewRpgStatus::IDLE};
// NewRpgStatus::GO_GRIND
WorldPosition grindPos{};
uint32 lastGoGrind{0};
// NewRpgStatus::GO_INNKEEPER
WorldPosition innKeeperPos{};
uint32 lastGoInnKeeper{0};
// NewRpgStatus::NEAR_NPC
GuidPosition npcPos{};
uint32 lastNearNpc{0};
uint32 lastReachNpc{0};
// NewRpgStatus::NEAR_RANDOM
uint32 lastNearRandom{0};
// NewRpgStatus::REST
uint32 lastRest{0};
std::string ToString()
{
std::stringstream out;
out << "Status: ";
switch (status)
{
case NewRpgStatus::GO_GRIND:
out << "GO_GRIND";
out << "\nGrindPos: " << grindPos.GetMapId() << " " << grindPos.GetPositionX() << " " << grindPos.GetPositionY() << " " << grindPos.GetPositionZ();
out << "\nlastGoGrind: " << lastGoGrind;
break;
case NewRpgStatus::GO_INNKEEPER:
out << "GO_INNKEEPER";
out << "\nInnKeeperPos: " << innKeeperPos.GetMapId() << " " << innKeeperPos.GetPositionX() << " " << innKeeperPos.GetPositionY() << " " << innKeeperPos.GetPositionZ();
out << "\nlastGoInnKeeper: " << lastGoInnKeeper;
break;
case NewRpgStatus::NEAR_NPC:
out << "NEAR_NPC";
out << "\nNpcPos: " << npcPos.GetMapId() << " " << npcPos.GetPositionX() << " " << npcPos.GetPositionY() << " " << npcPos.GetPositionZ();
out << "\nlastNearNpc: " << lastNearNpc;
out << "\nlastReachNpc: " << lastReachNpc;
break;
case NewRpgStatus::NEAR_RANDOM:
out << "NEAR_RANDOM";
out << "\nlastNearRandom: " << lastNearRandom;
break;
case NewRpgStatus::IDLE:
out << "IDLE";
break;
case NewRpgStatus::REST:
out << "REST";
out << "\nlastRest: " << lastRest;
break;
default:
out << "UNKNOWN";
}
return out.str();
}
};
class NewRpgStrategy : public Strategy
{
public:
NewRpgStrategy(PlayerbotAI* botAI);
std::string const getName() override { return "new rpg"; }
NextAction** getDefaultActions() override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@@ -0,0 +1,4 @@
#include "NewRpgTriggers.h"
#include "PlayerbotAI.h"
bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; }

View File

@@ -0,0 +1,20 @@
#ifndef _PLAYERBOT_NEWRPGTRIGGERS_H
#define _PLAYERBOT_NEWRPGTRIGGERS_H
#include "NewRpgStrategy.h"
#include "Trigger.h"
class NewRpgStatusTrigger : public Trigger
{
public:
NewRpgStatusTrigger(PlayerbotAI* botAI, NewRpgStatus status = NewRpgStatus::IDLE)
: Trigger(botAI, "new rpg status"), status(status)
{
}
bool IsActive() override;
protected:
NewRpgStatus status;
};
#endif

View File

@@ -50,7 +50,7 @@ void HealShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr)));
triggers.push_back(new TriggerNode(
"group heal setting",
NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal on party", 22.0f), NULL)));
NextAction::array(0, new NextAction("riptide on party", 27.0f), new NextAction("chain heal on party", 26.0f), NULL)));
triggers.push_back(new TriggerNode(
"party member critical health",

View File

@@ -77,7 +77,7 @@ void MeleeShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"no fire totem",

View File

@@ -24,6 +24,7 @@ public:
creators["reputation"] = &ChatTriggerContext::reputation;
creators["log"] = &ChatTriggerContext::log;
creators["los"] = &ChatTriggerContext::los;
creators["rpg status"] = &ChatTriggerContext::rpg_status;
creators["aura"] = &ChatTriggerContext::aura;
creators["drop"] = &ChatTriggerContext::drop;
creators["share"] = &ChatTriggerContext::share;
@@ -211,6 +212,7 @@ private:
static Trigger* reputation(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "reputation"); }
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
static Trigger* aura(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "aura"); }
static Trigger* loot_all(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "add all loot"); }
static Trigger* release(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "release"); }

View File

@@ -12,6 +12,8 @@
#include "LfgTriggers.h"
#include "LootTriggers.h"
#include "NamedObjectContext.h"
#include "NewRpgStrategy.h"
#include "NewRpgTriggers.h"
#include "PvpTriggers.h"
#include "RaidNaxxTriggers.h"
#include "RpgTriggers.h"
@@ -213,6 +215,10 @@ public:
creators["rpg craft"] = &TriggerContext::rpg_craft;
creators["rpg trade useful"] = &TriggerContext::rpg_trade_useful;
creators["rpg duel"] = &TriggerContext::rpg_duel;
creators["go grind status"] = &TriggerContext::go_grind_status;
creators["go innkeeper status"] = &TriggerContext::go_innkeeper_status;
creators["near random status"] = &TriggerContext::near_random_status;
creators["near npc status"] = &TriggerContext::near_npc_status;
}
private:
@@ -402,6 +408,10 @@ private:
static Trigger* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftTrigger(botAI); }
static Trigger* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulTrigger(botAI); }
static Trigger* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelTrigger(botAI); }
static Trigger* go_grind_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_GRIND); }
static Trigger* go_innkeeper_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_INNKEEPER); }
static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_RANDOM); }
static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_NPC); }
};
#endif

View File

@@ -298,8 +298,9 @@ Unit* DpsTargetValue::Calculate()
return rti;
// FindLeastHpTargetStrategy strategy(botAI);
Group* group = bot->GetGroup();
float dps = AI_VALUE(float, "estimated group dps");
if (botAI->IsCaster(bot))
if (group && botAI->IsCaster(bot))
{
CasterFindTargetSmartStrategy strategy(botAI, dps);
return TargetValue::FindTarget(&strategy);

View File

@@ -52,7 +52,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
float distance = 0;
Unit* result = nullptr;
std::unordered_map<uint32, bool> needForQuestMap;
// std::unordered_map<uint32, bool> needForQuestMap;
for (ObjectGuid const guid : targets)
{
@@ -99,18 +99,18 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
continue;
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
// if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
// needForQuestMap[unit->GetEntry()] = needForQuest(unit);
if (!needForQuestMap[unit->GetEntry()])
{
Creature* creature = dynamic_cast<Creature*>(unit);
if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
{
continue;
}
}
// if (!needForQuestMap[unit->GetEntry()])
// {
// Creature* creature = dynamic_cast<Creature*>(unit);
// if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
// context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
// {
// continue;
// }
// }
if (Creature* creature = unit->ToCreature())
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())
@@ -142,8 +142,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
else
{
float newdistance = bot->GetDistance(unit);
if (!result || (newdistance < distance &&
urand(0, abs(distance - newdistance)) > sPlayerbotAIConfig->sightDistance * 0.1))
if (!result || (newdistance < distance))
{
distance = newdistance;
result = unit;

View File

@@ -14,7 +14,7 @@ class PlayerbotAI;
class PossibleRpgTargetsValue : public NearestUnitsValue
{
public:
PossibleRpgTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->rpgDistance);
PossibleRpgTargetsValue(PlayerbotAI* botAI, float range = 70.0f);
static std::vector<uint32> allowedNpcFlags;

View File

@@ -48,7 +48,7 @@ private:
{
return new ActionNode("summon succubus",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("summon imp"), nullptr),
/*A*/ NextAction::array(0, new NextAction("summon voidwalker"), nullptr),
/*C*/ nullptr);
}
static ActionNode* summon_felhunter([[maybe_unused]] PlayerbotAI* botAI)

View File

@@ -16,7 +16,7 @@ void GenericWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
/*triggers.push_back(new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 1),
nullptr))); triggers.push_back(new TriggerNode("shield bash", NextAction::array(0, new NextAction("shield bash",
ACTION_INTERRUPT + 4), nullptr))); triggers.push_back(new TriggerNode("shield bash on enemy healer",