From 59e45c251eb53abb0ed4b37a57254cbdf415e69a Mon Sep 17 00:00:00 2001 From: IntelligentQuantum Date: Thu, 28 Apr 2022 03:48:44 +0430 Subject: [PATCH] fix(Core/Spell): Improvements to path generation for Charge mechanic (#11534) It has its flaws but it can be improved and it's actually a lot better than what we currently have. --- src/server/game/Movement/MotionMaster.cpp | 13 +++++ src/server/game/Movement/MotionMaster.h | 1 + .../PointMovementGenerator.cpp | 2 +- src/server/game/Spells/Spell.cpp | 48 +++++++------------ src/server/game/Spells/Spell.h | 3 +- src/server/game/Spells/SpellEffects.cpp | 13 +++-- src/server/shared/SharedDefines.h | 5 ++ 7 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 4137758cc..6dd0d8f8a 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -608,6 +608,19 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, } } +void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/) +{ + G3D::Vector3 dest = path.GetActualEndPosition(); + + MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH); + + // Charge movement is not started when using EVENT_CHARGE_PREPATH + Movement::MoveSplineInit init(_owner); + init.MovebyPath(path.GetPath()); + init.SetVelocity(speed); + init.Launch(); +} + void MotionMaster::MoveSeekAssistance(float x, float y, float z) { // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index a23f7ce31..718177828 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -219,6 +219,7 @@ public: void MoveTakeoff(uint32 id, float x, float y, float z, float speed = 0.0f); // pussywizard: added for easy calling by passing 3 floats x, y, z void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, const Movement::PointsArray* path = nullptr, bool generatePath = false, float orientation = 0.0f, ObjectGuid targetGUID = ObjectGuid::Empty); + void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE); void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = 0) diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp index 501558066..6e8f80065 100644 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -117,7 +117,7 @@ bool PointMovementGenerator::DoUpdate(T* unit, uint32 /*diff*/) unit->AddUnitState(UNIT_STATE_ROAMING_MOVE); - if (i_recalculateSpeed && !unit->movespline->Finalized()) + if (id != EVENT_CHARGE_PREPATH && i_recalculateSpeed && !unit->movespline->Finalized()) { i_recalculateSpeed = false; Movement::MoveSplineInit init(unit); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index ebedfbb0d..93c8f5ed6 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -646,7 +646,6 @@ Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, m_glyphIndex = 0; m_preCastSpell = 0; m_spellAura = nullptr; - m_pathFinder = nullptr; // pussywizard _scriptsLoaded = false; //Auto Shot & Shoot (wand) @@ -701,7 +700,6 @@ Spell::~Spell() } delete m_spellValue; - delete m_pathFinder; // pussywizard CheckEffectExecuteData(); } @@ -6001,40 +5999,28 @@ SpellCastResult Spell::CheckCast(bool strict) { Unit* target = m_targets.GetUnitTarget(); if (!target) - return SPELL_FAILED_BAD_TARGETS; + return SPELL_FAILED_DONT_REPORT; - Position pos; - target->GetChargeContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); + // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells + if (!target->IsWithinLOSInMap(m_caster)) //Do full LoS/Path check. Don't exclude m2 + return SPELL_FAILED_LINE_OF_SIGHT; - if (m_caster->GetMapId() == 618) // pussywizard: 618 Ring of Valor - pos.m_positionZ = std::max(pos.m_positionZ, 28.28f); + float objSize = target->GetCombatReach(); + float range = m_spellInfo->GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict - float maxdist = m_caster->GetMeleeRange(target); - if (!target->IsInDist(&pos, maxdist)) + m_preGeneratedPath = std::make_unique(m_caster); + m_preGeneratedPath->SetPathLengthLimit(range); + + // first try with raycast, if it fails fall back to normal path + bool result = m_preGeneratedPath->CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false); + if (m_preGeneratedPath->GetPathType() & PATHFIND_SHORT) + return SPELL_FAILED_NOPATH; + else if (!result || m_preGeneratedPath->GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE)) + return SPELL_FAILED_NOPATH; + else if (m_preGeneratedPath->IsInvalidDestinationZ(target)) // Check position z, if not in a straight line return SPELL_FAILED_NOPATH; - if (m_caster->GetMapId() == 618) // pussywizard: 618 Ring of Valor - { - if (!((target->GetPositionZ() > 32.0f) ^ (m_caster->GetPositionZ() > 32.0f))) - break; - return SPELL_FAILED_NOPATH; - } - else if (m_caster->GetMapId() == 572) // pussywizard: 572 Ruins of Lordaeron - { - if (pos.GetPositionX() < 1275.0f || m_caster->GetPositionX() < 1275.0f) // special case (acid) - break; // can't force path because the way is around and the path is too long - } - - if (m_caster->GetTransport() != target->GetTransport()) - return SPELL_FAILED_NOPATH; - if (m_caster->GetTransport()) - break; - - m_pathFinder = new PathGenerator(m_caster); - m_pathFinder->CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + 0.15f, false); - G3D::Vector3 endPos = m_pathFinder->GetEndPosition(); // also check distance between target and the point calculated by mmaps - if (m_pathFinder->GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE) || target->GetExactDistSq(endPos.x, endPos.y, endPos.z) > maxdist * maxdist || m_pathFinder->getPathLength() > (40.0f + (m_caster->HasAura(58097) ? 5.0f : 0.0f))) - return SPELL_FAILED_NOPATH; + m_preGeneratedPath->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), objSize); // move back } if (Player* player = m_caster->ToPlayer()) player->SetCanTeleport(true); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index febc675ed..75cecae7b 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -544,8 +544,6 @@ public: UsedSpellMods m_appliedMods; - PathGenerator* m_pathFinder; // pussywizard: for precomputing path for charge - int32 GetCastTime() const { return m_casttime; } bool IsAutoRepeat() const { return m_autoRepeat; } void SetAutoRepeat(bool rep) { m_autoRepeat = rep; } @@ -770,6 +768,7 @@ public: bool m_skipCheck; uint8 m_auraScaleMask; + std::unique_ptr m_preGeneratedPath; // xinef: bool _spellTargetsSelected; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index c911d506d..9465bb82f 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4870,10 +4870,12 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) targetGUID = unitTarget->GetGUID(); } - if (m_pathFinder) + float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE; + // Spell is not using explicit target - no generated path + if (!m_preGeneratedPath) { - m_caster->GetMotionMaster()->MoveCharge(m_pathFinder->GetEndPosition().x, m_pathFinder->GetEndPosition().y, m_pathFinder->GetEndPosition().z, - 42.0f, EVENT_CHARGE, &m_pathFinder->GetPath(), false, 0.f, targetGUID); + Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster)); + m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed); if (m_caster->GetTypeId() == TYPEID_PLAYER) { @@ -4882,10 +4884,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) } else { - Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetObjectSize(), unitTarget->GetRelativeAngle(m_caster)); - - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ + Z_OFFSET_FIND_HEIGHT, SPEED_CHARGE, EVENT_CHARGE, - nullptr, false, 0.f, targetGUID); + m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed); if (m_caster->GetTypeId() == TYPEID_PLAYER) { diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 5d24d3fb6..36a0c9242 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -3282,6 +3282,11 @@ enum SummonType enum EventId { EVENT_CHARGE = 1003, + + /// Special charge event which is used for charge spells that have explicit targets + /// and had a path already generated - using it in PointMovementGenerator will not + /// create a new spline and launch it + EVENT_CHARGE_PREPATH = 1005, }; enum ResponseCodes