diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 57188453a1..e1196a47ee 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -840,8 +840,7 @@ void Creature::Update(uint32 diff) if (!IsAlive()) break; - if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) - Regenerate(GetPowerType(), diff); + Regenerate(GetPowerType(), diff); // Update serverside orientation of channeled spells that are suposed to track the channel target if (Spell const* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) @@ -881,24 +880,16 @@ void Creature::RegenerateHealth() uint32 addvalue = 0; // Not only pet, but any controlled creature - if (GetCharmerOrOwnerGUID()) + if (!GetCharmerOrOwnerGUID().IsEmpty()) { - float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH); - float Spirit = GetStat(STAT_SPIRIT); - - if (GetPower(POWER_MANA) > 0) - addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate); - else - addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate); + float healthIncreaseRate = sWorld->getRate(RATE_HEALTH); + addvalue = 0.015f * ((float)GetMaxHealth()) * healthIncreaseRate; } else addvalue = maxValue / 3; // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - AddPct(addvalue, (*i)->GetAmount()); - + addvalue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * UNIT_HEALTH_REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); ModifyHealth(addvalue); diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 471114dd29..492558a41c 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -922,7 +922,7 @@ void Creature::UpdatePowerRegeneration(Powers powerType) { case POWER_MANA: // Regeneration for controlled units - if (GetCharmerGUID() || GetOwnerGUID()) + if (!GetCharmerGUID().IsEmpty() || !GetOwnerGUID().IsEmpty()) { float spiritRegen = (GetStat(STAT_SPIRIT) / 5.f + 17.f); SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, spiritRegen + powerRegenMod); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 56c23b33cd..32e52f76e8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2222,178 +2222,124 @@ void Unit::HandleProcExtraAttackFor(Unit* victim) MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackType attType) const { - // This is only wrapper - - // Miss chance based on melee - //float miss_chance = MeleeMissChanceCalc(victim, attType); - float miss_chance = MeleeSpellMissChance(victim, attType, 0); - - // Critical hit chance - float crit_chance = GetUnitCriticalChance(attType, victim); - - // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case) - float dodge_chance = victim->GetUnitDodgeChance(this); - float block_chance = victim->GetUnitBlockChance(); - float parry_chance = victim->GetUnitParryChance(this); - - // Useful if want to specify crit & miss chances for melee, else it could be removed - TC_LOG_DEBUG("entities.unit", "MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance, crit_chance, dodge_chance, parry_chance, block_chance); - - return RollMeleeOutcomeAgainst(victim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100), int32(parry_chance*100), int32(block_chance*100)); -} - -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const -{ - if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) + if (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode()) return MELEE_HIT_EVADE; - int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim); - int32 victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + + // Miss chance based on melee + int32 miss_chance = int32(MeleeSpellMissChance(victim, attType, 0) * 100.f); + + // Critical hit chance + int32 crit_chance = int32(GetUnitCriticalChanceAgainst(attType, victim) * 100.0f); + + // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case) + int32 dodge_chance = int32(GetUnitDodgeChance(attType, victim) * 100.0f); + int32 block_chance = int32(GetUnitBlockChance(attType, victim) * 100.0f); + int32 parry_chance = int32(GetUnitParryChance(attType, victim) * 100.0f); + + + // melee attack table implementation + // outcome priority: + // 1. > 2. > 3. > 4. > 5. > 6. > 7. > 8. + // MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT - // bonus from skills is 0.04% - int32 skillBonus = 4 * (attackerMaxSkillValueForLevel - victimMaxSkillValueForLevel); int32 sum = 0, tmp = 0; - int32 roll = urand (0, 10000); - - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus); - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d", - roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance); - - tmp = miss_chance; - - if (tmp > 0 && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: MISS"); - return MELEE_HIT_MISS; - } - - // always crit against a sitting target (except 0 crit chance) - if (victim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !victim->IsStandState()) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT (sitting victim)"); - return MELEE_HIT_CRIT; - } - - // Dodge chance - - // only players can't dodge if attacker is behind - if (victim->GetTypeId() == TYPEID_PLAYER && !victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind and victim was a player."); - } - else - { - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodge_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - dodge_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodge_chance = int32 (float (dodge_chance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - - tmp = dodge_chance; - if ((tmp > 0) // check if unit _can_ dodge - && ((tmp -= skillBonus) > 0) - && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum); - return MELEE_HIT_DODGE; - } - } - - // parry & block chances + int32 roll = urand(0, 9999); // check if attack comes from behind, nobody can parry or block if attacker is behind - if (!victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind."); - else + bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION); + + // only creatures can dodge if attacker is behind + bool canDodge = victim->GetTypeId() != TYPEID_PLAYER || canParryOrBlock; + + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) { - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parry_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - parry_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) - { - int32 tmp2 = int32(parry_chance); - if (tmp2 > 0 // check if unit _can_ parry - && (tmp2 -= skillBonus) > 0 - && roll < (sum += tmp2)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp2, sum); - return MELEE_HIT_PARRY; - } - } - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) - { - tmp = block_chance; - if (tmp > 0 // check if unit _can_ block - && (tmp -= skillBonus) > 0 - && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum); - return MELEE_HIT_BLOCK; - } - } + canDodge = false; + canParryOrBlock = false; } - // Critical chance - tmp = crit_chance; - + // 1. MISS + tmp = miss_chance; if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_MISS; + + // always crit against a sitting target (except 0 crit chance) + if (victim->IsPlayer() && crit_chance > 0 && !victim->IsStandState()) + return MELEE_HIT_CRIT; + + // 2. DODGE + if (canDodge) { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum); - if (GetTypeId() == TYPEID_UNIT && (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT DISABLED)"); - else - return MELEE_HIT_CRIT; + tmp = dodge_chance; + if (tmp > 0 // check if unit _can_ dodge + && roll < (sum += tmp)) + return MELEE_HIT_DODGE; } + // 3. PARRY + if (canParryOrBlock) + { + tmp = parry_chance; + if (tmp > 0 // check if unit _can_ parry + && roll < (sum += tmp)) + return MELEE_HIT_PARRY; + } + + // 4. GLANCING // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon) - if (attType != RANGED_ATTACK && - (GetTypeId() == TYPEID_PLAYER || IsPet()) && + if ((GetTypeId() == TYPEID_PLAYER || IsPet()) && victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet() && getLevel() < victim->getLevelForTarget(this)) { // cap possible value (with bonuses > max skill) int32 skill = attackerMaxSkillValueForLevel; - tmp = (10 + (victimMaxSkillValueForLevel - skill)) * 100; - tmp = tmp > 4000 ? 4000 : tmp; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum); + // against boss-level targets - 24% chance of 25% average damage reduction (damage reduction range : 20-30%) + // against level 82 elites - 18% chance of 15% average damage reduction (damage reduction range : 10-20%) + tmp = 600 + (victimMaxSkillValueForLevel - skill) * 120; + tmp = std::min(tmp, 4000); + if (tmp > 0 && roll < (sum += tmp)) return MELEE_HIT_GLANCING; - } } + // 5. BLOCK + if (canParryOrBlock) + { + tmp = block_chance; + if (tmp > 0 // check if unit _can_ block + && roll < (sum += tmp)) + return MELEE_HIT_BLOCK; + } + + // 6.CRIT + tmp = crit_chance; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_CRIT; + + // 7. CRUSHING // mobs can score crushing blows if they're 4 or more levels above victim if (getLevelForTarget(victim) >= victim->getLevelForTarget(this) + 4 && // can be from by creature (if can) or from controlled player that considered as creature !IsControlledByPlayer() && - !(GetTypeId() == TYPEID_UNIT && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH)) + !(IsCreature() && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH)) { // when their weapon skill is 15 or more above victim's defense skill tmp = victimMaxSkillValueForLevel; // tmp = mob's level * 5 - player's current defense skill tmp = attackerMaxSkillValueForLevel - tmp; - if (tmp >= 15) - { - // add 2% chance per lacking skill point, min. is 15% - tmp = tmp * 200 - 1500; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum); - return MELEE_HIT_CRUSHING; - } - } + // minimum of 20 points diff (4 levels difference) + tmp = std::max(tmp, 20); + + // add 2% chance per lacking skill point, min. is 15% + tmp = tmp * 200 - 1500; + if (roll < (sum += tmp)) + return MELEE_HIT_CRUSHING; } - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: NORMAL"); + // 8. HIT return MELEE_HIT_NORMAL; } @@ -2465,22 +2411,23 @@ void Unit::SendMeleeAttackStop(Unit* victim) TC_LOG_DEBUG("entities.unit", "%s %u stopped attacking", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUID().GetCounter()); } -bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType /*attackType*/) +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)) { - // Check creatures flags_extra for disable block - if (victim->GetTypeId() == TYPEID_UNIT && - victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) - return false; - - if (roll_chance_f(victim->GetUnitBlockChance())) + float blockChance = GetUnitBlockChance(attackType, victim); + if (blockChance && roll_chance_f(blockChance)) return true; } + return false; } @@ -2561,6 +2508,14 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canParry = true; bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) + { + canDodge = false; + canParry = false; + canBlock = false; + } + // Ranged attacks can only miss, resist and deflect if (attType == RANGED_ATTACK) { @@ -2568,7 +2523,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canDodge = false; // only if in front - if (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) + if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) { int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; tmp += deflect_chance; @@ -2596,23 +2551,15 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; } } - // Check creatures flags_extra for disable parry - if (victim->GetTypeId() == TYPEID_UNIT) - { - uint32 flagEx = victim->ToCreature()->GetCreatureTemplate()->flags_extra; - if (flagEx & CREATURE_FLAG_EXTRA_NO_PARRY) - canParry = false; - // Check creatures flags_extra for disable block - if (flagEx & CREATURE_FLAG_EXTRA_NO_BLOCK) - canBlock = false; - } + // Ignore combat result aura AuraEffectList const& ignore = GetAuraEffectsByType(SPELL_AURA_IGNORE_COMBAT_RESULT); - for (AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + for (AuraEffect const* aurEff : ignore) { - if (!(*i)->IsAffectingSpell(spellInfo)) + if (!aurEff->IsAffectingSpell(spellInfo)) continue; - switch ((*i)->GetMiscValue()) + + switch (aurEff->GetMiscValue()) { case MELEE_HIT_DODGE: canDodge = false; @@ -2624,7 +2571,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; break; default: - TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue()); + TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", aurEff->GetId(), aurEff->GetMiscValue()); break; } } @@ -2632,15 +2579,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canDodge) { // Roll dodge - int32 dodgeChance = int32(victim->GetUnitDodgeChance(this) * 100.0f); - // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodgeChance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodgeChance = int32(float(dodgeChance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodgeChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - dodgeChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 dodgeChance = int32(GetUnitDodgeChance(attType, victim) * 100.0f); if (dodgeChance < 0) dodgeChance = 0; @@ -2651,12 +2590,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canParry) { // Roll parry - int32 parryChance = int32(victim->GetUnitParryChance(this) * 100.0f); - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parryChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - parryChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 parryChance = int32(GetUnitParryChance(attType, victim) * 100.0f); if (parryChance < 0) parryChance = 0; @@ -2667,7 +2601,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canBlock) { - int32 blockChance = int32(victim->GetUnitBlockChance() * 100.0f); + int32 blockChance = int32(GetUnitBlockChance(attType, victim) * 100.0f); if (blockChance < 0) blockChance = 0; tmp += blockChance; @@ -2683,12 +2617,12 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) { // Can`t miss on dead target (on skinning for example) - if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) + if (!victim->IsAlive() && !victim->IsPlayer()) return SPELL_MISS_NONE; SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); int32 thisLevel = getLevel(); - if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) + if (IsCreature() && ToCreature()->IsTrigger()) thisLevel = std::max(thisLevel, spellInfo->SpellLevel); // Base hit chance from attacker and victim levels @@ -2808,168 +2742,183 @@ uint32 Unit::GetUnitMeleeSkill(Unit const* target) const return (target ? getLevelForTarget(target) : getLevel()) * 5; } -float Unit::GetUnitDodgeChance(Unit const* attacker) const +float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; - - if (GetTypeId() == TYPEID_PLAYER) - return GetFloatValue(PLAYER_DODGE_PERCENTAGE); - else - { - if (IsTotem()) - return 0.0f; - else - { - float dodge = 0.0f; - int8 levelDifference = getLevel() - attacker->getLevel(); - if (levelDifference >= 0 && levelDifference <= 3) - dodge += EnemyDodgeChance[levelDifference]; - else if (levelDifference > 3) - dodge += EnemyDodgeChance[3]; - - dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); - return dodge; - } - } -} - -float Unit::GetUnitParryChance(Unit const* attacker) const -{ - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; - float chance = 0.0f; - if (Player const* player = ToPlayer()) + if (victim->IsPlayer()) + chance += victim->GetFloatValue(PLAYER_DODGE_PERCENTAGE); + else { - if (player->CanParry()) + if (!victim->IsTotem()) { - Item* tmpitem = player->GetWeaponForAttack(BASE_ATTACK, true); + int8 levelDifference = victim->getLevel() - getLevel(); + if (levelDifference < 0) + levelDifference = 0; + else if (levelDifference > 3) + levelDifference = 3; + + chance += EnemyDodgeChance[levelDifference]; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); + } + } + + // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE + chance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); + + // reduce dodge by SPELL_AURA_MOD_ENEMY_DODGE + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ENEMY_DODGE); + if (IsPlayer()) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + + return std::max(chance, 0.0f); +} + +float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const +{ + float chance = 0.0f; + + if (Player const* playerVictim = victim->ToPlayer()) + { + if (playerVictim->CanParry()) + { + Item* tmpitem = playerVictim->GetWeaponForAttack(BASE_ATTACK, true); if (!tmpitem) - tmpitem = player->GetWeaponForAttack(OFF_ATTACK, true); + tmpitem = playerVictim->GetWeaponForAttack(OFF_ATTACK, true); if (tmpitem) - chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE); + chance = playerVictim->GetFloatValue(PLAYER_PARRY_PERCENTAGE); } } - else if (GetTypeId() == TYPEID_UNIT) + else { - if (GetCreatureType() == CREATURE_TYPE_HUMANOID) + // Allow parries for creatures only if it's not a totem, does not have a virtual item equipped and does not have CREATURE_FLAG_EXTRA_NO_PARRY + if (!victim->IsTotem() && (GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0) || GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)) && + !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) { - float chance = 0.0f; - int8 levelDifference = getLevel() - attacker->getLevel(); - if (levelDifference >= 0 && levelDifference <= 3) - chance += EnemyParryChance[levelDifference]; + int8 levelDifference = victim->getLevel() - getLevel(); + if (levelDifference < 0) + levelDifference = 0; else if (levelDifference > 3) - chance += EnemyParryChance[3]; + levelDifference = 3; - chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + chance += EnemyParryChance[levelDifference]; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); } } - return chance > 0.0f ? chance : 0.0f; + if (IsPlayer()) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + + return std::max(chance, 0.0f); } -float Unit::GetUnitMissChance(WeaponAttackType attType, Unit const* attacker) const +float Unit::GetUnitMissChance(Unit const* victim) const { - float miss_chance = 0.0f; - int8 levelDifference = getLevel() - attacker->getLevel(); - if (levelDifference >= 0 && levelDifference <= 3) - miss_chance += MissChancePhysical[levelDifference]; + float chance = 0.0f; + + int8 levelDifference = victim->getLevel() - getLevel(); + if (levelDifference < 0) + chance += MissChancePhysical[0]; else if (levelDifference > 3) - miss_chance += MissChancePhysical[3] + 2.0f * (levelDifference - 3); - - // Normalize chance - miss_chance = std::min(100.0f, miss_chance); - - if (attType == RANGED_ATTACK) - miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); + chance += MissChancePhysical[3] + 2.0f * (levelDifference - 3); else - miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE); + chance += MissChancePhysical[levelDifference]; - return miss_chance; + return chance; } -float Unit::GetUnitBlockChance() const +float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + float chance = 0.f; - if (Player const* player = ToPlayer()) + if (Player const* playerVictim = victim->ToPlayer()) { - if (player->CanBlock()) + if (playerVictim->CanBlock()) { - Item* tmpitem = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item* tmpitem = playerVictim->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (tmpitem && !tmpitem->IsBroken()) - return GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + chance = playerVictim->GetFloatValue(PLAYER_BLOCK_PERCENTAGE); } - // is player but has no block ability or no not broken shield equipped - return 0.0f; } else { - if (IsTotem()) - return 0.0f; - else + if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) { - float block = 5.0f; - block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); - return block > 0.0f ? block : 0.0f; + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); } } + + return std::max(chance, 0.0f); } -float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const +float Unit::GetUnitCriticalChanceDone(WeaponAttackType attackType) const { - float crit; - + float chance = 0.f; if (GetTypeId() == TYPEID_PLAYER) { switch (attackType) { case BASE_ATTACK: - crit = GetFloatValue(PLAYER_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_CRIT_PERCENTAGE); break; case OFF_ATTACK: - crit = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE); break; case RANGED_ATTACK: - crit = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE); break; // Just for good manner default: - crit = 0.0f; + chance = 0.0f; break; } } else { - crit = 5.0f; - crit += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); - crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); + if (!(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) + { + chance = 5.0f; + chance += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); + } } + return chance; +} + +float Unit::GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const +{ + float chance = critDone; + // flat aura mods if (attackType == RANGED_ATTACK) - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); else - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); - AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); - for (AuraEffect const* aurEff : critChanceForCaster) + chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [attacker](AuraEffect const* aurEff) -> bool { - if (aurEff->GetCasterGUID() != GetGUID()) - continue; + if (aurEff->GetCasterGUID() == attacker->GetGUID()) + return true; + return false; + }); - crit += aurEff->GetAmount(); - } + // applied after resilience + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); + return std::max(chance, 0.f); +} - if (crit < 0.0f) - crit = 0.0f; - return crit; +float Unit::GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const +{ + float chance = GetUnitCriticalChanceDone(attackType); + return victim->GetUnitCriticalChanceTaken(this, attackType, chance); } void Unit::_DeleteRemovedAuras() @@ -7287,7 +7236,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto { if (victim) { - crit_chance += GetUnitCriticalChance(attackType, victim); + crit_chance += GetUnitCriticalChanceDone(attackType); crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); switch (spellProto->SpellFamilyName) @@ -10068,6 +10017,9 @@ void Unit::Regenerate(Powers powerType, uint32 diff) if (!maxValue) return; + if (!HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) + return; + uint32 curValue = GetPower(powerType); // Block power regeneration @@ -10091,7 +10043,7 @@ void Unit::Regenerate(Powers powerType, uint32 diff) addValue *= sWorld->getRate(RATE_POWER_RAGE_LOSS); break; case POWER_RUNIC_POWER: - addValue*= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); + addValue *= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); break; case POWER_FOCUS: addValue *= sWorld->getRate(RATE_POWER_FOCUS); @@ -12566,23 +12518,20 @@ void Unit::ApplyResilience(Unit const* victim, int32* damage) const // Crit or block - determined on damage calculation phase! (and can be both in some time) float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, uint32 spellId) const { - //calculate miss chance - float missChance = victim->GetUnitMissChance(attType, this); + // Calculate miss chance + float missChance = GetUnitMissChance(victim); if (!spellId && haveOffhandWeapon()) missChance += 19; - // Calculate hit chance - float hitChance = 100.0f; - // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + float resistMissChance = 100.0f; if (spellId) { if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance); + modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, resistMissChance); } - - missChance += hitChance - 100.0f; + missChance -= resistMissChance - 100.0f; if (attType == RANGED_ATTACK) missChance -= m_modRangedHitChance; @@ -12591,7 +12540,6 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, u // miss chance from auras after calculating skill based miss missChance -= GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); - if (attType == RANGED_ATTACK) missChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); else diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index b94d5c889f..3c8ea3cccd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1192,11 +1192,13 @@ class TC_GAME_API Unit : public WorldObject SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo); SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false, Optional effectMask = nullptr); - float GetUnitDodgeChance(Unit const* attacker) const; - float GetUnitParryChance(Unit const* attacker) const; - float GetUnitBlockChance() const; - float GetUnitMissChance(WeaponAttackType attType, Unit const* victim) const; - float GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const; + float GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const; + float GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const; + float GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const; + float GetUnitMissChance(Unit const* victim) const; + float GetUnitCriticalChanceDone(WeaponAttackType attackType) const; + float GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const; + float GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const; int32 GetMechanicResistChance(SpellInfo const* spellInfo) const; bool CanUseAttackType(uint8 attacktype) const; @@ -1208,7 +1210,6 @@ class TC_GAME_API Unit : public WorldObject float GetPPMProcChance(uint32 WeaponSpeed, float PPM, SpellInfo const* spellProto) const; MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; bool IsVendor() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR); } bool IsTrainer() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER); }