diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 4ac0c7f23..904bc5246 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -263,6 +263,146 @@ protected: double m_total_v_dist; }; +class FlyToCommand : public IMovementCommand { +public: + FlyToCommand(float x, float y, float z, MobMovementMode mob_movement_mode) + { + m_distance_moved_since_correction = 0.0; + m_move_to_x = x; + m_move_to_y = y; + m_move_to_z = z; + m_move_to_mode = mob_movement_mode; + m_last_sent_time = 0.0; + m_last_sent_speed = 0; + m_started = false; + m_total_h_dist = 0.0; + m_total_v_dist = 0.0; + } + + virtual ~FlyToCommand() + { + + } + + /** + * @param mob_movement_manager + * @param mob + * @return + */ + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->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) { + if (mob->IsFeared()) { + current_speed = mob->GetFearSpeed(); + } + else { + current_speed = mob->GetRunspeed(); + } + } + else { + current_speed = mob->GetWalkspeed(); + } + + if (!m_started) { + m_started = true; + //rotate to the point + mob->SetMoving(true); + mob->SetHeading(mob->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(mob->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); + m_total_v_dist = m_move_to_z - mob->GetZ(); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + } + + //When speed changes + if (current_speed != m_last_sent_speed) { + m_distance_moved_since_correction = 0.0; + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + } + + //If x seconds have passed without sending an update. + if (current_time - m_last_sent_time >= 0.5) { + m_distance_moved_since_correction = 0.0; + m_last_sent_speed = current_speed; + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + } + + auto &p = mob->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; + } + + mob->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 (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); + } + + mob->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); + + if (RuleB(Map, FixZWhenPathing)) { + mob->FixZ(); + } + 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 (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), npos.x, npos.y, z_at_pos); + } + + mob->SetPosition(npos.x, npos.y, z_at_pos); + } + + return false; + } + + virtual bool Started() const + { + return m_started; + } + +protected: + double m_distance_moved_since_correction; + 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; + double m_total_h_dist; + double m_total_v_dist; +}; + class SwimToCommand : public MoveToCommand { public: SwimToCommand(float x, float y, float z, MobMovementMode mob_movement_mode) : MoveToCommand(x, y, z, mob_movement_mode) @@ -921,6 +1061,8 @@ void MobMovementManager::FillCommandStruct( */ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { + Mob *target=who->GetTarget(); + if (!zone->HasMap() || !zone->HasWaterMap()) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); @@ -936,6 +1078,16 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove else if (who->IsUnderwaterOnly()) { UpdatePathUnderwater(who, x, y, z, mob_movement_mode); } + // If we can fly, and we have a target and we have LoS, simply fly to them. + // if we ever lose LoS we go back to mesh run mode. + else if (target && who->GetFlyMode() == GravityBehavior::Flying && + who->CheckLosFN(x,y,z,target->GetSize())) { + auto iter = _impl->Entries.find(who); + auto &ent = (*iter); + + PushFlyTo(ent.second, x, y, z, mob_movement_mode); + PushStopMoving(ent.second); + } else { UpdatePathGround(who, x, y, z, mob_movement_mode); } @@ -1275,6 +1427,18 @@ void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, ent.Commands.push_back(std::unique_ptr(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mob_movement_mode))); } +/** + * @param ent + * @param x + * @param y + * @param z + * @param mob_movement_mode + */ +void MobMovementManager::PushFlyTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) +{ + ent.Commands.push_back(std::unique_ptr(new FlyToCommand(x, y, z, mob_movement_mode))); +} + /** * @param mob_movement_entry */ diff --git a/zone/mob_movement_manager.h b/zone/mob_movement_manager.h index ff7e08436..85d062804 100644 --- a/zone/mob_movement_manager.h +++ b/zone/mob_movement_manager.h @@ -85,6 +85,7 @@ private: void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading); void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode); void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode); + void PushFlyTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode); void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode); void PushStopMoving(MobMovementEntry &mob_movement_entry); void PushEvadeCombat(MobMovementEntry &mob_movement_entry);