mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-16 01:01:30 +00:00
[Pathing] Improve roambox logic (#2983)
* [Pathing] Improve roambox logic * Cleanup roambox logic
This commit is contained in:
parent
5cfdeb928e
commit
889e57a5af
20
zone/bot.cpp
20
zone/bot.cpp
@ -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() {
|
||||
|
||||
140
zone/mob_ai.cpp
140
zone/mob_ai.cpp
@ -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) {
|
||||
|
||||
187
zone/npc.cpp
187
zone/npc.cpp
@ -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;
|
||||
}
|
||||
|
||||
27
zone/npc.h
27
zone/npc.h
@ -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];
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user