From 052f343e4dc0549958e389f588fef9284dd0bd7f Mon Sep 17 00:00:00 2001 From: regneq Date: Mon, 11 May 2015 21:42:48 -0700 Subject: [PATCH] smoother NPC pathing. (credit to Haynar from EQMacEmu) --- zone/client.h | 1 + zone/map.cpp | 37 +++++++++++- zone/map.h | 1 + zone/mob.cpp | 1 + zone/mob_ai.cpp | 148 ++++++++++++++++++++++++--------------------- zone/npc.h | 1 + zone/waypoints.cpp | 17 +++++- 7 files changed, 133 insertions(+), 73 deletions(-) diff --git a/zone/client.h b/zone/client.h index 38c5911e0..79dcf87ff 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1129,6 +1129,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + inline void ResetPositionTimer() { position_timer_counter = 0; } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); diff --git a/zone/map.cpp b/zone/map.cpp index d4bb5a268..37f394626 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -64,7 +64,7 @@ Map::~Map() { float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { if (!imp) - return false; + return BEST_Z_INVALID; glm::vec3 tmp; if(!result) @@ -93,6 +93,41 @@ float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { return BEST_Z_INVALID; } +float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const { + // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. + // + if (!imp) + return false; + + float ClosestZ = BEST_Z_INVALID; + + glm::vec3 tmp; + if (!result) + result = &tmp; + + glm::vec3 from(start.x, start.y, start.z); + glm::vec3 to(start.x, start.y, BEST_Z_INVALID); + float hit_distance; + bool hit = false; + + // first check is below us + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + ClosestZ = result->z; + + } + + // Find nearest Z above us + to.z = -BEST_Z_INVALID; + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + if (abs(from.z - result->z) < abs(ClosestZ - from.z)) + return result->z; + } + + return ClosestZ; +} + bool Map::LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const { if(!imp) return false; diff --git a/zone/map.h b/zone/map.h index 2bce1c6db..f3e81fe20 100644 --- a/zone/map.h +++ b/zone/map.h @@ -34,6 +34,7 @@ public: ~Map(); float FindBestZ(glm::vec3 &start, glm::vec3 *result) const; + float FindClosestZ(glm::vec3 &start, glm::vec3 *result) const; bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const; bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; diff --git a/zone/mob.cpp b/zone/mob.cpp index 05ec2015d..d95b3bea7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1201,6 +1201,7 @@ void Mob::SendPosition() PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; MakeSpawnUpdateNoDelta(spu); move_tic_count = 0; + tar_ndx = 20; entity_list.QueueClients(this, app, true); safe_delete(app); } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 020c833e8..18618e262 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1666,92 +1666,41 @@ void NPC::AI_DoMovement() { if (gridno > 0 || cur_wp==-2) { if (movetimercompleted==true) { // time to pause at wp is over - - int32 spawn_id = this->GetSpawnPointID(); - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - Spawn2 *found_spawn = nullptr; - - while(iterator.MoreElements()) - { - Spawn2* cur = iterator.GetData(); - iterator.Advance(); - if(cur->GetID() == spawn_id) - { - found_spawn = cur; - break; - } - } - - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(true); //depop and resart spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(false);//depop without spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else { - movetimercompleted=false; - - Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); - - //if we were under quest control (with no grid), we are done now.. - if(cur_wp == -2) { - Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); - roamer = false; - cur_wp = 0; - } - - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - - entity_list.OpenDoorsNear(CastToNPC()); - - if(!DistractedFromGrid) { - //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); - - //setup our next waypoint, if we are still on our normal grid - //remember that the quest event above could have done anything it wanted with our grid - if(gridno > 0) { - CastToNPC()->CalculateNewWaypoint(); - } - } - else { - DistractedFromGrid = false; - } - } + AI_SetupNextWaypoint(); } // endif (movetimercompleted==true) else if (!(AIwalking_timer->Enabled())) { // currently moving + bool doMove = true; if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); - SetWaypointPause(); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - SetMoving(false); - if (m_CurrentWayPoint.w >= 0.0) { - SetHeading(m_CurrentWayPoint.w); + if (cur_wp_pause != 0) { + SetWaypointPause(); + if (GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + SetMoving(false); + if (m_CurrentWayPoint.w >= 0.0) { + SetHeading(m_CurrentWayPoint.w); + } + SendPosition(); } - SendPosition(); //kick off event_waypoint arrive char temp[16]; sprintf(temp, "%d", cur_wp); parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); + // start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted. + if (!AIwalking_timer->Enabled()) + AI_SetupNextWaypoint(); + else + doMove = false; // wipe feign memory since we reached our first waypoint if(cur_wp == 1) ClearFeignMemory(); } - else - { // not at waypoint yet, so keep moving + if (doMove) + { // not at waypoint yet or at 0 pause WP, so keep moving if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); else @@ -1829,6 +1778,67 @@ void NPC::AI_DoMovement() { } } } +void NPC::AI_SetupNextWaypoint() { + int32 spawn_id = this->GetSpawnPointID(); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = nullptr; + + while (iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if (cur->GetID() == spawn_id) + { + found_spawn = cur; + break; + } + } + + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(true); //depop and restart spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(false);//depop without spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else { + movetimercompleted = false; + + Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); + + //if we were under quest control (with no grid), we are done now.. + if (cur_wp == -2) { + Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + roamer = false; + cur_wp = 0; + } + + if (GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + + entity_list.OpenDoorsNear(CastToNPC()); + + if (!DistractedFromGrid) { + //kick off event_waypoint depart + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); + + //setup our next waypoint, if we are still on our normal grid + //remember that the quest event above could have done anything it wanted with our grid + if (GetGrid() > 0) { + CastToNPC()->CalculateNewWaypoint(); + } + } + else { + DistractedFromGrid = false; + } + } +} // Note: Mob that caused this may not get added to the hate list until after this function call completes void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { diff --git a/zone/npc.h b/zone/npc.h index 95a857460..f960c736e 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -119,6 +119,7 @@ public: virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Stop(); void AI_DoMovement(); + void AI_SetupNextWaypoint(); bool AI_AddNPCSpells(uint32 iDBSpellsID); bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID); virtual bool AI_EngagedCastCheck(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index d3b85bf03..b81e100d7 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -394,6 +394,7 @@ void NPC::SetWaypointPause() if (cur_wp_pause == 0) { AIwalking_timer->Start(100); + AIwalking_timer->Trigger(); } else { @@ -514,7 +515,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b return true; } + bool send_update = false; int compare_steps = IsBoat() ? 1 : 20; + if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { float new_x = m_Position.x + m_TargetV.x*tar_vector; @@ -637,13 +640,14 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Position.y = y; m_Position.z = z; + tar_ndx = 20; Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); } } else { - tar_vector/=20; + tar_vector/=20.0f; float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; @@ -699,12 +703,19 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) + { SendPosUpdate(1); + CastToClient()->ResetPositionTimer(); + } else + { + // force an update now + move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); - - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); + } pLastChange = Timer::GetCurrentTime(); + return true; }