mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Spells: Fixed effects targeting the caster not hitting him immediately on spell launch if the spell targets a dest
* Also improve hit timing for charge spells
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user