diff options
| -rw-r--r-- | src/server/game/Movement/MotionMaster.cpp | 2 | ||||
| -rwxr-xr-x | src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp | 5 | ||||
| -rw-r--r-- | src/server/game/Movement/PathGenerator.cpp | 9 | ||||
| -rw-r--r-- | src/server/game/Movement/PathGenerator.h | 1 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 70 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.h | 2 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 36 |
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) { |
