diff --git a/zone/command.cpp b/zone/command.cpp index dd0bd95bb..3e9abd29f 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1264,7 +1264,7 @@ void command_movement(Client *c, const Seperator *sep) auto &mgr = MobMovementManager::Get(); if (sep->arg[1][0] == 0) { - c->Message(0, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop"); + c->Message(0, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); return; } @@ -1316,8 +1316,18 @@ void command_movement(Client *c, const Seperator *sep) target->StopNavigation(); } + else if (strcasecmp(sep->arg[1], "packet") == 0) + { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(0, "No target found."); + return; + } + + mgr.SendCommandToClients(target, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atoi(sep->arg[6]), ClientRangeAny); + } else { - c->Message(0, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop"); + c->Message(0, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index f7f5b4594..0ae9600d9 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3438,6 +3438,22 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z) { args.push_back(&evt.area_type); parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0, &args); } + + if (zone->watermap) { + auto mode = n->GetFlyMode(); + if (mode == GravityBehavior::Ground) { + if (zone->watermap->InLiquid(glm::vec3(x, y, z))) + { + n->SetFlyMode(GravityBehavior::Water); + } + } + else if (mode == GravityBehavior::Water) { + if (!zone->watermap->InLiquid(glm::vec3(x, y, z))) + { + n->SetFlyMode(GravityBehavior::Ground); + } + } + } } void EntityList::AddArea(int id, int type, float min_x, float max_x, float min_y, diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index ed9c4d5c9..6c740a397 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1367,15 +1367,7 @@ void Mob::AI_Process() { auto targetPosition = glm::vec3(target->GetX(), target->GetY(), target->GetZ()); if (!zone->watermap->InLiquid(targetPosition)) { Mob *tar = hate_list.GetEntWithMostHateOnList(this); - if (tar == target) { - WipeHateList(); - Heal(); - BuffFadeAll(); - AI_walking_timer->Start(100); - time_until_can_move = Timer::GetCurrentTime(); - return; - } - else if (tar != nullptr) { + if (tar != nullptr && tar != target) { SetTarget(tar); return; } diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index d73f0fef1..c4fc822a8 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -178,12 +178,12 @@ public: glm::vec2 ndir = glm::normalize(dir); double distance_moved = frame_time * current_speed * 0.4f * 1.45f; - if (distance_moved > len) { - m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); - + if (distance_moved > len) { if (m->IsNPC()) { entity_list.ProcessMove(m->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); } + + m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); m->TryFixZ(); return true; @@ -196,11 +196,11 @@ public: double start_z = m_move_to_z - m_total_v_dist; double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); - m->SetPosition(npos.x, npos.y, z_at_pos); - if (m->IsNPC()) { entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, z_at_pos); } + + m->SetPosition(npos.x, npos.y, z_at_pos); } return false; @@ -210,7 +210,7 @@ public: return m_started; } -private: +protected: double m_move_to_x; double m_move_to_y; @@ -224,6 +224,101 @@ private: double m_total_v_dist; }; +class SwimToCommand : public MoveToCommand +{ +public: + SwimToCommand(float x, float y, float z, MobMovementMode mode) : MoveToCommand(x, y, z, mode) { + + } + + 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->SetMoving(true); + m->SetHeading(m->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); + + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + m_total_h_dist = DistanceNoZ(m->GetPosition(), glm::vec4(m_move_to_x, m_move_to_z, 0.0f, 0.0f)); + m_total_v_dist = m_move_to_z - m->GetZ(); + 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 >= 1.5) { + 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::vec2 tar(m_move_to_x, m_move_to_y); + glm::vec2 pos(p.x, p.y); + double len = glm::distance(pos, tar); + if (len == 0) { + return true; + } + + m->SetMoved(true); + + glm::vec2 dir = tar - pos; + glm::vec2 ndir = glm::normalize(dir); + double distance_moved = frame_time * current_speed * 0.4f * 1.45f; + + if (distance_moved > len) { + if (m->IsNPC()) { + entity_list.ProcessMove(m->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); + } + + m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); + return true; + } + else { + glm::vec2 npos = pos + (ndir * static_cast(distance_moved)); + + len -= distance_moved; + double total_distance_traveled = m_total_h_dist - len; + double start_z = m_move_to_z - m_total_v_dist; + double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); + + if (m->IsNPC()) { + entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, z_at_pos); + } + + m->SetPosition(npos.x, npos.y, z_at_pos); + } + + return false; + } +}; + class TeleportToCommand : public IMovementCommand { public: @@ -243,14 +338,14 @@ public: 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); - if (m->IsNPC()) { entity_list.ProcessMove(m->CastToNPC(), m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); } + 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; } @@ -603,50 +698,72 @@ void MobMovementManager::FillCommandStruct(PlayerPositionUpdateServer_Struct *sp void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode) { - //If who is underwater & xyz is underwater & who can see xyz - //Create a route directly from who to xyz - //else - //Create Route - if (zone->HasMap() && zone->HasWaterMap()) { - if (zone->watermap->InLiquid(who->GetPosition()) && zone->watermap->InLiquid(glm::vec3(x, y, z)) && zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { - auto iter = _impl->Entries.find(who); - auto &ent = (*iter); - - PushMoveTo(ent.second, x, y, z, mode); - PushStopMoving(ent.second); - return; - } + if (!zone->HasMap() || !zone->HasWaterMap()) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + + PushMoveTo(ent.second, x, y, z, mode); + PushStopMoving(ent.second); + } + + if (zone->watermap->InLiquid(who->GetPosition()) + && zone->watermap->InLiquid(glm::vec3(x, y, z)) + && zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + + PushSwimTo(ent.second, x, y, z, mode); + PushStopMoving(ent.second); + return; } 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); - + IPathfinder::IPath route; + if (who->IsUnderwaterOnly()) { + route = zone->pathing->FindRoute(glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, PathingWater | PathingLava | PathingVWater | PathingPortal | PathingPrefer); + } + else { + route = zone->pathing->FindRoute(glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, PathingNotDisabled ^ PathingZoneLine); + } + //if route empty or only has two points, and we have los, then just force npc to move to location if (route.size() < 3) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); if (zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z)) && route.size() > 0) { - auto &first = route.front(); auto &last = route.back(); - - PushMoveTo(ent.second, x, y, z, mode); + + if (zone->watermap->InLiquid(who->GetPosition())) { + PushSwimTo(ent.second, x, y, z, mode); + } + else { + PushMoveTo(ent.second, x, y, z, mode); + } + PushStopMoving(ent.second); return; } else if(route.size() < 2) { + if (zone->watermap->InLiquid(who->GetPosition())) { + PushSwimTo(ent.second, x, y, z, mode); + } + else { + PushMoveTo(ent.second, x, y, z, mode); + } + PushMoveTo(ent.second, x, y, z, mode); PushStopMoving(ent.second); return; } } - + auto &first = route.front(); auto &last = route.back(); - + if (zone->HasWaterMap()) { //If who is underwater & who is not at the first node //Add node at who @@ -656,7 +773,7 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove IPathfinder::IPathNode node(who->GetPosition()); route.push_front(node); } - + //If xyz is underwater & xyz is not at the last node //Add node at xyz if (!IsPositionEqualWithinCertainZ(glm::vec3(x, y, z), last.pos, 5.0f) @@ -666,60 +783,54 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove route.push_back(node); } } - + //adjust route AdjustRoute(route, who->GetFlyMode(), who->GetZOffset()); - + auto eiter = _impl->Entries.find(who); auto &ent = (*eiter); auto iter = route.begin(); glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); bool first_node = true; - + //for each node while (iter != route.end()) { auto ¤t_node = (*iter); - + iter++; - + if (iter == route.end()) { continue; } - + previous_pos = current_node.pos; auto &next_node = (*iter); - + if (first_node) { - + if (mode == MovementWalking) { auto h = who->CalculateHeadingToTarget(next_node.pos.x, next_node.pos.y); PushRotateTo(ent.second, who, h, mode); } - + first_node = false; } - - //if underwater only mob and node -> node + 1 is moving to land (terminate route, npc will go to the point where it would normally exit the water but no further) - if (who->IsUnderwaterOnly()) { - if (zone->HasWaterMap() && !zone->watermap->InLiquid(next_node.pos)) { - PushStopMoving(ent.second); - return; - } - } - + //move to / teleport to node + 1 if (next_node.teleport && next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) { PushTeleportTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, CalculateHeadingAngleBetweenPositions(current_node.pos.x, current_node.pos.y, next_node.pos.x, next_node.pos.y)); } else { - if (next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) - { + if (zone->watermap->InLiquid(previous_pos)) { + PushSwimTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); + } + else { PushMoveTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); } } } - + //if stuck then handle stuck if (stuck) { PushMoveTo(ent.second, x, y, z, mode); @@ -739,6 +850,11 @@ void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, flo ent.Commands.push_back(std::unique_ptr(new MoveToCommand(x, y, z, mode))); } +void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode) +{ + ent.Commands.push_back(std::unique_ptr(new SwimToCommand(x, y, z, mode))); +} + void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode) { auto from = FixHeading(who->GetHeading()); diff --git a/zone/mob_movement_manager.h b/zone/mob_movement_manager.h index 4e8d73451..13b7b3b5b 100644 --- a/zone/mob_movement_manager.h +++ b/zone/mob_movement_manager.h @@ -60,6 +60,7 @@ private: 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 PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode); void PushStopMoving(MobMovementEntry &ent); diff --git a/zone/npc.cpp b/zone/npc.cpp index 05db28e1d..98d20bd72 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -39,6 +39,7 @@ #include "spawn2.h" #include "zone.h" #include "quest_parser_collection.h" +#include "water_map.h" #include #include @@ -412,6 +413,20 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, Gravit AISpellVar.idle_no_sp_recast_min = RuleI(Spells, AI_IdleNoSpellMinRecast); AISpellVar.idle_no_sp_recast_max = RuleI(Spells, AI_IdleNoSpellMaxRecast); AISpellVar.idle_beneficial_chance = RuleI(Spells, AI_IdleBeneficialChance); + + if (zone->watermap) { + auto mode = GetFlyMode(); + if (mode == GravityBehavior::Ground) { + if (zone->watermap->InLiquid(m_Position)) { + SetFlyMode(GravityBehavior::Water); + } + } + else if (mode == GravityBehavior::Water) { + if (!zone->watermap->InLiquid(m_Position)) { + SetFlyMode(GravityBehavior::Ground); + } + } + } } NPC::~NPC() diff --git a/zone/pathfinder_interface.h b/zone/pathfinder_interface.h index 85a153813..92405691a 100644 --- a/zone/pathfinder_interface.h +++ b/zone/pathfinder_interface.h @@ -6,6 +6,24 @@ class Client; class Seperator; +enum PathingPolyFlags +{ + PathingNormal = 1, + PathingWater = 2, + PathingLava = 4, + PathingZoneLine = 8, + PathingPvP = 16, + PathingSlime = 32, + PathingIce = 64, + PathingVWater = 128, + PathingGeneralArea = 256, + PathingPortal = 512, + PathingPrefer = 1024, + PathingDisabled = 2048, + PathingAll = 65535, + PathingNotDisabled = PathingAll ^ PathingDisabled +}; + class IPathfinder { public: @@ -29,7 +47,7 @@ public: IPathfinder() { } virtual ~IPathfinder() { } - virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck) = 0; + virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled) = 0; virtual glm::vec3 GetRandomLocation(const glm::vec3 &start) = 0; virtual void DebugCommand(Client *c, const Seperator *sep) = 0; diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index cddf8d151..5e3217191 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -33,7 +33,7 @@ PathfinderNavmesh::~PathfinderNavmesh() Clear(); } -IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck) +IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags) { partial = false; @@ -50,7 +50,7 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl glm::vec3 dest_location(end.x, end.z, end.y); dtQueryFilter filter; - filter.setIncludeFlags(65535U ^ 2048); + filter.setIncludeFlags(flags); filter.setAreaCost(0, 1.0f); //Normal filter.setAreaCost(1, 3.0f); //Water filter.setAreaCost(2, 5.0f); //Lava diff --git a/zone/pathfinder_nav_mesh.h b/zone/pathfinder_nav_mesh.h index 40fca0b3b..42dd28988 100644 --- a/zone/pathfinder_nav_mesh.h +++ b/zone/pathfinder_nav_mesh.h @@ -9,7 +9,7 @@ public: PathfinderNavmesh(const std::string &path); virtual ~PathfinderNavmesh(); - virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck); + virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled); virtual glm::vec3 GetRandomLocation(const glm::vec3 &start); virtual void DebugCommand(Client *c, const Seperator *sep); diff --git a/zone/pathfinder_null.cpp b/zone/pathfinder_null.cpp index 4a5cbbe63..400b98be4 100644 --- a/zone/pathfinder_null.cpp +++ b/zone/pathfinder_null.cpp @@ -1,6 +1,6 @@ #include "pathfinder_null.h" -IPathfinder::IPath PathfinderNull::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck) +IPathfinder::IPath PathfinderNull::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags) { partial = false; stuck = false; diff --git a/zone/pathfinder_null.h b/zone/pathfinder_null.h index 242dcf755..e5da24146 100644 --- a/zone/pathfinder_null.h +++ b/zone/pathfinder_null.h @@ -8,7 +8,7 @@ public: PathfinderNull() { } virtual ~PathfinderNull() { } - virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck); + virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled); virtual glm::vec3 GetRandomLocation(const glm::vec3 &start); virtual void DebugCommand(Client *c, const Seperator *sep) { } }; diff --git a/zone/pathfinder_waypoint.cpp b/zone/pathfinder_waypoint.cpp index 9e9831e9c..3d3f0162d 100644 --- a/zone/pathfinder_waypoint.cpp +++ b/zone/pathfinder_waypoint.cpp @@ -114,7 +114,7 @@ PathfinderWaypoint::~PathfinderWaypoint() { } -IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck) +IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags) { stuck = false; partial = false; diff --git a/zone/pathfinder_waypoint.h b/zone/pathfinder_waypoint.h index 1570eca24..8fc29d0c4 100644 --- a/zone/pathfinder_waypoint.h +++ b/zone/pathfinder_waypoint.h @@ -11,7 +11,7 @@ public: PathfinderWaypoint(const std::string &path); virtual ~PathfinderWaypoint(); - virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck); + virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled); virtual glm::vec3 GetRandomLocation(const glm::vec3 &start); virtual void DebugCommand(Client *c, const Seperator *sep);