Core/Auras: removed caster dependency from core
Port From (https://github.com/TrinityCore/TrinityCore/commit/fccf1a8c62820004df92b9ac567ba75f7281e6e2)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user