Core/Auras: removed caster dependency from core

Port From (https://github.com/TrinityCore/TrinityCore/commit/fccf1a8c62820004df92b9ac567ba75f7281e6e2)
This commit is contained in:
hondacrx
2021-06-22 15:01:42 -04:00
parent 1b846fa2c7
commit e98e45cf8b
15 changed files with 822 additions and 842 deletions
+72 -117
View File
@@ -46,7 +46,7 @@ namespace Game.Entities
InterruptNonMeleeSpells(false);
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat);
ProcSkillsAndAuras(null, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
ProcSkillsAndAuras(this, null, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
}
public virtual void AtExitCombat()
@@ -558,13 +558,13 @@ namespace Game.Entities
CalcDamageInfo damageInfo;
CalculateMeleeDamage(victim, 0, out damageInfo, attType);
// Send log damage message to client
DealDamageMods(victim, ref damageInfo.damage, ref damageInfo.absorb);
DealDamageMods(damageInfo.attacker, victim, ref damageInfo.damage, ref damageInfo.absorb);
SendAttackStateUpdate(damageInfo);
DealMeleeDamage(damageInfo, true);
DamageInfo dmgInfo = new(damageInfo);
ProcSkillsAndAuras(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, ProcFlagsSpellType.None, ProcFlagsSpellPhase.None, dmgInfo.GetHitMask(), null, dmgInfo, null);
ProcSkillsAndAuras(damageInfo.attacker, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, ProcFlagsSpellType.None, ProcFlagsSpellPhase.None, dmgInfo.GetHitMask(), null, dmgInfo, null);
Log.outDebug(LogFilter.Unit, "AttackerStateUpdate: {0} attacked {1} for {2} dmg, absorbed {3}, blocked {4}, resisted {5}.",
GetGUID().ToString(), victim.GetGUID().ToString(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
}
@@ -756,71 +756,6 @@ namespace Game.Entities
}
}
public void SetInCombatState(bool PvP, Unit enemy = null)
{
// only alive units can be in combat
if (!IsAlive())
return;
if (PvP)
{
combatTimer = 5000;
Player me = ToPlayer();
if (me)
me.EnablePvpRules(true);
}
if (IsInCombat() || HasUnitState(UnitState.Evade))
return;
AddUnitFlag(UnitFlags.InCombat);
Creature creature = ToCreature();
if (creature != null)
{
// Set home position at place of engaging combat for escorted creatures
if ((IsAIEnabled && creature.GetAI().IsEscorted()) || GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint ||
GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Point)
creature.SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), Orientation);
if (enemy != null)
{
if (IsAIEnabled)
creature.GetAI().JustEngagedWith(enemy);
if (creature.GetFormation() != null)
creature.GetFormation().MemberEngagingTarget(creature, enemy);
}
if (IsPet())
{
UpdateSpeed(UnitMoveType.Run);
UpdateSpeed(UnitMoveType.Swim);
UpdateSpeed(UnitMoveType.Flight);
}
if (!creature.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.MountedCombatAllowed))
Dismount();
}
foreach (var unit in m_Controlled)
unit.SetInCombatState(PvP, enemy);
foreach (var pair in GetAppliedAuras())
{
AuraApplication aurApp = pair.Value;
aurApp.GetBase().CallScriptEnterLeaveCombatHandlers(aurApp, true);
}
Spell spell = GetCurrentSpell(CurrentSpellTypes.Generic);
if (spell != null)
if (spell.GetState() == SpellState.Preparing && spell.m_spellInfo.HasAttribute(SpellAttr0.CantUsedInCombat) && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Combat))
InterruptNonMeleeSpells(false);
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat);
ProcSkillsAndAuras(enemy, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
}
internal void SendCombatLogMessage(CombatLogServerPacket combatLog)
{
CombatLogSender combatLogSender = new(combatLog);
@@ -837,14 +772,20 @@ namespace Game.Entities
return !m_threatManager.IsThreatListEmpty();
}
public void Kill(Unit victim, bool durabilityLoss = true, bool skipSettingDeathState = false)
public static void Kill(Unit attacker, Unit victim, bool durabilityLoss = true, bool skipSettingDeathState = false)
{
// Prevent killing unit twice (and giving reward from kill twice)
if (victim.GetHealth() == 0)
return;
if (attacker != null && !attacker.IsInMap(victim))
attacker = null;
// find player: owner of controlled `this` or `this` itself maybe
Player player = GetCharmerOrOwnerPlayerOrPlayerItself();
Player player = null;
if (attacker != null)
player = attacker.GetCharmerOrOwnerPlayerOrPlayerItself();
Creature creature = victim.ToCreature();
bool isRewardAllowed = true;
@@ -911,7 +852,7 @@ namespace Game.Entities
loot.Clear();
uint lootid = creature.GetCreatureTemplate().LootId;
if (lootid != 0)
loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), GetMap().GetDifficultyLootItemContext());
loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
@@ -933,24 +874,28 @@ namespace Game.Entities
}
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
if (IsPet() || IsTotem())
if (attacker != null && (attacker.IsPet() || attacker.IsTotem()))
{
// proc only once for victim
Unit owner = GetOwner();
Unit owner = attacker.GetOwner();
if (owner != null)
owner.ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
ProcSkillsAndAuras(owner, victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
}
if (!victim.IsCritter())
ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
ProcSkillsAndAuras(attacker, victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
// Proc auras on death - must be before aura/combat remove
victim.ProcSkillsAndAuras(victim, ProcFlags.None, ProcFlags.Death, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
ProcSkillsAndAuras(victim, victim, ProcFlags.None, ProcFlags.Death, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
// update get killing blow achievements, must be done before setDeathState to be able to require auras on target
// and before Spirit of Redemption as it also removes auras
if (player != null)
player.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim);
if (attacker != null)
{
Player killerPlayer = attacker.GetCharmerOrOwnerPlayerOrPlayerItself();
if (killerPlayer != null)
killerPlayer.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim);
}
if (!skipSettingDeathState)
{
@@ -985,11 +930,11 @@ namespace Game.Entities
// Durability loss is calculated more accurately again for each item in Player.DurabilityLoss
plrVictim.DurabilityLossAll(baseLoss, false);
// durability lost message
SendDurabilityLoss(plrVictim, loss);
plrVictim.SendDurabilityLoss(plrVictim, loss);
}
// Call KilledUnit for creatures
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
ToCreature().GetAI().KilledUnit(victim);
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
attacker.ToCreature().GetAI().KilledUnit(victim);
// last damage from non duel opponent or opponent controlled creature
if (plrVictim.duel != null)
@@ -1015,12 +960,12 @@ namespace Game.Entities
}
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
ToCreature().GetAI().KilledUnit(victim);
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
attacker.ToCreature().GetAI().KilledUnit(victim);
// Call creature just died function
if (creature.IsAIEnabled)
creature.GetAI().JustDied(this);
creature.GetAI().JustDied(attacker);
TempSummon summon = creature.ToTempSummon();
if (summon != null)
@@ -1028,22 +973,21 @@ namespace Game.Entities
Unit summoner = summon.GetSummoner();
if (summoner != null)
if (summoner.IsTypeId(TypeId.Unit) && summoner.IsAIEnabled)
summoner.ToCreature().GetAI().SummonedCreatureDies(creature, this);
summoner.ToCreature().GetAI().SummonedCreatureDies(creature, attacker);
}
// Dungeon specific stuff, only applies to players killing creatures
if (creature.GetInstanceId() != 0)
{
Map instanceMap = creature.GetMap();
Player creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
// @todo do instance binding anyway if the charmer/owner is offline
if (instanceMap.IsDungeon() && (creditedPlayer || this == victim))
/// @todo do instance binding anyway if the charmer/owner is offline
if (instanceMap.IsDungeon() && ((attacker != null && attacker.GetCharmerOrOwnerPlayerOrPlayerItself() != null) || attacker == victim))
{
if (instanceMap.IsRaidOrHeroicDungeon())
{
if (creature.GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.InstanceBind))
((InstanceMap)instanceMap).PermBindAllPlayers();
instanceMap.ToInstanceMap().PermBindAllPlayers();
}
else
{
@@ -1061,7 +1005,7 @@ namespace Game.Entities
// outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh...
// handle player kill only if not suicide (spirit of redemption for example)
if (player != null && this != victim)
if (player != null && attacker != victim)
{
OutdoorPvP pvp = player.GetOutdoorPvP();
if (pvp != null)
@@ -1087,35 +1031,44 @@ namespace Game.Entities
}
// achievement stuff
if (victim.IsTypeId(TypeId.Player))
if (attacker != null && victim.IsPlayer())
{
if (IsTypeId(TypeId.Unit))
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, GetEntry());
else if (IsTypeId(TypeId.Player) && victim != this)
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)ToPlayer().GetTeam());
if (attacker.IsCreature())
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, attacker.GetEntry());
else if (attacker.IsPlayer() && victim != attacker)
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)attacker.ToPlayer().GetTeam());
}
// Hook for OnPVPKill Event
Player killerPlr = ToPlayer();
Creature killerCre = ToCreature();
if (killerPlr != null)
if (attacker != null)
{
Player killedPlr = victim.ToPlayer();
Creature killedCre = victim.ToCreature();
if (killedPlr != null)
Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr);
else if (killedCre != null)
Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre);
}
else if (killerCre != null)
{
Player killed = victim.ToPlayer();
if (killed != null)
Global.ScriptMgr.OnPlayerKilledByCreature(killerCre, killed);
Player killerPlr = attacker.ToPlayer();
if (killerPlr != null)
{
Player killedPlr = victim.ToPlayer();
if (killedPlr != null)
Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr);
else
{
Creature killedCre = victim.ToCreature();
if (killedCre != null)
Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre);
}
}
else
{
Creature killerCre = attacker.ToCreature();
if (killerCre != null)
{
Player killed = victim.ToPlayer();
if (killed != null)
Global.ScriptMgr.OnPlayerKilledByCreature(killerCre, killed);
}
}
}
}
public void KillSelf(bool durabilityLoss = true, bool skipSettingDeathState = false) { Kill(this, durabilityLoss, skipSettingDeathState); }
public void KillSelf(bool durabilityLoss = true, bool skipSettingDeathState = false) { Kill(this, this, durabilityLoss, skipSettingDeathState); }
public virtual bool CanUseAttackType(WeaponAttackType attacktype)
{
@@ -1289,11 +1242,13 @@ namespace Game.Entities
if (!damageInfo.HitInfo.HasAnyFlag(HitInfo.Miss))
damageInfo.HitInfo |= HitInfo.AffectsVictim;
uint resilienceReduction = damageInfo.damage;
ApplyResilience(victim, ref resilienceReduction);
resilienceReduction = damageInfo.damage - resilienceReduction;
damageInfo.damage -= resilienceReduction;
damageInfo.cleanDamage += resilienceReduction;
int resilienceReduction = (int)damageInfo.damage;
if (CanApplyResilience())
ApplyResilience(victim, ref resilienceReduction);
resilienceReduction = (int)damageInfo.damage - resilienceReduction;
damageInfo.damage -= (uint)resilienceReduction;
damageInfo.cleanDamage += (uint)resilienceReduction;
// Calculate absorb resist
if (damageInfo.damage > 0)
@@ -1326,7 +1281,7 @@ namespace Game.Entities
int miss_chance = (int)(MeleeSpellMissChance(victim, attType, null) * 100.0f);
// Critical hit chance
int crit_chance = (int)(GetUnitCriticalChance(attType, victim) + GetTotalAuraModifier(AuraType.ModAutoAttackCritChance) * 100.0f);
int crit_chance = (int)((GetUnitCriticalChanceAgainst(attType, victim) + GetTotalAuraModifier(AuraType.ModAutoAttackCritChance)) * 100.0f);
int dodge_chance = (int)(GetUnitDodgeChance(attType, victim) * 100.0f);
int block_chance = (int)(GetUnitBlockChance(attType, victim) * 100.0f);
+244 -202
View File
@@ -105,6 +105,7 @@ namespace Game.Entities
}
int DoneTotal = 0;
float DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype);
// Done fixed damage bonus auras
int DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto.GetSchoolMask());
@@ -141,27 +142,21 @@ namespace Game.Entities
float coeff = effect.BonusCoefficient;
if (DoneAdvertisedBenefit != 0)
{
Player modOwner = GetSpellModOwner();
if (modOwner)
Player modOwner1 = GetSpellModOwner();
if (modOwner1)
{
coeff *= 100.0f;
modOwner.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff);
modOwner1.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff);
coeff /= 100.0f;
}
DoneTotal += (int)(DoneAdvertisedBenefit * coeff * stack);
}
// Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura.HandleAuraSpecificMods.
float tmpDamage = ((int)pdamage + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype));
float tmpDamage = (float)((int)pdamage + DoneTotal) * DoneTotalMod;
// apply spellmod to Done damage (flat and pct)
Player _modOwner = GetSpellModOwner();
if (_modOwner)
{
if (damagetype == DamageEffectType.DOT)
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref tmpDamage);
else
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref tmpDamage);
}
if (_modOwner != null)
_modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref tmpDamage);
return (uint)Math.Max(tmpDamage, 0.0f);
}
@@ -171,13 +166,21 @@ namespace Game.Entities
if (spellProto == null || !victim || damagetype == DamageEffectType.Direct)
return 1.0f;
// Some spells don't benefit from done mods
if (spellProto.HasAttribute(SpellAttr3.NoDoneBonus))
return 1.0f;
// Some spells don't benefit from pct done mods
if (spellProto.HasAttribute(SpellAttr6.IgnoreCasterDamageModifiers))
return 1.0f;
// For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone.
if (IsTypeId(TypeId.Unit) && IsTotem())
return 1.0f;
// For totems get damage bonus from owner
if (IsCreature() && IsTotem())
{
Unit owner = GetOwner();
if (owner != null)
return owner.SpellDamagePctDone(victim, spellProto, damagetype);
}
// Done total percent damage auras
float DoneTotalMod = 1.0f;
@@ -392,19 +395,23 @@ namespace Game.Entities
return GetTotalAuraModifierByMiscMask(AuraType.ModHealing, (int)schoolMask);
}
public int SpellCriticalHealingBonus(SpellInfo spellProto, int damage, Unit victim)
public static int SpellCriticalHealingBonus(Unit caster, SpellInfo spellProto, int damage, Unit victim)
{
// Calculate critical bonus
int crit_bonus = damage;
// adds additional damage to critBonus (from talents)
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
if (caster != null)
{
Player modOwner = caster.GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
}
damage += crit_bonus;
damage = (int)(damage * GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount));
if (caster != null)
damage = (int)(damage * caster.GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount));
return damage;
}
@@ -424,19 +431,20 @@ namespace Game.Entities
return healamount;
int DoneTotal = 0;
float DoneTotalMod = SpellHealingPctDone(victim, spellProto);
// done scripted mod (take it from owner)
Unit owner1 = GetOwner() ?? this;
var mOverrideClassScript = owner1.GetAuraEffectsByType(AuraType.OverrideClassScripts);
foreach (var eff in mOverrideClassScript)
foreach (var aurEff in mOverrideClassScript)
{
if (!eff.IsAffectingSpell(spellProto))
if (!aurEff.IsAffectingSpell(spellProto))
continue;
switch (eff.GetMiscValue())
switch (aurEff.GetMiscValue())
{
case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / LK Arena (4/5/6) Totem of the Third Wind / Savage Totem of the Third Wind
DoneTotal += eff.GetAmount();
DoneTotal += aurEff.GetAmount();
break;
default:
break;
@@ -491,25 +499,32 @@ namespace Game.Entities
DoneTotal = 0;
}
// use float as more appropriate for negative values and percent applying
float heal = (healamount + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellHealingPctDone(victim, spellProto));
float heal = (float)((int)healamount + DoneTotal) * DoneTotalMod;
// apply spellmod to Done amount
Player _modOwner = GetSpellModOwner();
if (_modOwner)
{
if (damagetype == DamageEffectType.DOT)
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref heal);
else
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref heal);
}
_modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref heal);
return (uint)Math.Max(heal, 0.0f);
}
public float SpellHealingPctDone(Unit victim, SpellInfo spellProto)
{
// For totems pct done mods are calculated when its calculation is run on the player in SpellHealingBonusDone.
if (IsTypeId(TypeId.Unit) && IsTotem())
// For totems get healing bonus from owner
if (IsCreature() && IsTotem())
{
Unit owner = GetOwner();
if (owner != null)
return owner.SpellHealingPctDone(victim, spellProto);
}
// Some spells don't benefit from done mods
if (spellProto.HasAttribute(SpellAttr3.NoDoneBonus))
return 1.0f;
// Some spells don't benefit from done mods
if (spellProto.HasAttribute(SpellAttr6.IgnoreHealingModifiers))
return 1.0f;
// No bonus healing for potion spells
@@ -647,40 +662,19 @@ namespace Game.Entities
return (uint)Math.Max(heal, 0.0f);
}
public bool IsSpellCrit(Unit victim, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
public float SpellCritChanceDone(SpellInfo spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
{
return RandomHelper.randChance(GetUnitSpellCriticalChance(victim, spell, aurEff, schoolMask, attackType));
}
public float GetUnitSpellCriticalChance(Unit victim, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
{
SpellInfo spellProto = spell?.GetSpellInfo() ?? aurEff?.GetSpellInfo();
//! Mobs can't crit with spells. Player Totems can
//! Fire Elemental (from totem) can too - but this part is a hack and needs more research
if (GetGUID().IsCreatureOrVehicle() && !(IsTotem() && GetOwnerGUID().IsPlayer()) && GetEntry() != 15438)
//! Mobs can't crit with spells. (Except player controlled)
if (IsCreature() && !GetSpellModOwner())
return 0.0f;
// not critting spell
if (spellProto.HasAttribute(SpellAttr2.CantCrit))
if (spellInfo.HasAttribute(SpellAttr2.CantCrit))
return 0.0f;
float crit_chance = 0.0f;
switch (spellProto.DmgClass)
switch (spellInfo.DmgClass)
{
case SpellDmgClass.None:
// We need more spells to find a general way (if there is any)
switch (spellProto.Id)
{
case 379: // Earth Shield
case 33778: // Lifebloom Final Bloom
case 64844: // Divine Hymn
case 71607: // Item - Bauble of True Blood 10m
case 71646: // Item - Bauble of True Blood 25m
break;
default:
return 0.0f;
}
goto case SpellDmgClass.Magic;
case SpellDmgClass.Magic:
{
if (schoolMask.HasAnyFlag(SpellSchoolMask.Normal))
@@ -690,26 +684,58 @@ namespace Game.Entities
crit_chance = ToPlayer().m_activePlayerData.SpellCritPercentage;
else
crit_chance = BaseSpellCritChance;
break;
}
case SpellDmgClass.Melee:
case SpellDmgClass.Ranged:
crit_chance += GetUnitCriticalChanceDone(attackType);
break;
case SpellDmgClass.None:
default:
return 0f;
}
// percent done
// only players use intelligence for critical chance computations
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritChance, ref crit_chance);
return Math.Max(crit_chance, 0.0f);
}
public float SpellCritChanceTaken(Unit caster, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, float doneChance, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
{
SpellInfo spellInfo = spell != null ? spell.GetSpellInfo() : aurEff.GetSpellInfo();
// not critting spell
if (spellInfo.HasAttribute(SpellAttr2.CantCrit))
return 0.0f;
float crit_chance = doneChance;
switch (spellInfo.DmgClass)
{
case SpellDmgClass.Magic:
{
// taken
if (victim)
if (!spellInfo.IsPositive())
{
// Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
crit_chance += GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
}
if (caster)
{
if (!spellProto.IsPositive())
{
// Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
crit_chance += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
}
// scripted (increase crit chance ... against ... target by x%
var mOverrideClassScript = GetAuraEffectsByType(AuraType.OverrideClassScripts);
var mOverrideClassScript = caster.GetAuraEffectsByType(AuraType.OverrideClassScripts);
foreach (var eff in mOverrideClassScript)
{
if (!eff.IsAffectingSpell(spellProto))
if (!eff.IsAffectingSpell(spellInfo))
continue;
switch (eff.GetMiscValue())
{
case 911: // Shatter
if (victim.HasAuraState(AuraStateType.Frozen, spellProto, this))
if (HasAuraState(AuraStateType.Frozen, spellInfo, this))
{
crit_chance *= 1.5f;
AuraEffect _eff = eff.GetBase().GetEffect(1);
@@ -722,19 +748,19 @@ namespace Game.Entities
}
}
// Custom crit by class
switch (spellProto.SpellFamilyName)
switch (spellInfo.SpellFamilyName)
{
case SpellFamilyNames.Rogue:
// Shiv-applied poisons can't crit
if (FindCurrentSpellBySpellId(5938) != null)
if (caster.FindCurrentSpellBySpellId(5938) != null)
crit_chance = 0.0f;
break;
case SpellFamilyNames.Shaman:
// Lava Burst
if (spellProto.SpellFamilyFlags[1].HasAnyFlag(0x00001000u))
if (spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x00001000u))
{
if (victim.GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Shaman, new FlagArray128(0x10000000, 0, 0), GetGUID()) != null)
if (victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100)
if (GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Shaman, new FlagArray128(0x10000000, 0, 0), caster.GetGUID()) != null)
if (GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100)
return 100.0f;
break;
}
@@ -742,9 +768,9 @@ namespace Game.Entities
}
// Spell crit suppression
if (victim.GetTypeId() == TypeId.Unit)
if (IsCreature())
{
int levelDiff = (int)(victim.GetLevelForTarget(this) - GetLevel());
int levelDiff = (int)(GetLevelForTarget(this) - caster.GetLevel());
crit_chance -= levelDiff * 1.0f;
}
}
@@ -753,38 +779,34 @@ namespace Game.Entities
case SpellDmgClass.Melee:
case SpellDmgClass.Ranged:
{
if (victim)
crit_chance += GetUnitCriticalChance(attackType, victim);
if (caster != null)
crit_chance += GetUnitCriticalChanceTaken(caster, attackType, crit_chance);
break;
}
case SpellDmgClass.None:
default:
return 0.0f;
return 0f;
}
// percent done
// only players use intelligence for critical chance computations
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritChance, ref crit_chance);
// for this types the bonus was already added in GetUnitCriticalChance, do not add twice
if (spellProto.DmgClass != SpellDmgClass.Melee && spellProto.DmgClass != SpellDmgClass.Ranged)
if (caster != null && spellInfo.DmgClass != SpellDmgClass.Melee && spellInfo.DmgClass != SpellDmgClass.Ranged)
{
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterWithAbilities, aurEff => aurEff.GetCasterGUID() == GetGUID() && aurEff.IsAffectingSpell(spellProto));
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterWithAbilities, aurEff => aurEff.GetCasterGUID() == caster.GetGUID() && aurEff.IsAffectingSpell(spellInfo));
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() != GetGUID());
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() == caster.GetGUID());
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !victim.HealthBelowPct(aurEff.GetMiscValueB()));
crit_chance += caster.GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !HealthBelowPct(aurEff.GetMiscValueB()));
TempSummon tempSummon = ToTempSummon();
TempSummon tempSummon = caster.ToTempSummon();
if (tempSummon != null)
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
}
// call script handlers
if (spell)
spell.CallScriptCalcCritChanceHandlers(victim, ref crit_chance);
spell.CallScriptCalcCritChanceHandlers(this, ref crit_chance);
else
aurEff.GetBase().CallScriptEffectCalcCritChanceHandlers(aurEff, aurEff.GetBase().GetApplicationOfTarget(victim.GetGUID()), victim, ref crit_chance);
aurEff.GetBase().CallScriptEffectCalcCritChanceHandlers(aurEff, aurEff.GetBase().GetApplicationOfTarget(GetGUID()), this, ref crit_chance);
return Math.Max(crit_chance, 0.0f);
}
@@ -1752,16 +1774,17 @@ namespace Game.Entities
return false;
}
public void ProcSkillsAndAuras(Unit actionTarget, ProcFlags typeMaskActor, ProcFlags typeMaskActionTarget, ProcFlagsSpellType spellTypeMask, ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
public static void ProcSkillsAndAuras(Unit actor, Unit actionTarget, ProcFlags typeMaskActor, ProcFlags typeMaskActionTarget, ProcFlagsSpellType spellTypeMask, ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
{
WeaponAttackType attType = damageInfo != null ? damageInfo.GetAttackType() : WeaponAttackType.BaseAttack;
if (typeMaskActor != 0)
ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType);
if (typeMaskActor != 0 && actor != null)
actor.ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType);
if (typeMaskActionTarget != 0 && actionTarget)
actionTarget.ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType);
actionTarget.ProcSkillsAndReactives(true, actor, typeMaskActionTarget, hitMask, attType);
TriggerAurasProcOnEvent(null, null, actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
if (actor != null)
actor.TriggerAurasProcOnEvent(null, null, actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
}
void ProcSkillsAndReactives(bool isVictim, Unit procTarget, ProcFlags typeMask, ProcFlagsHit hitMask, WeaponAttackType attType)
@@ -2125,30 +2148,33 @@ namespace Game.Entities
return false;
}
public uint SpellCriticalDamageBonus(SpellInfo spellProto, uint damage, Unit victim = null)
public static uint SpellCriticalDamageBonus(Unit caster, SpellInfo spellProto, uint damage, Unit victim = null)
{
// Calculate critical bonus
int crit_bonus = (int)damage * 2;
float crit_mod = 0.0f;
crit_mod += (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellProto.GetSchoolMask()) - 1.0f) * 100;
if (crit_bonus != 0)
MathFunctions.AddPct(ref crit_bonus, (int)crit_mod);
MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
if (caster != null)
{
return aurEff.GetCasterGUID() == GetGUID();
}));
crit_mod += (caster.GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellProto.GetSchoolMask()) - 1.0f) * 100;
crit_bonus -= (int)damage;
if (crit_bonus != 0)
MathFunctions.AddPct(ref crit_bonus, (int)crit_mod);
// adds additional damage to critBonus (from talents)
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
{
return aurEff.GetCasterGUID() == caster.GetGUID();
}));
crit_bonus += (int)damage;
crit_bonus -= (int)damage;
// adds additional damage to critBonus (from talents)
Player modOwner = caster.GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
crit_bonus += (int)damage;
}
return (uint)crit_bonus;
}
@@ -2197,46 +2223,53 @@ namespace Game.Entities
return spellInfo.GetSpellSpecific() == SpellSpecificType.MagePolymorph;
}
public void DealHeal(HealInfo healInfo)
public static void DealHeal(HealInfo healInfo)
{
int gain = 0;
Unit healer = healInfo.GetHealer();
Unit victim = healInfo.GetTarget();
uint addhealth = healInfo.GetHeal();
if (victim.IsAIEnabled)
victim.GetAI().HealReceived(this, addhealth);
if (healer)
{
if (victim.IsAIEnabled)
victim.GetAI().HealReceived(healer, addhealth);
if (IsAIEnabled)
GetAI().HealDone(victim, addhealth);
if (healer.IsAIEnabled)
healer.GetAI().HealDone(victim, addhealth);
}
if (addhealth != 0)
gain = (int)victim.ModifyHealth(addhealth);
// Hook for OnHeal Event
uint tempGain = (uint)gain;
Global.ScriptMgr.OnHeal(this, victim, ref tempGain);
Global.ScriptMgr.OnHeal(healer, victim, ref tempGain);
gain = (int)tempGain;
Unit unit = this;
Unit unit = healer;
if (healer != null && healer.IsCreature() && healer.IsTotem())
unit = healer.GetOwner();
if (IsTypeId(TypeId.Unit) && IsTotem())
unit = GetOwner();
Player player = unit.ToPlayer();
if (player != null)
if (unit)
{
Battleground bg = player.GetBattleground();
if (bg)
bg.UpdatePlayerScore(player, ScoreType.HealingDone, (uint)gain);
Player bgPlayer = unit.ToPlayer();
if (bgPlayer != null)
{
Battleground bg = bgPlayer.GetBattleground();
if (bg)
bg.UpdatePlayerScore(bgPlayer, ScoreType.HealingDone, (uint)gain);
// use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
if (gain != 0)
player.UpdateCriteria(CriteriaTypes.HealingDone, (uint)gain, 0, 0, victim);
// use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
if (gain != 0)
bgPlayer.UpdateCriteria(CriteriaTypes.HealingDone, (uint)gain, 0, 0, victim);
player.UpdateCriteria(CriteriaTypes.HighestHealCasted, addhealth);
bgPlayer.UpdateCriteria(CriteriaTypes.HighestHealCasted, addhealth);
}
}
if ((player = victim.ToPlayer()) != null)
Player player = victim.ToPlayer();
if (player != null)
{
player.UpdateCriteria(CriteriaTypes.TotalHealingReceived, (uint)gain);
player.UpdateCriteria(CriteriaTypes.HighestHealingReceived, addhealth);
@@ -2267,8 +2300,8 @@ namespace Game.Entities
{
// calculate heal absorb and reduce healing
CalcHealAbsorb(healInfo);
DealHeal(healInfo);
SendHealSpellLog(healInfo, critical);
return healInfo.GetEffectiveHeal();
}
@@ -2329,84 +2362,90 @@ namespace Game.Entities
SpellSchoolMask damageSchoolMask = damageInfo.schoolMask;
if (IsDamageReducedByArmor(damageSchoolMask, spellInfo))
damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType);
bool blocked = false;
// Per-school calc
switch (spellInfo.DmgClass)
// Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage.
if (!spellInfo.HasAttribute(SpellAttr4.FixedDamage))
{
// Melee and Ranged Spells
case SpellDmgClass.Ranged:
case SpellDmgClass.Melee:
{
// Physical Damage
if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal))
if (IsDamageReducedByArmor(damageSchoolMask, spellInfo))
damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType);
bool blocked = false;
// Per-school calc
switch (spellInfo.DmgClass)
{
// Melee and Ranged Spells
case SpellDmgClass.Ranged:
case SpellDmgClass.Melee:
{
// Spells with this attribute were already calculated in MeleeSpellHitResult
if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell))
// Physical Damage
if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal))
{
// Get blocked status
blocked = IsSpellBlocked(victim, spellInfo, attackType);
// Spells with this attribute were already calculated in MeleeSpellHitResult
if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell))
{
// Get blocked status
blocked = IsSpellBlocked(victim, spellInfo, attackType);
}
}
}
if (crit)
{
damageInfo.HitInfo |= HitInfo.CriticalHit;
// Calculate crit bonus
uint crit_bonus = (uint)damage;
// Apply crit_damage bonus for melee spells
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritDamageAndHealing, ref crit_bonus);
damage += (int)crit_bonus;
// Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
float critPctDamageMod = (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellInfo.GetSchoolMask()) - 1.0f) * 100;
if (critPctDamageMod != 0)
MathFunctions.AddPct(ref damage, (int)critPctDamageMod);
}
// Spell weapon based damage CAN BE crit & blocked at same time
if (blocked)
{
// double blocked amount if block is critical
float value = victim.GetBlockPercent(GetLevel());
if (victim.IsBlockCritical())
value *= 2; // double blocked percent
damageInfo.blocked = (uint)MathFunctions.CalculatePct(damage, value);
if (damage <= damageInfo.blocked)
if (crit)
{
damageInfo.blocked = (uint)damage;
damageInfo.fullBlock = true;
damageInfo.HitInfo |= HitInfo.CriticalHit;
// Calculate crit bonus
uint crit_bonus = (uint)damage;
// Apply crit_damage bonus for melee spells
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritDamageAndHealing, ref crit_bonus);
damage += (int)crit_bonus;
// Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
float critPctDamageMod = (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellInfo.GetSchoolMask()) - 1.0f) * 100;
if (critPctDamageMod != 0)
MathFunctions.AddPct(ref damage, (int)critPctDamageMod);
}
damage -= (int)damageInfo.blocked;
// Spell weapon based damage CAN BE crit & blocked at same time
if (blocked)
{
// double blocked amount if block is critical
float value = victim.GetBlockPercent(GetLevel());
if (victim.IsBlockCritical())
value *= 2; // double blocked percent
damageInfo.blocked = (uint)MathFunctions.CalculatePct(damage, value);
if (damage <= damageInfo.blocked)
{
damageInfo.blocked = (uint)damage;
damageInfo.fullBlock = true;
}
damage -= (int)damageInfo.blocked;
}
if (CanApplyResilience())
ApplyResilience(victim, ref damage);
break;
}
uint dmg = (uint)damage;
ApplyResilience(victim, ref dmg);
damage = (int)dmg;
break;
}
// Magical Attacks
case SpellDmgClass.None:
case SpellDmgClass.Magic:
{
// If crit add critical bonus
if (crit)
// Magical Attacks
case SpellDmgClass.None:
case SpellDmgClass.Magic:
{
damageInfo.HitInfo |= HitInfo.CriticalHit;
damage = (int)SpellCriticalDamageBonus(spellInfo, (uint)damage, victim);
// If crit add critical bonus
if (crit)
{
damageInfo.HitInfo |= HitInfo.CriticalHit;
damage = (int)SpellCriticalDamageBonus(this, spellInfo, (uint)damage, victim);
}
if (CanApplyResilience())
ApplyResilience(victim, ref damage);
break;
}
uint dmg = (uint)damage;
ApplyResilience(victim, ref dmg);
damage = (int)dmg;
default:
break;
}
default:
break;
}
}
// Script Hook For CalculateSpellDamageTaken -- Allow scripts to change the Damage post class mitigation calculations
@@ -2431,6 +2470,7 @@ namespace Game.Entities
damageInfo.damage = dmgInfo.GetDamage();
}
public void DealSpellDamage(SpellNonMeleeDamage damageInfo, bool durabilityLoss)
{
if (damageInfo == null)
@@ -2451,7 +2491,7 @@ namespace Game.Entities
// Call default DealDamage
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
DealDamage(victim, damageInfo.damage, cleanDamage, DamageEffectType.SpellDirect, damageInfo.schoolMask, damageInfo.Spell, durabilityLoss);
DealDamage(this, victim, damageInfo.damage, cleanDamage, DamageEffectType.SpellDirect, damageInfo.schoolMask, damageInfo.Spell, durabilityLoss);
}
public void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage log)
@@ -4181,6 +4221,7 @@ namespace Game.Entities
else
aurApp._HandleEffect(effIndex, true);
}
// handles effects of aura application
// should be done after registering aura in lists
public void _ApplyAura(AuraApplication aurApp, uint effMask)
@@ -4219,7 +4260,6 @@ namespace Game.Entities
if (aurApp.HasRemoveMode())
return;
aura.HandleAuraSpecificPeriodics(aurApp, caster);
aura.HandleAuraSpecificMods(aurApp, caster, true, false);
// apply effects of the aura
@@ -4229,6 +4269,7 @@ namespace Game.Entities
aurApp._HandleEffect(i, true);
}
}
public void _AddAura(UnitAura aura, Unit caster)
{
Cypher.Assert(!m_cleanupDone);
@@ -4261,6 +4302,7 @@ namespace Game.Entities
}
}
}
public Aura _TryStackingOrRefreshingExistingAura(SpellInfo newAura, uint effMask, Unit caster, int[] baseAmount = null, Item castItem = null, ObjectGuid casterGUID = default, bool resetPeriodicTimer = true, ObjectGuid castItemGuid = default, uint castItemId = 0, int castItemLevel = -1)
{
Cypher.Assert(!casterGUID.IsEmpty() || caster);
+281 -260
View File
@@ -2388,11 +2388,12 @@ namespace Game.Entities
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), index);
}
public bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1)
public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1)
{
// only physical spells damage gets reduced by armor
if ((schoolMask & SpellSchoolMask.Normal) == 0)
return false;
if (spellInfo != null)
{
// there are spells with no specific attribute but they have "ignores armor" in tooltip
@@ -2411,6 +2412,7 @@ namespace Game.Entities
}
}
}
return true;
}
@@ -2542,7 +2544,7 @@ namespace Game.Entities
void StartReactiveTimer(ReactiveType reactive) { m_reactiveTimer[reactive] = 4000; }
void DealDamageMods(Unit victim, ref uint damage)
public static void DealDamageMods(Unit attacker, Unit victim, ref uint damage)
{
if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsInEvadeMode()))
@@ -2550,7 +2552,8 @@ namespace Game.Entities
damage = 0;
}
}
public void DealDamageMods(Unit victim, ref uint damage, ref uint absorb)
public static void DealDamageMods(Unit attacker, Unit victim, ref uint damage, ref uint absorb)
{
if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
@@ -2560,156 +2563,22 @@ namespace Game.Entities
return;
}
damage = (uint)(damage * GetDamageMultiplierForTarget(victim));
if (attacker != null)
damage *= (uint)attacker.GetDamageMultiplierForTarget(victim);
}
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
{
Unit victim = damageInfo.target;
if (!victim.IsAlive() || victim.HasUnitState(UnitState.InFlight) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
return;
// Hmmmm dont like this emotes client must by self do all animations
if (damageInfo.HitInfo.HasAnyFlag(HitInfo.CriticalHit))
victim.HandleEmoteCommand(Emote.OneshotWoundCritical);
if (damageInfo.blocked_amount != 0 && damageInfo.TargetState != VictimState.Blocks)
victim.HandleEmoteCommand(Emote.OneshotParryShield);
if (damageInfo.TargetState == VictimState.Parry &&
(!IsTypeId(TypeId.Unit) || !ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoParryHasten)))
{
// Get attack timers
float offtime = victim.GetAttackTimer(WeaponAttackType.OffAttack);
float basetime = victim.GetAttackTimer(WeaponAttackType.BaseAttack);
// Reduce attack time
if (victim.HaveOffhandWeapon() && offtime < basetime)
{
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.OffAttack) * 0.20f;
float percent60 = 3.0f * percent20;
if (offtime > percent20 && offtime <= percent60)
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)percent20);
else if (offtime > percent60)
{
offtime -= 2.0f * percent20;
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)offtime);
}
}
else
{
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.BaseAttack) * 0.20f;
float percent60 = 3.0f * percent20;
if (basetime > percent20 && basetime <= percent60)
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)percent20);
else if (basetime > percent60)
{
basetime -= 2.0f * percent20;
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)basetime);
}
}
}
// Call default DealDamage
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, damageInfo.attackType, damageInfo.hitOutCome);
DealDamage(victim, damageInfo.damage, cleanDamage, DamageEffectType.Direct, (SpellSchoolMask)damageInfo.damageSchoolMask, null, durabilityLoss);
// If this is a creature and it attacks from behind it has a probability to daze it's victim
if ((damageInfo.hitOutCome == MeleeHitOutcome.Crit || damageInfo.hitOutCome == MeleeHitOutcome.Crushing || damageInfo.hitOutCome == MeleeHitOutcome.Normal || damageInfo.hitOutCome == MeleeHitOutcome.Glancing) &&
!IsTypeId(TypeId.Player) && !ToCreature().IsControlledByPlayer() && !victim.HasInArc(MathFunctions.PI, this)
&& (victim.IsTypeId(TypeId.Player) || !victim.ToCreature().IsWorldBoss()) && !victim.IsVehicle())
{
// 20% base chance
float chance = 20.0f;
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
if (victim.GetLevel() < 30)
chance = 0.65f * victim.GetLevelForTarget(this) + 0.5f;
uint victimDefense = victim.GetMaxSkillValueForLevel(this);
uint attackerMeleeSkill = GetMaxSkillValueForLevel();
chance *= attackerMeleeSkill / (float)victimDefense * 0.16f;
// -probability is between 0% and 40%
MathFunctions.RoundToInterval(ref chance, 0.0f, 40.0f);
if (RandomHelper.randChance(chance))
CastSpell(victim, 1604, true);
}
if (IsTypeId(TypeId.Player))
{
DamageInfo dmgInfo = new(damageInfo);
ToPlayer().CastItemCombatSpell(dmgInfo);
}
// Do effect if any damage done to target
if (damageInfo.damage != 0)
{
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
var vDamageShieldsCopy = victim.GetAuraEffectsByType(AuraType.DamageShield);
foreach (var dmgShield in vDamageShieldsCopy)
{
SpellInfo spellInfo = dmgShield.GetSpellInfo();
// Damage shield can be resisted...
var missInfo = victim.SpellHitResult(this, spellInfo, false);
if (missInfo != SpellMissInfo.None)
{
victim.SendSpellMiss(this, spellInfo.Id, missInfo);
continue;
}
// ...or immuned
if (IsImmunedToDamage(spellInfo))
{
victim.SendSpellDamageImmune(this, spellInfo.Id, false);
continue;
}
uint damage = (uint)dmgShield.GetAmount();
Unit caster = dmgShield.GetCaster();
if (caster)
{
damage = caster.SpellDamageBonusDone(this, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
damage = SpellDamageBonusTaken(caster, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
}
DamageInfo damageInfo1 = new(this, victim, damage, spellInfo, spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
victim.CalcAbsorbResist(damageInfo1);
damage = damageInfo1.GetDamage();
victim.DealDamageMods(this, ref damage);
SpellDamageShield damageShield = new();
damageShield.Attacker = victim.GetGUID();
damageShield.Defender = GetGUID();
damageShield.SpellID = spellInfo.Id;
damageShield.TotalDamage = damage;
damageShield.OriginalDamage = (int)damageInfo.originalDamage;
damageShield.OverKill = (uint)Math.Max(damage - GetHealth(), 0);
damageShield.SchoolMask = (uint)spellInfo.SchoolMask;
damageShield.LogAbsorbed = damageInfo1.GetAbsorb();
victim.DealDamage(this, damage, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true);
damageShield.LogData.Initialize(this);
victim.SendCombatLogMessage(damageShield);
}
}
}
public uint DealDamage(Unit victim, uint damage, CleanDamage cleanDamage = null, DamageEffectType damagetype = DamageEffectType.Direct, SpellSchoolMask damageSchoolMask = SpellSchoolMask.Normal, SpellInfo spellProto = null, bool durabilityLoss = true)
public static uint DealDamage(Unit attacker, Unit victim, uint damage, CleanDamage cleanDamage = null, DamageEffectType damagetype = DamageEffectType.Direct, SpellSchoolMask damageSchoolMask = SpellSchoolMask.Normal, SpellInfo spellProto = null, bool durabilityLoss = true)
{
if (victim.IsAIEnabled)
victim.GetAI().DamageTaken(this, ref damage);
victim.GetAI().DamageTaken(attacker, ref damage);
if (IsAIEnabled)
GetAI().DamageDealt(victim, ref damage, damagetype);
if (attacker != null && attacker.IsAIEnabled)
attacker.GetAI().DamageDealt(victim, ref damage, damagetype);
// Hook for OnDamage Event
Global.ScriptMgr.OnDamage(this, victim, ref damage);
Global.ScriptMgr.OnDamage(attacker, victim, ref damage);
if (victim.IsTypeId(TypeId.Player) && this != victim)
if (victim.IsTypeId(TypeId.Player) && attacker != victim)
{
// Signal to pets that their owner was attacked - except when DOT.
if (damagetype != DamageEffectType.DOT)
@@ -2719,7 +2588,7 @@ namespace Game.Entities
Creature cControlled = controlled.ToCreature();
if (cControlled != null)
if (cControlled.IsAIEnabled)
cControlled.GetAI().OwnerAttackedBy(this);
cControlled.GetAI().OwnerAttackedBy(attacker);
}
}
@@ -2740,7 +2609,7 @@ namespace Game.Entities
if (damage == 0 && damagetype != DamageEffectType.DOT && cleanDamage != null && cleanDamage.absorbed_damage != 0)
{
if (victim != this && victim.IsPlayer())
if (victim != attacker && victim.IsPlayer())
{
Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic);
if (spell != null)
@@ -2772,35 +2641,36 @@ namespace Game.Entities
uint share = MathFunctions.CalculatePct(damage, aura.GetAmount());
// @todo check packets if damage is done by victim, or by attacker of victim
DealDamageMods(shareDamageTarget, ref share);
DealDamage(shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false);
DealDamageMods(attacker, shareDamageTarget, ref share);
DealDamage(attacker, shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false);
}
}
// Rage from Damage made (only from direct weapon damage)
if (cleanDamage != null && (cleanDamage.attackType == WeaponAttackType.BaseAttack || cleanDamage.attackType == WeaponAttackType.OffAttack) && damagetype == DamageEffectType.Direct && this != victim && GetPowerType() == PowerType.Rage)
if (attacker != null && cleanDamage != null && (cleanDamage.attackType == WeaponAttackType.BaseAttack || cleanDamage.attackType == WeaponAttackType.OffAttack) && damagetype == DamageEffectType.Direct && attacker != victim && attacker.GetPowerType() == PowerType.Rage)
{
uint rage = (uint)(GetBaseAttackTime(cleanDamage.attackType) / 1000.0f * 1.75f);
uint rage = (uint)(attacker.GetBaseAttackTime(cleanDamage.attackType) / 1000.0f * 1.75f);
if (cleanDamage.attackType == WeaponAttackType.OffAttack)
rage /= 2;
RewardRage(rage);
attacker.RewardRage(rage);
}
if (damage == 0)
return 0;
Log.outDebug(LogFilter.Unit, "DealDamageStart");
uint health = (uint)victim.GetHealth();
Log.outDebug(LogFilter.Unit, "Unit {0} dealt {1} damage to unit {2}", GetGUID(), damage, victim.GetGUID());
// duel ends when player has 1 or less hp
bool duel_hasEnded = false;
bool duel_wasMounted = false;
if (victim.IsTypeId(TypeId.Player) && victim.ToPlayer().duel != null && damage >= (health - 1))
if (victim.IsPlayer() && victim.ToPlayer().duel != null && damage >= (health - 1))
{
if (!attacker)
return 0;
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
if (victim.ToPlayer().duel.opponent == this || victim.ToPlayer().duel.opponent.GetGUID() == GetOwnerGUID())
if (victim.ToPlayer().duel.opponent == attacker || victim.ToPlayer().duel.opponent.GetGUID() == attacker.GetOwnerGUID())
damage = health - 1;
duel_hasEnded = true;
@@ -2810,8 +2680,11 @@ namespace Game.Entities
Player victimRider = victim.GetCharmer().ToPlayer();
if (victimRider != null && victimRider.duel != null && victimRider.duel.isMounted)
{
if (!attacker)
return 0;
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
if (victimRider.duel.opponent == this || victimRider.duel.opponent.GetGUID() == GetCharmerGUID())
if (victimRider.duel.opponent == attacker || victimRider.duel.opponent.GetGUID() == attacker.GetCharmerGUID())
damage = health - 1;
duel_wasMounted = true;
@@ -2819,35 +2692,36 @@ namespace Game.Entities
}
}
if (IsTypeId(TypeId.Player) && this != victim)
if (attacker != null && attacker != victim)
{
Player killer = ToPlayer();
// in bg, count dmg if victim is also a player
if (victim.IsTypeId(TypeId.Player))
Player killer = attacker.ToPlayer();
if (killer != null)
{
Battleground bg = killer.GetBattleground();
if (bg)
bg.UpdatePlayerScore(killer, ScoreType.DamageDone, damage);
// in bg, count dmg if victim is also a player
if (victim.IsPlayer())
{
Battleground bg = killer.GetBattleground();
if (bg != null)
bg.UpdatePlayerScore(killer, ScoreType.DamageDone, damage);
}
killer.UpdateCriteria(CriteriaTypes.DamageDone, health > damage ? damage : health, 0, 0, victim);
killer.UpdateCriteria(CriteriaTypes.HighestHitDealt, damage);
}
killer.UpdateCriteria(CriteriaTypes.DamageDone, health > damage ? damage : health, 0, 0, victim);
killer.UpdateCriteria(CriteriaTypes.HighestHitDealt, damage);
}
if (victim.IsTypeId(TypeId.Player))
{
if (victim.IsPlayer())
victim.ToPlayer().UpdateCriteria(CriteriaTypes.HighestHitReceived, damage);
}
damage /= (uint)victim.GetHealthMultiplierForTarget(this);
if (attacker != null)
damage /= (uint)victim.GetHealthMultiplierForTarget(attacker);
if (victim.GetTypeId() != TypeId.Player && (!victim.IsControlledByPlayer() || victim.IsVehicle()))
{
if (!victim.ToCreature().HasLootRecipient())
victim.ToCreature().SetLootRecipient(this);
victim.ToCreature().SetLootRecipient(attacker);
if (IsControlledByPlayer())
if (attacker == null || attacker.IsControlledByPlayer())
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
}
@@ -2858,15 +2732,13 @@ namespace Game.Entities
{
killed = true;
Log.outDebug(LogFilter.Unit, "DealDamage: victim just died");
if (victim.IsTypeId(TypeId.Player) && victim != this)
if (victim.IsPlayer() && victim != attacker)
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, health);
if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.Self && victim.HasAuraType(AuraType.SchoolAbsorbOverkill))
{
var vAbsorbOverkill = victim.GetAuraEffectsByType(AuraType.SchoolAbsorbOverkill);
DamageInfo damageInfo = new(this, victim, damage, spellProto, damageSchoolMask, damagetype, cleanDamage != null ? cleanDamage.attackType : WeaponAttackType.BaseAttack);
DamageInfo damageInfo = new(attacker, victim, damage, spellProto, damageSchoolMask, damagetype, cleanDamage != null ? cleanDamage.attackType : WeaponAttackType.BaseAttack);
foreach (var absorbAurEff in vAbsorbOverkill)
{
@@ -2909,7 +2781,7 @@ namespace Game.Entities
if (currentAbsorb != 0)
{
SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = GetGUID();
absorbLog.Attacker = attacker != null ? attacker.GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = victim.GetGUID();
absorbLog.Caster = baseAura.GetCasterGUID();
absorbLog.AbsorbedSpellID = spellProto != null ? spellProto.Id : 0;
@@ -2917,7 +2789,7 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(victim);
SendCombatLogMessage(absorbLog);
victim.SendCombatLogMessage(absorbLog);
}
}
@@ -2926,11 +2798,9 @@ namespace Game.Entities
}
if (killed)
Kill(victim, durabilityLoss, skipSettingDeathState);
Kill(attacker, victim, durabilityLoss, skipSettingDeathState);
else
{
Log.outDebug(LogFilter.Unit, "DealDamageAlive");
if (victim.IsTypeId(TypeId.Player))
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, damage);
@@ -2945,7 +2815,8 @@ namespace Game.Entities
if (damagetype != DamageEffectType.DOT && damage > 0 && !victim.GetOwnerGUID().IsPlayer() && (spellProto == null || !spellProto.HasAura(AuraType.DamageShield)))
victim.ToCreature().SetLastDamagedTime(GameTime.GetGameTime() + SharedConst.MaxAggroResetTime);
victim.GetThreatManager().AddThreat(this, damage, spellProto);
if (attacker != null)
victim.GetThreatManager().AddThreat(attacker, damage, spellProto);
}
else // victim is a player
{
@@ -2957,19 +2828,19 @@ namespace Game.Entities
}
}
if (IsTypeId(TypeId.Player))
if (attacker != null && attacker.IsPlayer())
{
// random durability for items (HIT DONE)
if (RandomHelper.randChance(WorldConfig.GetFloatValue(WorldCfg.RateDurabilityLossDamage)))
{
byte slot = (byte)RandomHelper.IRand(0, EquipmentSlot.End - 1);
ToPlayer().DurabilityPointLossForEquipSlot(slot);
attacker.ToPlayer().DurabilityPointLossForEquipSlot(slot);
}
}
if (damagetype != DamageEffectType.NoDamage)
{
if (victim != this && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic))))
if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic))))
{
if (damagetype != DamageEffectType.DOT)
{
@@ -3044,11 +2915,147 @@ namespace Game.Entities
}
}
Log.outDebug(LogFilter.Unit, "DealDamageEnd returned {0} damage", damage);
return damage;
}
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
{
Unit victim = damageInfo.target;
if (!victim.IsAlive() || victim.HasUnitState(UnitState.InFlight) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
return;
// Hmmmm dont like this emotes client must by self do all animations
if (damageInfo.HitInfo.HasAnyFlag(HitInfo.CriticalHit))
victim.HandleEmoteCommand(Emote.OneshotWoundCritical);
if (damageInfo.blocked_amount != 0 && damageInfo.TargetState != VictimState.Blocks)
victim.HandleEmoteCommand(Emote.OneshotParryShield);
if (damageInfo.TargetState == VictimState.Parry &&
(!IsTypeId(TypeId.Unit) || !ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoParryHasten)))
{
// Get attack timers
float offtime = victim.GetAttackTimer(WeaponAttackType.OffAttack);
float basetime = victim.GetAttackTimer(WeaponAttackType.BaseAttack);
// Reduce attack time
if (victim.HaveOffhandWeapon() && offtime < basetime)
{
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.OffAttack) * 0.20f;
float percent60 = 3.0f * percent20;
if (offtime > percent20 && offtime <= percent60)
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)percent20);
else if (offtime > percent60)
{
offtime -= 2.0f * percent20;
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)offtime);
}
}
else
{
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.BaseAttack) * 0.20f;
float percent60 = 3.0f * percent20;
if (basetime > percent20 && basetime <= percent60)
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)percent20);
else if (basetime > percent60)
{
basetime -= 2.0f * percent20;
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)basetime);
}
}
}
// Call default DealDamage
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, damageInfo.attackType, damageInfo.hitOutCome);
DealDamage(this, victim, damageInfo.damage, cleanDamage, DamageEffectType.Direct, (SpellSchoolMask)damageInfo.damageSchoolMask, null, durabilityLoss);
// If this is a creature and it attacks from behind it has a probability to daze it's victim
if ((damageInfo.hitOutCome == MeleeHitOutcome.Crit || damageInfo.hitOutCome == MeleeHitOutcome.Crushing || damageInfo.hitOutCome == MeleeHitOutcome.Normal || damageInfo.hitOutCome == MeleeHitOutcome.Glancing) &&
!IsTypeId(TypeId.Player) && !ToCreature().IsControlledByPlayer() && !victim.HasInArc(MathFunctions.PI, this)
&& (victim.IsTypeId(TypeId.Player) || !victim.ToCreature().IsWorldBoss()) && !victim.IsVehicle())
{
// 20% base chance
float chance = 20.0f;
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
if (victim.GetLevel() < 30)
chance = 0.65f * victim.GetLevelForTarget(this) + 0.5f;
uint victimDefense = victim.GetMaxSkillValueForLevel(this);
uint attackerMeleeSkill = GetMaxSkillValueForLevel();
chance *= attackerMeleeSkill / (float)victimDefense * 0.16f;
// -probability is between 0% and 40%
MathFunctions.RoundToInterval(ref chance, 0.0f, 40.0f);
if (RandomHelper.randChance(chance))
CastSpell(victim, 1604, true);
}
if (IsTypeId(TypeId.Player))
{
DamageInfo dmgInfo = new(damageInfo);
ToPlayer().CastItemCombatSpell(dmgInfo);
}
// Do effect if any damage done to target
if (damageInfo.damage != 0)
{
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
var vDamageShieldsCopy = victim.GetAuraEffectsByType(AuraType.DamageShield);
foreach (var dmgShield in vDamageShieldsCopy)
{
SpellInfo spellInfo = dmgShield.GetSpellInfo();
// Damage shield can be resisted...
var missInfo = victim.SpellHitResult(this, spellInfo, false);
if (missInfo != SpellMissInfo.None)
{
victim.SendSpellMiss(this, spellInfo.Id, missInfo);
continue;
}
// ...or immuned
if (IsImmunedToDamage(spellInfo))
{
victim.SendSpellDamageImmune(this, spellInfo.Id, false);
continue;
}
uint damage = (uint)dmgShield.GetAmount();
Unit caster = dmgShield.GetCaster();
if (caster)
{
damage = caster.SpellDamageBonusDone(this, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
damage = SpellDamageBonusTaken(caster, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
}
DamageInfo damageInfo1 = new(this, victim, damage, spellInfo, spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
CalcAbsorbResist(damageInfo1);
damage = damageInfo1.GetDamage();
DealDamageMods(victim, this, ref damage);
SpellDamageShield damageShield = new();
damageShield.Attacker = victim.GetGUID();
damageShield.Defender = GetGUID();
damageShield.SpellID = spellInfo.Id;
damageShield.TotalDamage = damage;
damageShield.OriginalDamage = (int)damageInfo.originalDamage;
damageShield.OverKill = (uint)Math.Max(damage - GetHealth(), 0);
damageShield.SchoolMask = (uint)spellInfo.SchoolMask;
damageShield.LogAbsorbed = damageInfo1.GetAbsorb();
DealDamage(victim, this, damage, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true);
damageShield.LogData.Initialize(this);
victim.SendCombatLogMessage(damageShield);
}
}
}
public long ModifyHealth(long dVal)
{
long gain = 0;
@@ -3338,7 +3345,7 @@ namespace Game.Entities
RemovePvpFlag(UnitPVPStateFlags.PvP);
}
uint CalcSpellResistedDamage(Unit attacker, Unit victim, uint damage, SpellSchoolMask schoolMask, SpellInfo spellInfo)
static uint CalcSpellResistedDamage(Unit attacker, Unit victim, uint damage, SpellSchoolMask schoolMask, SpellInfo spellInfo)
{
// Magic damage, check for resists
if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Magic))
@@ -3359,7 +3366,7 @@ namespace Game.Entities
return 0;
}
float averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo);
float averageResist = CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo);
float[] discreteResistProbability = new float[11];
if (averageResist <= 0.1f)
@@ -3387,7 +3394,8 @@ namespace Game.Entities
{
int ignoredResistance = 0;
ignoredResistance += GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask);
if (attacker != null)
ignoredResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask);
ignoredResistance = Math.Min(ignoredResistance, 100);
MathFunctions.ApplyPct(ref damageResisted, 100 - ignoredResistance);
@@ -3407,20 +3415,23 @@ namespace Game.Entities
return (uint)damageResisted;
}
float CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo)
static float CalculateAverageResistReduction(Unit attacker, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null)
{
float victimResistance = (float)victim.GetResistance(schoolMask);
float victimResistance = victim.GetResistance(schoolMask);
// pets inherit 100% of masters penetration
// excluding traps
Player player = GetSpellModOwner();
if (player != null && GetEntry() != SharedConst.WorldTrigger)
if (attacker != null)
{
victimResistance += (float)player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
victimResistance -= (float)player.GetSpellPenetrationItemMod();
// pets inherit 100% of masters penetration
// excluding traps
Player player = attacker.GetSpellModOwner();
if (player != null && attacker.GetEntry() != SharedConst.WorldTrigger)
{
victimResistance += player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
victimResistance -= player.GetSpellPenetrationItemMod();
}
else
victimResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
}
else
victimResistance += (float)GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
// holy resistance exists in pve and comes from level difference, ignore template values
if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy))
@@ -3433,12 +3444,12 @@ namespace Game.Entities
victimResistance = Math.Max(victimResistance, 0.0f);
// level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
if (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell))
victimResistance += Math.Max(((float)victim.GetLevelForTarget(this) - (float)GetLevelForTarget(victim)) * 5.0f, 0.0f);
if (attacker != null && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)))
victimResistance += Math.Max(((float)victim.GetLevelForTarget(attacker) - (float)attacker.GetLevelForTarget(victim)) * 5.0f, 0.0f);
uint bossLevel = 83;
float bossResistanceConstant = 510.0f;
uint level = victim.GetLevelForTarget(this);
uint level = attacker != null ? victim.GetLevelForTarget(attacker) : attacker.GetLevel();
float resistanceConstant;
if (level == bossLevel)
@@ -3449,7 +3460,7 @@ namespace Game.Entities
return victimResistance / (victimResistance + resistanceConstant);
}
public void CalcAbsorbResist(DamageInfo damageInfo)
public static void CalcAbsorbResist(DamageInfo damageInfo)
{
if (!damageInfo.GetVictim() || !damageInfo.GetVictim().IsAlive() || damageInfo.GetDamage() == 0)
return;
@@ -3458,7 +3469,11 @@ namespace Game.Entities
damageInfo.ResistDamage(resistedDamage);
// Ignore Absorption Auras
float auraAbsorbMod = GetMaxPositiveAuraModifierByMiscMask(AuraType.ModTargetAbsorbSchool, (uint)damageInfo.GetSchoolMask());
float auraAbsorbMod = 0f;
Unit attacker = damageInfo.GetAttacker();
if (attacker != null)
auraAbsorbMod = attacker.GetMaxPositiveAuraModifierByMiscMask(AuraType.ModTargetAbsorbSchool, (uint)damageInfo.GetSchoolMask());
MathFunctions.RoundToInterval(ref auraAbsorbMod, 0.0f, 100.0f);
@@ -3522,7 +3537,7 @@ namespace Game.Entities
if (currentAbsorb != 0)
{
SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID();
absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = damageInfo.GetVictim().GetGUID();
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
@@ -3530,7 +3545,7 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(damageInfo.GetVictim());
SendCombatLogMessage(absorbLog);
damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
}
}
@@ -3596,7 +3611,7 @@ namespace Game.Entities
if (currentAbsorb != 0)
{
SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID();
absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = damageInfo.GetVictim().GetGUID();
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
@@ -3604,14 +3619,14 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(damageInfo.GetVictim());
SendCombatLogMessage(absorbLog);
damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
}
}
damageInfo.ModifyDamage(absorbIgnoringDamage);
// split damage auras - only when not damaging self
if (damageInfo.GetVictim() != this)
if (damageInfo.GetVictim() != damageInfo.GetAttacker())
{
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
@@ -3652,23 +3667,23 @@ namespace Game.Entities
}
uint split_absorb = 0;
DealDamageMods(caster, ref splitDamage, ref split_absorb);
DealDamageMods(damageInfo.GetAttacker(), caster, ref splitDamage, ref split_absorb);
SpellNonMeleeDamage log = new(this, caster, itr.GetSpellInfo(), itr.GetBase().GetSpellVisual(), damageInfo.GetSchoolMask(), itr.GetBase().GetCastGUID());
SpellNonMeleeDamage log = new(damageInfo.GetAttacker(), caster, itr.GetSpellInfo(), itr.GetBase().GetSpellVisual(), damageInfo.GetSchoolMask(), itr.GetBase().GetCastGUID());
CleanDamage cleanDamage = new(splitDamage, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
DealDamage(caster, splitDamage, cleanDamage, DamageEffectType.Direct, damageInfo.GetSchoolMask(), itr.GetSpellInfo(), false);
DealDamage(damageInfo.GetAttacker(), caster, splitDamage, cleanDamage, DamageEffectType.Direct, damageInfo.GetSchoolMask(), itr.GetSpellInfo(), false);
log.damage = splitDamage;
log.originalDamage = splitDamage;
log.absorb = split_absorb;
SendSpellNonMeleeDamageLog(log);
caster.SendSpellNonMeleeDamageLog(log);
// break 'Fear' and similar auras
ProcSkillsAndAuras(caster, ProcFlags.None, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, ProcFlagsHit.None, null, damageInfo, null);
ProcSkillsAndAuras(damageInfo.GetAttacker(), caster, ProcFlags.None, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, ProcFlagsHit.None, null, damageInfo, null);
}
}
}
public void CalcHealAbsorb(HealInfo healInfo)
public static void CalcHealAbsorb(HealInfo healInfo)
{
if (healInfo.GetHeal() == 0)
return;
@@ -3728,61 +3743,67 @@ namespace Game.Entities
}
}
public uint CalcArmorReducedDamage(Unit attacker, Unit victim, uint damage, SpellInfo spellInfo, WeaponAttackType attackType = WeaponAttackType.Max)
public static uint CalcArmorReducedDamage(Unit attacker, Unit victim, uint damage, SpellInfo spellInfo, WeaponAttackType attackType = WeaponAttackType.Max, uint attackerLevel = 0)
{
float armor = victim.GetArmor();
armor *= victim.GetArmorMultiplierForTarget(attacker);
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
int armorBypassPct = 0;
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
foreach (var eff in reductionAuras)
if (eff.GetCasterGUID() == GetGUID())
armorBypassPct += eff.GetAmount();
armor = MathFunctions.CalculatePct(armor, 100 - Math.Min(armorBypassPct, 100));
// Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
armor += GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)SpellSchoolMask.Normal);
if (spellInfo != null)
if (attacker != null)
{
Player modOwner = GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
}
armor *= victim.GetArmorMultiplierForTarget(attacker);
var resIgnoreAuras = GetAuraEffectsByType(AuraType.ModIgnoreTargetResist);
foreach (var eff in resIgnoreAuras)
{
if (eff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal) && eff.IsAffectingSpell(spellInfo))
armor = (float)Math.Floor(MathFunctions.AddPct(ref armor, -eff.GetAmount()));
}
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
int armorBypassPct = 0;
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
foreach (var eff in reductionAuras)
if (eff.GetCasterGUID() == attacker.GetGUID())
armorBypassPct += eff.GetAmount();
// Apply Player CR_ARMOR_PENETRATION rating
if (IsTypeId(TypeId.Player))
{
float arpPct = ToPlayer().GetRatingBonusValue(CombatRating.ArmorPenetration);
armor = MathFunctions.CalculatePct(armor, 100 - Math.Min(armorBypassPct, 100));
// no more than 100%
MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f);
// Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
armor += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)SpellSchoolMask.Normal);
float maxArmorPen;
if (victim.GetLevelForTarget(attacker) < 60)
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker);
else
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59);
if (spellInfo != null)
{
Player modOwner = attacker.GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
}
// Cap armor penetration to this number
maxArmorPen = Math.Min((armor + maxArmorPen) / 3.0f, armor);
// Figure out how much armor do we ignore
armor -= MathFunctions.CalculatePct(maxArmorPen, arpPct);
var resIgnoreAuras = attacker.GetAuraEffectsByType(AuraType.ModIgnoreTargetResist);
foreach (var eff in resIgnoreAuras)
{
if (eff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal) && eff.IsAffectingSpell(spellInfo))
armor = (float)Math.Floor(MathFunctions.AddPct(ref armor, -eff.GetAmount()));
}
// Apply Player CR_ARMOR_PENETRATION rating
if (attacker.IsPlayer())
{
float arpPct = attacker.ToPlayer().GetRatingBonusValue(CombatRating.ArmorPenetration);
// no more than 100%
MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f);
float maxArmorPen;
if (victim.GetLevelForTarget(attacker) < 60)
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker);
else
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59);
// Cap armor penetration to this number
maxArmorPen = Math.Min((armor + maxArmorPen) / 3.0f, armor);
// Figure out how much armor do we ignore
armor -= MathFunctions.CalculatePct(maxArmorPen, arpPct);
}
}
if (MathFunctions.fuzzyLe(armor, 0.0f))
return damage;
uint attackerLevel = attacker.GetLevelForTarget(victim);
if (attacker != null)
attackerLevel = attacker.GetLevelForTarget(victim);
// Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ?
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attacker.GetClass());
if ((armor + armorConstant) == 0)