fix(Core/Movement): prevent false MovementInform on gossip pause (#25428)
This commit is contained in:
parent
c839ddf829
commit
49b3926f6c
8 changed files with 160 additions and 37 deletions
|
|
@ -3091,6 +3091,21 @@ CreatureMovementData const& Creature::GetMovementTemplate() const
|
|||
return GetCreatureTemplate()->Movement;
|
||||
}
|
||||
|
||||
void Creature::PauseMovementForInteraction()
|
||||
{
|
||||
if (uint32 pause = GetMovementTemplate().GetInteractionPauseTimer())
|
||||
{
|
||||
uint8 pauseSlot = MOTION_SLOT_IDLE;
|
||||
|
||||
if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == POINT_MOTION_TYPE)
|
||||
pauseSlot = MOTION_SLOT_ACTIVE;
|
||||
else if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == POINT_MOTION_TYPE)
|
||||
pauseSlot = MOTION_SLOT_CONTROLLED;
|
||||
|
||||
PauseMovement(pause, pauseSlot);
|
||||
}
|
||||
}
|
||||
|
||||
void Creature::AllLootRemovedFromCorpse()
|
||||
{
|
||||
if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public:
|
|||
[[nodiscard]] bool IsTrigger() const { return HasFlagsExtra(CREATURE_FLAG_EXTRA_TRIGGER); }
|
||||
[[nodiscard]] bool IsGuard() const { return HasFlagsExtra(CREATURE_FLAG_EXTRA_GUARD); }
|
||||
CreatureMovementData const& GetMovementTemplate() const;
|
||||
void PauseMovementForInteraction();
|
||||
[[nodiscard]] bool CanWalk() const { return GetMovementTemplate().IsGroundAllowed(); }
|
||||
[[nodiscard]] bool CanSwim() const override;
|
||||
[[nodiscard]] bool CanEnterWater() const override;
|
||||
|
|
|
|||
|
|
@ -47,9 +47,7 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recvData)
|
|||
if (!unit->IsBattleMaster()) // it's not battlemaster
|
||||
return;
|
||||
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
unit->PauseMovement(pause);
|
||||
unit->PauseMovementForInteraction();
|
||||
unit->SetHomePosition(unit->GetPosition());
|
||||
|
||||
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry());
|
||||
|
|
|
|||
|
|
@ -871,9 +871,7 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
|
|||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
}
|
||||
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = vendor->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
vendor->PauseMovement(pause);
|
||||
vendor->PauseMovementForInteraction();
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (vendor->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ void WorldSession::SendTrainerList(Creature* npc)
|
|||
return;
|
||||
}
|
||||
|
||||
npc->PauseMovementForInteraction();
|
||||
|
||||
trainer->SendSpells(npc, _player, GetSessionDbLocaleIndex());
|
||||
}
|
||||
|
||||
|
|
@ -165,9 +167,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
|
|||
//if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
unit->PauseMovement(pause);
|
||||
unit->PauseMovementForInteraction();
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (unit->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
|
|
|
|||
|
|
@ -93,9 +93,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData)
|
|||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = creature->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
creature->PauseMovement(pause);
|
||||
creature->PauseMovementForInteraction();
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (creature->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,16 @@
|
|||
#include "ObjectAccessor.h"
|
||||
#include "Player.h"
|
||||
#include "World.h"
|
||||
#include <limits>
|
||||
|
||||
//----- Point Movement Generator
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::DoInitialize(T* unit)
|
||||
{
|
||||
_stalled = false;
|
||||
_hasBeenStalled = false;
|
||||
_pauseTime.reset();
|
||||
|
||||
if (unit->HasUnitState(UNIT_STATE_NOT_MOVE) || unit->IsMovementPreventedByCasting())
|
||||
{
|
||||
// the next line is to ensure that a new spline is created in DoUpdate() once the unit is no longer rooted/stunned
|
||||
|
|
@ -101,7 +106,7 @@ void PointMovementGenerator<T>::DoInitialize(T* unit)
|
|||
}
|
||||
|
||||
template<class T>
|
||||
bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
||||
bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
|
||||
{
|
||||
if (!unit)
|
||||
return false;
|
||||
|
|
@ -115,38 +120,102 @@ bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
|||
if (unit->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
{
|
||||
if (!unit->HasUnitState(UNIT_STATE_CHARGING))
|
||||
{
|
||||
unit->StopMoving();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
if (id != EVENT_CHARGE_PREPATH && i_recalculateSpeed && !unit->movespline->Finalized())
|
||||
if (_pauseTime.has_value())
|
||||
{
|
||||
if (diff >= static_cast<uint32>(_pauseTime.value()))
|
||||
_pauseTime.reset();
|
||||
else
|
||||
{
|
||||
_pauseTime = static_cast<int32>(_pauseTime.value() - diff);
|
||||
return true;
|
||||
}
|
||||
|
||||
_hasBeenStalled = false;
|
||||
_stalled = false;
|
||||
i_recalculateSpeed = true;
|
||||
}
|
||||
|
||||
// Relaunch path when speed changed or when resuming from a stall.
|
||||
// Keep an indefinitely paused movement stalled until Resume() clears _stalled.
|
||||
if (id != EVENT_CHARGE_PREPATH && !_stalled && (i_recalculateSpeed || _hasBeenStalled))
|
||||
{
|
||||
i_recalculateSpeed = false;
|
||||
Movement::MoveSplineInit init(unit);
|
||||
auto rebasePrecomputedPath = [this, unit](std::optional<uint32> offset = std::nullopt)
|
||||
{
|
||||
Movement::PointsArray rebasedPath;
|
||||
G3D::Vector3 currentPos(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ());
|
||||
|
||||
if (m_precomputedPath.empty())
|
||||
return rebasedPath;
|
||||
|
||||
if (offset.has_value())
|
||||
{
|
||||
if (offset.value() >= m_precomputedPath.size())
|
||||
{
|
||||
rebasedPath.push_back(currentPos);
|
||||
rebasedPath.push_back(m_precomputedPath.back());
|
||||
return rebasedPath;
|
||||
}
|
||||
|
||||
rebasedPath.insert(rebasedPath.end(), m_precomputedPath.begin() + offset.value(), m_precomputedPath.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 closestPointIndex = 0;
|
||||
float closestPointDist = std::numeric_limits<float>::max();
|
||||
for (uint32 pointIndex = 1; pointIndex < m_precomputedPath.size(); ++pointIndex)
|
||||
{
|
||||
float const sqDist = (currentPos - m_precomputedPath[pointIndex]).squaredLength();
|
||||
if (sqDist < closestPointDist)
|
||||
{
|
||||
closestPointDist = sqDist;
|
||||
closestPointIndex = pointIndex;
|
||||
}
|
||||
}
|
||||
|
||||
rebasedPath.insert(rebasedPath.end(), m_precomputedPath.begin() + closestPointIndex, m_precomputedPath.end());
|
||||
}
|
||||
|
||||
// MovebyPath requires the first point to be the mover's current position.
|
||||
rebasedPath.insert(rebasedPath.begin(), currentPos);
|
||||
return rebasedPath;
|
||||
};
|
||||
|
||||
// xinef: speed changed during path execution, calculate remaining path and launch it once more
|
||||
if (m_precomputedPath.size())
|
||||
{
|
||||
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
|
||||
Movement::PointsArray::iterator offsetItr = m_precomputedPath.begin();
|
||||
std::advance(offsetItr, offset);
|
||||
m_precomputedPath.erase(m_precomputedPath.begin(), offsetItr);
|
||||
if (!unit->movespline->Finalized())
|
||||
{
|
||||
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
|
||||
m_precomputedPath = rebasePrecomputedPath(offset);
|
||||
|
||||
// restore 0 element (current position)
|
||||
m_precomputedPath.insert(m_precomputedPath.begin(), G3D::Vector3(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()));
|
||||
if (m_precomputedPath.size() > 2)
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
else if (m_precomputedPath.size() == 2)
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unit was stopped (finalized) due to Pause/StopMoving; trim path from current position.
|
||||
m_precomputedPath = rebasePrecomputedPath();
|
||||
|
||||
if (m_precomputedPath.size() > 2)
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
else if (m_precomputedPath.size() == 2)
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z, true);
|
||||
if (m_precomputedPath.size() > 2)
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
else if (m_precomputedPath.size() == 2)
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z, true);
|
||||
else
|
||||
init.MoveTo(i_x, i_y, i_z, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
init.MoveTo(i_x, i_y, i_z, true);
|
||||
|
||||
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
|
||||
init.SetVelocity(speed);
|
||||
|
||||
|
|
@ -159,14 +228,21 @@ bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
|||
init.SetAnimation(*_animTier);
|
||||
|
||||
if (i_orientation > 0.0f)
|
||||
{
|
||||
init.SetFacing(i_orientation);
|
||||
}
|
||||
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
return !unit->movespline->Finalized();
|
||||
// If finalized but we were stalled (paused) keep generator active so Finalize() won't be called
|
||||
if (unit->movespline->Finalized())
|
||||
{
|
||||
if (_hasBeenStalled)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
|
@ -186,10 +262,35 @@ void PointMovementGenerator<T>::DoFinalize(T* unit)
|
|||
}
|
||||
}
|
||||
|
||||
if (unit->movespline->Finalized())
|
||||
// Only inform AI if this is a real arrival, not a finalize caused by a Pause/Stop
|
||||
if (unit->movespline->Finalized() && !_hasBeenStalled)
|
||||
MovementInform(unit);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::Pause(uint32 timer)
|
||||
{
|
||||
_stalled = timer ? false : true;
|
||||
_hasBeenStalled = true;
|
||||
if (timer)
|
||||
_pauseTime = static_cast<int32>(timer);
|
||||
else
|
||||
_pauseTime.reset();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::Resume(uint32 overrideTimer)
|
||||
{
|
||||
_hasBeenStalled = false;
|
||||
_stalled = false;
|
||||
if (overrideTimer)
|
||||
_pauseTime = static_cast<int32>(overrideTimer);
|
||||
else
|
||||
_pauseTime.reset();
|
||||
|
||||
i_recalculateSpeed = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::DoReset(T* unit)
|
||||
{
|
||||
|
|
@ -236,6 +337,11 @@ template void PointMovementGenerator<Creature>::DoReset(Creature*);
|
|||
template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
||||
template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||
|
||||
template void PointMovementGenerator<Player>::Pause(uint32);
|
||||
template void PointMovementGenerator<Creature>::Pause(uint32);
|
||||
template void PointMovementGenerator<Player>::Resume(uint32);
|
||||
template void PointMovementGenerator<Creature>::Resume(uint32);
|
||||
|
||||
void AssistanceMovementGenerator::Finalize(Unit* unit)
|
||||
{
|
||||
unit->ToCreature()->SetNoCallAssistance(false);
|
||||
|
|
|
|||
|
|
@ -39,13 +39,17 @@ public:
|
|||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
void MovementInform(T*);
|
||||
|
||||
void unitSpeedChanged() { i_recalculateSpeed = true; }
|
||||
void Pause(uint32 timer = 0) override;
|
||||
void Resume(uint32 overrideTimer = 0) override;
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
|
||||
void unitSpeedChanged() override { i_recalculateSpeed = true; }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() override { return POINT_MOTION_TYPE; }
|
||||
|
||||
bool GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; return true; }
|
||||
private:
|
||||
|
|
@ -53,7 +57,7 @@ private:
|
|||
float i_x, i_y, i_z;
|
||||
float speed;
|
||||
float i_orientation;
|
||||
bool i_recalculateSpeed;
|
||||
bool i_recalculateSpeed{};
|
||||
Movement::PointsArray m_precomputedPath;
|
||||
bool _generatePath;
|
||||
bool _forceDestination;
|
||||
|
|
@ -62,6 +66,9 @@ private:
|
|||
ForcedMovement _forcedMovement;
|
||||
ObjectGuid _facingTargetGuid;
|
||||
std::optional<AnimTier> _animTier;
|
||||
bool _stalled{};
|
||||
bool _hasBeenStalled{};
|
||||
std::optional<int32> _pauseTime;
|
||||
};
|
||||
|
||||
class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
|
||||
|
|
@ -70,8 +77,8 @@ public:
|
|||
AssistanceMovementGenerator(float _x, float _y, float _z) :
|
||||
PointMovementGenerator<Creature>(0, _x, _y, _z, FORCED_MOVEMENT_NONE) {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
|
||||
void Finalize(Unit*);
|
||||
MovementGeneratorType GetMovementGeneratorType() override { return ASSISTANCE_MOTION_TYPE; }
|
||||
void Finalize(Unit*) override;
|
||||
};
|
||||
|
||||
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue