From b4242735749ca6ff4d7e370cc0d41105f5856f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefano=20Borz=C3=AC?= Date: Fri, 26 Mar 2021 15:56:35 +0100 Subject: [PATCH] feat(Core/Spells): improve SpellCheckRange(), spell cast when dismounting, falling or levitating spells (#3384) --- src/server/game/Movement/MotionMaster.cpp | 12 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 12 +- src/server/game/Spells/Spell.cpp | 156 +++++++++++++----- 3 files changed, 127 insertions(+), 53 deletions(-) diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 3aee9721e..e4ba623f4 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -607,15 +607,19 @@ void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC) // Abort too if the ground is very near if (fabs(_owner->GetPositionZ() - tz) < 0.1f) + { return; + } + _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING); + _owner->m_movementInfo.SetFallTime(0); + // don't run spline movement for players if (_owner->GetTypeId() == TYPEID_PLAYER) { - _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING); - _owner->m_movementInfo.SetFallTime(0); - _owner->ToPlayer()->SetFallInformation(time(nullptr), _owner->GetPositionZ()); + return; } - else if (_owner->GetTypeId() == TYPEID_UNIT && addFlagForNPC) // pussywizard + + if (_owner->GetTypeId() == TYPEID_UNIT && addFlagForNPC) // pussywizard { _owner->RemoveUnitMovementFlag(MOVEMENTFLAG_MASK_MOVING); _owner->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 147b0b8a2..ffc675343 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2862,10 +2862,10 @@ void AuraEffect::HandleAuraAllowFlight(AuraApplication const* aurApp, uint8 mode return; } - target->SetCanFly(apply); - - if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating()) + if (target->SetCanFly(apply) && !apply && !target->IsLevitating()) + { target->GetMotionMaster()->MoveFall(); + } } void AuraEffect::HandleAuraWaterWalk(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3249,10 +3249,10 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(AuraApplication const* aurApp, // do not remove unit flag if there are more than this auraEffect of that kind on unit on unit if (mode & AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK && (apply || (!target->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !target->HasAuraType(SPELL_AURA_FLY)))) { - target->SetCanFly(apply); - - if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating()) + if (target->SetCanFly(apply) && !apply && !target->IsLevitating()) + { target->GetMotionMaster()->MoveFall(); + } } //! Someone should clean up these hacks and remove it from this function. It doesn't even belong here. diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index de12ae8e0..3fe7b4b1d 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2424,8 +2424,15 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) } } + bool enablePvP = false; // need to check PvP state before spell effects, but act on it afterwards + if (spellHitTarget) { + // if target is flagged for pvp also flag caster if a player + if (effectUnit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAura(SPELL_AURA_BIND_SIGHT)) { + enablePvP = true; // Decide on PvP flagging now, but act on it later. + } + SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); if (missInfo2 != SPELL_MISS_NONE) { @@ -2684,9 +2691,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) DoTriggersOnSpellHit(spellHitTarget, mask); // if target is fallged for pvp also flag caster if a player - // xinef: do not flag spells with aura bind sight (no special attribute) - if (effectUnit->IsPvP() && effectUnit != m_caster && m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAura(SPELL_AURA_BIND_SIGHT)) + if (enablePvP) + { m_caster->ToPlayer()->UpdatePvP(true); + } CallScriptAfterHitHandlers(); } @@ -6398,73 +6406,135 @@ SpellCastResult Spell::CheckRange(bool strict) if (!strict && m_casttime == 0) return SPELL_CAST_OK; - uint32 range_type = 0; - - if (m_spellInfo->RangeEntry) - { - // check needed by 68766 51693 - both spells are cast on enemies and have 0 max range - // these are triggered by other spells - possibly we should omit range check in that case? - if (m_spellInfo->RangeEntry->ID == 1) - return SPELL_CAST_OK; - - range_type = m_spellInfo->RangeEntry->type; - } - + uint32 rangeType = m_spellInfo->RangeEntry->type; Unit* target = m_targets.GetUnitTarget(); - float max_range = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); - float min_range = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo); + float minRange = 0.0f; + float maxRange = 0.0f; + float rangeMod = 0.0f; // xinef: hack for npc shooters - if (min_range && GetCaster()->GetTypeId() == TYPEID_UNIT && !IS_PLAYER_GUID(GetCaster()->GetOwnerGUID()) && min_range <= 6.0f) - range_type = SPELL_RANGE_RANGED; + if (minRange && GetCaster()->GetTypeId() == TYPEID_UNIT && !IS_PLAYER_GUID(GetCaster()->GetOwnerGUID()) && minRange <= 6.0f) + { + rangeType = SPELL_RANGE_RANGED; + } + + if (strict && IsNextMeleeSwingSpell()) + { + maxRange = 100.0f; + } + else if (m_spellInfo->RangeEntry) + { + if (rangeType & SPELL_RANGE_MELEE) + { + rangeMod = m_caster->GetCombatReach() + 4.0f / 3.0f; + if (target) + { + rangeMod += target->GetCombatReach(); + } + else + { + rangeMod += m_caster->GetCombatReach(); + } + rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE); + } + else + { + float meleeRange = 0.0f; + if (rangeType & SPELL_RANGE_RANGED) + { + meleeRange = m_caster->GetCombatReach() + 4.0f / 3.0f; + if (target) + { + meleeRange += target->GetCombatReach(); + } + else + { + meleeRange += m_caster->GetCombatReach(); + } + + meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE); + } + + minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; + maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); + + if (target || m_targets.GetCorpseTarget()) + { + rangeMod = m_caster->GetCombatReach(); + if (target) + { + rangeMod += target->GetCombatReach(); + } + + if (minRange > 0.0f && !(rangeType & SPELL_RANGE_RANGED)) + { + minRange += rangeMod; + } + } + } + + if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && + (rangeType & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + { + rangeMod += 5.0f / 3.0f; + } + } + + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (Item* ranged = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK, true)) + { + maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; + } + } if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); + { + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this); + } - // xinef: dont check max_range to strictly after cast - if (range_type != SPELL_RANGE_MELEE && !strict) - max_range += std::min(3.0f, max_range * 0.1f); // 10% but no more than 3yd + maxRange += rangeMod; + minRange *= minRange; + maxRange *= maxRange; if (target) { if (target != m_caster) { - // Xinef: WHAT DA FUCK IS THIS SHIT? Spells with 5yd range can hit target 9yd away? >.> - if (range_type == SPELL_RANGE_MELEE) + if (m_caster->GetExactDistSq(target) > maxRange) { - float real_max_range = max_range; - if (m_caster->GetTypeId() != TYPEID_UNIT && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking()) - real_max_range -= MIN_MELEE_REACH; // Because of lag, we can not check too strictly here (is only used if both caster and target are moving) - else - real_max_range -= 2 * MIN_MELEE_REACH; - - if (!m_caster->IsWithinMeleeRange(target, std::max(real_max_range, 0.0f))) - return SPELL_FAILED_OUT_OF_RANGE; + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; } - else if (!m_caster->IsWithinCombatRange(target, max_range)) - return SPELL_FAILED_OUT_OF_RANGE; //0x5A; - if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED && range_type == SPELL_RANGE_RANGED) + if (minRange > 0.0f && m_caster->GetExactDistSq(target) < minRange) { - if (m_caster->IsWithinMeleeRange(target)) - return SPELL_FAILED_TOO_CLOSE; + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; } if (m_caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target)) + { return SPELL_FAILED_UNIT_NOT_INFRONT; + } } // Xinef: check min range for self casts - if (min_range && range_type != SPELL_RANGE_RANGED && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0 + if (minRange && rangeType != SPELL_RANGE_RANGED && m_caster->IsWithinCombatRange(target, minRange)) // skip this check if minRange = 0 + { return SPELL_FAILED_TOO_CLOSE; + } } if (m_targets.HasDst() && !m_targets.HasTraj()) { - if (!m_caster->IsWithinDist3d(m_targets.GetDstPos(), max_range)) - return SPELL_FAILED_OUT_OF_RANGE; - if (min_range && m_caster->IsWithinDist3d(m_targets.GetDstPos(), min_range)) - return SPELL_FAILED_TOO_CLOSE; + if (m_caster->GetExactDistSq(m_targets.GetDstPos()) > maxRange) + { + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; + } + + if (minRange > 0.0f && m_caster->GetExactDistSq(m_targets.GetDstPos()) < minRange) + { + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; + } } return SPELL_CAST_OK; @@ -7668,7 +7738,7 @@ void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier, // Sweeping strikes wtf shit ;d if (m_caster->getClass() == CLASS_WARRIOR && ssEffect < MAX_SPELL_EFFECTS && m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && - ((m_spellInfo->Id != 50622 && m_spellInfo->Id != 44949) || firstTarget)) + ((m_spellInfo->Id != 50622 && m_spellInfo->Id != 44949) || firstTarget)) { if (Aura* aur = m_caster->GetAura(12328)) {