Core/Units: backported hit, dodge, parry and crit chance updates from 335 and fixed logics in 4.x specific code
This commit is contained in:
1 parent
ec1b73e6eb
commit
a657a21c4a
4 files changed
+245
-305
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<int32>(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
|
||||
|
||||
@@ -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<uint8> 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); }
|
||||
|
||||
Reference in new issue
Block a user