aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Movement
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Movement')
-rw-r--r--src/server/game/Movement/MotionMaster.cpp33
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.cpp28
2 files changed, 50 insertions, 11 deletions
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index bccfa78dd0d..fb55617f58a 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -29,6 +29,7 @@
#include "PathGenerator.h"
#include "Player.h"
#include "ScriptSystem.h"
+#include <boost/container/static_vector.hpp>
#include <algorithm>
#include <iterator>
@@ -806,20 +807,44 @@ void MotionMaster::MoveKnockbackFrom(Position const& origin, float speedXY, floa
if (_owner->GetTypeId() == TYPEID_PLAYER)
return;
- if (speedXY < 0.01f)
+ if (std::abs(speedXY) < 0.01f && std::abs(speedZ) < 0.01f)
return;
Position dest = _owner->GetPosition();
+ float o = dest == origin ? 0.0f : _owner->GetRelativeAngle(origin) + float(M_PI);
+ if (speedXY < 0)
+ {
+ speedXY = -speedXY;
+ o = o - float(M_PI);
+ }
+
+ if (speedZ < 0)
+ speedZ = -speedZ; // doesn't seem to be supported on official servers - packet sent for knockback with positive and negative speed has the same flags and JumpGravity
+
float moveTimeHalf = speedZ / Movement::gravity;
float dist = 2 * moveTimeHalf * speedXY;
float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
- // Use a mmap raycast to get a valid destination.
- _owner->MovePositionToFirstCollision(dest, dist, _owner->GetRelativeAngle(origin) + float(M_PI));
+ boost::container::static_vector<G3D::Vector3, 3> path;
+ path.push_back(PositionToVector3(dest));
+
+ if (dist > 0.01f)
+ {
+ // Use a mmap raycast to get a valid destination.
+ _owner->MovePositionToFirstCollision(dest, dist, o);
+ path.push_back(PositionToVector3(dest));
+ }
+ else
+ {
+ // vertical knockbacks get a fake 0.5 higher additional point to avoid clientside spline length checks
+ // sniffs confirmed that it is always 0.5, no matter what the max height is
+ path.push_back(PositionToVector3(dest.GetPositionWithOffset({ 0.0f, 0.0f, 0.5f })));
+ path.push_back(PositionToVector3(dest));
+ }
std::function<void(Movement::MoveSplineInit&)> initializer = [=, effect = (spellEffectExtraData ? Optional<Movement::SpellEffectExtraData>(*spellEffectExtraData) : Optional<Movement::SpellEffectExtraData>())](Movement::MoveSplineInit& init)
{
- init.MoveTo(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false);
+ init.MovebyPath({ path.data(), path.size() });
init.SetParabolic(max_height, 0);
init.SetOrientationFixed(true);
init.SetVelocity(speedXY);
diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp
index a317b018ed6..7d76570b81d 100644
--- a/src/server/game/Movement/Spline/MoveSpline.cpp
+++ b/src/server/game/Movement/Spline/MoveSpline.cpp
@@ -120,9 +120,8 @@ void MoveSpline::computeFallElevation(int32 time_point, float& el) const
struct FallInitializer
{
- FallInitializer(float _start_elevation) : start_elevation(_start_elevation) { }
float start_elevation;
- inline int32 operator()(Spline<int32>& s, int32 i)
+ inline int32 operator()(Spline<int32> const& s, int32 i) const
{
return Movement::computeFallTime(start_elevation - s.getPoint(i+1).z, false) * 1000.f;
}
@@ -132,11 +131,21 @@ enum{
minimal_duration = 1
};
+struct ParabolicInPlaceInitializer
+{
+ float parabolic_amplitude;
+ inline int32 operator()(Spline<int32> const& /*s*/, int32 /*i*/)
+ {
+ return time += Movement::computeFallTime(parabolic_amplitude, false) * 1000.f;
+ }
+
+ int32 time = minimal_duration;
+};
+
struct CommonInitializer
{
- CommonInitializer(float _velocity) : velocityInv(1000.f/_velocity), time(minimal_duration) { }
float velocityInv;
- int32 time;
+ int32 time = minimal_duration;
inline int32 operator()(Spline<int32>& s, int32 i)
{
time += (s.SegLength(i) * velocityInv);
@@ -160,12 +169,17 @@ void MoveSpline::init_spline(MoveSplineInitArgs const& args)
// init spline timestamps
if (splineflags.Falling)
{
- FallInitializer init(spline.getPoint(spline.first()).z);
+ FallInitializer init{ .start_elevation = spline.getPoint(spline.first()).z };
+ spline.initLengths(init);
+ }
+ else if (splineflags.Parabolic && args.velocity < 0.01f)
+ {
+ ParabolicInPlaceInitializer init{ .parabolic_amplitude = args.parabolic_amplitude };
spline.initLengths(init);
}
else
{
- CommonInitializer init(args.velocity);
+ CommonInitializer init{ .velocityInv = 1000.0f / args.velocity };
spline.initLengths(init);
}
@@ -254,7 +268,7 @@ bool MoveSplineInitArgs::Validate(Unit const* unit)
}()
CHECK(path.size() > 1, unit->GetDebugInfo());
- CHECK(velocity >= 0.01f, unit->GetDebugInfo());
+ CHECK(velocity >= 0.01f || (flags.Parabolic && parabolic_amplitude != 0.0f), unit->GetDebugInfo());
CHECK(effect_start_point < std::ssize(path), unit->GetDebugInfo());
CHECK(_checkPathLengths(), unit->GetGUID().ToString());
if (spellEffectExtra)