diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4b355cd7ce..08c3732d92 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11329,22 +11329,7 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) if (forced && IsClientControlled()) { - Player* player = const_cast(GetClientControlling()); - uint32 const counter = player->GetSession()->GetOrderCounter(); - - // register forced speed changes for WorldSession::HandleForceSpeedChangeAck - // and do it only for real sent packets and use run for run/mounted as client expected - ++player->m_forced_speed_changes[mtype]; - - WorldPacket data(speedOpcodes[static_cast(SpeedOpcodeIndex::PC)], 18); - data << GetPackGUID(); - data << counter; - if (mtype == MOVE_RUN) - data << uint8(0); // new 2.1.0 - - data << GetSpeed(mtype); - player->GetSession()->SendPacket(&data); - player->GetSession()->IncrementOrderCounter(); + SendSpeedToController(mtype, const_cast(GetClientControlling())); } else if (forced) { @@ -11377,6 +11362,24 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) } } +void Unit::SendSpeedToController(UnitMoveType mtype, Player* target) const +{ + SpeedOpcodePair const& speedOpcodes = SetSpeed2Opc_table[mtype]; + uint32 const counter = target->GetSession()->GetOrderCounter(); + + ++target->m_forced_speed_changes[mtype]; + + WorldPacket data(speedOpcodes[static_cast(SpeedOpcodeIndex::PC)], 18); + data << GetPackGUID(); + data << counter; + if (mtype == MOVE_RUN) + data << uint8(0); + + data << GetSpeed(mtype); + target->GetSession()->SendPacket(&data); + target->GetSession()->IncrementOrderCounter(); +} + void Unit::setDeathState(DeathState s, bool despawn) { // death state needs to be updated before RemoveAllAurasOnDeath() calls HandleChannelDeathItem(..) so that @@ -14698,6 +14701,10 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au _oldFactionId = GetFaction(); SetFaction(charmer->GetFaction()); + // snapshot current speed rates so we can detect speed changes + for (uint8 i = MOVE_WALK; i < MAX_MOVE_TYPE; ++i) + _charmStartSpeedRate[i] = m_speed_rate[i]; + // Set charmed charmer->SetCharm(this, true); @@ -14849,9 +14856,6 @@ void Unit::RemoveCharmedBy(Unit* charmer) CastStop(); AttackStop(); - // xinef: update speed after charming - UpdateSpeed(MOVE_RUN, false); - // xinef: do not break any controlled motion slot if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) { @@ -14954,7 +14958,15 @@ void Unit::RemoveCharmedBy(Unit* charmer) RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); } else - ToPlayer()->SetClientControl(this, true); // verified + { + Player* targetPlayer = ToPlayer(); + targetPlayer->SetClientControl(this, true); // verified + + // Re-sync only the speed types that changed during the charm. + for (uint8 i = MOVE_WALK; i < MAX_MOVE_TYPE; ++i) + if (m_speed_rate[i] != _charmStartSpeedRate[i]) + SendSpeedToController(UnitMoveType(i), targetPlayer); + } // a guardian should always have charminfo if (playerCharmer && this != charmer->GetFirstControlled()) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 041ab0beaa..0700f1adca 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1744,6 +1744,7 @@ public: [[nodiscard]] float GetSpeedRate(UnitMoveType mtype) const { return m_speed_rate[mtype]; } void SetSpeed(UnitMoveType mtype, float rate, bool forced = false); void SetSpeedRate(UnitMoveType mtype, float rate) { m_speed_rate[mtype] = rate; } + void SendSpeedToController(UnitMoveType mtype, Player* target) const; void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); } @@ -2186,6 +2187,9 @@ protected: float m_speed_rate[MAX_MOVE_TYPE]; + // snapshot of speed rates taken when SetCharmedBy() is called + float _charmStartSpeedRate[MAX_MOVE_TYPE]{}; + CharmInfo* m_charmInfo; SharedVisionList m_sharedVision;