aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOvah <dreadkiller@gmx.de>2022-10-01 17:21:07 +0200
committerGitHub <noreply@github.com>2022-10-01 17:21:07 +0200
commitb47ef3ce90f6192784f8ce903b6738d568631809 (patch)
tree6db340ab46755252d18316a8d4205331e75314da /src
parent2b8fc95fdec69ca5c057c2e63a1313d8165d44ea (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.cpp45
-rw-r--r--src/server/game/Entities/Unit/Unit.h5
-rw-r--r--src/server/game/Spells/Spell.cpp11
-rw-r--r--src/server/shared/SharedDefines.h2
-rw-r--r--src/server/shared/enuminfo_SharedDefines.cpp6
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;