[Pathing] Improve roambox logic (#2983)

* [Pathing] Improve roambox logic

* Cleanup roambox logic
This commit is contained in:
Chris Miles 2023-02-24 14:07:34 -06:00 committed by GitHub
parent 5cfdeb928e
commit 889e57a5af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 216 additions and 174 deletions

View File

@ -2241,16 +2241,16 @@ void Bot::AI_Bot_Init()
AIautocastspell_timer.reset(nullptr);
casting_spell_AIindex = static_cast<uint8>(AIBot_spells.size());
roambox_max_x = 0;
roambox_max_y = 0;
roambox_min_x = 0;
roambox_min_y = 0;
roambox_distance = 0;
roambox_destination_x = 0;
roambox_destination_y = 0;
roambox_destination_z = 0;
roambox_min_delay = 2500;
roambox_delay = 2500;
m_roambox.max_x = 0;
m_roambox.max_y = 0;
m_roambox.min_x = 0;
m_roambox.min_y = 0;
m_roambox.distance = 0;
m_roambox.dest_x = 0;
m_roambox.dest_y = 0;
m_roambox.dest_z = 0;
m_roambox.delay = 2500;
m_roambox.min_delay = 2500;
}
void Bot::SpellProcess() {

View File

@ -404,16 +404,16 @@ void NPC::AI_Init()
AIautocastspell_timer.reset(nullptr);
casting_spell_AIindex = static_cast<uint8>(AIspells.size());
roambox_max_x = 0;
roambox_max_y = 0;
roambox_min_x = 0;
roambox_min_y = 0;
roambox_distance = 0;
roambox_destination_x = 0;
roambox_destination_y = 0;
roambox_destination_z = 0;
roambox_min_delay = 2500;
roambox_delay = 2500;
m_roambox.max_x = 0;
m_roambox.max_y = 0;
m_roambox.min_x = 0;
m_roambox.min_y = 0;
m_roambox.distance = 0;
m_roambox.dest_x = 0;
m_roambox.dest_y = 0;
m_roambox.dest_z = 0;
m_roambox.delay = 2500;
m_roambox.min_delay = 2500;
}
void Client::AI_Init()
@ -1592,123 +1592,9 @@ void NPC::AI_DoMovement() {
return;
}
/**
* Roambox logic sets precedence
*/
if (roambox_distance > 0) {
// Check if we're already moving to a WP
// If so, if we're not moving we have arrived and need to set delay
if (GetCWP() == EQ::WaypointStatus::RoamBoxPauseInProgress && !IsMoving()) {
// We have arrived
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
int random_timer = RandomTimer(
GetRoamboxMinDelay(),
move_delay_max
);
LogNPCRoamBoxDetail(
"({}) Timer calc | random_timer [{}] roambox_move_delay [{}] move_min [{}] move_max [{}]",
GetCleanName(),
random_timer,
roambox_move_delay,
(int) GetRoamboxMinDelay(),
move_delay_max
);
time_until_can_move = Timer::GetCurrentTime() + random_timer;
SetCurrentWP(0);
return;
}
// Set a new destination
if (!IsMoving() && time_until_can_move < Timer::GetCurrentTime()) {
auto move_x = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
auto move_y = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
roambox_destination_x = EQ::Clamp((GetX() + move_x), roambox_min_x, roambox_max_x);
roambox_destination_y = EQ::Clamp((GetY() + move_y), roambox_min_y, roambox_max_y);
/**
* If our roambox was configured with large distances, chances of hitting the min or max end of
* the clamp is high, this causes NPC's to gather on the border of a box, to reduce clustering
* either lower the roambox distance or the code will do a simple random between min - max when it
* hits the min or max of the clamp
*/
if (roambox_destination_x == roambox_min_x || roambox_destination_x == roambox_max_x) {
roambox_destination_x = static_cast<float>(zone->random.Real(roambox_min_x, roambox_max_x));
}
if (roambox_destination_y == roambox_min_y || roambox_destination_y == roambox_max_y) {
roambox_destination_y = static_cast<float>(zone->random.Real(roambox_min_y, roambox_max_y));
}
/**
* If mob was not spawned in water, let's not randomly roam them into water
* if the roam box was sloppily configured
*/
if (!GetWasSpawnedInWater()) {
roambox_destination_z = GetGroundZ(roambox_destination_x, roambox_destination_y);
if (zone->HasMap() && zone->HasWaterMap()) {
auto position = glm::vec3(
roambox_destination_x,
roambox_destination_y,
roambox_destination_z
);
/**
* If someone brought us into water when we naturally wouldn't path there, return to spawn
*/
if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) {
roambox_destination_x = m_SpawnPoint.x;
roambox_destination_y = m_SpawnPoint.y;
}
if (zone->watermap->InLiquid(position)) {
LogNPCRoamBoxDetail("[{}] | My destination is in water and I don't belong there!", GetCleanName());
return;
}
}
}
else { // Mob was in water, make sure new spot is in water also
roambox_destination_z = m_Position.z;
auto position = glm::vec3(
roambox_destination_x,
roambox_destination_y,
m_Position.z + 15
);
if (zone->HasWaterMap() && !zone->watermap->InLiquid(position)) {
roambox_destination_x = m_SpawnPoint.x;
roambox_destination_y = m_SpawnPoint.y;
roambox_destination_z = m_SpawnPoint.z;
}
}
LogNPCRoamBox("[{}] | Pathing to [{}] [{}] [{}]", GetCleanName(),
roambox_destination_x, roambox_destination_y,
roambox_destination_z);
LogNPCRoamBox(
"NPC ({}) distance [{}] X (min/max) [{} / {}] Y (min/max) [{} / {}] | Dest x/y/z [{} / {} / {}]",
GetCleanName(),
roambox_distance,
roambox_min_x,
roambox_max_x,
roambox_min_y,
roambox_max_y,
roambox_destination_x,
roambox_destination_y,
roambox_destination_z
);
SetCurrentWP(EQ::WaypointStatus::RoamBoxPauseInProgress);
NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z);
}
// Roambox logic sets precedence
if (m_roambox.distance > 0) {
HandleRoambox();
return;
}
else if (roamer) {

View File

@ -255,15 +255,18 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
}
guard_anim = eaStanding;
roambox_distance = 0;
roambox_max_x = -2;
roambox_max_y = -2;
roambox_min_x = -2;
roambox_min_y = -2;
roambox_destination_x = -2;
roambox_destination_y = -2;
roambox_min_delay = 1000;
roambox_delay = 1000;
m_roambox.max_x = -2;
m_roambox.max_y = -2;
m_roambox.min_x = -2;
m_roambox.min_y = -2;
m_roambox.distance = 0;
m_roambox.dest_x = -2;
m_roambox.dest_y = -2;
m_roambox.dest_z = 0;
m_roambox.delay = 1000;
m_roambox.min_delay = 1000;
p_depop = false;
loottable_id = npc_type_data->loottable_id;
skip_global_loot = npc_type_data->skip_global_loot;
@ -440,52 +443,52 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
float NPC::GetRoamboxMaxX() const
{
return roambox_max_x;
return m_roambox.max_x;
}
float NPC::GetRoamboxMaxY() const
{
return roambox_max_y;
return m_roambox.max_y;
}
float NPC::GetRoamboxMinX() const
{
return roambox_min_x;
return m_roambox.min_x;
}
float NPC::GetRoamboxMinY() const
{
return roambox_min_y;
return m_roambox.min_y;
}
float NPC::GetRoamboxDistance() const
{
return roambox_distance;
return m_roambox.distance;
}
float NPC::GetRoamboxDestinationX() const
{
return roambox_destination_x;
return m_roambox.dest_x;
}
float NPC::GetRoamboxDestinationY() const
{
return roambox_destination_y;
return m_roambox.dest_y;
}
float NPC::GetRoamboxDestinationZ() const
{
return roambox_destination_z;
return m_roambox.dest_z;
}
uint32 NPC::GetRoamboxDelay() const
{
return roambox_delay;
return m_roambox.delay;
}
uint32 NPC::GetRoamboxMinDelay() const
{
return roambox_min_delay;
return m_roambox.min_delay;
}
NPC::~NPC()
@ -3811,3 +3814,149 @@ void NPC::SendPositionToClients()
}
safe_delete(p);
}
void NPC::HandleRoambox()
{
bool has_arrived = GetCWP() == EQ::WaypointStatus::RoamBoxPauseInProgress && !IsMoving();
if (has_arrived) {
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
int random_timer = RandomTimer(
GetRoamboxMinDelay(),
move_delay_max
);
LogNPCRoamBoxDetail(
"({}) random_timer [{}] roambox_move_delay [{}] move_min [{}] move_max [{}]",
GetCleanName(),
random_timer,
roambox_move_delay,
(int) GetRoamboxMinDelay(),
move_delay_max
);
time_until_can_move = Timer::GetCurrentTime() + random_timer;
SetCurrentWP(0);
return;
}
bool ready_to_set_new_destination = !IsMoving() && time_until_can_move < Timer::GetCurrentTime();
if (ready_to_set_new_destination) {
// make several attempts to find a valid next move in the box
bool can_path = false;
for (int i = 0; i < 10; i++) {
auto move_x = static_cast<float>(zone->random.Real(-m_roambox.distance, m_roambox.distance));
auto move_y = static_cast<float>(zone->random.Real(-m_roambox.distance, m_roambox.distance));
auto requested_x = EQ::Clamp((GetX() + move_x), m_roambox.min_x, m_roambox.max_x);
auto requested_y = EQ::Clamp((GetY() + move_y), m_roambox.min_y, m_roambox.max_y);
auto requested_z = GetGroundZ(requested_x, requested_y);
std::vector<float> heights = {0, 250, -250};
for (auto &h: heights) {
if (CheckLosFN(requested_x, requested_y, requested_z + h, GetSize())) {
LogNPCRoamBox("[{}] Found line of sight to path attempt [{}] at height [{}]", GetCleanName(), i, h);
can_path = true;
break;
}
}
if (!can_path) {
LogNPCRoamBox("[{}] | Failed line of sight to path attempt [{}]", GetCleanName(), i);
continue;
}
m_roambox.dest_x = requested_x;
m_roambox.dest_y = requested_y;
/**
* If our roambox was configured with large distances, chances of hitting the min or max end of
* the clamp is high, this causes NPC's to gather on the border of a box, to reduce clustering
* either lower the roambox distance or the code will do a simple random between min - max when it
* hits the min or max of the clamp
*/
if (m_roambox.dest_x == m_roambox.min_x || m_roambox.dest_x == m_roambox.max_x) {
m_roambox.dest_x = static_cast<float>(zone->random.Real(m_roambox.min_x, m_roambox.max_x));
}
if (m_roambox.dest_y == m_roambox.min_y || m_roambox.dest_y == m_roambox.max_y) {
m_roambox.dest_y = static_cast<float>(zone->random.Real(m_roambox.min_y, m_roambox.max_y));
}
// If mob was not spawned in water, let's not randomly roam them into water
// if the roam box was sloppily configured
if (!GetWasSpawnedInWater()) {
m_roambox.dest_z = GetGroundZ(m_roambox.dest_x, m_roambox.dest_y);
if (zone->HasMap() && zone->HasWaterMap()) {
auto position = glm::vec3(
m_roambox.dest_x,
m_roambox.dest_y,
m_roambox.dest_z
);
// If someone brought us into water when we naturally wouldn't path there, return to spawn
if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) {
m_roambox.dest_x = m_SpawnPoint.x;
m_roambox.dest_y = m_SpawnPoint.y;
}
if (zone->watermap->InLiquid(position)) {
LogNPCRoamBoxDetail("[{}] | My destination is in water and I don't belong there!", GetCleanName());
return;
}
}
}
else { // Mob was in water, make sure new spot is in water also
m_roambox.dest_z = m_Position.z;
auto position = glm::vec3(
m_roambox.dest_x,
m_roambox.dest_y,
m_Position.z + 15
);
if (zone->HasWaterMap() && !zone->watermap->InLiquid(position)) {
m_roambox.dest_x = m_SpawnPoint.x;
m_roambox.dest_y = m_SpawnPoint.y;
m_roambox.dest_z = m_SpawnPoint.z;
}
}
LogNPCRoamBox(
"[{}] | Pathing to [{}] [{}] [{}]",
GetCleanName(),
m_roambox.dest_x,
m_roambox.dest_y,
m_roambox.dest_z
);
LogNPCRoamBox(
"NPC ({}) distance [{}] X (min/max) [{} / {}] Y (min/max) [{} / {}] | Dest x/y/z [{} / {} / {}]",
GetCleanName(),
m_roambox.distance,
m_roambox.min_x,
m_roambox.max_x,
m_roambox.min_y,
m_roambox.max_y,
m_roambox.dest_x,
m_roambox.dest_y,
m_roambox.dest_z
);
if (can_path) {
SetCurrentWP(EQ::WaypointStatus::RoamBoxPauseInProgress);
NavigateTo(m_roambox.dest_x, m_roambox.dest_y, m_roambox.dest_z);
return;
}
}
// failed to find path, reset timer
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
int random_timer = RandomTimer(
GetRoamboxMinDelay(),
move_delay_max
);
time_until_can_move = Timer::GetCurrentTime() + random_timer;
}
return;
}

View File

@ -80,6 +80,19 @@ struct AISpellsVar_Struct {
uint8 idle_beneficial_chance;
};
struct Roambox {
float max_x;
float max_y;
float min_x;
float min_y;
float distance;
float dest_x;
float dest_y;
float dest_z;
uint32 delay;
uint32 min_delay;
};
class SwarmPet;
class Client;
class Group;
@ -538,6 +551,8 @@ public:
protected:
void HandleRoambox();
const NPCType* NPCTypedata;
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
@ -635,16 +650,8 @@ protected:
glm::vec4 m_GuardPoint;
glm::vec4 m_GuardPointSaved;
EmuAppearance guard_anim;
float roambox_max_x;
float roambox_max_y;
float roambox_min_x;
float roambox_min_y;
float roambox_distance;
float roambox_destination_x;
float roambox_destination_y;
float roambox_destination_z;
uint32 roambox_delay;
uint32 roambox_min_delay;
Roambox m_roambox = {};
uint16 skills[EQ::skills::HIGHEST_SKILL + 1];

View File

@ -66,14 +66,14 @@ void NPC::AI_SetRoambox(
uint32 min_delay
)
{
roambox_distance = distance;
roambox_max_x = max_x;
roambox_min_x = min_x;
roambox_max_y = max_y;
roambox_min_y = min_y;
roambox_destination_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = delay;
roambox_min_delay = min_delay;
m_roambox.distance = distance;
m_roambox.max_x = max_x;
m_roambox.min_x = min_x;
m_roambox.max_y = max_y;
m_roambox.min_y = min_y;
m_roambox.dest_x = max_x + 1; // this will trigger a recalc
m_roambox.delay = delay;
m_roambox.min_delay = min_delay;
}
void NPC::DisplayWaypointInfo(Client *client) {