bool TargetedMovementGeneratorMedium<T, D>::Update(T& owner, const uint32& time_diff) { if (!i_target.isValid() || !i_target->IsInWorld()) return false; if (!owner.isAlive()) return true; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) { D::_clearUnitStateMove(owner); return true; } if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && owner.hasUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT)) { D::_clearUnitStateMove(owner); return true; } // prevent movement while casting spells with cast time or channel time if (owner.IsNonMeleeSpellCasted(false, false, true)) { if (!owner.IsStopped()) owner.StopMoving(); return true; } // prevent crash after creature killed pet if (static_cast<D*>(this)->_lostTarget(owner)) { D::_clearUnitStateMove(owner); return true; } bool targetMoved = false; i_recheckDistance.Update(time_diff); if (i_recheckDistance.Passed()) { i_recheckDistance.Reset(this->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE ? 50 : 100); G3D::Vector3 dest = owner.movespline->FinalDestination(); targetMoved = RequiresNewPosition(owner, dest.x, dest.y, dest.z); } if (m_speedChanged || targetMoved) _setTargetLocation(owner, targetMoved); if (owner.movespline->Finalized()) { if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget())) owner.SetInFront(i_target.getTarget()); if (!i_targetReached) { i_targetReached = true; static_cast<D*>(this)->_reachTarget(owner); } } return true; }
bool TargetedMovementGeneratorMedium<T, D>::Update(T& owner, const uint32& time_diff) { if (!i_target.isValid() || !i_target->IsInWorld()) return false; if (!owner.isAlive()) return true; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) { D::_clearUnitStateMove(owner); return true; } if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && owner.hasUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT)) { D::_clearUnitStateMove(owner); return true; } // prevent movement while casting spells with cast time or channel time if (owner.IsNonMeleeSpellCasted(false, false, true)) { if (!owner.IsStopped()) owner.StopMoving(); return true; } // prevent crash after creature killed pet if (static_cast<D*>(this)->_lostTarget(owner)) { D::_clearUnitStateMove(owner); return true; } bool targetMoved = false; i_recheckDistance.Update(time_diff); if (i_recheckDistance.Passed()) { i_recheckDistance.Reset(this->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE ? 50 : 100); G3D::Vector3 dest = owner.movespline->FinalDestination(); targetMoved = RequiresNewPosition(owner, dest.x, dest.y, dest.z); if (!targetMoved) { // This unit is in hitbox of target // howewer we have to check if the target not moved a bit to update the orientation // client do it automatically 'visually' but it need this new orientation send or it will retrieve old orientation in some case (like stun) G3D::Vector3 currTargetPos; i_target->GetPosition(currTargetPos.x, currTargetPos.y, currTargetPos.z); if (owner.movespline->Finalized() && currTargetPos != m_prevTargetPos) { // position changed we need to adjust owner orientation to continue facing it m_prevTargetPos = currTargetPos; owner.SetInFront(i_target.getTarget()); // set movementinfo orientation, needed for next movement if any float angle = owner.GetAngle(i_target.getTarget()); owner.SetFacingTo(angle); // inform client that orientation changed //sLog.outString("Updating facing to with angle (%3.3f)!!!", angle); } } } if (m_speedChanged || targetMoved) _setTargetLocation(owner, targetMoved); if (owner.movespline->Finalized()) { if (!i_targetReached) { i_targetReached = true; static_cast<D*>(this)->_reachTarget(owner); } } return true; }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!i_target.isValid() || !i_target->IsInWorld()) return; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) return; float x, y, z; // i_path can be nullptr in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !i_path) { owner.GetPosition(x, y, z); // prevent redundant micro-movement for pets, other followers. if (!RequiresNewPosition(owner, x, y, z)) { if (!owner.movespline->Finalized()) return; } // Chase Movement and angle == 0 case: Chase to current angle else if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && i_angle == 0.0f) { i_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), this->GetDynamicTargetDistance(owner, false), i_target->GetAngle(&owner)); } // Targeted movement to at i_offset distance from target and i_angle from target facing else { i_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), this->GetDynamicTargetDistance(owner, false), i_target->GetOrientation() + i_angle); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 end = i_path->getEndPosition(); x = end.x; y = end.y; z = end.z; } if (!i_path) i_path = new PathFinder(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); i_path->calculate(x, y, z, forceDest); if (i_path->getPathType() & PATHFIND_NOPATH) return; D::_addUnitStateMove(owner); i_targetReached = false; m_speedChanged = false; Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->getPath()); init.SetWalk(((D*)this)->EnableWalking()); init.Launch(); }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!m_target.isValid() || !m_target->IsInWorld()) return; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) return; if (!IsAbleMoveWhenCast(owner.GetEntry()) && owner.IsNonMeleeSpellCasted(false)) return; if (!m_target->isInAccessablePlaceFor(&owner)) return; float x, y, z; // m_path can be nullptr in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !m_path) { owner.GetPosition(x, y, z); // Prevent redundant micro-movement for pets, other followers. if (!RequiresNewPosition(owner, x, y, z)) { if (!owner.movespline->Finalized()) return; } else { float absAngle; // Chase Movement and angle == 0 case: Chase to current angle if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && m_angle == 0.0f) absAngle = m_target->GetAngle(&owner); // Targeted movement to at m_offset distance from target and m_angle from target facing else absAngle = m_target->GetOrientation() + m_angle; m_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), static_cast<D*>(this)->GetDynamicTargetDistance(owner, false), absAngle); // Add hover height if (owner.IsLevitating()) z += owner.GetFloatValue(UNIT_FIELD_HOVERHEIGHT); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 endPos = m_path->getEndPosition(); x = endPos.x; y = endPos.y; z = endPos.z; } if (!m_path) m_path = new PathFinder(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); m_path->calculate(x, y, z, forceDest); m_speedChanged = false; // don't move if path points equivalent if (m_path->getStartPosition() == m_path->getEndPosition()) { owner.StopMoving(true); return; } if (m_path->getPathType() & PATHFIND_NOPATH) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"TargetedMovementGeneratorMedium:: unit %s cannot find path to %s (%f, %f, %f), gained PATHFIND_NOPATH! Owerride used.", owner.GetGuidStr().c_str(), m_target.isValid() ? m_target->GetObjectGuid().GetString().c_str() : "<none>", x, y, z); //return; } D::_addUnitStateMove(owner); m_targetReached = false; Movement::MoveSplineInit<Unit*> init(owner); init.MovebyPath(m_path->getPath()); init.SetSmooth(); // fix broken fly movement for old creatures init.SetWalk( ((D*)this)->EnableWalking() || // hack for old creatures with bugged fly animation (owner.GetTypeId() == TYPEID_UNIT && owner.IsLevitating() && owner.GetFloatValue(UNIT_FIELD_HOVERHEIGHT) == 0.0f)); init.Launch(); }
bool TargetedMovementGeneratorMedium<T, D>::update(T& owner, const uint32& time_diff) { if (!m_target.isValid() || !m_target->IsInWorld()) return false; if (!owner.isAlive()) return true; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE) || (owner.IsInUnitState(UNIT_ACTION_CHASE) && owner.hasUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT))) { D::_clearUnitStateMove(owner); return true; } // prevent movement while casting spells with cast time or channel time if (!IsAbleMoveWhenCast(owner.GetEntry()) && owner.IsNonMeleeSpellCasted(false, false, true)) { owner.StopMoving(); return true; } // prevent crash after creature killed pet if (static_cast<D*>(this)->_lostTarget(owner)) { D::_clearUnitStateMove(owner); if (m_waitTargetTimer >= TARGET_NOT_ACCESSIBLE_MAX_TIMER) return false; else { m_waitTargetTimer += 5 * time_diff; return true; } } if (!m_target->isInAccessablePlaceFor(&owner)) return true; bool moveToTarget = false; m_recheckDistanceTimer.Update(time_diff); if (m_recheckDistanceTimer.Passed()) { m_recheckDistanceTimer.Reset(this->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE ? RECHECK_DISTANCE_TIMER_FOLLOW : RECHECK_DISTANCE_TIMER); G3D::Vector3 dest = owner.movespline->FinalDestination(); moveToTarget = dest == Vector3() ? true : RequiresNewPosition(owner, dest.x, dest.y, dest.z); if (owner.GetTypeId() == TYPEID_UNIT && !((Creature*)&owner)->IsPet()) { Unit* pVictim = owner.getVictim(); if (pVictim && pVictim->GetObjectGuid() == m_target->GetObjectGuid()) { if (!pVictim->isAlive() && owner.movespline->Finalized()) return false; if (!m_offset && owner.movespline->Finalized() && !owner.CanReachWithMeleeAttack(pVictim) && !m_target->m_movementInfo.HasMovementFlag(MOVEFLAG_PENDINGSTOP)) { if (m_waitTargetTimer < TARGET_NOT_ACCESSIBLE_MAX_TIMER) m_waitTargetTimer += m_recheckDistanceTimer.GetExpiry(); else return false; } else m_waitTargetTimer = 0; } } } if (m_speedChanged || moveToTarget) _setTargetLocation(owner, moveToTarget); if (owner.movespline->Finalized()) { if (fabs(m_angle) < M_NULL_F && !owner.HasInArc(0.01f, m_target.getTarget())) owner.SetInFront(m_target.getTarget()); if (!m_targetReached) { m_targetReached = true; static_cast<D*>(this)->_reachTarget(owner); } } return true; }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!i_target.isValid() || !i_target->IsInWorld()) return; if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) || owner.isDead()) return; if (owner.GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner.ToCreature())) return; float x, y, z; // i_path can be NULL in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !i_path) { owner.GetPosition(x, y, z); // prevent redundant micro-movement for pets, other followers. if (!RequiresNewPosition(owner, x, y, z)) { if (!owner.movespline->Finalized()) return; } else { if (!i_offset) { // to nearest random contact position i_target->GetRandomContactPoint(&owner, x, y, z, 0, CONTACT_DISTANCE); // Sometimes target is available only from certain angles // in that case we use the exact location (blizzlike hahavior) if (fabsf(i_target->GetPositionZ() - z) > owner.GetObjectSize()) i_target->GetPosition(x, y, z); } else if (!i_angle && !owner.HasUnitState(UNIT_STATE_FOLLOW)) { // caster chase i_target->GetContactPoint(&owner, x, y, z, i_offset * urand(80, 95) * 0.01f); } else { // to at i_offset distance from target and i_angle from target facing i_target->GetClosePoint(x, y, z, owner.GetObjectSize(), i_offset, i_angle); // Sometimes target is available only from certain angles // in that case we use the exact location (blizzlike hahavior) if (fabsf(i_target->GetPositionZ() - z) > (owner.GetObjectSize() + i_offset)) i_target->GetPosition(x, y, z); } } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 end = i_path->getEndPosition(); x = end.x; y = end.y; z = end.z; } if (!i_path) i_path = new PathInfo(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->HasUnitTypeMask(UNIT_MASK_MINION) && owner.HasUnitState(UNIT_STATE_FOLLOW)); bool result = i_path->Update(x, y, z, forceDest); if (!result || (i_path->getPathType() & PATHFIND_NOPATH)) { // Cant reach target m_speedChanged = true; return; } i_targetReached = false; m_speedChanged = false; owner.AddUnitState(UNIT_STATE_CHASE); Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->getFullPath()); init.SetWalk(((D*)this)->EnableWalking(owner)); static_cast<D*>(this)->_updateSpeed(owner); init.Launch(); }
bool TargetedMovementGeneratorMedium<T, D>::Update(T& owner, const uint32& time_diff) { if (!i_target.isValid() || !i_target->IsInWorld()) return false; if (!owner.IsAlive()) return true; if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED)) return true; // prevent movement while casting spells with cast time or channel time if (owner.IsNonMeleeSpellCast(false, false, true) || owner.HasUnitState(UNIT_STATE_CASTING)) { if (!owner.IsStopped()) owner.StopMoving(); return true; } if (!owner.HasAuraType(SPELL_AURA_MOD_INVISIBILITY) && !owner.CanSeeOrDetect(i_target.getTarget(), true)) { owner.AttackStop(); if (owner.GetOwner()) owner.GetMotionMaster()->MoveFollow(owner.GetOwner(), PET_FOLLOW_DIST, owner.GetFollowAngle()); else owner.StopMoving(); return true; } // prevent crash after creature killed pet if (!owner.HasUnitState(UNIT_STATE_FOLLOW) && owner.GetVictim() != i_target.getTarget()) return true; if (i_path && i_path->getPathType() & PATHFIND_NOPATH) { if (Creature* me = owner.ToCreature()) { if (m_evadeTimer <= time_diff) { if (me->AI()) me->AI()->EnterEvadeMode(); } else m_evadeTimer -= time_diff; } return true; } bool targetMoved = false; i_recheckDistance.Update(time_diff); if (i_recheckDistance.Passed()) { i_recheckDistance.Reset(this->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE ? 50 : 100); G3D::Vector3 dest = owner.movespline->FinalDestination(); targetMoved = RequiresNewPosition(owner, dest.x, dest.y, dest.z); } if (m_speedChanged || targetMoved) _setTargetLocation(owner, targetMoved); if (owner.movespline->Finalized()) { if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget())) owner.SetInFront(i_target.getTarget()); if (!i_targetReached) { i_targetReached = true; static_cast<D*>(this)->_reachTarget(owner); } } // Implemented for PetAI to handle resetting flags when pet owner reached if (owner.movespline->Finalized()) MovementInform(owner); return true; }
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination) { if (!i_target.isValid() || !i_target->IsInWorld()) return; if (owner.hasUnitState(UNIT_STAT_NOT_MOVE)) return; float x, y, z; // i_path can be nullptr in case this is the first call for this MMGen (via Update) // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned if (updateDestination || !i_path) { owner.GetPosition(x, y, z); // prevent redundant micro-movement for pets, other followers. if (!RequiresNewPosition(owner, x, y, z)) { if (!owner.movespline->Finalized()) return; } // Chase Movement and angle == 0 case: Chase to current angle else if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE && i_angle == 0.0f) { i_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), this->GetDynamicTargetDistance(owner, false), i_target->GetAngle(&owner)); } // Targeted movement to at i_offset distance from target and i_angle from target facing else { i_target->GetNearPoint(&owner, x, y, z, owner.GetObjectBoundingRadius(), this->GetDynamicTargetDistance(owner, false), i_target->GetOrientation() + i_angle); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) G3D::Vector3 end = i_path->getEndPosition(); x = end.x; y = end.y; z = end.z; } if (!i_path) i_path = new PathFinder(&owner); // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); i_path->calculate(x, y, z, forceDest); if (i_path->getPathType() & PATHFIND_NOPATH) return; auto& path = i_path->getPath(); if (this->GetMovementGeneratorType() == CHASE_MOTION_TYPE) { if (float offset = this->i_offset) // need to cut path until most distant viable point { float dist = this->i_offset * CHASE_MOVE_CLOSER_FACTOR + CHASE_DEFAULT_RANGE_FACTOR * this->i_target->GetCombinedCombatReach(&owner); float tarX, tarY, tarZ; i_target->GetPosition(tarX, tarY, tarZ); auto iter = std::next(path.begin(), 1); // need to start at index 1, index 0 is start position and filled in init.Launch() for (; iter != path.end(); ++iter) { G3D::Vector3 data = (*iter); if (!i_target->IsWithinDist3d(data.x, data.y, data.z, dist)) continue; if (!owner.GetMap()->IsInLineOfSight(tarX, tarY, tarZ + 2.0f, data.x, data.y, data.z + 2.0f, IGNORE_M2)) continue; // both in LOS and in range - advance to next and stop ++iter; break; } if (iter != path.end()) path.erase(iter, path.end()); } } D::_addUnitStateMove(owner); i_targetReached = false; m_speedChanged = false; Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->getPath()); init.SetWalk(((D*)this)->EnableWalking()); init.Launch(); }