diff --git a/common/ruletypes.h b/common/ruletypes.h index c2f11c1c3..0f8420aa1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -567,7 +567,7 @@ RULE_BOOL(TaskSystem, EnableTaskProximity, true) RULE_CATEGORY_END() RULE_CATEGORY(Range) -RULE_INT(Range, Say, 135) +RULE_INT(Range, Say, 15) RULE_INT(Range, Emote, 135) RULE_INT(Range, BeginCast, 200) RULE_INT(Range, Anims, 135) diff --git a/zone/client.cpp b/zone/client.cpp index 2448897f9..a22052415 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1108,7 +1108,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s CheckLDoNHail(GetTarget()); CheckEmoteHail(GetTarget(), message); - if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { + if(DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { NPC *tar = GetTarget()->CastToNPC(); parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); diff --git a/zone/command.cpp b/zone/command.cpp index 84cd3566a..6d9d875b1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1284,9 +1284,11 @@ void command_movement(Client *c, const Seperator *sep) c->Message(0, "Requires target"); } + mgr.NavigateTo(target, c->GetX(), c->GetY(), c->GetZ(), false, MovementRunning); - auto heading = target->CalculateHeadingToTarget(c->GetX(), c->GetY()); - mgr.RotateTo(target, heading, 16.0f); + //auto heading = target->CalculateHeadingToTarget(c->GetX(), c->GetY()); + //mgr.SendCommandToClients(target, 0.0f, 0.0f, 0.0f, atof(sep->arg[2]), 0, ClientRangeAny); + //mgr.RotateTo(target, heading); //double a1 = atof(sep->arg[2]); //double a2 = atof(sep->arg[3]); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 67784eb57..33d786b11 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1124,9 +1124,19 @@ double Lua_Mob::CalculateHeadingToTarget(double in_x, double in_y) { return self->CalculateHeadingToTarget(static_cast(in_x), static_cast(in_y)); } -void Lua_Mob::NavigateTo(double x, double y, double z, double speed) { +void Lua_Mob::RunTo(double x, double y, double z) { Lua_Safe_Call_Void(); - self->NavigateTo(static_cast(x), static_cast(y), static_cast(z), static_cast(speed)); + self->RunTo(static_cast(x), static_cast(y), static_cast(z)); +} + +void Lua_Mob::WalkTo(double x, double y, double z) { + Lua_Safe_Call_Void(); + self->WalkTo(static_cast(x), static_cast(y), static_cast(z)); +} + +void Lua_Mob::NavigateTo(double x, double y, double z) { + Lua_Safe_Call_Void(); + self->NavigateTo(static_cast(x), static_cast(y), static_cast(z)); } void Lua_Mob::StopNavigation() { @@ -2360,7 +2370,9 @@ luabind::scope lua_register_mob() { .def("FaceTarget", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::FaceTarget) .def("SetHeading", (void(Lua_Mob::*)(double))&Lua_Mob::SetHeading) .def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget) - .def("NavigateTo", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::NavigateTo) + .def("RunTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::RunTo) + .def("WalkTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::WalkTo) + .def("NavigateTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::NavigateTo) .def("StopNavigation", (void(Lua_Mob::*)(void))&Lua_Mob::StopNavigation) .def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance) .def("SendTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendTo) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 7300e3085..fc58af6d5 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -240,7 +240,9 @@ public: void FaceTarget(Lua_Mob target); void SetHeading(double in); double CalculateHeadingToTarget(double in_x, double in_y); - void NavigateTo(double x, double y, double z, double speed); + void RunTo(double x, double y, double z); + void WalkTo(double x, double y, double z); + void NavigateTo(double x, double y, double z); void StopNavigation(); float CalculateDistance(double x, double y, double z); void SendTo(double x, double y, double z); diff --git a/zone/merc.cpp b/zone/merc.cpp index 2a1e581d9..bfa4a7233 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1479,7 +1479,7 @@ void Merc::AI_Process() { } else if (!CheckLosFN(GetTarget())) { auto Goal = GetTarget()->GetPosition(); - NavigateTo(Goal.x, Goal.y, Goal.z, GetRunspeed()); + RunTo(Goal.x, Goal.y, Goal.z); return; } @@ -1545,7 +1545,7 @@ void Merc::AI_Process() { float newZ = 0; FaceTarget(GetTarget()); if (PlotPositionAroundTarget(this, newX, newY, newZ)) { - NavigateTo(newX, newY, newZ, GetRunspeed()); + RunTo(newX, newY, newZ); return; } } @@ -1557,7 +1557,7 @@ void Merc::AI_Process() { float newY = 0; float newZ = 0; if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { - NavigateTo(newX, newY, newZ, GetRunspeed()); + RunTo(newX, newY, newZ); return; } } @@ -1568,7 +1568,7 @@ void Merc::AI_Process() { float newY = 0; float newZ = 0; if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { - NavigateTo(newX, newY, newZ, GetRunspeed()); + RunTo(newX, newY, newZ); return; } } @@ -1695,7 +1695,7 @@ void Merc::AI_Process() { { if(!IsRooted()) { Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); - NavigateTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); + RunTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); return; } @@ -1750,15 +1750,20 @@ void Merc::AI_Process() { if (follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); - int speed = GetRunspeed(); + bool running = true; if (dist < GetFollowDistance() + 1000) - speed = GetWalkspeed(); + running = false; SetRunAnimSpeed(0); if (dist > GetFollowDistance()) { - NavigateTo(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + if (running) { + RunTo(follow->GetX(), follow->GetY(), follow->GetZ()); + } + else { + WalkTo(follow->GetX(), follow->GetY(), follow->GetZ()); + } if (rest_timer.Enabled()) rest_timer.Disable(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 59691c78f..6df25048b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -442,9 +442,6 @@ Mob::Mob(const char* in_name, PrimaryAggro = false; AssistAggro = false; npc_assist_cap = 0; - - PathRecalcTimer.reset(new Timer(500)); - PathingLoopCount = 0; } Mob::~Mob() @@ -1622,8 +1619,6 @@ void Mob::ShowBuffList(Client* client) { } void Mob::GMMove(float x, float y, float z, float heading, bool SendUpdate) { - Route.clear(); - if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } @@ -2710,7 +2705,12 @@ void Mob::FaceTarget(Mob* mob_to_face /*= 0*/) { float current_heading = GetHeading(); float new_heading = CalculateHeadingToTarget(faced_mob->GetX(), faced_mob->GetY()); if(current_heading != new_heading) { - mMovementManager->RotateTo(this, new_heading, 16.0f); + if (IsEngaged() || IsRunning()) { + mMovementManager->RotateTo(this, new_heading); + } + else { + mMovementManager->RotateTo(this, new_heading, MovementWalking); + } } if(IsNPC() && !IsEngaged()) { @@ -3553,10 +3553,12 @@ void Mob::SetFlyMode(GravityBehavior flymode) void Mob::Teleport(const glm::vec3 &pos) { + mMovementManager->Teleport(this, pos.x, pos.y, pos.z, m_Position.w); } void Mob::Teleport(const glm::vec4 &pos) { + mMovementManager->Teleport(this, pos.x, pos.y, pos.z, pos.w); } bool Mob::IsNimbusEffectActive(uint32 nimbus_effect) @@ -5580,25 +5582,7 @@ float Mob::HeadingAngleToMob(float other_x, float other_y) float this_x = GetX(); float this_y = GetY(); - float y_diff = std::abs(this_y - other_y); - float x_diff = std::abs(this_x - other_x); - if (y_diff < 0.0000009999999974752427) - y_diff = 0.0000009999999974752427; - - float angle = atan2(x_diff, y_diff) * 180.0f * 0.3183099014828645f; // angle, nice "pi" - - // return the right thing based on relative quadrant - // I'm sure this could be improved for readability, but whatever - if (this_y >= other_y) { - if (other_x >= this_x) - return (90.0f - angle + 90.0f) * 511.5f * 0.0027777778f; - if (other_x <= this_x) - return (angle + 180.0f) * 511.5f * 0.0027777778f; - } - if (this_y > other_y || other_x > this_x) - return angle * 511.5f * 0.0027777778f; - else - return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f; + return CalculateHeadingAngleBetweenPositions(this_x, this_y, other_x, other_y); } bool Mob::GetSeeInvisible(uint8 see_invis) diff --git a/zone/mob.h b/zone/mob.h index 3f35a25b8..35ebaf87c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -523,6 +523,7 @@ public: uint32 GetNPCTypeID() const { return npctype_id; } void SetNPCTypeID(uint32 npctypeid) { npctype_id = npctypeid; } inline const glm::vec4& GetPosition() const { return m_Position; } + inline void SetPosition(const float x, const float y, const float z) { m_Position.x = x; m_Position.y = y; m_Position.z = z; } inline const float GetX() const { return m_Position.x; } inline const float GetY() const { return m_Position.y; } inline const float GetZ() const { return m_Position.z; } @@ -968,7 +969,9 @@ public: inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);} float CalculateHeadingToTarget(float in_x, float in_y) { return HeadingAngleToMob(in_x, in_y); } - void NavigateTo(float x, float y, float z, float speed); + void WalkTo(float x, float y, float z); + void RunTo(float x, float y, float z); + void NavigateTo(float x, float y, float z); void StopNavigation(); float CalculateDistance(float x, float y, float z); float GetGroundZ(float new_x, float new_y, float z_offset=0.0); @@ -1133,9 +1136,6 @@ public: int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item); int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr); - //Pathing - glm::vec3 UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); - // Bots HealRotation methods #ifdef BOTS bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } @@ -1292,7 +1292,6 @@ protected: void CalculateNewFearpoint(); float FindGroundZ(float new_x, float new_y, float z_offset=0.0); float FindDestGroundZ(glm::vec3 dest, float z_offset=0.0); - glm::vec3 HandleStuckPath(const glm::vec3 &To, const glm::vec3 &From); virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0); int16 GetSympatheticSpellProcRate(uint16 spell_id); @@ -1481,15 +1480,7 @@ protected: glm::vec3 m_FearWalkTarget; bool currently_fleeing; - // Pathing - // - glm::vec3 PathingDestination; - IPathfinder::IPath Route; - std::unique_ptr PathRecalcTimer; bool DistractedFromGrid; - glm::vec3 PathingLastPosition; - int PathingLoopCount; - uint32 pDontHealMeBefore; uint32 pDontBuffMeBefore; uint32 pDontDotMeBefore; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 3862f7857..750f5c792 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -796,16 +796,12 @@ void Client::AI_Process() } else { if (AI_movement_timer->Check()) { - int speed = GetFearSpeed(); - animation = speed; - speed *= 2; - SetCurrentSpeed(speed); // Check if we have reached the last fear point - if(IsPositionEqual(GetX(), GetY(), GetZ(), m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z)) { + if(IsPositionEqual(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget)) { CalculateNewFearpoint(); } - NavigateTo(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed); + RunTo(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z); } return; } @@ -873,11 +869,7 @@ void Client::AI_Process() { if(AI_movement_timer->Check()) { - int newspeed = GetRunspeed(); - animation = newspeed; - newspeed *= 2; - SetCurrentSpeed(newspeed); - NavigateTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), newspeed); + RunTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); } } else if(IsMoving()) @@ -920,12 +912,12 @@ void Client::AI_Process() SendPositionUpdate(); // this shouldn't happen a lot (and hard to make it) so lets not rate limit } else if (dist >= 400) { // >=20 if (AI_movement_timer->Check()) { - int nspeed = (dist >= 1225 ? GetRunspeed() : GetWalkspeed()); // >= 35 - animation = nspeed; - nspeed *= 2; - SetCurrentSpeed(nspeed); - - NavigateTo(owner->GetX(), owner->GetY(), owner->GetZ(), nspeed); + if (dist >= 1225) { + RunTo(owner->GetX(), owner->GetY(), owner->GetZ()); + } + else { + WalkTo(owner->GetX(), owner->GetY(), owner->GetZ()); + } } } else { StopNavigation(); @@ -1089,15 +1081,14 @@ void Mob::AI_Process() { else { if (AI_movement_timer->Check()) { // Check if we have reached the last fear point - if (IsPositionEqual(GetX(), GetY(), GetZ(), m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z)) { + if (IsPositionEqual(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget)) { // Calculate a new point to run to CalculateNewFearpoint(); } NavigateTo( m_FearWalkTarget.x, m_FearWalkTarget.y, - m_FearWalkTarget.z, - GetFearSpeed() + m_FearWalkTarget.z ); } return; @@ -1216,18 +1207,7 @@ void Mob::AI_Process() { StopNavigation(); if (AI_movement_timer->Check()) { - if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) { - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - } - SetCurrentSpeed(0); - } - if (IsMoving()) { - if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) { - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - } - SetCurrentSpeed(0); + FaceTarget(); } //casting checked above... @@ -1419,7 +1399,7 @@ void Mob::AI_Process() { else if (AI_movement_timer->Check() && target) { if (!IsRooted()) { Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); - NavigateTo(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed()); + RunTo(target->GetX(), target->GetY(), target->GetZ()); } else if (IsMoving()) { @@ -1497,13 +1477,13 @@ void Mob::AI_Process() { if (distance_to_owner >= 400 || z_distance > 100) { - int pet_speed = GetWalkspeed(); + bool running = false; /** * Distance: >= 35 (Run if far away) */ if (distance_to_owner >= 1225) { - pet_speed = GetRunspeed(); + running = true; } /** @@ -1518,7 +1498,12 @@ void Mob::AI_Process() { auto &Goal = owner->GetPosition(); - NavigateTo(Goal.x, Goal.y, Goal.z, pet_speed); + if (running) { + RunTo(Goal.x, Goal.y, Goal.z); + } + else { + WalkTo(Goal.x, Goal.y, Goal.z); + } } } else { @@ -1558,17 +1543,22 @@ void Mob::AI_Process() { * Default follow distance is 100 */ if (distance >= follow_distance) { - int speed = GetWalkspeed(); + bool running = false; if (distance >= follow_distance + 150) { - speed = GetRunspeed(); + running = true; } bool waypoint_changed, node_reached; auto &Goal = follow->GetPosition(); - NavigateTo(Goal.x, Goal.y, Goal.z, speed); + if (running) { + RunTo(Goal.x, Goal.y, Goal.z); + } + else { + WalkTo(Goal.x, Goal.y, Goal.z); + } } else { moved = false; @@ -1694,7 +1684,7 @@ void NPC::AI_DoMovement() { roambox_destination_y); } - NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z, move_speed); + NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z); if (m_Position.x == roambox_destination_x && m_Position.y == roambox_destination_y) { time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay); @@ -1761,8 +1751,7 @@ void NPC::AI_DoMovement() { NavigateTo( m_CurrentWayPoint.x, m_CurrentWayPoint.y, - m_CurrentWayPoint.z, - move_speed + m_CurrentWayPoint.z ); } @@ -1782,10 +1771,9 @@ void NPC::AI_DoMovement() { } else if (IsGuarding()) { - bool at_gp = IsPositionEqual(GetX(), GetY(), GetZ(), m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); + bool at_gp = IsPositionEqual(m_Position, m_GuardPoint); if (at_gp) { - StopNavigation(); if (moved) { Log(Logs::Detail, @@ -1808,7 +1796,7 @@ void NPC::AI_DoMovement() { } } else { - NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, move_speed); + NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); } } } diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index a6151a887..0da3436c4 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -1,53 +1,265 @@ #include "mob_movement_manager.h" #include "client.h" #include "mob.h" +#include "zone.h" +#include "position.h" #include "../common/eq_packet_structs.h" #include "../common/misc_functions.h" +#include "../common/data_verification.h" #include +#include #include #include extern double frame_time; +extern Zone *zone; -struct RotateCommand +class IMovementCommand { - RotateCommand() { - rotate_to = 0.0; - rotate_to_speed = 0.0; - rotate_dir = 1.0; - active = false; - started = false; - } - - double rotate_to; - double rotate_to_speed; - double rotate_dir; - bool active; - bool started; +public: + IMovementCommand() = default; + virtual ~IMovementCommand() = default; + virtual bool Process(MobMovementManager *mgr, Mob *m) = 0; + virtual bool Started() const = 0; }; -struct MovementCommand +class RotateToCommand : public IMovementCommand { - MovementCommand() { - move_to_x = 0.0; - move_to_y = 0.0; - move_to_z = 0.0; - move_to_speed = 0.0; - last_sent_short_distance = 0.0; - last_sent_medium_distance = 0.0; - last_sent_long_distance = 0.0; - active = false; +public: + RotateToCommand(double rotate_to, double dir, MobMovementMode mode) { + m_rotate_to = rotate_to; + m_rotate_to_dir = dir; + m_rotate_to_mode = mode; + m_started = false; } - double move_to_x; - double move_to_y; - double move_to_z; - double move_to_speed; - double last_sent_short_distance; - double last_sent_medium_distance; - double last_sent_long_distance; - bool active; + virtual ~RotateToCommand() { + + } + + virtual bool Process(MobMovementManager *mgr, Mob *m) { + if (!m->IsAIControlled()) { + return true; + } + + auto rotate_to_speed = m_rotate_to_mode == MovementRunning ? 50.0 : 16.0; //todo: get this from mob + + if (!m_started) { + m_started = true; + + if (rotate_to_speed > 0.0 && rotate_to_speed <= 25.0) { //send basic rotation + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, m_rotate_to_dir * rotate_to_speed, 0, ClientRangeClose); + } + } + + auto from = FixHeading(m->GetHeading()); + auto to = FixHeading(m_rotate_to); + auto diff = to - from; + + while (diff < -256.0) { + diff += 512.0; + } + + while (diff > 256) { + diff -= 512.0; + } + + auto dist = std::abs(diff); + auto td = rotate_to_speed * 19.0 * frame_time; + + if (td >= dist) { + m->SetHeading(to); + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); + return true; + } + + from += td * m_rotate_to_dir; + m->SetHeading(FixHeading(from)); + return false; + } + + virtual bool Started() const { + return m_started; + } + +private: + double m_rotate_to; + double m_rotate_to_dir; + MobMovementMode m_rotate_to_mode; + bool m_started; +}; + +class MoveToCommand : public IMovementCommand +{ +public: + MoveToCommand(float x, float y, float z, MobMovementMode mode) { + m_move_to_x = x; + m_move_to_y = y; + m_move_to_z = z; + m_move_to_mode = mode; + m_last_sent_time = 0.0; + m_last_sent_speed = 0; + m_started = false; + } + + virtual ~MoveToCommand() { + + } + + virtual bool Process(MobMovementManager *mgr, Mob *m) { + if (!m->IsAIControlled()) { + return true; + } + + //Send a movement packet when you start moving + double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; + int current_speed = 0; + + if (m_move_to_mode == MovementRunning) { + current_speed = m->GetRunspeed(); + } + else { + current_speed = m->GetWalkspeed(); + } + + if (!m_started) { + m_started = true; + //rotate to the point + m->SetHeading(m->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); + + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + return false; + } + + //When speed changes + if (current_speed != m_last_sent_speed) { + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + return false; + } + + //If x seconds have passed without sending an update. + if (current_time - m_last_sent_time >= 5.0) { + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + return false; + } + + auto &p = m->GetPosition(); + glm::vec3 tar(m_move_to_x, m_move_to_y, m_move_to_z); + glm::vec3 pos(p.x, p.y, p.z); + + double len = glm::distance(pos, tar); + if (len == 0) { + return true; + } + + glm::vec3 dir = tar - pos; + glm::vec3 ndir = glm::normalize(dir); + double distance_moved = frame_time * current_speed * 0.4f * 1.4f; + + if (distance_moved > len) { + m->SetPosition(tar.x, tar.y, tar.z); + + if (m->IsNPC()) { + entity_list.ProcessMove(m->CastToNPC(), tar.x, tar.y, tar.z); + } + + m->TryFixZ(); + return true; + } + else { + glm::vec3 npos = pos + (ndir * static_cast(distance_moved)); + m->SetPosition(npos.x, npos.y, npos.z); + + if (m->IsNPC()) { + entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, npos.z); + } + } + + return false; + } + + virtual bool Started() const { + return m_started; + } + +private: + + double m_move_to_x; + double m_move_to_y; + double m_move_to_z; + MobMovementMode m_move_to_mode; + bool m_started; + + double m_last_sent_time; + int m_last_sent_speed; +}; + +class TeleportToCommand : public IMovementCommand +{ +public: + TeleportToCommand(float x, float y, float z, float heading) { + m_teleport_to_x = x; + m_teleport_to_y = y; + m_teleport_to_z = z; + m_teleport_to_heading = heading; + } + + virtual ~TeleportToCommand() { + + } + + virtual bool Process(MobMovementManager *mgr, Mob *m) { + if (!m->IsAIControlled()) { + return true; + } + + m->SetPosition(m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); + m->SetHeading(mgr->FixHeading(m_teleport_to_heading)); + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); + return true; + } + + virtual bool Started() const { + return false; + } + +private: + + double m_teleport_to_x; + double m_teleport_to_y; + double m_teleport_to_z; + double m_teleport_to_heading; +}; + +class StopMovingCommand : public IMovementCommand +{ +public: + StopMovingCommand() { + } + + virtual ~StopMovingCommand() { + + } + + virtual bool Process(MobMovementManager *mgr, Mob *m) { + if (!m->IsAIControlled()) { + return true; + } + + mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); + return true; + } + + virtual bool Started() const { + return false; + } }; struct MovementStats @@ -65,10 +277,25 @@ struct MovementStats uint64_t TotalSentHeading; }; +struct NavigateTo +{ + NavigateTo() { + navigate_to_x = 0.0; + navigate_to_y = 0.0; + navigate_to_z = 0.0; + last_set_time = 0.0; + } + + double navigate_to_x; + double navigate_to_y; + double navigate_to_z; + double last_set_time; +}; + struct MobMovementEntry { - RotateCommand RotateCommand; - MovementCommand MoveCommand; + std::deque> Commands; + NavigateTo NavigateTo; }; struct MobMovementManager::Implementation @@ -83,106 +310,6 @@ MobMovementManager::MobMovementManager() _impl.reset(new Implementation()); } -void MobMovementManager::ProcessRotateCommand(Mob *m, RotateCommand &cmd) -{ - if (m->IsEngaged()) { - auto to = FixHeading(cmd.rotate_to); - m->SetHeading(to); - SendCommandToAllClients(m, 0.0, 0.0, 0.0, 0.0, 0); - cmd.active = false; - return; - } - - if (!cmd.started) { - cmd.started = true; - - SendCommandToAllClients(m, 0.0, 0.0, 0.0, cmd.rotate_dir * cmd.rotate_to_speed, 0); - } - - auto from = FixHeading(m->GetHeading()); - auto to = FixHeading(cmd.rotate_to); - float dist = 0.0; - if (cmd.rotate_dir > 0.0) { - if (to > from) { - dist = to - from; - } - else { - dist = 512.0 - from + to; - } - } - else { - if (from > to) { - dist = from - to; - } - else { - dist = (512.0 - to) + from; - } - } - - auto td = cmd.rotate_to_speed * 19.0 * frame_time; - - if (td >= dist) { - m->SetHeading(to); - SendCommandToAllClients(m, 0.0, 0.0, 0.0, 0.0, 0); - cmd.active = false; - } - - from += td * cmd.rotate_dir; - m->SetHeading(FixHeading(from)); -} - -void MobMovementManager::SendCommandToAllClients(Mob *m, float dx, float dy, float dz, float dh, int anim) -{ - EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct*)outapp.pBuffer; - FillCommandStruct(spu, m, dx, dy, dz, dh, anim); - - for (auto &c : _impl->Clients) { - _impl->Stats.TotalSent++; - - if (anim != 0) { - _impl->Stats.TotalSentMovement++; - } - else if (dh != 0) { - _impl->Stats.TotalSentHeading++; - } - else { - _impl->Stats.TotalSentPosition++; - } - - c->QueuePacket(&outapp); - } -} - -void MobMovementManager::FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim) -{ - memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); - spu->spawn_id = m->GetID(); - spu->x_pos = FloatToEQ19(m->GetX()); - spu->y_pos = FloatToEQ19(m->GetY()); - spu->z_pos = FloatToEQ19(m->GetZ()); - spu->heading = FloatToEQ12(m->GetHeading()); - spu->delta_x = FloatToEQ13(dx); - spu->delta_y = FloatToEQ13(dy); - spu->delta_z = FloatToEQ13(dz); - spu->delta_heading = FloatToEQ10(dh); - spu->animation = anim; -} - -float MobMovementManager::FixHeading(float in) -{ - auto h = in; - while (h > 512.0) { - h -= 512.0; - } - - while (h < 0.0) { - h += 512.0; - } - - return h; -} - MobMovementManager::~MobMovementManager() { } @@ -191,12 +318,17 @@ void MobMovementManager::Process() { for (auto &iter : _impl->Entries) { auto &ent = iter.second; + auto &commands = ent.Commands; - if (ent.RotateCommand.active) { - ProcessRotateCommand(iter.first, ent.RotateCommand); - } - else if (ent.MoveCommand.active) { + while (true != commands.empty()) { + auto &cmd = commands.front(); + auto r = cmd->Process(this, iter.first); + if (true != r) { + break; + } + + commands.pop_front(); } } } @@ -229,52 +361,250 @@ void MobMovementManager::RemoveClient(Client *c) } } -void MobMovementManager::RotateTo(Mob *who, float to, float speed) +void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mode) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); - auto from = FixHeading(who->GetHeading()); - to = FixHeading(to); - - ent.second.RotateCommand.active = true; - ent.second.RotateCommand.started = false; - ent.second.RotateCommand.rotate_to = to; - ent.second.RotateCommand.rotate_to_speed = speed; - - double pdist = 0.0; - double ndist = 0.0; - if (to > from) { - pdist = to - from; - } - else { - pdist = 512.0 - from + to; - } - - if (from > to) { - ndist = from - to; - } - else { - ndist = (512.0 - to) + from; - } - - if (pdist <= ndist) { - ent.second.RotateCommand.rotate_dir = 1.0; - } - else { - ent.second.RotateCommand.rotate_dir = -1.0; + if (true != ent.second.Commands.empty()) { + return; } + + PushRotateTo(ent.second, who, to, mode); } void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float heading) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + + ent.second.Commands.clear(); + + PushTeleportTo(ent.second, x, y, z, heading); } -void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, float speed) +void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, bool force, MobMovementMode mode) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + auto &nav = ent.second.NavigateTo; + + double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; + if (force || (current_time - nav.last_set_time) > 0.5) { + //Can potentially recalc + auto within = IsPositionWithinSimpleCylinder(glm::vec3(x, y, z), glm::vec3(nav.navigate_to_x, nav.navigate_to_y, nav.navigate_to_z), 1.5f, 6.0f); + + if (false == within) { + //Path is no longer valid, calculate a new path + UpdatePath(who, x, y, z, mode); + } + } } void MobMovementManager::StopNavigation(Mob *who) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + if (true == ent.second.Commands.empty()) { + return; + } + + auto &running_cmd = ent.second.Commands.front(); + if (false == running_cmd->Started()) { + ent.second.Commands.clear(); + return; + } + + SendCommandToClients(who, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); + ent.second.Commands.clear(); +} + +void MobMovementManager::SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range) +{ + if (range == ClientRangeNone) { + return; + } + + EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct*)outapp.pBuffer; + FillCommandStruct(spu, m, dx, dy, dz, dh, anim); + + if (range == ClientRangeAny) { + for (auto &c : _impl->Clients) { + _impl->Stats.TotalSent++; + + if (anim != 0) { + _impl->Stats.TotalSentMovement++; + } + else if (dh != 0) { + _impl->Stats.TotalSentHeading++; + } + else { + _impl->Stats.TotalSentPosition++; + } + + c->QueuePacket(&outapp); + } + } + else { + for (auto &c : _impl->Clients) { + float dist = c->CalculateDistance(m->GetX(), m->GetY(), m->GetZ()); + + bool match = false; + if (range & ClientRangeClose) { + if (dist < 200.0f) { + match = true; + } + } + + if (!match && range & ClientRangeMedium) { + if (dist >= 200.0f && dist < 1000.0f) { + match = true; + } + } + + if (!match && range & ClientRangeLong) { + if (dist >= 1000.0f) { + match = true; + } + } + + if (match) { + _impl->Stats.TotalSent++; + + if (anim != 0) { + _impl->Stats.TotalSentMovement++; + } + else if (dh != 0) { + _impl->Stats.TotalSentHeading++; + } + else { + _impl->Stats.TotalSentPosition++; + } + + c->QueuePacket(&outapp); + } + } + } +} + +float MobMovementManager::FixHeading(float in) +{ + auto h = in; + while (h > 512.0) { + h -= 512.0; + } + + while (h < 0.0) { + h += 512.0; + } + + return h; +} + +void MobMovementManager::FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim) +{ + memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = m->GetID(); + spu->x_pos = FloatToEQ19(m->GetX()); + spu->y_pos = FloatToEQ19(m->GetY()); + spu->z_pos = FloatToEQ19(m->GetZ()); + spu->heading = FloatToEQ12(m->GetHeading()); + spu->delta_x = FloatToEQ13(dx); + spu->delta_y = FloatToEQ13(dy); + spu->delta_z = FloatToEQ13(dz); + spu->delta_heading = FloatToEQ10(dh); + spu->animation = anim; +} + +void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode) +{ + bool partial = false; + bool stuck = false; + auto route = zone->pathing->FindRoute(glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck); + + if (route.empty()) { + return; + } + + auto &first = route.front(); + + //if who is already at the first node, then cull it + if (IsPositionEqualWithinCertainZ(glm::vec3(who->GetX(), who->GetY(), who->GetZ()), first.pos, 5.0f)) { + route.pop_front(); + } + + if (route.empty()) { + return; + } + + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + + first = route.front(); + //If mode = walking then rotateto first node (if possible, live does this) + if (mode == MovementWalking) { + auto h = who->CalculateHeadingToTarget(first.pos.x, first.pos.y); + PushRotateTo(ent.second, who, h, mode); + } + + //for each node create a moveto/teleport command + glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); + for (auto &node : route) { + if (node.teleport) { + PushTeleportTo(ent.second, node.pos.x, node.pos.y, node.pos.z, + CalculateHeadingAngleBetweenPositions(previous_pos.x, previous_pos.y, node.pos.x, node.pos.y)); + } + else { + PushMoveTo(ent.second, node.pos.x, node.pos.y, node.pos.z, mode); + } + + previous_pos = node.pos; + } + + if (stuck) { + PushTeleportTo(ent.second, x, y, z, + CalculateHeadingAngleBetweenPositions(previous_pos.x, previous_pos.y, x, y)); + } + else { + PushStopMoving(ent.second); + } +} + +void MobMovementManager::PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading) +{ + ent.Commands.push_back(std::unique_ptr(new TeleportToCommand(x, y, z, heading))); +} + +void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode) +{ + ent.Commands.push_back(std::unique_ptr(new MoveToCommand(x, y, z, mode))); +} + +void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode) +{ + auto from = FixHeading(who->GetHeading()); + to = FixHeading(to); + + float diff = to - from; + + if (std::abs(diff) < 0.001f) { + return; + } + + while (diff < -256.0) { + diff += 512.0; + } + + while (diff > 256) { + diff -= 512.0; + } + + ent.Commands.push_back(std::unique_ptr(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mode))); +} + +void MobMovementManager::PushStopMoving(MobMovementEntry &ent) +{ + ent.Commands.push_back(std::unique_ptr(new StopMovingCommand())); } diff --git a/zone/mob_movement_manager.h b/zone/mob_movement_manager.h index 793c3406e..6d04ea323 100644 --- a/zone/mob_movement_manager.h +++ b/zone/mob_movement_manager.h @@ -5,8 +5,28 @@ class Mob; class Client; struct RotateCommand; +struct MovementCommand; +struct MobMovementEntry; struct PlayerPositionUpdateServer_Struct; +enum ClientRange : int +{ + ClientRangeNone = 0, + ClientRangeClose = 1, + ClientRangeMedium = 2, + ClientRangeCloseMedium = 3, + ClientRangeLong = 4, + ClientRangeCloseLong = 5, + ClientRangeMediumLong = 6, + ClientRangeAny = 7 +}; + +enum MobMovementMode : int +{ + MovementWalking = 0, + MovementRunning = 1 +}; + class MobMovementManager { public: @@ -17,10 +37,12 @@ public: void AddClient(Client *c); void RemoveClient(Client *c); - void RotateTo(Mob *who, float to, float speed); + void RotateTo(Mob *who, float to, MobMovementMode mode = MovementRunning); void Teleport(Mob *who, float x, float y, float z, float heading); - void NavigateTo(Mob *who, float x, float y, float z, float speed); + void NavigateTo(Mob *who, float x, float y, float z, bool force = false, MobMovementMode mode = MovementRunning); void StopNavigation(Mob *who); + void SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range); + float FixHeading(float in); //void Dump(Mob *m, Client *to); //void DumpStats(Client *to); //void ClearStats(); @@ -35,10 +57,12 @@ private: MobMovementManager(const MobMovementManager&); MobMovementManager& operator=(const MobMovementManager&); - void ProcessRotateCommand(Mob *m, RotateCommand &cmd); - void SendCommandToAllClients(Mob *m, float dx, float dy, float dz, float dh, int anim); void FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim); - float FixHeading(float in); + void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode); + void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading); + void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); + void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode); + void PushStopMoving(MobMovementEntry &ent); struct Implementation; std::unique_ptr _impl; diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index 7842d84a4..615ba1c4e 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -134,7 +134,7 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl glm::vec3 PathfinderNavmesh::GetRandomLocation() { if (!m_impl->nav_mesh) { - return glm::vec3(); + return glm::vec3(0.f); } if (!m_impl->query) { @@ -155,7 +155,7 @@ glm::vec3 PathfinderNavmesh::GetRandomLocation() return glm::vec3(point[0], point[2], point[1]); } - return glm::vec3(); + return glm::vec3(0.f); } void PathfinderNavmesh::DebugCommand(Client *c, const Seperator *sep) diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 96916754c..db133a944 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -22,193 +22,6 @@ void AdjustRoute(std::list &nodes, int flymode, float of } } -glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached) -{ - glm::vec3 To(ToX, ToY, ToZ); - if (Speed <= 0) { - return To; - } - - glm::vec3 From(GetX(), GetY(), GetZ()); - - if (DistanceSquared(To, From) < 1.0f) { - WaypointChanged = false; - NodeReached = true; - Route.clear(); - return To; - } - - if (Route.empty()) { - bool partial = false; - bool stuck = false; - Route = zone->pathing->FindRoute(From, To, partial, stuck); - AdjustRoute(Route, flymode, GetZOffset()); - - PathingDestination = To; - WaypointChanged = true; - NodeReached = false; - if (stuck) { - return HandleStuckPath(To, From); - } - - if (Route.empty()) { - return To; - } - else { - return (*Route.begin()).pos; - } - } - else { - if (PathRecalcTimer->Check()) { - bool SameDestination = DistanceSquared(To, PathingDestination) < 100.0f; - if (!SameDestination) { - //We had a route but our target position moved too much - bool partial = false; - bool stuck = false; - Route = zone->pathing->FindRoute(From, To, partial, stuck); - AdjustRoute(Route, flymode, GetZOffset()); - - PathingDestination = To; - WaypointChanged = true; - NodeReached = false; - - if (stuck) { - return HandleStuckPath(To, From); - } - - if (Route.empty()) { - return To; - } - else { - return (*Route.begin()).pos; - } - } - } - - if (!IsRooted()) { - bool AtPrevNode = DistanceSquared(From, PathingLastPosition) < 1.0f; - if (AtPrevNode) { - PathingLoopCount++; - auto front = (*Route.begin()).pos; - - if (PathingLoopCount > 5) { - Teleport(front); //todo new teleport - SendPosition(); - Route.pop_front(); - - WaypointChanged = true; - NodeReached = true; - PathingLoopCount = 0; - } - - return front; - } - else { - PathingLastPosition = From; - PathingLoopCount = 0; - } - } - else { - PathingLastPosition = From; - PathingLoopCount = 0; - } - - bool AtNextNode = false; - if (flymode == GravityBehavior::Flying) { - AtNextNode = DistanceSquared(From, (*Route.begin()).pos) < 4.0f; - } - else { - float z_dist = From.z - (*Route.begin()).pos.z; - z_dist *= z_dist; - AtNextNode = DistanceSquaredNoZ(From, (*Route.begin()).pos) < 4.0f && z_dist < 25.0f; - } - - if (AtNextNode) { - WaypointChanged = false; - NodeReached = true; - - Route.pop_front(); - - if (Route.empty()) { - bool partial = false; - bool stuck = false; - Route = zone->pathing->FindRoute(From, To, partial, stuck); - AdjustRoute(Route, flymode, GetZOffset()); - - PathingDestination = To; - WaypointChanged = true; - - if (stuck) { - return HandleStuckPath(To, From); - } - - if(Route.empty()) { - return To; - } - else { - return (*Route.begin()).pos; - } - } - else { - auto node = *Route.begin(); - if (node.teleport) { - Route.pop_front(); - - if (Route.empty()) { - return To; - } - - auto nextNode = *Route.begin(); - - Teleport(nextNode.pos); - - Route.pop_front(); - - if (Route.empty()) { - return To; - } - - return (*Route.begin()).pos; - } - - return node.pos; - } - } - else { - WaypointChanged = false; - NodeReached = false; - return (*Route.begin()).pos; - } - } - - return To; -} - -glm::vec3 Mob::HandleStuckPath(const glm::vec3 &To, const glm::vec3 &From) -{ - bool partial = false; - bool stuck = false; - auto r = zone->pathing->FindRoute(To, From, partial, stuck); - Route.clear(); - - if (r.size() < 1) { - Teleport(To); - return To; - } - - auto iter = r.rbegin(); - auto final_node = (*iter); - Teleport(final_node.pos); - - if (r.size() < 2) { - return final_node.pos; - } - else { - iter++; - return (*iter).pos; - } -} - void CullPoints(std::vector &points) { if (!zone->HasMap()) { return; diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index e7504c744..6177961f4 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -5477,18 +5477,71 @@ XS(XS_Mob_CalculateHeadingToTarget) { XSRETURN(1); } +XS(XS_Mob_RunTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RunTo) { + dXSARGS; + if (items < 4 || items > 5) + Perl_croak(aTHX_ + "Usage: Mob::RunTo(THIS, float x, float y, float z)"); + { + Mob *THIS; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV *)SvRV(ST(0))); + THIS = INT2PTR(Mob *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + + THIS->RunTo(x, y, z); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_WalkTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_WalkTo) { + dXSARGS; + if (items < 4 || items > 5) + Perl_croak(aTHX_ + "Usage: Mob::WalkTo(THIS, float x, float y, float z)"); + { + Mob *THIS; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV *)SvRV(ST(0))); + THIS = INT2PTR(Mob *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + + THIS->WalkTo(x, y, z); + } + XSRETURN_EMPTY; +} + XS(XS_Mob_NavigateTo); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_NavigateTo) { dXSARGS; - if (items < 5 || items > 6) + if (items < 4 || items > 5) Perl_croak(aTHX_ - "Usage: Mob::NavigateTo(THIS, float x, float y, float z, float speed)"); + "Usage: Mob::NavigateTo(THIS, float x, float y, float z)"); { Mob *THIS; float x = (float) SvNV(ST(1)); float y = (float) SvNV(ST(2)); float z = (float) SvNV(ST(3)); - float speed = (float) SvNV(ST(4)); if (sv_derived_from(ST(0), "Mob")) { IV tmp = SvIV((SV *) SvRV(ST(0))); @@ -5499,7 +5552,7 @@ XS(XS_Mob_NavigateTo) { Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->NavigateTo(x, y, z, speed); + THIS->NavigateTo(x, y, z); } XSRETURN_EMPTY; } @@ -8660,7 +8713,9 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "WipeHateList"), XS_Mob_WipeHateList, file, "$"); newXSproto(strcpy(buf, "CheckAggro"), XS_Mob_CheckAggro, file, "$$"); newXSproto(strcpy(buf, "CalculateHeadingToTarget"), XS_Mob_CalculateHeadingToTarget, file, "$$$"); - newXSproto(strcpy(buf, "NavigateTo"), XS_Mob_NavigateTo, file, "$$$$$"); + newXSproto(strcpy(buf, "RunTo"), XS_Mob_RunTo, file, "$$$$"); + newXSproto(strcpy(buf, "WalkTo"), XS_Mob_WalkTo, file, "$$$$"); + newXSproto(strcpy(buf, "NavigateTo"), XS_Mob_NavigateTo, file, "$$$$"); newXSproto(strcpy(buf, "StopNavigation"), XS_Mob_StopNavigation, file, "$"); newXSproto(strcpy(buf, "CalculateDistance"), XS_Mob_CalculateDistance, file, "$$$$"); newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$"); diff --git a/zone/position.cpp b/zone/position.cpp index 83d1491b2..9f9e36fc2 100644 --- a/zone/position.cpp +++ b/zone/position.cpp @@ -5,6 +5,8 @@ #include #include "../common/string_util.h" +static const float position_eps = 0.0001f; + std::string to_string(const glm::vec4 &position) { return StringFormat("(%.3f, %.3f, %.3f, %.3f)", position.x,position.y,position.z,position.w); } @@ -161,8 +163,88 @@ float GetReciprocalHeading(const float heading) return result; } -bool IsPositionEqual(float x1, float y1, float z1, float x2, float y2, float z2) +bool IsPositionEqual(const glm::vec2 &p1, const glm::vec2 &p2) { - static const float eps = 0.0001f; - return std::abs(x1 - x2) < eps && std::abs(y1 - y2) < eps && std::abs(z1 - z2) < eps; + return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps; +} + +bool IsPositionEqual(const glm::vec3 &p1, const glm::vec3 &p2) +{ + return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < position_eps; +} + +bool IsPositionEqual(const glm::vec4 &p1, const glm::vec4 &p2) +{ + return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < position_eps; +} + +bool IsPositionEqualWithinCertainZ(const glm::vec3 &p1, const glm::vec3 &p2, float z_eps) { + return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < z_eps; +} + +bool IsPositionEqualWithinCertainZ(const glm::vec4 &p1, const glm::vec4 &p2, float z_eps) { + return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < z_eps; +} + +bool IsPositionWithinSimpleCylinder(const glm::vec3 &p1, const glm::vec3 &cylinder_center, float cylinder_radius, float cylinder_height) +{ + //If we're outside the height of cylinder then we're not in it (duh) + auto d = std::abs(p1.z - cylinder_center.z); + if (d > cylinder_height / 2.0) { + return false; + } + + glm::vec2 p1d(p1.x, p1.y); + glm::vec2 ccd(cylinder_center.x, cylinder_center.y); + + //If we're outside the radius of the cylinder then we're not in it (also duh) + d = Distance(p1d, ccd); + if (d > cylinder_radius) { + return false; + } + + return true; +} + +bool IsPositionWithinSimpleCylinder(const glm::vec4 &p1, const glm::vec4 &cylinder_center, float cylinder_radius, float cylinder_height) +{ + //If we're outside the height of cylinder then we're not in it (duh) + auto d = std::abs(p1.z - cylinder_center.z); + if (d > cylinder_height / 2.0) { + return false; + } + + glm::vec2 p1d(p1.x, p1.y); + glm::vec2 ccd(cylinder_center.x, cylinder_center.y); + + //If we're outside the radius of the cylinder then we're not in it (also duh) + d = Distance(p1d, ccd); + if (d > cylinder_radius) { + return false; + } + + return true; +} + +float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float y2) +{ + float y_diff = std::abs(y1 - y2); + float x_diff = std::abs(x1 - x2); + if (y_diff < 0.0000009999999974752427) + y_diff = 0.0000009999999974752427; + + float angle = atan2(x_diff, y_diff) * 180.0f * 0.3183099014828645f; // angle, nice "pi" + + // return the right thing based on relative quadrant + // I'm sure this could be improved for readability, but whatever + if (y1 >= y2) { + if (x2 >= x1) + return (90.0f - angle + 90.0f) * 511.5f * 0.0027777778f; + if (x2 <= x1) + return (angle + 180.0f) * 511.5f * 0.0027777778f; + } + if (y1 > y2 || x2 > x1) + return angle * 511.5f * 0.0027777778f; + else + return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f; } diff --git a/zone/position.h b/zone/position.h index 479f43bb4..ccccfb857 100644 --- a/zone/position.h +++ b/zone/position.h @@ -50,6 +50,15 @@ float DistanceSquaredNoZ(const glm::vec4& point1, const glm::vec4& point2); float GetReciprocalHeading(const glm::vec4& point1); float GetReciprocalHeading(const float heading); -bool IsPositionEqual(float x1, float y1, float z1, float x2, float y2, float z2); +bool IsPositionEqual(const glm::vec2 &p1, const glm::vec2 &p2); +bool IsPositionEqual(const glm::vec3 &p1, const glm::vec3 &p2); +bool IsPositionEqual(const glm::vec4 &p1, const glm::vec4 &p2); +bool IsPositionEqualWithinCertainZ(const glm::vec3 &p1, const glm::vec3 &p2, float z_eps); +bool IsPositionEqualWithinCertainZ(const glm::vec4 &p1, const glm::vec4 &p2, float z_eps); + +bool IsPositionWithinSimpleCylinder(const glm::vec3 &p1, const glm::vec3 &cylinder_center, float cylinder_radius, float cylinder_height); +bool IsPositionWithinSimpleCylinder(const glm::vec4 &p1, const glm::vec4 &cylinder_center, float cylinder_radius, float cylinder_height); + +float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float y2); #endif diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index c49328848..79806ac3a 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -427,7 +427,7 @@ void NPC::SaveGuardSpot(bool iClearGuardSpot) { } void NPC::NextGuardPosition() { - NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, GetMovespeed()); + NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); if ((m_Position.x == m_GuardPoint.x) && (m_Position.y == m_GuardPoint.y) && (m_Position.z == m_GuardPoint.z)) { if (moved) @@ -442,8 +442,24 @@ float Mob::CalculateDistance(float x, float y, float z) { return (float)sqrtf(((m_Position.x - x)*(m_Position.x - x)) + ((m_Position.y - y)*(m_Position.y - y)) + ((m_Position.z - z)*(m_Position.z - z))); } -void Mob::NavigateTo(float x, float y, float z, float speed) { - mMovementManager->NavigateTo(this, x, y, z, speed); +void Mob::WalkTo(float x, float y, float z) +{ + mMovementManager->NavigateTo(this, x, y, z, false, MovementWalking); +} + +void Mob::RunTo(float x, float y, float z) +{ + mMovementManager->NavigateTo(this, x, y, z, false, MovementRunning); +} + +void Mob::NavigateTo(float x, float y, float z) +{ + if (IsRunning()) { + mMovementManager->NavigateTo(this, x, y, z, false, MovementRunning); + } + else { + mMovementManager->NavigateTo(this, x, y, z, false, MovementWalking); + } } void Mob::StopNavigation() {