[New Rpg] New rpg start up (add GO_GRIND and NEAR_RANDOM status)

This commit is contained in:
Yunfan Li
2024-11-30 23:48:29 +08:00
parent 3e449fff73
commit 0fd894176b
27 changed files with 625 additions and 65 deletions

View File

@@ -62,6 +62,7 @@
#include "VehicleActions.h"
#include "WorldBuffAction.h"
#include "XpGainAction.h"
#include "NewRpgAction.h"
class PlayerbotAI;
@@ -240,6 +241,10 @@ 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 move random"] = &ActionContext::new_rpg_move_random;
}
private:
@@ -415,6 +420,10 @@ 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_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(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

@@ -0,0 +1,214 @@
#include "NewRpgAction.h"
#include <cmath>
#include "NewRpgStrategy.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:
{
// // IDLE -> NEAR_NPC
// if (!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime() && urand(1, 100) <= 50)
// {
// info.lastNearNpc = getMSTime();
// GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
// if (possibleTargets.empty())
// break;
// info.status = NewRpgStatus::NEAR_NPC;
// }
// IDLE -> GO_GRIND
if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime())
{
info.lastGrind = getMSTime();
WorldPosition pos = SelectRandomGrindPos();
if (pos == WorldPosition())
break;
info.status = NewRpgStatus::GO_GRIND;
info.grindPos = pos;
return true;
}
break;
}
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.grindPos = WorldPosition();
return true;
}
// just choose another grindPos
if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime())
{
WorldPosition pos = SelectRandomGrindPos();
if (pos == WorldPosition())
break;
info.status = NewRpgStatus::GO_GRIND;
info.lastGrind = getMSTime();
info.grindPos = pos;
return true;
}
break;
}
case NewRpgStatus::NEAR_RANDOM:
{
// NEAR_RANDOM -> GO_GRIND
if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime())
{
WorldPosition pos = SelectRandomGrindPos();
if (pos == WorldPosition())
break;
info.lastGrind = getMSTime();
botAI->rpgInfo.status = NewRpgStatus::GO_GRIND;
botAI->rpgInfo.grindPos = pos;
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->GetExactDist(loc) < 500.0f)
{
hi_prepared_locs.push_back(loc);
}
if (bot->GetExactDist(loc) < 1500.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_INFO("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;
}
bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest)
{
float dis = bot->GetExactDist(dest);
if (dis < pathFinderDis)
{
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
}
int attempt = 10;
float minDelta = MAXFLOAT;
const float x = bot->GetPositionX();
const float y = bot->GetPositionY();
const float z = bot->GetPositionZ();
float rx, ry, rz;
bool found = false;
while (--attempt)
{
float angle = bot->GetAngle(&dest);
float delta = (rand_norm() - 0.5) * M_PI;
angle += delta;
float dis = rand_norm() * pathFinderDis;
float dx = x + cos(angle) * dis;
float dy = y + sin(angle) * dis;
float dz = z;
PathGenerator path(bot);
path.CalculatePath(dx, dy, dz);
// bool canReach = bot->GetMap()->CanReachPositionAndGetValidCoords(bot, x, y, z, dx, dy, dz, false);
bool canReach = path.GetPathType() & (PATHFIND_NORMAL | PATHFIND_INCOMPLETE);
if (canReach)
{
G3D::Vector3 endPos = path.GetPath().back();
if (fabs(delta) < minDelta)
{
found = true;
minDelta = fabs(delta);
rx = endPos.x;
ry = endPos.y;
rz = endPos.z;
}
}
}
if (found)
{
return MoveTo(bot->GetMapId(), rx, ry, rz, false, false, false, true);
}
// fallback to direct move
float angle = bot->GetAngle(&dest);
return MoveTo(bot->GetMapId(), x + cos(angle) * pathFinderDis, y + sin(angle) * pathFinderDis, z);
}
bool NewRpgGoGrindAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.grindPos); }
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;
}

View File

@@ -0,0 +1,57 @@
#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 = 5 * 60 * 1000;
WorldPosition SelectRandomGrindPos();
};
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 NewRpgMoveRandomAction : public MovementAction
{
public:
NewRpgMoveRandomAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move random") {}
bool Execute(Event event) override;
protected:
const float moveStep = 50.0f;
};
#endif

View File

@@ -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;
}