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
+9 -9
View File
@@ -568,7 +568,7 @@ namespace Game.Chat
// flat melee damage without resistence/etc reduction // flat melee damage without resistence/etc reduction
if (string.IsNullOrEmpty(schoolStr)) if (string.IsNullOrEmpty(schoolStr))
{ {
attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, SpellSchoolMask.Normal, null, false); Unit.DealDamage(attacker, target, damage_, null, DamageEffectType.Direct, SpellSchoolMask.Normal, null, false);
if (target != attacker) if (target != attacker)
attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, SpellSchoolMask.Normal, damage_, 0, 0, VictimState.Hit, 0); attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, SpellSchoolMask.Normal, damage_, 0, 0, VictimState.Hit, 0);
return true; return true;
@@ -579,8 +579,8 @@ namespace Game.Chat
SpellSchoolMask schoolmask = (SpellSchoolMask)(1 << school); SpellSchoolMask schoolmask = (SpellSchoolMask)(1 << school);
if (attacker.IsDamageReducedByArmor(schoolmask)) if (Unit.IsDamageReducedByArmor(schoolmask))
damage_ = attacker.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack); damage_ = Unit.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack);
string spellStr = args.NextString(); string spellStr = args.NextString();
@@ -588,7 +588,7 @@ namespace Game.Chat
if (string.IsNullOrEmpty(spellStr)) if (string.IsNullOrEmpty(spellStr))
{ {
DamageInfo dmgInfo = new(attacker, target, damage_, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack); DamageInfo dmgInfo = new(attacker, target, damage_, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
attacker.CalcAbsorbResist(dmgInfo); Unit.CalcAbsorbResist(dmgInfo);
if (dmgInfo.GetDamage() == 0) if (dmgInfo.GetDamage() == 0)
return true; return true;
@@ -597,8 +597,8 @@ namespace Game.Chat
uint absorb = dmgInfo.GetAbsorb(); uint absorb = dmgInfo.GetAbsorb();
uint resist = dmgInfo.GetResist(); uint resist = dmgInfo.GetResist();
attacker.DealDamageMods(target, ref damage_, ref absorb); Unit.DealDamageMods(attacker, target, ref damage_, ref absorb);
attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, schoolmask, null, false); Unit.DealDamage(attacker, target, damage_, null, DamageEffectType.Direct, schoolmask, null, false);
attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, schoolmask, damage_, absorb, resist, VictimState.Hit, 0); attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, schoolmask, damage_, absorb, resist, VictimState.Hit, 0);
return true; return true;
} }
@@ -613,9 +613,9 @@ namespace Game.Chat
if (spellInfo == null) if (spellInfo == null)
return false; return false;
SpellNonMeleeDamage damageInfo = new(attacker, target, spellInfo, new Networking.Packets.SpellCastVisual(spellInfo.GetSpellXSpellVisualId(attacker), 0), spellInfo.SchoolMask); SpellNonMeleeDamage damageInfo = new(attacker, target, spellInfo, new SpellCastVisual(spellInfo.GetSpellXSpellVisualId(attacker), 0), spellInfo.SchoolMask);
damageInfo.damage = damage_; damageInfo.damage = damage_;
attacker.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
target.DealSpellDamage(damageInfo, true); target.DealSpellDamage(damageInfo, true);
target.SendSpellNonMeleeDamageLog(damageInfo); target.SendSpellNonMeleeDamageLog(damageInfo);
return true; return true;
@@ -670,7 +670,7 @@ namespace Game.Chat
return false; return false;
if (target.IsAlive()) if (target.IsAlive())
handler.GetSession().GetPlayer().Kill(target); Unit.Kill(handler.GetSession().GetPlayer(), target);
return true; return true;
} }
+2 -2
View File
@@ -3956,7 +3956,7 @@ namespace Game.Entities
break; break;
} }
DealDamageMods(this, ref damage, ref absorb); DealDamageMods(null, this, ref damage, ref absorb);
EnvironmentalDamageLog packet = new(); EnvironmentalDamageLog packet = new();
packet.Victim = GetGUID(); packet.Victim = GetGUID();
@@ -3965,7 +3965,7 @@ namespace Game.Entities
packet.Absorbed = (int)absorb; packet.Absorbed = (int)absorb;
packet.Resisted = (int)resist; packet.Resisted = (int)resist;
uint final_damage = DealDamage(this, damage, null, DamageEffectType.Self, SpellSchoolMask.Normal, null, false); uint final_damage = DealDamage(this, this, damage, null, DamageEffectType.Self, SpellSchoolMask.Normal, null, false);
packet.LogData.Initialize(this); packet.LogData.Initialize(this);
SendCombatLogMessage(packet); SendCombatLogMessage(packet);
+49 -20
View File
@@ -667,7 +667,7 @@ namespace Game.Entities
return 0; return 0;
} }
public virtual uint GetPowerIndex(PowerType powerType) { return 0; } public virtual uint GetPowerIndex(PowerType powerType) { return 0; }
public float GetPowerPct(PowerType powerType) { return GetMaxPower(powerType) != 0 ? 100.0f* GetPower(powerType) / GetMaxPower(powerType) : 0.0f; } public float GetPowerPct(PowerType powerType) { return GetMaxPower(powerType) != 0 ? 100.0f * GetPower(powerType) / GetMaxPower(powerType) : 0.0f; }
void TriggerOnPowerChangeAuras(PowerType power, int oldVal, int newVal) void TriggerOnPowerChangeAuras(PowerType power, int oldVal, int newVal)
{ {
@@ -713,27 +713,43 @@ namespace Game.Entities
} }
} }
public void ApplyResilience(Unit victim, ref uint damage) public bool CanApplyResilience()
{
return !IsVehicle() && GetOwnerGUID().IsPlayer();
}
public static void ApplyResilience(Unit victim, ref int damage)
{ {
// player mounted on multi-passenger mount is also classified as vehicle // player mounted on multi-passenger mount is also classified as vehicle
if (IsVehicle() || (victim.IsVehicle() && !victim.IsTypeId(TypeId.Player))) if (victim.IsVehicle() && !victim.IsPlayer())
return;
// Don't consider resilience if not in PvP - player or pet
if (!GetCharmerOrOwnerPlayerOrPlayerItself())
return; return;
Unit target = null; Unit target = null;
if (victim.IsTypeId(TypeId.Player)) if (victim.IsPlayer())
target = victim; target = victim;
else if (victim.IsTypeId(TypeId.Unit) && victim.GetOwner() && victim.GetOwner().IsTypeId(TypeId.Player)) else // victim->GetTypeId() == TYPEID_UNIT
target = victim.GetOwner(); {
Unit owner = victim.GetOwner();
if (owner != null)
if (owner.IsPlayer())
target = owner;
}
if (!target) if (!target)
return; return;
damage -= target.GetDamageReduction(damage); damage -= (int)target.GetDamageReduction((uint)damage);
} }
public int CalculateAOEAvoidance(int damage, uint schoolMask, Unit caster)
{
damage = (int)((float)damage * GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, schoolMask));
if (caster.IsCreature())
damage = (int)((float)damage * GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, schoolMask));
return damage;
}
// player or player's pet resilience (-1%) // player or player's pet resilience (-1%)
uint GetDamageReduction(uint damage) { return GetCombatRatingDamageReduction(CombatRating.ResiliencePlayerDamage, 1.0f, 100.0f, damage); } uint GetDamageReduction(uint damage) { return GetCombatRatingDamageReduction(CombatRating.ResiliencePlayerDamage, 1.0f, 100.0f, damage); }
@@ -802,7 +818,7 @@ namespace Game.Entities
return missChance; return missChance;
} }
float GetUnitCriticalChance(WeaponAttackType attackType, Unit victim) float GetUnitCriticalChanceDone(WeaponAttackType attackType)
{ {
float chance = 0.0f; float chance = 0.0f;
Player thisPlayer = ToPlayer(); Player thisPlayer = ToPlayer();
@@ -830,25 +846,38 @@ namespace Game.Entities
chance += GetTotalAuraModifier(AuraType.ModCritPct); chance += GetTotalAuraModifier(AuraType.ModCritPct);
} }
} }
return chance;
}
float GetUnitCriticalChanceTaken(Unit attacker, WeaponAttackType attackType, float critDone)
{
float chance = critDone;
// flat aura mods // flat aura mods
if (attackType == WeaponAttackType.RangedAttack) if (attackType == WeaponAttackType.RangedAttack)
chance += victim.GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance); chance += GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance);
else else
chance += victim.GetTotalAuraModifier(AuraType.ModAttackerMeleeCritChance); chance += GetTotalAuraModifier(AuraType.ModAttackerMeleeCritChance);
chance += GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !victim.HealthBelowPct(aurEff.GetMiscValueB())); chance += GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !HealthBelowPct(aurEff.GetMiscValueB()));
chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() == GetGUID()); chance += GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() == attacker.GetGUID());
TempSummon tempSummon = ToTempSummon(); TempSummon tempSummon = attacker.ToTempSummon();
if (tempSummon) if (tempSummon != null)
chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID()); chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
chance += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance); chance += GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
return Math.Max(chance, 0.0f); return Math.Max(chance, 0.0f);
} }
float GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit victim)
{
float chance = GetUnitCriticalChanceDone(attackType);
return victim.GetUnitCriticalChanceTaken(this, attackType, chance);
}
float GetUnitDodgeChance(WeaponAttackType attType, Unit victim) float GetUnitDodgeChance(WeaponAttackType attType, Unit victim)
{ {
int levelDiff = (int)(victim.GetLevelForTarget(this) - GetLevelForTarget(victim)); int levelDiff = (int)(victim.GetLevelForTarget(this) - GetLevelForTarget(victim));
+72 -117
View File
@@ -46,7 +46,7 @@ namespace Game.Entities
InterruptNonMeleeSpells(false); InterruptNonMeleeSpells(false);
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat); 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() public virtual void AtExitCombat()
@@ -558,13 +558,13 @@ namespace Game.Entities
CalcDamageInfo damageInfo; CalcDamageInfo damageInfo;
CalculateMeleeDamage(victim, 0, out damageInfo, attType); CalculateMeleeDamage(victim, 0, out damageInfo, attType);
// Send log damage message to client // 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); SendAttackStateUpdate(damageInfo);
DealMeleeDamage(damageInfo, true); DealMeleeDamage(damageInfo, true);
DamageInfo dmgInfo = new(damageInfo); 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}.", 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); 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) internal void SendCombatLogMessage(CombatLogServerPacket combatLog)
{ {
CombatLogSender combatLogSender = new(combatLog); CombatLogSender combatLogSender = new(combatLog);
@@ -837,14 +772,20 @@ namespace Game.Entities
return !m_threatManager.IsThreatListEmpty(); 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) // Prevent killing unit twice (and giving reward from kill twice)
if (victim.GetHealth() == 0) if (victim.GetHealth() == 0)
return; return;
if (attacker != null && !attacker.IsInMap(victim))
attacker = null;
// find player: owner of controlled `this` or `this` itself maybe // 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(); Creature creature = victim.ToCreature();
bool isRewardAllowed = true; bool isRewardAllowed = true;
@@ -911,7 +852,7 @@ namespace Game.Entities
loot.Clear(); loot.Clear();
uint lootid = creature.GetCreatureTemplate().LootId; uint lootid = creature.GetCreatureTemplate().LootId;
if (lootid != 0) 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) if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold); 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 // 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 // proc only once for victim
Unit owner = GetOwner(); Unit owner = attacker.GetOwner();
if (owner != null) 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()) 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 // 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 // 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 // and before Spirit of Redemption as it also removes auras
if (player != null) if (attacker != null)
player.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim); {
Player killerPlayer = attacker.GetCharmerOrOwnerPlayerOrPlayerItself();
if (killerPlayer != null)
killerPlayer.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim);
}
if (!skipSettingDeathState) if (!skipSettingDeathState)
{ {
@@ -985,11 +930,11 @@ namespace Game.Entities
// Durability loss is calculated more accurately again for each item in Player.DurabilityLoss // Durability loss is calculated more accurately again for each item in Player.DurabilityLoss
plrVictim.DurabilityLossAll(baseLoss, false); plrVictim.DurabilityLossAll(baseLoss, false);
// durability lost message // durability lost message
SendDurabilityLoss(plrVictim, loss); plrVictim.SendDurabilityLoss(plrVictim, loss);
} }
// Call KilledUnit for creatures // Call KilledUnit for creatures
if (IsTypeId(TypeId.Unit) && IsAIEnabled) if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
ToCreature().GetAI().KilledUnit(victim); attacker.ToCreature().GetAI().KilledUnit(victim);
// last damage from non duel opponent or opponent controlled creature // last damage from non duel opponent or opponent controlled creature
if (plrVictim.duel != null) 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 // Call KilledUnit for creatures, this needs to be called after the lootable flag is set
if (IsTypeId(TypeId.Unit) && IsAIEnabled) if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
ToCreature().GetAI().KilledUnit(victim); attacker.ToCreature().GetAI().KilledUnit(victim);
// Call creature just died function // Call creature just died function
if (creature.IsAIEnabled) if (creature.IsAIEnabled)
creature.GetAI().JustDied(this); creature.GetAI().JustDied(attacker);
TempSummon summon = creature.ToTempSummon(); TempSummon summon = creature.ToTempSummon();
if (summon != null) if (summon != null)
@@ -1028,22 +973,21 @@ namespace Game.Entities
Unit summoner = summon.GetSummoner(); Unit summoner = summon.GetSummoner();
if (summoner != null) if (summoner != null)
if (summoner.IsTypeId(TypeId.Unit) && summoner.IsAIEnabled) 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 // Dungeon specific stuff, only applies to players killing creatures
if (creature.GetInstanceId() != 0) if (creature.GetInstanceId() != 0)
{ {
Map instanceMap = creature.GetMap(); 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 (instanceMap.IsRaidOrHeroicDungeon())
{ {
if (creature.GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.InstanceBind)) if (creature.GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.InstanceBind))
((InstanceMap)instanceMap).PermBindAllPlayers(); instanceMap.ToInstanceMap().PermBindAllPlayers();
} }
else 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... // 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) // 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(); OutdoorPvP pvp = player.GetOutdoorPvP();
if (pvp != null) if (pvp != null)
@@ -1087,35 +1031,44 @@ namespace Game.Entities
} }
// achievement stuff // achievement stuff
if (victim.IsTypeId(TypeId.Player)) if (attacker != null && victim.IsPlayer())
{ {
if (IsTypeId(TypeId.Unit)) if (attacker.IsCreature())
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, GetEntry()); victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, attacker.GetEntry());
else if (IsTypeId(TypeId.Player) && victim != this) else if (attacker.IsPlayer() && victim != attacker)
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)ToPlayer().GetTeam()); victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)attacker.ToPlayer().GetTeam());
} }
// Hook for OnPVPKill Event // Hook for OnPVPKill Event
Player killerPlr = ToPlayer(); if (attacker != null)
Creature killerCre = ToCreature();
if (killerPlr != null)
{ {
Player killedPlr = victim.ToPlayer(); Player killerPlr = attacker.ToPlayer();
Creature killedCre = victim.ToCreature(); if (killerPlr != null)
if (killedPlr != null) {
Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr); Player killedPlr = victim.ToPlayer();
else if (killedCre != null) if (killedPlr != null)
Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre); Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr);
} else
else if (killerCre != null) {
{ Creature killedCre = victim.ToCreature();
Player killed = victim.ToPlayer(); if (killedCre != null)
if (killed != null) Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre);
Global.ScriptMgr.OnPlayerKilledByCreature(killerCre, killed); }
}
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) public virtual bool CanUseAttackType(WeaponAttackType attacktype)
{ {
@@ -1289,11 +1242,13 @@ namespace Game.Entities
if (!damageInfo.HitInfo.HasAnyFlag(HitInfo.Miss)) if (!damageInfo.HitInfo.HasAnyFlag(HitInfo.Miss))
damageInfo.HitInfo |= HitInfo.AffectsVictim; damageInfo.HitInfo |= HitInfo.AffectsVictim;
uint resilienceReduction = damageInfo.damage; int resilienceReduction = (int)damageInfo.damage;
ApplyResilience(victim, ref resilienceReduction); if (CanApplyResilience())
resilienceReduction = damageInfo.damage - resilienceReduction; ApplyResilience(victim, ref resilienceReduction);
damageInfo.damage -= resilienceReduction;
damageInfo.cleanDamage += resilienceReduction; resilienceReduction = (int)damageInfo.damage - resilienceReduction;
damageInfo.damage -= (uint)resilienceReduction;
damageInfo.cleanDamage += (uint)resilienceReduction;
// Calculate absorb resist // Calculate absorb resist
if (damageInfo.damage > 0) if (damageInfo.damage > 0)
@@ -1326,7 +1281,7 @@ namespace Game.Entities
int miss_chance = (int)(MeleeSpellMissChance(victim, attType, null) * 100.0f); int miss_chance = (int)(MeleeSpellMissChance(victim, attType, null) * 100.0f);
// Critical hit chance // 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 dodge_chance = (int)(GetUnitDodgeChance(attType, victim) * 100.0f);
int block_chance = (int)(GetUnitBlockChance(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; int DoneTotal = 0;
float DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype);
// Done fixed damage bonus auras // Done fixed damage bonus auras
int DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto.GetSchoolMask()); int DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto.GetSchoolMask());
@@ -141,27 +142,21 @@ namespace Game.Entities
float coeff = effect.BonusCoefficient; float coeff = effect.BonusCoefficient;
if (DoneAdvertisedBenefit != 0) if (DoneAdvertisedBenefit != 0)
{ {
Player modOwner = GetSpellModOwner(); Player modOwner1 = GetSpellModOwner();
if (modOwner) if (modOwner1)
{ {
coeff *= 100.0f; coeff *= 100.0f;
modOwner.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff); modOwner1.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff);
coeff /= 100.0f; coeff /= 100.0f;
} }
DoneTotal += (int)(DoneAdvertisedBenefit * coeff * stack); 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 = (float)((int)pdamage + DoneTotal) * DoneTotalMod;
float tmpDamage = ((int)pdamage + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype));
// apply spellmod to Done damage (flat and pct) // apply spellmod to Done damage (flat and pct)
Player _modOwner = GetSpellModOwner(); Player _modOwner = GetSpellModOwner();
if (_modOwner) if (_modOwner != null)
{ _modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref tmpDamage);
if (damagetype == DamageEffectType.DOT)
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref tmpDamage);
else
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref tmpDamage);
}
return (uint)Math.Max(tmpDamage, 0.0f); return (uint)Math.Max(tmpDamage, 0.0f);
} }
@@ -171,13 +166,21 @@ namespace Game.Entities
if (spellProto == null || !victim || damagetype == DamageEffectType.Direct) if (spellProto == null || !victim || damagetype == DamageEffectType.Direct)
return 1.0f; 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 // Some spells don't benefit from pct done mods
if (spellProto.HasAttribute(SpellAttr6.IgnoreCasterDamageModifiers)) if (spellProto.HasAttribute(SpellAttr6.IgnoreCasterDamageModifiers))
return 1.0f; return 1.0f;
// For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone. // For totems get damage bonus from owner
if (IsTypeId(TypeId.Unit) && IsTotem()) if (IsCreature() && IsTotem())
return 1.0f; {
Unit owner = GetOwner();
if (owner != null)
return owner.SpellDamagePctDone(victim, spellProto, damagetype);
}
// Done total percent damage auras // Done total percent damage auras
float DoneTotalMod = 1.0f; float DoneTotalMod = 1.0f;
@@ -392,19 +395,23 @@ namespace Game.Entities
return GetTotalAuraModifierByMiscMask(AuraType.ModHealing, (int)schoolMask); 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 // Calculate critical bonus
int crit_bonus = damage; int crit_bonus = damage;
// adds additional damage to critBonus (from talents) // adds additional damage to critBonus (from talents)
Player modOwner = GetSpellModOwner(); if (caster != null)
if (modOwner != null) {
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus); Player modOwner = caster.GetSpellModOwner();
if (modOwner != null)
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
}
damage += crit_bonus; damage += crit_bonus;
damage = (int)(damage * GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount)); if (caster != null)
damage = (int)(damage * caster.GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount));
return damage; return damage;
} }
@@ -424,19 +431,20 @@ namespace Game.Entities
return healamount; return healamount;
int DoneTotal = 0; int DoneTotal = 0;
float DoneTotalMod = SpellHealingPctDone(victim, spellProto);
// done scripted mod (take it from owner) // done scripted mod (take it from owner)
Unit owner1 = GetOwner() ?? this; Unit owner1 = GetOwner() ?? this;
var mOverrideClassScript = owner1.GetAuraEffectsByType(AuraType.OverrideClassScripts); 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; 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 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; break;
default: default:
break; break;
@@ -491,25 +499,32 @@ namespace Game.Entities
DoneTotal = 0; DoneTotal = 0;
} }
// use float as more appropriate for negative values and percent applying float heal = (float)((int)healamount + DoneTotal) * DoneTotalMod;
float heal = (healamount + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellHealingPctDone(victim, spellProto));
// apply spellmod to Done amount // apply spellmod to Done amount
Player _modOwner = GetSpellModOwner(); Player _modOwner = GetSpellModOwner();
if (_modOwner) if (_modOwner)
{ _modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref heal);
if (damagetype == DamageEffectType.DOT)
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref heal);
else
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref heal);
}
return (uint)Math.Max(heal, 0.0f); return (uint)Math.Max(heal, 0.0f);
} }
public float SpellHealingPctDone(Unit victim, SpellInfo spellProto) public float SpellHealingPctDone(Unit victim, SpellInfo spellProto)
{ {
// For totems pct done mods are calculated when its calculation is run on the player in SpellHealingBonusDone. // For totems get healing bonus from owner
if (IsTypeId(TypeId.Unit) && IsTotem()) 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; return 1.0f;
// No bonus healing for potion spells // No bonus healing for potion spells
@@ -647,40 +662,19 @@ namespace Game.Entities
return (uint)Math.Max(heal, 0.0f); 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)); //! Mobs can't crit with spells. (Except player controlled)
} if (IsCreature() && !GetSpellModOwner())
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)
return 0.0f; return 0.0f;
// not critting spell // not critting spell
if (spellProto.HasAttribute(SpellAttr2.CantCrit)) if (spellInfo.HasAttribute(SpellAttr2.CantCrit))
return 0.0f; return 0.0f;
float crit_chance = 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: case SpellDmgClass.Magic:
{ {
if (schoolMask.HasAnyFlag(SpellSchoolMask.Normal)) if (schoolMask.HasAnyFlag(SpellSchoolMask.Normal))
@@ -690,26 +684,58 @@ namespace Game.Entities
crit_chance = ToPlayer().m_activePlayerData.SpellCritPercentage; crit_chance = ToPlayer().m_activePlayerData.SpellCritPercentage;
else else
crit_chance = BaseSpellCritChance; 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 // 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% // scripted (increase crit chance ... against ... target by x%
var mOverrideClassScript = GetAuraEffectsByType(AuraType.OverrideClassScripts); var mOverrideClassScript = caster.GetAuraEffectsByType(AuraType.OverrideClassScripts);
foreach (var eff in mOverrideClassScript) foreach (var eff in mOverrideClassScript)
{ {
if (!eff.IsAffectingSpell(spellProto)) if (!eff.IsAffectingSpell(spellInfo))
continue; continue;
switch (eff.GetMiscValue()) switch (eff.GetMiscValue())
{ {
case 911: // Shatter case 911: // Shatter
if (victim.HasAuraState(AuraStateType.Frozen, spellProto, this)) if (HasAuraState(AuraStateType.Frozen, spellInfo, this))
{ {
crit_chance *= 1.5f; crit_chance *= 1.5f;
AuraEffect _eff = eff.GetBase().GetEffect(1); AuraEffect _eff = eff.GetBase().GetEffect(1);
@@ -722,19 +748,19 @@ namespace Game.Entities
} }
} }
// Custom crit by class // Custom crit by class
switch (spellProto.SpellFamilyName) switch (spellInfo.SpellFamilyName)
{ {
case SpellFamilyNames.Rogue: case SpellFamilyNames.Rogue:
// Shiv-applied poisons can't crit // Shiv-applied poisons can't crit
if (FindCurrentSpellBySpellId(5938) != null) if (caster.FindCurrentSpellBySpellId(5938) != null)
crit_chance = 0.0f; crit_chance = 0.0f;
break; break;
case SpellFamilyNames.Shaman: case SpellFamilyNames.Shaman:
// Lava Burst // 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 (GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Shaman, new FlagArray128(0x10000000, 0, 0), caster.GetGUID()) != null)
if (victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100) if (GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100)
return 100.0f; return 100.0f;
break; break;
} }
@@ -742,9 +768,9 @@ namespace Game.Entities
} }
// Spell crit suppression // 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; crit_chance -= levelDiff * 1.0f;
} }
} }
@@ -753,38 +779,34 @@ namespace Game.Entities
case SpellDmgClass.Melee: case SpellDmgClass.Melee:
case SpellDmgClass.Ranged: case SpellDmgClass.Ranged:
{ {
if (victim) if (caster != null)
crit_chance += GetUnitCriticalChance(attackType, victim); crit_chance += GetUnitCriticalChanceTaken(caster, attackType, crit_chance);
break; break;
} }
case SpellDmgClass.None:
default: 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 // 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) 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 // call script handlers
if (spell) if (spell)
spell.CallScriptCalcCritChanceHandlers(victim, ref crit_chance); spell.CallScriptCalcCritChanceHandlers(this, ref crit_chance);
else 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); return Math.Max(crit_chance, 0.0f);
} }
@@ -1752,16 +1774,17 @@ namespace Game.Entities
return false; 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; WeaponAttackType attType = damageInfo != null ? damageInfo.GetAttackType() : WeaponAttackType.BaseAttack;
if (typeMaskActor != 0) if (typeMaskActor != 0 && actor != null)
ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); actor.ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType);
if (typeMaskActionTarget != 0 && actionTarget) 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) void ProcSkillsAndReactives(bool isVictim, Unit procTarget, ProcFlags typeMask, ProcFlagsHit hitMask, WeaponAttackType attType)
@@ -2125,30 +2148,33 @@ namespace Game.Entities
return false; 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 // Calculate critical bonus
int crit_bonus = (int)damage * 2; int crit_bonus = (int)damage * 2;
float crit_mod = 0.0f; float crit_mod = 0.0f;
crit_mod += (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellProto.GetSchoolMask()) - 1.0f) * 100; if (caster != null)
if (crit_bonus != 0)
MathFunctions.AddPct(ref crit_bonus, (int)crit_mod);
MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
{ {
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) MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
Player modOwner = GetSpellModOwner(); {
if (modOwner != null) return aurEff.GetCasterGUID() == caster.GetGUID();
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus); }));
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; return (uint)crit_bonus;
} }
@@ -2197,46 +2223,53 @@ namespace Game.Entities
return spellInfo.GetSpellSpecific() == SpellSpecificType.MagePolymorph; return spellInfo.GetSpellSpecific() == SpellSpecificType.MagePolymorph;
} }
public void DealHeal(HealInfo healInfo) public static void DealHeal(HealInfo healInfo)
{ {
int gain = 0; int gain = 0;
Unit healer = healInfo.GetHealer();
Unit victim = healInfo.GetTarget(); Unit victim = healInfo.GetTarget();
uint addhealth = healInfo.GetHeal(); uint addhealth = healInfo.GetHeal();
if (victim.IsAIEnabled) if (healer)
victim.GetAI().HealReceived(this, addhealth); {
if (victim.IsAIEnabled)
victim.GetAI().HealReceived(healer, addhealth);
if (IsAIEnabled) if (healer.IsAIEnabled)
GetAI().HealDone(victim, addhealth); healer.GetAI().HealDone(victim, addhealth);
}
if (addhealth != 0) if (addhealth != 0)
gain = (int)victim.ModifyHealth(addhealth); gain = (int)victim.ModifyHealth(addhealth);
// Hook for OnHeal Event // Hook for OnHeal Event
uint tempGain = (uint)gain; uint tempGain = (uint)gain;
Global.ScriptMgr.OnHeal(this, victim, ref tempGain); Global.ScriptMgr.OnHeal(healer, victim, ref tempGain);
gain = (int)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()) if (unit)
unit = GetOwner();
Player player = unit.ToPlayer();
if (player != null)
{ {
Battleground bg = player.GetBattleground(); Player bgPlayer = unit.ToPlayer();
if (bg) if (bgPlayer != null)
bg.UpdatePlayerScore(player, ScoreType.HealingDone, (uint)gain); {
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) // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
if (gain != 0) if (gain != 0)
player.UpdateCriteria(CriteriaTypes.HealingDone, (uint)gain, 0, 0, victim); 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.TotalHealingReceived, (uint)gain);
player.UpdateCriteria(CriteriaTypes.HighestHealingReceived, addhealth); player.UpdateCriteria(CriteriaTypes.HighestHealingReceived, addhealth);
@@ -2267,8 +2300,8 @@ namespace Game.Entities
{ {
// calculate heal absorb and reduce healing // calculate heal absorb and reduce healing
CalcHealAbsorb(healInfo); CalcHealAbsorb(healInfo);
DealHeal(healInfo); DealHeal(healInfo);
SendHealSpellLog(healInfo, critical); SendHealSpellLog(healInfo, critical);
return healInfo.GetEffectiveHeal(); return healInfo.GetEffectiveHeal();
} }
@@ -2329,84 +2362,90 @@ namespace Game.Entities
SpellSchoolMask damageSchoolMask = damageInfo.schoolMask; SpellSchoolMask damageSchoolMask = damageInfo.schoolMask;
if (IsDamageReducedByArmor(damageSchoolMask, spellInfo)) // Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage.
damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType); if (!spellInfo.HasAttribute(SpellAttr4.FixedDamage))
bool blocked = false;
// Per-school calc
switch (spellInfo.DmgClass)
{ {
// Melee and Ranged Spells if (IsDamageReducedByArmor(damageSchoolMask, spellInfo))
case SpellDmgClass.Ranged: damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType);
case SpellDmgClass.Melee:
{ bool blocked = false;
// Physical Damage // Per-school calc
if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal)) switch (spellInfo.DmgClass)
{
// Melee and Ranged Spells
case SpellDmgClass.Ranged:
case SpellDmgClass.Melee:
{ {
// Spells with this attribute were already calculated in MeleeSpellHitResult // Physical Damage
if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell)) if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal))
{ {
// Get blocked status // Spells with this attribute were already calculated in MeleeSpellHitResult
blocked = IsSpellBlocked(victim, spellInfo, attackType); if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell))
{
// Get blocked status
blocked = IsSpellBlocked(victim, spellInfo, attackType);
}
} }
}
if (crit) 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)
{ {
damageInfo.blocked = (uint)damage; damageInfo.HitInfo |= HitInfo.CriticalHit;
damageInfo.fullBlock = true;
// 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; // Magical Attacks
ApplyResilience(victim, ref dmg); case SpellDmgClass.None:
damage = (int)dmg; case SpellDmgClass.Magic:
break;
}
// Magical Attacks
case SpellDmgClass.None:
case SpellDmgClass.Magic:
{
// If crit add critical bonus
if (crit)
{ {
damageInfo.HitInfo |= HitInfo.CriticalHit; // If crit add critical bonus
damage = (int)SpellCriticalDamageBonus(spellInfo, (uint)damage, victim); 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; default:
ApplyResilience(victim, ref dmg);
damage = (int)dmg;
break; break;
} }
default:
break;
} }
// Script Hook For CalculateSpellDamageTaken -- Allow scripts to change the Damage post class mitigation calculations // 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(); damageInfo.damage = dmgInfo.GetDamage();
} }
public void DealSpellDamage(SpellNonMeleeDamage damageInfo, bool durabilityLoss) public void DealSpellDamage(SpellNonMeleeDamage damageInfo, bool durabilityLoss)
{ {
if (damageInfo == null) if (damageInfo == null)
@@ -2451,7 +2491,7 @@ namespace Game.Entities
// Call default DealDamage // Call default DealDamage
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); 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) public void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage log)
@@ -4181,6 +4221,7 @@ namespace Game.Entities
else else
aurApp._HandleEffect(effIndex, true); aurApp._HandleEffect(effIndex, true);
} }
// handles effects of aura application // handles effects of aura application
// should be done after registering aura in lists // should be done after registering aura in lists
public void _ApplyAura(AuraApplication aurApp, uint effMask) public void _ApplyAura(AuraApplication aurApp, uint effMask)
@@ -4219,7 +4260,6 @@ namespace Game.Entities
if (aurApp.HasRemoveMode()) if (aurApp.HasRemoveMode())
return; return;
aura.HandleAuraSpecificPeriodics(aurApp, caster);
aura.HandleAuraSpecificMods(aurApp, caster, true, false); aura.HandleAuraSpecificMods(aurApp, caster, true, false);
// apply effects of the aura // apply effects of the aura
@@ -4229,6 +4269,7 @@ namespace Game.Entities
aurApp._HandleEffect(i, true); aurApp._HandleEffect(i, true);
} }
} }
public void _AddAura(UnitAura aura, Unit caster) public void _AddAura(UnitAura aura, Unit caster)
{ {
Cypher.Assert(!m_cleanupDone); 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) 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); 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); 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 // only physical spells damage gets reduced by armor
if ((schoolMask & SpellSchoolMask.Normal) == 0) if ((schoolMask & SpellSchoolMask.Normal) == 0)
return false; return false;
if (spellInfo != null) if (spellInfo != null)
{ {
// there are spells with no specific attribute but they have "ignores armor" in tooltip // there are spells with no specific attribute but they have "ignores armor" in tooltip
@@ -2411,6 +2412,7 @@ namespace Game.Entities
} }
} }
} }
return true; return true;
} }
@@ -2542,7 +2544,7 @@ namespace Game.Entities
void StartReactiveTimer(ReactiveType reactive) { m_reactiveTimer[reactive] = 4000; } 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) if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsInEvadeMode())) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsInEvadeMode()))
@@ -2550,7 +2552,8 @@ namespace Game.Entities
damage = 0; 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) if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks())) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
@@ -2560,156 +2563,22 @@ namespace Game.Entities
return; 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())) 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)
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)
{ {
if (victim.IsAIEnabled) if (victim.IsAIEnabled)
victim.GetAI().DamageTaken(this, ref damage); victim.GetAI().DamageTaken(attacker, ref damage);
if (IsAIEnabled) if (attacker != null && attacker.IsAIEnabled)
GetAI().DamageDealt(victim, ref damage, damagetype); attacker.GetAI().DamageDealt(victim, ref damage, damagetype);
// Hook for OnDamage Event // 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. // Signal to pets that their owner was attacked - except when DOT.
if (damagetype != DamageEffectType.DOT) if (damagetype != DamageEffectType.DOT)
@@ -2719,7 +2588,7 @@ namespace Game.Entities
Creature cControlled = controlled.ToCreature(); Creature cControlled = controlled.ToCreature();
if (cControlled != null) if (cControlled != null)
if (cControlled.IsAIEnabled) 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 (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); Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic);
if (spell != null) if (spell != null)
@@ -2772,35 +2641,36 @@ namespace Game.Entities
uint share = MathFunctions.CalculatePct(damage, aura.GetAmount()); uint share = MathFunctions.CalculatePct(damage, aura.GetAmount());
// @todo check packets if damage is done by victim, or by attacker of victim // @todo check packets if damage is done by victim, or by attacker of victim
DealDamageMods(shareDamageTarget, ref share); DealDamageMods(attacker, shareDamageTarget, ref share);
DealDamage(shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false); DealDamage(attacker, shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false);
} }
} }
// Rage from Damage made (only from direct weapon damage) // 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) if (cleanDamage.attackType == WeaponAttackType.OffAttack)
rage /= 2; rage /= 2;
RewardRage(rage);
attacker.RewardRage(rage);
} }
if (damage == 0) if (damage == 0)
return 0; return 0;
Log.outDebug(LogFilter.Unit, "DealDamageStart");
uint health = (uint)victim.GetHealth(); 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 // duel ends when player has 1 or less hp
bool duel_hasEnded = false; bool duel_hasEnded = false;
bool duel_wasMounted = 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 // 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; damage = health - 1;
duel_hasEnded = true; duel_hasEnded = true;
@@ -2810,8 +2680,11 @@ namespace Game.Entities
Player victimRider = victim.GetCharmer().ToPlayer(); Player victimRider = victim.GetCharmer().ToPlayer();
if (victimRider != null && victimRider.duel != null && victimRider.duel.isMounted) 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 // 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; damage = health - 1;
duel_wasMounted = true; duel_wasMounted = true;
@@ -2819,35 +2692,36 @@ namespace Game.Entities
} }
} }
if (IsTypeId(TypeId.Player) && this != victim) if (attacker != null && attacker != victim)
{ {
Player killer = ToPlayer(); Player killer = attacker.ToPlayer();
if (killer != null)
// in bg, count dmg if victim is also a player
if (victim.IsTypeId(TypeId.Player))
{ {
Battleground bg = killer.GetBattleground(); // in bg, count dmg if victim is also a player
if (bg) if (victim.IsPlayer())
bg.UpdatePlayerScore(killer, ScoreType.DamageDone, damage); {
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); 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.GetTypeId() != TypeId.Player && (!victim.IsControlledByPlayer() || victim.IsVehicle()))
{ {
if (!victim.ToCreature().HasLootRecipient()) 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); victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
} }
@@ -2858,15 +2732,13 @@ namespace Game.Entities
{ {
killed = true; killed = true;
Log.outDebug(LogFilter.Unit, "DealDamage: victim just died"); if (victim.IsPlayer() && victim != attacker)
if (victim.IsTypeId(TypeId.Player) && victim != this)
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, health); victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, health);
if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.Self && victim.HasAuraType(AuraType.SchoolAbsorbOverkill)) if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.Self && victim.HasAuraType(AuraType.SchoolAbsorbOverkill))
{ {
var vAbsorbOverkill = victim.GetAuraEffectsByType(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) foreach (var absorbAurEff in vAbsorbOverkill)
{ {
@@ -2909,7 +2781,7 @@ namespace Game.Entities
if (currentAbsorb != 0) if (currentAbsorb != 0)
{ {
SpellAbsorbLog absorbLog = new(); SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = GetGUID(); absorbLog.Attacker = attacker != null ? attacker.GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = victim.GetGUID(); absorbLog.Victim = victim.GetGUID();
absorbLog.Caster = baseAura.GetCasterGUID(); absorbLog.Caster = baseAura.GetCasterGUID();
absorbLog.AbsorbedSpellID = spellProto != null ? spellProto.Id : 0; absorbLog.AbsorbedSpellID = spellProto != null ? spellProto.Id : 0;
@@ -2917,7 +2789,7 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb; absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage(); absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(victim); absorbLog.LogData.Initialize(victim);
SendCombatLogMessage(absorbLog); victim.SendCombatLogMessage(absorbLog);
} }
} }
@@ -2926,11 +2798,9 @@ namespace Game.Entities
} }
if (killed) if (killed)
Kill(victim, durabilityLoss, skipSettingDeathState); Kill(attacker, victim, durabilityLoss, skipSettingDeathState);
else else
{ {
Log.outDebug(LogFilter.Unit, "DealDamageAlive");
if (victim.IsTypeId(TypeId.Player)) if (victim.IsTypeId(TypeId.Player))
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, damage); 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))) if (damagetype != DamageEffectType.DOT && damage > 0 && !victim.GetOwnerGUID().IsPlayer() && (spellProto == null || !spellProto.HasAura(AuraType.DamageShield)))
victim.ToCreature().SetLastDamagedTime(GameTime.GetGameTime() + SharedConst.MaxAggroResetTime); 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 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) // random durability for items (HIT DONE)
if (RandomHelper.randChance(WorldConfig.GetFloatValue(WorldCfg.RateDurabilityLossDamage))) if (RandomHelper.randChance(WorldConfig.GetFloatValue(WorldCfg.RateDurabilityLossDamage)))
{ {
byte slot = (byte)RandomHelper.IRand(0, EquipmentSlot.End - 1); byte slot = (byte)RandomHelper.IRand(0, EquipmentSlot.End - 1);
ToPlayer().DurabilityPointLossForEquipSlot(slot); attacker.ToPlayer().DurabilityPointLossForEquipSlot(slot);
} }
} }
if (damagetype != DamageEffectType.NoDamage) 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) if (damagetype != DamageEffectType.DOT)
{ {
@@ -3044,11 +2915,147 @@ namespace Game.Entities
} }
} }
Log.outDebug(LogFilter.Unit, "DealDamageEnd returned {0} damage", damage);
return 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) public long ModifyHealth(long dVal)
{ {
long gain = 0; long gain = 0;
@@ -3338,7 +3345,7 @@ namespace Game.Entities
RemovePvpFlag(UnitPVPStateFlags.PvP); 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 // Magic damage, check for resists
if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Magic)) if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Magic))
@@ -3359,7 +3366,7 @@ namespace Game.Entities
return 0; return 0;
} }
float averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo); float averageResist = CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo);
float[] discreteResistProbability = new float[11]; float[] discreteResistProbability = new float[11];
if (averageResist <= 0.1f) if (averageResist <= 0.1f)
@@ -3387,7 +3394,8 @@ namespace Game.Entities
{ {
int ignoredResistance = 0; int ignoredResistance = 0;
ignoredResistance += GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask); if (attacker != null)
ignoredResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask);
ignoredResistance = Math.Min(ignoredResistance, 100); ignoredResistance = Math.Min(ignoredResistance, 100);
MathFunctions.ApplyPct(ref damageResisted, 100 - ignoredResistance); MathFunctions.ApplyPct(ref damageResisted, 100 - ignoredResistance);
@@ -3407,20 +3415,23 @@ namespace Game.Entities
return (uint)damageResisted; 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 if (attacker != null)
// excluding traps
Player player = GetSpellModOwner();
if (player != null && GetEntry() != SharedConst.WorldTrigger)
{ {
victimResistance += (float)player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); // pets inherit 100% of masters penetration
victimResistance -= (float)player.GetSpellPenetrationItemMod(); // 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 // holy resistance exists in pve and comes from level difference, ignore template values
if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy)) if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy))
@@ -3433,12 +3444,12 @@ namespace Game.Entities
victimResistance = Math.Max(victimResistance, 0.0f); victimResistance = Math.Max(victimResistance, 0.0f);
// level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
if (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)) if (attacker != null && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)))
victimResistance += Math.Max(((float)victim.GetLevelForTarget(this) - (float)GetLevelForTarget(victim)) * 5.0f, 0.0f); victimResistance += Math.Max(((float)victim.GetLevelForTarget(attacker) - (float)attacker.GetLevelForTarget(victim)) * 5.0f, 0.0f);
uint bossLevel = 83; uint bossLevel = 83;
float bossResistanceConstant = 510.0f; float bossResistanceConstant = 510.0f;
uint level = victim.GetLevelForTarget(this); uint level = attacker != null ? victim.GetLevelForTarget(attacker) : attacker.GetLevel();
float resistanceConstant; float resistanceConstant;
if (level == bossLevel) if (level == bossLevel)
@@ -3449,7 +3460,7 @@ namespace Game.Entities
return victimResistance / (victimResistance + resistanceConstant); return victimResistance / (victimResistance + resistanceConstant);
} }
public void CalcAbsorbResist(DamageInfo damageInfo) public static void CalcAbsorbResist(DamageInfo damageInfo)
{ {
if (!damageInfo.GetVictim() || !damageInfo.GetVictim().IsAlive() || damageInfo.GetDamage() == 0) if (!damageInfo.GetVictim() || !damageInfo.GetVictim().IsAlive() || damageInfo.GetDamage() == 0)
return; return;
@@ -3458,7 +3469,11 @@ namespace Game.Entities
damageInfo.ResistDamage(resistedDamage); damageInfo.ResistDamage(resistedDamage);
// Ignore Absorption Auras // 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); MathFunctions.RoundToInterval(ref auraAbsorbMod, 0.0f, 100.0f);
@@ -3522,7 +3537,7 @@ namespace Game.Entities
if (currentAbsorb != 0) if (currentAbsorb != 0)
{ {
SpellAbsorbLog absorbLog = new(); SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID(); absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = damageInfo.GetVictim().GetGUID(); absorbLog.Victim = damageInfo.GetVictim().GetGUID();
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID(); absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0; absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
@@ -3530,7 +3545,7 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb; absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage(); absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(damageInfo.GetVictim()); absorbLog.LogData.Initialize(damageInfo.GetVictim());
SendCombatLogMessage(absorbLog); damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
} }
} }
@@ -3596,7 +3611,7 @@ namespace Game.Entities
if (currentAbsorb != 0) if (currentAbsorb != 0)
{ {
SpellAbsorbLog absorbLog = new(); SpellAbsorbLog absorbLog = new();
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID(); absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
absorbLog.Victim = damageInfo.GetVictim().GetGUID(); absorbLog.Victim = damageInfo.GetVictim().GetGUID();
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID(); absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0; absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
@@ -3604,14 +3619,14 @@ namespace Game.Entities
absorbLog.Absorbed = currentAbsorb; absorbLog.Absorbed = currentAbsorb;
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage(); absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
absorbLog.LogData.Initialize(damageInfo.GetVictim()); absorbLog.LogData.Initialize(damageInfo.GetVictim());
SendCombatLogMessage(absorbLog); damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
} }
} }
damageInfo.ModifyDamage(absorbIgnoringDamage); damageInfo.ModifyDamage(absorbIgnoringDamage);
// split damage auras - only when not damaging self // 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 // 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 // Let's copy the list so we can prevent iterator invalidation
@@ -3652,23 +3667,23 @@ namespace Game.Entities
} }
uint split_absorb = 0; 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); 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.damage = splitDamage;
log.originalDamage = splitDamage; log.originalDamage = splitDamage;
log.absorb = split_absorb; log.absorb = split_absorb;
SendSpellNonMeleeDamageLog(log); caster.SendSpellNonMeleeDamageLog(log);
// break 'Fear' and similar auras // 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) if (healInfo.GetHeal() == 0)
return; 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(); float armor = victim.GetArmor();
armor *= victim.GetArmorMultiplierForTarget(attacker); if (attacker != null)
// 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)
{ {
Player modOwner = GetSpellModOwner(); armor *= victim.GetArmorMultiplierForTarget(attacker);
if (modOwner != null)
modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
}
var resIgnoreAuras = GetAuraEffectsByType(AuraType.ModIgnoreTargetResist); // bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
foreach (var eff in resIgnoreAuras) int armorBypassPct = 0;
{ var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
if (eff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal) && eff.IsAffectingSpell(spellInfo)) foreach (var eff in reductionAuras)
armor = (float)Math.Floor(MathFunctions.AddPct(ref armor, -eff.GetAmount())); if (eff.GetCasterGUID() == attacker.GetGUID())
} armorBypassPct += eff.GetAmount();
// Apply Player CR_ARMOR_PENETRATION rating armor = MathFunctions.CalculatePct(armor, 100 - Math.Min(armorBypassPct, 100));
if (IsTypeId(TypeId.Player))
{
float arpPct = ToPlayer().GetRatingBonusValue(CombatRating.ArmorPenetration);
// no more than 100% // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f); armor += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)SpellSchoolMask.Normal);
float maxArmorPen; if (spellInfo != null)
if (victim.GetLevelForTarget(attacker) < 60) {
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker); Player modOwner = attacker.GetSpellModOwner();
else if (modOwner != null)
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59); modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
}
// Cap armor penetration to this number var resIgnoreAuras = attacker.GetAuraEffectsByType(AuraType.ModIgnoreTargetResist);
maxArmorPen = Math.Min((armor + maxArmorPen) / 3.0f, armor); foreach (var eff in resIgnoreAuras)
// Figure out how much armor do we ignore {
armor -= MathFunctions.CalculatePct(maxArmorPen, arpPct); 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)) if (MathFunctions.fuzzyLe(armor, 0.0f))
return damage; return damage;
uint attackerLevel = attacker.GetLevelForTarget(victim); if (attacker != null)
attackerLevel = attacker.GetLevelForTarget(victim);
// Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ? // Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ?
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attacker.GetClass()); float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attacker.GetClass());
if ((armor + armorConstant) == 0) if ((armor + armorConstant) == 0)
+1 -1
View File
@@ -207,7 +207,7 @@ namespace Game
if (opcode == ClientOpcodes.MoveJump) if (opcode == ClientOpcodes.MoveJump)
{ {
plrMover.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2.Jump); // Mind Control plrMover.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2.Jump); // Mind Control
plrMover.ProcSkillsAndAuras(null, ProcFlags.Jump, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null); Unit.ProcSkillsAndAuras(plrMover, null, ProcFlags.Jump, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
} }
} }
} }
+1 -1
View File
@@ -367,7 +367,7 @@ namespace Game.Maps
var playerList = instance.GetPlayers(); var playerList = instance.GetPlayers();
foreach (var player in playerList) foreach (var player in playerList)
if (player.IsAlive()) if (player.IsAlive())
player.ProcSkillsAndAuras(null, ProcFlags.EncounterStart, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null); Unit.ProcSkillsAndAuras(player, null, ProcFlags.EncounterStart, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
break; break;
} }
case EncounterState.Fail: case EncounterState.Fail:
+14 -55
View File
@@ -347,6 +347,19 @@ namespace Game.Spells
} }
} }
public float CalcPeriodicCritChance(Unit caster)
{
if (!caster)
return 0.0f;
Player modOwner = caster.GetSpellModOwner();
if (modOwner == null)
return 0.0f;
float critChance = modOwner.SpellCritChanceDone(GetSpellInfo(), GetSpellInfo().GetSchoolMask(), GetSpellInfo().GetAttackType());
return Math.Max(0.0f, critChance);
}
public Unit GetCaster() public Unit GetCaster()
{ {
if (m_owner.GetGUID() == m_casterGuid) if (m_owner.GetGUID() == m_casterGuid)
@@ -854,13 +867,8 @@ namespace Game.Spells
effect.ChangeAmount(effect.CalculateAmount(caster), false, true); effect.ChangeAmount(effect.CalculateAmount(caster), false, true);
foreach (var app in applications) foreach (var app in applications)
{
if (!app.HasRemoveMode()) if (!app.HasRemoveMode())
{
HandleAuraSpecificPeriodics(app, caster);
HandleAuraSpecificMods(app, caster, true, true); HandleAuraSpecificMods(app, caster, true, true);
}
}
SetNeedClientUpdateForTargets(); SetNeedClientUpdateForTargets();
} }
@@ -1406,55 +1414,6 @@ namespace Game.Spells
} }
} }
public void HandleAuraSpecificPeriodics(AuraApplication aurApp, Unit caster)
{
Unit target = aurApp.GetTarget();
if (!caster || aurApp.HasRemoveMode())
return;
foreach (AuraEffect effect in GetAuraEffects())
{
if (effect == null || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura))
continue;
switch (effect.GetSpellEffectInfo().ApplyAuraName)
{
case AuraType.PeriodicDamage:
case AuraType.PeriodicDamagePercent:
case AuraType.PeriodicLeech:
{
// ignore non positive values (can be result apply spellmods to aura damage
uint damage = (uint)Math.Max(effect.GetAmount(), 0);
// Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
effect.SetDonePct(caster.SpellDamagePctDone(target, m_spellInfo, DamageEffectType.DOT)); // Calculate done percentage first!
effect.SetDamage((int)(caster.SpellDamageBonusDone(target, m_spellInfo, damage, DamageEffectType.DOT, effect.GetSpellEffectInfo(), GetStackAmount()) * effect.GetDonePct()));
effect.SetCritChance(caster.GetUnitSpellCriticalChance(target, null, effect, m_spellInfo.GetSchoolMask()));
break;
}
case AuraType.PeriodicHeal:
case AuraType.ObsModHealth:
{
// ignore non positive values (can be result apply spellmods to aura damage
uint damage = (uint)Math.Max(effect.GetAmount(), 0);
// Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
effect.SetDonePct(caster.SpellHealingPctDone(target, m_spellInfo)); // Calculate done percentage first!
effect.SetDamage((int)(caster.SpellHealingBonusDone(target, m_spellInfo, damage, DamageEffectType.DOT, effect.GetSpellEffectInfo(), GetStackAmount()) * effect.GetDonePct()));
effect.SetCritChance(caster.GetUnitSpellCriticalChance(target, null, effect, m_spellInfo.GetSchoolMask()));
break;
}
default:
break;
}
}
}
bool CanBeAppliedOn(Unit target) bool CanBeAppliedOn(Unit target)
{ {
// unit not in world or during remove from world // unit not in world or during remove from world
@@ -1842,7 +1801,7 @@ namespace Game.Spells
public float CalcPPMProcChance(Unit actor) public float CalcPPMProcChance(Unit actor)
{ {
// Formula see http://us.battle.net/wow/en/forum/topic/8197741003#1 // Formula see http://us.battle.net/wow/en/forum/topic/8197741003#1
float ppm = m_spellInfo.CalcProcPPM(actor, m_castItemLevel); float ppm = m_spellInfo.CalcProcPPM(actor, GetCastItemLevel());
float averageProcInterval = 60.0f / ppm; float averageProcInterval = 60.0f / ppm;
var currentTime = GameTime.GetGameTimeSteadyPoint(); var currentTime = GameTime.GetGameTimeSteadyPoint();
+120 -150
View File
@@ -39,12 +39,11 @@ namespace Game.Spells
m_spellInfo = baseAura.GetSpellInfo(); m_spellInfo = baseAura.GetSpellInfo();
_effectInfo = spellEfffectInfo; _effectInfo = spellEfffectInfo;
m_baseAmount = baseAmount.HasValue ? baseAmount.Value : _effectInfo.CalcBaseValue(caster, baseAura.GetAuraType() == AuraObjectType.Unit ? baseAura.GetOwner().ToUnit() : null, baseAura.GetCastItemId(), baseAura.GetCastItemLevel()); m_baseAmount = baseAmount.HasValue ? baseAmount.Value : _effectInfo.CalcBaseValue(caster, baseAura.GetAuraType() == AuraObjectType.Unit ? baseAura.GetOwner().ToUnit() : null, baseAura.GetCastItemId(), baseAura.GetCastItemLevel());
m_donePct = 1.0f;
m_canBeRecalculated = true; m_canBeRecalculated = true;
m_isPeriodic = false; m_isPeriodic = false;
CalculatePeriodic(caster, true, false); CalculatePeriodic(caster, true, false);
m_amount = CalculateAmount(caster); _amount = CalculateAmount(caster);
CalculateSpellMod(); CalculateSpellMod();
} }
@@ -267,7 +266,7 @@ namespace Game.Spells
if (Convert.ToBoolean(handleMask & AuraEffectHandleModes.ChangeAmount)) if (Convert.ToBoolean(handleMask & AuraEffectHandleModes.ChangeAmount))
{ {
if (!mark) if (!mark)
m_amount = newAmount; _amount = newAmount;
else else
SetAmount(newAmount); SetAmount(newAmount);
CalculateSpellMod(); CalculateSpellMod();
@@ -427,6 +426,11 @@ namespace Game.Spells
} }
} }
public float GetCritChanceFor(Unit caster, Unit target)
{
return target.SpellCritChanceTaken(caster, null, this, GetSpellInfo().GetSchoolMask(), GetBase().CalcPeriodicCritChance(caster), GetSpellInfo().GetAttackType());
}
public bool IsAffectingSpell(SpellInfo spell) public bool IsAffectingSpell(SpellInfo spell)
{ {
if (spell == null) if (spell == null)
@@ -766,9 +770,9 @@ namespace Game.Spells
public int GetMiscValueB() { return GetSpellEffectInfo().MiscValueB; } public int GetMiscValueB() { return GetSpellEffectInfo().MiscValueB; }
public int GetMiscValue() { return GetSpellEffectInfo().MiscValue; } public int GetMiscValue() { return GetSpellEffectInfo().MiscValue; }
public AuraType GetAuraType() { return GetSpellEffectInfo().ApplyAuraName; } public AuraType GetAuraType() { return GetSpellEffectInfo().ApplyAuraName; }
public int GetAmount() { return m_amount; } public int GetAmount() { return _amount; }
public bool HasAmount() { return m_amount != 0; } public bool HasAmount() { return _amount != 0; }
public void SetAmount(int _amount) { m_amount = _amount; m_canBeRecalculated = false; } public void SetAmount(int amount) { _amount = amount; m_canBeRecalculated = false; }
public int GetPeriodicTimer() { return _periodicTimer; } public int GetPeriodicTimer() { return _periodicTimer; }
public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; } public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; }
@@ -791,13 +795,6 @@ namespace Game.Spells
public bool CanBeRecalculated() { return m_canBeRecalculated; } public bool CanBeRecalculated() { return m_canBeRecalculated; }
public void SetCanBeRecalculated(bool val) { m_canBeRecalculated = val; } public void SetCanBeRecalculated(bool val) { m_canBeRecalculated = val; }
public void SetDamage(int val) { m_damage = val; }
public int GetDamage() { return m_damage; }
public void SetCritChance(float val) { m_critChance = val; }
public float GetCritChance() { return m_critChance; }
public void SetDonePct(float val) { m_donePct = val; }
public float GetDonePct() { return m_donePct; }
public void ResetTicks() { _ticksDone = 0; } public void ResetTicks() { _ticksDone = 0; }
public uint GetTickNumber() { return _ticksDone; } public uint GetTickNumber() { return _ticksDone; }
public uint GetRemainingTicks() { return GetTotalTicks() - _ticksDone; } public uint GetRemainingTicks() { return GetTotalTicks() - _ticksDone; }
@@ -822,10 +819,7 @@ namespace Game.Spells
SpellModifier m_spellmod; SpellModifier m_spellmod;
public int m_baseAmount; public int m_baseAmount;
int m_amount; int _amount;
int m_damage;
float m_critChance;
float m_donePct;
// periodic stuff // periodic stuff
int _periodicTimer; int _periodicTimer;
@@ -2518,7 +2512,7 @@ namespace Game.Spells
// so this break such spells or most of them. // so this break such spells or most of them.
// Current formula about m_amount: effect base points + dieside - 1 // Current formula about m_amount: effect base points + dieside - 1
// TO DO: Reasearch more about 0/0 and fix it. // TO DO: Reasearch more about 0/0 and fix it.
caster._EnterVehicle(target.GetVehicleKit(), (sbyte)(m_amount - 1), aurApp); caster._EnterVehicle(target.GetVehicleKit(), (sbyte)(GetAmount() - 1), aurApp);
} }
else else
{ {
@@ -2527,7 +2521,7 @@ namespace Game.Spells
if (GetId() == 53111) // Devour Humanoid if (GetId() == 53111) // Devour Humanoid
{ {
target.Kill(caster); Unit.Kill(target, caster);
if (caster.IsTypeId(TypeId.Unit)) if (caster.IsTypeId(TypeId.Unit))
caster.ToCreature().DespawnOrUnsummon(); caster.ToCreature().DespawnOrUnsummon();
} }
@@ -2985,7 +2979,7 @@ namespace Game.Spells
if (GetMiscValue() == i || GetMiscValue() == -1) if (GetMiscValue() == i || GetMiscValue() == -1)
{ {
if (apply) if (apply)
target.ApplyStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, m_amount); target.ApplyStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, GetAmount());
else else
{ {
float amount = target.GetTotalAuraMultiplier(AuraType.ModPercentStat, aurEff => float amount = target.GetTotalAuraMultiplier(AuraType.ModPercentStat, aurEff =>
@@ -3154,7 +3148,7 @@ namespace Game.Spells
{ {
if (GetMiscValue() == (int)stat || GetMiscValue() == -1) if (GetMiscValue() == (int)stat || GetMiscValue() == -1)
{ {
target.HandleStatFlatModifier(UnitMods.StatStart + (int)stat, UnitModifierFlatType.BasePCTExcludeCreate, m_amount, apply); target.HandleStatFlatModifier(UnitMods.StatStart + (int)stat, UnitModifierFlatType.BasePCTExcludeCreate, GetAmount(), apply);
target.UpdateStatBuffMod(stat); target.UpdateStatBuffMod(stat);
} }
} }
@@ -3170,7 +3164,7 @@ namespace Game.Spells
if (!target) if (!target)
return; return;
target.ApplyModOverrideSpellPowerByAPPercent(m_amount, apply); target.ApplyModOverrideSpellPowerByAPPercent(GetAmount(), apply);
target.UpdateSpellDamageAndHealingBonus(); target.UpdateSpellDamageAndHealingBonus();
} }
@@ -3184,7 +3178,7 @@ namespace Game.Spells
if (!target) if (!target)
return; return;
target.ApplyModOverrideAPBySpellPowerPercent(m_amount, apply); target.ApplyModOverrideAPBySpellPowerPercent(GetAmount(), apply);
target.UpdateAttackPowerAndDamage(); target.UpdateAttackPowerAndDamage();
target.UpdateAttackPowerAndDamage(true); target.UpdateAttackPowerAndDamage(true);
} }
@@ -3730,7 +3724,7 @@ namespace Game.Spells
target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, spellGroupVal, !apply); target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, spellGroupVal, !apply);
} }
target.ApplyCastTimePercentMod(m_amount, apply); target.ApplyCastTimePercentMod(GetAmount(), apply);
target.ApplyAttackTimePercentMod(WeaponAttackType.BaseAttack, GetAmount(), apply); target.ApplyAttackTimePercentMod(WeaponAttackType.BaseAttack, GetAmount(), apply);
target.ApplyAttackTimePercentMod(WeaponAttackType.OffAttack, GetAmount(), apply); target.ApplyAttackTimePercentMod(WeaponAttackType.OffAttack, GetAmount(), apply);
target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, GetAmount(), apply); target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, GetAmount(), apply);
@@ -4443,7 +4437,7 @@ namespace Game.Spells
//Adding items //Adding items
uint noSpaceForCount; uint noSpaceForCount;
uint count = (uint)m_amount; uint count = (uint)GetAmount();
List<ItemPosCount> dest = new(); List<ItemPosCount> dest = new();
InventoryResult msg = plCaster.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, GetSpellEffectInfo().ItemType, count, out noSpaceForCount); InventoryResult msg = plCaster.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, GetSpellEffectInfo().ItemType, count, out noSpaceForCount);
@@ -4493,7 +4487,7 @@ namespace Game.Spells
return; return;
uint factionId = (uint)GetMiscValue(); uint factionId = (uint)GetMiscValue();
ReputationRank factionRank = (ReputationRank)m_amount; ReputationRank factionRank = (ReputationRank)GetAmount();
player.GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply); player.GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply);
player.GetReputationMgr().SendForceReactions(); player.GetReputationMgr().SendForceReactions();
@@ -4880,7 +4874,10 @@ namespace Game.Spells
void HandlePeriodicDamageAurasTick(Unit target, Unit caster) void HandlePeriodicDamageAurasTick(Unit target, Unit caster)
{ {
if (!caster || !target.IsAlive()) // dynobj auras must always have a caster
Cypher.Assert(GetSpellEffectInfo().Effect != SpellEffectName.PersistentAreaAura || caster != null);
if (!target.IsAlive())
return; return;
if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo())) if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo()))
@@ -4896,38 +4893,28 @@ namespace Game.Spells
CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
// AOE spells are not affected by the new periodic system.
bool isAreaAura = GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura);
// ignore non positive values (can be result apply spellmods to aura damage // ignore non positive values (can be result apply spellmods to aura damage
uint damage = (uint)(isAreaAura ? Math.Max(GetAmount(), 0) : m_damage); uint damage = (uint)Math.Max(GetAmount(), 0);
// Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations // Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations
if (isAreaAura) Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
switch (GetAuraType()) switch (GetAuraType())
{ {
case AuraType.PeriodicDamage: case AuraType.PeriodicDamage:
{ {
if (isAreaAura) if (caster != null)
damage = (uint)(caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()) * caster.SpellDamagePctDone(target, m_spellInfo, DamageEffectType.DOT)); damage = (uint)caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
// Calculate armor mitigation
if (caster.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex()))
{
uint damageReductedArmor = caster.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo());
cleanDamage.mitigated_damage += damage - damageReductedArmor;
damage = damageReductedArmor;
}
// There is a Chance to make a Soul Shard when Drain soul does damage // There is a Chance to make a Soul Shard when Drain soul does damage
if (GetSpellInfo().SpellFamilyName == SpellFamilyNames.Warlock && GetSpellInfo().SpellFamilyFlags[0].HasAnyFlag(0x00004000u)) if (caster != null && GetSpellInfo().SpellFamilyName == SpellFamilyNames.Warlock && GetSpellInfo().SpellFamilyFlags[0].HasAnyFlag(0x00004000u))
{ {
if (caster.IsTypeId(TypeId.Player) && caster.ToPlayer().IsHonorOrXPTarget(target)) if (caster.IsTypeId(TypeId.Player) && caster.ToPlayer().IsHonorOrXPTarget(target))
caster.CastSpell(caster, 95810, new CastSpellExtraArgs(this)); caster.CastSpell(caster, 95810, new CastSpellExtraArgs(this));
} }
else if (GetSpellInfo().SpellFamilyName == SpellFamilyNames.Generic)
if (GetSpellInfo().SpellFamilyName == SpellFamilyNames.Generic)
{ {
switch (GetId()) switch (GetId())
{ {
@@ -4947,48 +4934,54 @@ namespace Game.Spells
{ {
WeaponAttackType attackType = GetSpellInfo().GetAttackType(); WeaponAttackType attackType = GetSpellInfo().GetAttackType();
uint weaponDamage = MathFunctions.CalculatePct(caster.CalculateDamage(attackType, false, true), GetAmount()); damage = MathFunctions.CalculatePct(caster.CalculateDamage(attackType, false, true), GetAmount());
// Add melee damage bonuses (also check for negative) // Add melee damage bonuses (also check for negative)
uint damageBonusDone = caster.MeleeDamageBonusDone(target, Math.Max(weaponDamage, 0), attackType, DamageEffectType.DOT, GetSpellInfo()); if (caster != null)
damage = caster.MeleeDamageBonusDone(target, damage, attackType, DamageEffectType.DOT, GetSpellInfo());
damage = target.MeleeDamageBonusTaken(caster, damageBonusDone, attackType, DamageEffectType.DOT, GetSpellInfo()); damage = target.MeleeDamageBonusTaken(caster, damage, attackType, DamageEffectType.DOT, GetSpellInfo());
break; break;
} }
case AuraType.PeriodicDamagePercent: case AuraType.PeriodicDamagePercent:
// ceil obtained value, it may happen that 10 ticks for 10% damage may not kill owner // ceil obtained value, it may happen that 10 ticks for 10% damage may not kill owner
damage = (uint)Math.Ceiling(MathFunctions.CalculatePct((float)target.GetMaxHealth(), (float)damage)); damage = (uint)Math.Ceiling(MathFunctions.CalculatePct((float)target.GetMaxHealth(), (float)damage));
damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
break; break;
default: default:
break; break;
} }
if (!m_spellInfo.HasAttribute(SpellAttr4.FixedDamage)) bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target));
if (crit)
damage = Unit.SpellCriticalDamageBonus(caster, m_spellInfo, damage, target);
// Calculate armor mitigation
if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex()))
{ {
if (GetSpellEffectInfo().IsTargetingArea() || isAreaAura) uint damageReductedArmor = Unit.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetAttackType(), GetBase().GetCasterLevel());
{ cleanDamage.mitigated_damage += damage - damageReductedArmor;
damage = (uint)(damage * target.GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask)); damage = damageReductedArmor;
if (!caster.IsTypeId(TypeId.Player))
damage = (uint)(damage * target.GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
}
} }
bool crit = RandomHelper.randChance(isAreaAura ? caster.GetUnitSpellCriticalChance(target, null, this, m_spellInfo.GetSchoolMask()) : m_critChance);
if (crit)
damage = caster.SpellCriticalDamageBonus(m_spellInfo, damage, target);
uint dmg = damage;
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage)) if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage))
caster.ApplyResilience(target, ref dmg); {
damage = dmg; if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura)
damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, caster);
}
int dmg = (int)damage;
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage) && caster != null && caster.CanApplyResilience())
Unit.ApplyResilience(target, ref dmg);
damage = (uint)dmg;
DamageInfo damageInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack); DamageInfo damageInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack);
caster.CalcAbsorbResist(damageInfo); Unit.CalcAbsorbResist(damageInfo);
damage = damageInfo.GetDamage(); damage = damageInfo.GetDamage();
uint absorb = damageInfo.GetAbsorb(); uint absorb = damageInfo.GetAbsorb();
uint resist = damageInfo.GetResist(); uint resist = damageInfo.GetResist();
caster.DealDamageMods(target, ref damage, ref absorb); Unit.DealDamageMods(caster, target, ref damage, ref absorb);
// Set trigger flag // Set trigger flag
ProcFlags procAttacker = ProcFlags.DonePeriodic; ProcFlags procAttacker = ProcFlags.DonePeriodic;
@@ -5005,17 +4998,20 @@ namespace Game.Spells
if (overkill < 0) if (overkill < 0)
overkill = 0; overkill = 0;
SpellPeriodicAuraLogInfo pInfo = new(this, damage, dmg, (uint)overkill, absorb, resist, 0.0f, crit); SpellPeriodicAuraLogInfo pInfo = new(this, damage, (uint)dmg, (uint)overkill, absorb, resist, 0.0f, crit);
caster.DealDamage(target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), true); Unit.DealDamage(caster, target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), true);
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null); Unit.ProcSkillsAndAuras(caster, target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
target.SendPeriodicAuraLog(pInfo); target.SendPeriodicAuraLog(pInfo);
} }
void HandlePeriodicHealthLeechAuraTick(Unit target, Unit caster) void HandlePeriodicHealthLeechAuraTick(Unit target, Unit caster)
{ {
if (!caster || !target.IsAlive()) // dynobj auras must always have a caster
Cypher.Assert(GetSpellEffectInfo().Effect != SpellEffectName.PersistentAreaAura || caster != null);
if (!target.IsAlive())
return; return;
if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo())) if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo()))
@@ -5030,47 +5026,40 @@ namespace Game.Spells
CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
bool isAreaAura = GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura);
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
uint damage = (uint)(isAreaAura ? Math.Max(GetAmount(), 0) : m_damage); uint damage = (uint)Math.Max(GetAmount(), 0);
if (caster)
damage = caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
if (isAreaAura)
{
// Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
damage = (uint)(caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()) * caster.SpellDamagePctDone(target, m_spellInfo, DamageEffectType.DOT));
}
damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target));
if (crit)
damage = Unit.SpellCriticalDamageBonus(caster, m_spellInfo, damage, target);
// Calculate armor mitigation // Calculate armor mitigation
if (caster.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex())) if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex()))
{ {
uint damageReductedArmor = caster.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo()); uint damageReductedArmor = Unit.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetAttackType(), GetBase().GetCasterLevel());
cleanDamage.mitigated_damage += damage - damageReductedArmor; cleanDamage.mitigated_damage += damage - damageReductedArmor;
damage = damageReductedArmor; damage = damageReductedArmor;
} }
if (!m_spellInfo.HasAttribute(SpellAttr4.FixedDamage)) if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage))
{ {
if (GetSpellEffectInfo().IsTargetingArea() || isAreaAura) if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura)
{ damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, caster);
damage = (uint)(damage * target.GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
if (!caster.IsTypeId(TypeId.Player))
damage = (uint)(damage * target.GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
}
} }
bool crit = RandomHelper.randChance(isAreaAura ? caster.GetUnitSpellCriticalChance(target, null, this, m_spellInfo.GetSchoolMask()) : m_critChance); int dmg = (int)damage;
if (crit) if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage) && caster != null && caster.CanApplyResilience())
damage = caster.SpellCriticalDamageBonus(m_spellInfo, damage, target); Unit.ApplyResilience(target, ref dmg);
uint dmg = damage; damage = (uint)dmg;
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage))
caster.ApplyResilience(target, ref dmg);
damage = dmg;
DamageInfo damageInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack); DamageInfo damageInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack);
caster.CalcAbsorbResist(damageInfo); Unit.CalcAbsorbResist(damageInfo);
uint absorb = damageInfo.GetAbsorb(); uint absorb = damageInfo.GetAbsorb();
uint resist = damageInfo.GetResist(); uint resist = damageInfo.GetResist();
@@ -5078,7 +5067,7 @@ namespace Game.Spells
// SendSpellNonMeleeDamageLog expects non-absorbed/non-resisted damage // SendSpellNonMeleeDamageLog expects non-absorbed/non-resisted damage
SpellNonMeleeDamage log = new(caster, target, GetSpellInfo(), GetBase().GetSpellVisual(), GetSpellInfo().GetSchoolMask(), GetBase().GetCastGUID()); SpellNonMeleeDamage log = new(caster, target, GetSpellInfo(), GetBase().GetSpellVisual(), GetSpellInfo().GetSchoolMask(), GetBase().GetCastGUID());
log.damage = damage; log.damage = damage;
log.originalDamage = dmg; log.originalDamage = (uint)dmg;
log.absorb = absorb; log.absorb = absorb;
log.resist = resist; log.resist = resist;
log.periodicLog = true; log.periodicLog = true;
@@ -5096,22 +5085,22 @@ namespace Game.Spells
procVictim |= ProcFlags.TakenDamage; procVictim |= ProcFlags.TakenDamage;
} }
int new_damage = (int)caster.DealDamage(target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), false); int new_damage = (int)Unit.DealDamage(caster, target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), false);
if (caster.IsAlive()) if (!caster || !caster.IsAlive())
{ return;
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster); Unit.ProcSkillsAndAuras(caster, target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
uint heal = (caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount())); float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster);
heal = (caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
HealInfo healInfo = new(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask()); uint heal = (caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
caster.HealBySpell(healInfo); heal = caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
caster.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); HealInfo healInfo = new(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.ProcSkillsAndAuras(caster, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo); caster.HealBySpell(healInfo);
}
caster.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
Unit.ProcSkillsAndAuras(caster, caster, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
caster.SendSpellNonMeleeDamageLog(log); caster.SendSpellNonMeleeDamageLog(log);
} }
@@ -5143,12 +5132,15 @@ namespace Game.Spells
HealInfo healInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask()); HealInfo healInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.HealBySpell(healInfo); caster.HealBySpell(healInfo);
caster.ProcSkillsAndAuras(target, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, ProcFlagsHit.Normal, null, null, healInfo); Unit.ProcSkillsAndAuras(caster, target, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, ProcFlagsHit.Normal, null, null, healInfo);
} }
void HandlePeriodicHealAurasTick(Unit target, Unit caster) void HandlePeriodicHealAurasTick(Unit target, Unit caster)
{ {
if (!caster || !target.IsAlive()) // dynobj auras must always have a caster
Cypher.Assert(GetSpellEffectInfo().Effect != SpellEffectName.PersistentAreaAura || caster != null);
if (!target.IsAlive())
return; return;
if (target.HasUnitState(UnitState.Isolated)) if (target.HasUnitState(UnitState.Isolated))
@@ -5158,51 +5150,28 @@ namespace Game.Spells
} }
// heal for caster damage (must be alive) // heal for caster damage (must be alive)
if (target != caster && GetSpellInfo().HasAttribute(SpellAttr2.HealthFunnel) && !caster.IsAlive()) if (target != caster && GetSpellInfo().HasAttribute(SpellAttr2.HealthFunnel) && (caster == null || !caster.IsAlive()))
return; return;
// don't regen when permanent aura target has full power // don't regen when permanent aura target has full power
if (GetBase().IsPermanent() && target.IsFullHealth()) if (GetBase().IsPermanent() && target.IsFullHealth())
return; return;
bool isAreaAura = GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura);
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
int damage = isAreaAura ? Math.Max(GetAmount(), 0) : m_damage; uint damage = (uint)Math.Max(GetAmount(), 0);
if (GetAuraType() == AuraType.ObsModHealth) if (GetAuraType() == AuraType.ObsModHealth)
{ {
// Taken mods damage = (uint)target.CountPctFromMaxHealth((int)damage);
float TakenTotalMod = 1.0f;
// Tenacity increase healing % taken
AuraEffect Tenacity = target.GetAuraEffect(58549, 0);
if (Tenacity != null)
MathFunctions.AddPct(ref TakenTotalMod, Tenacity.GetAmount());
// Healing taken percent
float minval = target.GetMaxNegativeAuraModifier(AuraType.ModHealingPct);
if (minval != 0)
MathFunctions.AddPct(ref TakenTotalMod, minval);
float maxval = target.GetMaxPositiveAuraModifier(AuraType.ModHealingPct);
if (maxval != 0)
MathFunctions.AddPct(ref TakenTotalMod, maxval);
TakenTotalMod = Math.Max(TakenTotalMod, 0.0f);
damage = (int)target.CountPctFromMaxHealth(damage);
damage = (int)(damage * TakenTotalMod);
}
else
{
if (isAreaAura)
damage = (int)(caster.SpellHealingBonusDone(target, GetSpellInfo(), (uint)damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()) * caster.SpellHealingPctDone(target, m_spellInfo));
damage = (int)target.SpellHealingBonusTaken(caster, GetSpellInfo(), (uint)damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
} }
else if (caster != null)
damage = caster.SpellHealingBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
bool crit = RandomHelper.randChance(isAreaAura ? caster.GetUnitSpellCriticalChance(target, null, this, m_spellInfo.GetSchoolMask()) : m_critChance); damage = target.SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target));
if (crit) if (crit)
damage = caster.SpellCriticalHealingBonus(m_spellInfo, damage, target); damage = (uint)Unit.SpellCriticalHealingBonus(caster, m_spellInfo, (int)damage, target);
Log.outDebug(LogFilter.Spells, "PeriodicTick: {0} (TypeId: {1}) heal of {2} (TypeId: {3}) for {4} health inflicted by {5}", Log.outDebug(LogFilter.Spells, "PeriodicTick: {0} (TypeId: {1}) heal of {2} (TypeId: {3}) for {4} health inflicted by {5}",
GetCasterGUID().ToString(), GetCaster().GetTypeId(), target.GetGUID().ToString(), target.GetTypeId(), damage, GetId()); GetCasterGUID().ToString(), GetCaster().GetTypeId(), target.GetGUID().ToString(), target.GetTypeId(), damage, GetId());
@@ -5210,13 +5179,14 @@ namespace Game.Spells
uint heal = (uint)damage; uint heal = (uint)damage;
HealInfo healInfo = new(caster, target, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask()); HealInfo healInfo = new(caster, target, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.CalcHealAbsorb(healInfo); Unit.CalcHealAbsorb(healInfo);
caster.DealHeal(healInfo); Unit.DealHeal(healInfo);
SpellPeriodicAuraLogInfo pInfo = new(this, heal, (uint)damage, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit); SpellPeriodicAuraLogInfo pInfo = new(this, heal, (uint)damage, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
target.SendPeriodicAuraLog(pInfo); target.SendPeriodicAuraLog(pInfo);
target.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); if (caster != null)
target.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
// %-based heal - does not proc auras // %-based heal - does not proc auras
if (GetAuraType() == AuraType.ObsModHealth) if (GetAuraType() == AuraType.ObsModHealth)
@@ -5227,7 +5197,7 @@ namespace Game.Spells
ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal; ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal;
// ignore item heals // ignore item heals
if (GetBase().GetCastItemGUID().IsEmpty()) if (GetBase().GetCastItemGUID().IsEmpty())
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo); Unit.ProcSkillsAndAuras(caster, target, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
} }
void HandlePeriodicManaLeechAuraTick(Unit target, Unit caster) void HandlePeriodicManaLeechAuraTick(Unit target, Unit caster)
@@ -5248,7 +5218,7 @@ namespace Game.Spells
return; return;
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
int drainAmount = Math.Max(m_amount, 0); int drainAmount = Math.Max(GetAmount(), 0);
int drainedAmount = -target.ModifyPower(powerType, -drainAmount); int drainedAmount = -target.ModifyPower(powerType, -drainAmount);
float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster); float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster);
@@ -5307,7 +5277,7 @@ namespace Game.Spells
return; return;
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
int amount = Math.Max(m_amount, 0) * target.GetMaxPower(powerType) / 100; int amount = Math.Max(GetAmount(), 0) * target.GetMaxPower(powerType) / 100;
SpellPeriodicAuraLogInfo pInfo = new(this, (uint)amount, (uint)amount, 0, 0, 0, 0.0f, false); SpellPeriodicAuraLogInfo pInfo = new(this, (uint)amount, (uint)amount, 0, 0, 0, 0.0f, false);
@@ -5336,7 +5306,7 @@ namespace Game.Spells
return; return;
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
int amount = Math.Max(m_amount, 0); int amount = Math.Max(GetAmount(), 0);
SpellPeriodicAuraLogInfo pInfo = new(this, (uint)amount, (uint)amount, 0, 0, 0, 0.0f, false); SpellPeriodicAuraLogInfo pInfo = new(this, (uint)amount, (uint)amount, 0, 0, 0, 0.0f, false);
int gain = target.ModifyPower(powerType, amount); int gain = target.ModifyPower(powerType, amount);
@@ -5361,7 +5331,7 @@ namespace Game.Spells
} }
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
int damage = Math.Max(m_amount, 0); int damage = Math.Max(GetAmount(), 0);
uint gain = (uint)(-target.ModifyPower(powerType, -damage)); uint gain = (uint)(-target.ModifyPower(powerType, -damage));
@@ -5373,7 +5343,7 @@ namespace Game.Spells
// no SpellDamageBonus for burn mana // no SpellDamageBonus for burn mana
caster.CalculateSpellDamageTaken(damageInfo, (int)(gain * dmgMultiplier), spellProto); caster.CalculateSpellDamageTaken(damageInfo, (int)(gain * dmgMultiplier), spellProto);
caster.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
// Set trigger flag // Set trigger flag
ProcFlags procAttacker = ProcFlags.DonePeriodic; ProcFlags procAttacker = ProcFlags.DonePeriodic;
@@ -5389,7 +5359,7 @@ namespace Game.Spells
caster.DealSpellDamage(damageInfo, true); caster.DealSpellDamage(damageInfo, true);
DamageInfo dotDamageInfo = new(damageInfo, DamageEffectType.DOT, WeaponAttackType.BaseAttack, hitMask); DamageInfo dotDamageInfo = new(damageInfo, DamageEffectType.DOT, WeaponAttackType.BaseAttack, hitMask);
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, ProcFlagsSpellPhase.None, hitMask, null, dotDamageInfo, null); Unit.ProcSkillsAndAuras(caster, target, procAttacker, procVictim, spellTypeMask, ProcFlagsSpellPhase.None, hitMask, null, dotDamageInfo, null);
caster.SendSpellNonMeleeDamageLog(damageInfo); caster.SendSpellNonMeleeDamageLog(damageInfo);
} }
@@ -5452,7 +5422,7 @@ namespace Game.Spells
int damage = (int)target.SpellDamageBonusDone(triggerTarget, GetSpellInfo(), (uint)GetAmount(), DamageEffectType.SpellDirect, GetSpellEffectInfo()); int damage = (int)target.SpellDamageBonusDone(triggerTarget, GetSpellInfo(), (uint)GetAmount(), DamageEffectType.SpellDirect, GetSpellEffectInfo());
damage = (int)triggerTarget.SpellDamageBonusTaken(target, GetSpellInfo(), (uint)damage, DamageEffectType.SpellDirect, GetSpellEffectInfo()); damage = (int)triggerTarget.SpellDamageBonusTaken(target, GetSpellInfo(), (uint)damage, DamageEffectType.SpellDirect, GetSpellEffectInfo());
target.CalculateSpellDamageTaken(damageInfo, damage, GetSpellInfo()); target.CalculateSpellDamageTaken(damageInfo, damage, GetSpellInfo());
target.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
target.DealSpellDamage(damageInfo, true); target.DealSpellDamage(damageInfo, true);
target.SendSpellNonMeleeDamageLog(damageInfo); target.SendSpellNonMeleeDamageLog(damageInfo);
} }
+12 -13
View File
@@ -1947,7 +1947,7 @@ namespace Game.Spells
if (crit) if (crit)
{ {
hitMask |= ProcFlagsHit.Critical; hitMask |= ProcFlagsHit.Critical;
addhealth = (uint)caster.SpellCriticalHealingBonus(m_spellInfo, (int)addhealth, null); addhealth = (uint)Unit.SpellCriticalHealingBonus(caster, m_spellInfo, (int)addhealth, null);
} }
else else
hitMask |= ProcFlagsHit.Normal; hitMask |= ProcFlagsHit.Normal;
@@ -1959,7 +1959,7 @@ namespace Game.Spells
// Do triggers for unit // Do triggers for unit
if (canEffectTrigger) if (canEffectTrigger)
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.Hit, hitMask, this, null, healInfo); Unit.ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.Hit, hitMask, this, null, healInfo);
} }
// Do damage and triggers // Do damage and triggers
else if (m_damage > 0) else if (m_damage > 0)
@@ -1978,7 +1978,7 @@ namespace Game.Spells
{ {
// Add bonuses and fill damageInfo struct // Add bonuses and fill damageInfo struct
caster.CalculateSpellDamageTaken(damageInfo, m_damage, m_spellInfo, m_attackType, target.crit); caster.CalculateSpellDamageTaken(damageInfo, m_damage, m_spellInfo, m_attackType, target.crit);
caster.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
hitMask |= Unit.CreateProcHitMask(damageInfo, missInfo); hitMask |= Unit.CreateProcHitMask(damageInfo, missInfo);
procVictim |= ProcFlags.TakenDamage; procVictim |= ProcFlags.TakenDamage;
@@ -1995,7 +1995,7 @@ namespace Game.Spells
if (canEffectTrigger) if (canEffectTrigger)
{ {
DamageInfo spellDamageInfo = new(damageInfo, DamageEffectType.SpellDirect, m_attackType, hitMask); DamageInfo spellDamageInfo = new(damageInfo, DamageEffectType.SpellDirect, m_attackType, hitMask);
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, hitMask, this, spellDamageInfo, null); Unit.ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, hitMask, this, spellDamageInfo, null);
if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) && if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) &&
(m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged)) (m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged))
@@ -2012,7 +2012,7 @@ namespace Game.Spells
if (canEffectTrigger) if (canEffectTrigger)
{ {
DamageInfo spellNoDamageInfo = new(damageInfo, DamageEffectType.NoDamage, m_attackType, hitMask); DamageInfo spellNoDamageInfo = new(damageInfo, DamageEffectType.NoDamage, m_attackType, hitMask);
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.NoDmgHeal, ProcFlagsSpellPhase.Hit, hitMask, this, spellNoDamageInfo, null); Unit.ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, ProcFlagsSpellType.NoDmgHeal, ProcFlagsSpellPhase.Hit, hitMask, this, spellNoDamageInfo, null);
if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) && if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) &&
(m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged)) (m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged))
@@ -2960,7 +2960,7 @@ namespace Game.Spells
if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreAuraInterruptFlags) && !m_spellInfo.HasAttribute(SpellAttr2.IgnoreActionAuraInterruptFlags)) if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreAuraInterruptFlags) && !m_spellInfo.HasAttribute(SpellAttr2.IgnoreActionAuraInterruptFlags))
m_originalCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.ActionDelayed); m_originalCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.ActionDelayed);
m_originalCaster.ProcSkillsAndAuras(null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Cast, hitMask, this, null, null); Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Cast, hitMask, this, null, null);
// Call CreatureAI hook OnSuccessfulSpellCast // Call CreatureAI hook OnSuccessfulSpellCast
Creature caster = m_originalCaster.ToCreature(); Creature caster = m_originalCaster.ToCreature();
@@ -3187,7 +3187,7 @@ namespace Game.Spells
procAttacker = IsPositive() ? ProcFlags.DoneSpellNoneDmgClassPos : ProcFlags.DoneSpellNoneDmgClassNeg; procAttacker = IsPositive() ? ProcFlags.DoneSpellNoneDmgClassPos : ProcFlags.DoneSpellNoneDmgClassNeg;
} }
m_originalCaster.ProcSkillsAndAuras(null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Finish, m_hitMask, this, null, null); Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Finish, m_hitMask, this, null, null);
} }
void SendSpellCooldown() void SendSpellCooldown()
@@ -6865,11 +6865,9 @@ namespace Game.Spells
{ {
if (effect.IsTargetingArea() || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura)) if (effect.IsTargetingArea() || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura))
{ {
m_damage = (int)(m_damage * unit.GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask)); m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_caster);
if (!m_caster.IsTypeId(TypeId.Player))
m_damage = (int)(m_damage * unit.GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
if (m_caster.IsTypeId(TypeId.Player)) if (m_caster.IsPlayer())
{ {
long targetAmount = GetUnitTargetCountForEffect(effect.EffectIndex); long targetAmount = GetUnitTargetCountForEffect(effect.EffectIndex);
if (targetAmount > 20) if (targetAmount > 20)
@@ -6887,7 +6885,8 @@ namespace Game.Spells
} }
} }
targetInfo.crit = m_caster.IsSpellCrit(unit, this, null, m_spellSchoolMask, m_attackType); float critChance = m_caster.SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType);
targetInfo.crit = RandomHelper.randChance(unit.SpellCritChanceTaken(m_caster, this, null, m_spellSchoolMask, critChance, m_attackType));
} }
SpellCastResult CanOpenLock(uint effIndex, uint lockId, ref SkillType skillId, ref int reqSkillValue, ref int skillValue) SpellCastResult CanOpenLock(uint effIndex, uint lockId, ref SkillType skillId, ref int reqSkillValue, ref int skillValue)
@@ -8163,7 +8162,7 @@ namespace Game.Spells
ProcFlagsSpellPhase spellPhaseMask = ProcFlagsSpellPhase.None; ProcFlagsSpellPhase spellPhaseMask = ProcFlagsSpellPhase.None;
ProcFlagsHit hitMask = ProcFlagsHit.Reflect; ProcFlagsHit hitMask = ProcFlagsHit.Reflect;
caster.ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, null, null, null); Unit.ProcSkillsAndAuras(caster, _victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, null, null, null);
return true; return true;
} }
+6 -9
View File
@@ -110,7 +110,7 @@ namespace Game.Spells
data.SpellID = m_spellInfo.Id; data.SpellID = m_spellInfo.Id;
m_caster.SendMessageToSet(data, true); m_caster.SendMessageToSet(data, true);
m_caster.DealDamage(unitTarget, (uint)unitTarget.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false); Unit.DealDamage(m_caster, unitTarget, (uint)unitTarget.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false);
} }
[SpellEffectHandler(SpellEffectName.EnvironmentalDamage)] [SpellEffectHandler(SpellEffectName.EnvironmentalDamage)]
@@ -128,7 +128,7 @@ namespace Game.Spells
else else
{ {
DamageInfo damageInfo = new(m_caster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack); DamageInfo damageInfo = new(m_caster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
m_caster.CalcAbsorbResist(damageInfo); Unit.CalcAbsorbResist(damageInfo);
SpellNonMeleeDamage log = new(m_caster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId); SpellNonMeleeDamage log = new(m_caster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId);
log.damage = damageInfo.GetDamage(); log.damage = damageInfo.GetDamage();
@@ -908,9 +908,8 @@ namespace Game.Spells
addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)caster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo); addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)caster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo);
else else
{ {
addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
uint bonus = caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo); uint bonus = caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
damage = (int)(bonus + (bonus * _variance)); addhealth = (int)(bonus + (uint)(bonus * _variance));
} }
addhealth = (int)unitTarget.SpellHealingBonusTaken(caster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo); addhealth = (int)unitTarget.SpellHealingBonusTaken(caster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
@@ -937,9 +936,7 @@ namespace Game.Spells
return; return;
uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitTarget.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo); uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitTarget.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo);
heal = unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal, effectInfo); m_healing += (int)unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);
m_healing += (int)heal;
} }
[SpellEffectHandler(SpellEffectName.HealMechanical)] [SpellEffectHandler(SpellEffectName.HealMechanical)]
@@ -2705,9 +2702,9 @@ namespace Game.Spells
int duration = m_spellInfo.GetDuration(); int duration = m_spellInfo.GetDuration();
unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), (uint)unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, (uint)(1 << (int)effIndex))); unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), (uint)unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, (uint)(1 << (int)effIndex)));
if (m_spellInfo.DmgClass == SpellDmgClass.Magic) if (m_spellInfo.DmgClass == SpellDmgClass.Magic)
m_originalCaster.ProcSkillsAndAuras(unitTarget, ProcFlags.DoneSpellMagicDmgClassNeg, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); Unit.ProcSkillsAndAuras(m_originalCaster, unitTarget, ProcFlags.DoneSpellMagicDmgClassNeg, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null);
else if (m_spellInfo.DmgClass == SpellDmgClass.Melee) else if (m_spellInfo.DmgClass == SpellDmgClass.Melee)
m_originalCaster.ProcSkillsAndAuras(unitTarget, ProcFlags.DoneSpellMeleeDmgClass, ProcFlags.TakenSpellMeleeDmgClass, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); Unit.ProcSkillsAndAuras(m_originalCaster, unitTarget, ProcFlags.DoneSpellMeleeDmgClass, ProcFlags.TakenSpellMeleeDmgClass, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null);
} }
ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo.Id); ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo.Id);
unitTarget.InterruptSpell(i, false); unitTarget.InterruptSpell(i, false);
+8
View File
@@ -2986,6 +2986,14 @@ namespace Game.Entities
spellInfo.GetEffect(0).TriggerSpell = 46737; spellInfo.GetEffect(0).TriggerSpell = 46737;
spellInfo.GetEffect(0).ApplyAuraName = AuraType.PeriodicTriggerSpell; spellInfo.GetEffect(0).ApplyAuraName = AuraType.PeriodicTriggerSpell;
break; break;
case 379: // Earth Shield
case 71607: // Item - Bauble of True Blood 10m
case 71646: // Item - Bauble of True Blood 25m
case 71610: // Item - Althor's Abacus trigger 10m
case 71641: // Item - Althor's Abacus trigger 25m
// We need more spells to find a general way (if there is any)
spellInfo.DmgClass = SpellDmgClass.Magic;
break;
case 63026: // Summon Aspirant Test NPC (HACK: Target shouldn't be changed) case 63026: // Summon Aspirant Test NPC (HACK: Target shouldn't be changed)
case 63137: // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination) case 63137: // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination)
spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb);
+1 -1
View File
@@ -271,7 +271,7 @@ namespace Scripts.Spells.DeathKnight
int amount = (int)damageInfo.GetDamage() / 2; int amount = (int)damageInfo.GetDamage() / 2;
SpellNonMeleeDamage log = new SpellNonMeleeDamage(drw, drw.GetVictim(), spellInfo, new SpellCastVisual(spellInfo.GetSpellXSpellVisualId(drw), 0), spellInfo.GetSchoolMask()); SpellNonMeleeDamage log = new SpellNonMeleeDamage(drw, drw.GetVictim(), spellInfo, new SpellCastVisual(spellInfo.GetSpellXSpellVisualId(drw), 0), spellInfo.GetSchoolMask());
log.damage = (uint)amount; log.damage = (uint)amount;
drw.DealDamage(drw.GetVictim(), (uint)amount, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true); Unit.DealDamage(drw, drw.GetVictim(), (uint)amount, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true);
drw.SendSpellNonMeleeDamageLog(log); drw.SendSpellNonMeleeDamageLog(log);
} }
+2 -2
View File
@@ -287,7 +287,7 @@ namespace Scripts.Spells.Monk
if (auraEff != null) if (auraEff != null)
{ {
float total = auraEff.GetAmount(); float total = auraEff.GetAmount();
float tickDamage = aurEff.GetDamage(); float tickDamage = aurEff.GetAmount();
auraEff.ChangeAmount((int)(total - tickDamage)); auraEff.ChangeAmount((int)(total - tickDamage));
} }
} }
@@ -359,7 +359,7 @@ namespace Scripts.Spells.Monk
{ {
AuraEffect eff = auraDamage.GetEffect(0); AuraEffect eff = auraDamage.GetEffect(0);
if (eff != null) if (eff != null)
eff.SetDamage((int)tickDamage); eff.ChangeAmount((int)tickDamage);
} }
} }
} }