diff options
author | Ovah <dreadkiller@gmx.de> | 2022-10-01 17:21:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-01 17:21:07 +0200 |
commit | b47ef3ce90f6192784f8ce903b6738d568631809 (patch) | |
tree | 6db340ab46755252d18316a8d4205331e75314da /src | |
parent | 2b8fc95fdec69ca5c057c2e63a1313d8165d44ea (diff) |
Core/Spells: fixed up block mechanics (#28286)
* Ranged attacks can now be blocked
* Implement SPELL_ATTR3_COMPLETELY_BLOCKED
* Fixed a bug which was causing blocking to block entire spell effects regardless if the attribute was present or not
* Fixed a logic mistake which was causing blocks to roll twice (once for effect negation and once on hit)
* No longer send blocked miss conditions in spell_go packets when the spell is not completely blocked to match sniff data
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 45 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 5 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 11 | ||||
-rw-r--r-- | src/server/shared/SharedDefines.h | 2 | ||||
-rw-r--r-- | src/server/shared/enuminfo_SharedDefines.cpp | 6 |
5 files changed, 20 insertions, 49 deletions
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index eeb8c7410af..1e828f1f33e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -985,7 +985,7 @@ void Unit::CastStop(uint32 except_spellid) InterruptSpell(CurrentSpellTypes(i), false); } -void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit, Spell* spell /*= nullptr*/) +void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit /*= false*/, bool blocked /*= false*/, Spell* spell /*= nullptr*/) { if (damage < 0) return; @@ -1003,7 +1003,6 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama if (Unit::IsDamageReducedByArmor(damageSchoolMask, spellInfo)) damage = Unit::CalcArmorReducedDamage(this, victim, damage, spellInfo, attackType); - bool blocked = false; // Per-school calc switch (spellInfo->DmgClass) { @@ -1011,17 +1010,6 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama case SPELL_DAMAGE_CLASS_RANGED: case SPELL_DAMAGE_CLASS_MELEE: { - // Physical Damage - if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) - { - // Spells with this attribute were already calculated in MeleeSpellHitResult - if (!spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL)) - { - // Get blocked status - blocked = isSpellBlocked(victim, spellInfo, attackType); - } - } - if (crit) { damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; @@ -1055,7 +1043,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama { damageInfo->blocked = victim->GetShieldBlockValue(); // double blocked amount if block is critical - if (victim->isBlockCritical()) + if (victim->IsBlockCritical()) damageInfo->blocked += damageInfo->blocked; if (damage <= int32(damageInfo->blocked)) { @@ -1305,7 +1293,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, Weapon damageInfo->HitInfo |= HITINFO_BLOCK; damageInfo->Blocked = damageInfo->Target->GetShieldBlockValue(); // double blocked amount if block is critical - if (damageInfo->Target->isBlockCritical()) + if (damageInfo->Target->IsBlockCritical()) damageInfo->Blocked *= 2; uint32 remainingBlock = damageInfo->Blocked; @@ -2357,27 +2345,7 @@ void Unit::SendMeleeAttackStop(Unit* victim) TC_LOG_DEBUG("entities.unit", "%s stopped attacking", GetGUID().ToString().c_str()); } -bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType) -{ - // These spells can't be blocked - if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) || spellProto->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))) - return false; - - // Can't block when casting/controlled - if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) - return false; - - if (victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION) || victim->HasInArc(float(M_PI), this)) - { - float blockChance = GetUnitBlockChance(attackType, victim); - if (blockChance && roll_chance_f(blockChance)) - return true; - } - - return false; -} - -bool Unit::isBlockCritical() +bool Unit::IsBlockCritical() { if (roll_chance_i(GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_CRIT_CHANCE))) return true; @@ -2461,7 +2429,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canDodge = !spellInfo->HasAttribute(SPELL_ATTR7_CANT_DODGE); bool canParry = !spellInfo->HasAttribute(SPELL_ATTR7_CANT_PARRY); - bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); + bool canBlock = true; // all melee and ranged attacks can be blocked // if victim is casting or cc'd it can't avoid attacks if (victim->IsNonMeleeSpellCast(false, false, true) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) @@ -2471,7 +2439,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canBlock = false; } - // Ranged attacks can only miss, resist and deflect + // Ranged attacks can only miss, resist and deflect and get blocked if (attType == RANGED_ATTACK) { canParry = false; @@ -2485,7 +2453,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (roll < tmp) return SPELL_MISS_DEFLECT; } - return SPELL_MISS_NONE; } // Check for attack from behind diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 22ce2f53230..b7d8afcf47e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1044,7 +1044,7 @@ class TC_GAME_API Unit : public WorldObject void SetLastDamagedTargetGuid(ObjectGuid guid) { _lastDamagedTargetGuid = guid; } ObjectGuid GetLastDamagedTargetGuid() const { return _lastDamagedTargetGuid; } - void CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = BASE_ATTACK, bool crit = false, Spell* spell = nullptr); + void CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = BASE_ATTACK, bool crit = false, bool blocked = false, Spell* spell = nullptr); void DealSpellDamage(SpellNonMeleeDamage const* damageInfo, bool durabilityLoss); // player or player's pet resilience (-1%) @@ -1620,8 +1620,7 @@ class TC_GAME_API Unit : public WorldObject uint32 MeleeDamageBonusDone(Unit* pVictim, uint32 damage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL); uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL); - bool isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType = BASE_ATTACK); - bool isBlockCritical(); + bool IsBlockCritical(); float SpellCritChanceDone(SpellInfo const* spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK, bool isPeriodic = false) const; float SpellCritChanceTaken(Unit const* caster, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, float doneChance, WeaponAttackType attackType = BASE_ATTACK, bool isPeriodic = false) const; static uint32 SpellCriticalDamageBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 1cdcd9128e6..30d0467dda3 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2307,7 +2307,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) spell->m_healing = Healing; _spellHitTarget = nullptr; - if (MissCondition == SPELL_MISS_NONE) + if (MissCondition == SPELL_MISS_NONE || (MissCondition == SPELL_MISS_BLOCK && !spell->GetSpellInfo()->HasAttribute(SPELL_ATTR3_COMPLETELY_BLOCKED))) _spellHitTarget = unit; else if (MissCondition == SPELL_MISS_REFLECT && ReflectResult == SPELL_MISS_NONE) _spellHitTarget = spell->m_caster->ToUnit(); @@ -2499,7 +2499,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) caster->SetLastDamagedTargetGuid(spell->unitTarget->GetGUID()); // Add bonuses and fill damageInfo struct - caster->CalculateSpellDamageTaken(&damageInfo, spell->m_damage, spell->m_spellInfo, spell->m_attackType, IsCrit, spell); + caster->CalculateSpellDamageTaken(&damageInfo, spell->m_damage, spell->m_spellInfo, spell->m_attackType, IsCrit, MissCondition == SPELL_MISS_BLOCK, spell); Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); // Send log damage message to client @@ -4437,6 +4437,11 @@ void Spell::UpdateSpellCastDataTargets(WorldPackets::Spells::SpellCastData& data } else // misses { + // Only send blocks in spell_go packets if we know that the spell is not going to do anything to the target. + // Spells that are partially blocked will send their block result in their according combat log packet. + if (targetInfo.MissCondition == SPELL_MISS_BLOCK && !m_spellInfo->HasAttribute(SPELL_ATTR3_COMPLETELY_BLOCKED)) + continue; + WorldPackets::Spells::SpellMissStatus missStatus; missStatus.TargetGUID = targetInfo.TargetGUID; missStatus.Reason = targetInfo.MissCondition; @@ -7642,7 +7647,7 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe { Unit* unit = nullptr; // In case spell hit target, do all effect on that target - if (targetInfo.MissCondition == SPELL_MISS_NONE) + if (targetInfo.MissCondition == SPELL_MISS_NONE || (targetInfo.MissCondition == SPELL_MISS_BLOCK && !m_spellInfo->HasAttribute(SPELL_ATTR3_COMPLETELY_BLOCKED))) unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); // In case spell reflect from target, do all effect on caster (if hit) else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE) diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index b19c205d97b..0bdd89004f7 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -517,7 +517,7 @@ enum SpellAttr3 : uint32 SPELL_ATTR3_UNK0 = 0x00000001, // TITLE Unknown attribute 0@Attr3 SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK = 0x00000002, // 1 Ignores subclass mask check when checking proc SPELL_ATTR3_UNK2 = 0x00000004, // TITLE Unknown attribute 2@Attr3 - SPELL_ATTR3_BLOCKABLE_SPELL = 0x00000008, // TITLE Blockable spell + SPELL_ATTR3_COMPLETELY_BLOCKED = 0x00000008, // TITLE Completely Blocked SPELL_ATTR3_IGNORE_RESURRECTION_TIMER = 0x00000010, // TITLE Ignore resurrection timer SPELL_ATTR3_UNK5 = 0x00000020, // TITLE Unknown attribute 5@Attr3 SPELL_ATTR3_UNK6 = 0x00000040, // TITLE Unknown attribute 6@Attr3 diff --git a/src/server/shared/enuminfo_SharedDefines.cpp b/src/server/shared/enuminfo_SharedDefines.cpp index 31398718e2f..bc6b4d77638 100644 --- a/src/server/shared/enuminfo_SharedDefines.cpp +++ b/src/server/shared/enuminfo_SharedDefines.cpp @@ -658,7 +658,7 @@ TC_API_EXPORT EnumText EnumUtils<SpellAttr3>::ToString(SpellAttr3 value) case SPELL_ATTR3_UNK0: return { "SPELL_ATTR3_UNK0", "Unknown attribute 0@Attr3", "" }; case SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK: return { "SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK", "SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK", "1 Ignores subclass mask check when checking proc" }; case SPELL_ATTR3_UNK2: return { "SPELL_ATTR3_UNK2", "Unknown attribute 2@Attr3", "" }; - case SPELL_ATTR3_BLOCKABLE_SPELL: return { "SPELL_ATTR3_BLOCKABLE_SPELL", "Blockable spell", "" }; + case SPELL_ATTR3_COMPLETELY_BLOCKED: return { "SPELL_ATTR3_COMPLETELY_BLOCKED", "Completely Blocked", "" }; case SPELL_ATTR3_IGNORE_RESURRECTION_TIMER: return { "SPELL_ATTR3_IGNORE_RESURRECTION_TIMER", "Ignore resurrection timer", "" }; case SPELL_ATTR3_UNK5: return { "SPELL_ATTR3_UNK5", "Unknown attribute 5@Attr3", "" }; case SPELL_ATTR3_UNK6: return { "SPELL_ATTR3_UNK6", "Unknown attribute 6@Attr3", "" }; @@ -702,7 +702,7 @@ TC_API_EXPORT SpellAttr3 EnumUtils<SpellAttr3>::FromIndex(size_t index) case 0: return SPELL_ATTR3_UNK0; case 1: return SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK; case 2: return SPELL_ATTR3_UNK2; - case 3: return SPELL_ATTR3_BLOCKABLE_SPELL; + case 3: return SPELL_ATTR3_COMPLETELY_BLOCKED; case 4: return SPELL_ATTR3_IGNORE_RESURRECTION_TIMER; case 5: return SPELL_ATTR3_UNK5; case 6: return SPELL_ATTR3_UNK6; @@ -743,7 +743,7 @@ TC_API_EXPORT size_t EnumUtils<SpellAttr3>::ToIndex(SpellAttr3 value) case SPELL_ATTR3_UNK0: return 0; case SPELL_ATTR3_IGNORE_PROC_SUBCLASS_MASK: return 1; case SPELL_ATTR3_UNK2: return 2; - case SPELL_ATTR3_BLOCKABLE_SPELL: return 3; + case SPELL_ATTR3_COMPLETELY_BLOCKED: return 3; case SPELL_ATTR3_IGNORE_RESURRECTION_TIMER: return 4; case SPELL_ATTR3_UNK5: return 5; case SPELL_ATTR3_UNK6: return 6; |