aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Movement/MotionMaster.cpp2
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp5
-rw-r--r--src/server/game/Movement/PathGenerator.cpp9
-rw-r--r--src/server/game/Movement/PathGenerator.h1
-rw-r--r--src/server/game/Spells/Spell.cpp70
-rw-r--r--src/server/game/Spells/Spell.h2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp36
7 files changed, 89 insertions, 36 deletions
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 4afc3a0f610..a049481bee9 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -767,6 +767,8 @@ void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_C
MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH);
+ // If this is ever changed to not happen immediately then all spell effect handlers that use this must be updated
+
// Charge movement is not started when using EVENT_CHARGE_PREPATH
Movement::MoveSplineInit init(_owner);
init.MovebyPath(path.GetPath());
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 01021660876..52c5be97046 100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -195,9 +195,12 @@ void PointMovementGenerator::Finalize(Unit* owner, bool active, bool movementInf
void PointMovementGenerator::MovementInform(Unit* owner)
{
+ // deliver EVENT_CHARGE to scripts, EVENT_CHARGE_PREPATH is just internal implementation detail of this movement generator
+ uint32 movementId = _movementId == EVENT_CHARGE_PREPATH ? EVENT_CHARGE : _movementId;
+
if (Creature* creature = owner->ToCreature())
if (creature->AI())
- creature->AI()->MovementInform(POINT_MOTION_TYPE, _movementId);
+ creature->AI()->MovementInform(POINT_MOTION_TYPE, movementId);
}
//---- AssistanceMovementGenerator
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index 88a3d18bf28..ec046c78289 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -968,6 +968,15 @@ float PathGenerator::Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) c
return (p1 - p2).squaredLength();
}
+float PathGenerator::GetPathLength() const
+{
+ float length = 0.0f;
+ for (std::size_t i = 0; i < _pathPoints.size() - 1; ++i)
+ length += (_pathPoints[i + 1] - _pathPoints[i]).length();
+
+ return length;
+}
+
void PathGenerator::ShortenPathUntilDist(G3D::Vector3 const& target, float dist)
{
if (GetPathType() == PATHFIND_BLANK || _pathPoints.size() < 2)
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
index 96acbd20616..b06573e7253 100644
--- a/src/server/game/Movement/PathGenerator.h
+++ b/src/server/game/Movement/PathGenerator.h
@@ -79,6 +79,7 @@ class TC_GAME_API PathGenerator
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
Movement::PointsArray const& GetPath() const { return _pathPoints; }
+ float GetPathLength() const;
PathType GetPathType() const { return _type; }
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 74433b0b958..f13171af2df 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -850,8 +850,42 @@ uint64 Spell::CalculateDelayMomentForDst(float launchDelay) const
void Spell::RecalculateDelayMomentForDst()
{
- m_delayMoment = CalculateDelayMomentForDst(0.0f);
- m_caster->m_Events.ModifyEventTime(_spellEvent, Milliseconds(GetDelayStart() + m_delayMoment));
+ UpdateDelayMomentForDst(CalculateDelayMomentForDst(0.0f));
+}
+
+void Spell::UpdateDelayMomentForDst(uint64 hitDelay)
+{
+ m_delayMoment = hitDelay;
+
+ if (GetDelayStart())
+ m_caster->m_Events.ModifyEventTime(_spellEvent, Milliseconds(GetDelayStart() + m_delayMoment));
+}
+
+void Spell::UpdateDelayMomentForUnitTarget(Unit* unit, uint64 hitDelay)
+{
+ auto itr = std::find_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [unit](Spell::TargetInfo const& targetInfo)
+ {
+ return targetInfo.TargetGUID == unit->GetGUID();
+ });
+
+ uint64 oldDelay = itr->TimeDelay;
+ itr->TimeDelay = hitDelay;
+
+ if (hitDelay && (!m_delayMoment || m_delayMoment > hitDelay))
+ m_delayMoment = hitDelay;
+ else if (m_delayMoment && oldDelay < hitDelay)
+ {
+ // if new hit delay is greater than old delay for this target we must check all other spell targets to see if m_delayMoment can be increased
+ auto minDelayTargetItr = std::min_element(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [](Spell::TargetInfo const& itr, Spell::TargetInfo const& smallest)
+ {
+ return itr.TimeDelay < smallest.TimeDelay;
+ });
+
+ m_delayMoment = minDelayTargetItr->TimeDelay;
+ }
+
+ if (GetDelayStart())
+ m_caster->m_Events.ModifyEventTime(_spellEvent, Milliseconds(GetDelayStart() + m_delayMoment));
}
void Spell::SelectEffectImplicitTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, uint32& processedEffectMask)
@@ -3915,7 +3949,9 @@ uint64 Spell::handle_delayed(uint64 t_offset)
return 0;
}
+ // when spell has a single missile we hit all targets (except caster) at the same time
bool single_missile = m_targets.HasDst();
+ bool ignoreTargetInfoTimeDelay = single_missile;
uint64 next_time = 0;
if (!m_launchHandled)
@@ -3926,21 +3962,13 @@ uint64 Spell::handle_delayed(uint64 t_offset)
HandleLaunchPhase();
m_launchHandled = true;
- if (m_delayMoment > t_offset)
- {
- if (single_missile)
- return m_delayMoment;
-
- next_time = m_delayMoment;
- if ((m_UniqueTargetInfo.size() > 2 || (m_UniqueTargetInfo.size() == 1 && m_UniqueTargetInfo.front().TargetGUID == m_caster->GetGUID())) || !m_UniqueGOTargetInfo.empty())
- {
- t_offset = 0; // if LaunchDelay was present then the only target that has timeDelay = 0 is m_caster - and that is the only target we want to process now
- }
- }
}
- if (single_missile && !t_offset)
- return m_delayMoment;
+ if (m_delayMoment > t_offset)
+ {
+ ignoreTargetInfoTimeDelay = false;
+ next_time = m_delayMoment;
+ }
Player* modOwner = m_caster->GetSpellModOwner();
if (modOwner)
@@ -3948,7 +3976,7 @@ uint64 Spell::handle_delayed(uint64 t_offset)
PrepareTargetProcessing();
- if (!m_immediateHandled && t_offset)
+ if (!m_immediateHandled && m_delayMoment <= t_offset)
{
_handle_immediate_phase();
m_immediateHandled = true;
@@ -3959,13 +3987,13 @@ uint64 Spell::handle_delayed(uint64 t_offset)
std::vector<TargetInfo> delayedTargets;
m_UniqueTargetInfo.erase(std::remove_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [&](TargetInfo& target) -> bool
{
- if (single_missile || target.TimeDelay <= t_offset)
+ if (ignoreTargetInfoTimeDelay || target.TimeDelay <= t_offset)
{
target.TimeDelay = t_offset;
delayedTargets.emplace_back(std::move(target));
return true;
}
- else if (next_time == 0 || target.TimeDelay < next_time)
+ else if (!single_missile && (next_time == 0 || target.TimeDelay < next_time))
next_time = target.TimeDelay;
return false;
@@ -3979,13 +4007,13 @@ uint64 Spell::handle_delayed(uint64 t_offset)
std::vector<GOTargetInfo> delayedGOTargets;
m_UniqueGOTargetInfo.erase(std::remove_if(m_UniqueGOTargetInfo.begin(), m_UniqueGOTargetInfo.end(), [&](GOTargetInfo& goTarget) -> bool
{
- if (single_missile || goTarget.TimeDelay <= t_offset)
+ if (ignoreTargetInfoTimeDelay || goTarget.TimeDelay <= t_offset)
{
goTarget.TimeDelay = t_offset;
delayedGOTargets.emplace_back(std::move(goTarget));
return true;
}
- else if (next_time == 0 || goTarget.TimeDelay < next_time)
+ else if (!single_missile && (next_time == 0 || goTarget.TimeDelay < next_time))
next_time = goTarget.TimeDelay;
return false;
@@ -8111,7 +8139,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
if (m_Spell->m_spellInfo->LaunchDelay)
ASSERT(n_offset == uint64(std::floor(m_Spell->m_spellInfo->LaunchDelay * 1000.0f)));
else
- ASSERT(n_offset == m_Spell->GetDelayMoment());
+ ASSERT(n_offset == m_Spell->GetDelayMoment(), UI64FMTD " == " UI64FMTD, n_offset, m_Spell->GetDelayMoment());
// re-plan the event for the delay moment
m_Spell->GetCaster()->m_Events.AddEvent(this, Milliseconds(e_time + n_offset), false);
return false; // event not complete
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index d14329c7223..96b029eae5d 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -613,6 +613,8 @@ class TC_GAME_API Spell
uint64 GetDelayMoment() const { return m_delayMoment; }
uint64 CalculateDelayMomentForDst(float launchDelay) const;
void RecalculateDelayMomentForDst();
+ void UpdateDelayMomentForDst(uint64 hitDelay);
+ void UpdateDelayMomentForUnitTarget(Unit* unit, uint64 hitDelay);
uint8 GetRuneState() const { return m_runesState; }
void SetRuneState(uint8 value) { m_runesState = value; }
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 35a4879dd5c..c50847c70e2 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -54,6 +54,7 @@
#include "Map.h"
#include "MiscPackets.h"
#include "MotionMaster.h"
+#include "MoveSpline.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
@@ -3799,23 +3800,19 @@ void Spell::EffectCharge()
// Spell is not using explicit target - no generated path
if (!m_preGeneratedPath)
{
- //unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster));
- if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
- speed = pos.GetExactDist(m_caster) / speed;
- unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed, EVENT_CHARGE, false, unitTarget, spellEffectExtraData ? &*spellEffectExtraData : nullptr);
+ m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster);
+ m_preGeneratedPath->CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
}
- else
- {
- if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
- {
- G3D::Vector3 pos = m_preGeneratedPath->GetActualEndPosition();
- speed = Position(pos.x, pos.y, pos.z).GetExactDist(m_caster) / speed;
- }
- unitCaster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, unitTarget, spellEffectExtraData ? &*spellEffectExtraData : nullptr);
- }
+ if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ speed = m_preGeneratedPath->GetPathLength() / speed;
+
+ unitCaster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, unitTarget, spellEffectExtraData ? &*spellEffectExtraData : nullptr);
+
+ // abuse implementation detail of MoveCharge accepting PathGenerator argument (instantly started spline)
+ UpdateDelayMomentForUnitTarget(unitTarget, unitCaster->movespline->Duration());
}
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
@@ -3851,7 +3848,18 @@ void Spell::EffectChargeDest()
pos = unitCaster->GetFirstCollisionPosition(dist, angle);
}
- unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ PathGenerator path(unitCaster);
+ path.CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
+
+ float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE;
+
+ if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ speed = path.GetPathLength() / speed;
+
+ unitCaster->GetMotionMaster()->MoveCharge(path, speed);
+
+ // abuse implementation detail of MoveCharge accepting PathGenerator argument (instantly started spline)
+ UpdateDelayMomentForDst(unitCaster->movespline->Duration());
}
else if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT)
{